Return statements
This commit is contained in:
parent
6d9d68d693
commit
7ccc3e32ba
5 changed files with 122 additions and 49 deletions
|
@ -51,6 +51,7 @@ typedef enum {
|
||||||
STMT_PRINT,
|
STMT_PRINT,
|
||||||
STMT_VAR,
|
STMT_VAR,
|
||||||
STMT_WHILE,
|
STMT_WHILE,
|
||||||
|
STMT_RETURN,
|
||||||
} stmt_type_t;
|
} stmt_type_t;
|
||||||
|
|
||||||
typedef enum {
|
typedef enum {
|
||||||
|
@ -96,8 +97,9 @@ typedef struct ht_t ht_t;
|
||||||
struct fn_t {
|
struct fn_t {
|
||||||
fn_type_t type;
|
fn_type_t type;
|
||||||
int arity;
|
int arity;
|
||||||
|
ht_t *env;
|
||||||
stmt_t *stmt;
|
stmt_t *stmt;
|
||||||
value_t *(*call)(stmt_t *stmt, val_array_t *arguments, ht_t *env);
|
value_t *(*call)(struct fn_t *stmt, val_array_t *arguments, ht_t *env);
|
||||||
};
|
};
|
||||||
|
|
||||||
struct expr_t {
|
struct expr_t {
|
||||||
|
|
|
@ -10,7 +10,7 @@ typedef struct ht_t {
|
||||||
struct ht_t *enclosing;
|
struct ht_t *enclosing;
|
||||||
} ht_t;
|
} ht_t;
|
||||||
|
|
||||||
#define DEFAULT_HT_SIZE 50
|
#define DEFAULT_HT_SIZE 500
|
||||||
|
|
||||||
ht_t *ht_init(ht_t *env);
|
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);
|
||||||
|
|
|
@ -57,6 +57,9 @@ void ht_add(ht_t *ht, char *name, value_t *value)
|
||||||
|
|
||||||
value_t *ht_get(ht_t *ht, token_t *name, int check_enclosing)
|
value_t *ht_get(ht_t *ht, token_t *name, int check_enclosing)
|
||||||
{
|
{
|
||||||
|
if (!ht) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
unsigned int idx = hash(name->value) % DEFAULT_HT_SIZE;
|
unsigned int idx = hash(name->value) % DEFAULT_HT_SIZE;
|
||||||
/* Linear probing to search for the key */
|
/* Linear probing to search for the key */
|
||||||
for (int i = 0; i < DEFAULT_HT_SIZE; i++) {
|
for (int i = 0; i < DEFAULT_HT_SIZE; i++) {
|
||||||
|
|
|
@ -10,7 +10,12 @@
|
||||||
#include "lexer.h"
|
#include "lexer.h"
|
||||||
#include "parser.h"
|
#include "parser.h"
|
||||||
|
|
||||||
void evaluate_statement(stmt_t *stmt, ht_t *env);
|
typedef struct {
|
||||||
|
int has_returned;
|
||||||
|
value_t *value;
|
||||||
|
} return_state_t;
|
||||||
|
|
||||||
|
void evaluate_statement(stmt_t *stmt, ht_t *env, return_state_t *state);
|
||||||
|
|
||||||
void free_val(value_t *value)
|
void free_val(value_t *value)
|
||||||
{
|
{
|
||||||
|
@ -30,6 +35,9 @@ value_t *visit_literal(expr_t *expr)
|
||||||
{
|
{
|
||||||
value_t *val = malloc(sizeof(value_t));
|
value_t *val = malloc(sizeof(value_t));
|
||||||
memcpy(val, expr->as.literal.value, sizeof(value_t));
|
memcpy(val, expr->as.literal.value, sizeof(value_t));
|
||||||
|
if (val->type == VAL_STRING) {
|
||||||
|
val->as.string = strdup(expr->as.literal.value->as.string);
|
||||||
|
}
|
||||||
return val;
|
return val;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -312,7 +320,7 @@ value_t *visit_call(expr_t *expr, ht_t *env)
|
||||||
snprintf(err, 512, "Expected %d arguments but got %d.", callee->as.function->arity, arguments->length);
|
snprintf(err, 512, "Expected %d arguments but got %d.", callee->as.function->arity, arguments->length);
|
||||||
runtime_error(err, expr->line);
|
runtime_error(err, expr->line);
|
||||||
}
|
}
|
||||||
value_t *res = callee->as.function->call(callee->as.function->stmt, arguments, env);
|
value_t *res = callee->as.function->call(callee->as.function, arguments, callee->as.function->env);
|
||||||
free_vals(arguments);
|
free_vals(arguments);
|
||||||
free_val(callee);
|
free_val(callee);
|
||||||
return res;
|
return res;
|
||||||
|
@ -350,6 +358,11 @@ value_t *evaluate(expr_t *expr, ht_t *env)
|
||||||
|
|
||||||
void print_value(value_t *value)
|
void print_value(value_t *value)
|
||||||
{
|
{
|
||||||
|
if (!value) {
|
||||||
|
printf("nil\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
switch (value->type) {
|
switch (value->type) {
|
||||||
case VAL_BOOL:
|
case VAL_BOOL:
|
||||||
printf("%s\n", value->as.boolean == 1 ? "true" : "false");
|
printf("%s\n", value->as.boolean == 1 ? "true" : "false");
|
||||||
|
@ -384,23 +397,23 @@ void print_value(value_t *value)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void evaluate_statements(stmt_array_t *array, ht_t *env)
|
void evaluate_statements(stmt_array_t *array, ht_t *env, return_state_t *state)
|
||||||
{
|
{
|
||||||
for (int i = 0; i < array->length; i++) {
|
for (int i = 0; i < array->length; i++) {
|
||||||
evaluate_statement(array->statements[i], env);
|
evaluate_statement(array->statements[i], env, state);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void evaluate_block(stmt_array_t *array, ht_t *cur_env, ht_t *scope_env)
|
void evaluate_block(stmt_array_t *array, ht_t *cur_env, ht_t *scope_env, return_state_t *state)
|
||||||
{
|
{
|
||||||
ht_t *previous = cur_env;
|
ht_t *previous = cur_env;
|
||||||
cur_env = scope_env;
|
cur_env = scope_env;
|
||||||
evaluate_statements(array, cur_env);
|
evaluate_statements(array, cur_env, state);
|
||||||
ht_free(scope_env);
|
/* ht_free(scope_env); */
|
||||||
cur_env = previous;
|
cur_env = previous;
|
||||||
}
|
}
|
||||||
|
|
||||||
value_t *_clock(stmt_t *stmt, val_array_t *arguments, ht_t *env)
|
value_t *_clock(fn_t *fn, val_array_t *arguments, ht_t *env)
|
||||||
{
|
{
|
||||||
value_t *val = malloc(sizeof(value_t));
|
value_t *val = malloc(sizeof(value_t));
|
||||||
val->type = VAL_NUMBER;
|
val->type = VAL_NUMBER;
|
||||||
|
@ -408,26 +421,34 @@ value_t *_clock(stmt_t *stmt, val_array_t *arguments, ht_t *env)
|
||||||
return val;
|
return val;
|
||||||
}
|
}
|
||||||
|
|
||||||
value_t *_call(stmt_t *stmt, val_array_t *arguments, ht_t *env)
|
value_t *_call(fn_t *fn, val_array_t *arguments, ht_t *env)
|
||||||
{
|
{
|
||||||
ht_t *fn_env = ht_init(env);
|
ht_t *fn_env = ht_init(fn->env);
|
||||||
for (int i = 0; i < stmt->as.function.params->length; i++) {
|
for (int i = 0; i < fn->stmt->as.function.params->length; i++) {
|
||||||
ht_add(fn_env, stmt->as.function.params->tokens[i].value, arguments->arguments[i]);
|
ht_add(fn_env, fn->stmt->as.function.params->tokens[i].value, arguments->arguments[i]);
|
||||||
}
|
}
|
||||||
|
|
||||||
evaluate_block(stmt->as.function.body->as.block.statements, env, fn_env);
|
return_state_t state = { 0, NULL };
|
||||||
|
|
||||||
|
evaluate_block(fn->stmt->as.function.body->as.block.statements, env, fn_env, &state);
|
||||||
|
|
||||||
|
/* ht_free(fn_env); */
|
||||||
|
if (state.has_returned) {
|
||||||
|
return state.value;
|
||||||
|
}
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
void evaluate_statement(stmt_t *stmt, ht_t *env)
|
void evaluate_statement(stmt_t *stmt, ht_t *env, return_state_t *state)
|
||||||
{
|
{
|
||||||
|
if (state && state->has_returned)
|
||||||
|
return;
|
||||||
switch (stmt->type) {
|
switch (stmt->type) {
|
||||||
case STMT_IF:
|
case STMT_IF:
|
||||||
if (is_truthy(evaluate(stmt->as._if.condition, env))) {
|
if (is_truthy(evaluate(stmt->as._if.condition, env))) {
|
||||||
evaluate_statement(stmt->as._if.then_branch, env);
|
evaluate_statement(stmt->as._if.then_branch, env, state);
|
||||||
} else if (stmt->as._if.else_branch) {
|
} else if (stmt->as._if.else_branch) {
|
||||||
evaluate_statement(stmt->as._if.else_branch, env);
|
evaluate_statement(stmt->as._if.else_branch, env, state);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
@ -454,15 +475,20 @@ void evaluate_statement(stmt_t *stmt, ht_t *env)
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
case STMT_BLOCK:
|
case STMT_BLOCK:;
|
||||||
evaluate_block(stmt->as.block.statements, env, ht_init(env));
|
ht_t *cp_env = ht_init(env);
|
||||||
|
evaluate_block(stmt->as.block.statements, env, cp_env, state);
|
||||||
|
/* ht_free(cp_env); */
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case STMT_WHILE:;
|
case STMT_WHILE:;
|
||||||
value_t *cond = evaluate(stmt->as._while.condition, env);
|
value_t *cond = evaluate(stmt->as._while.condition, env);
|
||||||
while (is_truthy(cond)) {
|
while (is_truthy(cond)) {
|
||||||
evaluate_statement(stmt->as._while.body, env);
|
evaluate_statement(stmt->as._while.body, env, state);
|
||||||
free_val(cond);
|
free_val(cond);
|
||||||
|
if (state->has_returned) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
cond = evaluate(stmt->as._while.condition, env);
|
cond = evaluate(stmt->as._while.condition, env);
|
||||||
}
|
}
|
||||||
free_val(cond);
|
free_val(cond);
|
||||||
|
@ -472,6 +498,7 @@ void evaluate_statement(stmt_t *stmt, ht_t *env)
|
||||||
fn_t *fn = malloc(sizeof(fn_t));
|
fn_t *fn = malloc(sizeof(fn_t));
|
||||||
fn->type = FN_CUSTOM;
|
fn->type = FN_CUSTOM;
|
||||||
fn->arity = stmt->as.function.params->length;
|
fn->arity = stmt->as.function.params->length;
|
||||||
|
fn->env = env;
|
||||||
fn->stmt = stmt;
|
fn->stmt = stmt;
|
||||||
fn->call = _call;
|
fn->call = _call;
|
||||||
|
|
||||||
|
@ -482,6 +509,14 @@ void evaluate_statement(stmt_t *stmt, ht_t *env)
|
||||||
free_val(fn_val);
|
free_val(fn_val);
|
||||||
free(fn);
|
free(fn);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case STMT_RETURN:;
|
||||||
|
value_t *value = NULL;
|
||||||
|
if (stmt->as._return.value) {
|
||||||
|
value = evaluate(stmt->as._return.value, env);
|
||||||
|
}
|
||||||
|
state->has_returned = 1;
|
||||||
|
state->value = value;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
|
@ -502,7 +537,10 @@ void interpret(stmt_array_t *array)
|
||||||
clock_fn->as.function = fn;
|
clock_fn->as.function = fn;
|
||||||
|
|
||||||
ht_add(env, "clock", clock_fn);
|
ht_add(env, "clock", clock_fn);
|
||||||
evaluate_statements(array, env);
|
|
||||||
|
return_state_t state = { 0, NULL };
|
||||||
|
|
||||||
|
evaluate_statements(array, env, &state);
|
||||||
ht_free(env);
|
ht_free(env);
|
||||||
free(clock_fn);
|
free(clock_fn);
|
||||||
free(fn);
|
free(fn);
|
||||||
|
|
84
src/parser.c
84
src/parser.c
|
@ -29,6 +29,7 @@ void error(token_t *token, char *message)
|
||||||
fprintf(stderr, "[line %d] at '%s': %s\n", token->line, token->value, message);
|
fprintf(stderr, "[line %d] at '%s': %s\n", token->line, token->value, message);
|
||||||
}
|
}
|
||||||
errno = 65;
|
errno = 65;
|
||||||
|
exit(65);
|
||||||
synchronize();
|
synchronize();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -330,34 +331,42 @@ void free_statement(stmt_t *stmt)
|
||||||
if (!stmt) {
|
if (!stmt) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (stmt->type == STMT_PRINT) {
|
switch (stmt->type) {
|
||||||
free_expr(stmt->as.print.expression);
|
case STMT_PRINT:
|
||||||
free(stmt);
|
free_expr(stmt->as.print.expression);
|
||||||
} else if (stmt->type == STMT_EXPR) {
|
break;
|
||||||
free_expr(stmt->as.expr.expression);
|
case STMT_EXPR:
|
||||||
free(stmt);
|
free_expr(stmt->as.expr.expression);
|
||||||
} else if (stmt->type == STMT_VAR) {
|
break;
|
||||||
free(stmt->as.variable.name.value);
|
case STMT_VAR:
|
||||||
free_expr(stmt->as.variable.initializer);
|
free(stmt->as.variable.name.value);
|
||||||
free(stmt);
|
free_expr(stmt->as.variable.initializer);
|
||||||
} else if (stmt->type == STMT_BLOCK) {
|
break;
|
||||||
free_statements(stmt->as.block.statements);
|
case STMT_BLOCK:
|
||||||
free(stmt);
|
free_statements(stmt->as.block.statements);
|
||||||
} else if (stmt->type == STMT_IF) {
|
break;
|
||||||
free_expr(stmt->as._if.condition);
|
case STMT_IF:
|
||||||
free_statement(stmt->as._if.then_branch);
|
free_expr(stmt->as._if.condition);
|
||||||
free_statement(stmt->as._if.else_branch);
|
free_statement(stmt->as._if.then_branch);
|
||||||
free(stmt);
|
free_statement(stmt->as._if.else_branch);
|
||||||
} else if (stmt->type == STMT_WHILE) {
|
break;
|
||||||
free_expr(stmt->as._while.condition);
|
case STMT_WHILE:
|
||||||
free_statement(stmt->as._while.body);
|
free_expr(stmt->as._while.condition);
|
||||||
free(stmt);
|
free_statement(stmt->as._while.body);
|
||||||
} else if (stmt->type == STMT_FUN) {
|
break;
|
||||||
free(stmt->as.function.name.value);
|
case STMT_FUN:
|
||||||
free_array(stmt->as.function.params);
|
free(stmt->as.function.name.value);
|
||||||
free_statement(stmt->as.function.body);
|
free_array(stmt->as.function.params);
|
||||||
free(stmt);
|
free_statement(stmt->as.function.body);
|
||||||
|
break;
|
||||||
|
case STMT_RETURN:
|
||||||
|
free(stmt->as._return.keyword.value);
|
||||||
|
free_expr(stmt->as._return.value);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
free(stmt);
|
||||||
}
|
}
|
||||||
|
|
||||||
void free_statements(stmt_array_t *array)
|
void free_statements(stmt_array_t *array)
|
||||||
|
@ -467,6 +476,24 @@ stmt_t *print_stmt(void)
|
||||||
return stmt;
|
return stmt;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
stmt_t *return_stmt(void)
|
||||||
|
{
|
||||||
|
token_t *keyword = previous();
|
||||||
|
expr_t *value = NULL;
|
||||||
|
if (!check(TOKEN_SEMICOLON)) {
|
||||||
|
value = expression();
|
||||||
|
}
|
||||||
|
|
||||||
|
consume(TOKEN_SEMICOLON, "Expect ';' after return value.");
|
||||||
|
stmt_t *stmt = malloc(sizeof(stmt_t));
|
||||||
|
stmt->type = STMT_RETURN;
|
||||||
|
stmt->as._return.keyword.type = keyword->type;
|
||||||
|
stmt->as._return.keyword.value = strdup(keyword->value);
|
||||||
|
stmt->as._return.keyword.line = keyword->line;
|
||||||
|
stmt->as._return.value = value;
|
||||||
|
return stmt;
|
||||||
|
}
|
||||||
|
|
||||||
stmt_t *while_stmt(void)
|
stmt_t *while_stmt(void)
|
||||||
{
|
{
|
||||||
consume(TOKEN_LEFT_PAREN, "Expect '(' after 'while'.");
|
consume(TOKEN_LEFT_PAREN, "Expect '(' after 'while'.");
|
||||||
|
@ -522,6 +549,9 @@ stmt_t *statement(void)
|
||||||
if (match(TOKEN_PRINT)) {
|
if (match(TOKEN_PRINT)) {
|
||||||
return print_stmt();
|
return print_stmt();
|
||||||
}
|
}
|
||||||
|
if (match(TOKEN_RETURN)) {
|
||||||
|
return return_stmt();
|
||||||
|
}
|
||||||
if (match(TOKEN_WHILE)) {
|
if (match(TOKEN_WHILE)) {
|
||||||
return while_stmt();
|
return while_stmt();
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue