From 5dab1701cde93e90a174a1c04248b2424119e156 Mon Sep 17 00:00:00 2001 From: Tim Teichmann Date: Tue, 21 Jul 2026 00:18:11 +0200 Subject: [PATCH] feat: evaluation --- lisp.c | 244 ++++++++++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 231 insertions(+), 13 deletions(-) diff --git a/lisp.c b/lisp.c index 8e36980..8dcbdd2 100644 --- a/lisp.c +++ b/lisp.c @@ -1,12 +1,15 @@ #include #include #include +#include #include +#include +#include #define NOB_IMPLEMENTATION #include "nob.h" -typedef enum e_Token_Kind { +typedef enum e_TokenKind { TOKEN_KIND_PAR_LEFT, TOKEN_KIND_PAR_RIGHT, TOKEN_KIND_NUMBER, @@ -24,6 +27,90 @@ typedef struct s_Tokens { size_t capacity; } Tokens; +typedef enum e_NodeKind { + NODE_KIND_SYMBOL, + NODE_KIND_LIST, + NODE_KIND_NUMBER, + NODE_KIND_FUNCTION, +} NodeKind; + +typedef struct s_Node { + NodeKind kind; + void *data; +} Node; + +typedef struct s_Nodes { + Node *items; + size_t count; + size_t capacity; +} Nodes; + +typedef Node *(*lisp_function_ptr)(Nodes *args); +typedef struct s_LispFunction { + Nob_String_View name; + lisp_function_ptr apply; +} LispFunction; + +Node *lisp_function_addition(Nodes *args) { + if (args->count != 2) { + printf("'+' expects two arguments!\n"); + return NULL; + } + + if (args->items[0].kind != NODE_KIND_NUMBER || + args->items[1].kind != NODE_KIND_NUMBER) { + printf("'+' can only add numbers, you specified something else!\n"); + return NULL; + } + + Node *node = malloc(sizeof(Node)); + node->kind = NODE_KIND_NUMBER; + + long long *num = malloc(sizeof(long long)); + // trust me, this is safe :) + long long a = *((long long *)args->items[0].data); + long long b = *((long long *)args->items[1].data); + *num = a + b; + + node->data = num; + return node; +} + +static LispFunction globalScope[] = { + (LispFunction){.name = (Nob_String_View){.items = "+", .count = 1}, + .apply = lisp_function_addition}}; + +Node *lisp_function_lookup(Nob_String_View name) { + for (size_t i = 0; i < NOB_ARRAY_LEN(globalScope); i++) { + if (nob_sv_eq(globalScope[i].name, name)) { + Node *node = malloc(sizeof(Node)); + node->kind = NODE_KIND_FUNCTION; + // taking the reference should be fine because globalScope is in the + // static memory + node->data = &globalScope[i]; + return node; + } + } + + printf("Undefined symbol: '" SV_Fmt "'!\n", SV_Arg(name)); + return NULL; +} + +const char *token_kind_name(TokenKind kind) { + switch (kind) { + case TOKEN_KIND_PAR_LEFT: + return "PAR_LEFT"; + case TOKEN_KIND_PAR_RIGHT: + return "PAR_RIGHT"; + case TOKEN_KIND_NUMBER: + return "NUMBER"; + case TOKEN_KIND_SYMBOL: + return "SYMBOL"; + default: + UNREACHABLE("Unknown token type"); + } +} + void tokenize(Nob_String_View content, Tokens *tokens) { for (size_t i = 0; i < content.count; i++) { char c = content.data[i]; @@ -78,19 +165,140 @@ void tokenize(Nob_String_View content, Tokens *tokens) { } } -const char *token_kind_name(TokenKind kind) { - switch (kind) { - case TOKEN_KIND_PAR_LEFT: - return "PAR_LEFT"; - case TOKEN_KIND_PAR_RIGHT: - return "PAR_RIGHT"; - case TOKEN_KIND_NUMBER: - return "NUMBER"; - case TOKEN_KIND_SYMBOL: - return "SYMBOL"; - default: - UNREACHABLE("Unknown token type"); +Node *parse(Tokens *tokens) { + if (tokens->count == 0) { + printf("unexpected end of input"); + return NULL; } + + Token tok = tokens->items[0]; + tokens->items++; + tokens->count--; + + Node *node = malloc(sizeof(Node)); + if (tok.kind == TOKEN_KIND_PAR_LEFT) { + Nodes *nodes = malloc(sizeof(Nodes)); + memset(nodes, 0, sizeof(Nodes)); + + while (true) { + if (tokens->count == 0) { + printf("expected closing ')'"); + return NULL; + } + + if (tokens->items[0].kind == TOKEN_KIND_PAR_RIGHT) { + tokens->items++; + tokens->count--; + break; + } + + Node *node = parse(tokens); + if (node == NULL) + return NULL; + + nob_da_append(nodes, *node); + } + + node->kind = NODE_KIND_LIST; + node->data = nodes; + return node; + } + + if (tok.kind == TOKEN_KIND_PAR_RIGHT) { + printf("unexpected ')'"); + return NULL; + } + + switch (tok.kind) { + case TOKEN_KIND_NUMBER: { + long long *num = malloc(sizeof(long long)); + *num = strtoll(tok.data.data, NULL, 10); + + node->kind = NODE_KIND_NUMBER; + node->data = num; + } break; + case TOKEN_KIND_SYMBOL: { + Nob_String_View *dat = malloc(sizeof(Nob_String_View)); + *dat = tok.data; + + node->kind = NODE_KIND_SYMBOL; + node->data = dat; + } break; + default: { + UNREACHABLE(""); + } break; + } + return node; +} + +void node_print(Node node) { + switch (node.kind) { + case NODE_KIND_NUMBER: { + long long num = *((long long *)node.data); + printf("%lld", num); + } break; + case NODE_KIND_SYMBOL: { + Nob_String_View sv = *((Nob_String_View *)node.data); + printf(SV_Fmt, SV_Arg(sv)); + } break; + case NODE_KIND_LIST: { + Nodes nodes = *((Nodes *)node.data); + printf("("); + for (size_t i = 0; i < nodes.count; i++) { + Node node = nodes.items[i]; + node_print(node); + if (i != nodes.count - 1) { + printf(" "); + } + } + printf(")"); + } break; + case NODE_KIND_FUNCTION: { + LispFunction func = *((LispFunction *)node.data); + printf(SV_Fmt, SV_Arg(func.name)); + } break; + } +} + +Node *eval(Node *node) { + if (node == NULL) + return NULL; + + switch (node->kind) { + case NODE_KIND_NUMBER: + return node; + case NODE_KIND_SYMBOL: { + // trust me, this is safe :) + Nob_String_View sv = *((Nob_String_View *)node->data); + return lisp_function_lookup(sv); + } + case NODE_KIND_LIST: { + Nodes children = *((Nodes *)node->data); + if (children.count == 0) + return node; + + Node *head = &children.items[0]; + Node *function = eval(head); + + Nodes *args = malloc(sizeof(Nodes)); + for (size_t i = 1; i < children.count; i++) { + nob_da_append(args, *eval(&children.items[i])); + } + + if (function->kind == NODE_KIND_FUNCTION) { + LispFunction func = *((LispFunction *)function->data); + return func.apply(args); + } + + printf("Not a function: '"); + node_print(*function); + printf("'\n"); + return NULL; + } + default: + UNREACHABLE(""); + } + return NULL; } int main(int argc, char **argv) { @@ -104,11 +312,21 @@ int main(int argc, char **argv) { Tokens tokens = {0}; tokenize(view, &tokens); +#if 0 for (size_t i = 0; i < tokens.count; i++) { Token tok = tokens.items[i]; printf("Token(kind=%s, data=" SV_Fmt ")\n", token_kind_name(tok.kind), SV_Arg(tok.data)); } +#endif + + Node *result = parse(&tokens); + if (result == NULL) + return 1; + + result = eval(result); + node_print(*result); + printf("\n"); return 0; }