diff options
Diffstat (limited to 'src/main.c')
-rw-r--r-- | src/main.c | 324 |
1 files changed, 266 insertions, 58 deletions
@@ -2,6 +2,7 @@ #include <stdlib.h> #include <string.h> #include <getopt.h> +#include <stdbool.h> #include "lexer.h" #include "value.h" @@ -62,11 +63,22 @@ static void print_token(struct token *token) static void print_value(value_t value) { char buf[256] = {0}; - value_string(value, LEN(buf), buf); + value_string(value, sizeof(buf), buf); info("%-12s %s", value ? value_type_string[value->type] : "VALUE", buf); } +static void print_toklist(struct toklist *toklist) +{ + info("TOKLIST_START"); + list_for_each(pos, &toklist->list) { + struct toklist *entry = list_entry(pos, struct toklist, list); + for(size_t i = 0; i < entry->tokens_len; i++) + print_token(&entry->tokens[i]); + } + info("TOKLIST_END"); +} + struct tctx { enum tctx_type { TCTX_LEXER, @@ -82,8 +94,7 @@ struct tctx { } lex_ctx; struct tok_ctx { - struct token *list; - size_t len; + struct toklist *head; size_t idx; } tok_ctx; } value; @@ -96,11 +107,10 @@ static void tctx_init_lexer(struct tctx *tctx, lexer_t lexer) tctx->token = &tctx->value.lex_ctx.token; } -static void tctx_init_toklist(struct tctx *tctx, struct token *toklist, size_t len) +static void tctx_init_toklist(struct tctx *tctx, struct toklist *list) { tctx->type = TCTX_TOKLIST; - tctx->value.tok_ctx.list = toklist; - tctx->value.tok_ctx.len = len; + tctx->value.tok_ctx.head = list; tctx->value.tok_ctx.idx = 0; tctx->token = NULL; } @@ -115,8 +125,12 @@ static struct token *next_token(struct tctx *tctx) break; case TCTX_TOKLIST: ; struct tok_ctx *t = &tctx->value.tok_ctx; - if(t->idx == t->len) goto fail; - tctx->token = &t->list[t->idx++]; + while(t->idx >= t->head->tokens_len) { + ERR_NZ(list_is_tail(&t->head->list), _r, goto fail); + t->head = list_next_entry(t->head, struct toklist, list); + t->idx = 0; + } + tctx->token = &t->head->tokens[t->idx++]; break; } @@ -126,8 +140,9 @@ fail: return NULL; } +static char *str_alloc_copy(char *src); -value_t apply(value_t proc, size_t argc, value_t *argv); +value_t apply(env_t env, value_t proc, size_t argc, value_t *argv); value_t evaluate_expr(env_t env, struct tctx *tctx); value_t evaluate_sexp(env_t env, struct tctx *tctx); @@ -136,11 +151,15 @@ value_t evaluate_id (env_t env, struct tctx *tctx); value_t evaluate_lambda(env_t env, struct tctx *tctx); value_t evaluate_define(env_t env, struct tctx *tctx); value_t evaluate_if (env_t env, struct tctx *tctx); +value_t evaluate_defmacro(env_t env, struct tctx *tctx); + +value_t quote_expr(env_t env, struct tctx *tctx, bool is_quasi); +value_t quote_sexp(env_t env, struct tctx *tctx, bool is_quasi); -value_t quote_expr(env_t env, struct tctx *tctx); -value_t quote_sexp(env_t env, struct tctx *tctx); +static int toklist_expr(struct tctx *tctx, struct toklist **toklist); -size_t toklist_expr(struct tctx *tctx, struct token **toklist); +static struct toklist *value_to_toklist(value_t value); +static struct toklist *cons_to_toklist(value_t value); static env_t global_env = ENV_EMPTY; static value_t global_nil = VALUE_EMPTY; @@ -249,6 +268,14 @@ int main(int argc, char **argv) return 0; } +static char *str_alloc_copy(char *src) +{ + if(!src) return src; + + size_t len = strlen(src) + 1; + return memcpy(malloc(len), src, len); +} + #define HAS_ENOUGH_ARGS(proc, type, argc, fail) \ if(argc != proc->value.type.argc) { \ err("Wrong number of arguemnts, expected %zu, but got %zu", \ @@ -263,7 +290,7 @@ static value_t apply_lambda(struct proc *proc, value_t *args) struct tctx tctx = {0}; ERR_Z(env = env_create(env_copy(proc->parent_env), destroy_env), goto exit); - tctx_init_toklist(&tctx, proc->body, proc->body_len); + tctx_init_toklist(&tctx, proc->body); for(size_t i = 0; i < proc->argc; i++) { ERR_NZ(hashtable_insert(env->table, @@ -279,7 +306,30 @@ exit: return ret; } -value_t apply(value_t proc, size_t argc, value_t *argv) +static value_t apply_macro(env_t env, struct proc *proc, value_t *args) +{ + value_t ret = VALUE_EMPTY; + value_t macro_ret = VALUE_EMPTY; + + struct toklist *toklist = NULL; + + ERR_Z(macro_ret = apply_lambda(proc, args), goto exit); + ERR_Z(toklist = value_to_toklist(macro_ret), goto exit); + + struct tctx tctx = {0}; + tctx_init_toklist(&tctx, toklist); + + TOKEN_NEXT(&tctx); + ret = evaluate_expr(env, &tctx); + +exit: + if(toklist) toklist_destroy(toklist); + value_destroy(macro_ret); + + return ret; +} + +value_t apply(env_t env, value_t proc, size_t argc, value_t *argv) { if(proc == VALUE_EMPTY) return value_copy(global_nil); @@ -287,11 +337,14 @@ value_t apply(value_t proc, size_t argc, value_t *argv) case VALUE_PROC: HAS_ENOUGH_ARGS(proc, proc, argc, return VALUE_EMPTY); return apply_lambda(&proc->value.proc, argv); + case VALUE_MACRO: + HAS_ENOUGH_ARGS(proc, proc, argc, return VALUE_EMPTY); + return apply_macro(env, &proc->value.proc, argv); case VALUE_PROC_BUILTIN: HAS_ENOUGH_ARGS(proc, proc_builtin, argc, return VALUE_EMPTY); return proc->value.proc_builtin.proc(argv); default: - err("Value is not a procedure"); + err("'%s' is not a procedure", value_type_string[proc->type]); return VALUE_EMPTY; } } @@ -313,7 +366,11 @@ value_t evaluate_expr(env_t env, struct tctx *tctx) break; case TOKEN_QUOTE: TOKEN_NEXT(tctx); - ERR_Z(ret = quote_expr(env, tctx), goto exit); + ERR_Z(ret = quote_expr(env, tctx, false), goto exit); + break; + case TOKEN_QUASI: + TOKEN_NEXT(tctx); + ERR_Z(ret = quote_expr(env, tctx, true), goto exit); break; default: err("Did not exptect token '%s'", token_type_string[TOKEN(tctx)->type]); @@ -349,6 +406,14 @@ value_t evaluate_sexp(env_t env, struct tctx *tctx) goto exit; } + if(body[0]) + if(body[0]->type == VALUE_MACRO) { + ERR_Z(body[argc] = quote_expr(env, tctx, false), goto exit); + + TOKEN_NEXT(tctx); + continue; + } + switch(TOKEN(tctx)->type) { case TOKEN_LAMBDA: SPECIAL_FORM(ret, argc, evaluate_lambda(env, tctx), goto exit); @@ -358,7 +423,7 @@ value_t evaluate_sexp(env_t env, struct tctx *tctx) goto exit; case TOKEN_QUOTE_FORM: TOKEN_NEXT(tctx); - SPECIAL_FORM(ret, argc, quote_expr(env, tctx), goto exit); + SPECIAL_FORM(ret, argc, quote_expr(env, tctx, false), goto exit); TOKEN_MATCH(tctx, TOKEN_RP, value_destroy(ret); ret = VALUE_EMPTY; @@ -367,6 +432,9 @@ value_t evaluate_sexp(env_t env, struct tctx *tctx) case TOKEN_IF: SPECIAL_FORM(ret, argc, evaluate_if(env, tctx), goto exit); goto exit; + case TOKEN_DEFMACRO: + SPECIAL_FORM(ret, argc, evaluate_defmacro(env, tctx), goto exit); + goto exit; default: ERR_Z(body[argc] = evaluate_expr(env, tctx), goto exit); break; @@ -375,7 +443,7 @@ value_t evaluate_sexp(env_t env, struct tctx *tctx) TOKEN_NEXT(tctx); } - ret = apply(body[0], argc-1, &body[1]); + ret = apply(env, body[0], argc-1, &body[1]); exit: for(size_t i = 0; i < argc; i++) @@ -410,8 +478,7 @@ value_t evaluate_lambda(env_t env, struct tctx *tctx) size_t argc = 0; value_t *arg_keys = NULL; - struct token *body = NULL; - size_t body_len = 0; + struct toklist *body = NULL; TOKEN_SKIP(tctx, TOKEN_LAMBDA, goto fail); TOKEN_SKIP(tctx, TOKEN_LP, goto fail); @@ -433,14 +500,14 @@ value_t evaluate_lambda(env_t env, struct tctx *tctx) TOKEN_NEXT(tctx); } - ERR_Z(body_len = toklist_expr(tctx, &body), goto fail); + ERR_NZ(toklist_expr(tctx, &body), _r, goto fail); TOKEN_MATCH(tctx, 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}; + struct proc proc = {env_copy(env), arg_keys, argc, body}; ERR_Z(ret = value_create(VALUE_PROC, &proc), env_destroy(env); // remove the copy goto fail); @@ -450,20 +517,11 @@ value_t evaluate_lambda(env_t env, struct tctx *tctx) 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(body) toklist_destroy(body); if(arg_keys) free(arg_keys); return VALUE_EMPTY; } -#define STR_ALLOC_COPY(dest, str) do { \ - size_t len = strlen(str) + 1; \ - dest = malloc(len); \ - memcpy((dest), (str), len); \ - } while(0) - value_t evaluate_define(env_t env, struct tctx *tctx) { // TODO: don't alloc when the key is the same @@ -480,7 +538,7 @@ value_t evaluate_define(env_t env, struct tctx *tctx) switch(TOKEN(tctx)->type) { case TOKEN_ID: - STR_ALLOC_COPY(key, TOKEN(tctx)->value.id); + key = str_alloc_copy(TOKEN(tctx)->value.id); break; default: err("Did not exptect token '%s'", token_type_string[TOKEN(tctx)->type]); @@ -556,7 +614,53 @@ exit: return ret; } -value_t quote_expr(env_t env, struct tctx *tctx) +value_t evaluate_defmacro(env_t env, struct tctx *tctx) +{ + char *key = NULL; + value_t lambda = VALUE_EMPTY; + + TOKEN_SKIP(tctx, TOKEN_DEFMACRO, goto fail); + + switch(TOKEN(tctx)->type) + { + case TOKEN_ID: + key = str_alloc_copy(TOKEN(tctx)->value.id); + break; + default: + err("Did not exptect token '%s'", token_type_string[TOKEN(tctx)->type]); + goto fail; + } + + // unsafe and bad + tctx->token->type = TOKEN_LAMBDA; + + ERR_Z(lambda = evaluate_lambda(env, tctx), goto fail); + lambda->type = VALUE_MACRO; + + // TOKEN_MATCH(tctx, TOKEN_RP, goto fail); + + value_t prevval = VALUE_EMPTY; + char *prevkey = NULL; + + ERR_NZ( + hashtable_insert(global_env->table, (void *)key, (void *)lambda, + (void**)&prevkey, (void **)&prevval), + r, { + err("Couldn't insert symbol into the hashtable due to %s", strerror(r)); + goto fail; + }); + + if(prevkey) free(prevkey); + value_destroy(prevval); + return value_copy(global_nil); + +fail: + value_destroy(lambda); + if(key)free(key); + return VALUE_EMPTY; +} + +value_t quote_expr(env_t env, struct tctx *tctx, bool is_quasi) { value_t ret = VALUE_EMPTY; @@ -566,23 +670,28 @@ value_t quote_expr(env_t env, struct tctx *tctx) case TOKEN_INT: ERR_Z(ret = value_from_token(TOKEN(tctx)), goto exit); break; + case TOKEN_UNQUOTE: + if(is_quasi) { + TOKEN_SKIP(tctx, TOKEN_UNQUOTE, goto exit); + ERR_Z(ret = evaluate_expr(env, tctx), goto exit); + break; + } case TOKEN_QUOTE: + case TOKEN_QUASI: case TOKEN_LAMBDA: case TOKEN_DEFINE: case TOKEN_QUOTE_FORM: - case TOKEN_IF: ; + case TOKEN_IF: + case TOKEN_DEFMACRO: ; char name[64] = {0}; ERR_Z(token_value_string(TOKEN(tctx), LEN(name), name), goto exit); struct token temp = {0}; temp.type = TOKEN_ID; temp.value.id = name; ERR_Z(ret = value_from_token(&temp), goto exit); break; case TOKEN_LP: - ERR_Z(ret = quote_sexp(env, tctx), goto exit); - break; - case TOKEN_UNQUOTE: - TOKEN_SKIP(tctx, TOKEN_UNQUOTE, goto exit); - ERR_Z(ret = evaluate_expr(env, tctx), goto exit); + ERR_Z(ret = quote_sexp(env, tctx, is_quasi), goto exit); break; + default: err("Did not exptect token '%s'", token_type_string[TOKEN(tctx)->type]); break; @@ -591,7 +700,7 @@ exit: return ret; } -value_t quote_sexp(env_t env, struct tctx *tctx) +value_t quote_sexp(env_t env, struct tctx *tctx, bool is_quasi) { value_t ret = VALUE_EMPTY; value_t left = VALUE_EMPTY; @@ -604,14 +713,14 @@ value_t quote_sexp(env_t env, struct tctx *tctx) goto exit; } - ERR_Z(left = quote_expr(env, tctx), goto exit); + ERR_Z(left = quote_expr(env, tctx, is_quasi), goto exit); TOKEN_NEXT(tctx); if(TOKEN(tctx)->type == TOKEN_DOT) { // Parse cons TOKEN_NEXT(tctx); - ERR_Z(right = quote_expr(env, tctx), return VALUE_EMPTY); + ERR_Z(right = quote_expr(env, tctx, is_quasi), return VALUE_EMPTY); TOKEN_MATCH(tctx, TOKEN_RP, return VALUE_EMPTY); struct cons cons = {value_copy(left), value_copy(right)}; @@ -629,7 +738,7 @@ value_t quote_sexp(env_t env, struct tctx *tctx) if(TOKEN(tctx)->type == TOKEN_DOT) { TOKEN_NEXT(tctx); - ERR_Z(new = quote_expr(env, tctx), goto exit); + ERR_Z(new = quote_expr(env, tctx, is_quasi), goto exit); TOKEN_MATCH(tctx, TOKEN_RP, value_destroy(new); @@ -640,7 +749,7 @@ value_t quote_sexp(env_t env, struct tctx *tctx) break; } - ERR_Z(new = quote_expr(env, tctx), + ERR_Z(new = quote_expr(env, tctx, is_quasi), goto exit); value_t new_cons = VALUE_EMPTY; @@ -663,37 +772,136 @@ exit: return ret; } -#define IS_OP(token) (token->type == TOKEN_QUOTE) -size_t toklist_expr(struct tctx *tctx, struct token **toklist) +static struct toklist *toklist_create(struct token *tokens, size_t tokens_len) +{ + struct toklist *toklist; + toklist = malloc(sizeof(*toklist)); + LIST_EMPTY(&toklist->list); + + toklist->tokens = calloc(tokens_len, sizeof(*tokens)); + memcpy(toklist->tokens, tokens, tokens_len * sizeof(*tokens)); + toklist->tokens_len = tokens_len; + + return toklist; +} + +#define IS_OP(token) (token->type == TOKEN_QUOTE || \ + token->type == TOKEN_QUASI || \ + token->type == TOKEN_UNQUOTE) + +static int toklist_expr(struct tctx *tctx, struct toklist **toklist) { - struct token tokens[256]; + struct token tokens[64] = {0}; size_t tokens_len = 0; + struct list_head *tail = LIST_END; + size_t depth = 0; + do { TOKEN_NEXT(tctx); if(tokens_len >= LEN(tokens)) { - err("Too many tokens in expr"); - goto fail; + if(toklist) { + tail = list_add(tail, &toklist_create(tokens, tokens_len)->list); + } + tokens_len = 0; } if(TOKEN(tctx)->type == TOKEN_LP) depth++; else if(TOKEN(tctx)->type == TOKEN_RP) depth--; + + if(toklist) + token_clone(&tokens[tokens_len++], TOKEN(tctx)); - token_clone(&tokens[tokens_len++], TOKEN(tctx)); + } while(depth != 0 || IS_OP(TOKEN(tctx))); - } while(depth > 0 || IS_OP(TOKEN(tctx))); + if(toklist) { + tail = list_add(tail, &toklist_create(tokens, tokens_len)->list); + *toklist = list_entry(list_get_head(tail), struct toklist, list); + } - if(!toklist) goto fail; + return 0; + +// fail: + // for(size_t i = 0; i < tokens_len; i++) token_dealloc(&tokens[i]); + // return 1; +} + +#define SET_TOKEN_TYPE(t, ttype) (t)->type = (ttype) +#define SET_TOKEN_VALUE(t, member, tvalue) (t)->value.member = (tvalue) + +static struct toklist *value_to_toklist(value_t value) +{ + struct token token = {0}; + + switch(value->type) { + case VALUE_ATOM: + SET_TOKEN_TYPE(&token, TOKEN_ID); + SET_TOKEN_VALUE(&token, id, + str_alloc_copy(value->value.atom)); + break; + case VALUE_STR: + SET_TOKEN_TYPE(&token, TOKEN_STR); + SET_TOKEN_VALUE(&token, str, + str_alloc_copy(value->value.str)); + break; + case VALUE_INT: + SET_TOKEN_TYPE(&token, TOKEN_INT); + SET_TOKEN_VALUE(&token, num, value->value.num); + break; + case VALUE_CONS: + return cons_to_toklist(value); + default: + err("Cant turn '%s' to a token", value_type_string[value->type]); + return NULL; + } + + return toklist_create(&token, 1); +} + +static struct list_head *add_token_toklist(enum token_type type, struct list_head *tail) +{ + struct token token = {0}; + token.type = type; + return list_add(tail, &(toklist_create(&token, 1)->list)); +} + +static struct toklist *cons_to_toklist(value_t value) +{ +#define ADD_TOKEN(tail, ttype) \ + tail = add_token_toklist(ttype, tail) +#define ADD_TOKLIST(tail, toklist) \ + tail = list_get_tail(list_append(tail, &toklist->list)) + + struct list_head *tail = LIST_END; + + ADD_TOKEN(tail, TOKEN_LP); + + while(1) { + value_t left = value->value.cons.left; + value = value->value.cons.right; - *toklist = calloc(tokens_len, sizeof(*tokens)); - memcpy(*toklist, tokens, tokens_len * sizeof(*tokens)); + struct toklist *new = NULL; + ERR_Z(new = value_to_toklist(left), goto fail); + ADD_TOKLIST(tail, new); - return tokens_len; + if(value->type == VALUE_NIL) break; + if(value->type != VALUE_CONS) { + ADD_TOKEN(tail, TOKEN_DOT); + ERR_Z(new = value_to_toklist(value), goto fail); + ADD_TOKLIST(tail, new); + break; + } + } + + ADD_TOKEN(tail, TOKEN_RP); + + return list_entry(list_get_head(tail), struct toklist, list); fail: - for(size_t i = 0; i < tokens_len; i++) token_dealloc(&tokens[i]); - return 0; + err("Failed to turn value to toklist"); + toklist_destroy(list_entry(list_get_head(tail), struct toklist, list)); + return NULL; } |