aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitignore2
-rw-r--r--Makefile4
-rw-r--r--README.md2
-rw-r--r--files/test.l2
-rw-r--r--src/env.c16
-rw-r--r--src/env.h8
-rw-r--r--src/lexer.c37
-rw-r--r--src/lexer.h3
-rw-r--r--src/main.c195
-rw-r--r--src/value.c32
-rw-r--r--src/value.h7
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 {