#include "interpreter.h" #include "expr.h" #include "token.h" #include #include #include #include Object* Evaluate(Expr*); Object* CreateObject(const void*, TokenType); Object* VisitLiteralExpression(Expr*); Object* VisitGroupingExpression(Expr*); Object* VisitBinaryExpression(Expr*); int IsTruthy(Object*); int IsEqual(Object*, Object*); int ConcatStringObject(Object*, const Object*); void CheckNumberOperands(TokenType, int, ...); void PrintObject(Object*); void PrintObject(Object* o) { switch(o->instance) { case INS_BOOLEAN: printf("BOOL: %d\n", o->value.boolean); break; case INS_DOUBLE: printf("DOUBLE: %f\n", o->value.number); break; case INS_STRING: printf("STRING: %s\n", o->value.string); break; default: printf("NULL\n"); } } void Interpret(Expr* exp) { Object* c = Evaluate(exp); switch(c->instance) { case INS_BOOLEAN: printf("Bool %d\n", c->value.boolean); break; case INS_DOUBLE: printf("Number %f\n", c->value.number); break; case INS_STRING: printf("String '%s'\n", c->value.string); break; default: printf("Default\n"); } printf("Freeing Object...\n"); FreeObject(c); } Object* VisitLiteralExpression(Expr* expr) { return CreateObject(expr->expression.Literal->literal, expr->expression.Literal->type); } //NOTE: We rely on this helper method which simply sends the expression back into //the interpreter’s visitor implementation Object* VisitGroupingExpression(Expr* expression) { return Evaluate(expression->expression.Grouping.expression); } Object* VisitBinaryExpression(Expr* expression) { Object* left = Evaluate(expression->expression.Binary.left); Object* right = Evaluate(expression->expression.Binary.right); double computed_value; switch(expression->expression.Binary.op->type) { case Greater: CheckNumberOperands(expression->expression.Binary.op->type, 2, left, right); computed_value = left->value.number > right->value.number; FreeObject(left); FreeObject(right); return CreateObject(&computed_value, TRUE); //return left > right; case Greater_Equal: CheckNumberOperands(expression->expression.Binary.op->type, 2, left, right); computed_value = left->value.number >= right->value.number; FreeObject(left); FreeObject(right); return CreateObject(&computed_value, TRUE); //return left >= right; case Less: CheckNumberOperands(expression->expression.Binary.op->type, 2, left, right); computed_value = left->value.number < right->value.number; FreeObject(left); FreeObject(right); return CreateObject(&computed_value, TRUE); //return left < right; case Less_Equal: CheckNumberOperands(expression->expression.Binary.op->type, 2, left, right); computed_value = left->value.number <= right->value.number; FreeObject(left); FreeObject(right); return CreateObject(&computed_value, TRUE); //return left <= right; case Bang_Equal: CheckNumberOperands(expression->expression.Binary.op->type, 2, left, right); computed_value = !IsEqual(left, right); FreeObject(left); FreeObject(right); return CreateObject(&computed_value, TRUE); //return !IsEqual(left, right); case Equal_Equal: CheckNumberOperands(expression->expression.Binary.op->type, 2, left, right); computed_value = IsEqual(left, right); FreeObject(left); FreeObject(right); return CreateObject(&computed_value, TRUE); //return IsEqual(left, right); case Minus: CheckNumberOperands(expression->expression.Binary.op->type, 2, left, right); left->value.number -= right->value.number; FreeObject(right); return left; //return left - right; case Slash: CheckNumberOperands(expression->expression.Binary.op->type, 2, left, right); left->value.number /= right->value.number; FreeObject(right); return left; //return *((double*)left) / *((double*)right); case Star: CheckNumberOperands(expression->expression.Binary.op->type, 2, left, right); left->value.number *= right->value.number; FreeObject(right); return left; //return *((double*)left) * *((double*)right); case Plus: if (left->instance == INS_DOUBLE && right->instance == INS_DOUBLE) { left->value.number += right->value.number; FreeObject(right); return left; } if (left->instance == INS_STRING && right->instance == INS_STRING) { if (ConcatStringObject(left, right)) { FreeObject(right); return left; } } break; default: FreeObject(left); FreeObject(right); return NULL; //Should be unreachable. } FreeObject(left); FreeObject(right); // Unreachable return NULL; } Object* VisitUnaryExpression(Expr* expression) { Object* right = Evaluate(expression->expression.Unary.right); Object* c; double computed_value; switch (expression->expression.Unary.op->type) { case Minus: CheckNumberOperands(expression->expression.Unary.op->type, 1, right);//This needs to "throw" if the function fails. computed_value = -right->value.number; FreeObject(right); c = CreateObject(&computed_value, Number); return c; case Bang: computed_value = !IsTruthy(right); FreeObject(right); c = CreateObject(&computed_value, TRUE); //TRUE or FALSE, doesn't matter here since it becomes INS_BOOLEAN in the end. return c; default: FreeObject(right); return NULL; //Should be unreachable. } FreeObject(right); return NULL; //Should be unreachable. } Object* Evaluate(Expr* expression) { switch (expression->type) { case EXPRESSION: fprintf(stderr, "EXPRESSION case seen for expression.\n"); break; case UNARY: return VisitUnaryExpression(expression); case GROUPING: return VisitGroupingExpression(expression->expression.Grouping.expression); case BINARY: return VisitBinaryExpression(expression); case LITERAL: return VisitLiteralExpression(expression); default: fprintf(stderr, "Default case seen for expression.\n"); break; } //return expr.accept(this); return NULL; } Object* CreateObject(const void* value, TokenType type) { Object* object = calloc(1, sizeof(Object)); if (!object) { fprintf(stderr, "Failed to calloc object. %s.\n", strerror(errno)); return NULL; } switch(type) { case NIL: object->instance = INS_NULL; break; case Number: //printf("NUM: %p, %f\n", value, *(double*) value); object->instance = INS_DOUBLE; object->value.number = *(double*) value; break; case TRUE: case FALSE: object->instance = INS_BOOLEAN; object->value.boolean = *(double*) value; break; default: if (value) { unsigned int length = strlen(value); object->instance = INS_STRING; object->value.string = calloc(length + 1, sizeof(char)); if (!object->value.string) { fprintf(stderr, "Faild to calloc %u bytes for a new string Object. %s.\n", length + 1, strerror(errno)); return NULL; } memcpy(object->value.string, value, length); } else object->instance = INS_NULL; //TODO: this is most likely an error, but we'll ignore that for now. } return object; } void FreeObject(Object* object) { if (object->instance == INS_STRING) free(object->value.string); free(object); } int ConcatStringObject(Object* a, const Object* b) { if (!a || !b) return 0; if (a->instance != INS_STRING || b->instance != INS_STRING) return 0; int a_length = strlen(a->value.string); int b_length = strlen(b->value.string); char* c; c = realloc(a->value.string, a_length + b_length + 1); if (!c) { fprintf(stderr, "Failed to realloc for object concat. %s.\n", strerror(errno)); return 0; } a->value.string = c; a->value.string[a_length + b_length] = '\0'; strncat(a->value.string, b->value.string, b_length); return 1; } int IsTruthy(Object* object) { if (!object || object->instance == INS_NULL) return 0; if (object->instance == INS_BOOLEAN) return object->value.boolean; return 1; } int IsEqual(Object* a, Object* b) { if (a->instance == INS_NULL && b->instance == INS_NULL) return 1; if (a->instance == INS_NULL) return 0; if (b->instance == INS_DOUBLE && b->instance == INS_DOUBLE) { return a->value.number == b->value.number; } if (a->instance == INS_STRING && b->instance == INS_STRING) { return strcmp(a->value.string, b->value.string) == 0; } return 0; } void CheckNumberOperands(TokenType operator, int operandCount, ...) { va_list list; va_start(list, operandCount); for(int i = 0; i < operandCount; i++) { Object* operand = va_arg(list, Object*); if (operand->instance != INS_DOUBLE) { va_end(list); return; //TODO: "throw" runtime error "Operand must be a number." } } va_end(list); }