From 54f071ac7d47ef515a3f6a4db9e83f2f9aca3c8c Mon Sep 17 00:00:00 2001 From: kartofen Date: Sun, 25 Aug 2024 12:30:48 +0300 Subject: lambda nearly done --- .gitignore | 2 +- Makefile | 4 +- README.md | 2 +- files/test.l | 2 + src/env.c | 16 ++--- src/env.h | 8 +-- src/lexer.c | 37 ++++++++++++ src/lexer.h | 3 + src/main.c | 195 ++++++++++++++++++++++++++++++++++++++++++++--------------- src/value.c | 32 ++++++---- src/value.h | 7 ++- 11 files changed, 224 insertions(+), 84 deletions(-) diff --git a/.gitignore b/.gitignore index a922064..a6be7c1 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,3 @@ bin/ obj/ -file/*.log \ No newline at end of file +**.log \ No newline at end of file diff --git a/Makefile b/Makefile index b474b62..240e4f8 100644 --- a/Makefile +++ b/Makefile @@ -4,7 +4,7 @@ ifdef PROD CFLAGS := -std=c99 -Wpedantic -O3 -s # production flags else CFLAGS := -std=c99 -Wall -Wextra -Wpedantic -Wshadow -Wpointer-arith \ - -Wstrict-prototypes -Wmissing-prototypes -Wcast-qual -g -DDEBUG + -Wstrict-prototypes -Wmissing-prototypes -Wcast-qual -g3 -DDEBUG ifdef MEMDEBUG CFLAGS += -DENABLE_MEMDEBUG @@ -27,7 +27,7 @@ OBJS = $(SRCS:$(SRC)/%.c=$(OBJ)/%-$(FLAGHASH).o) DEPS = $(OBJS:%.o=%.d) -include $(DEPS) -.PHONY: all clean $(NAME) analyze valgrind +.PHONY: all clean $(NAME) analyze valgrind cppcheck .DEFAULT_GOAL := all all: $(NAME) diff --git a/README.md b/README.md index fe3e984..20780b0 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ A simple lisp/scheme interpreter -### TODO +#### TODO * lambda * macros diff --git a/files/test.l b/files/test.l index 49b4cad..65707ed 100644 --- a/files/test.l +++ b/files/test.l @@ -1,3 +1,5 @@ +(lambda (a b) (blah kajflkj foo bar)) + (define a 69) '(sn ,(+ a 1)) (define a 70) diff --git a/src/env.c b/src/env.c index 1aa452b..fcf8b49 100644 --- a/src/env.c +++ b/src/env.c @@ -3,7 +3,7 @@ #include "common.h" #include "env.h" -#include "hashtable.h" +#include "value.h" #define ENV_TABLE_CAP (1 << 8) @@ -32,17 +32,13 @@ static bool equal(void *key1, void *key2) return false; } -static void env_add_ref(env_t env); - env_t env_create(env_t parent, env_destroy_func destroy_func) { env_t env = malloc(sizeof(*env)); env->destroy_func = destroy_func; env->parent = parent; - env->refs = 0; + env->refs = 1; - env_add_ref(env); - ERR_Z(env->table = hashtable_create(ENV_TABLE_CAP, hash, equal), env_destroy(env)); @@ -58,7 +54,6 @@ void env_destroy(env_t env) if(env->refs > 0) return; - hashtable_for_each_item(env->table, item, i) { env->destroy_func((char *)item->key, (value_t)item->data); } @@ -67,12 +62,13 @@ void env_destroy(env_t env) free(env); } - -static void env_add_ref(env_t env) +env_t env_copy(env_t env) { env->refs++; if(env->parent) { - env_add_ref(env->parent); + env_copy(env->parent); } + + return env; } diff --git a/src/env.h b/src/env.h index 577133a..d6e2ad3 100644 --- a/src/env.h +++ b/src/env.h @@ -2,14 +2,14 @@ #define ENV_H // #include "value.h" -typedef struct value * value_t; +typedef struct value * _value_t; #include "hashtable.h" typedef struct symbol_table *env_t; #define ENV_EMPTY NULL -typedef void (*env_destroy_func)(char *key, value_t value); +typedef void (*env_destroy_func)(char *key, _value_t value); struct symbol_table { hashtable_t table; @@ -21,10 +21,8 @@ struct symbol_table { }; env_t env_create(env_t parent, env_destroy_func destroy_func); +env_t env_copy(env_t env); void env_destroy(env_t env); -int env_insert(env_t env, value_t key, value_t data); -int env_query (env_t env, value_t key, value_t *data); -int env_delete(env_t env, value_t key); #endif diff --git a/src/lexer.c b/src/lexer.c index 407be25..b8897da 100644 --- a/src/lexer.c +++ b/src/lexer.c @@ -120,6 +120,43 @@ int token_value_string(struct token *token, size_t buf_sz, char *buf) return 0; } +#define STR_ALLOC_COPY(dest, str) do { \ + size_t len = strlen(str) + 1; \ + dest = malloc(len); \ + memcpy((dest), (str), len); \ + } while(0) + +void token_clone(struct token *dest, struct token *src) +{ + dest->type = src->type; + + switch(src->type) { + case TOKEN_ID: + STR_ALLOC_COPY(dest->value.id, src->value.id); + return; + case TOKEN_STR: + STR_ALLOC_COPY(dest->value.str, src->value.str); + return; + case TOKEN_INT: + dest->value.num = src->value.num; + return; + default: return; + } +} + +void token_dealloc(struct token *token) +{ + switch(token->type) { + case TOKEN_ID: + free(token->value.id); + return; + case TOKEN_STR: + free(token->value.str); + return; + default: return; + } +} + static int on_separator(lexer_t lexer, enum token_type type) { if(lexer->acc_idx > 0) return acc_empty(lexer); diff --git a/src/lexer.h b/src/lexer.h index 7c67028..c2e4637 100644 --- a/src/lexer.h +++ b/src/lexer.h @@ -55,4 +55,7 @@ int lexer_token_next(lexer_t lexer, struct token *token); int token_value_string(struct token *token, size_t buf_sz, char *buf); +void token_clone(struct token *dest, struct token *src); +void token_dealloc(struct token *token); + #endif diff --git a/src/main.c b/src/main.c index 195e213..8a07041 100644 --- a/src/main.c +++ b/src/main.c @@ -33,28 +33,27 @@ ERR_NZ(lexer_token_next(lexer, &token), r, \ die("Can't get next token")); \ print_token(&token); \ - } while(0); + } while(0) #endif -#define TOKEN_SKIP(ttype, fail) do { \ - if(token.type == ttype) { \ - TOKEN_NEXT(); \ - break; \ - } \ +#define TOKEN_ASSERT(ttype, fail) \ + if(token.type != ttype) { \ err("Expected token '%s', not '%s'", \ token_type_string[ttype], \ token_type_string[token.type]); \ fail; \ + } + +#define TOKEN_SKIP(ttype, fail) do { \ + TOKEN_ASSERT(ttype, fail) \ + else { \ + TOKEN_NEXT(); \ + } \ } while(0) -#define TOKEN_MATCH(ttype, fail) do { \ - TOKEN_NEXT(); \ - if(token.type == ttype) break; \ - \ - err("Expected token '%s', not '%s'", \ - token_type_string[ttype], \ - token_type_string[token.type]); \ - fail; \ +#define TOKEN_MATCH(ttype, fail) do { \ + TOKEN_NEXT(); \ + TOKEN_ASSERT(ttype, fail); \ } while(0) #define HAS_ENOUGH_ARGS(proc, type, argc, fail) \ @@ -88,14 +87,18 @@ env_t env = ENV_EMPTY; struct token token; value_t apply(value_t proc, size_t argc, value_t *argv); -value_t evaluate(void); + +value_t evaluate_expr(void); value_t evaluate_sexp(void); value_t evaluate_id(void); value_t evaluate_lambda(void); value_t evaluate_define(void); -value_t evaluate_quote(void); + +value_t quote_expr(void); value_t quote_sexp(void); +size_t toklist_expr(struct token **toklist); + static void destroy_env(char *key, value_t value) { (void)key; @@ -121,6 +124,7 @@ int main(void) (void *)builtin_proc_name_list[i], (void *)proc_value, NULL, NULL); } + char *filename = "files/test.l"; FILE *fp = fopen(filename, "r"); @@ -131,10 +135,10 @@ int main(void) lexer = lexer_create(fp); while(lexer_token_next(lexer, &token) == 0) { - value_t val = evaluate(); + value_t val = evaluate_expr(); - #ifdef FDEBUG - print_value(); + #ifdef DEBUG + print_value(val); #else char buf[256] = {0}; value_string(val, LEN(buf), buf); @@ -148,7 +152,7 @@ int main(void) fclose(fp); env_destroy(env); - env_destroy(user_env); + // env_destroy(user_env); return 0; } @@ -160,6 +164,11 @@ value_t apply(value_t proc, size_t argc, value_t *argv) switch(proc->type) { case VALUE_PROC: HAS_ENOUGH_ARGS(proc, proc, argc, return VALUE_EMPTY); + // create new env + // evaluate body + // destroy new env + // switch back to the previous env + NOT_IMPLEMENTED(); return VALUE_EMPTY; case VALUE_PROC_BUILTIN: @@ -179,12 +188,9 @@ value_t apply(value_t proc, size_t argc, value_t *argv) #define SPECIAL_FORM(ret, argc, fn, fail) do { \ IS_NOT_FIRST(argc, fail); \ ERR_Z(ret = fn, fail); \ - TOKEN_MATCH(TOKEN_RP, \ - ret = VALUE_EMPTY; \ - fail); \ } while(0) -value_t evaluate(void) +value_t evaluate_expr(void) { value_t ret = VALUE_EMPTY; @@ -201,7 +207,7 @@ value_t evaluate(void) break; case TOKEN_QUOTE: TOKEN_NEXT(); - ERR_Z(ret = evaluate_quote(), goto exit); + ERR_Z(ret = quote_expr(), goto exit); break; default: err("Did not exptect token '%s'", token_type_string[token.type]); @@ -236,10 +242,14 @@ value_t evaluate_sexp(void) goto exit; case TOKEN_QUOTE_FORM: TOKEN_NEXT(); - SPECIAL_FORM(ret, argc, evaluate_quote(), goto exit); + SPECIAL_FORM(ret, argc, quote_expr(), goto exit); + TOKEN_MATCH(TOKEN_RP, + value_destroy(ret); + ret = VALUE_EMPTY; + goto exit); goto exit; default: - ERR_Z(body[argc] = evaluate(), goto exit); + ERR_Z(body[argc] = evaluate_expr(), goto exit); break; } @@ -283,7 +293,56 @@ value_t evaluate_id(void) value_t evaluate_lambda(void) { - NOT_IMPLEMENTED(); + value_t ret = VALUE_EMPTY; + + value_t args[32] = {VALUE_EMPTY}; + size_t argc = 0; + value_t *arg_keys = NULL; + + struct token *body = NULL; + size_t body_len = 0; + + TOKEN_SKIP(TOKEN_LAMBDA, goto fail); + TOKEN_SKIP(TOKEN_LP, goto fail); + + while(token.type != TOKEN_RP) + { + if(argc >= LEN(args)) { + err("Too many arguments"); + goto fail; + } + + if(token.type != TOKEN_ID) { + err("Token '%s' not expected in lambda args", token_type_string[token.type]); + goto fail; + } + + ERR_Z(args[argc++] = value_from_token(&token), goto fail); + + TOKEN_NEXT(); + } + + ERR_Z(body_len = toklist_expr(&body), goto fail); + + TOKEN_MATCH(TOKEN_RP, goto fail); + + arg_keys = calloc(argc, sizeof(*arg_keys)); + memcpy(arg_keys, args, argc * sizeof(*arg_keys)); + + struct proc proc = {env_copy(env), arg_keys, argc, body, body_len}; + ERR_Z(ret = value_create(VALUE_PROC, &proc), goto fail); + + return ret; + +fail: + err("Procedure creation failed"); + for(size_t i = 0; i < argc; i++) value_destroy(args[i]); + if(body) { + for(size_t i = 0; i < body_len; i++) token_dealloc(&body[i]); + free(body); + } + if(arg_keys) free(arg_keys); + return VALUE_EMPTY; } #define STR_ALLOC_COPY(dest, str) do { \ @@ -295,14 +354,15 @@ value_t evaluate_lambda(void) value_t evaluate_define(void) { // TODO: don't alloc when the key is the same - - value_t ret = VALUE_EMPTY; - char *key = NULL; - - TOKEN_SKIP(TOKEN_DEFINE, goto exit); + char *key = NULL; // only in the outside environement - if(env->parent != user_env) goto exit; + if(env->parent != user_env) { + err("define can only be called in the outermost environement"); + goto fail; + } + + TOKEN_SKIP(TOKEN_DEFINE, goto fail); switch(token.type) { @@ -311,34 +371,40 @@ value_t evaluate_define(void) break; default: err("Did not exptect token '%s'", token_type_string[token.type]); - goto exit; + goto fail; } TOKEN_NEXT(); - ERR_Z(ret = evaluate(), goto exit); - TOKEN_MATCH(TOKEN_RP, goto exit); + value_t val = VALUE_EMPTY; + ERR_Z(val = evaluate_expr(), goto fail); + + TOKEN_MATCH(TOKEN_RP, + value_destroy(val); + goto fail); value_t prevval = VALUE_EMPTY; char *prevkey = NULL; - + ERR_NZ( - hashtable_insert(user_env->table, (void *)key, (void *)ret, + hashtable_insert(user_env->table, (void *)key, (void *)val, (void**)&prevkey, (void **)&prevval), r, { err("Couldn't insert symbol into the hashtable due to %s", strerror(r)); - value_destroy(ret); - free(key); - return VALUE_EMPTY; + value_destroy(val); // the copy + goto fail; }); if(prevkey) free(prevkey); value_destroy(prevval); -exit: + return VALUE_EMPTY; + +fail: + if(key) free(key); return VALUE_EMPTY; } -value_t evaluate_quote(void) +value_t quote_expr(void) { value_t ret = VALUE_EMPTY; @@ -362,7 +428,7 @@ value_t evaluate_quote(void) break; case TOKEN_UNQUOTE: TOKEN_SKIP(TOKEN_UNQUOTE, goto exit); - ERR_Z(ret = evaluate(), goto exit); + ERR_Z(ret = evaluate_expr(), goto exit); break; default: err("Did not exptect token '%s'", token_type_string[token.type]); @@ -377,7 +443,7 @@ value_t quote_sexp(void) value_t ret = VALUE_EMPTY; value_t left = VALUE_EMPTY; value_t right = VALUE_EMPTY; - value_t nil = VALUE_EMPTY; + value_t nil = VALUE_EMPTY; // TODO: make global nil and copy it ERR_Z(nil = value_create(VALUE_NIL, NULL), goto exit); @@ -390,14 +456,14 @@ value_t quote_sexp(void) goto exit; } - ERR_Z(left = evaluate_quote(), goto exit); + ERR_Z(left = quote_expr(), goto exit); TOKEN_NEXT(); if(token.type == TOKEN_DOT) { // Parse cons TOKEN_NEXT(); - ERR_Z(right = evaluate_quote(), return VALUE_EMPTY); + ERR_Z(right = quote_expr(), return VALUE_EMPTY); TOKEN_MATCH(TOKEN_RP, return VALUE_EMPTY); struct cons cons = {value_copy(left), value_copy(right)}; @@ -411,7 +477,7 @@ value_t quote_sexp(void) while(token.type != TOKEN_RP) { value_t new = VALUE_EMPTY; - ERR_Z(new = evaluate_quote(), + ERR_Z(new = quote_expr(), goto exit); value_t new_cons = VALUE_EMPTY; @@ -434,3 +500,34 @@ exit: value_destroy(nil); return ret; } + +size_t toklist_expr(struct token **toklist) +{ + struct token tokens[256]; + size_t tokens_len = 0; + + size_t depth = 0; + do { + TOKEN_NEXT(); + + if(tokens_len >= LEN(tokens)) { + err("Too many tokens in expr"); + goto fail; + } + + if(token.type == TOKEN_LP) depth++; + else if(token.type == TOKEN_RP) depth--; + + token_clone(&tokens[tokens_len++], &token); + + } while(depth > 0); + + *toklist = calloc(tokens_len, sizeof(*tokens)); + memcpy(*toklist, tokens, tokens_len * sizeof(*tokens)); + + return tokens_len; + +fail: + for(size_t i = 0; i < tokens_len; i++) token_dealloc(&tokens[i]); + return 0; +} diff --git a/src/value.c b/src/value.c index 233b83c..3c06fa3 100644 --- a/src/value.c +++ b/src/value.c @@ -32,17 +32,17 @@ const char * const value_type_string[] = { X(VALUE_PROC_BUILTIN, \ FN(snprintf, "%p", *(void **)&VALUE(v).proc_builtin.proc)) -#define DR(value) (--(value)->refs == 0) -#define VALUE_DESTROY_TABLE(X, v) \ - X(VALUE_NIL, (void)DR(v)) \ - X(VALUE_ATOM, if(DR(v)) free(VALUE(v).atom)) \ - X(VALUE_STR, if(DR(v)) free(VALUE(v).str)) \ - X(VALUE_INT, (void)DR(v)) \ - X(VALUE_CONS, (void)DR(v); \ - value_destroy(VALUE(v).cons.left); \ - value_destroy(VALUE(v).cons.right)) \ - X(VALUE_PROC, if(DR(v)) proc_destroy(&VALUE(v).proc)) \ - X(VALUE_PROC_BUILTIN, (void)DR(v)) +#define NOREFS(value) (--(value)->refs == 0) +#define VALUE_DESTROY_TABLE(X, v) \ + X(VALUE_NIL, (void)NOREFS(v)) \ + X(VALUE_ATOM, if(NOREFS(v)) free(VALUE(v).atom)) \ + X(VALUE_STR, if(NOREFS(v)) free(VALUE(v).str)) \ + X(VALUE_INT, (void)NOREFS(v)) \ + X(VALUE_CONS, (void)NOREFS(v); \ + value_destroy(VALUE(v).cons.left); \ + value_destroy(VALUE(v).cons.right)) \ + X(VALUE_PROC, if(NOREFS(v)) proc_destroy(&VALUE(v).proc)) \ + X(VALUE_PROC_BUILTIN, (void)NOREFS(v)) #define CASE_RETURN_APPLY(vtype, apply) \ case vtype: return apply; @@ -147,6 +147,7 @@ static int cons_print(char *buf, size_t buf_sz, struct cons *cons) static int proc_print(char *buf, size_t buf_sz, struct proc *proc) { + return 0; (void)buf; (void)buf_sz; (void)proc; NOT_IMPLEMENTED(); @@ -154,6 +155,11 @@ static int proc_print(char *buf, size_t buf_sz, struct proc *proc) static void proc_destroy(struct proc *proc) { - (void)proc; - NOT_IMPLEMENTED(); + for(size_t i = 0; i < proc->argc; i++) value_destroy(proc->arg_keys[i]); + free(proc->arg_keys); + + for(size_t i = 0; i < proc->body_len; i++) token_dealloc(&proc->body[i]); + free(proc->body); + + env_destroy(proc->parent_env); } diff --git a/src/value.h b/src/value.h index c2f43e3..ed30b99 100644 --- a/src/value.h +++ b/src/value.h @@ -39,12 +39,13 @@ struct value { } cons; struct proc { - size_t argc; - value_t *arg_keys; env_t parent_env; - size_t body_len; + value_t *arg_keys; + size_t argc; + struct token *body; + size_t body_len; } proc; struct proc_builtin { -- cgit v1.2.3