From ef0cbf240782010a4b71d32546e022dfd4f0b6bf Mon Sep 17 00:00:00 2001 From: Bad Diode Date: Sun, 31 Oct 2021 14:25:44 +0100 Subject: Add number of arguments check in function calls --- src/parser.c | 83 +++++++++++++++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 71 insertions(+), 12 deletions(-) (limited to 'src/parser.c') diff --git a/src/parser.c b/src/parser.c index 59e8075..d4deb74 100644 --- a/src/parser.c +++ b/src/parser.c @@ -313,6 +313,7 @@ parse_list(Parser *parser, Errors *errors) { Object *root = object_alloc(start, OBJ_TYPE_PAIR); root->head = NULL; root->tail = NULL; + root->n_elems = 0; Object *current = root; bool first = true; while (has_next_token(parser)) { @@ -346,6 +347,7 @@ parse_list(Parser *parser, Errors *errors) { next->tail = NULL; current->tail = next; current = current->tail; + root->n_elems++; } error_push(errors, (Error){ .type = ERR_TYPE_PARSER, @@ -404,16 +406,16 @@ parse_tree(Parser *parser, Errors *errors) { return NULL; } -bool +Object * symbol_in_env(Environment *env, Object *symbol) { while (env != NULL) { Object *found = ht_lookup(env->table, symbol); if (found != NULL) { - return true; + return found; } env = env->parent; } - return false; + return NULL; } void @@ -423,7 +425,7 @@ check_object_scope(Environment *env, Object *obj, Errors *errors) { } switch (obj->type) { case OBJ_TYPE_SYMBOL: { - if (!symbol_in_env(env, obj)) { + if (symbol_in_env(env, obj) == NULL) { error_push(errors, (Error){ .type = ERR_TYPE_PARSER, .value = ERR_SYMBOL_NOT_FOUND, @@ -505,6 +507,54 @@ remove_unused_expr(Object *obj) { } } +void +check_function_args(Environment *env, Object *obj, Errors *errors) { + if (obj == NULL || obj->visited) { + return; + } + obj->visited = true; + switch (obj->type) { + case OBJ_TYPE_SYMBOL: { + Object *found = symbol_in_env(env, obj); + check_function_args(env, found, errors); + } break; + case OBJ_TYPE_DEF: + case OBJ_TYPE_SET: { + check_function_args(env, obj->var_expr, errors); + } break; + case OBJ_TYPE_IF: { + check_function_args(env, obj->condition, errors); + check_function_args(env, obj->expr_true, errors); + check_function_args(env, obj->expr_false, errors); + } break; + case OBJ_TYPE_PAIR: { + Object *head = obj->head; + if (IS_SYMBOL(head)) { + head = symbol_in_env(env, head); + } + if (IS_LAMBDA(head)) { + if (obj->n_elems != array_size(head->params)) { + error_push(errors, (Error){ + .type = ERR_TYPE_PARSER, + .value = ERR_NOT_ENOUGH_ARGS, + .line = obj->line, + .col = obj->col + }); + } + } + check_function_args(env, head, errors); + check_function_args(env, obj->tail, errors); + } break; + case OBJ_TYPE_LAMBDA: { + for (size_t i = 0; i < array_size(obj->body); i++) { + Object *expr = obj->body[i]; + check_function_args(obj->env, expr, errors); + } + } break; + default: break; + } +} + Root * parse(Token *tokens, Errors *errors) { array_init(roots, 0); @@ -564,7 +614,6 @@ parse(Token *tokens, Errors *errors) { } } array_push(final_roots, root); - remove_unused_expr(root); if (errors->n != 0) { return NULL; @@ -573,6 +622,19 @@ parse(Token *tokens, Errors *errors) { array_free(roots); roots = final_roots; + // Check that the number of arguments in function calls match the number of + // formal parameters. + for (size_t i = 0; i < array_size(roots); i++) { + Object *root = roots[i]; + check_function_args(global_env, root, errors); + if (errors->n != 0) { + return NULL; + } + } + + // TODO: Type check basic expressions (e.g. arithmetic/numeric comparisons). + // We can't be sure when we have functions unless the return type is known. + return roots; } @@ -591,6 +653,7 @@ object_alloc(Token tok, ObjectType type) { node->line = tok.line; node->col = tok.col; node->type = type; + node->visited = false; array_push(objects, node); return node; } @@ -739,13 +802,9 @@ object_equal(Object *a, Object *b) { return false; } } + return true; } break; - case OBJ_TYPE_LAMBDA: { - // return a->closure == b.closure; - } break; - default: { - return false; - } break; + default: break; } - return true; + return false; } -- cgit v1.2.1