feat: evaluation

This commit is contained in:
2026-07-21 00:18:11 +02:00
parent e9404ea473
commit 5dab1701cd
+231 -13
View File
@@ -1,12 +1,15 @@
#include <ctype.h> #include <ctype.h>
#include <stdbool.h> #include <stdbool.h>
#include <stddef.h> #include <stddef.h>
#include <stdint.h>
#include <stdio.h> #include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define NOB_IMPLEMENTATION #define NOB_IMPLEMENTATION
#include "nob.h" #include "nob.h"
typedef enum e_Token_Kind { typedef enum e_TokenKind {
TOKEN_KIND_PAR_LEFT, TOKEN_KIND_PAR_LEFT,
TOKEN_KIND_PAR_RIGHT, TOKEN_KIND_PAR_RIGHT,
TOKEN_KIND_NUMBER, TOKEN_KIND_NUMBER,
@@ -24,6 +27,90 @@ typedef struct s_Tokens {
size_t capacity; size_t capacity;
} Tokens; } 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) { void tokenize(Nob_String_View content, Tokens *tokens) {
for (size_t i = 0; i < content.count; i++) { for (size_t i = 0; i < content.count; i++) {
char c = content.data[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) { Node *parse(Tokens *tokens) {
switch (kind) { if (tokens->count == 0) {
case TOKEN_KIND_PAR_LEFT: printf("unexpected end of input");
return "PAR_LEFT"; return NULL;
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");
} }
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) { int main(int argc, char **argv) {
@@ -104,11 +312,21 @@ int main(int argc, char **argv) {
Tokens tokens = {0}; Tokens tokens = {0};
tokenize(view, &tokens); tokenize(view, &tokens);
#if 0
for (size_t i = 0; i < tokens.count; i++) { for (size_t i = 0; i < tokens.count; i++) {
Token tok = tokens.items[i]; Token tok = tokens.items[i];
printf("Token(kind=%s, data=" SV_Fmt ")\n", token_kind_name(tok.kind), printf("Token(kind=%s, data=" SV_Fmt ")\n", token_kind_name(tok.kind),
SV_Arg(tok.data)); SV_Arg(tok.data));
} }
#endif
Node *result = parse(&tokens);
if (result == NULL)
return 1;
result = eval(result);
node_print(*result);
printf("\n");
return 0; return 0;
} }