Files
clox/src/interpreter.c
T

312 lines
9.7 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#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 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 interpreters 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);
}