Files
lisp/lisp.c
T

275 lines
7.7 KiB
C

#include "lisp.h"
static Scope globalScope = {0};
static Node *scope_lookup(Scopes *scopes, Nob_String_View name) {
nob_da_foreach(Scope, scope, scopes) {
nob_da_foreach(ScopeObject, scopeObject, scope) {
if (nob_sv_eq(name, scopeObject->name)) {
return &scopeObject->node;
}
}
}
printf("Undefined symbol: '" SV_Fmt "'!\n", SV_Arg(name));
return NULL;
}
static void scope_add_function(Scope *scope, const char *name,
LispFunction function) {
Nob_String_View obj_name = nob_sv_from_cstr(name);
ScopeObject obj = (ScopeObject){
.name = obj_name,
.node = (Node){.kind = NODE_KIND_FUNCTION,
.as_function = {.name = obj_name, .apply = function}}};
nob_da_append(scope, obj);
}
static bool node_is_true(Node node) {
switch (node.kind) {
case NODE_KIND_NUMBER:
return node.as_number != 0;
case NODE_KIND_LIST:
return node.as_list->count > 0;
default:
return true;
}
}
void build_scopes(Scopes *scopes) {
// clang-format off
scope_add_function(&globalScope, "+", lisp_function_add);
scope_add_function(&globalScope, "-", lisp_function_sub);
scope_add_function(&globalScope, "/", lisp_function_div);
scope_add_function(&globalScope, "*", lisp_function_mul);
scope_add_function(&globalScope, "%", lisp_function_mod);
scope_add_function(&globalScope, "=", lisp_function_eq);
scope_add_function(&globalScope, "<", lisp_function_lt);
scope_add_function(&globalScope, ">", lisp_function_gt);
scope_add_function(&globalScope, "<=", lisp_function_lt_eq);
scope_add_function(&globalScope, ">=", lisp_function_gt_eq);
scope_add_function(&globalScope, "println", lisp_function_println);
scope_add_function(&globalScope, "list", lisp_function_list);
scope_add_function(&globalScope, "append", lisp_function_append);
scope_add_function(&globalScope, "length", lisp_function_length);
scope_add_function(&globalScope, "null?", lisp_function_isnull);
scope_add_function(&globalScope, "builtins", lisp_function_builtins);
// clang-format on
nob_da_append(scopes, globalScope);
}
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 node_print(Node node) {
switch (node.kind) {
case NODE_KIND_NUMBER: {
printf("%lld", node.as_number);
} break;
case NODE_KIND_SYMBOL: {
printf(SV_Fmt, SV_Arg(node.as_symbol));
} break;
case NODE_KIND_LIST: {
Nodes *children = node.as_list;
printf("(");
for (size_t i = 0; i < children->count; i++) {
Node node = children->items[i];
node_print(node);
if (i != children->count - 1) {
printf(" ");
}
}
printf(")");
} break;
case NODE_KIND_FUNCTION: {
Nob_String_View func_name = node.as_function.name;
printf(SV_Fmt, SV_Arg(func_name));
} break;
}
}
void tokenize(Nob_String_View content, Tokens *tokens) {
for (size_t i = 0; i < content.count; i++) {
char c = content.data[i];
int kind = -1;
switch (c) {
case '(': {
kind = TOKEN_KIND_PAR_LEFT;
} break;
case ')': {
kind = TOKEN_KIND_PAR_RIGHT;
} break;
}
if (kind != -1) {
Token tok = (Token){.kind = kind, .data = {0}};
nob_da_append(tokens, tok);
continue;
}
Nob_String_Builder sb = {0};
while (isdigit(c)) {
nob_sb_appendf(&sb, "%c", c);
c = content.data[++i];
}
if (sb.count > 0) {
i--;
Token tok =
(Token){.kind = TOKEN_KIND_NUMBER, .data = nob_sb_to_sv(sb)};
nob_da_append(tokens, tok);
continue;
}
// symbols shouldn't start with a digit
bool condition = isascii(c) && !isdigit(c) && !isblank(c) &&
!iscntrl(c) && c != '(' && c != ')';
while (condition) {
nob_sb_appendf(&sb, "%c", c);
c = content.data[++i];
condition = isascii(c) && !isblank(c) && !iscntrl(c) && c != '(' &&
c != ')';
}
if (sb.count > 0) {
i--;
Token tok =
(Token){.kind = TOKEN_KIND_SYMBOL, .data = nob_sb_to_sv(sb)};
nob_da_append(tokens, tok);
}
}
}
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->as_list = nodes;
return node;
}
if (tok.kind == TOKEN_KIND_PAR_RIGHT) {
printf("unexpected ')'");
return NULL;
}
switch (tok.kind) {
case TOKEN_KIND_NUMBER: {
long long num = strtoll(tok.data.data, NULL, 10);
// TODO:
// - error checking
// - double support
node->kind = NODE_KIND_NUMBER;
node->as_number = num;
} break;
case TOKEN_KIND_SYMBOL: {
node->kind = NODE_KIND_SYMBOL;
node->as_symbol = tok.data;
} break;
default: {
UNREACHABLE("");
} break;
}
return node;
}
Node *eval(Node *node, Scopes *scopes) {
if (node == NULL || scopes == NULL)
return NULL;
switch (node->kind) {
case NODE_KIND_NUMBER:
return node;
case NODE_KIND_SYMBOL: {
return scope_lookup(scopes, node->as_symbol);
}
case NODE_KIND_LIST: {
Nodes *children = node->as_list;
if (children->count == 0)
return node;
Node *head = &children->items[0];
if (head->kind != NODE_KIND_SYMBOL)
return node;
Nodes *args = malloc(sizeof(Nodes));
for (size_t i = 1; i < children->count; i++) {
nob_da_append(args, *eval(&children->items[i], scopes));
}
if (nob_sv_eq(head->as_symbol,
(Nob_String_View){.count = 2, .data = "if"})) {
if (args->count != 3) {
printf("'if' expects three arguments!\n");
return NULL;
}
Node *condition = eval(&args->items[0], scopes);
return node_is_true(*condition) ? eval(&args->items[1], scopes)
: eval(&args->items[2], scopes);
} else {
Node *func_node = eval(head, scopes);
if (func_node->kind == NODE_KIND_FUNCTION) {
return func_node->as_function.apply(args, scopes);
}
printf("Not a function: '");
node_print(*func_node);
printf("'\n");
return NULL;
}
}
default:
UNREACHABLE("");
}
return NULL;
}