From 729f07655aec0964b4a695851e2f461ed56c8f19 Mon Sep 17 00:00:00 2001 From: Tim Teichmann Date: Fri, 24 Jul 2026 23:52:09 +0200 Subject: [PATCH] feat: lambda functions --- lisp.c | 346 +++++++++++++++++++++++++++++++++++++-------------------- lisp.h | 5 + 2 files changed, 229 insertions(+), 122 deletions(-) diff --git a/lisp.c b/lisp.c index 2b8f4ea..c4af421 100644 --- a/lisp.c +++ b/lisp.c @@ -100,6 +100,9 @@ void node_print(Node node) { Nob_String_View func_name = node.as_function.name; printf(SV_Fmt, SV_Arg(func_name)); } break; + case NODE_KIND_LAMBDA: { + printf(""); + } break; default: UNREACHABLE("Unknown node type"); } @@ -300,125 +303,177 @@ int eval_one(Node input, Scopes *scopes, Node *result) { return 0; } - Node head = children->items[0]; - if (head.kind != NODE_KIND_SYMBOL) { - *result = input; - return 0; - } - - if (nob_sv_eq(head.as_symbol, - (Nob_String_View){.count = 2, .data = "if"})) { - if (children->count != 3 + 1) { - printf("'if' expects three arguments!\n"); - return 1; - } - - Node condition = {0}; - int ret = eval_one(children->items[0 + 1], scopes, &condition); - if (ret != 0) { - return ret; - } - - if (node_is_true(condition)) { - ret = eval_one(children->items[1 + 1], scopes, result); - } else { - ret = eval_one(children->items[2 + 1], scopes, result); - } - return ret; - } - - if (nob_sv_eq(head.as_symbol, - (Nob_String_View){.count = 6, .data = "define"})) { - if (children->count != 2 + 1) { - printf("'define' expects two arguments!\n"); - return 1; - } - Node name_node = children->items[0 + 1]; - if (name_node.kind != NODE_KIND_SYMBOL) { - printf("First argument of 'define' must be a symbol!\n"); - return 1; - } - - Node value = {0}; - int ret = eval_one(children->items[1 + 1], scopes, &value); - if (ret != 0) { - return ret; - } - - nob_da_foreach(ScopeObject, scope_object, &global_scope) { - if (nob_sv_eq(scope_object->name, name_node.as_symbol)) { - scope_object->node = value; - *result = children->items[0 + 1]; - return 0; - } - } - - ScopeObject obj = - (ScopeObject){.name = name_node.as_symbol, .node = value}; - nob_da_append(&global_scope, obj); - *result = children->items[0 + 1]; - return 0; - } - - if (nob_sv_eq(head.as_symbol, - (Nob_String_View){.count = 3, .data = "let"})) { - if (children->count != 2 + 1) { - printf("'let' expects two arguments!\n"); - return 1; - } - - Node definitions = children->items[0 + 1]; - if (definitions.kind != NODE_KIND_LIST) { - printf("First argument of 'let' must be a list!\n"); - return 1; - } - - Node expression = children->items[1 + 1]; - if (expression.kind != NODE_KIND_LIST) { - printf("Second argument of 'let' must be a list!\n"); - return 1; - } - - // it's fine to allocate this on the stack because we will throw it - // off the scope stack after parsing 'let' anyways therefore it'll - // outlive the recursive call - Scope scope = {0}; - - nob_da_foreach(Node, def, definitions.as_list) { - if (def->kind != NODE_KIND_LIST || def->as_list->count != 2 || - def->as_list->items[0].kind != NODE_KIND_SYMBOL) { - printf("'let' expects a list of key-value pairs, e.g. " - "'(let ((x 42) (y 2)) (+ x y))'\n"); + Node head_node = children->items[0]; + if (head_node.kind == NODE_KIND_SYMBOL) { + if (nob_sv_eq(head_node.as_symbol, + (Nob_String_View){.count = 2, .data = "if"})) { + if (children->count != 3 + 1) { + printf("'if' expects three arguments!\n"); return 1; } - Node key = def->as_list->items[0]; - Node value_node = def->as_list->items[1]; - - Node value = {0}; - int ret = eval_one(value_node, scopes, &value); - if (ret != 0) - return ret; - - ScopeObject obj = - (ScopeObject){.name = key.as_symbol, .node = value}; - nob_da_append(&scope, obj); - } - - Node expression_value = {0}; - { - nob_da_append(scopes, &scope); - - int ret = eval_one(expression, scopes, &expression_value); + Node condition = {0}; + int ret = eval_one(children->items[0 + 1], scopes, &condition); if (ret != 0) { return ret; } - UNUSED(nob_da_pop(scopes)); + if (node_is_true(condition)) { + ret = eval_one(children->items[1 + 1], scopes, result); + } else { + ret = eval_one(children->items[2 + 1], scopes, result); + } + return ret; } - *result = expression_value; - return 0; + if (nob_sv_eq(head_node.as_symbol, + (Nob_String_View){.count = 6, .data = "define"})) { + if (children->count != 2 + 1) { + printf("'define' expects two arguments!\n"); + return 1; + } + Node name_node = children->items[0 + 1]; + if (name_node.kind != NODE_KIND_SYMBOL) { + printf("First argument of 'define' must be a symbol!\n"); + return 1; + } + + Node value = {0}; + int ret = eval_one(children->items[1 + 1], scopes, &value); + if (ret != 0) { + return ret; + } + + nob_da_foreach(ScopeObject, scope_object, &global_scope) { + if (nob_sv_eq(scope_object->name, name_node.as_symbol)) { + scope_object->node = value; + *result = children->items[0 + 1]; + return 0; + } + } + + ScopeObject obj = + (ScopeObject){.name = name_node.as_symbol, .node = value}; + nob_da_append(&global_scope, obj); + *result = children->items[0 + 1]; + return 0; + } + + if (nob_sv_eq(head_node.as_symbol, + (Nob_String_View){.count = 3, .data = "let"})) { + if (children->count != 2 + 1) { + printf("'let' expects two arguments!\n"); + return 1; + } + + Node definitions = children->items[0 + 1]; + if (definitions.kind != NODE_KIND_LIST) { + printf("First argument of 'let' must be a list!\n"); + return 1; + } + + Node expression = children->items[1 + 1]; + if (expression.kind != NODE_KIND_LIST) { + printf("Second argument of 'let' must be a list!\n"); + return 1; + } + + // it's fine to allocate this on the stack because we will throw + // it off the scope stack after parsing 'let' anyways therefore + // it'll outlive the recursive call + Scope scope = {0}; + + nob_da_foreach(Node, def, definitions.as_list) { + if (def->kind != NODE_KIND_LIST || + def->as_list->count != 2 || + def->as_list->items[0].kind != NODE_KIND_SYMBOL) { + printf("'let' expects a list of key-value pairs, e.g. " + "'(let ((x 42) (y 2)) (+ x y))'\n"); + return 1; + } + + Node key = def->as_list->items[0]; + Node value_node = def->as_list->items[1]; + + Node value = {0}; + int ret = eval_one(value_node, scopes, &value); + if (ret != 0) + return ret; + + ScopeObject obj = + (ScopeObject){.name = key.as_symbol, .node = value}; + nob_da_append(&scope, obj); + } + + Node expression_value = {0}; + { + nob_da_append(scopes, &scope); + + int ret = eval_one(expression, scopes, &expression_value); + if (ret != 0) { + return ret; + } + + UNUSED(nob_da_pop(scopes)); + } + + *result = expression_value; + return 0; + } + + if (nob_sv_eq(head_node.as_symbol, + (Nob_String_View){.count = 6, .data = "lambda"})) { + if (children->count != 2 + 1) { + printf("'lambda' expects two argument!\n"); + return 1; + } + + Node params = children->items[0 + 1]; + if (params.kind != NODE_KIND_LIST) { + printf("'lambda' expects a list of parameters as the first " + "argument!\n"); + return 1; + } + + nob_da_foreach(Node, node, params.as_list) { + if (node->kind != NODE_KIND_SYMBOL) { + printf("'lambda' expects a list of symbols " + "(parameters) as " + "the " + "first argument!\n"); + return 1; + } + } + + result->kind = NODE_KIND_LAMBDA; + + Nodes *params_list = malloc(sizeof(Nodes)); + if (params_list == NULL) { + printf("Failed to allocate memory, something is really " + "wrong!\n"); + return 1; + } + memcpy(params_list, params.as_list, sizeof(Nodes)); + + result->as_lambda.params = params_list; + + Node *body = malloc(sizeof(Node)); + if (body == NULL) { + printf("Failed to allocate memory, something is really " + "wrong!\n"); + return 1; + } + memcpy(body, &children->items[1 + 1], sizeof(Node)); + + result->as_lambda.body = body; + return 0; + } + } + + Node head = {0}; + int ret = eval_one(head_node, scopes, &head); + if (ret != 0) { + return ret; } Nodes args = {0}; @@ -431,22 +486,69 @@ int eval_one(Node input, Scopes *scopes, Node *result) { nob_da_append(&args, n); } - Node func_node = {0}; - int ret = eval_one(head, scopes, &func_node); - if (ret != 0) { - return ret; - } - - if (func_node.kind == NODE_KIND_FUNCTION) { - func_node.as_function.apply(args, scopes, result); + switch (head.kind) { + case NODE_KIND_FUNCTION: { + head.as_function.apply(args, scopes, result); return 0; } + case NODE_KIND_LAMBDA: { + Nodes *lambda_params = head.as_lambda.params; + if (lambda_params == NULL) { + printf("Couldn't evaluate lambda, no params!\n"); + return 1; + } - printf("Not a function: '"); - node_print(func_node); - printf("'\n"); - return 1; + if (lambda_params->count != args.count) { + printf("Invalid amount of arguments to lambda: expected %zu, " + "found %zu\n!", + lambda_params->count, args.count); + return 1; + } + + Node *lambda_body = head.as_lambda.body; + if (lambda_body == NULL) { + printf("Couldn't evaluate lambda, no body!\n"); + return 1; + } + + Scope scope = {0}; + // params->count == args.count + for (size_t i = 0; i < lambda_params->count; i++) { + Node param = lambda_params->items[i]; + // this should already be checked when we emit the lambda node + NOB_ASSERT(param.kind == NODE_KIND_SYMBOL); + Node value = args.items[i]; + + ScopeObject obj = {.name = param.as_symbol, .node = value}; + nob_da_append(&scope, obj); + } + + Node expression_value = {0}; + { + nob_da_append(scopes, &scope); + + int ret = eval_one(*lambda_body, scopes, &expression_value); + if (ret != 0) { + return ret; + } + + UNUSED(nob_da_pop(scopes)); + } + + *result = expression_value; + return 0; + } break; + default: { + printf("Not a function-like object: '"); + node_print(head); + printf("'\n"); + return 1; + } + } } + case NODE_KIND_LAMBDA: { + return 0; + } break; default: return 1; } diff --git a/lisp.h b/lisp.h index 4a8ee70..888268b 100644 --- a/lisp.h +++ b/lisp.h @@ -26,6 +26,7 @@ typedef enum e_NodeKind { NODE_KIND_LIST, NODE_KIND_NUMBER, NODE_KIND_FUNCTION, + NODE_KIND_LAMBDA, } NodeKind; typedef struct s_Node Node; @@ -46,6 +47,10 @@ struct s_Node { Nob_String_View name; LispFunction apply; } as_function; + struct { + Nodes *params; + Node *body; + } as_lambda; }; };