diff --git a/include/ast.h b/include/ast.h index 5d5f272..be33c53 100644 --- a/include/ast.h +++ b/include/ast.h @@ -3,13 +3,20 @@ #include "lexer.h" +#define DEFAULT_STMTS_SIZE 512 +#define DEFAULT_ARGS_SIZE 255 + /* - expression → literal | unary | binary | grouping ; - literal → NUMBER | STRING | "true" | "false" | "nil" ; - grouping → "(" expression ")" ; - unary → ( "-" | "!" ) expression ; - binary → expression operator expression ; - operator → "==" | "!=" | "<" | "<=" | ">" | ">=" | "+" | "-" | "*" | "/" ; +expression → equality ; +equality → comparison ( ( "!=" | "==" ) comparison )* ; +comparison → term ( ( ">" | ">=" | "<" | "<=" ) term )* ; +term → factor ( ( "-" | "+" ) factor )* ; +factor → unary ( ( "/" | "*" ) unary )* ; +unary → ( "!" | "-" ) unary | primary ; +primary → NUMBER | STRING | "true" | "false" | "nil" | "(" expression ")" ; +statement → exprStmt | ifStmt | printStmt | block ; +ifStmt → "if" "(" expression ")" statement ( "else" statement )? ; +block → "{" declaration* "}" ; */ typedef enum { @@ -32,18 +39,68 @@ typedef enum { VAL_NIL, VAL_NUMBER, VAL_STRING, + VAL_FN, } value_type_t; +typedef enum { + STMT_BLOCK, + STMT_CLASS, + STMT_EXPR, + STMT_FUN, + STMT_IF, + STMT_PRINT, + STMT_VAR, + STMT_WHILE, +} stmt_type_t; + +typedef enum { + FN_NATIVE, + FN_CUSTOM, +} fn_type_t; + +typedef struct expr_t expr_t; +typedef struct value_t value_t; +typedef struct stmt_t stmt_t; +typedef struct fn_t fn_t; + typedef struct { + expr_t **arguments; + int length; + int capacity; +} arg_array_t; + +typedef struct { + value_t **arguments; + int length; + int capacity; +} val_array_t; + +typedef struct { + stmt_t **statements; + int length; + int capacity; +} stmt_array_t; + +struct value_t { value_type_t type; union { int boolean; double number; char *string; + fn_t *function; } as; -} value_t; +}; -typedef struct expr_t { +typedef struct ht_t ht_t; + +struct fn_t { + fn_type_t type; + int arity; + stmt_t *stmt; + value_t *(*call)(stmt_t *stmt, val_array_t *arguments, ht_t *env); +}; + +struct expr_t { expr_type_t type; int line; union { @@ -59,7 +116,7 @@ typedef struct expr_t { struct { struct expr_t *callee; token_t paren; - struct expr_t **arguments; + arg_array_t *args; } call; struct { struct expr_t *object; @@ -69,7 +126,7 @@ typedef struct expr_t { struct expr_t *expression; } grouping; struct { - value_t value; + value_t *value; } literal; struct { token_t operator; @@ -96,7 +153,49 @@ typedef struct expr_t { token_t name; } variable; } as; -} expr_t; +}; + +struct stmt_t { + stmt_type_t type; + union { + struct { + stmt_array_t *statements; + } block; + struct { + token_t name; + token_t superclass; + struct stmt_t **methods; + } class; + struct { + expr_t *expression; + } expr; + struct { + token_t name; + array_t *params; + struct stmt_t *body; + } function; + struct { + expr_t *condition; + struct stmt_t *then_branch; + struct stmt_t *else_branch; + } _if; + struct { + expr_t *expression; + } print; + struct { + token_t keyword; + expr_t *value; + } _return; + struct { + token_t name; + expr_t *initializer; + } variable; + struct { + expr_t *condition; + struct stmt_t *body; + } _while; + } as; +}; expr_t *create_binary_expr(token_t *operator, expr_t *left, expr_t *right); expr_t *create_unary_expr(token_t *operator, expr_t *right); @@ -105,6 +204,7 @@ expr_t *create_grouping_expr(expr_t *expression); expr_t *create_variable_expr(token_t *name); expr_t *create_assign_expr(expr_t *name, expr_t *value); expr_t *create_logical_expr(token_t *operator, expr_t *left, expr_t *right); +expr_t *create_call_expr(expr_t *callee, token_t *paren, arg_array_t *args); void print_ast(expr_t *expr); #endif diff --git a/include/env.h b/include/env.h index 9da5d39..25bcaa3 100644 --- a/include/env.h +++ b/include/env.h @@ -13,10 +13,10 @@ typedef struct ht_t { #define DEFAULT_HT_SIZE 50 ht_t *ht_init(ht_t *env); -void ht_add(ht_t *ht, char *name, value_t value); +void ht_add(ht_t *ht, char *name, value_t *value); value_t *ht_get(ht_t *ht, token_t *name, int check_enclosing); -void ht_replace(ht_t *ht, char *name, value_t value); -void ht_assign(ht_t *ht, token_t *name, value_t value); +void ht_replace(ht_t *ht, char *name, value_t *value); +void ht_assign(ht_t *ht, token_t *name, value_t *value); void ht_free(ht_t *ht); #endif diff --git a/include/interpreter.h b/include/interpreter.h index a2672f8..e3a241e 100644 --- a/include/interpreter.h +++ b/include/interpreter.h @@ -3,11 +3,11 @@ #include "ast.h" #include "env.h" -#include "stmt.h" +void free_val(value_t *value); void runtime_error(const char *message, int line); -value_t evaluate(expr_t *expr, ht_t *env); -void print_value(value_t value); -void evaluate_statements(stmt_array_t *array, ht_t *env); +value_t *evaluate(expr_t *expr, ht_t *env); +void print_value(value_t *value); +void interpret(stmt_array_t *array); #endif diff --git a/include/lexer.h b/include/lexer.h index f6f2e29..aa3a785 100644 --- a/include/lexer.h +++ b/include/lexer.h @@ -41,6 +41,7 @@ typedef struct { int capacity; } array_t; +void token_add(array_t *array, token_t token); array_t *tokenize(char *filename); void print_tokens(token_t *tokens); void free_array(array_t *array); diff --git a/include/parser.h b/include/parser.h index 004c476..57fd4bc 100644 --- a/include/parser.h +++ b/include/parser.h @@ -1,19 +1,8 @@ #ifndef PARSER_H #define PARSER_H -/* -expression → equality ; -equality → comparison ( ( "!=" | "==" ) comparison )* ; -comparison → term ( ( ">" | ">=" | "<" | "<=" ) term )* ; -term → factor ( ( "-" | "+" ) factor )* ; -factor → unary ( ( "/" | "*" ) unary )* ; -unary → ( "!" | "-" ) unary | primary ; -primary → NUMBER | STRING | "true" | "false" | "nil" | "(" expression ")" ; -*/ - #include "ast.h" #include "lexer.h" -#include "stmt.h" stmt_array_t *parse(token_t *tks); expr_t *parse_expr(token_t *tks); diff --git a/include/stmt.h b/include/stmt.h deleted file mode 100644 index af2bdba..0000000 --- a/include/stmt.h +++ /dev/null @@ -1,77 +0,0 @@ -#ifndef STMT_H -#define STMT_H - -#include "ast.h" -#include "lexer.h" - -#define DEFAULT_STMTS_SIZE 512 - -/* -statement → exprStmt | ifStmt | printStmt | block ; -ifStmt → "if" "(" expression ")" statement - ( "else" statement )? ; - block → "{" declaration* "}" ; -*/ - -typedef enum { - STMT_BLOCK, - STMT_CLASS, - STMT_EXPR, - STMT_FUN, - STMT_IF, - STMT_PRINT, - STMT_VAR, - STMT_WHILE, -} stmt_type_t; - -typedef struct stmt_t stmt_t; - -typedef struct { - stmt_t **statements; - int length; - int capacity; -} stmt_array_t; - -struct stmt_t { - stmt_type_t type; - union { - struct { - stmt_array_t *statements; - } block; - struct { - token_t name; - token_t superclass; - struct stmt_t **methods; - } class; - struct { - expr_t *expression; - } expr; - struct { - token_t name; - array_t *params; - struct stmt_t **body; - } function; - struct { - expr_t *condition; - struct stmt_t *then_branch; - struct stmt_t *else_branch; - } _if; - struct { - expr_t *expression; - } print; - struct { - token_t keyword; - expr_t *value; - } _return; - struct { - token_t name; - expr_t *initializer; - } variable; - struct { - expr_t *condition; - struct stmt_t *body; - } _while; - } as; -}; - -#endif diff --git a/src/ast.c b/src/ast.c index 4a5cdb5..e85be2b 100644 --- a/src/ast.c +++ b/src/ast.c @@ -35,28 +35,29 @@ expr_t *create_literal_expr(token_t *token) expr_t *expr = malloc(sizeof(expr_t)); expr->type = EXPR_LITERAL; expr->line = token->line; + expr->as.literal.value = malloc(sizeof(value_t)); switch (token->type) { case TOKEN_NUMBER: - expr->as.literal.value.type = VAL_NUMBER; + expr->as.literal.value->type = VAL_NUMBER; double num; sscanf(token->value, "%lf", &num); - expr->as.literal.value.as.number = num; + expr->as.literal.value->as.number = num; break; case TOKEN_NIL: - expr->as.literal.value.type = VAL_NIL; - expr->as.literal.value.as.number = 0; + expr->as.literal.value->type = VAL_NIL; + expr->as.literal.value->as.number = 0; break; case TOKEN_TRUE: case TOKEN_FALSE: - expr->as.literal.value.type = VAL_BOOL; - expr->as.literal.value.as.boolean = token->type == TOKEN_TRUE; + expr->as.literal.value->type = VAL_BOOL; + expr->as.literal.value->as.boolean = token->type == TOKEN_TRUE; break; case TOKEN_STRING: - expr->as.literal.value.type = VAL_STRING; - expr->as.literal.value.as.string = strdup(token->value); + expr->as.literal.value->type = VAL_STRING; + expr->as.literal.value->as.string = strdup(token->value); break; default: @@ -113,14 +114,27 @@ expr_t *create_logical_expr(token_t *operator, expr_t *left, expr_t *right) return expr; } +expr_t *create_call_expr(expr_t *callee, token_t *paren, arg_array_t *args) +{ + expr_t *expr = malloc(sizeof(expr_t)); + expr->type = EXPR_CALL; + expr->line = paren->line; + expr->as.call.callee = callee; + expr->as.call.paren.type = paren->type; + expr->as.call.paren.value = strdup(paren->value); + expr->as.call.paren.line = paren->line; + expr->as.call.args = args; + return expr; +} + void print_ast(expr_t *expr) { if (!expr) return; if (expr->type == EXPR_LITERAL) { - switch (expr->as.literal.value.type) { + switch (expr->as.literal.value->type) { case VAL_BOOL: - printf("%s", expr->as.literal.value.as.boolean ? "true" : "false"); + printf("%s", expr->as.literal.value->as.boolean ? "true" : "false"); break; case VAL_NIL: @@ -128,7 +142,7 @@ void print_ast(expr_t *expr) break; case VAL_NUMBER:; - double value = expr->as.literal.value.as.number; + double value = expr->as.literal.value->as.number; if (value == (int) value) { printf("%.1f", value); } else { @@ -137,7 +151,11 @@ void print_ast(expr_t *expr) break; case VAL_STRING: - printf("%s", expr->as.literal.value.as.string); + printf("%s", expr->as.literal.value->as.string); + break; + + case VAL_FN: + printf("<native fn>"); break; } } else if (expr->type == EXPR_BINARY) { diff --git a/src/env.c b/src/env.c index 03bbe96..9af008f 100644 --- a/src/env.c +++ b/src/env.c @@ -5,6 +5,8 @@ #include "env.h" #include "interpreter.h" +void free_statement(stmt_t *stmt); + ht_t *ht_init(ht_t *env) { ht_t *ht = malloc(sizeof(ht_t) * DEFAULT_HT_SIZE); @@ -28,7 +30,7 @@ unsigned int hash(char *key) return h; } -void ht_add(ht_t *ht, char *name, value_t value) +void ht_add(ht_t *ht, char *name, value_t *value) { unsigned int idx = hash(name) % DEFAULT_HT_SIZE; /* Linear probing for collision resolution */ @@ -36,11 +38,14 @@ void ht_add(ht_t *ht, char *name, value_t value) int probe_idx = (idx + i) % DEFAULT_HT_SIZE; if (!ht[probe_idx].name) { ht[probe_idx].name = strdup(name); - ht[probe_idx].value.type = value.type; - if (value.type == VAL_STRING) { - ht[probe_idx].value.as.string = strdup(value.as.string); + ht[probe_idx].value.type = value->type; + if (value->type == VAL_STRING) { + ht[probe_idx].value.as.string = strdup(value->as.string); + } else if (value->type == VAL_FN) { + ht[probe_idx].value.as.function = malloc(sizeof(fn_t)); + memcpy(ht[probe_idx].value.as.function, value->as.function, sizeof(fn_t)); } else { - ht[probe_idx].value.as = value.as; + ht[probe_idx].value.as = value->as; } return; } else { @@ -56,8 +61,15 @@ value_t *ht_get(ht_t *ht, token_t *name, int check_enclosing) /* Linear probing to search for the key */ for (int i = 0; i < DEFAULT_HT_SIZE; i++) { int probe_idx = (idx + i) % DEFAULT_HT_SIZE; - if (ht[probe_idx].name && !strcmp(ht[probe_idx].name, name->value)) - return &ht[probe_idx].value; + if (ht[probe_idx].name && !strcmp(ht[probe_idx].name, name->value)) { + value_t *val = malloc(sizeof(value_t)); + memcpy(val, &ht[probe_idx].value, sizeof(value_t)); + if (val->type == VAL_STRING) { + val->as.string = strdup(ht[probe_idx].value.as.string); + } + + return val; + } } if (check_enclosing) { if (ht->enclosing) { @@ -73,7 +85,7 @@ value_t *ht_get(ht_t *ht, token_t *name, int check_enclosing) return NULL; } -void ht_replace(ht_t *ht, char *name, value_t value) +void ht_replace(ht_t *ht, char *name, value_t *value) { unsigned int idx = hash(name) % DEFAULT_HT_SIZE; @@ -87,20 +99,22 @@ void ht_replace(ht_t *ht, char *name, value_t value) if (ht[probe_idx].value.type == VAL_STRING) { free(ht[probe_idx].value.as.string); } - ht[probe_idx].value.type = value.type; - ht[probe_idx].value.as = value.as; - if (value.type == VAL_STRING) { - ht[probe_idx].value.as.string = strdup(value.as.string); + ht[probe_idx].value.type = value->type; + ht[probe_idx].value.as = value->as; + if (value->type == VAL_STRING) { + ht[probe_idx].value.as.string = strdup(value->as.string); } return; } } } -void ht_assign(ht_t *ht, token_t *name, value_t value) +void ht_assign(ht_t *ht, token_t *name, value_t *value) { - if (ht_get(ht, name, 0)) { + value_t *val = ht_get(ht, name, 0); + if (val) { ht_replace(ht, name->value, value); + free_val(val); return; } if (ht->enclosing) { @@ -119,6 +133,8 @@ void ht_free(ht_t *ht) free(ht[i].name); if (ht[i].value.type == VAL_STRING) { free(ht[i].value.as.string); + } else if (ht[i].value.type == VAL_FN) { + free(ht[i].value.as.function); } } } diff --git a/src/interpreter.c b/src/interpreter.c index de8780b..cdb8fea 100644 --- a/src/interpreter.c +++ b/src/interpreter.c @@ -1,7 +1,8 @@ +#include <errno.h> #include <stdio.h> #include <stdlib.h> #include <string.h> -#include <errno.h> +#include <time.h> #include "ast.h" #include "env.h" @@ -9,12 +10,30 @@ #include "lexer.h" #include "parser.h" -value_t visit_literal(expr_t *expr) +void evaluate_statement(stmt_t *stmt, ht_t *env); + +void free_val(value_t *value) { - return expr->as.literal.value; + if (!value) + return; + if (value->type == VAL_STRING) { + if (value->as.string) { + free(value->as.string); + } + } else if (value->type == VAL_FN) { + + } + free(value); } -value_t visit_grouping(expr_t *expr, ht_t *env) +value_t *visit_literal(expr_t *expr) +{ + value_t *val = malloc(sizeof(value_t)); + memcpy(val, expr->as.literal.value, sizeof(value_t)); + return val; +} + +value_t *visit_grouping(expr_t *expr, ht_t *env) { return evaluate(expr->as.grouping.expression, env); } @@ -26,56 +45,66 @@ void runtime_error(const char *message, int line) exit(70); } -value_t visit_binary(expr_t *expr, ht_t *env) +value_t *visit_binary(expr_t *expr, ht_t *env) { token_type_t op_type = expr->as.binary.operator.type; - value_t right = evaluate(expr->as.binary.right, env); - value_t left = evaluate(expr->as.binary.left, env); + value_t *right = evaluate(expr->as.binary.right, env); + value_t *left = evaluate(expr->as.binary.left, env); // Arithmetic - if (left.type == VAL_NUMBER && right.type == VAL_NUMBER) { - value_t result = {.type = VAL_NUMBER}; + if (left->type == VAL_NUMBER && right->type == VAL_NUMBER) { + value_t *result = malloc(sizeof(value_t)); + result->type = VAL_NUMBER; switch (op_type) { case TOKEN_PLUS: - result.as.number = left.as.number + right.as.number; + result->as.number = left->as.number + right->as.number; + free_val(left); + free_val(right); return result; case TOKEN_MINUS: - result.as.number = left.as.number - right.as.number; + result->as.number = left->as.number - right->as.number; + free_val(left); + free_val(right); return result; case TOKEN_STAR: - result.as.number = left.as.number * right.as.number; + result->as.number = left->as.number * right->as.number; + free_val(left); + free_val(right); return result; case TOKEN_SLASH: - if (right.as.number == 0) { + if (right->as.number == 0) { runtime_error("Division by zero.", expr->line); } - result.as.number = left.as.number / right.as.number; + result->as.number = left->as.number / right->as.number; + free_val(left); + free_val(right); return result; default: break; } + free_val(result); } // Comparison if (op_type == TOKEN_EQUAL_EQUAL || op_type == TOKEN_BANG_EQUAL) { int is_equal; - if (left.type != right.type) { + if (left->type != right->type) { is_equal = 0; } else { - switch (left.type) { + switch (left->type) { case VAL_NUMBER: - is_equal = left.as.number == right.as.number; + is_equal = left->as.number == right->as.number; break; case VAL_BOOL: - is_equal = left.as.boolean == right.as.boolean; + is_equal = left->as.boolean == right->as.boolean; break; case VAL_STRING: - is_equal = strcmp(left.as.string, right.as.string) == 0; + is_equal = strcmp(left->as.string, right->as.string) == 0; break; case VAL_NIL: @@ -87,29 +116,41 @@ value_t visit_binary(expr_t *expr, ht_t *env) break; } } - value_t result = {.type = VAL_BOOL}; - result.as.boolean = op_type == TOKEN_EQUAL_EQUAL ? is_equal : !is_equal; + value_t *result = malloc(sizeof(value_t)); + result->type = VAL_BOOL; + result->as.boolean = op_type == TOKEN_EQUAL_EQUAL ? is_equal : !is_equal; + free_val(left); + free_val(right); return result; } // Number Comparison - if (left.type == VAL_NUMBER && right.type == VAL_NUMBER) { - value_t result = {.type = VAL_BOOL }; + if (left->type == VAL_NUMBER && right->type == VAL_NUMBER) { + value_t *result = malloc(sizeof(value_t)); + result->type = VAL_BOOL; switch (op_type) { case TOKEN_GREATER: - result.as.boolean= left.as.number > right.as.number; + result->as.boolean = left->as.number > right->as.number; + free_val(left); + free_val(right); return result; case TOKEN_GREATER_EQUAL: - result.as.boolean = left.as.number >= right.as.number; + result->as.boolean = left->as.number >= right->as.number; + free_val(left); + free_val(right); return result; case TOKEN_LESS: - result.as.boolean = left.as.number < right.as.number; + result->as.boolean = left->as.number < right->as.number; + free_val(left); + free_val(right); return result; case TOKEN_LESS_EQUAL: - result.as.boolean = left.as.number <= right.as.number; + result->as.boolean = left->as.number <= right->as.number; + free_val(left); + free_val(right); return result; default: break; @@ -117,40 +158,46 @@ value_t visit_binary(expr_t *expr, ht_t *env) } // String concatenation - if (left.type == VAL_STRING && right.type == VAL_STRING) { + if (left->type == VAL_STRING && right->type == VAL_STRING) { if (op_type == TOKEN_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); + value_t *result = malloc(sizeof(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); + free_val(left); + free_val(right); return result; } } // String/number comparisons - if ((left.type == VAL_STRING && right.type == VAL_NUMBER) || - (left.type == VAL_NUMBER && right.type == VAL_STRING)) { + 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}; - + value_t *val = malloc(sizeof(value_t)); + val->type = VAL_NIL; + free_val(left); + free_val(right); + return val; } -int is_truthy(value_t value) +int is_truthy(value_t *value) { - switch (value.type) { + switch (value->type) { case VAL_NIL: return 0; case VAL_BOOL: - return value.as.boolean; + return value->as.boolean; case VAL_NUMBER: - if (value.as.number == 0) + if (value->as.number == 0) return 0; return 1; @@ -162,45 +209,57 @@ int is_truthy(value_t value) } } -value_t visit_unary(expr_t *expr, ht_t *env) +value_t *visit_unary(expr_t *expr, ht_t *env) { - value_t operand = evaluate(expr->as.unary.right, env); + value_t *operand = evaluate(expr->as.unary.right, env); if (expr->as.unary.operator.type == TOKEN_MINUS) { - if (operand.type == VAL_NUMBER) { - value_t result = {.type = VAL_NUMBER, .as.number = -operand.as.number}; + if (operand->type == VAL_NUMBER) { + value_t *result = malloc(sizeof(value_t)); + result->type = VAL_NUMBER; + result->as.number = -operand->as.number; + free_val(operand); return result; } else { runtime_error("Operand must be a number.", expr->line); } } else if (expr->as.unary.operator.type == TOKEN_BANG) { - value_t result = {.type = VAL_BOOL, .as.boolean = !is_truthy(operand)}; + value_t *result = malloc(sizeof(value_t)); + result->type = VAL_BOOL; + result->as.boolean = !is_truthy(operand); + free_val(operand); + return result; } - return (value_t){.type = VAL_NIL}; + value_t *val = malloc(sizeof(value_t)); + val->type = VAL_NIL; + free_val(operand); + return val; } -value_t visit_variable(expr_t *expr, ht_t *env) +value_t *visit_variable(expr_t *expr, ht_t *env) { value_t *val = ht_get(env, &expr->as.variable.name, 1); if (val) { - return *val; + return val; } else { - return (value_t) {.type = VAL_NIL}; + val = malloc(sizeof(value_t)); + val->type = VAL_NIL; + return val; } } -value_t visit_assign(expr_t *expr, ht_t *env) +value_t *visit_assign(expr_t *expr, ht_t *env) { - value_t value = evaluate(expr->as.assign.value, env); + value_t *value = evaluate(expr->as.assign.value, env); ht_assign(env, &expr->as.assign.name->as.variable.name, value); return value; } -value_t visit_logical(expr_t *expr, ht_t *env) +value_t *visit_logical(expr_t *expr, ht_t *env) { - value_t left = evaluate(expr->as.logical.left, env); + value_t *left = evaluate(expr->as.logical.left, env); if (expr->as.logical.operator.type == TOKEN_OR) { if (is_truthy(left)) @@ -213,11 +272,58 @@ value_t visit_logical(expr_t *expr, ht_t *env) return evaluate(expr->as.logical.right, env); } -value_t evaluate(expr_t *expr, ht_t *env) +void val_add(val_array_t *array, value_t *expr) +{ + if (array->length == array->capacity) { + array->capacity *= 2; + array->arguments = realloc(array->arguments, array->capacity * sizeof(expr_t *)); + } + array->arguments[array->length++] = expr; +} + +void free_vals(val_array_t *array) +{ + for (int i = 0; i < array->length; i++) { + free_val(array->arguments[i]); + } + free(array->arguments); + free(array); +} + +value_t *visit_call(expr_t *expr, ht_t *env) +{ + value_t *callee = evaluate(expr->as.call.callee, env); + if (callee->type != VAL_FN) { + runtime_error("Can only call functions and classes.", expr->line); + } + + val_array_t *arguments = malloc(sizeof(val_array_t)); + arguments->arguments = malloc(DEFAULT_ARGS_SIZE * sizeof(value_t *)); + arguments->length = 0; + arguments->capacity = DEFAULT_ARGS_SIZE; + + for (int i = 0; i < expr->as.call.args->length; i++) { + value_t *val = evaluate(expr->as.call.args->arguments[i], env); + val_add(arguments, val); + } + + if (arguments->length != callee->as.function->arity) { + char err[512]; + snprintf(err, 512, "Expected %d arguments but got %d.", callee->as.function->arity, arguments->length); + runtime_error(err, expr->line); + } + value_t *res = callee->as.function->call(callee->as.function->stmt, arguments, env); + free_vals(arguments); + free_val(callee); + return res; +} + +value_t *evaluate(expr_t *expr, ht_t *env) { if (!expr) { - value_t nil_value = {.type = VAL_NIL }; - return nil_value; + value_t *nil = malloc(sizeof(value_t)); + nil->type = VAL_NIL; + return nil; } switch (expr->type) { case EXPR_LITERAL: @@ -234,17 +340,19 @@ value_t evaluate(expr_t *expr, ht_t *env) return visit_assign(expr, env); case EXPR_LOGICAL: return visit_logical(expr, env); + case EXPR_CALL: + return visit_call(expr, env); default: exit(65); break; } } -void print_value(value_t value) +void print_value(value_t *value) { - switch (value.type) { + switch (value->type) { case VAL_BOOL: - printf("%s\n", value.as.boolean == 1 ? "true" : "false"); + printf("%s\n", value->as.boolean == 1 ? "true" : "false"); break; case VAL_NIL: @@ -252,66 +360,22 @@ void print_value(value_t value) break; case VAL_STRING: - printf("%s\n", value.as.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); + if (value->as.number == (int) value->as.number) { + printf("%d\n", (int) value->as.number); } else { - printf("%g\n", value.as.number); + printf("%g\n", value->as.number); } break; - default: - break; - } -} - -void evaluate_block(stmt_array_t *array, ht_t *cur_env, ht_t *scope_env) -{ - ht_t *previous = cur_env; - cur_env = scope_env; - evaluate_statements(array, cur_env); - ht_free(scope_env); - cur_env = previous; -} - -void evaluate_statement(stmt_t *stmt, ht_t *env) -{ - switch (stmt->type) { - case STMT_IF: - if (is_truthy(evaluate(stmt->as._if.condition, env))) { - evaluate_statement(stmt->as._if.then_branch, env); - } else if (stmt->as._if.else_branch) { - evaluate_statement(stmt->as._if.else_branch, env); - } - break; - - case STMT_PRINT: - print_value(evaluate(stmt->as.print.expression, env)); - break; - - case STMT_EXPR: - evaluate(stmt->as.expr.expression, env); - break; - - case STMT_VAR: { - value_t value = {.type = VAL_NIL}; - if (stmt->as.variable.initializer) { - value = evaluate(stmt->as.variable.initializer, env); - } - ht_add(env, stmt->as.variable.name.value, value); - break; - } - - case STMT_BLOCK: - evaluate_block(stmt->as.block.statements, env, ht_init(env)); - break; - - case STMT_WHILE: - while (is_truthy(evaluate(stmt->as._while.condition, env))) { - evaluate_statement(stmt->as._while.body, env); + case VAL_FN: + if (value->as.function->type == FN_NATIVE) { + printf("<native fn>\n"); + } else { + printf("<fn %s>\n", value->as.function->stmt->as.function.name.value); } break; @@ -326,3 +390,121 @@ void evaluate_statements(stmt_array_t *array, ht_t *env) evaluate_statement(array->statements[i], env); } } + +void evaluate_block(stmt_array_t *array, ht_t *cur_env, ht_t *scope_env) +{ + ht_t *previous = cur_env; + cur_env = scope_env; + evaluate_statements(array, cur_env); + ht_free(scope_env); + cur_env = previous; +} + +value_t *_clock(stmt_t *stmt, val_array_t *arguments, ht_t *env) +{ + value_t *val = malloc(sizeof(value_t)); + val->type = VAL_NUMBER; + val->as.number = time(NULL); + return val; +} + +value_t *_call(stmt_t *stmt, val_array_t *arguments, ht_t *env) +{ + ht_t *fn_env = ht_init(env); + for (int i = 0; i < stmt->as.function.params->length; i++) { + ht_add(fn_env, stmt->as.function.params->tokens[i].value, arguments->arguments[i]); + } + + evaluate_block(stmt->as.function.body->as.block.statements, env, fn_env); + + return NULL; +} + +void evaluate_statement(stmt_t *stmt, ht_t *env) +{ + switch (stmt->type) { + case STMT_IF: + if (is_truthy(evaluate(stmt->as._if.condition, env))) { + evaluate_statement(stmt->as._if.then_branch, env); + } else if (stmt->as._if.else_branch) { + evaluate_statement(stmt->as._if.else_branch, env); + } + break; + + case STMT_PRINT:; + value_t *val = evaluate(stmt->as.print.expression, env); + print_value(val); + free_val(val); + break; + + case STMT_EXPR:; + value_t *res = evaluate(stmt->as.expr.expression, env); + free_val(res); + break; + + case STMT_VAR: { + value_t *value = malloc(sizeof(value_t)); + value->type = VAL_NIL; + if (stmt->as.variable.initializer) { + free(value); + value = evaluate(stmt->as.variable.initializer, env); + } + ht_add(env, stmt->as.variable.name.value, value); + free_val(value); + break; + } + + case STMT_BLOCK: + evaluate_block(stmt->as.block.statements, env, ht_init(env)); + break; + + case STMT_WHILE:; + value_t *cond = evaluate(stmt->as._while.condition, env); + while (is_truthy(cond)) { + evaluate_statement(stmt->as._while.body, env); + free_val(cond); + cond = evaluate(stmt->as._while.condition, env); + } + free_val(cond); + break; + + case STMT_FUN:; + fn_t *fn = malloc(sizeof(fn_t)); + fn->type = FN_CUSTOM; + fn->arity = stmt->as.function.params->length; + fn->stmt = stmt; + fn->call = _call; + + value_t *fn_val = malloc(sizeof(value_t)); + fn_val->type = VAL_FN; + fn_val->as.function = fn; + ht_add(env, stmt->as.function.name.value, fn_val); + free_val(fn_val); + free(fn); + break; + + default: + break; + } +} + +void interpret(stmt_array_t *array) +{ + ht_t *env = ht_init(NULL); + value_t *clock_fn = malloc(sizeof(value_t)); + clock_fn->type = VAL_FN; + fn_t *fn = malloc(sizeof(fn_t)); + fn->type = FN_NATIVE; + fn->arity = 0; + /* Native function don't have body */ + fn->stmt = NULL; + fn->call = _clock; + clock_fn->as.function = fn; + + ht_add(env, "clock", clock_fn); + evaluate_statements(array, env); + ht_free(env); + free(clock_fn); + free(fn); + free_statements(array); +} diff --git a/src/parser.c b/src/parser.c index 704c93a..321e291 100644 --- a/src/parser.c +++ b/src/parser.c @@ -4,11 +4,13 @@ #include <errno.h> #include "ast.h" +#include "interpreter.h" #include "lexer.h" #include "parser.h" int current = 0; token_t *tokens; +void free_args(arg_array_t *array); expr_t *expression(void); stmt_t *expression_stmt(void); stmt_t *statement(void); @@ -54,9 +56,7 @@ void free_expr(expr_t *expr) break; case EXPR_LITERAL: - if (expr->as.literal.value.type == VAL_STRING) { - free(expr->as.literal.value.as.string); - } + free(expr->as.literal.value); free(expr); break; @@ -78,6 +78,13 @@ void free_expr(expr_t *expr) free(expr); break; + case EXPR_CALL: + free_args(expr->as.call.args); + free_expr(expr->as.call.callee); + free(expr->as.call.paren.value); + free(expr); + break; + default: break; } @@ -109,6 +116,16 @@ int check(token_type_t type) return tokens[current].type == type; } +int match(token_type_t type) +{ + if (check(type)) { + advance(); + return 1; + } else { + return 0; + } +} + token_t *consume(token_type_t type, char *message) { if (!check(type)) { @@ -123,21 +140,18 @@ token_t *consume(token_type_t type, char *message) expr_t *primary(void) { - if (check(TOKEN_FALSE) || check(TOKEN_TRUE) || check(TOKEN_NIL) || - check(TOKEN_NUMBER) || check(TOKEN_STRING)) { - token_t *tok = peek(); - advance(); + if (match(TOKEN_FALSE) || match(TOKEN_TRUE) || match(TOKEN_NIL) || + match(TOKEN_NUMBER) || match(TOKEN_STRING)) { + token_t *tok = previous(); return create_literal_expr(tok); } - if (check(TOKEN_IDENTIFIER)) { - token_t *tok = peek(); - advance(); + if (match(TOKEN_IDENTIFIER)) { + token_t *tok = previous(); return create_variable_expr(tok); } - if (check(TOKEN_LEFT_PAREN)) { - advance(); + if (match(TOKEN_LEFT_PAREN)) { expr_t *expr = expression(); consume(TOKEN_RIGHT_PAREN, "Expect ')' after expression."); return create_grouping_expr(expr); @@ -146,25 +160,67 @@ expr_t *primary(void) return NULL; } +void arg_add(arg_array_t *array, expr_t *expr) +{ + if (array->length == array->capacity) { + array->capacity *= 2; + array->arguments = realloc(array->arguments, array->capacity * sizeof(expr_t *)); + } + array->arguments[array->length++] = expr; +} + +void free_args(arg_array_t *array) +{ + for (int i = 0; i < array->length; i++) { + free_expr(array->arguments[i]); + } + free(array->arguments); + free(array); +} + +expr_t *call(void) +{ + expr_t *expr = primary(); + while (1) { + if (match(TOKEN_LEFT_PAREN)) { + arg_array_t *args = malloc(sizeof(arg_array_t)); + args->arguments = malloc(DEFAULT_ARGS_SIZE * sizeof(expr_t *)); + args->length = 0; + args->capacity = DEFAULT_ARGS_SIZE; + if (!check(TOKEN_RIGHT_PAREN)) { + do { + if (args->length >= 255) { + error(peek(), "Can't have more than 255 arguments."); + } + arg_add(args, expression()); + } while (match(TOKEN_COMMA)); + } + token_t *paren = consume(TOKEN_RIGHT_PAREN, "Expect ')' after arguments."); + expr = create_call_expr(expr, paren, args); + } else { + break; + } + } + return expr; +} + expr_t *unary(void) { - if (check(TOKEN_BANG) || check(TOKEN_MINUS)) { - token_t *operator = peek(); - advance(); + if (match(TOKEN_BANG) || match(TOKEN_MINUS)) { + token_t *operator = previous(); expr_t *right = unary(); return create_unary_expr(operator, right); } - return primary(); + return call(); } expr_t *factor(void) { expr_t *expr = unary(); - while (check(TOKEN_SLASH) || check(TOKEN_STAR)) { - token_t *operator = peek(); - advance(); + while (match(TOKEN_SLASH) || match(TOKEN_STAR)) { + token_t *operator = previous(); expr_t *right = unary(); expr = create_binary_expr(operator, expr, right); } @@ -176,9 +232,8 @@ expr_t *term(void) { expr_t *expr = factor(); - while (check(TOKEN_MINUS) || check(TOKEN_PLUS)) { - token_t *operator = peek(); - advance(); + while (match(TOKEN_MINUS) || match(TOKEN_PLUS)) { + token_t *operator = previous(); expr_t *right = factor(); expr = create_binary_expr(operator, expr, right); } @@ -190,10 +245,9 @@ expr_t *comparison(void) { expr_t *expr = term(); - while (check(TOKEN_GREATER) || check(TOKEN_GREATER_EQUAL) || check(TOKEN_LESS) - || check(TOKEN_LESS_EQUAL)) { - token_t *operator = peek(); - advance(); + while (match(TOKEN_GREATER) || match(TOKEN_GREATER_EQUAL) || match(TOKEN_LESS) + || match(TOKEN_LESS_EQUAL)) { + token_t *operator = previous(); expr_t *right = term(); expr = create_binary_expr(operator, expr, right); } @@ -205,9 +259,8 @@ expr_t *equality(void) { expr_t *expr = comparison(); - while (check(TOKEN_BANG_EQUAL) || check(TOKEN_EQUAL_EQUAL)) { - token_t *operator = peek(); - advance(); + while (match(TOKEN_BANG_EQUAL) || match(TOKEN_EQUAL_EQUAL)) { + token_t *operator = previous(); expr_t *right = comparison(); expr = create_binary_expr(operator, expr, right); } @@ -219,9 +272,8 @@ expr_t *and(void) { expr_t *expr = equality(); - while (check(TOKEN_AND)) { - token_t *operator = peek(); - advance(); + while (match(TOKEN_AND)) { + token_t *operator = previous(); expr_t *right = equality(); expr = create_logical_expr(operator, expr, right); } @@ -233,9 +285,8 @@ expr_t *or(void) { expr_t *expr = and(); - while (check(TOKEN_OR)) { - token_t *operator = peek(); - advance(); + while (match(TOKEN_OR)) { + token_t *operator = previous(); expr_t *right = and(); expr = create_logical_expr(operator, expr, right); } @@ -247,9 +298,8 @@ expr_t *assignment(void) { expr_t *expr = or(); - if (check(TOKEN_EQUAL)) { - token_t *equals = peek(); - advance(); + if (match(TOKEN_EQUAL)) { + token_t *equals = previous(); expr_t *value = assignment(); if (expr->type == EXPR_VARIABLE) { @@ -302,6 +352,11 @@ void free_statement(stmt_t *stmt) free_expr(stmt->as._while.condition); free_statement(stmt->as._while.body); free(stmt); + } else if (stmt->type == STMT_FUN) { + free(stmt->as.function.name.value); + free_array(stmt->as.function.params); + free_statement(stmt->as.function.body); + free(stmt); } } @@ -318,10 +373,8 @@ stmt_t *for_stmt(void) { consume(TOKEN_LEFT_PAREN, "Expect '(' after 'for'."); stmt_t *initializer = NULL; - if (check(TOKEN_SEMICOLON)) { - advance(); - } else if (check(TOKEN_VAR)) { - advance(); + if (match(TOKEN_SEMICOLON)) { + } else if (match(TOKEN_VAR)) { initializer = var_declaration(); } else { initializer = expression_stmt(); @@ -393,8 +446,7 @@ stmt_t *if_stmt(void) consume(TOKEN_RIGHT_PAREN, "Expect ')' after if condition."); stmt_t *then_branch = statement(); stmt_t *else_branch = NULL; - if (check(TOKEN_ELSE)) { - advance(); + if (match(TOKEN_ELSE)) { else_branch = statement(); } stmt_t *stmt = malloc(sizeof(stmt_t)); @@ -461,36 +513,68 @@ stmt_t *expression_stmt(void) stmt_t *statement(void) { - if (check(TOKEN_FOR)) { - advance(); + if (match(TOKEN_FOR)) { return for_stmt(); } - if (check(TOKEN_IF)) { - advance(); + if (match(TOKEN_IF)) { return if_stmt(); } - if (check(TOKEN_PRINT)) { - advance(); + if (match(TOKEN_PRINT)) { return print_stmt(); } - if (check(TOKEN_WHILE)) { - advance(); + if (match(TOKEN_WHILE)) { return while_stmt(); } - if (check(TOKEN_LEFT_BRACE)) { - advance(); + if (match(TOKEN_LEFT_BRACE)) { return block_stmt(); } return expression_stmt(); } +stmt_t *function(char *kind) +{ + char err[512]; + snprintf(err, 512, "Expect %s name.", kind); + token_t *name = consume(TOKEN_IDENTIFIER, err); + snprintf(err, 512, "Expect '(' after %s name.", kind); + consume(TOKEN_LEFT_PAREN, err); + array_t *parameters = malloc(sizeof(array_t)); + parameters->tokens = malloc(DEFAULT_ARGS_SIZE * sizeof(token_t)); + parameters->length = 0; + parameters->capacity = DEFAULT_ARGS_SIZE; + + if (!check(TOKEN_RIGHT_PAREN)) { + do { + if (parameters->length >= 255) { + error(peek(), "Can't have more than 255 parameters."); + } + + token_t *param = consume(TOKEN_IDENTIFIER, "Expect parameter name."); + token_t param_cpy; + memcpy(¶m_cpy, param, sizeof(token_t)); + param_cpy.value = strdup(param->value); + token_add(parameters, param_cpy); + } while (match(TOKEN_COMMA)); + } + consume(TOKEN_RIGHT_PAREN, "Expect ')' after parameters."); + snprintf(err, 512, "Expect '{' before %s body.", kind); + consume(TOKEN_LEFT_BRACE, err); + stmt_t *stmt = malloc(sizeof(stmt_t)); + stmt->type = STMT_FUN; + stmt->as.function.name.type = name->type; + stmt->as.function.name.value = strdup(name->value); + stmt->as.function.name.line = name->line; + stmt->as.function.params = parameters; + stmt->as.function.body = block_stmt(); + return stmt; +} + stmt_t *var_declaration(void) { token_t *name = consume(TOKEN_IDENTIFIER, "Expect variable name."); expr_t *initializer = NULL; - if (check(TOKEN_EQUAL)) { - advance(); + if (match(TOKEN_EQUAL)) { initializer = expression(); } @@ -507,8 +591,10 @@ stmt_t *var_declaration(void) stmt_t *declaration(void) { - if (check(TOKEN_VAR)) { - advance(); + if (match(TOKEN_FUN)) { + return function("function"); + } + if (match(TOKEN_VAR)) { return var_declaration(); } @@ -522,7 +608,7 @@ stmt_array_t *parse(token_t *tks) return NULL; } else { stmt_array_t *statements = malloc(sizeof(stmt_array_t)); - statements->statements = malloc(DEFAULT_STMTS_SIZE * sizeof(stmt_t)); + statements->statements = malloc(DEFAULT_STMTS_SIZE * sizeof(stmt_t *)); statements->length = 0; statements->capacity = DEFAULT_STMTS_SIZE; while (!end()) { diff --git a/src/rd.c b/src/rd.c index db608fd..6910394 100644 --- a/src/rd.c +++ b/src/rd.c @@ -34,18 +34,15 @@ int main(int argc, char **argv) free_expr(expr); } else if (!strcmp(command, "evaluate")) { expr_t *expr = parse_expr(array->tokens); - value_t val = evaluate(expr, NULL); + value_t *val = evaluate(expr, NULL); print_value(val); free_array(array); free_expr(expr); } else if (!strcmp(command, "run")) { - ht_t *env = ht_init(NULL); stmt_array_t *stmts = parse(array->tokens); if (errno != 65) { - evaluate_statements(stmts, env); - ht_free(env); + interpret(stmts); free_array(array); - free_statements(stmts); } } else { fprintf(stderr, "Unknown command: %s\n", command);