summary refs log tree commit diff stats
path: root/lith.c
diff options
context:
space:
mode:
Diffstat (limited to 'lith.c')
-rw-r--r--lith.c256
1 files changed, 128 insertions, 128 deletions
diff --git a/lith.c b/lith.c
index 670a05c..400149c 100644
--- a/lith.c
+++ b/lith.c
@@ -238,7 +238,8 @@ static lith_value *read_expr(lith_st *L, char *start, char **end)
     if (*t == '(') {
         return read_list_expr(L, *end, end);
     } else if (*t == ')') {
-        lith_simple_error(L, LITH_ERR_SYNTAX, "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 == '`')) {
         if (*t == '\'')
@@ -279,56 +280,46 @@ static size_t list_length(lith_value *v)
     return len;
 }
 
-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;
-    *improper = !LITH_IS_NIL(args);
-    return i;
-}
-
 /* builtin functions of lith */
 
-/* (car '(a . b)) -> a */
+/* car[1] :: (car '(a . b)) -> a */
 static lith_value *builtin__car(lith_st *L, lith_value *args)
 {
     lith_value *list;
-    if (!lith_expect_nargs(L, "car", 1, args, 1)) return NULL;
     list = LITH_CAR(args);
     if (!lith_expect_type(L, "car", 1, LITH_TYPE_PAIR, list)) return NULL;
     return LITH_CAR(list);
 }
 
-/* (cdr '(a . b)) -> b */
+/* cdr[1] :: (cdr '(a . b)) -> b */
 static lith_value *builtin__cdr(lith_st *L, lith_value *args)
 {
     lith_value *pair;
-    if (!lith_expect_nargs(L, "cdr", 1, args, 1)) return NULL;
     pair = LITH_CAR(args);
     if (!lith_expect_type(L, "cdr", 1, LITH_TYPE_PAIR, pair)) return NULL;
     return LITH_CDR(pair);
 }
 
-/* (cons a b) -> (a . b) */
+/* cons[2] :: (cons a b) -> (a . b) */
 static lith_value *builtin__cons(lith_st *L, lith_value *args)
 {
     lith_value *head, *tail;
-    if (!lith_expect_nargs(L, "cons", 2, args, 1)) return NULL;
     head = LITH_CAR(args);
     tail = LITH_CAR(LITH_CDR(args));
     return LITH_CONS(L, head, tail);
 }
 
-static void lith__print(lith_value *v)
+static void lith__print(lith_st *L, lith_value *v)
 {
     if (LITH_IS(v, LITH_TYPE_STRING)) {
         fwrite(v->value.string.buf, 1, v->value.string.len, stdout);
     } else {
-        lith_print_value(v, stdout);
+        lith_print_value(L, v, stdout);
     }
 }
 
-/* (print ...) -> ()
+/* print[1+] ::
+ * (print ...) -> ()
  * and prints the values
  * separated by ' '
  * and a newline ('\n')
@@ -336,13 +327,12 @@ static void lith__print(lith_value *v)
 static lith_value *builtin__print(lith_st *L, lith_value *args)
 {
     lith_value *v;
-    if (!lith_expect_nargs(L, "print", 1, args, 0)) return NULL;
     v = args;
-    lith__print(LITH_CAR(v));
+    lith__print(L, LITH_CAR(v));
     v = LITH_CDR(v);
     while (!LITH_IS_NIL(v)) {
         putchar(' ');
-        lith__print(LITH_CAR(v));
+        lith__print(L, LITH_CAR(v));
         v = LITH_CDR(v);
     }
     putchar('\n');
@@ -354,7 +344,6 @@ static lith_value *builtin__print(lith_st *L, lith_value *args)
         n2_is_integer, n2_is_number, \
         n1_is_numeric, n2_is_numeric; \
     lith_value *arg1, *arg2; \
-    if (!lith_expect_nargs(L, fname, 2, args, 1)) return NULL; \
     arg1 = LITH_CAR(args); \
     arg2 = LITH_CAR(LITH_CDR(args)); \
     n1_is_integer = LITH_IS(arg1, LITH_TYPE_INTEGER); \
@@ -383,7 +372,7 @@ static lith_value *builtin__print(lith_st *L, lith_value *args)
             : arg2->value.number)); \
     }
 
-/* op1 <- (:+), (:-), (:*)
+/* op1[2] ::: op1 <- (:+), (:-), (:*)
  * (op1 int int) -> int
  * (op1 int num) -> num
  * (op1 num int) -> num
@@ -415,6 +404,7 @@ static lith_value *builtin__multiply(lith_st *L, lith_value *args)
     }
 
 /* type int_n0 = int \ {0} ; hah!
+ * :/[2] ::
  * (:/ int int_n0) -> int
  * (:/ int num) -> num
  * (:/ num int) -> num
@@ -428,11 +418,10 @@ static lith_value *builtin__divide(lith_st *L, lith_value *args)
     COMMON2(/)
 }
 
-/* (:% int int) -> int */
+/* :%[2] (:% int int) -> int */
 static lith_value *builtin__modulus(lith_st *L, lith_value *args)
 {
     lith_value *arg1, *arg2;
-    if (!lith_expect_nargs(L, ":%", 2, args, 1)) return NULL;
     arg1 = LITH_CAR(args);
     arg2 = LITH_CAR(LITH_CDR(args));
     if (!LITH_IS(arg1, LITH_TYPE_INTEGER) || !LITH_IS(arg2, LITH_TYPE_INTEGER)) {
@@ -459,7 +448,7 @@ static lith_value *builtin__modulus(lith_st *L, lith_value *args)
     }
 
 /* type numeric = int U num ; huh!
- * op2 <- (:<, :==, :>)
+ * op2[2] :: op2 <- (:<, :==, :>)
  * (op2 numeric numeric) -> bool
  */
 
@@ -486,12 +475,11 @@ static lith_value *builtin__is_greater_than(lith_st *L, lith_value *args)
 #undef COMMON2
 #undef COMMON1
 
-/* (eq? a b) -> bool */
+/* eq?[2] :: (eq? a b) -> bool */
 static lith_value *builtin__is_eq(lith_st *L, lith_value *args)
 {
     int eq;
     lith_value *arg1, *arg2;
-    if (!lith_expect_nargs(L, "eq?", 2, args, 1)) return NULL;
     arg1 = LITH_CAR(args);
     arg2 = LITH_CAR(LITH_CDR(args));
     if (arg1->type != arg2->type) return L->False;
@@ -512,27 +500,33 @@ static lith_value *builtin__is_eq(lith_st *L, lith_value *args)
     return LITH_IN_BOOL(eq);
 }
 
-/* (typeof a) -> sym */
+/* typeof[1] :: (typeof a) -> sym */
 static lith_value *builtin__typeof(lith_st *L, lith_value *args)
 {
     lith_value *val;
-    if (!lith_expect_nargs(L, "typeof", 1, args, 1)) return NULL;
     val = LITH_CAR(args);
     return lith_get_symbol(L, L->types[val->type]);
 }
 
-/* (nil? a) -> bool */
+/* nil?[1] :: (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 */
+/* list?[1] :: (list? a) -> bool */
+static lith_value *builtin__is_list(lith_st *L, lith_value *args)
+{
+    lith_value *val;
+    val = LITH_CAR(args);
+    return LITH_IN_BOOL(LITH_IS(val, LITH_TYPE_PAIR)
+        && is_proper_list(val));
+}
+
+/* apply[2] :: (apply (i... -> a) (i...)) -> a */
 static lith_value *builtin__apply(lith_st *L, lith_value *args)
 {
     lith_value *f, *aargs, *cargs;
-    if (!lith_expect_nargs(L, "apply", 2, args, 1)) return NULL;
     f = LITH_CAR(args);
     aargs = LITH_CAR(LITH_CDR(args));
     cargs = lith_copy_value(L, aargs);
@@ -540,11 +534,10 @@ static lith_value *builtin__apply(lith_st *L, lith_value *args)
     return lith_apply(L, f, cargs);
 }
 
-/* (error str) -> _|_ */
+/* error[1] :: (error str) -> _|_ */
 static lith_value *builtin__error(lith_st *L, lith_value *args)
 {
     lith_value *arg;
-    if (!lith_expect_nargs(L, "error", 1, args, 1)) return NULL;
     arg = LITH_CAR(args);
     if (!lith_expect_type(L, "error", 1, LITH_TYPE_STRING, arg)) return NULL;
     L->error = LITH_ERR_CUSTOM;
@@ -552,7 +545,7 @@ static lith_value *builtin__error(lith_st *L, lith_value *args)
     return NULL;
 }
 
-/* (load str) -> ()
+/* load[1] :: (load str) -> ()
  * the contents of the file given by
  * the string containing the path of that file is executed
  */
@@ -560,7 +553,6 @@ static lith_value *builtin__error(lith_st *L, lith_value *args)
 static lith_value *builtin__load(lith_st *L, lith_value *args)
 {
     lith_value *filename;
-    if (!lith_expect_nargs(L, "load", 1, args, 1)) return NULL;
     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);
@@ -572,7 +564,7 @@ static lith_value *builtin__load(lith_st *L, lith_value *args)
 
 /* some more utilities */
 
-char *slurp(lith_st *L, char *filename)
+static char *slurp(lith_st *L, char *filename)
 {
     FILE *file;
     char *buffer;
@@ -613,25 +605,26 @@ static void init_types(char **types)
 }
 
 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}
+    {"car", 1, 1, builtin__car},
+    {"cdr", 1, 1, builtin__cdr},
+    {"cons", 2, 1, builtin__cons},
+    {"typeof", 1, 1, builtin__typeof},
+    {"print", 1, 0, builtin__print},
+    {":+", 2, 1, builtin__add},
+    {":-", 2, 1, builtin__subtract},
+    {":*", 2, 1, builtin__multiply},
+    {":/", 2, 1, builtin__divide},
+    {":%", 2, 1, builtin__modulus},
+    {":<", 2, 1, builtin__is_less_than},
+    {":==", 2, 1, builtin__is_num_equal},
+    {":>", 2, 1, builtin__is_greater_than},
+    {"eq?", 2, 1, builtin__is_eq},
+    {"nil?", 1, 1, builtin__is_nil},
+    {"list?", 1, 1, builtin__is_list},
+    {"apply", 2, 1, builtin__apply},
+    {"error", 1, 1, builtin__error},
+    {"load", 1, 1, builtin__load},
+    {NULL, 0, 0, NULL}
 };
 
 /* Public functions */
@@ -726,21 +719,31 @@ lith_value *lith_make_symbol(lith_st *L, char *symbol)
     return val;
 }
 
-lith_value *lith_make_builtin(lith_st *L, lith_builtin_function function)
+lith_value *lith_make_builtin(lith_st *L, lith_value *name, lith_builtin_function function,
+                              size_t expect, int exact)
 {
     lith_value *val;
+    lith_callable *f;
     val = lith_new_value(L);
     if (!val) return NULL;
+    f = emalloc(L, sizeof(*f));
+    if (!f) { free(val); return NULL; }
+    f->name = name;
+    f->function = function;
+    f->expect = expect;
+    f->exact = exact;
     val->type = LITH_TYPE_BUILTIN;
-    val->value.function = function;
+    val->value.callable = f;
     return val;
 }
 
 lith_value *lith_make_closure(lith_st *L, lith_env *parent_env,
-                              lith_value *name, lith_value *arg_names, lith_value *body)
+                              lith_value *name, lith_value *arg_names, lith_value *body,
+                              size_t expect, int exact
+)
 {
     lith_value *val;
-    lith_closure *f;
+    lith_callable *f;
     val = lith_new_value(L);
     if (!val) return NULL;
     f = emalloc(L, sizeof(*f));
@@ -753,8 +756,10 @@ lith_value *lith_make_closure(lith_st *L, lith_env *parent_env,
     f->parent = parent_env;
     f->args = arg_names;
     f->body = body;
+    f->expect = expect;
+    f->exact = exact;
     val->type = LITH_TYPE_CLOSURE;
-    val->value.closure = f;
+    val->value.callable = f;
     return val;
 }
 
@@ -788,10 +793,12 @@ void lith_free_value(lith_value *val)
     if (LITH_IS(val, LITH_TYPE_PAIR)) {
         lith_free_value(LITH_CAR(val));
         lith_free_value(LITH_CDR(val));
+    } else if (LITH_IS(val, LITH_TYPE_BUILTIN)) {
+        free(val->value.callable);
     } else if (LITH_IS(val, LITH_TYPE_CLOSURE) || LITH_IS(val, LITH_TYPE_MACRO)) {
-        lith_free_value(val->value.closure->args);
-        lith_free_value(val->value.closure->body);
-        free(val->value.closure);
+        lith_free_value(val->value.callable->args);
+        lith_free_value(val->value.callable->body);
+        free(val->value.callable);
     } 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)
@@ -818,43 +825,44 @@ lith_value *lith_get_symbol(lith_st *L, char *name)
     return sym;
 }
 
-void lith_print_value(lith_value *val, FILE *file)
+void lith_print_value(lith_st *L, lith_value *val, FILE *file)
 {
+    lith_callable *fn;
     if (LITH_IS_NIL(val)) {
         fprintf(file, "()");
     } else if (LITH_IS(val, LITH_TYPE_SYMBOL)) {
         fprintf(file, "%s", val->value.symbol);
     } else if (LITH_IS(val, LITH_TYPE_STRING)) {
-        print_string(val->value.string, stdout);
+        print_string(val->value.string, file);
     } else if (LITH_IS(val, LITH_TYPE_BOOLEAN)) {
         fprintf(file, "#%c", val->value.boolean ? 't' : 'f');
     } else if (LITH_IS(val, LITH_TYPE_INTEGER)) {
         fprintf(file, "%ld", val->value.integer);
     } else if (LITH_IS(val, LITH_TYPE_NUMBER)) {
         fprintf(file, "%.15g", val->value.number);
-    } else if (LITH_IS(val, LITH_TYPE_BUILTIN)) {
-        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_CALLABLE(val)) {
+        fn = val->value.callable;
+        fprintf(file, "#<%s ", L->types[val->type]);
+        if (fn->name)
+            lith_print_value(L, fn->name, file);
+        else
+            fprintf(file, "[anon]");
+        fprintf(file, "[%zu%s]", fn->expect, fn->exact ? "" : "+");
+        fprintf(file, " at %p>", (void *)fn);
     } else if (!LITH_IS(val, LITH_TYPE_PAIR)) {
-        fprintf(file, "#<unknown object at %p>", val);
+        fprintf(file, "#<unknown object at %p>", (void *)val);
     } else {
         fputc('(', file);
-        lith_print_value(LITH_CAR(val), file);
+        lith_print_value(L, LITH_CAR(val), file);
         val = LITH_CDR(val);
         while (!LITH_IS_NIL(val)) {
             if (LITH_IS(val, LITH_TYPE_PAIR)) {
                 fputc(' ', file);
-                lith_print_value(LITH_CAR(val), file);
+                lith_print_value(L, LITH_CAR(val), file);
                 val = LITH_CDR(val);
             } else {
                 fprintf(file, " . ");
-                lith_print_value(val, file);
+                lith_print_value(L, val, file);
                 break;
             }
         }
@@ -865,6 +873,7 @@ void lith_print_value(lith_value *val, FILE *file)
 lith_value *lith_copy_value(lith_st *L, lith_value *val)
 {
     lith_value *head, *pair, *p, *v, *w;
+    lith_callable *f;
     if (!val) return NULL;
     switch (val->type) {
     case LITH_TYPE_INTEGER:
@@ -874,11 +883,13 @@ lith_value *lith_copy_value(lith_st *L, lith_value *val)
     case LITH_TYPE_STRING:
         return lith_make_string(L, val->value.string.buf, val->value.string.len);
     case LITH_TYPE_BUILTIN:
-        return lith_make_builtin(L, val->value.function);
+        f = val->value.callable;
+        return lith_make_builtin(L, lith_copy_value(L, f->name), f->function, f->expect, f->exact);
     case LITH_TYPE_MACRO:
     case LITH_TYPE_CLOSURE:
-        v = lith_make_closure(L, val->value.closure->parent, val->value.closure->name,
-                val->value.closure->args, val->value.closure->body);
+        f = val->value.callable;
+        v = lith_make_closure(L, f->parent, lith_copy_value(L, f->name),
+                f->args, f->body, f->expect, f->exact);
         if (LITH_IS(val, LITH_TYPE_MACRO))
             v->type = LITH_TYPE_MACRO;
         return v;
@@ -964,7 +975,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, stderr);
+        lith_print_value(L, E.expr, stderr);
     }
     fputc('\n', stderr);
 }
@@ -1046,11 +1057,14 @@ void lith_env_put(lith_st *L, lith_env *V, lith_value *name, lith_value *value)
 void lith_fill_env(lith_st *L, lith_lib lib)
 {
     lith_env *V;
+    lith_value *name;
     struct lith_lib_fn *fns;
     V = L->global;
     for (fns = lib; fns->name; ++fns) {
-        lith_env_put(L, V, lith_get_symbol(L, fns->name),
-            lith_make_builtin(L, fns->fn));
+        name = lith_get_symbol(L, fns->name);
+        if (!name) return;
+        lith_env_put(L, V, name,
+            lith_make_builtin(L, name, fns->fn, fns->expect, fns->exact));
     }
 }
 
@@ -1092,6 +1106,7 @@ int lith_expect_type(lith_st *L, char *name, size_t narg,
 
 lith_value *lith_eval_expr(lith_st *L, lith_env *V, lith_value *expr)
 {
+    size_t i;
     lith_value *f, *rest, *sym, *val, *args, *p, *q, *r;
     if (LITH_IS(expr, LITH_TYPE_SYMBOL)) {
         return lith_copy_value(L, lith_env_get(L, V, expr));
@@ -1121,28 +1136,15 @@ lith_value *lith_eval_expr(lith_st *L, lith_env *V, lith_value *expr)
             if (LITH_IS_ERR(L)) return NULL;
             p = LITH_CDR(rest);
             return lith_eval_expr(L, V, LITH_CAR(LITH_TO_BOOL(val) ? p : LITH_CDR(p)));
-        } else if (LITH_SYM_EQ(f, "define")) {
-            if (!lith_expect_nargs(L, "define", 2, rest, 0))
+        } else if (LITH_SYM_EQ(f, "def")) {
+            if (!lith_expect_nargs(L, "def", 2, rest, 1))
                 return NULL;
             sym = LITH_CAR(rest);
             p = LITH_CDR(rest);
-            if (!LITH_IS(sym, LITH_TYPE_SYMBOL)) {
-                if (!LITH_IS(sym, LITH_TYPE_PAIR)) {
-                    lith_simple_error(L, LITH_ERR_TYPE,
-                        "first argument must be a symbol or pair");
-                    L->error_state.name = "define";
-                    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, 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 (!lith_expect_type(L, "def", 1, LITH_TYPE_SYMBOL, sym)) return NULL;
+            val = lith_eval_expr(L, V, LITH_CAR(p));
+            if (LITH_IS_CALLABLE(val))
+                val->value.callable->name = sym;
             if (!val) return NULL;
             lith_env_put(L, V, sym, val);
             return L->nil;
@@ -1157,17 +1159,17 @@ lith_value *lith_eval_expr(lith_st *L, lith_env *V, lith_value *expr)
             if (!val) return NULL;
             lith_env_set(L, V, sym, val);
             if (LITH_IS_CALLABLE(val))
-                val->value.closure->name = sym;
+                val->value.callable->name = sym;
             return L->nil;
-        } else if (LITH_SYM_EQ(f, "define-macro")) {
-            if (!lith_expect_nargs(L, "define-macro", 2, rest, 0))
+        } else if (LITH_SYM_EQ(f, "macro")) {
+            if (!lith_expect_nargs(L, "macro", 2, rest, 0))
                 return NULL;
             args = LITH_CAR(rest);
             p = LITH_CDR(rest);
-            if (!lith_expect_type(L, "define-macro", 1, LITH_TYPE_PAIR, args))
+            if (!lith_expect_type(L, "macro", 1, LITH_TYPE_PAIR, args))
                 return NULL;
             sym = LITH_CAR(args);
-            if (!lith_expect_type(L, "define-macro", 1, LITH_TYPE_SYMBOL, sym))
+            if (!lith_expect_type(L, "macro", 1, LITH_TYPE_SYMBOL, sym))
                 return NULL;
             q = LITH_CONS(L, LITH_CDR(args), p);
             if (!q) return NULL;
@@ -1176,7 +1178,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;
+            val->value.callable->name = sym;
             lith_env_put(L, V, sym, val);
             return L->nil;
         } else if (LITH_SYM_EQ(f, "lambda")) {
@@ -1189,7 +1191,7 @@ lith_value *lith_eval_expr(lith_st *L, lith_env *V, lith_value *expr)
                     "body of lambda expression must be proper list");
                 return NULL;
             }
-            for (q = args; LITH_IS(q, LITH_TYPE_PAIR); q = LITH_CDR(q)) {
+            for (i = 0, q = args; LITH_IS(q, LITH_TYPE_PAIR); q = LITH_CDR(q), i++) {
                 if (!LITH_IS(LITH_CAR(q), LITH_TYPE_SYMBOL)) {
                     lith_simple_error(L, LITH_ERR_SYNTAX,
                         "arguments in lambda expression must be symbols");
@@ -1201,7 +1203,7 @@ lith_value *lith_eval_expr(lith_st *L, lith_env *V, lith_value *expr)
                     "arguments in lambda expression must be symbols");
                 return NULL;
             }
-            return lith_make_closure(L, V, NULL, args, p);
+            return lith_make_closure(L, V, NULL, args, p, i, LITH_IS_NIL(q));
         }
     }
     f = lith_eval_expr(L, V, f);
@@ -1233,26 +1235,24 @@ 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 is_improper_list;
-    size_t len;
     lith_env *env;
     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)) {
+    lith_callable *fn;
+    
+    if (!LITH_IS_CALLABLE(f)) {
         lith_simple_error(L, LITH_ERR_TYPE, "can not call non-callable");
         L->error_state.name = "{apply}";
         return NULL;
     }
-    fn = f->value.closure;
+    fn = f->value.callable;
+    if (!lith_expect_nargs(L,
+        fn->name ? fn->name->value.symbol : "{lambda}",
+        fn->expect, args, fn->exact)) return NULL;
+    if (LITH_IS(f, LITH_TYPE_BUILTIN))
+        return (*fn->function)(L, args);
     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);
@@ -1280,12 +1280,12 @@ void lith_run_string(lith_st *L, lith_env *V, char *input, int repl)
         if ((expr = lith_read_expr(L, end, &end))) {
             if (!repl) {
                 printf(">> ");
-                lith_print_value(expr, stdout);
+                lith_print_value(L, expr, stdout);
                 putchar('\n');
             }
             if ((res = lith_eval_expr(L, V, expr))) {
                 printf("-> ");
-                lith_print_value(res, stdout);
+                lith_print_value(L, res, stdout);
                 lith_free_value(res);
                 putchar('\n');
             }
@@ -1329,7 +1329,7 @@ void lith_run_file(lith_st *L, lith_env *V, char *filename)
     lith_print_error(L, 1);
     if (expr) {
         fprintf(stderr, "error occurred when evaluating the expression:\n\t");
-        lith_print_value(expr, stderr);
+        lith_print_value(L, expr, stderr);
         fputc('\n', stderr);
         lith_free_value(expr);
     }