Function call, declaration

This commit is contained in:
Night Kaly 2025-01-21 01:29:31 +00:00
parent 56d5d9d84b
commit 6d9d68d693
Signed by: night0721
SSH key fingerprint: SHA256:B/hgVwUoBpx5vdNsXl9w8XwZljA9766uk6T4ubZp5HM
11 changed files with 622 additions and 310 deletions

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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);

View file

@ -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);

View file

@ -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

View file

@ -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) {

View file

@ -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);
}
}
}

View file

@ -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);
}

View file

@ -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(&param_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()) {

View file

@ -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);