aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Makefile4
-rw-r--r--src/builtins.c27
-rw-r--r--src/builtins.h4
-rw-r--r--src/interp.c47
-rw-r--r--src/interp.h4
-rw-r--r--src/main.c24
-rw-r--r--src/parser.c43
-rw-r--r--src/prelude.c2
-rw-r--r--src/prelude.lisp10
-rw-r--r--src/primitives.c83
-rw-r--r--src/primitives.h7
-rw-r--r--tests/lisp/arithmetic.lisp11
-rw-r--r--tests/lisp/comment.lisp7
-rw-r--r--tests/lisp/control-flow.lisp33
-rw-r--r--tests/lisp/lambda.lisp12
-rw-r--r--tests/lisp/let-binding.lisp7
-rw-r--r--tests/lisp/macro.lisp10
-rw-r--r--tests/lisp/test.lisp8
18 files changed, 318 insertions, 25 deletions
diff --git a/Makefile b/Makefile
index 4072aa8..ec5b5da 100644
--- a/Makefile
+++ b/Makefile
@@ -33,6 +33,10 @@ test: $(tests_bin)
@echo
@echo "Run tests:"
@scripts/runall.sh $^
+ @echo "Run scripts:"
+ cd tests/lisp && \
+ ../../bamboo-lisp test.lisp
+
$(obj):%.o:%.c
$(cc) -c $(cflags) $< -MD -MF $@.d -o $@
diff --git a/src/builtins.c b/src/builtins.c
index f64083b..88bcc9b 100644
--- a/src/builtins.c
+++ b/src/builtins.c
@@ -2,6 +2,33 @@
#include "interp.h"
#include "sexp.h"
+SExpRef builtin_exit(Interp *interp, SExpRef args) {
+ if (LENGTH(args) == 0) {
+ Interp_free(interp);
+ exit(0);
+ }
+ if (LENGTH(args) == 1) {
+ SExpRef x = CAR(args);
+ if (VALTYPE(x) != kIntegerSExp) goto error;
+ int retcode = REF(x)->integer;
+ Interp_free(interp);
+ exit(retcode);
+ }
+error:
+ return new_error(interp, "exit: argument error.\n");
+}
+
+SExpRef builtin_error(Interp *interp, SExpRef args) {
+ if (LENGTH(args) != 1) return new_error(interp, "err.\n");
+ if (VALTYPE(CAR(args)) == kStringSExp || VALTYPE(CAR(args)) == kSymbolSExp) {
+ return new_error(interp, "%s\n", REF(CAR(args))->str);
+ }
+ const char *str = lisp_to_string(interp, CAR(args));
+ SExpRef ret = new_error(interp, "%s\n", REF(CAR(args))->str);
+ free((void*)str);
+ return ret;
+}
+
SExpRef builtin_list(Interp *interp, SExpRef args) {
return args;
}
diff --git a/src/builtins.h b/src/builtins.h
index 5ed9133..8f9c428 100644
--- a/src/builtins.h
+++ b/src/builtins.h
@@ -3,19 +3,19 @@
#include "interp.h"
+SExpRef builtin_exit(Interp *interp, SExpRef sexp);
+SExpRef builtin_error(Interp *interp, SExpRef sexp);
SExpRef builtin_list(Interp *interp, SExpRef sexp);
SExpRef builtin_car(Interp *interp, SExpRef sexp);
SExpRef builtin_cdr(Interp *interp, SExpRef sexp);
SExpRef builtin_cons(Interp *interp, SExpRef sexp);
SExpRef builtin_not(Interp *interp, SExpRef sexp);
-
SExpRef builtin_add(Interp *interp, SExpRef sexp);
SExpRef builtin_sub(Interp *interp, SExpRef sexp);
SExpRef builtin_mul(Interp *interp, SExpRef sexp);
SExpRef builtin_div(Interp *interp, SExpRef sexp);
SExpRef builtin_idiv(Interp *interp, SExpRef sexp);
SExpRef builtin_mod(Interp *interp, SExpRef sexp);
-
SExpRef builtin_num_equal(Interp *interp, SExpRef sexp);
SExpRef builtin_num_neq(Interp *interp, SExpRef sexp);
SExpRef builtin_gt(Interp *interp, SExpRef sexp);
diff --git a/src/interp.c b/src/interp.c
index 397e85c..5893ccc 100644
--- a/src/interp.c
+++ b/src/interp.c
@@ -60,6 +60,7 @@ void Interp_init(Interp *self) {
self->stack = lisp_cons(self, self->top_level, self->nil);
self->reg = self->nil;
+ Interp_add_primitive(self, "eval", primitive_eval);
Interp_add_primitive(self, "if", primitive_if);
Interp_add_primitive(self, "cond", primitive_cond);
Interp_add_primitive(self, "progn", primitive_progn);
@@ -78,7 +79,14 @@ void Interp_init(Interp *self) {
Interp_add_primitive(self, "macroexpand-1", primitive_macroexpand1);
Interp_add_primitive(self, "and", primitive_and);
Interp_add_primitive(self, "or", primitive_or);
-
+ Interp_add_primitive(self, "return", primitive_return);
+ Interp_add_primitive(self, "break", primitive_break);
+ Interp_add_primitive(self, "continue", primitive_continue);
+ Interp_add_primitive(self, "assert", primitive_assert);
+ Interp_add_primitive(self, "assert-error", primitive_assert_error);
+ Interp_add_primitive(self, "load", primitive_load);
+
+ Interp_add_userfunc(self, "error", builtin_error);
Interp_add_userfunc(self, "show", builtin_show);
Interp_add_userfunc(self, "car", builtin_car);
Interp_add_userfunc(self, "list", builtin_list);
@@ -97,7 +105,9 @@ void Interp_init(Interp *self) {
Interp_add_userfunc(self, ">=", builtin_ge);
Interp_add_userfunc(self, "<=", builtin_le);
Interp_add_userfunc(self, "not", builtin_not);
- Interp_add_userfunc(self, "gcstat", builtin_gcstat);
+ Interp_add_userfunc(self, "exit", builtin_exit);
+ // debug functions
+ Interp_add_userfunc(self, "_gcstat", builtin_gcstat);
SExpRef ret = Interp_eval_string(self, bamboo_lisp_prelude);
Interp *interp = self;
@@ -244,7 +254,7 @@ void Interp_gc(Interp *interp, SExpRef tmproot) {
} else if (obj->type == kMacroSExp) {
SExpRefVector_push_back(&gcstack, obj->macro.args);
SExpRefVector_push_back(&gcstack, obj->macro.body);
- } else if (obj->type == kReturnSignal || obj->type == kBreakSignal) {
+ } else if (obj->type == kReturnSignal) {
SExpRefVector_push_back(&gcstack, obj->ret);
} else if (obj->type == kTailcallSExp) {
SExpRefVector_push_back(&gcstack, obj->tailcall.args);
@@ -533,7 +543,9 @@ end:
int lisp_length(Interp *interp, SExpRef lst) {
int cnt = 0;
- if (VALTYPE(lst) == kPairSExp) {
+ if (VALTYPE(lst) == kNilSExp) {
+ return 0;
+ } else if (VALTYPE(lst) == kPairSExp) {
while (REF(lst)->type == kPairSExp) {
cnt++;
lst = CDR(lst);
@@ -541,8 +553,7 @@ int lisp_length(Interp *interp, SExpRef lst) {
return cnt;
} else if (VALTYPE(lst) == kStringSExp) {
return strlen(REF(lst)->str);
- }
- return 1;
+ } else return -1;
}
static SExpRef build_function_env(Interp *interp, SExpRef func, SExpRef args) {
@@ -701,7 +712,9 @@ end:
return ret;
tailcall:
while (1) {
+ PUSH_REG(CONS(fn, args));
ret = lisp_apply(interp, fn, args, false);
+ POP_REG();
if (VALTYPE(ret) != kTailcallSExp) break;
fn = REF(ret)->tailcall.fn;
args = REF(ret)->tailcall.args;
@@ -834,3 +847,25 @@ SExpRef new_symbol(Interp *interp, const char *val) {
}
}
+SExpRef new_return(Interp *interp, SExpRef obj) {
+ SExpRef ret = new_sexp(interp);
+ SExp *psexp = Interp_ref(interp, ret);
+ psexp->type = kReturnSignal;
+ psexp->ret = obj;
+ return ret;
+}
+
+SExpRef new_break(Interp *interp) {
+ SExpRef ret = new_sexp(interp);
+ SExp *psexp = Interp_ref(interp, ret);
+ psexp->type = kBreakSignal;
+ return ret;
+}
+
+SExpRef new_continue(Interp *interp) {
+ SExpRef ret = new_sexp(interp);
+ SExp *psexp = Interp_ref(interp, ret);
+ psexp->type = kContinueSignal;
+ return ret;
+}
+
diff --git a/src/interp.h b/src/interp.h
index 4329442..76a3757 100644
--- a/src/interp.h
+++ b/src/interp.h
@@ -76,6 +76,7 @@ SExpRef Interp_load_file(Interp *interp, const char *filename);
#define PUSH_REG(_x) { interp->reg = CONS((_x), interp->reg); }
#define POP_REG() { interp->reg = CDR(interp->reg); }
+const char* lisp_to_string(Interp *interp, SExpRef val);
SExpRef lisp_macroexpand1(Interp *interp, SExpRef macro, SExpRef args);
SExpRef lisp_reverse(Interp *interp, SExpRef lst);
void lisp_defun(Interp *interp, const char *name, SExpRef val);
@@ -102,6 +103,9 @@ SExpRef lisp_div(Interp *interp, SExpRef args);
SExpRef new_error(Interp *interp, const char *format, ...);
SExpRef new_sexp(Interp *ctx);
+SExpRef new_return(Interp *ctx, SExpRef ret);
+SExpRef new_break(Interp *ctx);
+SExpRef new_continue(Interp *ctx);
SExpRef new_boolean(Interp *ctx, bool val);
SExpRef new_char(Interp *ctx, char val);
SExpRef new_integer(Interp *ctx, int64_t val);
diff --git a/src/main.c b/src/main.c
index 4fddea9..86a5d36 100644
--- a/src/main.c
+++ b/src/main.c
@@ -2,10 +2,28 @@
#include "parser.h"
#include "sexp.h"
-int main() {
- int ret = -1;
+int main(int argc, char **argv) {
+ int mainret = 0;
Interp interp;
Interp_init(&interp);
+ if (argc > 2) {
+ fprintf(stderr, "Usage: bamboo-lisp [file.lisp]\n");
+ return -1;
+ }
+ if (argc == 2) {
+ const char *filename = argv[1];
+ SExpRef ret = Interp_load_file(&interp, filename);
+ if (Interp_ref(&interp, ret)->type == kErrSignal) {
+ fprintf(stderr, "Error: %s", Interp_ref(&interp, ret)->str);
+ mainret = -1; goto end;
+ }
+ if (Interp_ref(&interp, ret)->type == kBreakSignal
+ || Interp_ref(&interp, ret)->type == kContinueSignal
+ || Interp_ref(&interp, ret)->type == kReturnSignal) {
+ fprintf(stderr, "Error: unexpected control flow signal.\n");
+ mainret = -1; goto end;
+ }
+ }
Parser_set_readline(interp.parser);
SExpRef sexp, res;
ParseResult parse_result;
@@ -34,5 +52,5 @@ int main() {
}
end:
Interp_free(&interp);
- return 0;
+ return mainret;
}
diff --git a/src/parser.c b/src/parser.c
index 87690c8..6982198 100644
--- a/src/parser.c
+++ b/src/parser.c
@@ -11,6 +11,15 @@
#define BUFSIZE 1024
+static void skip_comment(Parser *parser) {
+ if (Parser_peek(parser) == ';') {
+ while (1) {
+ int peek = Parser_peek(parser);
+ if (peek == '\n' || peek == EOF) break;
+ Parser_getchar(parser);
+ }
+ }
+}
static void skip_spaces(Parser *parser) {
while (isspace(Parser_peek(parser))) {
@@ -18,8 +27,19 @@ static void skip_spaces(Parser *parser) {
}
}
+static void skip_blank(Parser *parser) {
+ while (1) {
+ int peek = Parser_peek(parser);
+ if (!isspace(peek) && peek != ';') {
+ break;
+ }
+ skip_comment(parser);
+ skip_spaces(parser);
+ }
+}
+
bool Parser_is_end(Parser *parser) {
- skip_spaces(parser);
+ skip_blank(parser);
if (Parser_peek(parser) == EOF) return true;
return false;
}
@@ -71,6 +91,7 @@ void Parser_set_readline(Parser *parser) {
parser->readline_eof = false;
}
+
int Parser_getchar(Parser *ctx) {
if (ctx->parse_type == kParseString) {
if (*ctx->str_cursor == '\0') return EOF;
@@ -142,7 +163,7 @@ int Parser_peek(Parser *ctx) {
}
ParseResult parse_sexp(Parser *parser) {
- skip_spaces(parser);
+ skip_blank(parser);
if (Parser_peek(parser) == EOF) {
return ParseErr(parser, "Unexpected EOF.\n");
}
@@ -185,7 +206,7 @@ static ParseResult expect_space(Parser *parser) {
if (Parser_peek(parser) == EOF) {
return ParseErr(parser, "Unexpected EOF.\n");
}
- if (isspace(Parser_peek(parser))) {
+ if (isspace(Parser_peek(parser)) || Parser_peek(parser) == ';') {
return ParseOk(parser->ctx->nil);
}
return ParseErr(parser, "Expect space.\n");
@@ -196,7 +217,8 @@ static ParseResult expect_space_or_end(Parser *parser) {
return ParseErr(parser, "Unexpected EOF.\n");
}
if (isspace(Parser_peek(parser))
- || Parser_peek(parser) == ')') {
+ || Parser_peek(parser) == ')'
+ || Parser_peek(parser) == ';') {
return ParseOk(parser->ctx->nil);
}
return ParseErr(parser, "Expect space.\n");
@@ -220,7 +242,7 @@ ParseResult parse_list(Parser *parser) {
ret = expect_char(parser, '(');
if (ParseResult_is_err(ret)) goto end;
- skip_spaces(parser);
+ skip_blank(parser);
while (1) {
if (Parser_peek(parser) == EOF) {
ret = ParseErr(parser, "Unexpected EOF.\n");
@@ -240,16 +262,16 @@ ParseResult parse_list(Parser *parser) {
SExpRefVector_push_back(&elems, ret.val);
// ret = expect_space_or_end(parser);
// if (ParseResult_is_err(ret)) goto end;
- skip_spaces(parser);
+ skip_blank(parser);
}
// dot
ret = expect_space(parser);
if (ParseResult_is_err(ret)) goto end;
- skip_spaces(parser);
+ skip_blank(parser);
ret = parse_sexp(parser);
if (ParseResult_is_err(ret)) goto end;
SExpRefVector_push_back(&elems, ret.val);
- skip_spaces(parser);
+ skip_blank(parser);
ret = expect_char(parser, ')');
if (ParseResult_is_err(ret)) goto end;
ret = ParseOk(build_list_from_vector(parser->ctx, elems));
@@ -265,6 +287,7 @@ static char *read_token(Parser *parser) {
&& Parser_peek(parser) != ')'
&& Parser_peek(parser) != '('
&& Parser_peek(parser) != '"'
+ && Parser_peek(parser) != ';'
&& (i == 0 || Parser_peek(parser) != '#')
&& i < BUFSIZE - 1) {
parser->token_buf[i] = Parser_getchar(parser);
@@ -307,7 +330,9 @@ static bool is_symbol_subsequent(char c) {
static ParseResult parse_token(Parser *parser, const char *token) {
int len = strlen(token);
- if (len == 0) return ParseErr(parser, "Empty token.\n");
+ if (len == 0) {
+ return ParseErr(parser, "Empty token.\n");
+ }
if (len == 1) {
if (token[0] == '-' || token[0] == '+') {
return ParseOk(new_symbol(parser->ctx, token));
diff --git a/src/prelude.c b/src/prelude.c
index f810262..1389f28 100644
--- a/src/prelude.c
+++ b/src/prelude.c
@@ -1,6 +1,6 @@
#include "prelude.h"
-const char *bamboo_lisp_prelude = "(defvar nil \'())\n\n(defvar pi 3.1415926)\n\n(defmacro incq (i)\n `(setq ,i (+ ,i 1)))\n\n(defmacro decq (i)\n `(setq ,i (- ,i 1)))\n\n(defun zerop (x) (= x 0))\n";
+const char *bamboo_lisp_prelude = "(defvar nil \'())\n\n(defvar pi 3.1415926)\n\n(defmacro incq (i)\n `(setq ,i (+ ,i 1)))\n\n(defmacro decq (i)\n `(setq ,i (- ,i 1)))\n\n(defun zerop (x) (= x 0))\n\n(defmacro when (pred . body)\n `(if ,pred\n (progn ,@body)\n nil))\n\n(defmacro unless (pred . body)\n `(if ,pred\n nil\n (progn ,@body)))\n";
diff --git a/src/prelude.lisp b/src/prelude.lisp
index 3e7ab81..7b49977 100644
--- a/src/prelude.lisp
+++ b/src/prelude.lisp
@@ -9,3 +9,13 @@
`(setq ,i (- ,i 1)))
(defun zerop (x) (= x 0))
+
+(defmacro when (pred . body)
+ `(if ,pred
+ (progn ,@body)
+ nil))
+
+(defmacro unless (pred . body)
+ `(if ,pred
+ nil
+ (progn ,@body)))
diff --git a/src/primitives.c b/src/primitives.c
index 435080c..87c266e 100644
--- a/src/primitives.c
+++ b/src/primitives.c
@@ -1,6 +1,76 @@
#include "primitives.h"
#include "interp.h"
#include "sexp.h"
+#include "parser.h"
+
+SExpRef primitive_assert_error(Interp *interp, SExpRef args, bool istail) {
+ SExpRef eargs = lisp_eval_args(interp, args);
+ if (VALTYPE(eargs) == kErrSignal) return interp->t;
+ return new_error(interp, "assert-error failed: no error.\n");
+}
+
+SExpRef primitive_load(Interp *interp, SExpRef args, bool istail) {
+ if (CAR(interp->stack).idx != interp->top_level.idx) {
+ return new_error(interp, "load: load can only be in top level.\n");
+ }
+ if (LENGTH(args) != 1) return new_error(interp, "load: syntax error.\n");
+ if (VALTYPE(CAR(args)) != kStringSExp) return new_error(interp, "load: syntax error.\n");
+ Parser *old_parser = interp->parser;
+ Parser *new_parser = malloc(sizeof(Parser));
+ Parser_init(new_parser);
+ new_parser->ctx = interp;
+ interp->parser = new_parser;
+ SExpRef ret = Interp_load_file(interp, REF(CAR(args))->str);
+ Parser_free(new_parser);
+ free(new_parser);
+ interp->parser = old_parser;
+ return ret;
+}
+
+SExpRef primitive_return(Interp *interp, SExpRef args, bool istail) {
+ if (LENGTH(args) > 1) {
+ return new_error(interp, "return: syntax error.\n");
+ }
+ SExpRef ret = NIL;
+ if (!NILP(args)) {
+ ret = lisp_eval(interp, CAR(args), true);
+ }
+ return new_return(interp, ret);
+}
+
+SExpRef primitive_break(Interp *interp, SExpRef args, bool istail) {
+ if (LENGTH(args) > 0) {
+ return new_error(interp, "break: syntax error.\n");
+ }
+ return new_break(interp);
+}
+
+SExpRef primitive_continue(Interp *interp, SExpRef args, bool istail) {
+ if (LENGTH(args) > 0) {
+ return new_error(interp, "continue: syntax error.\n");
+ }
+ return new_continue(interp);
+}
+
+SExpRef primitive_assert(Interp *interp, SExpRef args, bool istail) {
+ SExpRef eargs = lisp_eval_args(interp, args);
+ if (LENGTH(args) != 1) {
+ return new_error(interp, "assert: expect 1 arg.\n");
+ }
+ if (TRUEP(CAR(eargs))) {
+ return interp->t;
+ } else {
+ const char *expstr = lisp_to_string(interp, args);
+ SExpRef ret = new_error(interp, "Assertion failed: %s.\n", expstr);
+ free((void*)expstr);
+ return ret;
+ }
+}
+
+SExpRef primitive_eval(Interp *interp, SExpRef args, bool istail) {
+ args = lisp_eval_args(interp, args);
+ return lisp_eval(interp, args, istail);
+}
SExpRef primitive_if(Interp *interp, SExpRef args, bool istail) {
SExpRef cond, tb, fb;
@@ -111,7 +181,10 @@ SExpRef primitive_let(Interp *interp, SExpRef args, bool istail) {
while (!NILP(iter)) {
x = CAR(iter);
val = EVAL(CADR(x));
- if (CTL_FL(val)) goto end;
+ if (CTL_FL(val)) {
+ ret = val;
+ goto end;
+ }
ret = lisp_setq(interp, REF(CAR(x))->str, val);
if (CTL_FL(ret)) goto end;
iter = CDR(iter);
@@ -127,7 +200,7 @@ SExpRef primitive_let(Interp *interp, SExpRef args, bool istail) {
} else {
ret = EVAL(exp);
}
- if (CTL_FL(val)) goto end;
+ if (CTL_FL(ret)) goto end;
iter = CDR(iter);
}
end:
@@ -154,7 +227,7 @@ nextloop:
}
return cond;
}
- if (!TRUEP(cond)) return ret;
+ if (!TRUEP(cond)) return NIL;
iter = body;
while (!NILP(iter)) {
x = CAR(iter);
@@ -163,7 +236,7 @@ nextloop:
return ret;
}
if (VALTYPE(ret) == kBreakSignal) {
- return REF(ret)->ret;
+ return NIL;
}
if (VALTYPE(ret) == kContinueSignal) {
goto nextloop;
@@ -358,7 +431,9 @@ static SExpRef quasi_on_list(Interp *interp, SExpRef lst) {
iter = lst;
while (!NILP(iter)) {
x = CAR(iter);
+ PUSH_REG(newlst);
newx = quasi_impl(interp, x, &slicing);
+ POP_REG();
if (CTL_FL(newx)) return newx;
if (slicing) {
j = newx;
diff --git a/src/primitives.h b/src/primitives.h
index 5e22ba9..f5dd8e8 100644
--- a/src/primitives.h
+++ b/src/primitives.h
@@ -3,6 +3,13 @@
#include "interp.h"
+SExpRef primitive_assert_error(Interp *interp, SExpRef sexp, bool istail);
+SExpRef primitive_load(Interp *interp, SExpRef sexp, bool istail);
+SExpRef primitive_return(Interp *interp, SExpRef sexp, bool istail);
+SExpRef primitive_break(Interp *interp, SExpRef sexp, bool istail);
+SExpRef primitive_continue(Interp *interp, SExpRef sexp, bool istail);
+SExpRef primitive_assert(Interp *interp, SExpRef sexp, bool istail);
+SExpRef primitive_eval(Interp *interp, SExpRef sexp, bool istail);
SExpRef primitive_if(Interp *interp, SExpRef sexp, bool istail);
SExpRef primitive_cond(Interp *interp, SExpRef sexp, bool istail);
SExpRef primitive_progn(Interp *interp, SExpRef sexp, bool istail);
diff --git a/tests/lisp/arithmetic.lisp b/tests/lisp/arithmetic.lisp
new file mode 100644
index 0000000..658bcda
--- /dev/null
+++ b/tests/lisp/arithmetic.lisp
@@ -0,0 +1,11 @@
+(assert (= 1 (+ 1 0)))
+(assert (= -1 (- 0 1)))
+(assert (= -1 (- 1)))
+(assert (= 1.1 (+ 1 0.1)))
+(assert (= 2 (i/ 11 5)))
+(assert (= 1 (mod 11 5)))
+
+(assert-error (+ 1 "a"))
+(assert-error (- 1 "a"))
+(assert-error (* 1 "a"))
+(assert-error (/ 1 "a"))
diff --git a/tests/lisp/comment.lisp b/tests/lisp/comment.lisp
new file mode 100644
index 0000000..d84a2f7
--- /dev/null
+++ b/tests/lisp/comment.lisp
@@ -0,0 +1,7 @@
+(list 1 2 3) ;; comment
+(list 1;; comment
+ 2 3)
+(list 1 ;; comment
+;; comment
+ 2 3)
+;; comment
diff --git a/tests/lisp/control-flow.lisp b/tests/lisp/control-flow.lisp
new file mode 100644
index 0000000..5d7290d
--- /dev/null
+++ b/tests/lisp/control-flow.lisp
@@ -0,0 +1,33 @@
+(assert-error (if (error "") 1 2))
+
+(let ((i 0))
+ (while #t
+ (if (> i 4)
+ (break)
+ nil)
+ (incq i))
+ (assert (= i 5)))
+
+(let ((i 0))
+ (while #t
+ (if (> i 4)
+ (let () (break))
+ nil)
+ (incq i))
+ (assert (= i 5)))
+
+(let ((flag 0)
+ (i 0))
+ (while (< i 10)
+ (incq i)
+ (continue)
+ (setq flag 1))
+ (assert (= i 10))
+ (assert (= flag 0)))
+
+(assert-error (funcall (lambda () (break))))
+(assert-error (funcall (lambda () (continue))))
+(assert (= 1 (funcall (lambda () (return 1)))))
+(assert (= 1 (funcall (lambda () (while #t (return 1))))))
+(assert (= 1 (funcall (lambda () (let () (return 1))))))
+(assert (= 1 (funcall (lambda () (let ((x (return 1))) (return 2))))))
diff --git a/tests/lisp/lambda.lisp b/tests/lisp/lambda.lisp
new file mode 100644
index 0000000..5c93bdb
--- /dev/null
+++ b/tests/lisp/lambda.lisp
@@ -0,0 +1,12 @@
+(defun Y (f)
+ (funcall
+ (lambda (g) (funcall g g))
+ (lambda (h)
+ (funcall f (lambda args (apply (funcall h h) args))))))
+(defun fibo-impl (self)
+ (lambda (n)
+ (if (<= n 2)
+ 1
+ (+ (funcall self (- n 1)) (funcall self (- n 2))))))
+(defvar fibo (Y #'fibo-impl))
+(assert (= 55 (funcall fibo 10)))
diff --git a/tests/lisp/let-binding.lisp b/tests/lisp/let-binding.lisp
new file mode 100644
index 0000000..33d021e
--- /dev/null
+++ b/tests/lisp/let-binding.lisp
@@ -0,0 +1,7 @@
+(assert-error (let ((i 0)) (i > 4)))
+
+(assert (= 3
+(let ((a 1)
+ (b 2))
+ (+ a b))))
+
diff --git a/tests/lisp/macro.lisp b/tests/lisp/macro.lisp
new file mode 100644
index 0000000..4564cb9
--- /dev/null
+++ b/tests/lisp/macro.lisp
@@ -0,0 +1,10 @@
+(defmacro for (start pred inc . body)
+ `(let (,start)
+ (while ,pred
+ ,@body
+ ,inc)))
+(assert (= 10
+ (let ((sum 0))
+ (for (i 0) (< i 5) (incq i)
+ (setq sum (+ sum i)))
+ sum)))
diff --git a/tests/lisp/test.lisp b/tests/lisp/test.lisp
new file mode 100644
index 0000000..b42cc60
--- /dev/null
+++ b/tests/lisp/test.lisp
@@ -0,0 +1,8 @@
+(load "arithmetic.lisp")
+(load "control-flow.lisp")
+(load "lambda.lisp")
+(load "comment.lisp")
+(load "macro.lisp")
+(load "let-binding.lisp")
+
+(exit)