285 lines
8.4 KiB
C
285 lines
8.4 KiB
C
#include "interpreter.h"
|
||
#include "expr.h"
|
||
#include "token.h"
|
||
#include <errno.h>
|
||
#include <stdio.h>
|
||
#include <stdlib.h>
|
||
#include <string.h>
|
||
|
||
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 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) {
|
||
printf("Literal: %p\n", (double *) expr->expression.Literal->literal);
|
||
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);
|
||
PrintObject(left);
|
||
PrintObject(right);
|
||
double computed_value;
|
||
|
||
switch(expression->expression.Binary.op->type) {
|
||
case Greater:
|
||
computed_value = left->value.number > right->value.number;
|
||
FreeObject(left);
|
||
FreeObject(right);
|
||
return CreateObject(&computed_value, TRUE);
|
||
//return left > right;
|
||
case Greater_Equal:
|
||
computed_value = left->value.number >= right->value.number;
|
||
FreeObject(left);
|
||
FreeObject(right);
|
||
return CreateObject(&computed_value, TRUE);
|
||
//return left >= right;
|
||
case Less:
|
||
computed_value = left->value.number < right->value.number;
|
||
FreeObject(left);
|
||
FreeObject(right);
|
||
return CreateObject(&computed_value, TRUE);
|
||
//return left < right;
|
||
case Less_Equal:
|
||
computed_value = left->value.number <= right->value.number;
|
||
FreeObject(left);
|
||
FreeObject(right);
|
||
return CreateObject(&computed_value, TRUE);
|
||
//return left <= right;
|
||
case Bang_Equal:
|
||
computed_value = !IsEqual(left, right);
|
||
FreeObject(left);
|
||
FreeObject(right);
|
||
return CreateObject(&computed_value, TRUE);
|
||
//return !IsEqual(left, right);
|
||
case Equal_Equal:
|
||
computed_value = IsEqual(left, right);
|
||
FreeObject(left);
|
||
FreeObject(right);
|
||
return CreateObject(&computed_value, TRUE);
|
||
//return IsEqual(left, right);
|
||
case Minus:
|
||
left->value.number -= right->value.number;
|
||
FreeObject(right);
|
||
return left;
|
||
//return left - right;
|
||
case Slash:
|
||
left->value.number /= right->value.number;
|
||
FreeObject(right);
|
||
return left;
|
||
//return *((double*)left) / *((double*)right);
|
||
case Star:
|
||
printf("%f * %f = %f\n", left->value.number, right->value.number, left->value.number * right->value.number);
|
||
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:
|
||
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) {
|
||
int length = strlen(value);
|
||
object->instance = INS_STRING;
|
||
object->value.string = calloc(length + 1, sizeof(char));
|
||
|
||
strncpy(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;
|
||
} |