2010-08-09 07:01:59 +00:00
|
|
|
#include "interpreter.h"
|
|
|
|
|
2011-06-15 06:54:12 +00:00
|
|
|
/**
|
|
|
|
* Creates a new string by copying the contents of another string.
|
|
|
|
*
|
|
|
|
* \param [in] data The string to copy.
|
|
|
|
*
|
|
|
|
* \return A new string whose contents is a copy of \a data.
|
|
|
|
*
|
|
|
|
* \retval NULL Memory allocation failed.
|
|
|
|
*/
|
|
|
|
char *copyString(char *data)
|
2010-08-09 07:01:59 +00:00
|
|
|
{
|
|
|
|
char *p = malloc(sizeof(char) * (strlen(data) + 1));
|
|
|
|
if (!p) {
|
|
|
|
perror("malloc");
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
strcpy(p, data);
|
|
|
|
return p;
|
|
|
|
}
|
|
|
|
|
2011-06-15 06:54:12 +00:00
|
|
|
/**
|
|
|
|
* Checks if a string follows the format of a hexadecimal number.
|
|
|
|
*
|
|
|
|
* \param [in] data The characters to check the format of.
|
|
|
|
*
|
|
|
|
* \retval 0 The string is not a hexadecimal number.
|
|
|
|
*
|
|
|
|
* \retval 1 The string is a hexadecimal number.
|
|
|
|
*/
|
|
|
|
unsigned int isHexString(const char *data)
|
|
|
|
{
|
|
|
|
size_t n;
|
|
|
|
size_t len = strlen(data);
|
|
|
|
|
|
|
|
/* Check for empty string */
|
|
|
|
if (len == 0) return 0;
|
|
|
|
|
|
|
|
/* Check for non-digit and non-A-through-F characters */
|
|
|
|
for (n = 0; n < len; n++) {
|
|
|
|
if (!isdigit(data[n])
|
|
|
|
&& data[n] != 'A'
|
|
|
|
&& data[n] != 'B'
|
|
|
|
&& data[n] != 'C'
|
|
|
|
&& data[n] != 'D'
|
|
|
|
&& data[n] != 'E'
|
|
|
|
&& data[n] != 'F'
|
|
|
|
&& data[n] != 'a'
|
|
|
|
&& data[n] != 'b'
|
|
|
|
&& data[n] != 'c'
|
|
|
|
&& data[n] != 'd'
|
|
|
|
&& data[n] != 'e'
|
|
|
|
&& data[n] != 'f')
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Evaluates an identifier to produce its name as a string.
|
|
|
|
*
|
|
|
|
* \param [in] id The identifier to evaluate.
|
|
|
|
*
|
|
|
|
* \param [in] scope The scope to evaluate \a id under.
|
|
|
|
*
|
|
|
|
* \return A new string containing the evaluated name of the identifier.
|
|
|
|
*
|
|
|
|
* \retval NULL Memory allocation failed.
|
|
|
|
*/
|
|
|
|
char *resolveIdentifierName(IdentifierNode *id,
|
|
|
|
ScopeObject *scope)
|
|
|
|
{
|
|
|
|
ValueObject *val = NULL;
|
|
|
|
ValueObject *str = NULL;
|
|
|
|
char *ret = NULL;
|
|
|
|
|
|
|
|
if (!id) goto resolveIdentifierNameAbort;
|
|
|
|
|
|
|
|
if (id->type == IT_DIRECT) {
|
|
|
|
/* Just return a copy of the character array */
|
|
|
|
const char *temp = (char *)(id->id);
|
|
|
|
ret = malloc(sizeof(char) * (strlen(temp) + 1));
|
|
|
|
strcpy(ret, temp);
|
2014-10-25 04:57:44 +00:00
|
|
|
} else if (id->type == IT_INDIRECT) {
|
2011-06-15 06:54:12 +00:00
|
|
|
ExprNode *expr = (ExprNode *)(id->id);
|
|
|
|
|
|
|
|
/* Interpret the identifier expression */
|
|
|
|
val = interpretExprNode(expr, scope);
|
|
|
|
if (!val) goto resolveIdentifierNameAbort;
|
|
|
|
|
|
|
|
/* Then cast it to a string */
|
|
|
|
str = castStringExplicit(val, scope);
|
|
|
|
if (!str) goto resolveIdentifierNameAbort;
|
|
|
|
deleteValueObject(val);
|
|
|
|
|
|
|
|
/* Copy the evaluated string */
|
|
|
|
ret = copyString(getString(str));
|
|
|
|
if (!ret) goto resolveIdentifierNameAbort;
|
|
|
|
deleteValueObject(str);
|
2014-10-25 04:57:44 +00:00
|
|
|
} else {
|
2012-12-13 04:53:54 +00:00
|
|
|
char *name = resolveIdentifierName(id, scope);
|
|
|
|
error(IN_INVALID_IDENTIFIER_TYPE, id->fname, id->line, name);
|
|
|
|
free(name);
|
2011-06-15 06:54:12 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
|
|
|
|
resolveIdentifierNameAbort: /* Exception handline */
|
|
|
|
|
|
|
|
/* Clean up any allocated structures */
|
|
|
|
if (ret) free(ret);
|
|
|
|
if (str) deleteValueObject(str);
|
|
|
|
if (val) deleteValueObject(val);
|
|
|
|
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Creates a nil-type value.
|
|
|
|
*
|
|
|
|
* \return A new nil-type value.
|
|
|
|
*
|
|
|
|
* \retval NULL Memory allocation failed.
|
|
|
|
*/
|
2010-08-09 07:01:59 +00:00
|
|
|
ValueObject *createNilValueObject(void)
|
|
|
|
{
|
|
|
|
ValueObject *p = malloc(sizeof(ValueObject));
|
|
|
|
if (!p) {
|
|
|
|
perror("malloc");
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
p->type = VT_NIL;
|
|
|
|
p->semaphore = 1;
|
|
|
|
return p;
|
|
|
|
}
|
|
|
|
|
2011-06-15 06:54:12 +00:00
|
|
|
/**
|
|
|
|
* Creates a boolean-type value.
|
|
|
|
*
|
|
|
|
* \param [in] data The boolean data to store.
|
|
|
|
*
|
|
|
|
* \return A boolean-type value equalling 0 if \a data equals 0 and 1 otherwise.
|
|
|
|
*
|
|
|
|
* \retval NULL Memory allocation failed.
|
|
|
|
*/
|
|
|
|
ValueObject *createBooleanValueObject(int data)
|
2010-08-09 07:01:59 +00:00
|
|
|
{
|
|
|
|
ValueObject *p = malloc(sizeof(ValueObject));
|
|
|
|
if (!p) {
|
|
|
|
perror("malloc");
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
p->type = VT_BOOLEAN;
|
|
|
|
p->data.i = (data != 0);
|
|
|
|
p->semaphore = 1;
|
|
|
|
return p;
|
|
|
|
}
|
|
|
|
|
2011-06-15 06:54:12 +00:00
|
|
|
/**
|
|
|
|
* Creates a integer-type value.
|
|
|
|
*
|
|
|
|
* \param [in] data The integer data to store.
|
|
|
|
*
|
|
|
|
* \return An integer-type value equalling \a data.
|
|
|
|
*
|
|
|
|
* \retval NULL Memory allocation failed.
|
|
|
|
*/
|
2013-10-04 16:17:19 +00:00
|
|
|
ValueObject *createIntegerValueObject(long long data)
|
2010-08-09 07:01:59 +00:00
|
|
|
{
|
|
|
|
ValueObject *p = malloc(sizeof(ValueObject));
|
|
|
|
if (!p) {
|
|
|
|
perror("malloc");
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
p->type = VT_INTEGER;
|
|
|
|
p->data.i = data;
|
|
|
|
p->semaphore = 1;
|
|
|
|
return p;
|
|
|
|
}
|
|
|
|
|
2011-06-15 06:54:12 +00:00
|
|
|
/**
|
|
|
|
* Creates a floating-point-type value.
|
|
|
|
*
|
|
|
|
* \param [in] data The floating-point data to store.
|
|
|
|
*
|
|
|
|
* \return A floating-point-type value equalling \a data.
|
|
|
|
*
|
|
|
|
* \retval NULL Memory allocation failed.
|
|
|
|
*/
|
|
|
|
ValueObject *createFloatValueObject(float data)
|
2010-08-09 07:01:59 +00:00
|
|
|
{
|
|
|
|
ValueObject *p = malloc(sizeof(ValueObject));
|
|
|
|
if (!p) {
|
|
|
|
perror("malloc");
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
p->type = VT_FLOAT;
|
|
|
|
p->data.f = data;
|
|
|
|
p->semaphore = 1;
|
|
|
|
return p;
|
|
|
|
}
|
|
|
|
|
2011-06-15 06:54:12 +00:00
|
|
|
/**
|
|
|
|
* Creates a string-type value.
|
|
|
|
*
|
|
|
|
* \param [in] data The string data to store.
|
|
|
|
*
|
|
|
|
* \note \a data is stored as-is; no copy of it is made.
|
|
|
|
*
|
|
|
|
* \return A string-type value equalling \a data.
|
|
|
|
*
|
|
|
|
* \retval NULL Memory allocation failed.
|
|
|
|
*/
|
|
|
|
ValueObject *createStringValueObject(char *data)
|
2010-08-09 07:01:59 +00:00
|
|
|
{
|
|
|
|
ValueObject *p = malloc(sizeof(ValueObject));
|
|
|
|
if (!p) {
|
|
|
|
perror("malloc");
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
p->type = VT_STRING;
|
|
|
|
p->data.s = data;
|
|
|
|
p->semaphore = 1;
|
|
|
|
return p;
|
|
|
|
}
|
|
|
|
|
2011-06-15 06:54:12 +00:00
|
|
|
/**
|
|
|
|
* Creates a function-type value.
|
|
|
|
*
|
|
|
|
* \param [in] def The function definition to store.
|
|
|
|
*
|
|
|
|
* \return A function-type value containing \a data.
|
|
|
|
*
|
|
|
|
* \retval NULL Memory allocation failed.
|
|
|
|
*/
|
|
|
|
ValueObject *createFunctionValueObject(FuncDefStmtNode *def)
|
2010-11-16 05:25:51 +00:00
|
|
|
{
|
|
|
|
ValueObject *p = malloc(sizeof(ValueObject));
|
|
|
|
if (!p) {
|
|
|
|
perror("malloc");
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
p->type = VT_FUNC;
|
2011-06-15 06:54:12 +00:00
|
|
|
p->data.fn = def;
|
2010-11-16 05:25:51 +00:00
|
|
|
p->semaphore = 1;
|
|
|
|
return p;
|
|
|
|
}
|
|
|
|
|
2011-06-15 06:54:12 +00:00
|
|
|
/**
|
|
|
|
* Creates an array-type value.
|
|
|
|
*
|
|
|
|
* \param [in] parent The optional parent scope to use.
|
|
|
|
*
|
|
|
|
* \note \a parent may be NULL, in which case this array is treated as the root.
|
|
|
|
*
|
|
|
|
* \return An empty array-type value with parent \a parent.
|
|
|
|
*
|
|
|
|
* \retval NULL Memory allocation failed.
|
|
|
|
*/
|
|
|
|
ValueObject *createArrayValueObject(ScopeObject *parent)
|
2010-08-09 07:01:59 +00:00
|
|
|
{
|
2011-06-15 06:54:12 +00:00
|
|
|
ValueObject *p = malloc(sizeof(ValueObject));
|
2010-08-09 07:01:59 +00:00
|
|
|
if (!p) {
|
|
|
|
perror("malloc");
|
|
|
|
return NULL;
|
|
|
|
}
|
2011-06-15 06:54:12 +00:00
|
|
|
p->type = VT_ARRAY;
|
|
|
|
p->data.a = createScopeObject(parent);
|
|
|
|
if (!p->data.a) {
|
|
|
|
free(p);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
p->semaphore = 1;
|
2010-08-09 07:01:59 +00:00
|
|
|
return p;
|
|
|
|
}
|
|
|
|
|
2013-02-24 07:40:58 +00:00
|
|
|
/**
|
|
|
|
* Creates a blob-type value.
|
|
|
|
*
|
|
|
|
* \param [in] data The binary blob data to store.
|
|
|
|
*
|
|
|
|
* \note \a data is stored as-is; no copy of it is made.
|
|
|
|
*
|
|
|
|
* \return A string-type value equalling \a data.
|
|
|
|
*
|
|
|
|
* \retval NULL Memory allocation failed.
|
|
|
|
*/
|
|
|
|
ValueObject *createBlobValueObject(void *data)
|
|
|
|
{
|
|
|
|
ValueObject *p = malloc(sizeof(ValueObject));
|
|
|
|
if (!p) {
|
|
|
|
perror("malloc");
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
p->type = VT_BLOB;
|
|
|
|
p->data.b = data;
|
|
|
|
p->semaphore = 1;
|
|
|
|
return p;
|
|
|
|
}
|
|
|
|
|
2011-06-15 06:54:12 +00:00
|
|
|
/**
|
|
|
|
* Copies a value.
|
|
|
|
*
|
|
|
|
* Instead of actually performing a copy of memory, this function increments a
|
|
|
|
* semaphore in \a value and returns \a value again. The semaphore gets
|
|
|
|
* decremented when \a value gets deleted. This way, an immutable copy of a
|
|
|
|
* value may be made without actually copying its blocks of memory; this reduces
|
|
|
|
* the overhead associated with copying a value--a fairly common
|
|
|
|
* operation--while still preserving its usability.
|
|
|
|
*
|
|
|
|
* \param [in,out] value The value to copy.
|
|
|
|
*
|
|
|
|
* \return A value with the same type and contents as \a value.
|
|
|
|
*
|
|
|
|
* \retval NULL The type of \a value is unrecognized.
|
|
|
|
*/
|
|
|
|
ValueObject *copyValueObject(ValueObject *value)
|
2010-08-09 07:01:59 +00:00
|
|
|
{
|
2011-06-15 06:54:12 +00:00
|
|
|
V(value);
|
|
|
|
return value;
|
2010-08-09 07:01:59 +00:00
|
|
|
}
|
|
|
|
|
2011-06-15 06:54:12 +00:00
|
|
|
/**
|
|
|
|
* Deletes a value.
|
|
|
|
*
|
|
|
|
* This function decrements a semaphore in \a value and deletes \a value if the
|
|
|
|
* semaphore reaches 0 (no copies of this value are need anymore). The
|
|
|
|
* semaphore gets incremented when either the value is created or it gets
|
|
|
|
* copied. This way, an immutable copy of the value may be made without
|
|
|
|
* actually copying its memory.
|
|
|
|
*
|
|
|
|
* \param [in,out] value The value to delete.
|
|
|
|
*
|
|
|
|
* \post The memory at \a value and any of its members will be freed (although
|
|
|
|
* see note for full details).
|
|
|
|
*/
|
|
|
|
void deleteValueObject(ValueObject *value)
|
2010-12-20 23:30:37 +00:00
|
|
|
{
|
2011-06-15 06:54:12 +00:00
|
|
|
if (!value) return;
|
|
|
|
P(value);
|
|
|
|
if (!value->semaphore) {
|
|
|
|
if (value->type == VT_STRING)
|
|
|
|
free(value->data.s);
|
|
|
|
/* FuncDefStmtNode structures get freed with the parse tree */
|
|
|
|
else if (value->type == VT_ARRAY)
|
|
|
|
deleteScopeObject(value->data.a);
|
|
|
|
free(value);
|
2010-12-20 23:30:37 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2011-06-15 06:54:12 +00:00
|
|
|
/**
|
|
|
|
* Creates a scope.
|
|
|
|
*
|
|
|
|
* Scopes are used to map identifiers to values. Scopes are organized
|
|
|
|
* hierarchically.
|
|
|
|
*
|
|
|
|
* \param [in] parent The optional parent scope to use.
|
|
|
|
*
|
|
|
|
* \return An empty scope with parent \a parent.
|
|
|
|
*
|
|
|
|
* \retval NULL Memory allocation failed.
|
|
|
|
*/
|
|
|
|
ScopeObject *createScopeObject(ScopeObject *parent)
|
2010-08-09 07:01:59 +00:00
|
|
|
{
|
|
|
|
ScopeObject *p = malloc(sizeof(ScopeObject));
|
|
|
|
if (!p) {
|
|
|
|
perror("malloc");
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
p->impvar = createNilValueObject();
|
|
|
|
if (!p->impvar) {
|
|
|
|
free(p);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
p->numvals = 0;
|
|
|
|
p->names = NULL;
|
|
|
|
p->values = NULL;
|
|
|
|
p->parent = parent;
|
2011-07-19 07:02:43 +00:00
|
|
|
if (parent) p->caller = parent->caller;
|
|
|
|
else p->caller = NULL;
|
|
|
|
return p;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Creates a scope with a specific caller.
|
|
|
|
*
|
|
|
|
* \param [in] parent The optional parent scope to use.
|
|
|
|
*
|
|
|
|
* \param [in] caller The caller scope to use.
|
|
|
|
*
|
|
|
|
* \return An empty scope with parent \a parent and caller \a caller.
|
|
|
|
*
|
|
|
|
* \retval NULL Memory allocation failed.
|
|
|
|
*/
|
|
|
|
ScopeObject *createScopeObjectCaller(ScopeObject *parent,
|
|
|
|
ScopeObject *caller)
|
|
|
|
{
|
|
|
|
ScopeObject *p = createScopeObject(parent);
|
|
|
|
if (!p) return NULL;
|
|
|
|
if (caller) p->caller = caller;
|
2010-08-09 07:01:59 +00:00
|
|
|
return p;
|
|
|
|
}
|
|
|
|
|
2011-06-15 06:54:12 +00:00
|
|
|
/**
|
|
|
|
* Deletes a scope.
|
|
|
|
*
|
|
|
|
* \param [in,out] scope The scope to delete.
|
|
|
|
*
|
|
|
|
* \post The memory at \a scope and any of its members will be freed.
|
|
|
|
*/
|
|
|
|
void deleteScopeObject(ScopeObject *scope)
|
2010-08-09 07:01:59 +00:00
|
|
|
{
|
|
|
|
unsigned int n;
|
|
|
|
if (!scope) return;
|
|
|
|
for (n = 0; n < scope->numvals; n++) {
|
2010-12-20 23:30:37 +00:00
|
|
|
free(scope->names[n]);
|
2010-08-09 07:01:59 +00:00
|
|
|
deleteValueObject(scope->values[n]);
|
|
|
|
}
|
|
|
|
free(scope->names);
|
|
|
|
free(scope->values);
|
|
|
|
deleteValueObject(scope->impvar);
|
|
|
|
free(scope);
|
|
|
|
}
|
|
|
|
|
2011-06-15 06:54:12 +00:00
|
|
|
/**
|
|
|
|
* Creates a new, nil-type value in a scope.
|
|
|
|
*
|
|
|
|
* \param [in] src The scope to evaluate \a target under.
|
|
|
|
*
|
|
|
|
* \param [in,out] dest The scope to create the new value in.
|
|
|
|
*
|
|
|
|
* \param [in] target The name of the value to create.
|
|
|
|
*
|
|
|
|
* \return The newly-created value.
|
|
|
|
*
|
|
|
|
* \retval NULL Memory allocation failed.
|
|
|
|
*/
|
|
|
|
ValueObject *createScopeValue(ScopeObject *src,
|
|
|
|
ScopeObject *dest,
|
|
|
|
IdentifierNode *target)
|
|
|
|
{
|
|
|
|
ScopeObject *parent = dest;
|
|
|
|
IdentifierNode *child = target;
|
|
|
|
int status;
|
|
|
|
unsigned int newnumvals;
|
|
|
|
void *mem1 = NULL;
|
|
|
|
void *mem2 = NULL;
|
|
|
|
char *name = NULL;
|
2014-10-27 03:16:26 +00:00
|
|
|
unsigned int pos = 0;
|
2011-06-15 06:54:12 +00:00
|
|
|
|
|
|
|
/* Traverse the target to the terminal child and parent */
|
|
|
|
status = resolveTerminalSlot(src, dest, target, &parent, &child);
|
|
|
|
if (!status) goto createScopeValueAbort;
|
|
|
|
|
|
|
|
/* Store the new number of values */
|
|
|
|
newnumvals = dest->numvals + 1;
|
|
|
|
|
|
|
|
/* Look up the identifier name */
|
|
|
|
name = resolveIdentifierName(target, src);
|
|
|
|
if (!name) goto createScopeValueAbort;
|
|
|
|
|
2014-10-27 03:16:26 +00:00
|
|
|
/* realloc if power of two */
|
|
|
|
if (newnumvals && !(newnumvals & (newnumvals - 1))) {
|
|
|
|
int allocnumvals = (newnumvals << 1);
|
|
|
|
/* Add value to local scope */
|
|
|
|
mem1 = realloc(dest->names, sizeof(IdentifierNode *) * allocnumvals);
|
|
|
|
if (!mem1) {
|
|
|
|
perror("realloc");
|
|
|
|
goto createScopeValueAbort;
|
|
|
|
}
|
|
|
|
mem2 = realloc(dest->values, sizeof(ValueObject *) * allocnumvals);
|
|
|
|
if (!mem2) {
|
|
|
|
perror("realloc");
|
|
|
|
goto createScopeValueAbort;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
mem1 = dest->names;
|
|
|
|
mem2 = dest->values;
|
2011-06-15 06:54:12 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
dest->names = mem1;
|
|
|
|
dest->values = mem2;
|
2014-10-27 03:16:26 +00:00
|
|
|
|
|
|
|
/* Insert in lexical order */
|
|
|
|
{
|
|
|
|
int n;
|
|
|
|
|
|
|
|
if (dest->numvals > 0) {
|
|
|
|
/* Find insertion position */
|
|
|
|
pos = binarySearchIndex((const char **)dest->names, 0, dest->numvals - 1, (const char *)name);
|
|
|
|
|
|
|
|
/* Shift values down */
|
|
|
|
for (n = dest->numvals; n > pos; n--) {
|
|
|
|
dest->names[n] = dest->names[n - 1];
|
|
|
|
dest->values[n] = dest->values[n - 1];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Insert the value */
|
|
|
|
dest->names[pos] = name;
|
|
|
|
dest->values[pos] = createNilValueObject();
|
|
|
|
if (!dest->values[pos]) goto createScopeValueAbort;
|
|
|
|
}
|
|
|
|
|
2011-06-15 06:54:12 +00:00
|
|
|
dest->numvals = newnumvals;
|
|
|
|
|
2014-10-27 03:16:26 +00:00
|
|
|
return dest->values[pos];
|
2011-06-15 06:54:12 +00:00
|
|
|
|
|
|
|
createScopeValueAbort: /* In case something goes wrong... */
|
|
|
|
|
|
|
|
/* Clean up any allocated structures */
|
|
|
|
if (name) free(name);
|
|
|
|
if (mem1) free(mem1);
|
|
|
|
if (mem2) free(mem2);
|
|
|
|
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Updates a value in a scope.
|
|
|
|
*
|
|
|
|
* \param [in] src The scope to evaluate \a target under.
|
|
|
|
*
|
|
|
|
* \param [in,out] dest The scope to update the value in.
|
|
|
|
*
|
|
|
|
* \param [in] target The name of the value to create.
|
|
|
|
*
|
|
|
|
* \param [in] value The new value to assign.
|
|
|
|
*
|
|
|
|
* \return The updated value (will be the same as \a val).
|
|
|
|
*
|
|
|
|
* \retval NULL Either \a target could not be evaluated in \a src or \a target
|
|
|
|
* could not be found in \a dest.
|
|
|
|
*/
|
|
|
|
ValueObject *updateScopeValue(ScopeObject *src,
|
|
|
|
ScopeObject *dest,
|
|
|
|
IdentifierNode *target,
|
|
|
|
ValueObject *value)
|
|
|
|
{
|
|
|
|
ScopeObject *parent = dest;
|
|
|
|
IdentifierNode *child = target;
|
|
|
|
int status;
|
2010-12-20 23:30:37 +00:00
|
|
|
char *name = NULL;
|
2010-12-21 08:20:31 +00:00
|
|
|
|
2011-06-15 06:54:12 +00:00
|
|
|
/* Traverse the target to the terminal child and parent */
|
|
|
|
status = resolveTerminalSlot(src, dest, target, &parent, &child);
|
|
|
|
if (!status) goto updateScopeValueAbort;
|
|
|
|
|
2010-12-21 08:20:31 +00:00
|
|
|
/* Look up the identifier name */
|
2011-06-15 06:54:12 +00:00
|
|
|
name = resolveIdentifierName(child, src);
|
|
|
|
if (!name) goto updateScopeValueAbort;
|
|
|
|
|
|
|
|
/* Traverse upwards through scopes */
|
|
|
|
do {
|
2014-10-27 03:16:26 +00:00
|
|
|
if (parent->numvals == 0) continue;
|
|
|
|
|
|
|
|
unsigned int n = binarySearchIndex((const char **)parent->names, 0, parent->numvals - 1, (const char *)name);
|
|
|
|
|
|
|
|
if (n >= parent->numvals) continue;
|
|
|
|
|
|
|
|
if (!strcmp(parent->names[n], name)) {
|
|
|
|
free(name);
|
|
|
|
/* Delete the old value */
|
|
|
|
deleteValueObject(parent->values[n]);
|
|
|
|
/* Assign the new value */
|
|
|
|
if (value) {
|
|
|
|
parent->values[n] = value;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
parent->values[n] = createNilValueObject();
|
2011-06-15 06:54:12 +00:00
|
|
|
}
|
2014-10-27 03:16:26 +00:00
|
|
|
return parent->values[n];
|
2011-06-15 06:54:12 +00:00
|
|
|
}
|
|
|
|
} while ((parent = parent->parent));
|
|
|
|
|
2012-12-13 04:53:54 +00:00
|
|
|
{
|
|
|
|
char *name = resolveIdentifierName(target, src);
|
|
|
|
error(IN_UNABLE_TO_STORE_VARIABLE, target->fname, target->line, name);
|
|
|
|
free(name);
|
|
|
|
}
|
2011-06-15 06:54:12 +00:00
|
|
|
|
|
|
|
updateScopeValueAbort: /* In case something goes wrong... */
|
|
|
|
|
|
|
|
/* Clean up any allocated structures */
|
|
|
|
if (name) free(name);
|
|
|
|
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Gets a stored value in a scope.
|
|
|
|
*
|
|
|
|
* \param [in] src The scope to evaluate \a target under.
|
|
|
|
*
|
|
|
|
* \param [in,out] dest The scope to update the value in.
|
|
|
|
*
|
|
|
|
* \param [in] target The name of the value to get.
|
|
|
|
*
|
|
|
|
* \return The value in \a dest, named by evaluating \a target under \a src.
|
|
|
|
*
|
|
|
|
* \retval NULL Either \a target could not be evaluated in \a src or \a target
|
|
|
|
* could not be found in \a dest.
|
|
|
|
*/
|
|
|
|
ValueObject *getScopeValue(ScopeObject *src,
|
|
|
|
ScopeObject *dest,
|
|
|
|
IdentifierNode *target)
|
|
|
|
{
|
|
|
|
ScopeObject *parent = dest;
|
|
|
|
IdentifierNode *child = target;
|
|
|
|
char *name = NULL;
|
|
|
|
int status;
|
|
|
|
|
|
|
|
/* Traverse the target to the terminal child and parent */
|
|
|
|
status = resolveTerminalSlot(src, dest, target, &parent, &child);
|
|
|
|
if (!status) goto getScopeValueAbort;
|
|
|
|
|
|
|
|
/* Look up the identifier name */
|
|
|
|
name = resolveIdentifierName(child, src);
|
|
|
|
if (!name) goto getScopeValueAbort;
|
|
|
|
|
|
|
|
/* Traverse upwards through scopes */
|
|
|
|
do {
|
|
|
|
unsigned int n;
|
|
|
|
/* Check for value in current scope */
|
|
|
|
for (n = 0; n < parent->numvals; n++) {
|
|
|
|
if (!strcmp(parent->names[n], name)) {
|
|
|
|
free(name);
|
|
|
|
return parent->values[n];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} while ((parent = parent->parent));
|
|
|
|
|
2012-12-13 04:53:54 +00:00
|
|
|
{
|
|
|
|
char *name = resolveIdentifierName(child, src);
|
|
|
|
error(IN_VARIABLE_DOES_NOT_EXIST, child->fname, child->line, name);
|
|
|
|
free(name);
|
|
|
|
}
|
2011-06-15 06:54:12 +00:00
|
|
|
|
|
|
|
getScopeValueAbort: /* In case something goes wrong... */
|
|
|
|
|
|
|
|
/* Clean up any allocated structures */
|
|
|
|
if (name) free(name);
|
|
|
|
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2011-07-19 07:02:43 +00:00
|
|
|
* Gets a scope without accessing any arrays.
|
2011-06-15 06:54:12 +00:00
|
|
|
*
|
|
|
|
* \param [in] src The scope to evaluate \a target under.
|
|
|
|
*
|
|
|
|
* \param [in,out] dest The scope to update the value in.
|
|
|
|
*
|
2011-07-19 07:02:43 +00:00
|
|
|
* \param [in] target The name of the value containing the scope to get.
|
2011-06-15 06:54:12 +00:00
|
|
|
*
|
2011-07-19 07:02:43 +00:00
|
|
|
* \return The scope contained in the value in \a dest, named by evaluating \a
|
|
|
|
* target under \a src, without accessing any arrays.
|
2011-06-15 06:54:12 +00:00
|
|
|
*
|
|
|
|
* \retval NULL Either \a target could not be evaluated in \a src or \a target
|
|
|
|
* could not be found in \a dest.
|
|
|
|
*/
|
2011-07-19 07:02:43 +00:00
|
|
|
/** \todo Add this definition to interpreter.h */
|
|
|
|
ScopeObject *getScopeObjectLocal(ScopeObject *src,
|
|
|
|
ScopeObject *dest,
|
|
|
|
IdentifierNode *target)
|
2011-06-15 06:54:12 +00:00
|
|
|
{
|
|
|
|
ScopeObject *current = dest;
|
|
|
|
char *name = NULL;
|
|
|
|
|
|
|
|
/* Look up the identifier name */
|
|
|
|
name = resolveIdentifierName(target, src);
|
2011-07-19 07:02:43 +00:00
|
|
|
if (!name) goto getScopeObjectLocalAbort;
|
|
|
|
|
|
|
|
/* Check for calling object reference variable */
|
|
|
|
if (!strcmp(name, "ME")) {
|
|
|
|
/* Traverse upwards through callers */
|
|
|
|
for (current = dest;
|
|
|
|
current->caller;
|
|
|
|
current = current->caller);
|
|
|
|
free(name);
|
|
|
|
return current;
|
|
|
|
}
|
2010-12-21 08:20:31 +00:00
|
|
|
|
2010-08-09 07:01:59 +00:00
|
|
|
/* Traverse upwards through scopes */
|
|
|
|
do {
|
|
|
|
unsigned int n;
|
|
|
|
/* Check for value in current scope */
|
|
|
|
for (n = 0; n < current->numvals; n++) {
|
2010-12-20 23:30:37 +00:00
|
|
|
if (!strcmp(current->names[n], name)) {
|
2014-10-25 04:57:44 +00:00
|
|
|
if (current->values[n]->type != VT_ARRAY) {
|
|
|
|
error(IN_VARIABLE_NOT_AN_ARRAY, target->fname, target->line, name);
|
|
|
|
goto getScopeObjectLocalAbort;
|
|
|
|
}
|
2010-12-20 23:30:37 +00:00
|
|
|
free(name);
|
2011-07-19 07:02:43 +00:00
|
|
|
return getArray(current->values[n]);
|
2010-12-20 23:30:37 +00:00
|
|
|
}
|
2010-08-09 07:01:59 +00:00
|
|
|
}
|
|
|
|
} while ((current = current->parent));
|
2011-06-15 06:54:12 +00:00
|
|
|
|
2012-12-13 04:53:54 +00:00
|
|
|
{
|
|
|
|
char *name = resolveIdentifierName(target, src);
|
|
|
|
error(IN_VARIABLE_DOES_NOT_EXIST, target->fname, target->line, name);
|
|
|
|
free(name);
|
|
|
|
}
|
2011-07-18 03:22:20 +00:00
|
|
|
|
2011-07-19 07:02:43 +00:00
|
|
|
getScopeObjectLocalAbort: /* In case something goes wrong... */
|
2011-06-15 06:54:12 +00:00
|
|
|
|
|
|
|
/* Clean up any allocated structures */
|
|
|
|
if (name) free(name);
|
|
|
|
|
2010-08-09 07:01:59 +00:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2014-10-25 04:57:44 +00:00
|
|
|
/**
|
|
|
|
* Gets a scope (possibly by casting a function) without accessing any arrays.
|
|
|
|
*
|
|
|
|
* \param [in] src The scope to evaluate \a target under.
|
|
|
|
*
|
|
|
|
* \param [in,out] dest The scope to update the value in.
|
|
|
|
*
|
|
|
|
* \param [in] target The name of the value containing the scope to get.
|
|
|
|
*
|
|
|
|
* \return The scope contained in the value in \a dest, named by evaluating \a
|
|
|
|
* target under \a src, without accessing any arrays.
|
|
|
|
*
|
|
|
|
* \retval NULL Either \a target could not be evaluated in \a src or \a target
|
|
|
|
* could not be found in \a dest.
|
|
|
|
*/
|
|
|
|
/** \todo Add this definition to interpreter.h */
|
|
|
|
ScopeObject *getScopeObjectLocalCaller(ScopeObject *src,
|
|
|
|
ScopeObject *dest,
|
|
|
|
IdentifierNode *target)
|
|
|
|
{
|
|
|
|
ScopeObject *current = dest;
|
|
|
|
char *name = NULL;
|
|
|
|
|
|
|
|
/* Look up the identifier name */
|
|
|
|
name = resolveIdentifierName(target, src);
|
|
|
|
if (!name) goto getScopeObjectLocalCallerAbort;
|
|
|
|
|
|
|
|
/* Check for calling object reference variable */
|
|
|
|
if (!strcmp(name, "ME")) {
|
|
|
|
/* Traverse upwards through callers */
|
|
|
|
for (current = dest;
|
|
|
|
current->caller;
|
|
|
|
current = current->caller);
|
|
|
|
free(name);
|
|
|
|
return current;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Traverse upwards through scopes */
|
|
|
|
do {
|
|
|
|
unsigned int n;
|
|
|
|
/* Check for value in current scope */
|
|
|
|
for (n = 0; n < current->numvals; n++) {
|
|
|
|
if (!strcmp(current->names[n], name)) {
|
|
|
|
if (current->values[n]->type != VT_ARRAY
|
|
|
|
&& current->values[n]->type != VT_FUNC) {
|
|
|
|
error(IN_VARIABLE_NOT_AN_ARRAY, target->fname, target->line, name);
|
|
|
|
goto getScopeObjectLocalCallerAbort;
|
|
|
|
}
|
|
|
|
free(name);
|
2014-11-27 01:58:53 +00:00
|
|
|
if (current->values[n]->type == VT_ARRAY)
|
|
|
|
{
|
|
|
|
return getArray(current->values[n]);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
return dest;
|
|
|
|
}
|
2014-10-25 04:57:44 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
} while ((current = current->parent));
|
|
|
|
|
|
|
|
{
|
|
|
|
char *name = resolveIdentifierName(target, src);
|
|
|
|
error(IN_VARIABLE_DOES_NOT_EXIST, target->fname, target->line, name);
|
|
|
|
free(name);
|
|
|
|
}
|
|
|
|
|
|
|
|
getScopeObjectLocalCallerAbort: /* In case something goes wrong... */
|
|
|
|
|
|
|
|
/* Clean up any allocated structures */
|
|
|
|
if (name) free(name);
|
|
|
|
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2011-06-15 06:54:12 +00:00
|
|
|
/**
|
|
|
|
* Gets a value from a scope without accessing its ancestors.
|
|
|
|
*
|
|
|
|
* \param [in] src The scope to evaluate \a target under.
|
|
|
|
*
|
|
|
|
* \param [in,out] dest The scope to update the value in.
|
|
|
|
*
|
|
|
|
* \param [in] target The name of the value to get.
|
|
|
|
*
|
|
|
|
* \return The value in \a dest, named by evaluating \a target under \a src,
|
|
|
|
* without accessing any ancestors of \a dest.
|
|
|
|
*
|
|
|
|
* \retval NULL Either \a target could not be evaluated in \a src or \a target
|
|
|
|
* could not be found in \a dest.
|
|
|
|
*/
|
|
|
|
ValueObject *getScopeValueLocal(ScopeObject *src,
|
|
|
|
ScopeObject *dest,
|
|
|
|
IdentifierNode *target)
|
2010-08-09 07:01:59 +00:00
|
|
|
{
|
|
|
|
unsigned int n;
|
2010-12-20 23:30:37 +00:00
|
|
|
char *name = NULL;
|
2011-07-20 05:44:42 +00:00
|
|
|
ScopeObject *scope = NULL;
|
2010-12-21 08:20:31 +00:00
|
|
|
|
2011-06-15 06:54:12 +00:00
|
|
|
/* Access any slots */
|
|
|
|
while (target->slot) {
|
2011-07-19 07:02:43 +00:00
|
|
|
/*
|
|
|
|
* Look up the target in the dest scope, using the src scope
|
|
|
|
* for resolving variables in indirect identifiers
|
|
|
|
*/
|
2011-07-20 05:44:42 +00:00
|
|
|
scope = getScopeObjectLocal(src, dest, target);
|
2011-07-19 07:02:43 +00:00
|
|
|
if (!scope) return 0;
|
|
|
|
dest = scope;
|
|
|
|
|
2011-06-15 06:54:12 +00:00
|
|
|
target = target->slot;
|
|
|
|
}
|
|
|
|
|
2010-12-21 08:20:31 +00:00
|
|
|
/* Look up the identifier name */
|
2011-06-15 06:54:12 +00:00
|
|
|
name = resolveIdentifierName(target, src);
|
|
|
|
if (!name) goto getScopeValueLocalAbort;
|
2010-12-21 08:20:31 +00:00
|
|
|
|
2010-08-09 07:01:59 +00:00
|
|
|
/* Check for value in current scope */
|
2014-10-27 03:16:26 +00:00
|
|
|
if (dest->numvals > 0) {
|
|
|
|
n = binarySearchIndex((const char **)dest->names, 0, dest->numvals - 1, (const char *)name);
|
|
|
|
|
|
|
|
if (n < dest->numvals) {
|
|
|
|
if (!strcmp(dest->names[n], name)) {
|
|
|
|
free(name);
|
|
|
|
return dest->values[n];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
/*
|
2011-06-15 06:54:12 +00:00
|
|
|
for (n = 0; n < dest->numvals; n++) {
|
|
|
|
if (!strcmp(dest->names[n], name)) {
|
2010-12-20 23:30:37 +00:00
|
|
|
free(name);
|
2011-06-15 06:54:12 +00:00
|
|
|
return dest->values[n];
|
2010-12-20 23:30:37 +00:00
|
|
|
}
|
2010-08-09 07:01:59 +00:00
|
|
|
}
|
2014-10-27 03:16:26 +00:00
|
|
|
*/
|
2011-06-15 06:54:12 +00:00
|
|
|
|
|
|
|
getScopeValueLocalAbort: /* In case something goes wrong... */
|
|
|
|
|
|
|
|
/* Clean up any allocated structures */
|
|
|
|
if (name) free(name);
|
|
|
|
|
2010-08-09 07:01:59 +00:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2011-06-15 06:54:12 +00:00
|
|
|
/**
|
|
|
|
* Gets a scope from within another scope.
|
|
|
|
*
|
|
|
|
* \param [in] src The scope to evaluate \a target under.
|
|
|
|
*
|
|
|
|
* \param [in,out] dest The scope to update the value in.
|
|
|
|
*
|
|
|
|
* \param [in] target The name of the scope to get.
|
|
|
|
*
|
|
|
|
* \return The value in \a dest, named by evaluating \a target under \a src,
|
|
|
|
* without accessing any ancestors of \a dest.
|
|
|
|
*
|
|
|
|
* \retval NULL Either \a target could not be evaluated in \a src or \a target
|
|
|
|
* could not be found in \a dest.
|
|
|
|
*/
|
|
|
|
ScopeObject *getScopeObject(ScopeObject *src,
|
|
|
|
ScopeObject *dest,
|
|
|
|
IdentifierNode *target)
|
|
|
|
{
|
|
|
|
ValueObject *val = NULL;
|
2010-12-20 23:30:37 +00:00
|
|
|
char *name = NULL;
|
2011-06-15 06:54:12 +00:00
|
|
|
int status;
|
2011-07-19 07:02:43 +00:00
|
|
|
int isI;
|
2011-07-20 05:44:42 +00:00
|
|
|
int isME;
|
|
|
|
ScopeObject *scope;
|
2011-06-15 06:54:12 +00:00
|
|
|
|
2011-07-20 05:44:42 +00:00
|
|
|
/* Look up the identifier name */
|
|
|
|
name = resolveIdentifierName(target, src);
|
|
|
|
if (!name) goto getScopeObjectAbort;
|
|
|
|
|
|
|
|
/* Check for targets with special meanings */
|
|
|
|
isI = strcmp(name, "I");
|
|
|
|
isME = strcmp(name, "ME");
|
|
|
|
free(name);
|
|
|
|
name = NULL;
|
|
|
|
|
|
|
|
if (!isI) {
|
|
|
|
/* The function scope variable */
|
|
|
|
return src;
|
|
|
|
}
|
|
|
|
else if (!isME) {
|
|
|
|
/* The calling object scope variable */
|
|
|
|
scope = getScopeObjectLocal(src, dest, target);
|
|
|
|
if (!scope) goto getScopeObjectAbort;
|
|
|
|
return scope;
|
|
|
|
}
|
|
|
|
|
2011-06-15 06:54:12 +00:00
|
|
|
/* Access any slots */
|
|
|
|
while (target->slot) {
|
2011-07-19 07:02:43 +00:00
|
|
|
/*
|
|
|
|
* Look up the target in the dest scope, using the src scope
|
|
|
|
* for resolving variables in indirect identifiers
|
|
|
|
*/
|
2011-07-20 05:44:42 +00:00
|
|
|
scope = getScopeObjectLocal(src, dest, target);
|
|
|
|
if (!scope) goto getScopeObjectAbort;
|
2011-07-19 07:02:43 +00:00
|
|
|
dest = scope;
|
|
|
|
|
2011-06-15 06:54:12 +00:00
|
|
|
target = target->slot;
|
2010-08-09 07:01:59 +00:00
|
|
|
}
|
2010-12-21 08:20:31 +00:00
|
|
|
|
2011-06-15 06:54:12 +00:00
|
|
|
val = getScopeValue(src, dest, target);
|
|
|
|
if (!val) goto getScopeObjectAbort;
|
2011-07-02 05:38:12 +00:00
|
|
|
if (val->type != VT_ARRAY) {
|
2012-12-13 04:53:54 +00:00
|
|
|
char *name = resolveIdentifierName(target, src);
|
|
|
|
error(IN_VARIABLE_NOT_AN_ARRAY, target->fname, target->line, name);
|
|
|
|
free(name);
|
2011-07-02 05:38:12 +00:00
|
|
|
goto getScopeObjectAbort;
|
|
|
|
}
|
2011-06-15 06:54:12 +00:00
|
|
|
|
|
|
|
return getArray(val);
|
|
|
|
|
|
|
|
getScopeObjectAbort: /* In case something goes wrong... */
|
|
|
|
|
|
|
|
/* Clean up any allocated structures */
|
|
|
|
if (name) free(name);
|
|
|
|
|
2010-08-09 07:01:59 +00:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2011-06-15 06:54:12 +00:00
|
|
|
/**
|
|
|
|
* Deletes a value from a scope.
|
|
|
|
*
|
|
|
|
* \param [in] src The scope to evaluate \a target under.
|
|
|
|
*
|
|
|
|
* \param [in,out] dest The scope to update the value in.
|
|
|
|
*
|
|
|
|
* \param [in] target The name of the value to delete.
|
|
|
|
*/
|
|
|
|
void deleteScopeValue(ScopeObject *src,
|
|
|
|
ScopeObject *dest,
|
|
|
|
IdentifierNode *target)
|
|
|
|
{
|
|
|
|
ScopeObject *current = NULL;
|
2010-12-20 23:30:37 +00:00
|
|
|
char *name = NULL;
|
2011-06-15 06:54:12 +00:00
|
|
|
void *mem1 = NULL;
|
|
|
|
void *mem2 = NULL;
|
2011-07-20 05:44:42 +00:00
|
|
|
ScopeObject *scope = NULL;
|
2011-06-15 06:54:12 +00:00
|
|
|
|
|
|
|
/* Access any slots */
|
|
|
|
while (target->slot) {
|
2011-07-19 07:02:43 +00:00
|
|
|
/*
|
|
|
|
* Look up the target in the dest scope, using the src scope
|
|
|
|
* for resolving variables in indirect identifiers
|
|
|
|
*/
|
2011-07-20 05:44:42 +00:00
|
|
|
scope = getScopeObjectLocal(src, dest, target);
|
|
|
|
if (!scope) goto deleteScopeValueAbort;
|
2011-07-19 07:02:43 +00:00
|
|
|
dest = scope;
|
2011-06-15 06:54:12 +00:00
|
|
|
target = target->slot;
|
|
|
|
}
|
|
|
|
current = dest;
|
2010-12-21 08:20:31 +00:00
|
|
|
|
|
|
|
/* Look up the identifier name */
|
2011-06-15 06:54:12 +00:00
|
|
|
name = resolveIdentifierName(target, src);
|
2011-07-20 05:44:42 +00:00
|
|
|
if (!name) goto deleteScopeValueAbort;
|
2010-12-21 08:20:31 +00:00
|
|
|
|
2010-09-02 08:37:20 +00:00
|
|
|
/* Traverse upwards through scopes */
|
|
|
|
do {
|
2014-10-27 03:16:26 +00:00
|
|
|
if (current->numvals == 0) continue;
|
|
|
|
|
|
|
|
unsigned int n = binarySearchIndex((const char **)current->names, 0, current->numvals - 1, (const char *)name);
|
|
|
|
|
|
|
|
if (n >= current->numvals) continue;
|
|
|
|
|
|
|
|
if (!strcmp(current->names[n], name)) {
|
|
|
|
unsigned int i;
|
|
|
|
unsigned int newnumvals = dest->numvals - 1;
|
|
|
|
free(name);
|
|
|
|
/* Wipe out the name and value */
|
|
|
|
free(current->names[n]);
|
|
|
|
deleteValueObject(current->values[n]);
|
|
|
|
/* Reorder the tables */
|
|
|
|
for (i = n; i < current->numvals - 1; i++) {
|
|
|
|
current->names[i] = current->names[i + 1];
|
|
|
|
current->values[i] = current->values[i + 1];
|
|
|
|
}
|
|
|
|
/* Resize the tables */
|
|
|
|
mem1 = realloc(dest->names, sizeof(IdentifierNode *) * newnumvals);
|
|
|
|
if (!mem1) {
|
|
|
|
perror("realloc");
|
|
|
|
goto deleteScopeValueAbort;
|
|
|
|
}
|
|
|
|
mem2 = realloc(dest->values, sizeof(ValueObject *) * newnumvals);
|
|
|
|
if (!mem2) {
|
|
|
|
perror("realloc");
|
|
|
|
goto deleteScopeValueAbort;
|
2010-09-02 08:37:20 +00:00
|
|
|
}
|
2014-10-27 03:16:26 +00:00
|
|
|
dest->names = mem1;
|
|
|
|
dest->values = mem2;
|
|
|
|
dest->numvals = newnumvals;
|
|
|
|
return;
|
2010-09-02 08:37:20 +00:00
|
|
|
}
|
|
|
|
} while ((current = current->parent));
|
2011-06-15 06:54:12 +00:00
|
|
|
|
2010-12-20 23:30:37 +00:00
|
|
|
free(name);
|
2010-09-02 08:37:20 +00:00
|
|
|
|
2011-06-15 06:54:12 +00:00
|
|
|
return;
|
|
|
|
|
2011-07-20 05:44:42 +00:00
|
|
|
deleteScopeValueAbort: /* In case something goes wrong... */
|
2011-06-15 06:54:12 +00:00
|
|
|
|
|
|
|
/* Clean up any allocated structures */
|
|
|
|
if (name) free(name);
|
|
|
|
if (mem1) free(mem1);
|
|
|
|
if (mem2) free(mem2);
|
2011-07-20 05:44:42 +00:00
|
|
|
if (scope) free(scope);
|
2011-06-15 06:54:12 +00:00
|
|
|
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Creates a returned value.
|
|
|
|
*
|
|
|
|
* \param [in] type The type of returned value.
|
|
|
|
*
|
|
|
|
* \param [in] value An optional value to return.
|
|
|
|
*
|
|
|
|
* \return A pointer to a returned value with the desired properties.
|
|
|
|
*
|
|
|
|
* \retval NULL Memory allocation failed.
|
|
|
|
*/
|
|
|
|
ReturnObject *createReturnObject(ReturnType type,
|
|
|
|
ValueObject *value)
|
2010-08-09 07:01:59 +00:00
|
|
|
{
|
2011-06-15 06:54:12 +00:00
|
|
|
ReturnObject *p = malloc(sizeof(ReturnObject));
|
|
|
|
if (!p) {
|
|
|
|
perror("malloc");
|
|
|
|
return NULL;
|
2010-08-09 07:01:59 +00:00
|
|
|
}
|
2011-06-15 06:54:12 +00:00
|
|
|
p->type = type;
|
|
|
|
p->value = value;
|
|
|
|
return p;
|
2010-08-09 07:01:59 +00:00
|
|
|
}
|
|
|
|
|
2011-06-15 06:54:12 +00:00
|
|
|
/**
|
|
|
|
* Deletes a returned value.
|
|
|
|
*
|
|
|
|
* \param [in,out] object The returned value to be deleted.
|
|
|
|
*
|
|
|
|
* \post The memory at \a object and all of its members will be freed.
|
|
|
|
*/
|
|
|
|
void deleteReturnObject(ReturnObject *object)
|
2010-08-09 07:01:59 +00:00
|
|
|
{
|
2011-06-15 06:54:12 +00:00
|
|
|
if (!object) return;
|
|
|
|
if (object->type == RT_RETURN)
|
|
|
|
deleteValueObject(object->value);
|
|
|
|
free(object);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Starting from an initial parent scope and target identifier, traverses down
|
|
|
|
* until the target identifier is not a scope. Stores the value of the terminal
|
|
|
|
* child identifier and its parent scope.
|
|
|
|
*
|
|
|
|
* \param [in] src The scope to resolve \a target in.
|
|
|
|
*
|
|
|
|
* \param [in] dest The scope to retrieve \a target from.
|
|
|
|
*
|
|
|
|
* \param [in] target The array slot to traverse.
|
|
|
|
*
|
|
|
|
* \param [out] parent The parent of the terminal child identifier of \a target.
|
|
|
|
*
|
|
|
|
* \param [out] child The terminal child identifier of \a target.
|
|
|
|
*
|
|
|
|
* \return A status code indicating success or failure.
|
|
|
|
*
|
|
|
|
* \retval 0 Failed to traverse array.
|
|
|
|
*
|
|
|
|
* \retval 1 Succeeded to traverse array.
|
|
|
|
*
|
|
|
|
* \post \a parent will point to the parent scope containing the terminal child.
|
|
|
|
*
|
|
|
|
* \post \a child will point to the terminal child.
|
|
|
|
*/
|
|
|
|
int resolveTerminalSlot(ScopeObject *src,
|
|
|
|
ScopeObject *dest,
|
|
|
|
IdentifierNode *target,
|
|
|
|
ScopeObject **parent,
|
|
|
|
IdentifierNode **child)
|
|
|
|
{
|
2011-07-20 05:44:42 +00:00
|
|
|
ScopeObject *scope = NULL;
|
|
|
|
|
2011-06-15 06:54:12 +00:00
|
|
|
/* Start with default values */
|
|
|
|
*parent = dest;
|
|
|
|
*child = target;
|
|
|
|
|
|
|
|
/* Access any slots */
|
|
|
|
while (target->slot) {
|
2011-07-19 07:02:43 +00:00
|
|
|
/*
|
|
|
|
* Look up the target in the dest scope, using the src scope
|
|
|
|
* for resolving variables in indirect identifiers
|
|
|
|
*/
|
2011-07-20 05:44:42 +00:00
|
|
|
scope = getScopeObjectLocal(src, dest, target);
|
|
|
|
if (!scope) goto resolveTerminalSlotAbort;
|
2011-07-19 07:02:43 +00:00
|
|
|
dest = scope;
|
2011-06-15 06:54:12 +00:00
|
|
|
|
|
|
|
/* Change the target to the old target's slot */
|
|
|
|
target = target->slot;
|
2010-08-09 07:01:59 +00:00
|
|
|
}
|
2011-06-15 06:54:12 +00:00
|
|
|
|
|
|
|
/* Store the output values */
|
|
|
|
*parent = dest;
|
|
|
|
*child = target;
|
|
|
|
|
2010-08-09 07:01:59 +00:00
|
|
|
return 1;
|
2011-07-20 05:44:42 +00:00
|
|
|
|
|
|
|
resolveTerminalSlotAbort: /* In case something goes wrong... */
|
|
|
|
|
|
|
|
/* Clean up any allocated structures */
|
|
|
|
if (scope) deleteScopeObject(scope);
|
|
|
|
|
|
|
|
return 0;
|
2010-08-09 07:01:59 +00:00
|
|
|
}
|
|
|
|
|
2011-06-15 06:54:12 +00:00
|
|
|
/**
|
|
|
|
* Casts the contents of a value to boolean type in an implicit way. Casting is
|
|
|
|
* not done directly to \a node, instead, it is performed on a copy which is
|
|
|
|
* what is returned.
|
|
|
|
*
|
|
|
|
* \param [in] node The value to cast.
|
|
|
|
*
|
|
|
|
* \param [in] scope The scope to use for variable interpolation.
|
|
|
|
*
|
|
|
|
* \return A pointer to a value with a copy of the contents of \a node, cast to
|
|
|
|
* boolean type.
|
|
|
|
*
|
|
|
|
* \retval NULL An error occurred while casting.
|
|
|
|
*/
|
|
|
|
ValueObject *castBooleanImplicit(ValueObject *node,
|
|
|
|
ScopeObject *scope)
|
2010-08-09 07:01:59 +00:00
|
|
|
{
|
|
|
|
if (!node) return NULL;
|
|
|
|
return castBooleanExplicit(node, scope);
|
|
|
|
}
|
|
|
|
|
2011-06-15 06:54:12 +00:00
|
|
|
/**
|
|
|
|
* Casts the contents of a value to integer type in an implicit way. Casting is
|
|
|
|
* not done directly to \a node, instead, it is performed on a copy which is
|
|
|
|
* what is returned.
|
|
|
|
*
|
|
|
|
* \param [in] node The value to cast.
|
|
|
|
*
|
|
|
|
* \param [in] scope The scope to use for variable interpolation.
|
|
|
|
*
|
|
|
|
* \return A pointer to a value with a copy of the contents of \a node, cast to
|
|
|
|
* integer type.
|
|
|
|
*
|
|
|
|
* \retval NULL An error occurred while casting.
|
|
|
|
*/
|
|
|
|
ValueObject *castIntegerImplicit(ValueObject *node,
|
|
|
|
ScopeObject *scope)
|
2010-08-09 07:01:59 +00:00
|
|
|
{
|
|
|
|
if (!node) return NULL;
|
|
|
|
if (node->type == VT_NIL) {
|
2012-12-13 04:53:54 +00:00
|
|
|
error(IN_CANNOT_IMPLICITLY_CAST_NIL);
|
2010-08-09 07:01:59 +00:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
else return castIntegerExplicit(node, scope);
|
|
|
|
}
|
|
|
|
|
2011-06-15 06:54:12 +00:00
|
|
|
/**
|
|
|
|
* Casts the contents of a value to decimal type in an implicit way. Casting is
|
|
|
|
* not done directly to \a node, instead, it is performed on a copy which is
|
|
|
|
* what is returned.
|
|
|
|
*
|
|
|
|
* \param [in] node The value to cast.
|
|
|
|
*
|
|
|
|
* \param [in] scope The scope to use for variable interpolation.
|
|
|
|
*
|
|
|
|
* \return A pointer to a value with a copy of the contents of \a node, cast to
|
|
|
|
* decimal type.
|
|
|
|
*
|
|
|
|
* \retval NULL An error occurred while casting.
|
|
|
|
*/
|
|
|
|
ValueObject *castFloatImplicit(ValueObject *node,
|
|
|
|
ScopeObject *scope)
|
2010-08-09 07:01:59 +00:00
|
|
|
{
|
|
|
|
if (!node) return NULL;
|
|
|
|
if (node->type == VT_NIL) {
|
2012-12-13 04:53:54 +00:00
|
|
|
error(IN_CANNOT_IMPLICITLY_CAST_NIL);
|
2010-08-09 07:01:59 +00:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
else return castFloatExplicit(node, scope);
|
|
|
|
}
|
|
|
|
|
2011-06-15 06:54:12 +00:00
|
|
|
/**
|
|
|
|
* Casts the contents of a value to string type in an implicit way. Casting is
|
|
|
|
* not done directly to \a node, instead, it is performed on a copy which is
|
|
|
|
* what is returned.
|
|
|
|
*
|
|
|
|
* \param [in] node The value to cast.
|
|
|
|
*
|
|
|
|
* \param [in] scope The scope to use for variable interpolation.
|
|
|
|
*
|
|
|
|
* \note \a scope is used to resolve variable interpolation within the string
|
|
|
|
* before casting it. Therefore, a simple way to interpolate the variables
|
|
|
|
* within a string is to call this function with it.
|
|
|
|
*
|
|
|
|
* \return A pointer to a value with a copy of the contents of \a node, cast to
|
|
|
|
* string type.
|
|
|
|
*
|
|
|
|
* \retval NULL An error occurred while casting.
|
|
|
|
*/
|
|
|
|
ValueObject *castStringImplicit(ValueObject *node,
|
|
|
|
ScopeObject *scope)
|
2010-08-09 07:01:59 +00:00
|
|
|
{
|
|
|
|
if (!node) return NULL;
|
|
|
|
if (node->type == VT_NIL) {
|
2012-12-13 04:53:54 +00:00
|
|
|
error(IN_CANNOT_IMPLICITLY_CAST_NIL);
|
2010-08-09 07:01:59 +00:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
else return castStringExplicit(node, scope);
|
|
|
|
}
|
|
|
|
|
2011-06-15 06:54:12 +00:00
|
|
|
/**
|
|
|
|
* Casts the contents of a value to boolean type in an explicit way. Casting is
|
|
|
|
* not done directly to \a node, instead, it is performed on a copy which is
|
|
|
|
* what is returned.
|
|
|
|
*
|
|
|
|
* \param [in] node The value to cast.
|
|
|
|
*
|
|
|
|
* \param [in] scope The scope to use for variable interpolation.
|
|
|
|
*
|
|
|
|
* \return A pointer to a value with a copy of the contents of \a node, cast to
|
|
|
|
* boolean type.
|
|
|
|
*
|
|
|
|
* \retval NULL An error occurred while casting.
|
|
|
|
*/
|
|
|
|
ValueObject *castBooleanExplicit(ValueObject *node,
|
|
|
|
ScopeObject *scope)
|
2010-08-09 07:01:59 +00:00
|
|
|
{
|
|
|
|
if (!node) return NULL;
|
|
|
|
switch (node->type) {
|
|
|
|
case VT_NIL:
|
|
|
|
return createBooleanValueObject(0);
|
|
|
|
case VT_BOOLEAN:
|
|
|
|
return createBooleanValueObject(getInteger(node));
|
|
|
|
case VT_INTEGER:
|
|
|
|
return createBooleanValueObject(getInteger(node) != 0);
|
|
|
|
case VT_FLOAT:
|
2010-12-21 08:20:31 +00:00
|
|
|
return createBooleanValueObject(fabs(getFloat(node) - 0.0) > FLT_EPSILON);
|
2010-08-09 07:01:59 +00:00
|
|
|
case VT_STRING:
|
|
|
|
if (strstr(getString(node), ":{")) {
|
|
|
|
/* Perform interpolation */
|
|
|
|
ValueObject *ret = NULL;
|
|
|
|
ValueObject *interp = castStringExplicit(node, scope);
|
|
|
|
if (!interp) return NULL;
|
|
|
|
ret = createBooleanValueObject(getString(interp)[0] != '\0');
|
|
|
|
deleteValueObject(interp);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
return createBooleanValueObject(getString(node)[0] != '\0');
|
2010-12-21 08:20:31 +00:00
|
|
|
case VT_FUNC:
|
2012-12-13 04:53:54 +00:00
|
|
|
error(IN_CANNOT_CAST_FUNCTION_TO_BOOLEAN);
|
2010-12-21 08:20:31 +00:00
|
|
|
return NULL;
|
2011-06-15 06:54:12 +00:00
|
|
|
case VT_ARRAY:
|
2012-12-13 04:53:54 +00:00
|
|
|
error(IN_CANNOT_CAST_ARRAY_TO_BOOLEAN);
|
2011-06-15 06:54:12 +00:00
|
|
|
return NULL;
|
2010-12-21 08:20:31 +00:00
|
|
|
default:
|
2012-12-13 04:53:54 +00:00
|
|
|
error(IN_UNKNOWN_VALUE_DURING_BOOLEAN_CAST);
|
2010-12-21 08:20:31 +00:00
|
|
|
return NULL;
|
2010-08-09 07:01:59 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2011-06-15 06:54:12 +00:00
|
|
|
/**
|
|
|
|
* Casts the contents of a value to integer type in an explicit way. Casting is
|
|
|
|
* not done directly to \a node, instead, it is performed on a copy which is
|
|
|
|
* what is returned.
|
|
|
|
*
|
|
|
|
* \param [in] node The value to cast.
|
|
|
|
*
|
|
|
|
* \param [in] scope The scope to use for variable interpolation.
|
|
|
|
*
|
|
|
|
* \return A pointer to a value with a copy of the contents of \a node, cast to
|
|
|
|
* integer type.
|
|
|
|
*
|
|
|
|
* \retval NULL An error occurred while casting.
|
|
|
|
*/
|
|
|
|
ValueObject *castIntegerExplicit(ValueObject *node,
|
|
|
|
ScopeObject *scope)
|
2010-08-09 07:01:59 +00:00
|
|
|
{
|
|
|
|
if (!node) return NULL;
|
|
|
|
switch (node->type) {
|
|
|
|
case VT_NIL:
|
|
|
|
return createIntegerValueObject(0);
|
|
|
|
case VT_BOOLEAN:
|
|
|
|
case VT_INTEGER:
|
|
|
|
return createIntegerValueObject(getInteger(node));
|
|
|
|
case VT_FLOAT:
|
2013-10-04 16:17:19 +00:00
|
|
|
return createIntegerValueObject((long long)getFloat(node));
|
2010-08-09 07:01:59 +00:00
|
|
|
case VT_STRING:
|
|
|
|
if (strstr(getString(node), ":{")) {
|
|
|
|
/* Perform interpolation */
|
|
|
|
ValueObject *ret = NULL;
|
|
|
|
ValueObject *interp = castStringExplicit(node, scope);
|
|
|
|
if (!interp) return NULL;
|
relax numeric conversions from string
The spec does not establish any particular constraints to observe in
casting from `YARN` to `NUMB(A)R`. Presently, `lci` checks that the
string consists only of numeric characters prior to attempting the
conversion, halting with an error if it's found not to be the case.
This behavior is often more inconvenient than helpful. Many numeric
strings encountered in the wild are "roughly numeric", and it would be
wise to account for this observation. As a simple example, a user may
be prompted to input a number; in the case of their inadvertently
providing leading or trailing whitespace, a naive program will crash
rather than gracefully extracting the otherwise sensible input.
This patch removes the `isDecString()` function and instead leverages
the `strtoll()` and `strtof()` functions to handle casts from `YARN`
to `NUMBR` and `NUMBAR`, respectively. Thus, strings to be converted
are permitted to contain leading whitespace, and trailing non-numeric
characters are ignored. Additionally, `YARN`s to be cast to `NUMBR`
may lead with `"0"` or `"0x"` to indicate that the string should be
interpreted as an octal or hexadecimal value, respectively.
This change required the modification of several tests which previously
checked that casting an empty or completely non-numeric `YARN` resulted
in an error. These now verify that such a conversion results in a zero
of the appropriate type.
2015-08-15 23:43:05 +00:00
|
|
|
long long value = strtoll(getString(interp), NULL, 0);
|
2010-08-09 07:01:59 +00:00
|
|
|
ret = createIntegerValueObject(value);
|
|
|
|
deleteValueObject(interp);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
else {
|
relax numeric conversions from string
The spec does not establish any particular constraints to observe in
casting from `YARN` to `NUMB(A)R`. Presently, `lci` checks that the
string consists only of numeric characters prior to attempting the
conversion, halting with an error if it's found not to be the case.
This behavior is often more inconvenient than helpful. Many numeric
strings encountered in the wild are "roughly numeric", and it would be
wise to account for this observation. As a simple example, a user may
be prompted to input a number; in the case of their inadvertently
providing leading or trailing whitespace, a naive program will crash
rather than gracefully extracting the otherwise sensible input.
This patch removes the `isDecString()` function and instead leverages
the `strtoll()` and `strtof()` functions to handle casts from `YARN`
to `NUMBR` and `NUMBAR`, respectively. Thus, strings to be converted
are permitted to contain leading whitespace, and trailing non-numeric
characters are ignored. Additionally, `YARN`s to be cast to `NUMBR`
may lead with `"0"` or `"0x"` to indicate that the string should be
interpreted as an octal or hexadecimal value, respectively.
This change required the modification of several tests which previously
checked that casting an empty or completely non-numeric `YARN` resulted
in an error. These now verify that such a conversion results in a zero
of the appropriate type.
2015-08-15 23:43:05 +00:00
|
|
|
long long value = strtoll(getString(node), NULL, 0);
|
2010-08-09 07:01:59 +00:00
|
|
|
return createIntegerValueObject(value);
|
|
|
|
}
|
2010-12-21 08:20:31 +00:00
|
|
|
case VT_FUNC:
|
2012-12-13 04:53:54 +00:00
|
|
|
error(IN_CANNOT_CAST_FUNCTION_TO_INTEGER);
|
2010-12-21 08:20:31 +00:00
|
|
|
return NULL;
|
2011-06-15 06:54:12 +00:00
|
|
|
case VT_ARRAY:
|
2012-12-13 04:53:54 +00:00
|
|
|
error(IN_CANNOT_CAST_ARRAY_TO_INTEGER);
|
2011-06-15 06:54:12 +00:00
|
|
|
return NULL;
|
2010-12-21 08:20:31 +00:00
|
|
|
default:
|
2012-12-13 04:53:54 +00:00
|
|
|
error(IN_UNKNOWN_VALUE_DURING_INTEGER_CAST);
|
2010-12-21 08:20:31 +00:00
|
|
|
return NULL;
|
2010-08-09 07:01:59 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2011-06-15 06:54:12 +00:00
|
|
|
/**
|
|
|
|
* Casts the contents of a value to decimal type in an explicit way. Casting is
|
|
|
|
* not done directly to \a node, instead, it is performed on a copy which is
|
|
|
|
* what is returned.
|
|
|
|
*
|
|
|
|
* \param [in] node The value to cast.
|
|
|
|
*
|
|
|
|
* \param [in] scope The scope to use for variable interpolation.
|
|
|
|
*
|
|
|
|
* \return A pointer to a value with a copy of the contents of \a node, cast to
|
|
|
|
* decimal type.
|
|
|
|
*
|
|
|
|
* \retval NULL An error occurred while casting.
|
|
|
|
*/
|
|
|
|
ValueObject *castFloatExplicit(ValueObject *node,
|
|
|
|
ScopeObject *scope)
|
2010-08-09 07:01:59 +00:00
|
|
|
{
|
|
|
|
if (!node) return NULL;
|
|
|
|
switch (node->type) {
|
|
|
|
case VT_NIL:
|
|
|
|
return createFloatValueObject(0.0);
|
|
|
|
case VT_BOOLEAN:
|
|
|
|
case VT_INTEGER:
|
2010-12-21 08:20:31 +00:00
|
|
|
return createFloatValueObject((float)getInteger(node));
|
2010-08-09 07:01:59 +00:00
|
|
|
case VT_FLOAT:
|
|
|
|
return createFloatValueObject(getFloat(node));
|
|
|
|
case VT_STRING:
|
|
|
|
if (strstr(getString(node), ":{")) {
|
|
|
|
/* Perform interpolation */
|
|
|
|
ValueObject *ret = NULL;
|
|
|
|
ValueObject *interp = castStringExplicit(node, scope);
|
|
|
|
if (!interp) return NULL;
|
relax numeric conversions from string
The spec does not establish any particular constraints to observe in
casting from `YARN` to `NUMB(A)R`. Presently, `lci` checks that the
string consists only of numeric characters prior to attempting the
conversion, halting with an error if it's found not to be the case.
This behavior is often more inconvenient than helpful. Many numeric
strings encountered in the wild are "roughly numeric", and it would be
wise to account for this observation. As a simple example, a user may
be prompted to input a number; in the case of their inadvertently
providing leading or trailing whitespace, a naive program will crash
rather than gracefully extracting the otherwise sensible input.
This patch removes the `isDecString()` function and instead leverages
the `strtoll()` and `strtof()` functions to handle casts from `YARN`
to `NUMBR` and `NUMBAR`, respectively. Thus, strings to be converted
are permitted to contain leading whitespace, and trailing non-numeric
characters are ignored. Additionally, `YARN`s to be cast to `NUMBR`
may lead with `"0"` or `"0x"` to indicate that the string should be
interpreted as an octal or hexadecimal value, respectively.
This change required the modification of several tests which previously
checked that casting an empty or completely non-numeric `YARN` resulted
in an error. These now verify that such a conversion results in a zero
of the appropriate type.
2015-08-15 23:43:05 +00:00
|
|
|
float value = strtof(getString(interp), NULL);
|
2010-08-09 07:01:59 +00:00
|
|
|
ret = createFloatValueObject(value);
|
|
|
|
deleteValueObject(interp);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
else {
|
relax numeric conversions from string
The spec does not establish any particular constraints to observe in
casting from `YARN` to `NUMB(A)R`. Presently, `lci` checks that the
string consists only of numeric characters prior to attempting the
conversion, halting with an error if it's found not to be the case.
This behavior is often more inconvenient than helpful. Many numeric
strings encountered in the wild are "roughly numeric", and it would be
wise to account for this observation. As a simple example, a user may
be prompted to input a number; in the case of their inadvertently
providing leading or trailing whitespace, a naive program will crash
rather than gracefully extracting the otherwise sensible input.
This patch removes the `isDecString()` function and instead leverages
the `strtoll()` and `strtof()` functions to handle casts from `YARN`
to `NUMBR` and `NUMBAR`, respectively. Thus, strings to be converted
are permitted to contain leading whitespace, and trailing non-numeric
characters are ignored. Additionally, `YARN`s to be cast to `NUMBR`
may lead with `"0"` or `"0x"` to indicate that the string should be
interpreted as an octal or hexadecimal value, respectively.
This change required the modification of several tests which previously
checked that casting an empty or completely non-numeric `YARN` resulted
in an error. These now verify that such a conversion results in a zero
of the appropriate type.
2015-08-15 23:43:05 +00:00
|
|
|
float value = strtof(getString(node), NULL);
|
2010-08-09 07:01:59 +00:00
|
|
|
return createFloatValueObject(value);
|
|
|
|
}
|
2010-12-21 08:20:31 +00:00
|
|
|
case VT_FUNC:
|
2012-12-13 04:53:54 +00:00
|
|
|
error(IN_CANNOT_CAST_FUNCTION_TO_DECIMAL);
|
2010-12-21 08:20:31 +00:00
|
|
|
return NULL;
|
2011-06-15 06:54:12 +00:00
|
|
|
case VT_ARRAY:
|
2012-12-13 04:53:54 +00:00
|
|
|
error(IN_CANNOT_CAST_ARRAY_TO_DECIMAL);
|
2011-06-15 06:54:12 +00:00
|
|
|
return NULL;
|
2010-12-21 08:20:31 +00:00
|
|
|
default:
|
2012-12-13 04:53:54 +00:00
|
|
|
error(IN_UNKNOWN_VALUE_DURING_DECIMAL_CAST);
|
2010-12-21 08:20:31 +00:00
|
|
|
return NULL;
|
2010-08-09 07:01:59 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2011-06-15 06:54:12 +00:00
|
|
|
/**
|
|
|
|
* Casts the contents of a value to string type in an explicit way. Casting is
|
|
|
|
* not done directly to \a node, instead, it is performed on a copy which is
|
|
|
|
* what is returned.
|
|
|
|
*
|
|
|
|
* \param [in] node The value to cast.
|
|
|
|
*
|
|
|
|
* \param [in] scope The scope to use for variable interpolation.
|
|
|
|
*
|
|
|
|
* \note \a scope is used to resolve variable interpolation within the string
|
|
|
|
* before casting it. Therefore, a simple way to interpolate the variables
|
|
|
|
* within a string is to call this function with it.
|
|
|
|
*
|
|
|
|
* \return A pointer to a value with a copy of the contents of \a node, cast to
|
|
|
|
* string type.
|
|
|
|
*
|
|
|
|
* \retval NULL An error occurred while casting.
|
|
|
|
*/
|
|
|
|
ValueObject *castStringExplicit(ValueObject *node,
|
|
|
|
ScopeObject *scope)
|
2010-08-09 07:01:59 +00:00
|
|
|
{
|
|
|
|
if (!node) return NULL;
|
|
|
|
switch (node->type) {
|
|
|
|
case VT_NIL: {
|
2011-06-15 06:54:12 +00:00
|
|
|
char *str = copyString("");
|
2010-08-09 07:01:59 +00:00
|
|
|
if (!str) return NULL;
|
|
|
|
return createStringValueObject(str);
|
|
|
|
}
|
|
|
|
case VT_BOOLEAN: {
|
2011-06-15 06:54:12 +00:00
|
|
|
/*
|
|
|
|
* \note The spec does not define how TROOFs may be cast
|
|
|
|
* to YARNs.
|
|
|
|
*/
|
2012-12-13 04:53:54 +00:00
|
|
|
error(IN_CANNOT_CAST_BOOLEAN_TO_STRING);
|
2010-08-09 07:01:59 +00:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
case VT_INTEGER: {
|
|
|
|
char *data = NULL;
|
2011-06-15 06:54:12 +00:00
|
|
|
/*
|
|
|
|
* One character per integer bit plus one more for the
|
|
|
|
* null character
|
|
|
|
*/
|
2013-10-04 16:17:19 +00:00
|
|
|
size_t size = sizeof(long long) * 8 + 1;
|
2010-08-09 07:01:59 +00:00
|
|
|
data = malloc(sizeof(char) * size);
|
|
|
|
if (!data) return NULL;
|
2013-10-04 16:17:19 +00:00
|
|
|
sprintf(data, "%lli", getInteger(node));
|
2010-08-09 07:01:59 +00:00
|
|
|
return createStringValueObject(data);
|
|
|
|
}
|
|
|
|
case VT_FLOAT: {
|
|
|
|
char *data = NULL;
|
|
|
|
unsigned int precision = 2;
|
2011-06-15 06:54:12 +00:00
|
|
|
/*
|
|
|
|
* One character per float bit plus one more for the
|
|
|
|
* null character
|
|
|
|
*/
|
2010-08-09 07:01:59 +00:00
|
|
|
size_t size = sizeof(float) * 8 + 1;
|
|
|
|
data = malloc(sizeof(char) * size);
|
|
|
|
if (!data) return NULL;
|
|
|
|
sprintf(data, "%f", getFloat(node));
|
|
|
|
/* Truncate to a certain number of decimal places */
|
|
|
|
strchr(data, '.')[precision + 1] = '\0';
|
|
|
|
return createStringValueObject(data);
|
|
|
|
}
|
|
|
|
case VT_STRING: {
|
|
|
|
char *temp = NULL;
|
|
|
|
char *data = NULL;
|
|
|
|
char *str = getString(node);
|
2010-12-21 08:20:31 +00:00
|
|
|
unsigned int a, b;
|
|
|
|
size_t size;
|
2010-08-09 07:01:59 +00:00
|
|
|
/* Perform interpolation */
|
|
|
|
size = strlen(getString(node)) + 1;
|
|
|
|
temp = malloc(sizeof(char) * size);
|
|
|
|
for (a = 0, b = 0; str[b] != '\0'; ) {
|
|
|
|
if (!strncmp(str + b, ":)", 2)) {
|
|
|
|
temp[a] = '\n';
|
|
|
|
a++, b += 2;
|
|
|
|
}
|
2014-05-05 15:30:35 +00:00
|
|
|
else if (!strncmp(str + b, ":3", 2)) {
|
|
|
|
temp[a] = '\r';
|
|
|
|
a++, b += 2;
|
|
|
|
}
|
2010-08-09 07:01:59 +00:00
|
|
|
else if (!strncmp(str + b, ":>", 2)) {
|
|
|
|
temp[a] = '\t';
|
|
|
|
a++, b += 2;
|
|
|
|
}
|
|
|
|
else if (!strncmp(str + b, ":o", 2)) {
|
|
|
|
temp[a] = '\a';
|
|
|
|
a++, b += 2;
|
|
|
|
}
|
|
|
|
else if (!strncmp(str + b, ":\"", 2)) {
|
|
|
|
temp[a] = '"';
|
|
|
|
a++, b += 2;
|
|
|
|
}
|
|
|
|
else if (!strncmp(str + b, "::", 2)) {
|
|
|
|
temp[a] = ':';
|
|
|
|
a++, b += 2;
|
|
|
|
}
|
|
|
|
else if (!strncmp(str + b, ":(", 2)) {
|
|
|
|
const char *start = str + b + 2;
|
|
|
|
const char *end = strchr(start, ')');
|
2010-12-21 08:20:31 +00:00
|
|
|
size_t len;
|
|
|
|
char *image = NULL;
|
2010-08-09 07:01:59 +00:00
|
|
|
long codepoint;
|
|
|
|
char out[3];
|
2010-12-21 08:20:31 +00:00
|
|
|
size_t num;
|
2010-08-09 07:01:59 +00:00
|
|
|
void *mem = NULL;
|
2010-12-21 08:20:31 +00:00
|
|
|
if (end < start) {
|
2012-12-13 04:53:54 +00:00
|
|
|
error(IN_EXPECTED_CLOSING_PAREN);
|
2010-12-21 08:20:31 +00:00
|
|
|
free(temp);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
len = (size_t)(end - start);
|
|
|
|
image = malloc(sizeof(char) * (len + 1));
|
2010-08-09 07:01:59 +00:00
|
|
|
strncpy(image, start, len);
|
|
|
|
image[len] = '\0';
|
|
|
|
if (!isHexString(image)) {
|
2012-12-13 04:53:54 +00:00
|
|
|
error(IN_INVALID_HEX_NUMBER);
|
2010-08-09 07:01:59 +00:00
|
|
|
free(temp);
|
|
|
|
free(image);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
codepoint = strtol(image, NULL, 16);
|
|
|
|
free(image);
|
2010-12-21 08:20:31 +00:00
|
|
|
if (codepoint < 0) {
|
2012-12-13 04:53:54 +00:00
|
|
|
error(IN_CODE_POINT_MUST_BE_POSITIVE);
|
2010-12-21 08:20:31 +00:00
|
|
|
free(temp);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
num = convertCodePointToUTF8((unsigned int)codepoint, out);
|
2010-08-09 07:01:59 +00:00
|
|
|
if (num == 0) {
|
|
|
|
free(temp);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
size += num;
|
|
|
|
mem = realloc(temp, size);
|
|
|
|
if (!mem) {
|
|
|
|
perror("realloc");
|
|
|
|
free(temp);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
temp = mem;
|
|
|
|
strncpy(temp + a, out, num);
|
|
|
|
a += num, b += len + 3;
|
|
|
|
}
|
|
|
|
else if (!strncmp(str + b, ":[", 2)) {
|
|
|
|
const char *start = str + b + 2;
|
|
|
|
const char *end = strchr(start, ']');
|
2010-12-21 08:20:31 +00:00
|
|
|
size_t len;
|
|
|
|
char *image = NULL;
|
2010-08-09 07:01:59 +00:00
|
|
|
long codepoint;
|
|
|
|
char out[3];
|
2010-12-21 08:20:31 +00:00
|
|
|
size_t num;
|
2010-08-09 07:01:59 +00:00
|
|
|
void *mem = NULL;
|
2010-12-21 08:20:31 +00:00
|
|
|
if (end < start) {
|
2012-12-13 04:53:54 +00:00
|
|
|
error(IN_EXPECTED_CLOSING_SQUARE_BRACKET);
|
2010-12-21 08:20:31 +00:00
|
|
|
free(temp);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
len = (size_t)(end - start);
|
|
|
|
image = malloc(sizeof(char) * (len + 1));
|
2010-08-09 07:01:59 +00:00
|
|
|
strncpy(image, start, len);
|
|
|
|
strncpy(image, start, len);
|
|
|
|
image[len] = '\0';
|
|
|
|
codepoint = convertNormativeNameToCodePoint(image);
|
|
|
|
free(image);
|
|
|
|
if (codepoint < 0) {
|
2012-12-13 04:53:54 +00:00
|
|
|
error(IN_CODE_POINT_MUST_BE_POSITIVE);
|
2010-08-09 07:01:59 +00:00
|
|
|
free(temp);
|
|
|
|
return NULL;
|
|
|
|
}
|
2010-12-21 08:20:31 +00:00
|
|
|
num = convertCodePointToUTF8((unsigned int)codepoint, out);
|
2010-08-09 07:01:59 +00:00
|
|
|
size += num;
|
|
|
|
mem = realloc(temp, size);
|
|
|
|
if (!mem) {
|
|
|
|
perror("realloc");
|
|
|
|
free(temp);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
temp = mem;
|
|
|
|
strncpy(temp + a, out, num);
|
|
|
|
a += num, b += len + 3;
|
|
|
|
}
|
|
|
|
else if (!strncmp(str + b, ":{", 2)) {
|
|
|
|
IdentifierNode *target = NULL;
|
|
|
|
ValueObject *val = NULL, *use = NULL;
|
|
|
|
/* Copy the variable name into image */
|
|
|
|
const char *start = str + b + 2;
|
|
|
|
const char *end = strchr(start, '}');
|
2010-12-21 08:20:31 +00:00
|
|
|
size_t len;
|
|
|
|
char *image = NULL;
|
2010-08-09 07:01:59 +00:00
|
|
|
void *mem = NULL;
|
2010-12-21 08:20:31 +00:00
|
|
|
if (end < start) {
|
2012-12-13 04:53:54 +00:00
|
|
|
error(IN_EXPECTED_CLOSING_CURLY_BRACE);
|
2010-12-21 08:20:31 +00:00
|
|
|
free(temp);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
len = (size_t)(end - start);
|
|
|
|
image = malloc(sizeof(char) * (len + 1));
|
2010-08-09 07:01:59 +00:00
|
|
|
strncpy(image, start, len);
|
|
|
|
image[len] = '\0';
|
|
|
|
if (!strcmp(image, "IT"))
|
|
|
|
/* Lookup implicit variable */
|
|
|
|
val = scope->impvar;
|
|
|
|
else {
|
2011-06-15 06:54:12 +00:00
|
|
|
/*
|
|
|
|
* Create a new IdentifierNode
|
2012-12-13 04:53:54 +00:00
|
|
|
* structure and look up its
|
2011-06-15 06:54:12 +00:00
|
|
|
* value
|
|
|
|
*/
|
|
|
|
target = createIdentifierNode(IT_DIRECT, image, NULL, NULL, 0);
|
2010-08-09 07:01:59 +00:00
|
|
|
if (!target) {
|
|
|
|
free(temp);
|
|
|
|
return NULL;
|
|
|
|
}
|
2011-06-15 06:54:12 +00:00
|
|
|
val = getScopeValue(scope, scope, target);
|
2010-08-09 07:01:59 +00:00
|
|
|
if (!val) {
|
2012-12-13 04:53:54 +00:00
|
|
|
error(IN_VARIABLE_DOES_NOT_EXIST, target->fname, target->line, image);
|
2010-08-09 07:01:59 +00:00
|
|
|
deleteIdentifierNode(target);
|
|
|
|
free(temp);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
deleteIdentifierNode(target);
|
|
|
|
}
|
|
|
|
/* Cast the variable value to a string */
|
|
|
|
if (!(use = castStringImplicit(val, scope))) {
|
|
|
|
free(temp);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
/* Update the size of the new string */
|
|
|
|
size += strlen(getString(use));
|
|
|
|
mem = realloc(temp, size);
|
|
|
|
if (!mem) {
|
|
|
|
perror("realloc");
|
|
|
|
free(temp);
|
|
|
|
}
|
|
|
|
temp = mem;
|
|
|
|
/* Copy the variable string into the new string */
|
|
|
|
strcpy(temp + a, getString(use));
|
|
|
|
a += strlen(getString(use)), b += len + 3;
|
|
|
|
deleteValueObject(use);
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
temp[a] = str[b];
|
|
|
|
a++, b++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
temp[a] = '\0';
|
|
|
|
data = malloc(sizeof(char) * (strlen(temp) + 1));
|
|
|
|
strcpy(data, temp);
|
|
|
|
free(temp);
|
|
|
|
return createStringValueObject(data);
|
|
|
|
}
|
2010-12-21 08:20:31 +00:00
|
|
|
case VT_FUNC: {
|
2012-12-13 04:53:54 +00:00
|
|
|
error(IN_CANNOT_CAST_FUNCTION_TO_STRING);
|
2010-12-21 08:20:31 +00:00
|
|
|
return NULL;
|
|
|
|
}
|
2011-06-15 06:54:12 +00:00
|
|
|
case VT_ARRAY:
|
2012-12-13 04:53:54 +00:00
|
|
|
error(IN_CANNOT_CAST_ARRAY_TO_STRING);
|
2011-06-15 06:54:12 +00:00
|
|
|
return NULL;
|
2010-12-21 08:20:31 +00:00
|
|
|
default:
|
2012-12-13 04:53:54 +00:00
|
|
|
error(IN_UNKNOWN_VALUE_DURING_STRING_CAST);
|
2010-12-21 08:20:31 +00:00
|
|
|
return NULL;
|
2010-08-09 07:01:59 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2011-06-15 06:54:12 +00:00
|
|
|
/**
|
|
|
|
* Interprets an implicit variable.
|
|
|
|
*
|
|
|
|
* \param [in] node Not used (see note).
|
|
|
|
*
|
|
|
|
* \param [in] scope The scope from which to use the implicit variable.
|
|
|
|
*
|
|
|
|
* \note \a node is not used by this function but is still included in its
|
|
|
|
* prototype to allow this function to be stored in a jump table for fast
|
|
|
|
* execution.
|
|
|
|
*
|
|
|
|
* \return A pointer to the value of \a scope's implicit variable.
|
|
|
|
*/
|
|
|
|
ValueObject *interpretImpVarExprNode(ExprNode *node,
|
|
|
|
ScopeObject *scope)
|
2010-08-09 07:01:59 +00:00
|
|
|
{
|
|
|
|
node = NULL;
|
|
|
|
return scope->impvar;
|
|
|
|
}
|
|
|
|
|
2011-06-15 06:54:12 +00:00
|
|
|
/**
|
|
|
|
* Interprets a cast.
|
|
|
|
*
|
|
|
|
* \param [in] node A pointer to the expression to interpret.
|
|
|
|
*
|
|
|
|
* \param [in] scope A pointer to a scope to evaluate \a node under.
|
|
|
|
*
|
|
|
|
* \pre \a node contains a expression created by createCastExprNode().
|
|
|
|
*
|
|
|
|
* \return A pointer to the cast value.
|
|
|
|
*
|
|
|
|
* \retval NULL An error occurred during interpretation.
|
|
|
|
*/
|
|
|
|
ValueObject *interpretCastExprNode(ExprNode *node,
|
|
|
|
ScopeObject *scope)
|
2010-08-09 07:01:59 +00:00
|
|
|
{
|
|
|
|
CastExprNode *expr = (CastExprNode *)node->expr;
|
|
|
|
ValueObject *val = interpretExprNode(expr->target, scope);
|
|
|
|
ValueObject *ret = NULL;
|
|
|
|
if (!val) return NULL;
|
|
|
|
switch(expr->newtype->type) {
|
|
|
|
case CT_NIL:
|
|
|
|
deleteValueObject(val);
|
|
|
|
return createNilValueObject();
|
|
|
|
case CT_BOOLEAN:
|
|
|
|
ret = castBooleanExplicit(val, scope);
|
|
|
|
deleteValueObject(val);
|
|
|
|
return ret;
|
|
|
|
case CT_INTEGER:
|
|
|
|
ret = castIntegerExplicit(val, scope);
|
|
|
|
deleteValueObject(val);
|
|
|
|
return ret;
|
|
|
|
case CT_FLOAT:
|
|
|
|
ret = castFloatExplicit(val, scope);
|
|
|
|
deleteValueObject(val);
|
|
|
|
return ret;
|
|
|
|
case CT_STRING:
|
|
|
|
ret = castStringExplicit(val, scope);
|
|
|
|
deleteValueObject(val);
|
|
|
|
return ret;
|
|
|
|
default:
|
2012-12-13 04:53:54 +00:00
|
|
|
error(IN_UNKNOWN_CAST_TYPE);
|
2010-08-09 07:01:59 +00:00
|
|
|
deleteValueObject(val);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2011-06-15 06:54:12 +00:00
|
|
|
/**
|
|
|
|
* Interprets a function call.
|
|
|
|
*
|
|
|
|
* \param [in] node A pointer to the expression to interpret.
|
|
|
|
*
|
|
|
|
* \param [in,out] scope A pointer to a scope to evaluate \a node under.
|
|
|
|
*
|
|
|
|
* \pre \a node contains an expression created by createFuncCallExprNode().
|
|
|
|
*
|
|
|
|
* \return A pointer to the returned value.
|
|
|
|
*
|
|
|
|
* \retval NULL An error occurred during interpretation.
|
|
|
|
*/
|
|
|
|
ValueObject *interpretFuncCallExprNode(ExprNode *node,
|
|
|
|
ScopeObject *scope)
|
2010-08-09 07:01:59 +00:00
|
|
|
{
|
|
|
|
FuncCallExprNode *expr = (FuncCallExprNode *)node->expr;
|
|
|
|
unsigned int n;
|
2011-07-19 07:02:43 +00:00
|
|
|
ScopeObject *outer = NULL;
|
2010-11-16 05:25:51 +00:00
|
|
|
ValueObject *def = NULL;
|
2010-08-09 07:01:59 +00:00
|
|
|
ReturnObject *retval = NULL;
|
|
|
|
ValueObject *ret = NULL;
|
2011-06-15 06:54:12 +00:00
|
|
|
ScopeObject *dest = NULL;
|
2011-07-19 07:02:43 +00:00
|
|
|
ScopeObject *target = NULL;
|
|
|
|
|
2013-10-05 19:09:30 +00:00
|
|
|
dest = getScopeObject(scope, scope, expr->scope);
|
|
|
|
|
2014-10-25 04:57:44 +00:00
|
|
|
target = getScopeObjectLocalCaller(scope, dest, expr->name);
|
2011-07-19 07:02:43 +00:00
|
|
|
if (!target) return NULL;
|
|
|
|
|
|
|
|
outer = createScopeObjectCaller(scope, target);
|
2010-08-09 07:01:59 +00:00
|
|
|
if (!outer) return NULL;
|
2011-07-19 07:02:43 +00:00
|
|
|
|
2011-06-15 06:54:12 +00:00
|
|
|
def = getScopeValue(scope, dest, expr->name);
|
2011-07-19 07:02:43 +00:00
|
|
|
|
2010-11-16 05:25:51 +00:00
|
|
|
if (!def || def->type != VT_FUNC) {
|
|
|
|
IdentifierNode *id = (IdentifierNode *)(expr->name);
|
2010-12-20 23:30:37 +00:00
|
|
|
char *name = resolveIdentifierName(id, scope);
|
|
|
|
if (name) {
|
2012-12-13 04:53:54 +00:00
|
|
|
error(IN_UNDEFINED_FUNCTION, id->fname, id->line, name);
|
2010-12-20 23:30:37 +00:00
|
|
|
free(name);
|
|
|
|
}
|
2010-11-16 05:25:51 +00:00
|
|
|
deleteScopeObject(outer);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
/* Check for correct supplied arity */
|
|
|
|
if (getFunction(def)->args->num != expr->args->num) {
|
|
|
|
IdentifierNode *id = (IdentifierNode *)(expr->name);
|
2010-12-20 23:30:37 +00:00
|
|
|
char *name = resolveIdentifierName(id, scope);
|
|
|
|
if (name) {
|
2012-12-13 04:53:54 +00:00
|
|
|
error(IN_INCORRECT_NUMBER_OF_ARGUMENTS, id->fname, id->line, name);
|
2010-12-20 23:30:37 +00:00
|
|
|
free(name);
|
|
|
|
}
|
2010-11-16 05:25:51 +00:00
|
|
|
deleteScopeObject(outer);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
for (n = 0; n < getFunction(def)->args->num; n++) {
|
2010-08-09 07:01:59 +00:00
|
|
|
ValueObject *val = NULL;
|
2011-06-15 06:54:12 +00:00
|
|
|
if (!createScopeValue(scope, outer, getFunction(def)->args->ids[n])) {
|
2010-08-09 07:01:59 +00:00
|
|
|
deleteScopeObject(outer);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
if (!(val = interpretExprNode(expr->args->exprs[n], scope))) {
|
|
|
|
deleteScopeObject(outer);
|
|
|
|
return NULL;
|
|
|
|
}
|
2011-06-15 06:54:12 +00:00
|
|
|
if (!updateScopeValue(scope, outer, getFunction(def)->args->ids[n], val)) {
|
2010-08-09 07:01:59 +00:00
|
|
|
deleteScopeObject(outer);
|
|
|
|
deleteValueObject(val);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
}
|
2011-06-15 06:54:12 +00:00
|
|
|
/**
|
|
|
|
* \note We use interpretStmtNodeList here because we want to have
|
|
|
|
* access to the function's scope as we may need to retrieve the
|
|
|
|
* implicit variable in the case of a default return.
|
|
|
|
*/
|
2010-11-16 05:25:51 +00:00
|
|
|
if (!(retval = interpretStmtNodeList(getFunction(def)->body->stmts, outer))) {
|
2010-08-09 07:01:59 +00:00
|
|
|
deleteScopeObject(outer);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
switch (retval->type) {
|
|
|
|
case RT_DEFAULT:
|
|
|
|
/* Extract return value */
|
|
|
|
ret = outer->impvar;
|
|
|
|
outer->impvar = NULL;
|
|
|
|
break;
|
|
|
|
case RT_BREAK:
|
|
|
|
ret = createNilValueObject();
|
|
|
|
break;
|
|
|
|
case RT_RETURN:
|
|
|
|
/* Extract return value */
|
|
|
|
ret = retval->value;
|
|
|
|
retval->value = NULL;
|
|
|
|
break;
|
|
|
|
default:
|
2012-12-13 04:53:54 +00:00
|
|
|
error(IN_INVALID_RETURN_TYPE);
|
2010-08-09 07:01:59 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
deleteReturnObject(retval);
|
|
|
|
deleteScopeObject(outer);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2014-05-10 03:59:18 +00:00
|
|
|
/**
|
|
|
|
* Interprets a system command
|
|
|
|
*
|
|
|
|
* \param [in] node A pointer to the expression to interpret.
|
|
|
|
*
|
|
|
|
* \param [in,out] scope A pointer to a scope to evaluate \a node under.
|
|
|
|
*
|
|
|
|
* \pre \a node contains an expression created by createSystemCommandExprNode().
|
|
|
|
*
|
|
|
|
* \return A pointer to the returned value.
|
|
|
|
*
|
|
|
|
* \retval NULL An error occurred during interpretation.
|
|
|
|
*/
|
|
|
|
ValueObject *interpretSystemCommandExprNode(ExprNode *node,
|
|
|
|
ScopeObject *scope)
|
|
|
|
{
|
|
|
|
SystemCommandExprNode *expr = (SystemCommandExprNode *)node->expr;
|
|
|
|
ValueObject *val = NULL;
|
|
|
|
FILE *f;
|
|
|
|
char *cmd = NULL;
|
|
|
|
unsigned int buflen = 2048;
|
|
|
|
char buf[buflen];
|
|
|
|
char *out = NULL;
|
|
|
|
unsigned int len;
|
|
|
|
unsigned int total = 0;
|
|
|
|
unsigned int prev = 0;
|
|
|
|
|
|
|
|
/* Sanity checks */
|
|
|
|
if (scope == NULL) return NULL;
|
|
|
|
if (node->type != ET_SYSTEMCOMMAND) return NULL;
|
|
|
|
if (expr == NULL) return NULL;
|
|
|
|
|
|
|
|
val = interpretExprNode(expr->cmd, scope);
|
|
|
|
if (!val) return NULL;
|
|
|
|
|
2015-08-13 19:27:31 +00:00
|
|
|
cmd = getString(castStringExplicit(val, scope));
|
2014-05-10 03:59:18 +00:00
|
|
|
|
|
|
|
|
|
|
|
/* Open the command for reading */
|
|
|
|
f = popen(cmd, "r");
|
|
|
|
if (f == NULL) {
|
|
|
|
error(IN_UNABLE_TO_EXECUTE_COMMAND);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
while ((len = fread(buf, 1, buflen, f)) > 0) {
|
|
|
|
prev = total;
|
|
|
|
total += len;
|
|
|
|
out = realloc(out, total + 1);
|
|
|
|
memcpy(out + prev, buf, len);
|
|
|
|
}
|
|
|
|
if (total > 0) out[total] = '\0';
|
|
|
|
|
|
|
|
/* Close */
|
|
|
|
pclose(f);
|
|
|
|
|
|
|
|
/* Return the command object */
|
|
|
|
return createStringValueObject(out == NULL ? "" : out);
|
|
|
|
}
|
|
|
|
|
2011-06-15 06:54:12 +00:00
|
|
|
/**
|
|
|
|
* Interprets an identifier.
|
|
|
|
*
|
|
|
|
* \param [in] node A pointer to the expression to interpret.
|
|
|
|
*
|
|
|
|
* \param [in,out] scope A pointer to a scope to evaluate \a node under.
|
|
|
|
*
|
|
|
|
* \pre \a node contains an identifier created by createIdentifierNode().
|
|
|
|
*
|
|
|
|
* \return A pointer to the cast value.
|
|
|
|
*
|
|
|
|
* \retval NULL An error occurred during interpretation.
|
|
|
|
*/
|
|
|
|
ValueObject *interpretIdentifierExprNode(ExprNode *node,
|
|
|
|
ScopeObject *scope)
|
|
|
|
{
|
|
|
|
ValueObject *val = getScopeValue(scope, scope, node->expr);
|
|
|
|
if (!val) return NULL;
|
2010-08-09 07:01:59 +00:00
|
|
|
return copyValueObject(val);
|
|
|
|
}
|
|
|
|
|
2011-06-15 06:54:12 +00:00
|
|
|
/**
|
|
|
|
* Interprets a constant.
|
|
|
|
*
|
|
|
|
* \param [in] node A pointer to the expression to interpret.
|
|
|
|
*
|
|
|
|
* \param [in] scope Not used (see note).
|
|
|
|
*
|
|
|
|
* \note \a node is not used by this function but is still included in its
|
|
|
|
* prototype to allow this function to be stored in a jump table for fast
|
|
|
|
* execution.
|
|
|
|
*
|
|
|
|
* \pre \a node contains a constant created by createXConstantNode(), where X is
|
|
|
|
* either Boolean, Integer, Float, or String.
|
|
|
|
*
|
|
|
|
* \return A pointer to the constant value.
|
|
|
|
*
|
|
|
|
* \retval NULL An error occurred during interpretation.
|
|
|
|
*/
|
|
|
|
ValueObject *interpretConstantExprNode(ExprNode *node,
|
|
|
|
ScopeObject *scope)
|
2010-08-09 07:01:59 +00:00
|
|
|
{
|
|
|
|
ConstantNode *expr = (ConstantNode *)node->expr;
|
|
|
|
scope = NULL;
|
|
|
|
switch (expr->type) {
|
|
|
|
case CT_NIL:
|
|
|
|
return createNilValueObject();
|
|
|
|
case CT_BOOLEAN:
|
|
|
|
return createBooleanValueObject(expr->data.i);
|
|
|
|
case CT_INTEGER:
|
|
|
|
return createIntegerValueObject(expr->data.i);
|
|
|
|
case CT_FLOAT:
|
|
|
|
return createFloatValueObject(expr->data.f);
|
|
|
|
case CT_STRING: {
|
2011-06-15 06:54:12 +00:00
|
|
|
/*
|
|
|
|
* \note For efficiency, string interpolation should be
|
|
|
|
* performed by caller because it only needs to be
|
|
|
|
* performed when necessary.
|
|
|
|
*/
|
|
|
|
char *str = copyString(expr->data.s);
|
2010-08-09 07:01:59 +00:00
|
|
|
if (!str) return NULL;
|
|
|
|
return createStringValueObject(str);
|
|
|
|
}
|
|
|
|
default:
|
2012-12-13 04:53:54 +00:00
|
|
|
error(IN_UNKNOWN_CONSTANT_TYPE);
|
2010-08-09 07:01:59 +00:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2011-06-15 06:54:12 +00:00
|
|
|
/**
|
|
|
|
* Interprets a logical NOT operation.
|
|
|
|
*
|
|
|
|
* \param [in] expr A pointer to the expression to interpret.
|
|
|
|
*
|
|
|
|
* \param [in] scope A pointer to a scope to evaluate \a node under.
|
|
|
|
*
|
|
|
|
* \note Only the first element of \a args is used.
|
|
|
|
*
|
|
|
|
* \return A pointer to the value of the logical negation of the first element
|
|
|
|
* of \a args.
|
|
|
|
*
|
|
|
|
* \retval NULL An error occurred during interpretation.
|
|
|
|
*/
|
|
|
|
ValueObject *interpretNotOpExprNode(OpExprNode *expr,
|
|
|
|
ScopeObject *scope)
|
2010-08-09 07:01:59 +00:00
|
|
|
{
|
|
|
|
ValueObject *val = interpretExprNode(expr->args->exprs[0], scope);
|
|
|
|
ValueObject *use = val;
|
|
|
|
int retval;
|
|
|
|
unsigned short cast = 0;
|
|
|
|
if (!val) return NULL;
|
|
|
|
if (val->type != VT_BOOLEAN && val->type != VT_INTEGER) {
|
|
|
|
use = castBooleanImplicit(val, scope);
|
|
|
|
if (!use) {
|
|
|
|
deleteValueObject(val);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
cast = 1;
|
|
|
|
}
|
|
|
|
retval = getInteger(use);
|
|
|
|
if (cast) deleteValueObject(use);
|
|
|
|
deleteValueObject(val);
|
|
|
|
return createBooleanValueObject(!retval);
|
|
|
|
}
|
|
|
|
|
2011-06-15 06:54:12 +00:00
|
|
|
/**
|
|
|
|
* Adds an integer to an integer.
|
|
|
|
*
|
|
|
|
* \param [in] a The first operand.
|
|
|
|
*
|
|
|
|
* \param [in] b The second operand.
|
|
|
|
*
|
|
|
|
* \return A pointer to the value of the sum of \a a and \a b.
|
|
|
|
*/
|
|
|
|
ValueObject *opAddIntegerInteger(ValueObject *a,
|
|
|
|
ValueObject *b)
|
2010-08-09 07:01:59 +00:00
|
|
|
{
|
|
|
|
return createIntegerValueObject(getInteger(a) + getInteger(b));
|
|
|
|
}
|
|
|
|
|
2011-06-15 06:54:12 +00:00
|
|
|
/**
|
|
|
|
* Subtracts an integer from an integer.
|
|
|
|
*
|
|
|
|
* \param [in] a The first operand.
|
|
|
|
*
|
|
|
|
* \param [in] b The second operand.
|
|
|
|
*
|
|
|
|
* \return A pointer to the value of the difference of \a a and \a b.
|
|
|
|
*/
|
|
|
|
ValueObject *opSubIntegerInteger(ValueObject *a,
|
|
|
|
ValueObject *b)
|
2010-08-09 07:01:59 +00:00
|
|
|
{
|
|
|
|
return createIntegerValueObject(getInteger(a) - getInteger(b));
|
|
|
|
}
|
|
|
|
|
2011-06-15 06:54:12 +00:00
|
|
|
/**
|
|
|
|
* Multiplies an integer by an integer.
|
|
|
|
*
|
|
|
|
* \param [in] a The first operand.
|
|
|
|
*
|
|
|
|
* \param [in] b The second operand.
|
|
|
|
*
|
|
|
|
* \return A pointer to the value of the product of \a a and \a b.
|
|
|
|
*/
|
|
|
|
ValueObject *opMultIntegerInteger(ValueObject *a,
|
|
|
|
ValueObject *b)
|
2010-08-09 07:01:59 +00:00
|
|
|
{
|
|
|
|
return createIntegerValueObject(getInteger(a) * getInteger(b));
|
|
|
|
}
|
|
|
|
|
2011-06-15 06:54:12 +00:00
|
|
|
/**
|
|
|
|
* Divides an integer by an integer.
|
|
|
|
*
|
|
|
|
* \param [in] a The first operand.
|
|
|
|
*
|
|
|
|
* \param [in] b The second operand.
|
|
|
|
*
|
|
|
|
* \return A pointer to the value of the quotient of \a a and \a b.
|
|
|
|
*
|
|
|
|
* \retval NULL Division by zero.
|
|
|
|
*/
|
|
|
|
ValueObject *opDivIntegerInteger(ValueObject *a,
|
|
|
|
ValueObject *b)
|
2010-08-09 07:01:59 +00:00
|
|
|
{
|
|
|
|
if (getInteger(b) == 0) {
|
2012-12-13 04:53:54 +00:00
|
|
|
error(IN_DIVISION_BY_ZERO);
|
2010-08-09 07:01:59 +00:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
return createIntegerValueObject(getInteger(a) / getInteger(b));
|
|
|
|
}
|
|
|
|
|
2011-06-15 06:54:12 +00:00
|
|
|
/**
|
|
|
|
* Finds the maximum of an integer and an integer.
|
|
|
|
*
|
|
|
|
* \param [in] a The first operand.
|
|
|
|
*
|
|
|
|
* \param [in] b The second operand.
|
|
|
|
*
|
|
|
|
* \return A pointer to the value of the maximum of \a a and \a b.
|
|
|
|
*/
|
|
|
|
ValueObject *opMaxIntegerInteger(ValueObject *a,
|
|
|
|
ValueObject *b)
|
2010-08-09 07:01:59 +00:00
|
|
|
{
|
|
|
|
return createIntegerValueObject(getInteger(a) > getInteger(b) ? getInteger(a) : getInteger(b));
|
|
|
|
}
|
|
|
|
|
2011-06-15 06:54:12 +00:00
|
|
|
/**
|
|
|
|
* Finds the minimum of an integer and an integer.
|
|
|
|
*
|
|
|
|
* \param [in] a The first operand.
|
|
|
|
*
|
|
|
|
* \param [in] b The second operand.
|
|
|
|
*
|
|
|
|
* \return A pointer to the value of the minimum of \a a and \a b.
|
|
|
|
*/
|
|
|
|
ValueObject *opMinIntegerInteger(ValueObject *a,
|
|
|
|
ValueObject *b)
|
2010-08-09 07:01:59 +00:00
|
|
|
{
|
|
|
|
return createIntegerValueObject(getInteger(a) < getInteger(b) ? getInteger(a) : getInteger(b));
|
|
|
|
}
|
|
|
|
|
2011-06-15 06:54:12 +00:00
|
|
|
/**
|
|
|
|
* Calculates the modulus of an integer and an integer.
|
|
|
|
*
|
|
|
|
* \param [in] a The first operand.
|
|
|
|
*
|
|
|
|
* \param [in] b The second operand.
|
|
|
|
*
|
|
|
|
* \return A pointer to the value of the modulus of \a a and \a b.
|
|
|
|
*/
|
|
|
|
ValueObject *opModIntegerInteger(ValueObject *a,
|
|
|
|
ValueObject *b)
|
2010-08-09 07:01:59 +00:00
|
|
|
{
|
|
|
|
if (getInteger(b) == 0) {
|
2012-12-13 04:53:54 +00:00
|
|
|
error(IN_DIVISION_BY_ZERO);
|
2010-08-09 07:01:59 +00:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
return createIntegerValueObject(getInteger(a) % getInteger(b));
|
|
|
|
}
|
|
|
|
|
2011-06-15 06:54:12 +00:00
|
|
|
/**
|
|
|
|
* Adds an integer to a decimal.
|
|
|
|
*
|
|
|
|
* \param [in] a The first operand.
|
|
|
|
*
|
|
|
|
* \param [in] b The second operand.
|
|
|
|
*
|
|
|
|
* \return A pointer to the value of the sum of \a a and \a b.
|
|
|
|
*/
|
|
|
|
ValueObject *opAddIntegerFloat(ValueObject *a,
|
|
|
|
ValueObject *b)
|
2010-08-09 07:01:59 +00:00
|
|
|
{
|
2010-12-21 08:20:31 +00:00
|
|
|
return createFloatValueObject((float)(getInteger(a) + getFloat(b)));
|
2010-08-09 07:01:59 +00:00
|
|
|
}
|
|
|
|
|
2011-06-15 06:54:12 +00:00
|
|
|
/**
|
|
|
|
* Subtracts an integer from a decimal.
|
|
|
|
*
|
|
|
|
* \param [in] a The first operand.
|
|
|
|
*
|
|
|
|
* \param [in] b The second operand.
|
|
|
|
*
|
|
|
|
* \return A pointer to the value of the difference of \a a and \a b.
|
|
|
|
*/
|
|
|
|
ValueObject *opSubIntegerFloat(ValueObject *a,
|
|
|
|
ValueObject *b)
|
2010-08-09 07:01:59 +00:00
|
|
|
{
|
2010-12-21 08:20:31 +00:00
|
|
|
return createFloatValueObject((float)(getInteger(a) - getFloat(b)));
|
2010-08-09 07:01:59 +00:00
|
|
|
}
|
|
|
|
|
2011-06-15 06:54:12 +00:00
|
|
|
/**
|
|
|
|
* Multiplies an integer by a decimal.
|
|
|
|
*
|
|
|
|
* \param [in] a The first operand.
|
|
|
|
*
|
|
|
|
* \param [in] b The second operand.
|
|
|
|
*
|
|
|
|
* \return A pointer to the value of the product of \a a and \a b.
|
|
|
|
*/
|
|
|
|
ValueObject *opMultIntegerFloat(ValueObject *a,
|
|
|
|
ValueObject *b)
|
2010-08-09 07:01:59 +00:00
|
|
|
{
|
2010-12-21 08:20:31 +00:00
|
|
|
return createFloatValueObject((float)(getInteger(a) * getFloat(b)));
|
2010-08-09 07:01:59 +00:00
|
|
|
}
|
|
|
|
|
2011-06-15 06:54:12 +00:00
|
|
|
/**
|
|
|
|
* Divides an integer by a decimal.
|
|
|
|
*
|
|
|
|
* \param [in] a The first operand.
|
|
|
|
*
|
|
|
|
* \param [in] b The second operand.
|
|
|
|
*
|
|
|
|
* \return A pointer to the value of the quotient of \a a and \a b.
|
|
|
|
*
|
|
|
|
* \retval NULL Division by zero.
|
|
|
|
*/
|
|
|
|
ValueObject *opDivIntegerFloat(ValueObject *a,
|
|
|
|
ValueObject *b)
|
2010-08-09 07:01:59 +00:00
|
|
|
{
|
2010-12-21 08:20:31 +00:00
|
|
|
if (fabs(getFloat(b) - 0.0) < FLT_EPSILON) {
|
2012-12-13 04:53:54 +00:00
|
|
|
error(IN_DIVISION_BY_ZERO);
|
2010-08-09 07:01:59 +00:00
|
|
|
return NULL;
|
|
|
|
}
|
2010-12-21 08:20:31 +00:00
|
|
|
return createFloatValueObject((float)(getInteger(a) / getFloat(b)));
|
2010-08-09 07:01:59 +00:00
|
|
|
}
|
|
|
|
|
2011-06-15 06:54:12 +00:00
|
|
|
/**
|
|
|
|
* Finds the maximum of an integer and a decimal.
|
|
|
|
*
|
|
|
|
* \param [in] a The first operand.
|
|
|
|
*
|
|
|
|
* \param [in] b The second operand.
|
|
|
|
*
|
|
|
|
* \return A pointer to the value of the maximum of \a a and \a b.
|
|
|
|
*/
|
|
|
|
ValueObject *opMaxIntegerFloat(ValueObject *a,
|
|
|
|
ValueObject *b)
|
2010-08-09 07:01:59 +00:00
|
|
|
{
|
2010-12-21 08:20:31 +00:00
|
|
|
return createFloatValueObject((float)(getInteger(a)) > getFloat(b) ? (float)(getInteger(a)) : getFloat(b));
|
2010-08-09 07:01:59 +00:00
|
|
|
}
|
|
|
|
|
2011-06-15 06:54:12 +00:00
|
|
|
/**
|
|
|
|
* Finds the minimum of an integer and a decimal.
|
|
|
|
*
|
|
|
|
* \param [in] a The first operand.
|
|
|
|
*
|
|
|
|
* \param [in] b The second operand.
|
|
|
|
*
|
|
|
|
* \return A pointer to the value of the minimum of \a a and \a b.
|
|
|
|
*/
|
|
|
|
ValueObject *opMinIntegerFloat(ValueObject *a,
|
|
|
|
ValueObject *b)
|
2010-08-09 07:01:59 +00:00
|
|
|
{
|
2010-12-21 08:20:31 +00:00
|
|
|
return createFloatValueObject((float)(getInteger(a)) < getFloat(b) ? (float)(getInteger(a)) : getFloat(b));
|
2010-08-09 07:01:59 +00:00
|
|
|
}
|
|
|
|
|
2011-06-15 06:54:12 +00:00
|
|
|
/**
|
|
|
|
* Calculates the modulus of an integer and a decimal.
|
|
|
|
*
|
|
|
|
* \param [in] a The first operand.
|
|
|
|
*
|
|
|
|
* \param [in] b The second operand.
|
|
|
|
*
|
|
|
|
* \return A pointer to the value of the modulus of \a a and \a b.
|
|
|
|
*/
|
|
|
|
ValueObject *opModIntegerFloat(ValueObject *a,
|
|
|
|
ValueObject *b)
|
2010-08-09 07:01:59 +00:00
|
|
|
{
|
2010-12-21 08:20:31 +00:00
|
|
|
if (fabs(getFloat(b) - 0.0) < FLT_EPSILON) {
|
2012-12-13 04:53:54 +00:00
|
|
|
error(IN_DIVISION_BY_ZERO);
|
2010-08-09 07:01:59 +00:00
|
|
|
return NULL;
|
|
|
|
}
|
2010-12-21 08:20:31 +00:00
|
|
|
return createFloatValueObject((float)(fmod((double)(getInteger(a)), getFloat(b))));
|
2010-08-09 07:01:59 +00:00
|
|
|
}
|
|
|
|
|
2011-06-15 06:54:12 +00:00
|
|
|
/**
|
|
|
|
* Adds a decimal to an integer.
|
|
|
|
*
|
|
|
|
* \param [in] a The first operand.
|
|
|
|
*
|
|
|
|
* \param [in] b The second operand.
|
|
|
|
*
|
|
|
|
* \return A pointer to the value of the sum of \a a and \a b.
|
|
|
|
*/
|
|
|
|
ValueObject *opAddFloatInteger(ValueObject *a,
|
|
|
|
ValueObject *b)
|
2010-08-09 07:01:59 +00:00
|
|
|
{
|
|
|
|
return createFloatValueObject(getFloat(a) + getInteger(b));
|
|
|
|
}
|
|
|
|
|
2011-06-15 06:54:12 +00:00
|
|
|
/**
|
|
|
|
* Subtracts a decimal from an integer.
|
|
|
|
*
|
|
|
|
* \param [in] a The first operand.
|
|
|
|
*
|
|
|
|
* \param [in] b The second operand.
|
|
|
|
*
|
|
|
|
* \return A pointer to the value of the difference of \a a and \a b.
|
|
|
|
*/
|
|
|
|
ValueObject *opSubFloatInteger(ValueObject *a,
|
|
|
|
ValueObject *b)
|
2010-08-09 07:01:59 +00:00
|
|
|
{
|
|
|
|
return createFloatValueObject(getFloat(a) - getInteger(b));
|
|
|
|
}
|
|
|
|
|
2011-06-15 06:54:12 +00:00
|
|
|
/**
|
|
|
|
* Multiplies a decimal by an integer.
|
|
|
|
*
|
|
|
|
* \param [in] a The first operand.
|
|
|
|
*
|
|
|
|
* \param [in] b The second operand.
|
|
|
|
*
|
|
|
|
* \return A pointer to the value of the product of \a a and \a b.
|
|
|
|
*/
|
|
|
|
ValueObject *opMultFloatInteger(ValueObject *a,
|
|
|
|
ValueObject *b)
|
2010-08-09 07:01:59 +00:00
|
|
|
{
|
|
|
|
return createFloatValueObject(getFloat(a) * getInteger(b));
|
|
|
|
}
|
|
|
|
|
2011-06-15 06:54:12 +00:00
|
|
|
/**
|
|
|
|
* Divides a decimal by an integer.
|
|
|
|
*
|
|
|
|
* \param [in] a The first operand.
|
|
|
|
*
|
|
|
|
* \param [in] b The second operand.
|
|
|
|
*
|
|
|
|
* \return A pointer to the value of the quotient of \a a and \a b.
|
|
|
|
*
|
|
|
|
* \retval NULL Division by zero.
|
|
|
|
*/
|
|
|
|
ValueObject *opDivFloatInteger(ValueObject *a,
|
|
|
|
ValueObject *b)
|
2010-08-09 07:01:59 +00:00
|
|
|
{
|
|
|
|
if (getInteger(b) == 0) {
|
2012-12-13 04:53:54 +00:00
|
|
|
error(IN_DIVISION_BY_ZERO);
|
2010-08-09 07:01:59 +00:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
return createFloatValueObject(getFloat(a) / getInteger(b));
|
|
|
|
}
|
|
|
|
|
2011-06-15 06:54:12 +00:00
|
|
|
/**
|
|
|
|
* Finds the maximum of a decimal and an integer.
|
|
|
|
*
|
|
|
|
* \param [in] a The first operand.
|
|
|
|
*
|
|
|
|
* \param [in] b The second operand.
|
|
|
|
*
|
|
|
|
* \return A pointer to the value of the maximum of \a a and \a b.
|
|
|
|
*/
|
|
|
|
ValueObject *opMaxFloatInteger(ValueObject *a,
|
|
|
|
ValueObject *b)
|
2010-08-09 07:01:59 +00:00
|
|
|
{
|
2010-12-21 08:20:31 +00:00
|
|
|
return createFloatValueObject(getFloat(a) > (float)(getInteger(b)) ? getFloat(a) : (float)(getInteger(b)));
|
2010-08-09 07:01:59 +00:00
|
|
|
}
|
|
|
|
|
2011-06-15 06:54:12 +00:00
|
|
|
/**
|
|
|
|
* Finds the minimum of a decimal and an integer.
|
|
|
|
*
|
|
|
|
* \param [in] a The first operand.
|
|
|
|
*
|
|
|
|
* \param [in] b The second operand.
|
|
|
|
*
|
|
|
|
* \return A pointer to the value of the minimum of \a a and \a b.
|
|
|
|
*/
|
|
|
|
ValueObject *opMinFloatInteger(ValueObject *a,
|
|
|
|
ValueObject *b)
|
2010-08-09 07:01:59 +00:00
|
|
|
{
|
2010-12-21 08:20:31 +00:00
|
|
|
return createFloatValueObject(getFloat(a) < (float)(getInteger(b)) ? getFloat(a) : (float)(getInteger(b)));
|
2010-08-09 07:01:59 +00:00
|
|
|
}
|
|
|
|
|
2011-06-15 06:54:12 +00:00
|
|
|
/**
|
|
|
|
* Calculates the modulus of a decimal and an integer.
|
|
|
|
*
|
|
|
|
* \param [in] a The first operand.
|
|
|
|
*
|
|
|
|
* \param [in] b The second operand.
|
|
|
|
*
|
|
|
|
* \return A pointer to the value of the modulus of \a a and \a b.
|
|
|
|
*/
|
|
|
|
ValueObject *opModFloatInteger(ValueObject *a,
|
|
|
|
ValueObject *b)
|
2010-08-09 07:01:59 +00:00
|
|
|
{
|
|
|
|
if (getInteger(b) == 0) {
|
2012-12-13 04:53:54 +00:00
|
|
|
error(IN_DIVISION_BY_ZERO);
|
2010-08-09 07:01:59 +00:00
|
|
|
return NULL;
|
|
|
|
}
|
2010-12-21 08:20:31 +00:00
|
|
|
return createFloatValueObject((float)(fmod(getFloat(a), (double)(getInteger(b)))));
|
2010-08-09 07:01:59 +00:00
|
|
|
}
|
|
|
|
|
2011-06-15 06:54:12 +00:00
|
|
|
/**
|
|
|
|
* Adds a decimal to a decimal.
|
|
|
|
*
|
|
|
|
* \param [in] a The first operand.
|
|
|
|
*
|
|
|
|
* \param [in] b The second operand.
|
|
|
|
*
|
|
|
|
* \return A pointer to the value of the sum of \a a and \a b.
|
|
|
|
*/
|
|
|
|
ValueObject *opAddFloatFloat(ValueObject *a,
|
|
|
|
ValueObject *b)
|
2010-08-09 07:01:59 +00:00
|
|
|
{
|
|
|
|
return createFloatValueObject(getFloat(a) + getFloat(b));
|
|
|
|
}
|
|
|
|
|
2011-06-15 06:54:12 +00:00
|
|
|
/**
|
|
|
|
* Subtracts a decimal from a decimal.
|
|
|
|
*
|
|
|
|
* \param [in] a The first operand.
|
|
|
|
*
|
|
|
|
* \param [in] b The second operand.
|
|
|
|
*
|
|
|
|
* \return A pointer to the value of the difference of \a a and \a b.
|
|
|
|
*/
|
|
|
|
ValueObject *opSubFloatFloat(ValueObject *a,
|
|
|
|
ValueObject *b)
|
2010-08-09 07:01:59 +00:00
|
|
|
{
|
|
|
|
return createFloatValueObject(getFloat(a) - getFloat(b));
|
|
|
|
}
|
|
|
|
|
2011-06-15 06:54:12 +00:00
|
|
|
/**
|
|
|
|
* Multiplies a decimal by a decimal.
|
|
|
|
*
|
|
|
|
* \param [in] a The first operand.
|
|
|
|
*
|
|
|
|
* \param [in] b The second operand.
|
|
|
|
*
|
|
|
|
* \return A pointer to the value of the product of \a a and \a b.
|
|
|
|
*/
|
|
|
|
ValueObject *opMultFloatFloat(ValueObject *a,
|
|
|
|
ValueObject *b)
|
2010-08-09 07:01:59 +00:00
|
|
|
{
|
|
|
|
return createFloatValueObject(getFloat(a) * getFloat(b));
|
|
|
|
}
|
|
|
|
|
2011-06-15 06:54:12 +00:00
|
|
|
/**
|
|
|
|
* Divides a decimal by a decimal.
|
|
|
|
*
|
|
|
|
* \param [in] a The first operand.
|
|
|
|
*
|
|
|
|
* \param [in] b The second operand.
|
|
|
|
*
|
|
|
|
* \return A pointer to the value of the quotient of \a a and \a b.
|
|
|
|
*
|
|
|
|
* \retval NULL Division by zero.
|
|
|
|
*/
|
|
|
|
ValueObject *opDivFloatFloat(ValueObject *a,
|
|
|
|
ValueObject *b)
|
2010-08-09 07:01:59 +00:00
|
|
|
{
|
2010-12-21 08:20:31 +00:00
|
|
|
if (fabs(getFloat(b) - 0.0) < FLT_EPSILON) {
|
2012-12-13 04:53:54 +00:00
|
|
|
error(IN_DIVISION_BY_ZERO);
|
2010-08-09 07:01:59 +00:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
return createFloatValueObject(getFloat(a) / getFloat(b));
|
|
|
|
}
|
|
|
|
|
2011-06-15 06:54:12 +00:00
|
|
|
/**
|
|
|
|
* Finds the maximum of a decimal and a decimal.
|
|
|
|
*
|
|
|
|
* \param [in] a The first operand.
|
|
|
|
*
|
|
|
|
* \param [in] b The second operand.
|
|
|
|
*
|
|
|
|
* \return A pointer to the value of the maximum of \a a and \a b.
|
|
|
|
*/
|
|
|
|
ValueObject *opMaxFloatFloat(ValueObject *a,
|
|
|
|
ValueObject *b)
|
2010-08-09 07:01:59 +00:00
|
|
|
{
|
|
|
|
return createFloatValueObject(getFloat(a) > getFloat(b) ? getFloat(a) : getFloat(b));
|
|
|
|
}
|
|
|
|
|
2011-06-15 06:54:12 +00:00
|
|
|
/**
|
|
|
|
* Finds the minimum of a decimal and a decimal.
|
|
|
|
*
|
|
|
|
* \param [in] a The first operand.
|
|
|
|
*
|
|
|
|
* \param [in] b The second operand.
|
|
|
|
*
|
|
|
|
* \return A pointer to the value of the minimum of \a a and \a b.
|
|
|
|
*/
|
|
|
|
ValueObject *opMinFloatFloat(ValueObject *a,
|
|
|
|
ValueObject *b)
|
2010-08-09 07:01:59 +00:00
|
|
|
{
|
|
|
|
return createFloatValueObject(getFloat(a) < getFloat(b) ? getFloat(a) : getFloat(b));
|
|
|
|
}
|
|
|
|
|
2011-06-15 06:54:12 +00:00
|
|
|
/**
|
|
|
|
* Calculates the modulus of a decimal and a decimal.
|
|
|
|
*
|
|
|
|
* \param [in] a The first operand.
|
|
|
|
*
|
|
|
|
* \param [in] b The second operand.
|
|
|
|
*
|
|
|
|
* \return A pointer to the value of the modulus of \a a and \a b.
|
|
|
|
*/
|
|
|
|
ValueObject *opModFloatFloat(ValueObject *a,
|
|
|
|
ValueObject *b)
|
2010-08-09 07:01:59 +00:00
|
|
|
{
|
2010-12-21 08:20:31 +00:00
|
|
|
if (fabs(getFloat(b) - 0.0) < FLT_EPSILON) {
|
2012-12-13 04:53:54 +00:00
|
|
|
error(IN_DIVISION_BY_ZERO);
|
2010-08-09 07:01:59 +00:00
|
|
|
return NULL;
|
|
|
|
}
|
2010-12-21 08:20:31 +00:00
|
|
|
return createFloatValueObject((float)(fmod(getFloat(a), getFloat(b))));
|
2010-08-09 07:01:59 +00:00
|
|
|
}
|
|
|
|
|
2011-06-15 06:54:12 +00:00
|
|
|
/*
|
|
|
|
* A jump table for arithmetic operations. The first index determines the
|
|
|
|
* particular arithmetic operation to perform, the second index determines the
|
2010-08-09 07:01:59 +00:00
|
|
|
* type of the first argument, and the third index determines the type of the
|
2011-06-15 06:54:12 +00:00
|
|
|
* second object.
|
|
|
|
*/
|
2010-08-09 07:01:59 +00:00
|
|
|
static ValueObject *(*ArithOpJumpTable[7][2][2])(ValueObject *, ValueObject *) = {
|
|
|
|
{ { opAddIntegerInteger, opAddIntegerFloat }, { opAddFloatInteger, opAddFloatFloat } },
|
|
|
|
{ { opSubIntegerInteger, opSubIntegerFloat }, { opSubFloatInteger, opSubFloatFloat } },
|
|
|
|
{ { opMultIntegerInteger, opMultIntegerFloat }, { opMultFloatInteger, opMultFloatFloat } },
|
|
|
|
{ { opDivIntegerInteger, opDivIntegerFloat }, { opDivFloatInteger, opDivFloatFloat } },
|
|
|
|
{ { opModIntegerInteger, opModIntegerFloat }, { opModFloatInteger, opModFloatFloat } },
|
|
|
|
{ { opMaxIntegerInteger, opMaxIntegerFloat }, { opMaxFloatInteger, opMaxFloatFloat } },
|
|
|
|
{ { opMinIntegerInteger, opMinIntegerFloat }, { opMinFloatInteger, opMinFloatFloat } }
|
|
|
|
};
|
|
|
|
|
2011-06-15 06:54:12 +00:00
|
|
|
/**
|
|
|
|
* Interprets an arithmetic operation.
|
|
|
|
*
|
|
|
|
* \param [in] expr The operation to interpret.
|
|
|
|
*
|
|
|
|
* \param [in] scope The scope to evaluate \a expr under.
|
|
|
|
*
|
|
|
|
* \note Only supports binary arithmetic operations.
|
|
|
|
*
|
|
|
|
* \return A pointer to the value of the arithmetic operation.
|
|
|
|
*
|
|
|
|
* \retval NULL An error occurred during interpretation.
|
|
|
|
*/
|
|
|
|
ValueObject *interpretArithOpExprNode(OpExprNode *expr,
|
|
|
|
ScopeObject *scope)
|
2010-08-09 07:01:59 +00:00
|
|
|
{
|
|
|
|
ValueObject *val1 = interpretExprNode(expr->args->exprs[0], scope);
|
|
|
|
ValueObject *val2 = interpretExprNode(expr->args->exprs[1], scope);
|
|
|
|
ValueObject *use1 = val1;
|
|
|
|
ValueObject *use2 = val2;
|
|
|
|
unsigned int cast1 = 0;
|
|
|
|
unsigned int cast2 = 0;
|
|
|
|
ValueObject *ret = NULL;
|
|
|
|
if (!val1 || !val2) {
|
|
|
|
deleteValueObject(val1);
|
|
|
|
deleteValueObject(val2);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
/* Check if a floating point decimal string and cast */
|
|
|
|
switch (val1->type) {
|
|
|
|
case VT_NIL:
|
|
|
|
case VT_BOOLEAN:
|
|
|
|
use1 = castIntegerImplicit(val1, scope);
|
|
|
|
if (!use1) {
|
|
|
|
deleteValueObject(val1);
|
|
|
|
deleteValueObject(val2);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
cast1 = 1;
|
|
|
|
break;
|
|
|
|
case VT_INTEGER:
|
|
|
|
case VT_FLOAT:
|
|
|
|
break;
|
|
|
|
case VT_STRING: {
|
|
|
|
/* Perform interpolation */
|
|
|
|
ValueObject *interp = castStringExplicit(val1, scope);
|
|
|
|
if (!interp) {
|
|
|
|
deleteValueObject(val1);
|
|
|
|
deleteValueObject(val2);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
if (strchr(getString(interp), '.'))
|
|
|
|
use1 = castFloatImplicit(interp, scope);
|
|
|
|
else
|
|
|
|
use1 = castIntegerImplicit(interp, scope);
|
|
|
|
deleteValueObject(interp);
|
|
|
|
if (!use1) {
|
|
|
|
deleteValueObject(val1);
|
|
|
|
deleteValueObject(val2);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
cast1 = 1;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
default:
|
2012-12-13 04:53:54 +00:00
|
|
|
error(IN_INVALID_OPERAND_TYPE);
|
2010-08-09 07:01:59 +00:00
|
|
|
}
|
|
|
|
switch (val2->type) {
|
|
|
|
case VT_NIL:
|
|
|
|
case VT_BOOLEAN:
|
|
|
|
use2 = castIntegerImplicit(val2, scope);
|
|
|
|
if (!use2) {
|
|
|
|
deleteValueObject(val1);
|
|
|
|
deleteValueObject(val2);
|
|
|
|
if (cast1) deleteValueObject(use1);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
cast2 = 1;
|
|
|
|
break;
|
|
|
|
case VT_INTEGER:
|
|
|
|
case VT_FLOAT:
|
|
|
|
break;
|
|
|
|
case VT_STRING: {
|
|
|
|
/* Perform interpolation */
|
|
|
|
ValueObject *interp = castStringExplicit(val2, scope);
|
|
|
|
if (!interp) {
|
|
|
|
deleteValueObject(val1);
|
|
|
|
deleteValueObject(val2);
|
|
|
|
if (cast1) deleteValueObject(use1);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
if (strchr(getString(interp), '.'))
|
|
|
|
use2 = castFloatImplicit(interp, scope);
|
|
|
|
else
|
|
|
|
use2 = castIntegerImplicit(interp, scope);
|
|
|
|
deleteValueObject(interp);
|
|
|
|
if (!use2) {
|
|
|
|
deleteValueObject(val1);
|
|
|
|
deleteValueObject(val2);
|
|
|
|
if (cast1) deleteValueObject(use1);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
cast2 = 1;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
default:
|
2012-12-13 04:53:54 +00:00
|
|
|
error(IN_INVALID_OPERAND_TYPE);
|
2010-08-09 07:01:59 +00:00
|
|
|
}
|
|
|
|
/* Do math depending on value types */
|
|
|
|
ret = ArithOpJumpTable[expr->type][use1->type][use2->type](use1, use2);
|
|
|
|
/* Clean up after floating point decimal casts */
|
|
|
|
if (cast1) deleteValueObject(use1);
|
|
|
|
if (cast2) deleteValueObject(use2);
|
|
|
|
deleteValueObject(val1);
|
|
|
|
deleteValueObject(val2);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2011-06-15 06:54:12 +00:00
|
|
|
/**
|
|
|
|
* Interprets a boolean operation.
|
|
|
|
*
|
|
|
|
* \param [in] expr The operation to interpret.
|
|
|
|
*
|
|
|
|
* \param [in] scope The scope to evaluate \a expr under.
|
|
|
|
*
|
|
|
|
* \return A pointer to the value of the boolean operation.
|
|
|
|
*
|
|
|
|
* \retval NULL An error occurred during interpretation.
|
|
|
|
*/
|
|
|
|
ValueObject *interpretBoolOpExprNode(OpExprNode *expr,
|
|
|
|
ScopeObject *scope)
|
2010-08-09 07:01:59 +00:00
|
|
|
{
|
|
|
|
unsigned int n;
|
|
|
|
int acc = 0;
|
2011-06-15 06:54:12 +00:00
|
|
|
/*
|
|
|
|
* Proceed to apply the same operation on the accumulator for the
|
|
|
|
* remaining arguments.
|
|
|
|
*/
|
2010-08-09 07:01:59 +00:00
|
|
|
for (n = 0; n < expr->args->num; n++) {
|
|
|
|
ValueObject *val = interpretExprNode(expr->args->exprs[n], scope);
|
|
|
|
ValueObject *use = val;
|
2010-12-21 08:20:31 +00:00
|
|
|
int temp;
|
2010-08-09 07:01:59 +00:00
|
|
|
unsigned int cast = 0;
|
|
|
|
if (!val) return NULL;
|
|
|
|
if (val->type != VT_BOOLEAN && val->type != VT_INTEGER) {
|
|
|
|
use = castBooleanImplicit(val, scope);
|
|
|
|
if (!use) {
|
|
|
|
deleteValueObject(val);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
cast = 1;
|
|
|
|
}
|
|
|
|
temp = getInteger(use);
|
|
|
|
if (cast) deleteValueObject(use);
|
|
|
|
deleteValueObject(val);
|
|
|
|
if (n == 0) acc = temp;
|
|
|
|
else {
|
|
|
|
switch (expr->type) {
|
|
|
|
case OP_AND:
|
|
|
|
acc &= temp;
|
|
|
|
break;
|
|
|
|
case OP_OR:
|
|
|
|
acc |= temp;
|
|
|
|
break;
|
|
|
|
case OP_XOR:
|
|
|
|
acc ^= temp;
|
|
|
|
break;
|
|
|
|
default:
|
2012-12-13 04:53:54 +00:00
|
|
|
error(IN_INVALID_BOOLEAN_OPERATION_TYPE);
|
2010-08-09 07:01:59 +00:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
}
|
2011-06-15 06:54:12 +00:00
|
|
|
/**
|
|
|
|
* \note The specification does not say whether boolean logic
|
|
|
|
* short circuits or not. Here, we assume it does.
|
|
|
|
*/
|
2010-08-09 07:01:59 +00:00
|
|
|
if (expr->type == OP_AND && acc == 0) break;
|
|
|
|
else if (expr->type == OP_OR && acc == 1) break;
|
|
|
|
}
|
|
|
|
return createBooleanValueObject(acc);
|
|
|
|
}
|
|
|
|
|
2011-06-15 06:54:12 +00:00
|
|
|
/**
|
|
|
|
* Checks if an integer value is equal to another integer value.
|
|
|
|
*
|
|
|
|
* \param [in] a The first value to check.
|
|
|
|
*
|
|
|
|
* \param [in] b The second value to check.
|
|
|
|
*
|
|
|
|
* \return A pointer to a boolean value indicating if \a is equal to \a b.
|
|
|
|
*/
|
|
|
|
ValueObject *opEqIntegerInteger(ValueObject *a,
|
|
|
|
ValueObject *b)
|
2010-08-09 07:01:59 +00:00
|
|
|
{
|
|
|
|
return createBooleanValueObject(getInteger(a) == getInteger(b));
|
|
|
|
}
|
|
|
|
|
2011-06-15 06:54:12 +00:00
|
|
|
/**
|
|
|
|
* Checks if an integer value is not equal to another integer value.
|
|
|
|
*
|
|
|
|
* \param [in] a The first value to check.
|
|
|
|
*
|
|
|
|
* \param [in] b The second value to check.
|
|
|
|
*
|
|
|
|
* \return A pointer to a boolean value indicating if \a is not equal to \a b.
|
|
|
|
*/
|
|
|
|
ValueObject *opNeqIntegerInteger(ValueObject *a,
|
|
|
|
ValueObject *b)
|
2010-08-09 07:01:59 +00:00
|
|
|
{
|
|
|
|
return createBooleanValueObject(getInteger(a) != getInteger(b));
|
|
|
|
}
|
|
|
|
|
2011-06-15 06:54:12 +00:00
|
|
|
/**
|
|
|
|
* Checks if an integer value is equal to a decimal value.
|
|
|
|
*
|
|
|
|
* \param [in] a The first value to check.
|
|
|
|
*
|
|
|
|
* \param [in] b The second value to check.
|
|
|
|
*
|
|
|
|
* \return A pointer to a boolean value indicating if \a is equal to \a b.
|
|
|
|
*/
|
|
|
|
ValueObject *opEqIntegerFloat(ValueObject *a,
|
|
|
|
ValueObject *b)
|
2010-08-09 07:01:59 +00:00
|
|
|
{
|
2010-12-21 08:20:31 +00:00
|
|
|
return createBooleanValueObject(fabs((float)(getInteger(a)) - getFloat(b)) < FLT_EPSILON);
|
2010-08-09 07:01:59 +00:00
|
|
|
}
|
|
|
|
|
2011-06-15 06:54:12 +00:00
|
|
|
/**
|
|
|
|
* Checks if an integer value is not equal to a decimal value.
|
|
|
|
*
|
|
|
|
* \param [in] a The first value to check.
|
|
|
|
*
|
|
|
|
* \param [in] b The second value to check.
|
|
|
|
*
|
|
|
|
* \return A pointer to a boolean value indicating if \a is not equal to \a b.
|
|
|
|
*/
|
|
|
|
ValueObject *opNeqIntegerFloat(ValueObject *a,
|
|
|
|
ValueObject *b)
|
2010-08-09 07:01:59 +00:00
|
|
|
{
|
2010-12-21 08:20:31 +00:00
|
|
|
return createBooleanValueObject(fabs((float)(getInteger(a)) - getFloat(b)) > FLT_EPSILON);
|
2010-08-09 07:01:59 +00:00
|
|
|
}
|
|
|
|
|
2011-06-15 06:54:12 +00:00
|
|
|
/**
|
|
|
|
* Checks if a decimal value is equal to an integer value.
|
|
|
|
*
|
|
|
|
* \param [in] a The first value to check.
|
|
|
|
*
|
|
|
|
* \param [in] b The second value to check.
|
|
|
|
*
|
|
|
|
* \return A pointer to a boolean value indicating if \a is equal to \a b.
|
|
|
|
*/
|
|
|
|
ValueObject *opEqFloatInteger(ValueObject *a,
|
|
|
|
ValueObject *b)
|
2010-08-09 07:01:59 +00:00
|
|
|
{
|
2010-12-21 08:20:31 +00:00
|
|
|
return createBooleanValueObject(fabs(getFloat(a) - (float)(getInteger(b))) < FLT_EPSILON);
|
2010-08-09 07:01:59 +00:00
|
|
|
}
|
|
|
|
|
2011-06-15 06:54:12 +00:00
|
|
|
/**
|
|
|
|
* Checks if a decimal value is not equal to an integer value.
|
|
|
|
*
|
|
|
|
* \param [in] a The first value to check.
|
|
|
|
*
|
|
|
|
* \param [in] b The second value to check.
|
|
|
|
*
|
|
|
|
* \return A pointer to a boolean value indicating if \a is not equal to \a b.
|
|
|
|
*/
|
|
|
|
ValueObject *opNeqFloatInteger(ValueObject *a,
|
|
|
|
ValueObject *b)
|
2010-08-09 07:01:59 +00:00
|
|
|
{
|
2010-12-21 08:20:31 +00:00
|
|
|
return createBooleanValueObject(fabs(getFloat(a) - (float)(getInteger(b))) > FLT_EPSILON);
|
2010-08-09 07:01:59 +00:00
|
|
|
}
|
|
|
|
|
2011-06-15 06:54:12 +00:00
|
|
|
/**
|
|
|
|
* Checks if a decimal value is equal to another decimal value.
|
|
|
|
*
|
|
|
|
* \param [in] a The first value to check.
|
|
|
|
*
|
|
|
|
* \param [in] b The second value to check.
|
|
|
|
*
|
|
|
|
* \return A pointer to a boolean value indicating if \a is equal to \a b.
|
|
|
|
*/
|
|
|
|
ValueObject *opEqFloatFloat(ValueObject *a,
|
|
|
|
ValueObject *b)
|
2010-08-09 07:01:59 +00:00
|
|
|
{
|
2010-12-21 08:20:31 +00:00
|
|
|
return createBooleanValueObject(fabs(getFloat(a) - getFloat(b)) < FLT_EPSILON);
|
2010-08-09 07:01:59 +00:00
|
|
|
}
|
|
|
|
|
2011-06-15 06:54:12 +00:00
|
|
|
/**
|
|
|
|
* Checks if a decimal value is not equal to another decimal value.
|
|
|
|
*
|
|
|
|
* \param [in] a The first value to check.
|
|
|
|
*
|
|
|
|
* \param [in] b The second value to check.
|
|
|
|
*
|
|
|
|
* \return A pointer to a boolean value indicating if \a is not equal to \a b.
|
|
|
|
*/
|
|
|
|
ValueObject *opNeqFloatFloat(ValueObject *a,
|
|
|
|
ValueObject *b)
|
2010-08-09 07:01:59 +00:00
|
|
|
{
|
2010-12-21 08:20:31 +00:00
|
|
|
return createBooleanValueObject(fabs(getFloat(a) - getFloat(b)) > FLT_EPSILON);
|
2010-08-09 07:01:59 +00:00
|
|
|
}
|
|
|
|
|
2011-06-15 06:54:12 +00:00
|
|
|
/**
|
|
|
|
* Checks if a boolean value is equal to another boolean value.
|
|
|
|
*
|
|
|
|
* \param [in] a The first value to check.
|
|
|
|
*
|
|
|
|
* \param [in] b The second value to check.
|
|
|
|
*
|
|
|
|
* \return A pointer to a boolean value indicating if \a is equal to \a b.
|
|
|
|
*/
|
|
|
|
ValueObject *opEqBooleanBoolean(ValueObject *a,
|
|
|
|
ValueObject *b)
|
2010-08-09 07:01:59 +00:00
|
|
|
{
|
|
|
|
return createBooleanValueObject(getInteger(a) == getInteger(b));
|
|
|
|
}
|
|
|
|
|
2011-06-15 06:54:12 +00:00
|
|
|
/**
|
|
|
|
* Checks if a boolean value is not equal to another boolean value.
|
|
|
|
*
|
|
|
|
* \param [in] a The first value to check.
|
|
|
|
*
|
|
|
|
* \param [in] b The second value to check.
|
|
|
|
*
|
|
|
|
* \return A pointer to a boolean value indicating if \a is not equal to \a b.
|
|
|
|
*/
|
|
|
|
ValueObject *opNeqBooleanBoolean(ValueObject *a,
|
|
|
|
ValueObject *b)
|
2010-08-09 07:01:59 +00:00
|
|
|
{
|
|
|
|
return createBooleanValueObject(getInteger(a) != getInteger(b));
|
|
|
|
}
|
|
|
|
|
2011-06-15 06:54:12 +00:00
|
|
|
/**
|
|
|
|
* Checks if a string value is equal to another string value.
|
|
|
|
*
|
|
|
|
* \param [in] a The first value to check.
|
|
|
|
*
|
|
|
|
* \param [in] b The second value to check.
|
|
|
|
*
|
|
|
|
* \return A pointer to a boolean value indicating if \a is equal to \a b.
|
|
|
|
*/
|
|
|
|
ValueObject *opEqStringString(ValueObject *a,
|
|
|
|
ValueObject *b)
|
2010-08-09 07:01:59 +00:00
|
|
|
{
|
|
|
|
return createBooleanValueObject(strcmp(getString(a), getString(b)) == 0);
|
|
|
|
}
|
|
|
|
|
2011-06-15 06:54:12 +00:00
|
|
|
/**
|
|
|
|
* Checks if a string value is not equal to another string value.
|
|
|
|
*
|
|
|
|
* \param [in] a The first value to check.
|
|
|
|
*
|
|
|
|
* \param [in] b The second value to check.
|
|
|
|
*
|
|
|
|
* \return A pointer to a boolean value indicating if \a is not equal to \a b.
|
|
|
|
*/
|
|
|
|
ValueObject *opNeqStringString(ValueObject *a,
|
|
|
|
ValueObject *b)
|
2010-08-09 07:01:59 +00:00
|
|
|
{
|
|
|
|
return createBooleanValueObject(strcmp(getString(a), getString(b)) != 0);
|
|
|
|
}
|
|
|
|
|
2011-06-15 06:54:12 +00:00
|
|
|
/**
|
|
|
|
* Returns true because two nil values are always equal.
|
|
|
|
*
|
|
|
|
* \param [in] a Not used.
|
|
|
|
*
|
|
|
|
* \param [in] b Not used.
|
|
|
|
*
|
|
|
|
* \return A true boolean value.
|
|
|
|
*/
|
|
|
|
ValueObject *opEqNilNil(ValueObject *a,
|
|
|
|
ValueObject *b)
|
2010-08-09 07:01:59 +00:00
|
|
|
{
|
|
|
|
a = NULL;
|
|
|
|
b = NULL;
|
|
|
|
return createBooleanValueObject(1);
|
|
|
|
}
|
|
|
|
|
2011-06-15 06:54:12 +00:00
|
|
|
/**
|
|
|
|
* Returns false because two nil values are never not equal.
|
|
|
|
*
|
|
|
|
* \param [in] a Not used.
|
|
|
|
*
|
|
|
|
* \param [in] b Not used.
|
|
|
|
*
|
|
|
|
* \return A false boolean value.
|
|
|
|
*/
|
|
|
|
ValueObject *opNeqNilNil(ValueObject *a,
|
|
|
|
ValueObject *b)
|
2010-08-09 07:01:59 +00:00
|
|
|
{
|
|
|
|
a = NULL;
|
|
|
|
b = NULL;
|
|
|
|
return createBooleanValueObject(0);
|
|
|
|
}
|
|
|
|
|
2011-06-15 06:54:12 +00:00
|
|
|
/*
|
|
|
|
* A jump table for boolean operations. The first index determines the
|
|
|
|
* particular boolean operation to perform, the second index determines the type
|
|
|
|
* of the first argument, and the third index determines the type of the second
|
|
|
|
* object.
|
|
|
|
*/
|
2010-08-09 07:01:59 +00:00
|
|
|
static ValueObject *(*BoolOpJumpTable[2][5][5])(ValueObject *, ValueObject *) = {
|
|
|
|
{ /* OP_EQ */
|
|
|
|
/* Integer, Float, Boolean, String, Nil */
|
|
|
|
/* Integer */ { opEqIntegerInteger, opEqIntegerFloat, NULL, NULL, NULL },
|
|
|
|
/* Float */ { opEqFloatInteger, opEqFloatFloat, NULL, NULL, NULL },
|
|
|
|
/* Boolean */ { NULL, NULL, opEqBooleanBoolean, NULL, NULL },
|
|
|
|
/* String */ { NULL, NULL, NULL, opEqStringString, NULL },
|
|
|
|
/* Nil */ { NULL, NULL, NULL, NULL, opEqNilNil }
|
|
|
|
},
|
|
|
|
{ /* OP_NEQ */
|
|
|
|
/* Integer, Float, Boolean, String, Nil */
|
|
|
|
/* Integer */ { opNeqIntegerInteger, opNeqIntegerFloat, NULL, NULL, NULL },
|
|
|
|
/* Float */ { opNeqFloatInteger, opNeqFloatFloat, NULL, NULL, NULL },
|
|
|
|
/* Boolean */ { NULL, NULL, opNeqBooleanBoolean, NULL, NULL },
|
|
|
|
/* String */ { NULL, NULL, NULL, opNeqStringString, NULL },
|
|
|
|
/* Nil */ { NULL, NULL, NULL, NULL, opNeqNilNil }
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2011-06-15 06:54:12 +00:00
|
|
|
/**
|
|
|
|
* Interprets an equality operation.
|
|
|
|
*
|
|
|
|
* \param [in] expr The operation to interpret.
|
|
|
|
*
|
|
|
|
* \param [in] scope The scope to evaluate \a expr under.
|
|
|
|
*
|
|
|
|
* \return A pointer to the resulting value of the equality operation.
|
|
|
|
*
|
|
|
|
* \retval NULL An error occurred during interpretation.
|
|
|
|
*/
|
|
|
|
ValueObject *interpretEqualityOpExprNode(OpExprNode *expr,
|
|
|
|
ScopeObject *scope)
|
2010-08-09 07:01:59 +00:00
|
|
|
{
|
|
|
|
ValueObject *val1 = interpretExprNode(expr->args->exprs[0], scope);
|
|
|
|
ValueObject *val2 = interpretExprNode(expr->args->exprs[1], scope);
|
|
|
|
ValueObject *ret = NULL;
|
|
|
|
if (!val1 || !val2) {
|
|
|
|
deleteValueObject(val1);
|
|
|
|
deleteValueObject(val2);
|
|
|
|
return NULL;
|
|
|
|
}
|
2011-06-15 06:54:12 +00:00
|
|
|
/*
|
|
|
|
* Since there is no automatic casting, an equality (inequality) test
|
|
|
|
* against a non-number type will always fail (succeed).
|
|
|
|
*/
|
2010-08-09 07:01:59 +00:00
|
|
|
if ((val1->type != val2->type)
|
|
|
|
&& ((val1->type != VT_INTEGER && val1->type != VT_FLOAT)
|
|
|
|
|| (val2->type != VT_INTEGER && val2->type != VT_FLOAT))) {
|
|
|
|
switch (expr->type) {
|
|
|
|
case OP_EQ:
|
|
|
|
ret = createBooleanValueObject(0);
|
|
|
|
break;
|
|
|
|
case OP_NEQ:
|
|
|
|
ret = createBooleanValueObject(1);
|
|
|
|
break;
|
|
|
|
default:
|
2012-12-13 04:53:54 +00:00
|
|
|
error(IN_INVALID_EQUALITY_OPERATION_TYPE);
|
2010-08-09 07:01:59 +00:00
|
|
|
deleteValueObject(val1);
|
|
|
|
deleteValueObject(val2);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
}
|
2015-07-19 18:22:56 +00:00
|
|
|
else {
|
|
|
|
/* If comparing strings, interpolate them first */
|
|
|
|
ValueObject *old1 = val1;
|
|
|
|
ValueObject *old2 = val2;
|
|
|
|
if (val1->type == VT_STRING) {
|
|
|
|
val1 = castStringExplicit(val1, scope);
|
|
|
|
val2 = castStringExplicit(val2, scope);
|
|
|
|
deleteValueObject(old1);
|
|
|
|
deleteValueObject(old2);
|
|
|
|
}
|
2010-08-09 07:01:59 +00:00
|
|
|
ret = BoolOpJumpTable[expr->type - OP_EQ][val1->type][val2->type](val1, val2);
|
2015-07-19 18:22:56 +00:00
|
|
|
}
|
2010-08-09 07:01:59 +00:00
|
|
|
deleteValueObject(val1);
|
|
|
|
deleteValueObject(val2);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2011-06-15 06:54:12 +00:00
|
|
|
/**
|
|
|
|
* Interprets a concatenation operation.
|
|
|
|
*
|
|
|
|
* \param [in] expr The operation to interpret.
|
|
|
|
*
|
|
|
|
* \param [in] scope The scope to evaluate \a expr under.
|
|
|
|
*
|
|
|
|
* \return A pointer to the resulting value of the concatenation operation.
|
|
|
|
*
|
|
|
|
* \retval NULL An error occurred during interpretation.
|
|
|
|
*/
|
|
|
|
ValueObject *interpretConcatOpExprNode(OpExprNode *expr,
|
|
|
|
ScopeObject *scope)
|
2010-08-09 07:01:59 +00:00
|
|
|
{
|
|
|
|
unsigned int n;
|
|
|
|
/* Start out with the first string to concatenate. */
|
|
|
|
ValueObject *val = interpretExprNode(expr->args->exprs[0], scope);
|
|
|
|
ValueObject *use = castStringImplicit(val, scope);
|
|
|
|
char *acc = NULL;
|
|
|
|
void *mem = NULL;
|
|
|
|
if (!val || !use) {
|
|
|
|
deleteValueObject(val);
|
|
|
|
deleteValueObject(use);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
/* Start out an accumulator with the first string. */
|
|
|
|
mem = realloc(acc, sizeof(char) * (strlen(getString(use)) + 1));
|
|
|
|
if (!mem) {
|
|
|
|
perror("realloc");
|
|
|
|
deleteValueObject(val);
|
|
|
|
deleteValueObject(use);
|
|
|
|
free(acc);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
acc = mem;
|
|
|
|
acc[0] = '\0';
|
|
|
|
strcat(acc, getString(use));
|
|
|
|
deleteValueObject(val);
|
|
|
|
deleteValueObject(use);
|
|
|
|
for (n = 1; n < expr->args->num; n++) {
|
|
|
|
/* Grab the next string to concatenate. */
|
|
|
|
val = interpretExprNode(expr->args->exprs[n], scope);
|
|
|
|
use = castStringImplicit(val, scope);
|
|
|
|
if (!val || !use) {
|
|
|
|
deleteValueObject(val);
|
|
|
|
deleteValueObject(use);
|
|
|
|
free(acc);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
/* Add the next string to the accumulator. */
|
|
|
|
mem = realloc(acc, sizeof(char) * (strlen(acc) + strlen(getString(use)) + 1));
|
|
|
|
if (!mem) {
|
|
|
|
perror("realloc");
|
|
|
|
deleteValueObject(val);
|
|
|
|
deleteValueObject(use);
|
|
|
|
free(acc);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
acc = mem;
|
|
|
|
strcat(acc, getString(use));
|
|
|
|
deleteValueObject(val);
|
|
|
|
deleteValueObject(use);
|
|
|
|
}
|
|
|
|
return createStringValueObject(acc);
|
|
|
|
}
|
|
|
|
|
2011-06-15 06:54:12 +00:00
|
|
|
/*
|
|
|
|
* A jump table for operations. The index of a function in the table is given
|
|
|
|
* by its its index in the enumerated OpType type.
|
|
|
|
*/
|
2010-08-09 07:01:59 +00:00
|
|
|
static ValueObject *(*OpExprJumpTable[14])(OpExprNode *, ScopeObject *) = {
|
|
|
|
interpretArithOpExprNode,
|
|
|
|
interpretArithOpExprNode,
|
|
|
|
interpretArithOpExprNode,
|
|
|
|
interpretArithOpExprNode,
|
|
|
|
interpretArithOpExprNode,
|
|
|
|
interpretArithOpExprNode,
|
|
|
|
interpretArithOpExprNode,
|
|
|
|
interpretBoolOpExprNode,
|
|
|
|
interpretBoolOpExprNode,
|
|
|
|
interpretBoolOpExprNode,
|
|
|
|
interpretNotOpExprNode,
|
|
|
|
interpretEqualityOpExprNode,
|
|
|
|
interpretEqualityOpExprNode,
|
|
|
|
interpretConcatOpExprNode };
|
|
|
|
|
2011-06-15 06:54:12 +00:00
|
|
|
/**
|
|
|
|
* Interprets an operation.
|
|
|
|
*
|
|
|
|
* \param [in] node The operation to interpret.
|
|
|
|
*
|
|
|
|
* \param [in] scope The scope to evaluate \a expr under.
|
|
|
|
*
|
|
|
|
* \return A pointer to the resulting value of the operation.
|
|
|
|
*
|
|
|
|
* \retval NULL An error occurred during interpretation.
|
|
|
|
*/
|
|
|
|
ValueObject *interpretOpExprNode(ExprNode *node,
|
|
|
|
ScopeObject *scope)
|
2010-08-09 07:01:59 +00:00
|
|
|
{
|
|
|
|
OpExprNode *expr = (OpExprNode *)node->expr;
|
|
|
|
return OpExprJumpTable[expr->type](expr, scope);
|
|
|
|
}
|
|
|
|
|
2011-06-15 06:54:12 +00:00
|
|
|
/*
|
|
|
|
* A jump table for expressions. The index of a function in the table is given
|
|
|
|
* by its its index in the enumerated ExprType type.
|
|
|
|
*/
|
2014-05-10 03:59:18 +00:00
|
|
|
static ValueObject *(*ExprJumpTable[7])(ExprNode *, ScopeObject *) = {
|
2010-08-09 07:01:59 +00:00
|
|
|
interpretCastExprNode,
|
|
|
|
interpretConstantExprNode,
|
|
|
|
interpretIdentifierExprNode,
|
|
|
|
interpretFuncCallExprNode,
|
|
|
|
interpretOpExprNode,
|
2014-05-10 03:59:18 +00:00
|
|
|
interpretImpVarExprNode,
|
|
|
|
interpretSystemCommandExprNode };
|
2010-08-09 07:01:59 +00:00
|
|
|
|
2011-06-15 06:54:12 +00:00
|
|
|
/**
|
|
|
|
* Interprets an expression.
|
|
|
|
*
|
|
|
|
* \param [in] node The expression to interpret.
|
|
|
|
*
|
|
|
|
* \param [in] scope The scope to evaluate \a expr under.
|
|
|
|
*
|
|
|
|
* \return A pointer to the value of \a expr evaluated under \a scope.
|
|
|
|
*
|
|
|
|
* \retval NULL An error occurred during interpretation.
|
|
|
|
*/
|
|
|
|
ValueObject *interpretExprNode(ExprNode *node,
|
|
|
|
ScopeObject *scope)
|
2010-08-09 07:01:59 +00:00
|
|
|
{
|
|
|
|
return ExprJumpTable[node->type](node, scope);
|
|
|
|
}
|
|
|
|
|
2011-06-15 06:54:12 +00:00
|
|
|
/**
|
|
|
|
* Interprets a cast statement.
|
|
|
|
*
|
|
|
|
* \param [in] node The statement to interpret.
|
|
|
|
*
|
|
|
|
* \param [in] scope The scope to evaluate \a node under.
|
|
|
|
*
|
|
|
|
* \pre \a node contains a statement created by createCastStmtNode().
|
|
|
|
*
|
|
|
|
* \return A pointer to a default return value.
|
|
|
|
*
|
|
|
|
* \retval NULL An error occurred during interpretation.
|
|
|
|
*/
|
|
|
|
ReturnObject *interpretCastStmtNode(StmtNode *node,
|
|
|
|
ScopeObject *scope)
|
2010-08-09 07:01:59 +00:00
|
|
|
{
|
|
|
|
CastStmtNode *stmt = (CastStmtNode *)node->stmt;
|
2011-06-15 06:54:12 +00:00
|
|
|
ValueObject *val = getScopeValue(scope, scope, stmt->target);
|
2010-08-09 07:01:59 +00:00
|
|
|
ValueObject *cast = NULL;
|
|
|
|
if (!val) {
|
|
|
|
IdentifierNode *id = (IdentifierNode *)(stmt->target);
|
2010-12-20 23:30:37 +00:00
|
|
|
char *name = resolveIdentifierName(id, scope);
|
|
|
|
if (name) {
|
2012-12-13 04:53:54 +00:00
|
|
|
error(IN_VARIABLE_DOES_NOT_EXIST, id->fname, id->line, name);
|
2010-12-20 23:30:37 +00:00
|
|
|
free(name);
|
|
|
|
}
|
2010-08-09 07:01:59 +00:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
switch(stmt->newtype->type) {
|
|
|
|
case CT_NIL:
|
|
|
|
if (!(cast = createNilValueObject())) return NULL;
|
|
|
|
break;
|
|
|
|
case CT_BOOLEAN:
|
|
|
|
if (!(cast = castBooleanExplicit(val, scope))) return NULL;
|
|
|
|
break;
|
|
|
|
case CT_INTEGER:
|
|
|
|
if (!(cast = castIntegerExplicit(val, scope))) return NULL;
|
|
|
|
break;
|
|
|
|
case CT_FLOAT:
|
|
|
|
if (!(cast = castFloatExplicit(val, scope))) return NULL;
|
|
|
|
break;
|
|
|
|
case CT_STRING:
|
|
|
|
if (!(cast = castStringExplicit(val, scope))) return NULL;
|
|
|
|
break;
|
2013-12-30 17:45:34 +00:00
|
|
|
case CT_ARRAY: {
|
|
|
|
IdentifierNode *id = (IdentifierNode *)(stmt->target);
|
|
|
|
char *name = resolveIdentifierName(id, scope);
|
|
|
|
if (name) {
|
|
|
|
error(IN_CANNOT_CAST_VALUE_TO_ARRAY, id->fname, id->line, name);
|
|
|
|
free(name);
|
|
|
|
}
|
|
|
|
return NULL;
|
|
|
|
break;
|
|
|
|
}
|
2010-08-09 07:01:59 +00:00
|
|
|
}
|
2011-06-15 06:54:12 +00:00
|
|
|
if (!updateScopeValue(scope, scope, stmt->target, cast)) {
|
2010-08-09 07:01:59 +00:00
|
|
|
deleteValueObject(cast);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
return createReturnObject(RT_DEFAULT, NULL);
|
|
|
|
}
|
|
|
|
|
2011-06-15 06:54:12 +00:00
|
|
|
/**
|
|
|
|
* Interprets a print statement.
|
|
|
|
*
|
|
|
|
* \param [in] node The statement to interpret.
|
|
|
|
*
|
|
|
|
* \param [in] scope The scope to evaluate \a node under.
|
|
|
|
*
|
|
|
|
* \pre \a node contains a statement created by createPrintStmtNode().
|
|
|
|
*
|
|
|
|
* \return A pointer to a default return value.
|
|
|
|
*
|
|
|
|
* \retval NULL An error occurred during interpretation.
|
|
|
|
*/
|
|
|
|
ReturnObject *interpretPrintStmtNode(StmtNode *node,
|
|
|
|
ScopeObject *scope)
|
2010-08-09 07:01:59 +00:00
|
|
|
{
|
|
|
|
PrintStmtNode *stmt = (PrintStmtNode *)node->stmt;
|
|
|
|
unsigned int n;
|
|
|
|
for (n = 0; n < stmt->args->num; n++) {
|
|
|
|
ValueObject *val = interpretExprNode(stmt->args->exprs[n], scope);
|
|
|
|
ValueObject *use = castStringImplicit(val, scope);
|
|
|
|
if (!val || !use) {
|
|
|
|
deleteValueObject(val);
|
|
|
|
deleteValueObject(use);
|
|
|
|
return NULL;
|
|
|
|
}
|
2015-08-13 22:24:26 +00:00
|
|
|
fprintf(stmt->file, "%s", getString(use));
|
2010-08-09 07:01:59 +00:00
|
|
|
deleteValueObject(val);
|
|
|
|
deleteValueObject(use);
|
|
|
|
}
|
|
|
|
if (!stmt->nonl)
|
2015-08-13 22:24:26 +00:00
|
|
|
putc('\n', stmt->file);
|
2010-08-09 07:01:59 +00:00
|
|
|
return createReturnObject(RT_DEFAULT, NULL);
|
|
|
|
}
|
|
|
|
|
2011-06-15 06:54:12 +00:00
|
|
|
/**
|
|
|
|
* Interprets an input statement.
|
|
|
|
*
|
|
|
|
* \param [in] node The statement to interpret.
|
|
|
|
*
|
|
|
|
* \param [in] scope The scope to evaluate \a node under.
|
|
|
|
*
|
|
|
|
* \pre \a node contains a statement created by createInputStmtNode().
|
|
|
|
*
|
|
|
|
* \return A pointer to a default return value.
|
|
|
|
*
|
|
|
|
* \retval NULL An error occurred during interpretation.
|
|
|
|
*/
|
|
|
|
ReturnObject *interpretInputStmtNode(StmtNode *node,
|
|
|
|
ScopeObject *scope)
|
2010-08-09 07:01:59 +00:00
|
|
|
{
|
|
|
|
unsigned int size = 16;
|
|
|
|
unsigned int cur = 0;
|
|
|
|
char *temp = malloc(sizeof(char) * size);
|
2010-12-21 08:20:31 +00:00
|
|
|
int c;
|
2010-08-09 07:01:59 +00:00
|
|
|
void *mem = NULL;
|
|
|
|
InputStmtNode *stmt = (InputStmtNode *)node->stmt;
|
|
|
|
ValueObject *val = NULL;
|
2011-06-29 20:25:56 +00:00
|
|
|
while ((c = getchar()) && !feof(stdin)) {
|
2011-06-15 06:54:12 +00:00
|
|
|
/**
|
|
|
|
* \note The specification is unclear as to the exact semantics
|
|
|
|
* of input. Here, we read up until the first newline or EOF
|
|
|
|
* but do not store it.
|
|
|
|
*/
|
2010-12-21 08:20:31 +00:00
|
|
|
if (c == EOF || c == (int)'\r' || c == (int)'\n') break;
|
2016-03-27 20:23:57 +00:00
|
|
|
temp[cur] = (char)c;
|
|
|
|
cur++;
|
2015-07-27 04:39:37 +00:00
|
|
|
/* Reserve space to escape colon in input */
|
|
|
|
if (c == ':') {
|
|
|
|
cur++;
|
|
|
|
}
|
2010-08-09 07:01:59 +00:00
|
|
|
if (cur > size - 1) {
|
2015-07-27 04:39:37 +00:00
|
|
|
/* Increase buffer size */
|
2010-08-09 07:01:59 +00:00
|
|
|
size *= 2;
|
|
|
|
mem = realloc(temp, sizeof(char) * size);
|
|
|
|
if (!mem) {
|
|
|
|
perror("realloc");
|
|
|
|
free(temp);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
temp = mem;
|
|
|
|
}
|
2015-07-27 04:39:37 +00:00
|
|
|
/* Escape colon in input */
|
|
|
|
if (c == ':') {
|
|
|
|
temp[cur - 1] = ':';
|
|
|
|
}
|
2010-08-09 07:01:59 +00:00
|
|
|
}
|
|
|
|
temp[cur] = '\0';
|
|
|
|
val = createStringValueObject(temp);
|
|
|
|
if (!val) {
|
|
|
|
free(temp);
|
|
|
|
return NULL;
|
|
|
|
}
|
2011-06-15 06:54:12 +00:00
|
|
|
if (!updateScopeValue(scope, scope, stmt->target, val)) {
|
2010-08-09 07:01:59 +00:00
|
|
|
deleteValueObject(val);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
return createReturnObject(RT_DEFAULT, NULL);
|
|
|
|
}
|
|
|
|
|
2011-06-15 06:54:12 +00:00
|
|
|
/**
|
|
|
|
* Interprets an assignment statement.
|
|
|
|
*
|
|
|
|
* \param [in] node The statement to interpret.
|
|
|
|
*
|
|
|
|
* \param [in] scope The scope to evaluate \a node under.
|
|
|
|
*
|
|
|
|
* \pre \a node contains a statement created by createAssignmentStmtNode().
|
|
|
|
*
|
|
|
|
* \return A pointer to a default return value.
|
|
|
|
*
|
|
|
|
* \retval NULL An error occurred during interpretation.
|
|
|
|
*/
|
|
|
|
ReturnObject *interpretAssignmentStmtNode(StmtNode *node,
|
|
|
|
ScopeObject *scope)
|
2010-08-09 07:01:59 +00:00
|
|
|
{
|
|
|
|
AssignmentStmtNode *stmt = (AssignmentStmtNode *)node->stmt;
|
|
|
|
ValueObject *val = interpretExprNode(stmt->expr, scope);
|
2016-03-27 21:03:39 +00:00
|
|
|
if (!val) return NULL;
|
2016-03-27 20:56:22 +00:00
|
|
|
/* interpolate assigned strings */
|
|
|
|
if (val->type == VT_STRING) {
|
|
|
|
ValueObject *use = castStringImplicit(val, scope);
|
|
|
|
deleteValueObject(val);
|
2016-03-27 21:03:39 +00:00
|
|
|
if (!use) return NULL;
|
2016-03-27 20:56:22 +00:00
|
|
|
val = use;
|
|
|
|
}
|
2011-06-15 06:54:12 +00:00
|
|
|
if (!updateScopeValue(scope, scope, stmt->target, val)) {
|
2010-08-09 07:01:59 +00:00
|
|
|
deleteValueObject(val);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
return createReturnObject(RT_DEFAULT, NULL);
|
|
|
|
}
|
|
|
|
|
2011-06-15 06:54:12 +00:00
|
|
|
/**
|
|
|
|
* Interprets a declaration statement.
|
|
|
|
*
|
|
|
|
* \param [in] node The statement to interpret.
|
|
|
|
*
|
|
|
|
* \param [in] scope The scope to evaluate \a node under.
|
|
|
|
*
|
|
|
|
* \pre \a node contains a statement created by createDeclarationStmtNode().
|
|
|
|
*
|
|
|
|
* \return A pointer to a default return value.
|
|
|
|
*
|
|
|
|
* \retval NULL An error occurred during interpretation.
|
|
|
|
*/
|
|
|
|
ReturnObject *interpretDeclarationStmtNode(StmtNode *node,
|
|
|
|
ScopeObject *scope)
|
2010-08-09 07:01:59 +00:00
|
|
|
{
|
|
|
|
DeclarationStmtNode *stmt = (DeclarationStmtNode *)node->stmt;
|
|
|
|
ValueObject *init = NULL;
|
2011-06-15 06:54:12 +00:00
|
|
|
ScopeObject *dest = NULL;
|
|
|
|
dest = getScopeObject(scope, scope, stmt->scope);
|
2011-12-23 16:09:11 +00:00
|
|
|
if (!dest) return NULL;
|
2011-06-15 06:54:12 +00:00
|
|
|
if (getScopeValueLocal(scope, dest, stmt->target)) {
|
2010-08-09 07:01:59 +00:00
|
|
|
IdentifierNode *id = (IdentifierNode *)(stmt->target);
|
2010-12-20 23:30:37 +00:00
|
|
|
char *name = resolveIdentifierName(id, scope);
|
|
|
|
if (name) {
|
2012-12-13 04:53:54 +00:00
|
|
|
error(IN_REDEFINITION_OF_VARIABLE, id->fname, id->line, name);
|
2010-12-20 23:30:37 +00:00
|
|
|
free(name);
|
|
|
|
}
|
2010-08-09 07:01:59 +00:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
if (stmt->expr)
|
|
|
|
init = interpretExprNode(stmt->expr, scope);
|
2010-09-02 03:06:15 +00:00
|
|
|
else if (stmt->type) {
|
|
|
|
switch (stmt->type->type) {
|
|
|
|
case CT_NIL:
|
|
|
|
init = createNilValueObject();
|
|
|
|
break;
|
|
|
|
case CT_BOOLEAN:
|
|
|
|
init = createBooleanValueObject(0);
|
|
|
|
break;
|
|
|
|
case CT_INTEGER:
|
|
|
|
init = createIntegerValueObject(0);
|
|
|
|
break;
|
|
|
|
case CT_FLOAT:
|
|
|
|
init = createFloatValueObject(0.0);
|
|
|
|
break;
|
|
|
|
case CT_STRING:
|
2011-06-15 06:54:12 +00:00
|
|
|
init = createStringValueObject(copyString(""));
|
|
|
|
break;
|
|
|
|
case CT_ARRAY:
|
|
|
|
init = createArrayValueObject(scope);
|
2010-09-02 03:06:15 +00:00
|
|
|
break;
|
2011-06-15 06:54:12 +00:00
|
|
|
default:
|
2012-12-13 04:53:54 +00:00
|
|
|
error(IN_INVALID_DECLARATION_TYPE);
|
2011-06-15 06:54:12 +00:00
|
|
|
return NULL;
|
2010-09-02 03:06:15 +00:00
|
|
|
}
|
|
|
|
}
|
2011-12-23 16:09:11 +00:00
|
|
|
else if (stmt->parent) {
|
2011-10-28 22:31:36 +00:00
|
|
|
ScopeObject *parent = getScopeObject(scope, scope, stmt->parent);
|
|
|
|
if (!parent) return NULL;
|
|
|
|
init = createArrayValueObject(parent);
|
|
|
|
}
|
2010-08-09 07:01:59 +00:00
|
|
|
else
|
|
|
|
init = createNilValueObject();
|
|
|
|
if (!init) return NULL;
|
2011-06-15 06:54:12 +00:00
|
|
|
if (!createScopeValue(scope, dest, stmt->target)) {
|
2010-08-09 07:01:59 +00:00
|
|
|
deleteValueObject(init);
|
|
|
|
return NULL;
|
|
|
|
}
|
2011-06-15 06:54:12 +00:00
|
|
|
if (!updateScopeValue(scope, dest, stmt->target, init)) {
|
2010-08-09 07:01:59 +00:00
|
|
|
deleteValueObject(init);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
return createReturnObject(RT_DEFAULT, NULL);
|
|
|
|
}
|
|
|
|
|
2011-06-15 06:54:12 +00:00
|
|
|
/**
|
|
|
|
* Interprets an if/then/else statement.
|
|
|
|
*
|
|
|
|
* \param [in] node The statement to interpret.
|
|
|
|
*
|
|
|
|
* \param [in] scope The scope to evaluate \a node under.
|
|
|
|
*
|
|
|
|
* \pre \a node contains a statement created by createIfThenElseStmtNode().
|
|
|
|
*
|
|
|
|
* \return A pointer to a default return value.
|
|
|
|
*
|
|
|
|
* \retval NULL An error occurred during interpretation.
|
|
|
|
*/
|
|
|
|
ReturnObject *interpretIfThenElseStmtNode(StmtNode *node,
|
|
|
|
ScopeObject *scope)
|
2010-08-09 07:01:59 +00:00
|
|
|
{
|
|
|
|
IfThenElseStmtNode *stmt = (IfThenElseStmtNode *)node->stmt;
|
|
|
|
ValueObject *use1 = scope->impvar;
|
|
|
|
int use1val;
|
|
|
|
unsigned int cast1 = 0;
|
|
|
|
BlockNode *path = NULL;
|
|
|
|
if (scope->impvar->type != VT_BOOLEAN && scope->impvar->type != VT_INTEGER) {
|
|
|
|
use1 = castBooleanImplicit(scope->impvar, scope);
|
|
|
|
if (!use1) return NULL;
|
|
|
|
cast1 = 1;
|
|
|
|
}
|
|
|
|
use1val = getInteger(use1);
|
|
|
|
if (cast1) deleteValueObject(use1);
|
|
|
|
/* Determine which block of code to execute */
|
|
|
|
if (use1val)
|
|
|
|
path = stmt->yes;
|
|
|
|
else {
|
|
|
|
unsigned int n;
|
|
|
|
for (n = 0; n < stmt->guards->num; n++) {
|
|
|
|
ValueObject *val = interpretExprNode(stmt->guards->exprs[n], scope);
|
|
|
|
ValueObject *use2 = val;
|
|
|
|
int use2val;
|
|
|
|
unsigned int cast2 = 0;
|
|
|
|
if (!val) return NULL;
|
|
|
|
if (val->type != VT_BOOLEAN && val->type != VT_INTEGER) {
|
|
|
|
use2 = castBooleanImplicit(val, scope);
|
|
|
|
if (!use2) {
|
|
|
|
deleteValueObject(val);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
cast2 = 1;
|
|
|
|
}
|
|
|
|
use2val = getInteger(use2);
|
|
|
|
deleteValueObject(val);
|
|
|
|
if (cast2) deleteValueObject(use2);
|
|
|
|
if (use2val) {
|
|
|
|
path = stmt->blocks->blocks[n];
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
/* Reached the end without satisfying any guard */
|
|
|
|
if (n == stmt->guards->num)
|
|
|
|
path = stmt->no;
|
|
|
|
}
|
|
|
|
/* Interpret a path if one was reached */
|
|
|
|
if (path) {
|
|
|
|
ReturnObject *r = interpretBlockNode(path, scope);
|
|
|
|
if (!r)
|
|
|
|
return NULL;
|
|
|
|
/* Pass this up to the outer block to handle. */
|
|
|
|
else if (r->type == RT_BREAK || r->type == RT_RETURN)
|
|
|
|
return r;
|
|
|
|
else
|
|
|
|
deleteReturnObject(r);
|
|
|
|
}
|
|
|
|
return createReturnObject(RT_DEFAULT, NULL);
|
|
|
|
}
|
|
|
|
|
2011-06-15 06:54:12 +00:00
|
|
|
/**
|
|
|
|
* Interprets a switch statement.
|
|
|
|
*
|
|
|
|
* \param [in] node The statement to interpret.
|
|
|
|
*
|
|
|
|
* \param [in] scope The scope to evaluate \a node under.
|
|
|
|
*
|
|
|
|
* \pre \a node contains a statement created by createSwitchStmtNode().
|
|
|
|
*
|
|
|
|
* \note The specification is unclear as to whether guards are implicitly cast
|
|
|
|
* to the type of the implicit variable. This only matters in the case that
|
|
|
|
* mixed guard types are present, and in this code, the action that is performed
|
|
|
|
* is the same as the comparison operator, that is, in order for a guard to
|
|
|
|
* match, both its type and value must match the implicit variable.
|
|
|
|
*
|
|
|
|
* \return A pointer to a default return value.
|
|
|
|
*
|
|
|
|
* \retval NULL An error occurred during interpretation.
|
|
|
|
*/
|
|
|
|
ReturnObject *interpretSwitchStmtNode(StmtNode *node,
|
|
|
|
ScopeObject *scope)
|
2010-08-09 07:01:59 +00:00
|
|
|
{
|
|
|
|
SwitchStmtNode *stmt = (SwitchStmtNode *)node->stmt;
|
|
|
|
unsigned int n;
|
2011-06-15 06:54:12 +00:00
|
|
|
/*
|
|
|
|
* Loop over each of the guards, checking if any match the implicit
|
|
|
|
* variable.
|
|
|
|
*/
|
2010-08-09 07:01:59 +00:00
|
|
|
for (n = 0; n < stmt->guards->num; n++) {
|
|
|
|
ValueObject *use1 = scope->impvar;
|
|
|
|
ValueObject *use2 = interpretExprNode(stmt->guards->exprs[n], scope);
|
|
|
|
unsigned int done = 0;
|
|
|
|
if (!use2) return NULL;
|
|
|
|
if (use1->type == use2->type) {
|
|
|
|
switch (use1->type) {
|
|
|
|
case VT_NIL:
|
|
|
|
break;
|
|
|
|
case VT_BOOLEAN:
|
|
|
|
case VT_INTEGER:
|
|
|
|
if (getInteger(use1) == getInteger(use2))
|
|
|
|
done = 1;
|
|
|
|
break;
|
|
|
|
case VT_FLOAT:
|
2010-12-21 08:20:31 +00:00
|
|
|
if (fabs(getFloat(use1) - getFloat(use2)) < FLT_EPSILON)
|
2010-08-09 07:01:59 +00:00
|
|
|
done = 1;
|
|
|
|
break;
|
|
|
|
case VT_STRING:
|
2011-06-15 06:54:12 +00:00
|
|
|
/**
|
|
|
|
* \note Strings with interpolation
|
|
|
|
* should have already been checked for.
|
|
|
|
*/
|
2010-08-09 07:01:59 +00:00
|
|
|
if (!strcmp(getString(use1), getString(use2)))
|
|
|
|
done = 1;
|
|
|
|
break;
|
|
|
|
default:
|
2012-12-13 04:53:54 +00:00
|
|
|
error(IN_INVALID_TYPE);
|
2010-08-09 07:01:59 +00:00
|
|
|
deleteValueObject(use2);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
deleteValueObject(use2);
|
|
|
|
if (done) break;
|
|
|
|
}
|
|
|
|
/* If none of the guards match and a default block exists */
|
|
|
|
if (n == stmt->blocks->num && stmt->def) {
|
|
|
|
ReturnObject *r = interpretBlockNode(stmt->def, scope);
|
|
|
|
if (!r)
|
|
|
|
return NULL;
|
|
|
|
else if (r->type == RT_RETURN)
|
|
|
|
return r;
|
|
|
|
else
|
|
|
|
deleteReturnObject(r);
|
|
|
|
}
|
|
|
|
else {
|
2011-06-15 06:54:12 +00:00
|
|
|
/*
|
|
|
|
* Keep interpreting blocks starting at n until a break or
|
|
|
|
* return is encountered.
|
|
|
|
*/
|
2010-08-09 07:01:59 +00:00
|
|
|
for (; n < stmt->blocks->num; n++) {
|
|
|
|
ReturnObject *r = interpretBlockNode(stmt->blocks->blocks[n], scope);
|
|
|
|
if (!r)
|
|
|
|
return NULL;
|
|
|
|
else if (r->type == RT_BREAK) {
|
|
|
|
deleteReturnObject(r);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
else if (r->type == RT_RETURN)
|
|
|
|
return r;
|
|
|
|
else
|
|
|
|
deleteReturnObject(r);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return createReturnObject(RT_DEFAULT, NULL);
|
|
|
|
}
|
|
|
|
|
2011-06-15 06:54:12 +00:00
|
|
|
/**
|
|
|
|
* Interprets a break statement.
|
|
|
|
*
|
|
|
|
* \param [in] node Not used (see note).
|
|
|
|
*
|
|
|
|
* \param [in] scope Not used (see note).
|
|
|
|
*
|
|
|
|
* \pre \a node contains a statement created by createStmtNode() with arguments
|
|
|
|
* ST_BREAK and NULL.
|
|
|
|
*
|
|
|
|
* \note \a node and \a scope are not used by this function but are still
|
|
|
|
* included in its prototype to allow this function to be stored in a jump table
|
|
|
|
* for fast execution.
|
|
|
|
*
|
|
|
|
* \return A pointer to a break return value.
|
|
|
|
*
|
|
|
|
* \retval NULL An error occurred during interpretation.
|
|
|
|
*/
|
|
|
|
ReturnObject *interpretBreakStmtNode(StmtNode *node,
|
|
|
|
ScopeObject *scope)
|
2010-08-09 07:01:59 +00:00
|
|
|
{
|
|
|
|
node = NULL;
|
|
|
|
scope = NULL;
|
|
|
|
return createReturnObject(RT_BREAK, NULL);
|
|
|
|
}
|
|
|
|
|
2011-06-15 06:54:12 +00:00
|
|
|
/**
|
|
|
|
* Interprets a return statement.
|
|
|
|
*
|
|
|
|
* \param [in] node The statement to interpret.
|
|
|
|
*
|
|
|
|
* \param [in] scope The scope to evaluate \a node under.
|
|
|
|
*
|
|
|
|
* \pre \a node contains a statement created by createReturnStmtNode().
|
|
|
|
*
|
|
|
|
* \return A pointer to a return value of \a node interpreted under \a scope.
|
|
|
|
*
|
|
|
|
* \retval NULL An error occurred during interpretation.
|
|
|
|
*/
|
|
|
|
ReturnObject *interpretReturnStmtNode(StmtNode *node,
|
|
|
|
ScopeObject *scope)
|
2010-08-09 07:01:59 +00:00
|
|
|
{
|
|
|
|
/* Evaluate and return the expression. */
|
|
|
|
ReturnStmtNode *stmt = (ReturnStmtNode *)node->stmt;
|
|
|
|
ValueObject *value = interpretExprNode(stmt->value, scope);
|
|
|
|
if (!value) return NULL;
|
|
|
|
return createReturnObject(RT_RETURN, value);
|
|
|
|
}
|
|
|
|
|
2011-06-15 06:54:12 +00:00
|
|
|
/**
|
|
|
|
* Interprets a loop statement.
|
|
|
|
*
|
|
|
|
* \param [in] node The statement to interpret.
|
|
|
|
*
|
|
|
|
* \param [in] scope The scope to evaluate \a node under.
|
|
|
|
*
|
|
|
|
* \pre \a node contains a statement created by createLoopStmtNode().
|
|
|
|
*
|
|
|
|
* \return A pointer to a return value of \a node interpreted under \a scope.
|
|
|
|
*
|
|
|
|
* \retval NULL An error occurred during interpretation.
|
|
|
|
*/
|
|
|
|
ReturnObject *interpretLoopStmtNode(StmtNode *node,
|
|
|
|
ScopeObject *scope)
|
2010-08-09 07:01:59 +00:00
|
|
|
{
|
|
|
|
LoopStmtNode *stmt = (LoopStmtNode *)node->stmt;
|
|
|
|
ScopeObject *outer = createScopeObject(scope);
|
|
|
|
ValueObject *var = NULL;
|
|
|
|
if (!outer) return NULL;
|
|
|
|
/* Create a temporary loop variable if required */
|
|
|
|
if (stmt->var) {
|
2011-06-15 06:54:12 +00:00
|
|
|
var = createScopeValue(scope, outer, stmt->var);
|
2010-08-09 07:01:59 +00:00
|
|
|
if (!var) {
|
|
|
|
deleteScopeObject(outer);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
var->type = VT_INTEGER;
|
|
|
|
var->data.i = 0;
|
|
|
|
var->semaphore = 1;
|
|
|
|
}
|
|
|
|
while (1) {
|
|
|
|
if (stmt->guard) {
|
|
|
|
ValueObject *val = interpretExprNode(stmt->guard, outer);
|
|
|
|
ValueObject *use = val;
|
|
|
|
unsigned short cast = 0;
|
|
|
|
int guardval;
|
|
|
|
if (val->type != VT_BOOLEAN && val->type != VT_INTEGER) {
|
|
|
|
use = castBooleanImplicit(val, scope);
|
|
|
|
if (!use) {
|
|
|
|
deleteScopeObject(outer);
|
|
|
|
deleteValueObject(val);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
cast = 1;
|
|
|
|
}
|
|
|
|
guardval = getInteger(use);
|
|
|
|
if (cast) deleteValueObject(use);
|
|
|
|
deleteValueObject(val);
|
|
|
|
if (guardval == 0) break;
|
|
|
|
}
|
|
|
|
if (stmt->body) {
|
|
|
|
ReturnObject *result = interpretBlockNode(stmt->body, outer);
|
|
|
|
if (!result) {
|
|
|
|
deleteScopeObject(outer);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
else if (result->type == RT_BREAK) {
|
|
|
|
deleteReturnObject(result);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
else if (result->type == RT_RETURN) {
|
|
|
|
deleteScopeObject(outer);
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
deleteReturnObject(result);
|
|
|
|
}
|
|
|
|
if (stmt->update) {
|
2011-06-15 06:54:12 +00:00
|
|
|
/*
|
|
|
|
* A little efficiency hack: if we know the operation to
|
|
|
|
* perform, don't bother evaluating the ExprNode
|
2010-08-09 07:01:59 +00:00
|
|
|
* structure, just go ahead and do it to the loop
|
2011-06-15 06:54:12 +00:00
|
|
|
* variable.
|
|
|
|
*/
|
2010-08-09 07:01:59 +00:00
|
|
|
if (stmt->update->type == ET_OP) {
|
2014-10-24 05:57:10 +00:00
|
|
|
ValueObject *updated = NULL;
|
2011-06-15 06:54:12 +00:00
|
|
|
var = getScopeValue(scope, outer, stmt->var);
|
2010-08-09 07:01:59 +00:00
|
|
|
OpExprNode *op = (OpExprNode *)stmt->update->expr;
|
|
|
|
if (op->type == OP_ADD)
|
2014-10-24 05:57:10 +00:00
|
|
|
updated = createIntegerValueObject(var->data.i + 1);
|
2010-08-09 07:01:59 +00:00
|
|
|
else if (op->type == OP_SUB)
|
2014-10-24 05:57:10 +00:00
|
|
|
updated = createIntegerValueObject(var->data.i - 1);
|
|
|
|
|
|
|
|
if (!updateScopeValue(scope, outer, stmt->var, updated)) {
|
|
|
|
deleteValueObject(updated);
|
|
|
|
deleteScopeObject(outer);
|
|
|
|
return NULL;
|
|
|
|
}
|
2010-08-09 07:01:59 +00:00
|
|
|
}
|
|
|
|
else {
|
|
|
|
ValueObject *update = interpretExprNode(stmt->update, outer);
|
|
|
|
if (!update) {
|
|
|
|
deleteScopeObject(outer);
|
|
|
|
return NULL;
|
|
|
|
}
|
2011-06-15 06:54:12 +00:00
|
|
|
if (!updateScopeValue(scope, outer, stmt->var, update)) {
|
2010-08-09 07:01:59 +00:00
|
|
|
deleteScopeObject(outer);
|
|
|
|
deleteValueObject(update);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
deleteScopeObject(outer);
|
|
|
|
return createReturnObject(RT_DEFAULT, NULL);
|
|
|
|
}
|
|
|
|
|
2011-06-15 06:54:12 +00:00
|
|
|
/**
|
|
|
|
* Interprets a deallocation statement.
|
|
|
|
*
|
|
|
|
* \param [in] node The statement to interpret.
|
|
|
|
*
|
|
|
|
* \param [in] scope The scope to evaluate \a node under.
|
|
|
|
*
|
|
|
|
* \pre \a node contains a statement created by createDeallocationStmtNode().
|
|
|
|
*
|
|
|
|
* \return A pointer to a default return value.
|
|
|
|
*
|
|
|
|
* \retval NULL An error occurred during interpretation.
|
|
|
|
*/
|
|
|
|
ReturnObject *interpretDeallocationStmtNode(StmtNode *node,
|
|
|
|
ScopeObject *scope)
|
2010-09-02 08:37:20 +00:00
|
|
|
{
|
|
|
|
DeallocationStmtNode *stmt = (DeallocationStmtNode *)node->stmt;
|
2011-06-15 06:54:12 +00:00
|
|
|
if (!updateScopeValue(scope, scope, stmt->target, NULL)) return NULL;
|
2010-09-02 08:37:20 +00:00
|
|
|
/* If we want to completely remove the variable, use:
|
|
|
|
deleteScopeValue(scope, stmt->target);
|
|
|
|
*/
|
|
|
|
return createReturnObject(RT_DEFAULT, NULL);
|
|
|
|
}
|
|
|
|
|
2011-06-15 06:54:12 +00:00
|
|
|
/**
|
|
|
|
* Interprets a function definition statement.
|
|
|
|
*
|
|
|
|
* \param [in] node The statement to interpret.
|
|
|
|
*
|
|
|
|
* \param [in] scope The scope to evaluate \a node under.
|
|
|
|
*
|
|
|
|
* \pre \a node contains a statement created by createFuncDefStmtNode().
|
|
|
|
*
|
|
|
|
* \return A pointer to a default return value.
|
|
|
|
*
|
|
|
|
* \retval NULL An error occurred during interpretation.
|
|
|
|
*/
|
|
|
|
ReturnObject *interpretFuncDefStmtNode(StmtNode *node,
|
|
|
|
ScopeObject *scope)
|
2010-08-09 07:01:59 +00:00
|
|
|
{
|
2010-11-16 05:25:51 +00:00
|
|
|
/* Add the function to the current scope */
|
|
|
|
FuncDefStmtNode *stmt = (FuncDefStmtNode *)node->stmt;
|
|
|
|
ValueObject *init = NULL;
|
2011-06-15 06:54:12 +00:00
|
|
|
ScopeObject *dest = NULL;
|
2011-07-02 05:38:12 +00:00
|
|
|
|
2011-06-15 06:54:12 +00:00
|
|
|
dest = getScopeObject(scope, scope, stmt->scope);
|
2011-07-02 05:38:12 +00:00
|
|
|
if (!dest) return NULL;
|
2011-06-15 06:54:12 +00:00
|
|
|
if (getScopeValueLocal(scope, dest, stmt->name)) {
|
2010-11-16 05:25:51 +00:00
|
|
|
IdentifierNode *id = (IdentifierNode *)(stmt->name);
|
2010-12-20 23:30:37 +00:00
|
|
|
char *name = resolveIdentifierName(id, scope);
|
|
|
|
if (name) {
|
2012-12-13 04:53:54 +00:00
|
|
|
error(IN_FUNCTION_NAME_USED_BY_VARIABLE, id->fname, id->line, name);
|
2010-12-20 23:30:37 +00:00
|
|
|
free(name);
|
|
|
|
}
|
2010-11-16 05:25:51 +00:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
init = createFunctionValueObject(stmt);
|
|
|
|
if (!init) return NULL;
|
2011-06-15 06:54:12 +00:00
|
|
|
if (!createScopeValue(scope, dest, stmt->name)) {
|
2010-11-16 05:25:51 +00:00
|
|
|
deleteValueObject(init);
|
|
|
|
return NULL;
|
|
|
|
}
|
2011-06-15 06:54:12 +00:00
|
|
|
if (!updateScopeValue(scope, dest, stmt->name, init)) {
|
2010-11-16 05:25:51 +00:00
|
|
|
deleteValueObject(init);
|
|
|
|
return NULL;
|
|
|
|
}
|
2010-08-09 07:01:59 +00:00
|
|
|
return createReturnObject(RT_DEFAULT, NULL);
|
|
|
|
}
|
|
|
|
|
2011-06-15 06:54:12 +00:00
|
|
|
/**
|
|
|
|
* Interprets an expression statement.
|
|
|
|
*
|
|
|
|
* \param [in] node The statement to interpret.
|
|
|
|
*
|
|
|
|
* \param [in] scope The scope to evaluate \a node under.
|
|
|
|
*
|
|
|
|
* \pre \a node contains a statement created by createExprNode().
|
|
|
|
*
|
|
|
|
* \post The implicit variable of \a scope will be set the the value of \a node
|
|
|
|
* evaluated under \a scope.
|
|
|
|
*
|
|
|
|
* \return A pointer to a default return value.
|
|
|
|
*
|
|
|
|
* \retval NULL An error occurred during interpretation.
|
|
|
|
*/
|
|
|
|
ReturnObject *interpretExprStmtNode(StmtNode *node,
|
|
|
|
ScopeObject *scope)
|
2010-08-09 07:01:59 +00:00
|
|
|
{
|
|
|
|
/* Set the implicit variable to the result of the expression */
|
|
|
|
ExprNode *expr = (ExprNode *)node->stmt;
|
|
|
|
deleteValueObject(scope->impvar);
|
|
|
|
scope->impvar = interpretExprNode(expr, scope);
|
|
|
|
if (!scope->impvar) return NULL;
|
|
|
|
return createReturnObject(RT_DEFAULT, NULL);
|
|
|
|
}
|
|
|
|
|
2011-09-26 20:16:19 +00:00
|
|
|
/**
|
|
|
|
* Interprets an alternate array definition statement.
|
|
|
|
*
|
|
|
|
* \param [in] node The statement to interpret.
|
|
|
|
*
|
|
|
|
* \param [in] scope The scope to evaluate \a node under.
|
|
|
|
*
|
2013-02-24 00:51:07 +00:00
|
|
|
* \pre \a node contains a statement created by createAltArrayDefStmtNode().
|
2011-09-26 20:16:19 +00:00
|
|
|
*
|
|
|
|
* \return A pointer to a default return value.
|
|
|
|
*
|
|
|
|
* \retval NULL An error occurred during interpretation.
|
|
|
|
*/
|
|
|
|
ReturnObject *interpretAltArrayDefStmtNode(StmtNode *node,
|
|
|
|
ScopeObject *scope)
|
|
|
|
{
|
|
|
|
AltArrayDefStmtNode *stmt = (AltArrayDefStmtNode *)node->stmt;
|
|
|
|
ValueObject *init = NULL;
|
|
|
|
ScopeObject *dest = scope;
|
2011-10-01 16:56:03 +00:00
|
|
|
ReturnObject *ret = NULL;
|
2011-09-26 20:16:19 +00:00
|
|
|
if (getScopeValueLocal(scope, dest, stmt->name)) {
|
|
|
|
IdentifierNode *id = (IdentifierNode *)(stmt->name);
|
|
|
|
char *name = resolveIdentifierName(id, scope);
|
|
|
|
if (name) {
|
|
|
|
fprintf(stderr, "%s:%u: redefinition of existing variable at: %s\n", id->fname, id->line, name);
|
|
|
|
free(name);
|
|
|
|
}
|
|
|
|
return NULL;
|
|
|
|
}
|
2011-12-23 16:09:11 +00:00
|
|
|
if (stmt->parent) {
|
|
|
|
ScopeObject *parent = getScopeObject(scope, scope, stmt->parent);
|
|
|
|
if (!parent) return NULL;
|
|
|
|
init = createArrayValueObject(parent);
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
init = createArrayValueObject(scope);
|
|
|
|
}
|
2011-09-26 20:16:19 +00:00
|
|
|
if (!init) return NULL;
|
2011-12-23 16:09:11 +00:00
|
|
|
|
2011-09-26 20:16:19 +00:00
|
|
|
/* Populate the array body */
|
2011-10-01 16:56:03 +00:00
|
|
|
ret = interpretStmtNodeList(stmt->body->stmts, getArray(init));
|
|
|
|
if (!ret) {
|
|
|
|
deleteValueObject(init);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
deleteReturnObject(ret);
|
2011-09-26 20:16:19 +00:00
|
|
|
if (!createScopeValue(scope, dest, stmt->name)) {
|
|
|
|
deleteValueObject(init);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
if (!updateScopeValue(scope, dest, stmt->name, init)) {
|
|
|
|
deleteValueObject(init);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
return createReturnObject(RT_DEFAULT, NULL);
|
|
|
|
}
|
|
|
|
|
2013-02-24 00:51:07 +00:00
|
|
|
/**
|
|
|
|
* Interprets a binding statement.
|
|
|
|
*
|
|
|
|
* \param [in] node The statement to interpret.
|
|
|
|
*
|
|
|
|
* \param [in] scope The scope to evaluate \a node under.
|
|
|
|
*
|
|
|
|
* \pre \a node contains a statement created by createBindingStmtNode().
|
|
|
|
*
|
|
|
|
* \return A pointer to a default return value.
|
|
|
|
*
|
|
|
|
* \retval NULL An error occurred during interpretation.
|
|
|
|
*/
|
|
|
|
ReturnObject *interpretBindingStmtNode(StmtNode *node,
|
|
|
|
ScopeObject *scope)
|
|
|
|
{
|
|
|
|
BindingStmtNode *stmt = (BindingStmtNode *)node->stmt;
|
2013-02-24 07:40:58 +00:00
|
|
|
return (stmt->binding)(scope);
|
2013-02-24 00:51:07 +00:00
|
|
|
}
|
|
|
|
|
2013-02-24 20:44:33 +00:00
|
|
|
/**
|
|
|
|
* Interprets a library import statement.
|
|
|
|
*
|
|
|
|
* \param [in] node The statement to interpret.
|
|
|
|
*
|
|
|
|
* \param [in] scope The scope to evaluate \a node under.
|
|
|
|
*
|
|
|
|
* \pre \a node contains a statement created by createImportStmtNode().
|
|
|
|
*
|
|
|
|
* \return A pointer to a default return value.
|
|
|
|
*
|
|
|
|
* \retval NULL An error occurred during interpretation.
|
|
|
|
*/
|
|
|
|
ReturnObject *interpretImportStmtNode(StmtNode *node,
|
|
|
|
ScopeObject *scope)
|
|
|
|
{
|
|
|
|
ImportStmtNode *stmt = (ImportStmtNode *)node->stmt;
|
|
|
|
loadLibrary(scope, stmt->name);
|
|
|
|
return createReturnObject(RT_DEFAULT, NULL);
|
|
|
|
}
|
|
|
|
|
2011-06-15 06:54:12 +00:00
|
|
|
/*
|
|
|
|
* A jump table for statements. The index of a function in the table is given
|
|
|
|
* by its its index in the enumerated StmtType type.
|
|
|
|
*/
|
2013-02-24 20:44:33 +00:00
|
|
|
static ReturnObject *(*StmtJumpTable[16])(StmtNode *, ScopeObject *) = {
|
2010-08-09 07:01:59 +00:00
|
|
|
interpretCastStmtNode,
|
|
|
|
interpretPrintStmtNode,
|
|
|
|
interpretInputStmtNode,
|
|
|
|
interpretAssignmentStmtNode,
|
|
|
|
interpretDeclarationStmtNode,
|
|
|
|
interpretIfThenElseStmtNode,
|
|
|
|
interpretSwitchStmtNode,
|
|
|
|
interpretBreakStmtNode,
|
|
|
|
interpretReturnStmtNode,
|
|
|
|
interpretLoopStmtNode,
|
2010-09-02 08:37:20 +00:00
|
|
|
interpretDeallocationStmtNode,
|
2010-08-09 07:01:59 +00:00
|
|
|
interpretFuncDefStmtNode,
|
2011-09-26 20:16:19 +00:00
|
|
|
interpretExprStmtNode,
|
2013-02-24 00:51:07 +00:00
|
|
|
interpretAltArrayDefStmtNode,
|
2013-02-24 20:44:33 +00:00
|
|
|
interpretBindingStmtNode,
|
|
|
|
interpretImportStmtNode };
|
2010-08-09 07:01:59 +00:00
|
|
|
|
2011-06-15 06:54:12 +00:00
|
|
|
/**
|
|
|
|
* Interprets a statement.
|
|
|
|
*
|
|
|
|
* \param [in] node The statement to interpret.
|
|
|
|
*
|
|
|
|
* \param [in] scope The scope to evaluate \a node under.
|
|
|
|
*
|
|
|
|
* \pre \a node contains a statement created by parseStmtNode().
|
|
|
|
*
|
|
|
|
* \return A pointer to a return value set appropriately depending on the
|
|
|
|
* statement interpreted.
|
|
|
|
*
|
|
|
|
* \retval NULL An error occurred during interpretation.
|
|
|
|
*/
|
|
|
|
ReturnObject *interpretStmtNode(StmtNode *node,
|
|
|
|
ScopeObject *scope)
|
2010-08-09 07:01:59 +00:00
|
|
|
{
|
|
|
|
return StmtJumpTable[node->type](node, scope);
|
|
|
|
}
|
|
|
|
|
2011-06-15 06:54:12 +00:00
|
|
|
/**
|
|
|
|
* Interprets a list of statements.
|
|
|
|
*
|
|
|
|
* \param [in] list The statements to interpret.
|
|
|
|
*
|
|
|
|
* \param [in] scope The scope to evaluate \a list under.
|
|
|
|
*
|
|
|
|
* \return A pointer to a return value set appropriately depending on the
|
|
|
|
* statements interpreted.
|
|
|
|
*
|
|
|
|
* \retval NULL An error occurred during interpretation.
|
|
|
|
*/
|
|
|
|
ReturnObject *interpretStmtNodeList(StmtNodeList *list,
|
|
|
|
ScopeObject *scope)
|
2010-08-09 07:01:59 +00:00
|
|
|
{
|
|
|
|
ReturnObject *ret = NULL;
|
|
|
|
unsigned int n;
|
|
|
|
for (n = 0; n < list->num; n++) {
|
|
|
|
ret = interpretStmtNode(list->stmts[n], scope);
|
|
|
|
if (!ret)
|
|
|
|
return NULL;
|
|
|
|
else if (ret->type == RT_BREAK || ret->type == RT_RETURN)
|
|
|
|
return ret;
|
|
|
|
else {
|
|
|
|
deleteReturnObject(ret);
|
|
|
|
ret = NULL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (!ret) ret = createReturnObject(RT_DEFAULT, NULL);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2011-06-15 06:54:12 +00:00
|
|
|
/**
|
|
|
|
* Interprets a block of code.
|
|
|
|
*
|
|
|
|
* \param [in] node The block of code to interpret.
|
|
|
|
*
|
|
|
|
* \param [in] scope The scope to evaluate \a block under.
|
|
|
|
*
|
|
|
|
* \pre \a block contains a block of code created by parseBlockNode().
|
|
|
|
*
|
|
|
|
* \return A pointer to a return value set appropriately depending on the
|
|
|
|
* statements interpreted.
|
|
|
|
*
|
|
|
|
* \retval NULL An error occurred during interpretation.
|
|
|
|
*/
|
|
|
|
ReturnObject *interpretBlockNode(BlockNode *node,
|
|
|
|
ScopeObject *scope)
|
2010-08-09 07:01:59 +00:00
|
|
|
{
|
|
|
|
ReturnObject *ret = NULL;
|
|
|
|
ScopeObject *inner = createScopeObject(scope);
|
|
|
|
if (!inner) return NULL;
|
|
|
|
ret = interpretStmtNodeList(node->stmts, inner);
|
|
|
|
deleteScopeObject(inner);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2011-06-15 06:54:12 +00:00
|
|
|
/**
|
|
|
|
* Interprets a the main block of code.
|
|
|
|
*
|
|
|
|
* \param [in] main The main block of code to interpret.
|
|
|
|
*
|
2013-03-10 21:23:53 +00:00
|
|
|
* \param [in] scope The scope to evaluate \a main under.
|
|
|
|
*
|
2011-06-15 06:54:12 +00:00
|
|
|
* \pre \a main contains a block of code created by parseMainNode().
|
|
|
|
*
|
|
|
|
* \return The final status of the program.
|
|
|
|
*
|
|
|
|
* \retval 0 \a main was interpreted without any errors.
|
|
|
|
*
|
|
|
|
* \retval 1 An error occurred while interpreting \a main.
|
|
|
|
*/
|
2013-03-10 21:23:53 +00:00
|
|
|
int interpretMainNodeScope(MainNode *main, ScopeObject *scope)
|
2010-08-09 07:01:59 +00:00
|
|
|
{
|
|
|
|
ReturnObject *ret = NULL;
|
|
|
|
if (!main) return 1;
|
2013-03-10 21:23:53 +00:00
|
|
|
ret = interpretBlockNode(main->block, scope);
|
|
|
|
if (!ret) return 1;
|
2010-08-09 07:01:59 +00:00
|
|
|
deleteReturnObject(ret);
|
|
|
|
return 0;
|
|
|
|
}
|