lci/interpreter.c

3993 lines
103 KiB
C

#include "interpreter.h"
/**
* 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)
{
char *p = malloc(sizeof(char) * (strlen(data) + 1));
if (!p) {
perror("malloc");
return NULL;
}
strcpy(p, data);
return p;
}
/**
* 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);
} else if (id->type == IT_INDIRECT) {
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);
} else {
char *name = resolveIdentifierName(id, scope);
error(IN_INVALID_IDENTIFIER_TYPE, id->fname, id->line, name);
free(name);
}
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.
*/
ValueObject *createNilValueObject(void)
{
ValueObject *p = malloc(sizeof(ValueObject));
if (!p) {
perror("malloc");
return NULL;
}
p->type = VT_NIL;
p->semaphore = 1;
return p;
}
/**
* 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)
{
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;
}
/**
* 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.
*/
ValueObject *createIntegerValueObject(long long data)
{
ValueObject *p = malloc(sizeof(ValueObject));
if (!p) {
perror("malloc");
return NULL;
}
p->type = VT_INTEGER;
p->data.i = data;
p->semaphore = 1;
return p;
}
/**
* 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)
{
ValueObject *p = malloc(sizeof(ValueObject));
if (!p) {
perror("malloc");
return NULL;
}
p->type = VT_FLOAT;
p->data.f = data;
p->semaphore = 1;
return p;
}
/**
* 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)
{
ValueObject *p = malloc(sizeof(ValueObject));
if (!p) {
perror("malloc");
return NULL;
}
p->type = VT_STRING;
p->data.s = data;
p->semaphore = 1;
return p;
}
/**
* 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)
{
ValueObject *p = malloc(sizeof(ValueObject));
if (!p) {
perror("malloc");
return NULL;
}
p->type = VT_FUNC;
p->data.fn = def;
p->semaphore = 1;
return p;
}
/**
* 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)
{
ValueObject *p = malloc(sizeof(ValueObject));
if (!p) {
perror("malloc");
return NULL;
}
p->type = VT_ARRAY;
p->data.a = createScopeObject(parent);
if (!p->data.a) {
free(p);
return NULL;
}
p->semaphore = 1;
return p;
}
/**
* 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;
}
/**
* 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)
{
V(value);
return value;
}
/**
* 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)
{
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);
}
}
/**
* 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)
{
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;
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;
return p;
}
/**
* 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)
{
unsigned int n;
if (!scope) return;
for (n = 0; n < scope->numvals; n++) {
free(scope->names[n]);
deleteValueObject(scope->values[n]);
}
free(scope->names);
free(scope->values);
deleteValueObject(scope->impvar);
free(scope);
}
/**
* 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;
unsigned int pos = 0;
/* 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;
/* 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;
}
dest->names = mem1;
dest->values = mem2;
/* 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;
}
dest->numvals = newnumvals;
return dest->values[pos];
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;
char *name = NULL;
/* Traverse the target to the terminal child and parent */
status = resolveTerminalSlot(src, dest, target, &parent, &child);
if (!status) goto updateScopeValueAbort;
/* Look up the identifier name */
name = resolveIdentifierName(child, src);
if (!name) goto updateScopeValueAbort;
/* Traverse upwards through scopes */
do {
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();
}
return parent->values[n];
}
} while ((parent = parent->parent));
{
char *name = resolveIdentifierName(target, src);
error(IN_UNABLE_TO_STORE_VARIABLE, target->fname, target->line, name);
free(name);
}
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));
{
char *name = resolveIdentifierName(child, src);
error(IN_VARIABLE_DOES_NOT_EXIST, child->fname, child->line, name);
free(name);
}
getScopeValueAbort: /* In case something goes wrong... */
/* Clean up any allocated structures */
if (name) free(name);
return NULL;
}
/**
* Gets a scope 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 *getScopeObjectLocal(ScopeObject *src,
ScopeObject *dest,
IdentifierNode *target)
{
ScopeObject *current = dest;
char *name = NULL;
/* Look up the identifier name */
name = resolveIdentifierName(target, src);
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;
}
/* 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) {
error(IN_VARIABLE_NOT_AN_ARRAY, target->fname, target->line, name);
goto getScopeObjectLocalAbort;
}
free(name);
return getArray(current->values[n]);
}
}
} while ((current = current->parent));
{
char *name = resolveIdentifierName(target, src);
error(IN_VARIABLE_DOES_NOT_EXIST, target->fname, target->line, name);
free(name);
}
getScopeObjectLocalAbort: /* In case something goes wrong... */
/* Clean up any allocated structures */
if (name) free(name);
return NULL;
}
/**
* 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);
if (current->values[n]->type == VT_ARRAY)
{
return getArray(current->values[n]);
}
else
{
return dest;
}
}
}
} 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;
}
/**
* 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)
{
unsigned int n;
char *name = NULL;
ScopeObject *scope = NULL;
/* Access any slots */
while (target->slot) {
/*
* Look up the target in the dest scope, using the src scope
* for resolving variables in indirect identifiers
*/
scope = getScopeObjectLocal(src, dest, target);
if (!scope) return 0;
dest = scope;
target = target->slot;
}
/* Look up the identifier name */
name = resolveIdentifierName(target, src);
if (!name) goto getScopeValueLocalAbort;
/* Check for value in current scope */
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];
}
}
}
/*
for (n = 0; n < dest->numvals; n++) {
if (!strcmp(dest->names[n], name)) {
free(name);
return dest->values[n];
}
}
*/
getScopeValueLocalAbort: /* In case something goes wrong... */
/* Clean up any allocated structures */
if (name) free(name);
return NULL;
}
/**
* 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;
char *name = NULL;
int status;
int isI;
int isME;
ScopeObject *scope;
/* 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;
}
/* Access any slots */
while (target->slot) {
/*
* Look up the target in the dest scope, using the src scope
* for resolving variables in indirect identifiers
*/
scope = getScopeObjectLocal(src, dest, target);
if (!scope) goto getScopeObjectAbort;
dest = scope;
target = target->slot;
}
val = getScopeValue(src, dest, target);
if (!val) goto getScopeObjectAbort;
if (val->type != VT_ARRAY) {
char *name = resolveIdentifierName(target, src);
error(IN_VARIABLE_NOT_AN_ARRAY, target->fname, target->line, name);
free(name);
goto getScopeObjectAbort;
}
return getArray(val);
getScopeObjectAbort: /* In case something goes wrong... */
/* Clean up any allocated structures */
if (name) free(name);
return NULL;
}
/**
* 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;
char *name = NULL;
void *mem1 = NULL;
void *mem2 = NULL;
ScopeObject *scope = NULL;
/* Access any slots */
while (target->slot) {
/*
* Look up the target in the dest scope, using the src scope
* for resolving variables in indirect identifiers
*/
scope = getScopeObjectLocal(src, dest, target);
if (!scope) goto deleteScopeValueAbort;
dest = scope;
target = target->slot;
}
current = dest;
/* Look up the identifier name */
name = resolveIdentifierName(target, src);
if (!name) goto deleteScopeValueAbort;
/* Traverse upwards through scopes */
do {
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;
}
dest->names = mem1;
dest->values = mem2;
dest->numvals = newnumvals;
return;
}
} while ((current = current->parent));
free(name);
return;
deleteScopeValueAbort: /* In case something goes wrong... */
/* Clean up any allocated structures */
if (name) free(name);
if (mem1) free(mem1);
if (mem2) free(mem2);
if (scope) free(scope);
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)
{
ReturnObject *p = malloc(sizeof(ReturnObject));
if (!p) {
perror("malloc");
return NULL;
}
p->type = type;
p->value = value;
return p;
}
/**
* 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)
{
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)
{
ScopeObject *scope = NULL;
/* Start with default values */
*parent = dest;
*child = target;
/* Access any slots */
while (target->slot) {
/*
* Look up the target in the dest scope, using the src scope
* for resolving variables in indirect identifiers
*/
scope = getScopeObjectLocal(src, dest, target);
if (!scope) goto resolveTerminalSlotAbort;
dest = scope;
/* Change the target to the old target's slot */
target = target->slot;
}
/* Store the output values */
*parent = dest;
*child = target;
return 1;
resolveTerminalSlotAbort: /* In case something goes wrong... */
/* Clean up any allocated structures */
if (scope) deleteScopeObject(scope);
return 0;
}
/**
* 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)
{
if (!node) return NULL;
return castBooleanExplicit(node, scope);
}
/**
* 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)
{
if (!node) return NULL;
if (node->type == VT_NIL) {
error(IN_CANNOT_IMPLICITLY_CAST_NIL);
return NULL;
}
else return castIntegerExplicit(node, scope);
}
/**
* 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)
{
if (!node) return NULL;
if (node->type == VT_NIL) {
error(IN_CANNOT_IMPLICITLY_CAST_NIL);
return NULL;
}
else return castFloatExplicit(node, scope);
}
/**
* 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)
{
if (!node) return NULL;
if (node->type == VT_NIL) {
error(IN_CANNOT_IMPLICITLY_CAST_NIL);
return NULL;
}
else return castStringExplicit(node, scope);
}
/**
* 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)
{
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:
return createBooleanValueObject(fabs(getFloat(node) - 0.0) > FLT_EPSILON);
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');
case VT_FUNC:
error(IN_CANNOT_CAST_FUNCTION_TO_BOOLEAN);
return NULL;
case VT_ARRAY:
error(IN_CANNOT_CAST_ARRAY_TO_BOOLEAN);
return NULL;
default:
error(IN_UNKNOWN_VALUE_DURING_BOOLEAN_CAST);
return NULL;
}
}
/**
* 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)
{
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:
return createIntegerValueObject((long long)getFloat(node));
case VT_STRING:
if (strstr(getString(node), ":{")) {
/* Perform interpolation */
ValueObject *ret = NULL;
ValueObject *interp = castStringExplicit(node, scope);
if (!interp) return NULL;
long long value = strtoll(getString(interp), NULL, 0);
ret = createIntegerValueObject(value);
deleteValueObject(interp);
return ret;
}
else {
long long value = strtoll(getString(node), NULL, 0);
return createIntegerValueObject(value);
}
case VT_FUNC:
error(IN_CANNOT_CAST_FUNCTION_TO_INTEGER);
return NULL;
case VT_ARRAY:
error(IN_CANNOT_CAST_ARRAY_TO_INTEGER);
return NULL;
default:
error(IN_UNKNOWN_VALUE_DURING_INTEGER_CAST);
return NULL;
}
}
/**
* 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)
{
if (!node) return NULL;
switch (node->type) {
case VT_NIL:
return createFloatValueObject(0.0);
case VT_BOOLEAN:
case VT_INTEGER:
return createFloatValueObject((float)getInteger(node));
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;
float value = strtof(getString(interp), NULL);
ret = createFloatValueObject(value);
deleteValueObject(interp);
return ret;
}
else {
float value = strtof(getString(node), NULL);
return createFloatValueObject(value);
}
case VT_FUNC:
error(IN_CANNOT_CAST_FUNCTION_TO_DECIMAL);
return NULL;
case VT_ARRAY:
error(IN_CANNOT_CAST_ARRAY_TO_DECIMAL);
return NULL;
default:
error(IN_UNKNOWN_VALUE_DURING_DECIMAL_CAST);
return NULL;
}
}
/**
* 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)
{
if (!node) return NULL;
switch (node->type) {
case VT_NIL: {
char *str = copyString("");
if (!str) return NULL;
return createStringValueObject(str);
}
case VT_BOOLEAN: {
/*
* \note The spec does not define how TROOFs may be cast
* to YARNs.
*/
error(IN_CANNOT_CAST_BOOLEAN_TO_STRING);
return NULL;
}
case VT_INTEGER: {
char *data = NULL;
/*
* One character per integer bit plus one more for the
* null character
*/
size_t size = sizeof(long long) * 8 + 1;
data = malloc(sizeof(char) * size);
if (!data) return NULL;
sprintf(data, "%lli", getInteger(node));
return createStringValueObject(data);
}
case VT_FLOAT: {
char *data = NULL;
unsigned int precision = 2;
/*
* One character per float bit plus one more for the
* null character
*/
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);
unsigned int a, b;
size_t size;
/* 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;
}
else if (!strncmp(str + b, ":3", 2)) {
temp[a] = '\r';
a++, b += 2;
}
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, ')');
size_t len;
char *image = NULL;
long codepoint;
char out[3];
size_t num;
void *mem = NULL;
if (end < start) {
error(IN_EXPECTED_CLOSING_PAREN);
free(temp);
return NULL;
}
len = (size_t)(end - start);
image = malloc(sizeof(char) * (len + 1));
strncpy(image, start, len);
image[len] = '\0';
if (!isHexString(image)) {
error(IN_INVALID_HEX_NUMBER);
free(temp);
free(image);
return NULL;
}
codepoint = strtol(image, NULL, 16);
free(image);
if (codepoint < 0) {
error(IN_CODE_POINT_MUST_BE_POSITIVE);
free(temp);
return NULL;
}
num = convertCodePointToUTF8((unsigned int)codepoint, out);
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, ']');
size_t len;
char *image = NULL;
long codepoint;
char out[3];
size_t num;
void *mem = NULL;
if (end < start) {
error(IN_EXPECTED_CLOSING_SQUARE_BRACKET);
free(temp);
return NULL;
}
len = (size_t)(end - start);
image = malloc(sizeof(char) * (len + 1));
strncpy(image, start, len);
strncpy(image, start, len);
image[len] = '\0';
codepoint = convertNormativeNameToCodePoint(image);
free(image);
if (codepoint < 0) {
error(IN_CODE_POINT_MUST_BE_POSITIVE);
free(temp);
return NULL;
}
num = convertCodePointToUTF8((unsigned int)codepoint, out);
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, '}');
size_t len;
char *image = NULL;
void *mem = NULL;
if (end < start) {
error(IN_EXPECTED_CLOSING_CURLY_BRACE);
free(temp);
return NULL;
}
len = (size_t)(end - start);
image = malloc(sizeof(char) * (len + 1));
strncpy(image, start, len);
image[len] = '\0';
if (!strcmp(image, "IT"))
/* Lookup implicit variable */
val = scope->impvar;
else {
/*
* Create a new IdentifierNode
* structure and look up its
* value
*/
target = createIdentifierNode(IT_DIRECT, image, NULL, NULL, 0);
if (!target) {
free(temp);
return NULL;
}
val = getScopeValue(scope, scope, target);
if (!val) {
error(IN_VARIABLE_DOES_NOT_EXIST, target->fname, target->line, image);
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);
}
case VT_FUNC: {
error(IN_CANNOT_CAST_FUNCTION_TO_STRING);
return NULL;
}
case VT_ARRAY:
error(IN_CANNOT_CAST_ARRAY_TO_STRING);
return NULL;
default:
error(IN_UNKNOWN_VALUE_DURING_STRING_CAST);
return NULL;
}
}
/**
* 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)
{
node = NULL;
return scope->impvar;
}
/**
* 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)
{
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:
error(IN_UNKNOWN_CAST_TYPE);
deleteValueObject(val);
return NULL;
}
}
/**
* 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)
{
FuncCallExprNode *expr = (FuncCallExprNode *)node->expr;
unsigned int n;
ScopeObject *outer = NULL;
ValueObject *def = NULL;
ReturnObject *retval = NULL;
ValueObject *ret = NULL;
ScopeObject *dest = NULL;
ScopeObject *target = NULL;
dest = getScopeObject(scope, scope, expr->scope);
target = getScopeObjectLocalCaller(scope, dest, expr->name);
if (!target) return NULL;
outer = createScopeObjectCaller(scope, target);
if (!outer) return NULL;
def = getScopeValue(scope, dest, expr->name);
if (!def || def->type != VT_FUNC) {
IdentifierNode *id = (IdentifierNode *)(expr->name);
char *name = resolveIdentifierName(id, scope);
if (name) {
error(IN_UNDEFINED_FUNCTION, id->fname, id->line, name);
free(name);
}
deleteScopeObject(outer);
return NULL;
}
/* Check for correct supplied arity */
if (getFunction(def)->args->num != expr->args->num) {
IdentifierNode *id = (IdentifierNode *)(expr->name);
char *name = resolveIdentifierName(id, scope);
if (name) {
error(IN_INCORRECT_NUMBER_OF_ARGUMENTS, id->fname, id->line, name);
free(name);
}
deleteScopeObject(outer);
return NULL;
}
for (n = 0; n < getFunction(def)->args->num; n++) {
ValueObject *val = NULL;
if (!createScopeValue(scope, outer, getFunction(def)->args->ids[n])) {
deleteScopeObject(outer);
return NULL;
}
if (!(val = interpretExprNode(expr->args->exprs[n], scope))) {
deleteScopeObject(outer);
return NULL;
}
if (!updateScopeValue(scope, outer, getFunction(def)->args->ids[n], val)) {
deleteScopeObject(outer);
deleteValueObject(val);
return NULL;
}
}
/**
* \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.
*/
if (!(retval = interpretStmtNodeList(getFunction(def)->body->stmts, outer))) {
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:
error(IN_INVALID_RETURN_TYPE);
break;
}
deleteReturnObject(retval);
deleteScopeObject(outer);
return ret;
}
/**
* 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;
cmd = getString(castStringExplicit(val, scope));
/* 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);
}
/**
* 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;
return copyValueObject(val);
}
/**
* 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)
{
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: {
/*
* \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);
if (!str) return NULL;
return createStringValueObject(str);
}
default:
error(IN_UNKNOWN_CONSTANT_TYPE);
return NULL;
}
}
/**
* 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)
{
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);
}
/**
* 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)
{
return createIntegerValueObject(getInteger(a) + getInteger(b));
}
/**
* 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)
{
return createIntegerValueObject(getInteger(a) - getInteger(b));
}
/**
* 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)
{
return createIntegerValueObject(getInteger(a) * getInteger(b));
}
/**
* 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)
{
if (getInteger(b) == 0) {
error(IN_DIVISION_BY_ZERO);
return NULL;
}
return createIntegerValueObject(getInteger(a) / getInteger(b));
}
/**
* 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)
{
return createIntegerValueObject(getInteger(a) > getInteger(b) ? getInteger(a) : getInteger(b));
}
/**
* 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)
{
return createIntegerValueObject(getInteger(a) < getInteger(b) ? getInteger(a) : getInteger(b));
}
/**
* 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)
{
if (getInteger(b) == 0) {
error(IN_DIVISION_BY_ZERO);
return NULL;
}
return createIntegerValueObject(getInteger(a) % getInteger(b));
}
/**
* 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)
{
return createFloatValueObject((float)(getInteger(a) + getFloat(b)));
}
/**
* 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)
{
return createFloatValueObject((float)(getInteger(a) - getFloat(b)));
}
/**
* 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)
{
return createFloatValueObject((float)(getInteger(a) * getFloat(b)));
}
/**
* 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)
{
if (fabs(getFloat(b) - 0.0) < FLT_EPSILON) {
error(IN_DIVISION_BY_ZERO);
return NULL;
}
return createFloatValueObject((float)(getInteger(a) / getFloat(b)));
}
/**
* 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)
{
return createFloatValueObject((float)(getInteger(a)) > getFloat(b) ? (float)(getInteger(a)) : getFloat(b));
}
/**
* 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)
{
return createFloatValueObject((float)(getInteger(a)) < getFloat(b) ? (float)(getInteger(a)) : getFloat(b));
}
/**
* 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)
{
if (fabs(getFloat(b) - 0.0) < FLT_EPSILON) {
error(IN_DIVISION_BY_ZERO);
return NULL;
}
return createFloatValueObject((float)(fmod((double)(getInteger(a)), getFloat(b))));
}
/**
* 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)
{
return createFloatValueObject(getFloat(a) + getInteger(b));
}
/**
* 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)
{
return createFloatValueObject(getFloat(a) - getInteger(b));
}
/**
* 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)
{
return createFloatValueObject(getFloat(a) * getInteger(b));
}
/**
* 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)
{
if (getInteger(b) == 0) {
error(IN_DIVISION_BY_ZERO);
return NULL;
}
return createFloatValueObject(getFloat(a) / getInteger(b));
}
/**
* 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)
{
return createFloatValueObject(getFloat(a) > (float)(getInteger(b)) ? getFloat(a) : (float)(getInteger(b)));
}
/**
* 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)
{
return createFloatValueObject(getFloat(a) < (float)(getInteger(b)) ? getFloat(a) : (float)(getInteger(b)));
}
/**
* 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)
{
if (getInteger(b) == 0) {
error(IN_DIVISION_BY_ZERO);
return NULL;
}
return createFloatValueObject((float)(fmod(getFloat(a), (double)(getInteger(b)))));
}
/**
* 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)
{
return createFloatValueObject(getFloat(a) + getFloat(b));
}
/**
* 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)
{
return createFloatValueObject(getFloat(a) - getFloat(b));
}
/**
* 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)
{
return createFloatValueObject(getFloat(a) * getFloat(b));
}
/**
* 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)
{
if (fabs(getFloat(b) - 0.0) < FLT_EPSILON) {
error(IN_DIVISION_BY_ZERO);
return NULL;
}
return createFloatValueObject(getFloat(a) / getFloat(b));
}
/**
* 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)
{
return createFloatValueObject(getFloat(a) > getFloat(b) ? getFloat(a) : getFloat(b));
}
/**
* 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)
{
return createFloatValueObject(getFloat(a) < getFloat(b) ? getFloat(a) : getFloat(b));
}
/**
* 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)
{
if (fabs(getFloat(b) - 0.0) < FLT_EPSILON) {
error(IN_DIVISION_BY_ZERO);
return NULL;
}
return createFloatValueObject((float)(fmod(getFloat(a), getFloat(b))));
}
/*
* A jump table for arithmetic operations. The first index determines the
* particular arithmetic operation to perform, the second index determines the
* type of the first argument, and the third index determines the type of the
* second object.
*/
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 } }
};
/**
* 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)
{
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:
error(IN_INVALID_OPERAND_TYPE);
}
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:
error(IN_INVALID_OPERAND_TYPE);
}
/* 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;
}
/**
* 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)
{
unsigned int n;
int acc = 0;
/*
* Proceed to apply the same operation on the accumulator for the
* remaining arguments.
*/
for (n = 0; n < expr->args->num; n++) {
ValueObject *val = interpretExprNode(expr->args->exprs[n], scope);
ValueObject *use = val;
int temp;
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:
error(IN_INVALID_BOOLEAN_OPERATION_TYPE);
return NULL;
}
}
/**
* \note The specification does not say whether boolean logic
* short circuits or not. Here, we assume it does.
*/
if (expr->type == OP_AND && acc == 0) break;
else if (expr->type == OP_OR && acc == 1) break;
}
return createBooleanValueObject(acc);
}
/**
* 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)
{
return createBooleanValueObject(getInteger(a) == getInteger(b));
}
/**
* 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)
{
return createBooleanValueObject(getInteger(a) != getInteger(b));
}
/**
* 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)
{
return createBooleanValueObject(fabs((float)(getInteger(a)) - getFloat(b)) < FLT_EPSILON);
}
/**
* 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)
{
return createBooleanValueObject(fabs((float)(getInteger(a)) - getFloat(b)) > FLT_EPSILON);
}
/**
* 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)
{
return createBooleanValueObject(fabs(getFloat(a) - (float)(getInteger(b))) < FLT_EPSILON);
}
/**
* 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)
{
return createBooleanValueObject(fabs(getFloat(a) - (float)(getInteger(b))) > FLT_EPSILON);
}
/**
* 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)
{
return createBooleanValueObject(fabs(getFloat(a) - getFloat(b)) < FLT_EPSILON);
}
/**
* 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)
{
return createBooleanValueObject(fabs(getFloat(a) - getFloat(b)) > FLT_EPSILON);
}
/**
* 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)
{
return createBooleanValueObject(getInteger(a) == getInteger(b));
}
/**
* 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)
{
return createBooleanValueObject(getInteger(a) != getInteger(b));
}
/**
* 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)
{
return createBooleanValueObject(strcmp(getString(a), getString(b)) == 0);
}
/**
* 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)
{
return createBooleanValueObject(strcmp(getString(a), getString(b)) != 0);
}
/**
* 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)
{
a = NULL;
b = NULL;
return createBooleanValueObject(1);
}
/**
* 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)
{
a = NULL;
b = NULL;
return createBooleanValueObject(0);
}
/*
* 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.
*/
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 }
}
};
/**
* 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)
{
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;
}
/*
* Since there is no automatic casting, an equality (inequality) test
* against a non-number type will always fail (succeed).
*/
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:
error(IN_INVALID_EQUALITY_OPERATION_TYPE);
deleteValueObject(val1);
deleteValueObject(val2);
return NULL;
}
}
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);
}
ret = BoolOpJumpTable[expr->type - OP_EQ][val1->type][val2->type](val1, val2);
}
deleteValueObject(val1);
deleteValueObject(val2);
return ret;
}
/**
* 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)
{
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);
}
/*
* A jump table for operations. The index of a function in the table is given
* by its its index in the enumerated OpType type.
*/
static ValueObject *(*OpExprJumpTable[14])(OpExprNode *, ScopeObject *) = {
interpretArithOpExprNode,
interpretArithOpExprNode,
interpretArithOpExprNode,
interpretArithOpExprNode,
interpretArithOpExprNode,
interpretArithOpExprNode,
interpretArithOpExprNode,
interpretBoolOpExprNode,
interpretBoolOpExprNode,
interpretBoolOpExprNode,
interpretNotOpExprNode,
interpretEqualityOpExprNode,
interpretEqualityOpExprNode,
interpretConcatOpExprNode };
/**
* 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)
{
OpExprNode *expr = (OpExprNode *)node->expr;
return OpExprJumpTable[expr->type](expr, scope);
}
/*
* A jump table for expressions. The index of a function in the table is given
* by its its index in the enumerated ExprType type.
*/
static ValueObject *(*ExprJumpTable[7])(ExprNode *, ScopeObject *) = {
interpretCastExprNode,
interpretConstantExprNode,
interpretIdentifierExprNode,
interpretFuncCallExprNode,
interpretOpExprNode,
interpretImpVarExprNode,
interpretSystemCommandExprNode };
/**
* 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)
{
return ExprJumpTable[node->type](node, scope);
}
/**
* 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)
{
CastStmtNode *stmt = (CastStmtNode *)node->stmt;
ValueObject *val = getScopeValue(scope, scope, stmt->target);
ValueObject *cast = NULL;
if (!val) {
IdentifierNode *id = (IdentifierNode *)(stmt->target);
char *name = resolveIdentifierName(id, scope);
if (name) {
error(IN_VARIABLE_DOES_NOT_EXIST, id->fname, id->line, name);
free(name);
}
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;
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;
}
}
if (!updateScopeValue(scope, scope, stmt->target, cast)) {
deleteValueObject(cast);
return NULL;
}
return createReturnObject(RT_DEFAULT, NULL);
}
/**
* 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)
{
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;
}
fprintf(stmt->file, "%s", getString(use));
deleteValueObject(val);
deleteValueObject(use);
}
if (!stmt->nonl)
putc('\n', stmt->file);
return createReturnObject(RT_DEFAULT, NULL);
}
/**
* 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)
{
unsigned int size = 16;
unsigned int cur = 0;
char *temp = malloc(sizeof(char) * size);
int c;
void *mem = NULL;
InputStmtNode *stmt = (InputStmtNode *)node->stmt;
ValueObject *val = NULL;
while ((c = getchar()) && !feof(stdin)) {
/**
* \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.
*/
if (c == EOF || c == (int)'\r' || c == (int)'\n') break;
temp[cur] = (char)c;
cur++;
/* Reserve space to escape colon in input */
if (c == ':') {
cur++;
}
if (cur > size - 1) {
/* Increase buffer size */
size *= 2;
mem = realloc(temp, sizeof(char) * size);
if (!mem) {
perror("realloc");
free(temp);
return NULL;
}
temp = mem;
}
/* Escape colon in input */
if (c == ':') {
temp[cur - 1] = ':';
}
}
temp[cur] = '\0';
val = createStringValueObject(temp);
if (!val) {
free(temp);
return NULL;
}
if (!updateScopeValue(scope, scope, stmt->target, val)) {
deleteValueObject(val);
return NULL;
}
return createReturnObject(RT_DEFAULT, NULL);
}
/**
* 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)
{
AssignmentStmtNode *stmt = (AssignmentStmtNode *)node->stmt;
ValueObject *val = interpretExprNode(stmt->expr, scope);
if (!val) return NULL;
/* interpolate assigned strings */
if (val->type == VT_STRING) {
ValueObject *use = castStringImplicit(val, scope);
deleteValueObject(val);
if (!use) return NULL;
val = use;
}
if (!updateScopeValue(scope, scope, stmt->target, val)) {
deleteValueObject(val);
return NULL;
}
return createReturnObject(RT_DEFAULT, NULL);
}
/**
* 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)
{
DeclarationStmtNode *stmt = (DeclarationStmtNode *)node->stmt;
ValueObject *init = NULL;
ScopeObject *dest = NULL;
dest = getScopeObject(scope, scope, stmt->scope);
if (!dest) return NULL;
if (getScopeValueLocal(scope, dest, stmt->target)) {
IdentifierNode *id = (IdentifierNode *)(stmt->target);
char *name = resolveIdentifierName(id, scope);
if (name) {
error(IN_REDEFINITION_OF_VARIABLE, id->fname, id->line, name);
free(name);
}
return NULL;
}
if (stmt->expr)
init = interpretExprNode(stmt->expr, scope);
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:
init = createStringValueObject(copyString(""));
break;
case CT_ARRAY:
init = createArrayValueObject(scope);
break;
default:
error(IN_INVALID_DECLARATION_TYPE);
return NULL;
}
}
else if (stmt->parent) {
ScopeObject *parent = getScopeObject(scope, scope, stmt->parent);
if (!parent) return NULL;
init = createArrayValueObject(parent);
}
else
init = createNilValueObject();
if (!init) return NULL;
if (!createScopeValue(scope, dest, stmt->target)) {
deleteValueObject(init);
return NULL;
}
if (!updateScopeValue(scope, dest, stmt->target, init)) {
deleteValueObject(init);
return NULL;
}
return createReturnObject(RT_DEFAULT, NULL);
}
/**
* 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)
{
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);
}
/**
* 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)
{
SwitchStmtNode *stmt = (SwitchStmtNode *)node->stmt;
unsigned int n;
/*
* Loop over each of the guards, checking if any match the implicit
* variable.
*/
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:
if (fabs(getFloat(use1) - getFloat(use2)) < FLT_EPSILON)
done = 1;
break;
case VT_STRING:
/**
* \note Strings with interpolation
* should have already been checked for.
*/
if (!strcmp(getString(use1), getString(use2)))
done = 1;
break;
default:
error(IN_INVALID_TYPE);
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 {
/*
* Keep interpreting blocks starting at n until a break or
* return is encountered.
*/
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);
}
/**
* 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)
{
node = NULL;
scope = NULL;
return createReturnObject(RT_BREAK, NULL);
}
/**
* 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)
{
/* 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);
}
/**
* 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)
{
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) {
var = createScopeValue(scope, outer, stmt->var);
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) {
/*
* A little efficiency hack: if we know the operation to
* perform, don't bother evaluating the ExprNode
* structure, just go ahead and do it to the loop
* variable.
*/
if (stmt->update->type == ET_OP) {
ValueObject *updated = NULL;
var = getScopeValue(scope, outer, stmt->var);
OpExprNode *op = (OpExprNode *)stmt->update->expr;
if (op->type == OP_ADD)
updated = createIntegerValueObject(var->data.i + 1);
else if (op->type == OP_SUB)
updated = createIntegerValueObject(var->data.i - 1);
if (!updateScopeValue(scope, outer, stmt->var, updated)) {
deleteValueObject(updated);
deleteScopeObject(outer);
return NULL;
}
}
else {
ValueObject *update = interpretExprNode(stmt->update, outer);
if (!update) {
deleteScopeObject(outer);
return NULL;
}
if (!updateScopeValue(scope, outer, stmt->var, update)) {
deleteScopeObject(outer);
deleteValueObject(update);
return NULL;
}
}
}
}
deleteScopeObject(outer);
return createReturnObject(RT_DEFAULT, NULL);
}
/**
* 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)
{
DeallocationStmtNode *stmt = (DeallocationStmtNode *)node->stmt;
if (!updateScopeValue(scope, scope, stmt->target, NULL)) return NULL;
/* If we want to completely remove the variable, use:
deleteScopeValue(scope, stmt->target);
*/
return createReturnObject(RT_DEFAULT, NULL);
}
/**
* 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)
{
/* Add the function to the current scope */
FuncDefStmtNode *stmt = (FuncDefStmtNode *)node->stmt;
ValueObject *init = NULL;
ScopeObject *dest = NULL;
dest = getScopeObject(scope, scope, stmt->scope);
if (!dest) return NULL;
if (getScopeValueLocal(scope, dest, stmt->name)) {
IdentifierNode *id = (IdentifierNode *)(stmt->name);
char *name = resolveIdentifierName(id, scope);
if (name) {
error(IN_FUNCTION_NAME_USED_BY_VARIABLE, id->fname, id->line, name);
free(name);
}
return NULL;
}
init = createFunctionValueObject(stmt);
if (!init) return NULL;
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);
}
/**
* 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)
{
/* 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);
}
/**
* Interprets an alternate array 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 createAltArrayDefStmtNode().
*
* \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;
ReturnObject *ret = NULL;
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;
}
if (stmt->parent) {
ScopeObject *parent = getScopeObject(scope, scope, stmt->parent);
if (!parent) return NULL;
init = createArrayValueObject(parent);
}
else {
init = createArrayValueObject(scope);
}
if (!init) return NULL;
/* Populate the array body */
ret = interpretStmtNodeList(stmt->body->stmts, getArray(init));
if (!ret) {
deleteValueObject(init);
return NULL;
}
deleteReturnObject(ret);
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);
}
/**
* 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;
return (stmt->binding)(scope);
}
/**
* 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);
}
/*
* A jump table for statements. The index of a function in the table is given
* by its its index in the enumerated StmtType type.
*/
static ReturnObject *(*StmtJumpTable[16])(StmtNode *, ScopeObject *) = {
interpretCastStmtNode,
interpretPrintStmtNode,
interpretInputStmtNode,
interpretAssignmentStmtNode,
interpretDeclarationStmtNode,
interpretIfThenElseStmtNode,
interpretSwitchStmtNode,
interpretBreakStmtNode,
interpretReturnStmtNode,
interpretLoopStmtNode,
interpretDeallocationStmtNode,
interpretFuncDefStmtNode,
interpretExprStmtNode,
interpretAltArrayDefStmtNode,
interpretBindingStmtNode,
interpretImportStmtNode };
/**
* 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)
{
return StmtJumpTable[node->type](node, scope);
}
/**
* 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)
{
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;
}
/**
* 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)
{
ReturnObject *ret = NULL;
ScopeObject *inner = createScopeObject(scope);
if (!inner) return NULL;
ret = interpretStmtNodeList(node->stmts, inner);
deleteScopeObject(inner);
return ret;
}
/**
* Interprets a the main block of code.
*
* \param [in] main The main block of code to interpret.
*
* \param [in] scope The scope to evaluate \a main under.
*
* \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.
*/
int interpretMainNodeScope(MainNode *main, ScopeObject *scope)
{
ReturnObject *ret = NULL;
if (!main) return 1;
ret = interpretBlockNode(main->block, scope);
if (!ret) return 1;
deleteReturnObject(ret);
return 0;
}