radish/src/interpreter.c
2025-01-06 02:00:01 +00:00

233 lines
4.9 KiB
C

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include "ast.h"
#include "interpreter.h"
#include "lexer.h"
value_t visit_literal(expr_t *expr);
value_t visit_grouping(expr_t *expr);
value_t visit_binary(expr_t *expr);
value_t visit_unary(expr_t *expr);
value_t visit_literal(expr_t *expr)
{
return expr->as.literal.value;
}
value_t visit_grouping(expr_t *expr)
{
return evaluate(expr->as.grouping.expression);
}
void runtime_error(const char *message, int line)
{
fprintf(stderr, "%s\n[line %d]\n", message, line);
errno = 70;
exit(70);
}
value_t visit_binary(expr_t *expr)
{
token_type_t op_type = expr->as.binary.binary_op.type;
value_t right = evaluate(expr->as.binary.right);
value_t left = evaluate(expr->as.binary.left);
// Arithmetic
if (left.type == VAL_NUMBER && right.type == VAL_NUMBER) {
value_t result = {.type = VAL_NUMBER};
switch (op_type) {
case PLUS:
result.as.number = left.as.number + right.as.number;
return result;
case MINUS:
result.as.number = left.as.number - right.as.number;
return result;
case STAR:
result.as.number = left.as.number * right.as.number;
return result;
case SLASH:
if (right.as.number == 0) {
runtime_error("Division by zero.", expr->line);
}
result.as.number = left.as.number / right.as.number;
return result;
default:
break;
}
}
// Comparison
if (op_type == EQUAL_EQUAL || op_type == BANG_EQUAL) {
int is_equal;
if (left.type != right.type) {
is_equal = 0;
} else {
switch (left.type) {
case VAL_NUMBER:
is_equal = left.as.number == right.as.number;
break;
case VAL_BOOL:
is_equal = left.as.boolean == right.as.boolean;
break;
case VAL_STRING:
is_equal = strcmp(left.as.string, right.as.string) == 0;
break;
case VAL_NIL:
is_equal = 1; // nil == nil
break;
default:
is_equal = 0;
break;
}
}
value_t result = {.type = VAL_BOOL};
result.as.boolean = op_type == EQUAL_EQUAL ? is_equal : !is_equal;
return result;
}
// Number Comparison
if (left.type == VAL_NUMBER && right.type == VAL_NUMBER) {
value_t result = {.type = VAL_BOOL };
switch (op_type) {
case GREATER:
result.as.boolean= left.as.number > right.as.number;
return result;
case GREATER_EQUAL:
result.as.boolean = left.as.number >= right.as.number;
return result;
case LESS:
result.as.boolean = left.as.number < right.as.number;
return result;
case LESS_EQUAL:
result.as.boolean = left.as.number <= right.as.number;
return result;
default: break;
}
}
// String concatenation
if (left.type == VAL_STRING && right.type == VAL_STRING) {
if (op_type == PLUS) {
value_t result = {.type = VAL_STRING};
size_t left_len = strlen(left.as.string);
size_t right_len = strlen(right.as.string);
result.as.string = malloc(left_len + right_len + 1);
strcpy(result.as.string, left.as.string);
strcat(result.as.string, right.as.string);
return result;
}
}
// String/number comparisons
if ((left.type == VAL_STRING && right.type == VAL_NUMBER) || (left.type == VAL_NUMBER && right.type == VAL_STRING)) {
runtime_error("Operands must be numbers.", expr->line);
}
runtime_error("Operands must be two numbers or two strings.", expr->line);
return (value_t){.type = VAL_NIL};
}
int is_truthy(value_t *value)
{
switch (value->type) {
case VAL_NIL:
return 0;
case VAL_BOOL:
return value->as.boolean;
case VAL_NUMBER:
if (value->as.number == 0)
return 0;
return 1;
case VAL_STRING:
return 1;
default:
return 0;
}
}
value_t visit_unary(expr_t *expr)
{
value_t operand = evaluate(expr->as.unary.right);
if (expr->as.unary.unary_op.type == MINUS) {
if (operand.type == VAL_NUMBER) {
value_t result = {.type = VAL_NUMBER, .as.number = -operand.as.number};
return result;
} else {
runtime_error("Operand must be a number.", expr->line);
}
} else if (expr->as.unary.unary_op.type == BANG) {
value_t result = {.type = VAL_BOOL, .as.boolean = !is_truthy(&operand)};
return result;
}
return (value_t){.type = VAL_NIL};
}
value_t evaluate(expr_t *expr)
{
if (!expr) {
value_t nil_value = {.type = VAL_NIL };
return nil_value;
}
switch (expr->type) {
case LITERAL:
return visit_literal(expr);
case BINARY:
return visit_binary(expr);
case UNARY:
return visit_unary(expr);
case GROUPING:
return visit_grouping(expr);
default:
errno = 65;
break;
}
}
void print_value(value_t *value)
{
switch (value->type) {
case VAL_BOOL:
printf("%s\n", value->as.boolean == 1 ? "true" : "false");
break;
case VAL_NIL:
printf("nil\n");
break;
case VAL_STRING:
printf("%s\n", value->as.string);
break;
case VAL_NUMBER:
if (value->as.number == (int) value->as.number) {
printf("%d\n", (int) value->as.number);
} else {
printf("%g\n", value->as.number);
}
break;
default:
break;
}
}