summary refs log tree commit diff stats
diff options
context:
space:
mode:
authorSudipto Mallick <smallick.dev+lith@gmail.com>2020-07-06 19:18:09 +0000
committerSudipto Mallick <smallick.dev+lith@gmail.com>2020-07-07 13:52:17 +0000
commit5a8c3e315ef9892dfb6e23ade18955b26d58643c (patch)
tree2047965ff7084ddb0e25df18abbe6bec0c3baf67
parentf843bdcb2e97a330cd72f91173895b0b37bb3dd8 (diff)
downloadlith-5a8c3e315ef9892dfb6e23ade18955b26d58643c.tar.gz
some more reformattings, library support master
-rw-r--r--lib.lith9
-rw-r--r--lith.c422
-rw-r--r--lith.h53
-rw-r--r--main.c25
4 files changed, 321 insertions, 188 deletions
diff --git a/lib.lith b/lib.lith
index 3a66062..4cd5591 100644
--- a/lib.lith
+++ b/lib.lith
@@ -15,6 +15,10 @@
     (eq? (typeof i) 'integer))
 (define (number? n)
     (eq? (typeof n) 'number))
+(define (boolean? n)
+    (eq? (typeof n) 'boolean))
+(define (string? s)
+    (eq? (typeof s) 'string))
 
 (define (foldl f init lst)
     (if (nil? lst)
@@ -38,8 +42,6 @@
             (car lst)
             (last (cdr lst)))))
 
-(define (begin . body) (last body))
-
 (define (reverse lst)
     (foldl (lambda (a x) (cons x a)) () lst))
 
@@ -126,6 +128,9 @@
 
 (define (mod a b) (:% a b))
 
+(define-macro (begin a . body)
+    `((lambda () ,a . ,body)))
+
 (define-macro (cond . body)
     (if (nil? body)
         (error "cond: no else clause")
diff --git a/lith.c b/lith.c
index 2e9b472..7eefa37 100644
--- a/lith.c
+++ b/lith.c
@@ -26,29 +26,29 @@ static char *lith__strndup(lith_st *L, char *str, size_t len)
     return newstr;
 }
 
-static void print_string(lith_string string)
+static void print_string(lith_string string, FILE *file)
 {
     size_t i;
     char *s;
     s = string.buf;
-    putchar('"');
+    fputc('"', file);
     for (i = 0; i < string.len; s++, i++) {
         if ((*s == '\\') || (*s == '"')) {
-            putchar('\\');
-            putchar(*s);
+            fputc('\\', file);
+            fputc(*s, file);
         } else if (*s == '\n') {
-            printf("\\n");
+            fprintf(file, "\\n");
         } else if (*s == '\t') {
-            printf("\\t");
+            fprintf(file, "\\t");
         } else if (*s == '\0') {
-            printf("\\0");
+            fprintf(file, "\\0");
         } else if ((*s < 32) || (*s > 126)) {
-            printf("\\x%02X", (unsigned char)(*s));
+            fprintf(file, "\\x%02X", (unsigned char)(*s));
         } else {
-            putchar(*s);
+            fputc(*s, file);
         }
     }
-    putchar('"');
+    fputc('"', file);
 }
 
 static char *skip(lith_st *L, char *input)
@@ -70,7 +70,9 @@ static char *skip(lith_st *L, char *input)
 
 static int ishexchar(int c)
 {
-    return (('0' <= c) && (c <= '9')) || (('a' <= c) && (c <= 'f')) || (('A' <= c) && (c <= 'F'));
+    return (('0' <= c) && (c <= '9'))
+        || (('a' <= c) && (c <= 'f'))
+        || (('A' <= c) && (c <= 'F'));
 }
 
 static void eat_string(lith_st *L, char *start, char **end)
@@ -79,21 +81,17 @@ static void eat_string(lith_st *L, char *start, char **end)
         if (**end == '\\') {
             ++*end;
             if (**end == 'x') {
-                if (!((ishexchar(*++*end) && ishexchar(*++*end)))) { /* May God and You forgive me */
-                    L->error = LITH_ERR_SYNTAX;
-                    L->error_state.success = 0;
-                    L->error_state.msg =
+                if (!((ishexchar(*++*end) && ishexchar(*++*end)))) {
+                    lith_simple_error(L, LITH_ERR_SYNTAX,
                         "Invalid character escape literal, "
-                        "expecting two hexadecimal characters";
+                        "expecting two hexadecimal characters");
                     return;
                 }
             }
         }
     }
     if (!**end) {
-        L->error = LITH_ERR_EOF;
-        L->error_state.success = 0;
-        L->error_state.msg = "while reading a string literal";
+        lith_simple_error(L, LITH_ERR_EOF, "while reading a string literal");
     } else {
         /* skip the string ending " character */
         ++*end;
@@ -104,12 +102,13 @@ static void lex(lith_st *L, char *input, char **start, char **end)
 {
     if (!(input = skip(L, input))) { *start = *end = NULL; return; }
     *start = input;
-    if (strchr("()'@`", *input)) {
+    if ((*input == '(') || (*input == ')') || (*input == '\'')
+    || (*input == '@') || (*input == '`')) {
         *end = input + 1;
     } else if (*input == ',') {
         *end = input + ((input[1] == '@') ? 2 : 1);
     } else if (*input == '"') {
-        /* skip the string starting " character */
+        /* +1 to skip the string starting " character */
         eat_string(L, *start + 1, end);
     } else {
         *end = *start + strcspn(*start, " \t\n;()");
@@ -152,19 +151,18 @@ static lith_value *read_atom(lith_st *L, char *start, char *end)
     lith_value *val;
     
     if (*start == '"') {
+        /* -1 to skip the string ending " character */
         string = read_string(L, start, end - 1, &length);
         if (LITH_IS_ERR(L)) return NULL; 
         val = lith_make_string(L, string, length);
         free(string);
         return val;
     }
-    if ((*start == '#') && ((end - start) == 2) && ((start[1] == 't') || (start[1] == 'f'))) {
+    if ((*start == '#') && ((end - start) == 2)
+    && ((start[1] == 't') || (start[1] == 'f'))) {
         return (start[1] == 'f') ? L->False : L->True;
     }
-    if ((*start == '+') || (*start == '-'))
-        sign = (*start == '-') ? -1 : 1;
-    else
-        sign = 1;
+    sign = (*start == '-') ? -1 : 1;
     integer = strtol(start, &next, 10);
     if (*next == '.') {
         number = strtod(next, &next);
@@ -176,7 +174,7 @@ static lith_value *read_atom(lith_st *L, char *start, char *end)
     } else {
         string = lith__strndup(L, start, end - start);
         if (!string) return NULL;
-        val = (!strcmp(string, "nil")) ? L->nil : lith_get_symbol(L, string);
+        val = lith_get_symbol(L, string);
         free(string);
         return val;
     }
@@ -196,8 +194,8 @@ static lith_value *read_list_expr(lith_st *L, char *start, char **end)
         if (*t == ')') return v;
         if (*t == '.' && (*end - t == 1)) {
             if (LITH_IS_NIL(p)) {
-                L->error = LITH_ERR_SYNTAX;
-                L->error_state.msg = "invalid improper list starting with '.'";
+                lith_simple_error(L, LITH_ERR_SYNTAX,
+                    "improper lists do not start with '.'");
                 lith_free_value(v);
                 return NULL;
             }
@@ -209,8 +207,8 @@ static lith_value *read_list_expr(lith_st *L, char *start, char **end)
             LITH_CDR(p) = r;
             lex(L, *end, &t, end);
             if (LITH_IS_ERR(L) || (*t != ')')) {
-                L->error = LITH_ERR_SYNTAX;
-                L->error_state.msg = "expecting ')' for the end of this improper list";
+                lith_simple_error(L, LITH_ERR_SYNTAX,
+                    "expecting ')' at the end of this improper list");
                 lith_free_value(v);
                 return NULL;
             }
@@ -240,8 +238,7 @@ static lith_value *read_expr(lith_st *L, char *start, char **end)
     if (*t == '(') {
         return read_list_expr(L, *end, end);
     } else if (*t == ')') {
-        L->error = LITH_ERR_SYNTAX;
-        L->error_state.msg = "unbalanced parenthesis, expected an expression";
+        lith_simple_error(L, LITH_ERR_SYNTAX, "unbalanced parenthesis, expected an expression");
         return NULL;
     } else if ((*t == '\'') || (*t == '@') || (*t == ',') || (*t == '`')) {
         s = ((*t == '\'')
@@ -283,16 +280,17 @@ static size_t list_length(lith_value *v)
     return len;
 }
 
-static size_t lamargs_length(lith_value *args, int *im)
+static size_t lamargs_length(lith_value *args, int *improper)
 {
     size_t i;
     for (i = 0; LITH_IS(args, LITH_TYPE_PAIR); args = LITH_CDR(args)) ++i;
-    *im = !LITH_IS_NIL(args);
+    *improper = !LITH_IS_NIL(args);
     return i;
 }
 
 /* builtin functions of lith */
 
+/* (car '(a . b)) -> a */
 static lith_value *builtin__car(lith_st *L, lith_value *args)
 {
     lith_value *list;
@@ -302,6 +300,7 @@ static lith_value *builtin__car(lith_st *L, lith_value *args)
     return LITH_CAR(list);
 }
 
+/* (cdr '(a . b)) -> b */
 static lith_value *builtin__cdr(lith_st *L, lith_value *args)
 {
     lith_value *pair;
@@ -311,6 +310,7 @@ static lith_value *builtin__cdr(lith_st *L, lith_value *args)
     return LITH_CDR(pair);
 }
 
+/* (cons a b) -> (a . b) */
 static lith_value *builtin__cons(lith_st *L, lith_value *args)
 {
     lith_value *head, *tail;
@@ -322,18 +322,18 @@ static lith_value *builtin__cons(lith_st *L, lith_value *args)
 
 static void lith__print(lith_value *v)
 {
-    char *s;
-    size_t i, len;
     if (LITH_IS(v, LITH_TYPE_STRING)) {
-        s = v->value.string.buf;
-        len = v->value.string.len;
-        for (i = 0; i < len; i++)
-            putchar(*s++);
+        fwrite(v->value.string.buf, 1, v->value.string.len, stdout);
     } else {
-        lith_print_value(v);
+        lith_print_value(v, stdout);
     }
 }
 
+/* (print ...) -> ()
+ * and prints the values
+ * separated by ' '
+ * and a newline ('\n')
+ */
 static lith_value *builtin__print(lith_st *L, lith_value *args)
 {
     lith_value *v;
@@ -365,9 +365,8 @@ static lith_value *builtin__print(lith_st *L, lith_value *args)
     n2_is_number = LITH_IS(arg2, LITH_TYPE_NUMBER); \
     n2_is_numeric = n2_is_integer || n2_is_number; \
     if (!n1_is_numeric || !n2_is_numeric) { \
-        L->error = LITH_ERR_TYPE; \
-        L->error_state.manual = 1; \
-        L->error_state.msg = "expected numeric types (integers or numbers) as argument"; \
+        lith_simple_error(L, LITH_ERR_TYPE, \
+            "expected numeric types (integers or numbers) as argument"); \
         return NULL; \
     }
 
@@ -385,6 +384,13 @@ static lith_value *builtin__print(lith_st *L, lith_value *args)
             : arg2->value.number)); \
     }
 
+/* op1 <- (:+), (:-), (:*)
+ * (op1 int int) -> int
+ * (op1 int num) -> num
+ * (op1 num int) -> num
+ * (op1 num num) -> num
+ */
+
 static lith_value *builtin__add(lith_st *L, lith_value *args)
 {
     COMMON1(":+")
@@ -405,12 +411,17 @@ static lith_value *builtin__multiply(lith_st *L, lith_value *args)
 
 #define COMMON3(op, q) \
     if (q && (arg2->value.integer == 0L)) { \
-        L->error = LITH_ERR_TYPE; \
-        L->error_state.manual = 1; \
-        L->error_state.msg = "cannot " op " by zero!!"; \
+        lith_simple_error(L, LITH_ERR_TYPE, "cannot " op " by zero!!"); \
         return NULL; \
     }
 
+/* type int_n0 = int \ {0} ; hah!
+ * (:/ int int_n0) -> int
+ * (:/ int num) -> num
+ * (:/ num int) -> num
+ * (:/ num num) -> num
+ */
+
 static lith_value *builtin__divide(lith_st *L, lith_value *args)
 {
     COMMON1(":/")
@@ -418,6 +429,7 @@ static lith_value *builtin__divide(lith_st *L, lith_value *args)
     COMMON2(/)
 }
 
+/* (:% int int) -> int */
 static lith_value *builtin__modulus(lith_st *L, lith_value *args)
 {
     lith_value *arg1, *arg2;
@@ -425,9 +437,7 @@ static lith_value *builtin__modulus(lith_st *L, lith_value *args)
     arg1 = LITH_CAR(args);
     arg2 = LITH_CAR(LITH_CDR(args));
     if (!LITH_IS(arg1, LITH_TYPE_INTEGER) || !LITH_IS(arg2, LITH_TYPE_INTEGER)) {
-        L->error = LITH_ERR_TYPE;
-        L->error_state.manual = 1;
-        L->error_state.msg = "can calculate modulus with integral only arguments";
+        lith_simple_error(L, LITH_ERR_TYPE, "can calculate modulus with integral arguments only");
         return NULL;
     }
     COMMON3("mod", 1)
@@ -449,19 +459,24 @@ static lith_value *builtin__modulus(lith_st *L, lith_value *args)
         ); \
     }
 
-static lith_value *builtin__less_than(lith_st *L, lith_value *args)
+/* type numeric = int U num ; huh!
+ * op2 <- (:<, :==, :>)
+ * (op2 numeric numeric) -> bool
+ */
+
+static lith_value *builtin__is_less_than(lith_st *L, lith_value *args)
 {
     COMMON1(":<")
     COMMON4(<)
 }
 
-static lith_value *builtin__equal(lith_st *L, lith_value *args)
+static lith_value *builtin__is_num_equal(lith_st *L, lith_value *args)
 {
     COMMON1(":==")
     COMMON4(==)
 }
 
-static lith_value *builtin__greater_than(lith_st *L, lith_value *args)
+static lith_value *builtin__is_greater_than(lith_st *L, lith_value *args)
 {
     COMMON1(":>")
     COMMON4(>)
@@ -472,7 +487,8 @@ static lith_value *builtin__greater_than(lith_st *L, lith_value *args)
 #undef COMMON2
 #undef COMMON1
 
-static lith_value *builtin__eq(lith_st *L, lith_value *args)
+/* (eq? a b) -> bool */
+static lith_value *builtin__is_eq(lith_st *L, lith_value *args)
 {
     int eq;
     lith_value *arg1, *arg2;
@@ -497,6 +513,7 @@ static lith_value *builtin__eq(lith_st *L, lith_value *args)
     return LITH_IN_BOOL(eq);
 }
 
+/* (typeof a) -> sym */
 static lith_value *builtin__typeof(lith_st *L, lith_value *args)
 {
     lith_value *val;
@@ -505,12 +522,14 @@ static lith_value *builtin__typeof(lith_st *L, lith_value *args)
     return lith_get_symbol(L, L->types[val->type]);
 }
 
-static lith_value *builtin__nil(lith_st *L, lith_value *args)
+/* (nil? a) -> bool */
+static lith_value *builtin__is_nil(lith_st *L, lith_value *args)
 {
     if (!lith_expect_nargs(L, "nil?", 1, args, 1)) return NULL;
     return LITH_IN_BOOL(LITH_IS_NIL(LITH_CAR(args)));
 }
 
+/* (apply (i... -> a) (i...)) -> a */
 static lith_value *builtin__apply(lith_st *L, lith_value *args)
 {
     lith_value *f, *aargs, *cargs;
@@ -522,6 +541,7 @@ static lith_value *builtin__apply(lith_st *L, lith_value *args)
     return lith_apply(L, f, cargs);
 }
 
+/* (error str) -> _|_ */
 static lith_value *builtin__error(lith_st *L, lith_value *args)
 {
     lith_value *arg;
@@ -533,6 +553,11 @@ static lith_value *builtin__error(lith_st *L, lith_value *args)
     return NULL;
 }
 
+/* (load str) -> ()
+ * the contents of the file given by
+ * the string containing the path of that file is executed
+ */
+
 static lith_value *builtin__load(lith_st *L, lith_value *args)
 {
     lith_value *filename;
@@ -540,13 +565,14 @@ static lith_value *builtin__load(lith_st *L, lith_value *args)
     filename = LITH_CAR(args);
     if (!lith_expect_type(L, "load", 1, LITH_TYPE_STRING, filename)) return NULL;
     lith_run_file(L, L->global, filename->value.string.buf);
-    /* Forgive me */
     if (LITH_IS_ERR(L))
         return NULL;
     else
         return L->nil;
 }
 
+/* some more utilities */
+
 char *slurp(lith_st *L, char *filename)
 {
     FILE *file;
@@ -555,8 +581,7 @@ char *slurp(lith_st *L, char *filename)
     
     file = fopen(filename, "r");
     if (!file) {
-        L->error = LITH_ERR_CUSTOM;
-        L->error_state.msg = "could not open the file to be read";
+        lith_simple_error(L, LITH_ERR_CUSTOM, "could not open the file to be read");
         return NULL;
     }
     
@@ -588,6 +613,28 @@ static void init_types(char **types)
     types[LITH_TYPE_MACRO] = "macro";
 }
 
+struct lith_lib_fn lith_builtins[] = {
+    {"car", builtin__car},
+    {"cdr", builtin__cdr},
+    {"cons", builtin__cons},
+    {"typeof", builtin__typeof},
+    {"print", builtin__print},
+    {":+", builtin__add},
+    {":-", builtin__subtract},
+    {":*", builtin__multiply},
+    {":/", builtin__divide},
+    {":%", builtin__modulus},
+    {":<", builtin__is_less_than},
+    {":==", builtin__is_num_equal},
+    {":>", builtin__is_greater_than},
+    {"eq?", builtin__is_eq},
+    {"nil?", builtin__is_nil},
+    {"apply", builtin__apply},
+    {"error", builtin__error},
+    {"load", builtin__load},
+    {NULL, NULL}
+};
+
 /* Public functions */
 
 void lith_init(lith_st *L)
@@ -609,7 +656,7 @@ void lith_init(lith_st *L)
     L->global = lith_new_env(L, L->global);
     L->filename = "<<unspecified>>";
     init_types(L->types);
-    lith_fill_env(L);
+    lith_fill_env(L, lith_builtins);
 }
 
 void lith_free(lith_st *L)
@@ -624,7 +671,7 @@ void lith_free(lith_st *L)
         p = LITH_CDR(p);
     }
     if (L->error_state.expr)
-        free(L->error_state.expr);
+        lith_free_value(L->error_state.expr);
     free(L->False);
     free(L->True);
     free(L->nil);
@@ -690,18 +737,25 @@ lith_value *lith_make_builtin(lith_st *L, lith_builtin_function function)
     return val;
 }
 
-lith_value *lith_make_closure(lith_st *L, lith_env *parent_env, lith_value *arg_names, lith_value *body)
+lith_value *lith_make_closure(lith_st *L, lith_env *parent_env,
+                              lith_value *name, lith_value *arg_names, lith_value *body)
 {
-    lith_value *val, *p;
+    lith_value *val;
+    lith_closure *f;
+    val = lith_new_value(L);
+    if (!val) return NULL;
+    f = emalloc(L, sizeof(*f));
+    if (!f) { free(val); return NULL; }
     arg_names = lith_copy_value(L, arg_names);
-    if (!arg_names) return NULL;
+    if (!arg_names) { free(val); free(f); return NULL; }
     body = lith_copy_value(L, body);
-    if (!body) { lith_free_value(arg_names); return NULL; }
-    p = LITH_CONS(L, arg_names, body);
-    if (!p) { lith_free_value(arg_names); lith_free_value(body); return NULL; }
-    val = LITH_CONS(L, parent_env, p);
-    if (!val) { lith_free_value(p); return NULL; }
+    if (!body) { free(val); free(f); lith_free_value(arg_names); return NULL; }
+    f->name = name;
+    f->parent = parent_env;
+    f->args = arg_names;
+    f->body = body;
     val->type = LITH_TYPE_CLOSURE;
+    val->value.closure = f;
     return val;
 }
 
@@ -736,10 +790,13 @@ void lith_free_value(lith_value *val)
         lith_free_value(LITH_CAR(val));
         lith_free_value(LITH_CDR(val));
     } else if (LITH_IS(val, LITH_TYPE_CLOSURE) || LITH_IS(val, LITH_TYPE_MACRO)) {
-        lith_free_value(LITH_CDR(val));
+        lith_free_value(val->value.closure->args);
+        lith_free_value(val->value.closure->body);
+        free(val->value.closure);
     } else if (LITH_IS(val, LITH_TYPE_STRING)) {
         free(val->value.string.buf);
-    } else if (LITH_IS_NIL(val) || LITH_IS(val, LITH_TYPE_BOOLEAN) || LITH_IS(val, LITH_TYPE_SYMBOL)) {
+    } else if (LITH_IS_NIL(val) || LITH_IS(val, LITH_TYPE_BOOLEAN)
+           ||  LITH_IS(val, LITH_TYPE_SYMBOL)) {
         return;
     }
     free(val);
@@ -762,44 +819,47 @@ lith_value *lith_get_symbol(lith_st *L, char *name)
     return sym;
 }
 
-void lith_print_value(lith_value *val)
+void lith_print_value(lith_value *val, FILE *file)
 {
     if (LITH_IS_NIL(val)) {
-        printf("()");
+        fprintf(file, "()");
     } else if (LITH_IS(val, LITH_TYPE_SYMBOL)) {
-        printf("%s", val->value.symbol);
+        fprintf(file, "%s", val->value.symbol);
     } else if (LITH_IS(val, LITH_TYPE_STRING)) {
-        print_string(val->value.string);
+        print_string(val->value.string, stdout);
     } else if (LITH_IS(val, LITH_TYPE_BOOLEAN)) {
-        printf("#%c", val->value.boolean ? 't' : 'f');
+        fprintf(file, "#%c", val->value.boolean ? 't' : 'f');
     } else if (LITH_IS(val, LITH_TYPE_INTEGER)) {
-        printf("%ld", val->value.integer);
+        fprintf(file, "%ld", val->value.integer);
     } else if (LITH_IS(val, LITH_TYPE_NUMBER)) {
-        printf("%.*f", LITH_NFP, val->value.number);
+        fprintf(file, "%.15g", val->value.number);
     } else if (LITH_IS(val, LITH_TYPE_BUILTIN)) {
-        printf("#builtin:<%p>", val->value.function);
-    } else if (LITH_IS(val, LITH_TYPE_CLOSURE)) {
-        printf("#lambda:<%p>", val);
-    } else if (LITH_IS(val, LITH_TYPE_MACRO)) {
-        printf("#macro:<%p>", val);
+        fprintf(file, "#<builtin at %p>", val->value.function);
+    } else if (LITH_IS(val, LITH_TYPE_CLOSURE) || LITH_IS(val, LITH_TYPE_MACRO)) {
+        fprintf(file, "#<%s", LITH_IS(val, LITH_TYPE_MACRO) ? "macro" : "lambda");
+        if (val->value.closure->name) {
+            fputc(' ', file);
+            lith_print_value(val->value.closure->name, file);
+        }
+        fprintf(file, " at %p>", val->value.closure);
     } else if (!LITH_IS(val, LITH_TYPE_PAIR)) {
-        printf("#<%p>", val);
+        fprintf(file, "#<unknown object at %p>", val);
     } else {
-        putchar('(');
-        lith_print_value(LITH_CAR(val));
+        fputc('(', file);
+        lith_print_value(LITH_CAR(val), file);
         val = LITH_CDR(val);
         while (!LITH_IS_NIL(val)) {
             if (LITH_IS(val, LITH_TYPE_PAIR)) {
-                putchar(' ');
-                lith_print_value(LITH_CAR(val));
+                fputc(' ', file);
+                lith_print_value(LITH_CAR(val), file);
                 val = LITH_CDR(val);
             } else {
-                printf(" . ");
-                lith_print_value(val);
+                fprintf(file, " . ");
+                lith_print_value(val, file);
                 break;
             }
         }
-        putchar(')');
+        fputc(')', file);
     }
 }
 
@@ -818,7 +878,8 @@ lith_value *lith_copy_value(lith_st *L, lith_value *val)
         return lith_make_builtin(L, val->value.function);
     case LITH_TYPE_MACRO:
     case LITH_TYPE_CLOSURE:
-        v = lith_make_closure(L, LITH_CAR(val), LITH_CAR(LITH_CDR(val)), LITH_CDR(LITH_CDR(val)));
+        v = lith_make_closure(L, val->value.closure->parent, val->value.closure->name,
+                val->value.closure->args, val->value.closure->body);
         if (LITH_IS(val, LITH_TYPE_MACRO))
             v->type = LITH_TYPE_MACRO;
         return v;
@@ -828,7 +889,8 @@ lith_value *lith_copy_value(lith_st *L, lith_value *val)
         pair = LITH_CONS(L, head, L->nil);
         if (!pair) { lith_free_value(head); return NULL; }
         val = LITH_CDR(val);
-        for (p = pair; LITH_IS(val, LITH_TYPE_PAIR); val = LITH_CDR(val), p = LITH_CDR(p)) {
+        for (p = pair; LITH_IS(val, LITH_TYPE_PAIR);
+            val = LITH_CDR(val), p = LITH_CDR(p)) {
             v = lith_copy_value(L, LITH_CAR(val));
             if (!v) { lith_free_value(pair); return NULL; }
             w = LITH_CONS(L, v, L->nil);
@@ -845,6 +907,16 @@ lith_value *lith_copy_value(lith_st *L, lith_value *val)
     }
 }
 
+void lith_simple_error(lith_st *L, enum lith_error errtype, char *msg)
+{
+    L->error = errtype;
+    L->error_state.msg = msg;
+    if (errtype == LITH_ERR_EOF)
+        L->error_state.success = 0;
+    else if (errtype == LITH_ERR_TYPE)
+        L->error_state.manual = 1;
+}
+
 void lith_print_error(lith_st *L, int full)
 {
     struct lith_error_state E = L->error_state;
@@ -862,11 +934,14 @@ void lith_print_error(lith_st *L, int full)
         fprintf(stderr, "syntax error: %s", E.msg);
         break;
     case LITH_ERR_NOMEM:
-        fprintf(stderr, "no memory");
+        fprintf(stderr, "out of memory");
         break;
     case LITH_ERR_UNBOUND:
         fprintf(stderr, "unbound symbol: '%s'", E.sym);
         break;
+    case LITH_ERR_REDEFINE:
+        fprintf(stderr, "trying to redefine already defined symbol: '%s'", E.sym);
+        break;
     case LITH_ERR_NARGS:
         fprintf(stderr, "wrong number of arguments: "
             "expected %s%zu argument(s) but given %zu argument(s)",
@@ -890,7 +965,7 @@ void lith_print_error(lith_st *L, int full)
         fprintf(stderr, " [in '%s']", E.name);
     if (E.expr) {
         fprintf(stderr, "\noccured in: ");
-        lith_print_value(E.expr);
+        lith_print_value(E.expr, stderr);
     }
     fputc('\n', stderr);
 }
@@ -930,6 +1005,27 @@ lith_value *lith_env_get(lith_st *L, lith_env *V, lith_value *name)
     return NULL;
 }
 
+void lith_env_set(lith_st *L, lith_env *V, lith_value *name, lith_value *value)
+{
+    lith_env *parent;
+    lith_value *kvs, *kv;
+    parent = V;
+    do {
+        kvs = LITH_CDR(parent);
+        parent = LITH_CAR(parent);
+        while (!LITH_IS_NIL(kvs)) {
+            kv = LITH_CAR(kvs);
+            if (LITH_CAR(kv) == name) {
+                LITH_CDR(kv) = value;
+                return;
+            }
+            kvs = LITH_CDR(kvs);
+        }
+    } while (!LITH_IS_NIL(parent));
+    L->error = LITH_ERR_UNBOUND;
+    L->error_state.sym = name->value.symbol;
+}
+
 void lith_env_put(lith_st *L, lith_env *V, lith_value *name, lith_value *value)
 {
     lith_value *kvs, *kv;
@@ -937,7 +1033,8 @@ void lith_env_put(lith_st *L, lith_env *V, lith_value *name, lith_value *value)
     while (!LITH_IS_NIL(kvs)) {
         kv = LITH_CAR(kvs);
         if (name == LITH_CAR(kv)) {
-            LITH_CDR(kv) = value;
+            L->error = LITH_ERR_REDEFINE;
+            L->error_state.sym = name->value.symbol;
             return;
         }
         kvs = LITH_CDR(kvs);
@@ -947,35 +1044,19 @@ void lith_env_put(lith_st *L, lith_env *V, lith_value *name, lith_value *value)
     LITH_CDR(V) = LITH_CONS(L, kv, LITH_CDR(V));
 }
 
-void lith_fill_env(lith_st *L)
+void lith_fill_env(lith_st *L, lith_lib lib)
 {
     lith_env *V;
+    struct lith_lib_fn *fns;
     V = L->global;
-    #define LITH_REGISTER_BUILTIN(s, fn) \
-        lith_env_put(L, V, lith_get_symbol(L, s), \
-            lith_make_builtin(L, builtin__ ## fn))
-    LITH_REGISTER_BUILTIN("print", print);
-    LITH_REGISTER_BUILTIN("car", car);
-    LITH_REGISTER_BUILTIN("cdr", cdr);
-    LITH_REGISTER_BUILTIN("cons", cons);
-    LITH_REGISTER_BUILTIN(":+", add);
-    LITH_REGISTER_BUILTIN(":-", subtract);
-    LITH_REGISTER_BUILTIN(":*", multiply);
-    LITH_REGISTER_BUILTIN(":/", divide);
-    LITH_REGISTER_BUILTIN(":%", modulus);
-    LITH_REGISTER_BUILTIN(":<", less_than);
-    LITH_REGISTER_BUILTIN(":==", equal);
-    LITH_REGISTER_BUILTIN(":>", greater_than);
-    LITH_REGISTER_BUILTIN("eq?", eq);
-    LITH_REGISTER_BUILTIN("typeof", typeof);
-    LITH_REGISTER_BUILTIN("nil?", nil);
-    LITH_REGISTER_BUILTIN("apply", apply);
-    LITH_REGISTER_BUILTIN("error", error);
-    LITH_REGISTER_BUILTIN("load", load);
-    #undef LITH_REGISTER_BUILTIN
-}
-
-int lith_expect_nargs(lith_st *L, char *name, size_t expect, lith_value *args, int exact)
+    for (fns = lib; fns->name; ++fns) {
+        lith_env_put(L, V, lith_get_symbol(L, fns->name),
+            lith_make_builtin(L, fns->fn));
+    }
+}
+
+int lith_expect_nargs(lith_st *L, char *name, size_t expect,
+                      lith_value *args, int exact)
 {
     size_t len;
     struct lith_error_state *E;
@@ -994,7 +1075,8 @@ int lith_expect_nargs(lith_st *L, char *name, size_t expect, lith_value *args, i
     }
 }
 
-int lith_expect_type(lith_st *L, char *name, size_t narg, lith_valtype type, lith_value *val)
+int lith_expect_type(lith_st *L, char *name, size_t narg,
+                     lith_valtype type, lith_value *val)
 {
     struct lith_error_state *E;
     E = &L->error_state;
@@ -1017,8 +1099,8 @@ lith_value *lith_eval_expr(lith_st *L, lith_env *V, lith_value *expr)
     } else if (!LITH_IS(expr, LITH_TYPE_PAIR)) {
         return lith_copy_value(L, expr);
     } else if (!is_proper_list(expr)) {
-        L->error = LITH_ERR_SYNTAX;
-        L->error_state.msg = "atom or proper list expected as expression";
+        lith_simple_error(L, LITH_ERR_SYNTAX, 
+            "atom or proper list expected as expression");
         return NULL;
     }
     f = LITH_CAR(expr);
@@ -1041,23 +1123,37 @@ lith_value *lith_eval_expr(lith_st *L, lith_env *V, lith_value *expr)
             p = LITH_CDR(rest);
             if (!LITH_IS(sym, LITH_TYPE_SYMBOL)) {
                 if (!LITH_IS(sym, LITH_TYPE_PAIR)) {
-                    L->error = LITH_ERR_TYPE;
+                    lith_simple_error(L, LITH_ERR_TYPE,
+                        "first argument must be a symbol or pair");
                     L->error_state.name = "define";
-                    L->error_state.manual = 1;
-                    L->error_state.msg = "first argument must be a symbol or pair";
                     return NULL;
                 }
                 args = LITH_CDR(sym);
                 sym = LITH_CAR(sym);
                 if (!lith_expect_type(L, "define", 1, LITH_TYPE_SYMBOL, sym)) return NULL;
-                val = lith_make_closure(L, V, args, p);
+                val = lith_make_closure(L, V, sym, args, p);
             } else {
                 if (!lith_expect_nargs(L, "define", 2, rest, 1)) return NULL;
                 val = lith_eval_expr(L, V, LITH_CAR(p));
+                if (LITH_IS_CALLABLE(val))
+                    val->value.closure->name = sym;
             }
             if (!val) return NULL;
             lith_env_put(L, V, sym, val);
             return L->nil;
+        } else if (LITH_SYM_EQ(f, "set!")) {
+            if (!lith_expect_nargs(L, "set!", 2, rest, 1))
+                return NULL;
+            sym = LITH_CAR(rest);
+            val = LITH_CAR(LITH_CDR(rest));
+            if (!lith_expect_type(L, "set!", 1, LITH_TYPE_SYMBOL, sym))
+                return NULL;
+            val = lith_eval_expr(L, V, val);
+            if (!val) return NULL;
+            lith_env_set(L, V, sym, val);
+            if (LITH_IS_CALLABLE(val))
+                val->value.closure->name = sym;
+            return L->nil;
         } else if (LITH_SYM_EQ(f, "define-macro")) {
             if (!lith_expect_nargs(L, "define-macro", 2, rest, 0))
                 return NULL;
@@ -1075,6 +1171,7 @@ lith_value *lith_eval_expr(lith_st *L, lith_env *V, lith_value *expr)
             val = lith_eval_expr(L, V, r);
             if (!val) return NULL;
             val->type = LITH_TYPE_MACRO;
+            val->value.closure->name = sym;
             lith_env_put(L, V, sym, val);
             return L->nil;
         } else if (LITH_SYM_EQ(f, "lambda")) {
@@ -1083,23 +1180,23 @@ lith_value *lith_eval_expr(lith_st *L, lith_env *V, lith_value *expr)
             args = LITH_CAR(rest);
             p = LITH_CDR(rest);
             if (!is_proper_list(p)) {
-                L->error = LITH_ERR_SYNTAX;
-                L->error_state.msg = "body of lambda expression must be proper list";
+                lith_simple_error(L, LITH_ERR_SYNTAX,
+                    "body of lambda expression must be proper list");
                 return NULL;
             }
             for (q = args; LITH_IS(q, LITH_TYPE_PAIR); q = LITH_CDR(q)) {
                 if (!LITH_IS(LITH_CAR(q), LITH_TYPE_SYMBOL)) {
-                    L->error = LITH_ERR_SYNTAX;
-                    L->error_state.msg = "arguments in lambda expression must be symbols";
+                    lith_simple_error(L, LITH_ERR_SYNTAX,
+                        "arguments in lambda expression must be symbols");
                     return NULL;
                 }
             }
             if (!LITH_IS_NIL(q) && !LITH_IS(q, LITH_TYPE_SYMBOL)) {
-                L->error = LITH_ERR_SYNTAX;
-                L->error_state.msg = "arguments in lambda expression must be symbols";
+                lith_simple_error(L, LITH_ERR_SYNTAX,
+                    "arguments in lambda expression must be symbols");
                 return NULL;
             }
-            return lith_make_closure(L, V, args, p);
+            return lith_make_closure(L, V, NULL, args, p);
         }
     }
     f = lith_eval_expr(L, V, f);
@@ -1131,40 +1228,33 @@ lith_value *lith_eval_expr(lith_st *L, lith_env *V, lith_value *expr)
 
 lith_value *lith_apply(lith_st *L, lith_value *f, lith_value *args)
 {
-    int imgr, imer;
-    size_t gnargs, enargs;
+    int is_improper_list;
+    size_t len;
     lith_env *env;
-    lith_value *argn, *body, *r;
+    lith_value *expected_args, *body, *r;
+    lith_closure *fn;
     if (LITH_IS(f, LITH_TYPE_BUILTIN)) {
         return (*f->value.function)(L, args);
     } else if (!LITH_IS(f, LITH_TYPE_CLOSURE) && !LITH_IS(f, LITH_TYPE_MACRO)) {
-        L->error = LITH_ERR_TYPE;
-        L->error_state.manual = 1;
-        L->error_state.msg = "can not call non-callable";
+        lith_simple_error(L, LITH_ERR_TYPE, "can not call non-callable");
         L->error_state.name = "{apply}";
         return NULL;
     }
-    env = lith_new_env(L, LITH_CAR(f));
-    argn = LITH_CDR(f);
-    body = LITH_CDR(argn);
-    argn = LITH_CAR(argn);
-    gnargs = lamargs_length(args, &imgr);
-    enargs = lamargs_length(argn, &imer);
-    if (imer ? (gnargs < enargs) : (gnargs != enargs)) {
-        L->error = LITH_ERR_NARGS;
-        L->error_state.name = "{lambda}";
-        L->error_state.nargs.expected = enargs;
-        L->error_state.nargs.got = gnargs;
-        L->error_state.nargs.exact = !imer;
-        return NULL;
-    }
-    while (LITH_IS(argn, LITH_TYPE_PAIR)) {
-        lith_env_put(L, env, LITH_CAR(argn), LITH_CAR(args));
-        argn = LITH_CDR(argn);
+    fn = f->value.closure;
+    env = lith_new_env(L, fn->parent);
+    body = fn->body;
+    expected_args = fn->args;
+    len = lamargs_length(expected_args, &is_improper_list);
+    if (!lith_expect_nargs(L,
+        fn->name ? fn->name->value.symbol : "{lambda}",
+        len, args, !is_improper_list)) return NULL;
+    while (LITH_IS(expected_args, LITH_TYPE_PAIR)) {
+        lith_env_put(L, env, LITH_CAR(expected_args), LITH_CAR(args));
+        expected_args = LITH_CDR(expected_args);
         args = LITH_CDR(args);
     }
-    if (!LITH_IS_NIL(argn))
-        lith_env_put(L, env, argn, args);
+    if (!LITH_IS_NIL(expected_args))
+        lith_env_put(L, env, expected_args, args);
     r = NULL;
     while (!LITH_IS_NIL(body)) {
         if (r) lith_free_value(r);
@@ -1184,11 +1274,11 @@ void lith_run_string(lith_st *L, lith_env *V, char *input)
     while (!LITH_IS_ERR(L)) {
         if ((expr = lith_read_expr(L, end, &end))) {
             printf(">> ");
-            lith_print_value(expr);
+            lith_print_value(expr, stdout);
             putchar('\n');
             if ((res = lith_eval_expr(L, V, expr))) {
                 printf("-> ");
-                lith_print_value(res);
+                lith_print_value(res, stdout);
                 lith_free_value(res);
                 putchar('\n');
             }
@@ -1231,9 +1321,9 @@ void lith_run_file(lith_st *L, lith_env *V, char *filename)
     
     lith_print_error(L, 1);
     if (expr) {
-        printf("error occurred when evaluating the expression:\n\t");
-        lith_print_value(expr);
-        putchar('\n');
+        fprintf(stderr, "error occurred when evaluating the expression:\n\t");
+        lith_print_value(expr, stderr);
+        fputc('\n', stderr);
         lith_free_value(expr);
     }
 }
diff --git a/lith.h b/lith.h
index 3d9d1fb..719374e 100644
--- a/lith.h
+++ b/lith.h
@@ -3,6 +3,7 @@
 #define lith_h
 
 #include <stddef.h>
+#include <stdio.h>
 
 #define LITH_VERSION_STRING "0.1.0-alpha"
 
@@ -10,7 +11,9 @@ typedef struct lith_value lith_value;
 typedef struct lith_value lith_env;
 typedef struct lith_state lith_st;
 typedef struct lith_string lith_string;
+typedef struct lith_closure lith_closure;
 typedef enum lith_value_type lith_valtype;
+typedef struct lith_lib_fn *lith_lib;
 
 enum lith_error {
     LITH_ERR_OK,
@@ -18,6 +21,7 @@ enum lith_error {
     LITH_ERR_SYNTAX,
     LITH_ERR_NOMEM,
     LITH_ERR_UNBOUND,
+    LITH_ERR_REDEFINE,
     LITH_ERR_NARGS,
     LITH_ERR_TYPE,
     LITH_ERR_CUSTOM
@@ -46,10 +50,20 @@ struct lith_value {
         int boolean;
         long integer;
         double number;
-        struct lith_string { size_t len; char *buf; } string;
+        struct lith_string {
+            size_t len;
+            char *buf;
+        } string;
         char *symbol;
-        struct { struct lith_value *car, *cdr; } pair;
+        struct {
+            struct lith_value *car, *cdr;
+        } pair;
         lith_builtin_function function;
+        struct lith_closure {
+            lith_value *name;
+            lith_env *parent;
+            lith_value *args, *body;
+        } *closure;
     } value;
 };
 
@@ -63,14 +77,23 @@ struct lith_value {
 #define LITH_CDR(p) ((p)->value.pair.cdr)
 #define LITH_CONS lith_make_pair
 
+#define LITH_IS_CALLABLE(F) \
+    (LITH_IS(F, LITH_TYPE_MACRO) || LITH_IS(F, LITH_TYPE_CLOSURE))
+
 struct lith_state {
     enum lith_error error;
     struct lith_error_state {
         int success, manual;
         char *msg, *sym, *name;
         lith_value *expr;
-        struct lith_error_state__argsize { size_t expected, got; int exact; } nargs;
-        struct lith_error_state__type { lith_valtype expected, got; size_t narg; } type;
+        struct lith_error_state__argsize {
+            size_t expected, got;
+            int exact;
+        } nargs;
+        struct lith_error_state__type {
+            lith_valtype expected, got;
+            size_t narg;
+        } type;
     } error_state;
     char *types[LITH_NTYPES];
     lith_value *nil;
@@ -80,24 +103,29 @@ struct lith_state {
     char *filename;
 };
 
+struct lith_lib_fn {
+    char *name;
+    lith_builtin_function fn;
+};
+
+extern struct lith_lib_fn lith_builtins[];
+
 #define LITH_IS_ERR(L) ((L)->error != LITH_ERR_OK)
 #define LITH_AT_END_NO_ERR(L) (((L)->error == LITH_ERR_EOF) && (L)->error_state.success)
 
 #define LITH_TO_BOOL(B) ((!LITH_IS_NIL(B)) && !(LITH_IS(B, LITH_TYPE_BOOLEAN) && !((B)->value.boolean)))
 #define LITH_IN_BOOL(B) ((B) ? L->True : L->False)
 
-/* When a number is printed, how many digits you want after the decimal point */
-#ifndef LITH_NFP
-#define LITH_NFP 8
-#endif
+/* Public functions: the API of this library */
 
 void lith_init(lith_st *);
 void lith_free(lith_st *);
 void lith_clear_error_state(lith_st *);
 void lith_print_error(lith_st *, int);
+void lith_simple_error(lith_st *, enum lith_error, char *);
 
 lith_value *lith_new_value(lith_st *);
-void lith_print_value(lith_value *);
+void lith_print_value(lith_value *, FILE *);
 void lith_free_value(lith_value *);
 lith_value *lith_copy_value(lith_st *, lith_value *);
 
@@ -106,7 +134,7 @@ lith_value *lith_make_number(lith_st *, double);
 lith_value *lith_make_symbol(lith_st *, char *);
 lith_value *lith_make_string(lith_st *, char *, size_t);
 lith_value *lith_make_builtin(lith_st *, lith_builtin_function);
-lith_value *lith_make_closure(lith_st *, lith_env *, lith_value *, lith_value *);
+lith_value *lith_make_closure(lith_st *, lith_env *, lith_value *, lith_value *, lith_value *);
 lith_value *lith_make_pair(lith_st *, lith_value *, lith_value *);
 
 lith_value *lith_get_symbol(lith_st *, char *);
@@ -121,9 +149,10 @@ lith_env *lith_new_env(lith_st *, lith_env *);
 void lith_free_env(lith_env *);
 
 lith_value *lith_env_get(lith_st *, lith_env *, lith_value *);
+void lith_env_set(lith_st *, lith_env *, lith_value *, lith_value *);
 void lith_env_put(lith_st *, lith_env *, lith_value *, lith_value *);
 
-void lith_fill_env(lith_st *);
+void lith_fill_env(lith_st *, lith_lib);
 
 int lith_expect_type(lith_st *, char *, size_t, lith_valtype, lith_value *);
 int lith_expect_nargs(lith_st *, char *, size_t, lith_value *, int);
@@ -131,4 +160,4 @@ int lith_expect_nargs(lith_st *, char *, size_t, lith_value *, int);
 void lith_run_string(lith_st *, lith_env *, char *);
 void lith_run_file(lith_st *, lith_env *, char *);
 
-#endif
+#endif /* lith_h */
diff --git a/main.c b/main.c
index 5b192e0..dd2e824 100644
--- a/main.c
+++ b/main.c
@@ -32,9 +32,24 @@ int main(int argc, char **argv)
     int ret;
     lith_st T, *L;
     lith_env *V, *W;
-    char **arg;
+    char **arg, *a;
+    
+    if (argc < 2) {
+        show_help(argv[0]);
+        return 8;
+    }
+    
+    a = argv[1];
+    if (a[0] == '-') {
+        if (!strcmp(a, "-v") || !strcmp(a, "--version")) {
+            show_version();
+            return 0;
+        } else if (!strcmp(a, "-h") || !strcmp(a, "--help")) {
+            show_help(argv[0]);
+            return 0;
+        }
+    }
     
-    if (argc < 2) return 8;
     ret = 0;
     L = &T;
     lith_init(L);
@@ -59,12 +74,6 @@ int main(int argc, char **argv)
             lith_free_env(V);
             if (LITH_IS_ERR(L)) ret |= 32;
             lith_clear_error_state(L);
-        } else if (!strcmp(*arg, "-h") || !strcmp(*arg, "--help")) {
-            show_help(argv[0]);
-            break;
-        } else if (!strcmp(*arg, "-v") || !strcmp(*arg, "--version")) {
-            show_version();
-            break;
         } else {
             fprintf(stderr, "lith: invalid option '%s': try '%s --help' for available options\n", *arg, argv[0]);
             break;