diff --git a/Makefile b/Makefile index f938054..45e9534 100644 --- a/Makefile +++ b/Makefile @@ -1,5 +1,5 @@ CC=gcc -CFLAGS=-std=gnu99 -g -Wall +CFLAGS=-std=gnu99 -O2 -Wall -Wshadow -Wextra -Wno-unused-parameter LDFLAGS= .PHONY: clean test diff --git a/README.md b/README.md index 5c8a721..6d5990d 100644 --- a/README.md +++ b/README.md @@ -85,19 +85,15 @@ CTRL-Z SUSPEND PROCESS The REPL also saves the history of commands in the file history.txt This file is loaded at startup, so one can recall previous commands. -Future improvements: -- floating point numbers -- data files -- system calls - Known bugs: -* the paste function does not work very well. +* Operators "and" and "or" do not work like their typical Lisp counterpart +because they evaluate all their operands at the same time instead of one +by one. You may use the versions in the library.lisp file to correct this behavior. * recall of multiline commands does not work as expected. -* this doesn't have tail call optimization, so expect crashes with sometimes surprisingly short lists. - +* this doesn't have tail call optimization, so expect crashes with sometimes with surprisingly short lists. Original README (completed) ---------------- +=============== One day I wanted to see what I can do with 1k lines of C and decided to write a Lisp interpreter. That turned to be a @@ -269,7 +265,8 @@ exhaustion error. ( progn (print "I own ") (defun add(x y)(+ x y)) - (println (add 3 7) " cents") ) ; -> prints "I own 10 cents" + (print (add 3 7) + (println " cents") ) ; -> prints "I own 10 cents" ### Equivalence test operators @@ -414,15 +411,3 @@ than itself. Useful for writing a macro that introduces new identifiers. As in the traditional Lisp syntax, `;` (semicolon) starts a single line comment. The comment continues to the end of line. - -No GC Branch ------------- - -There is a MiniLisp branch from which the code for garbage collection has been -stripped. The accepted language is the same, but the code is simpler than the -master branch's one. The reader might want to read the nogc branch first, then -proceed to the master branch, to understand the code step by step. - -The nogc branch is available at -[nogc](https://github.com/rui314/minilisp/tree/nogc). The original is available -at [master](https://github.com/rui314/minilisp). diff --git a/src/gc.c b/src/gc.c index bbc299c..3f4dab0 100644 --- a/src/gc.c +++ b/src/gc.c @@ -116,11 +116,6 @@ void *alloc_semispace() { return mmap(NULL, MEMORY_SIZE, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANON, -1, 0); } -void free_semispace(void *ptr){ - if (ptr) munmap(ptr, MEMORY_SIZE); - mem_nused = 0; -} - // Copies the root objects. static void forward_root_objects(void *root) { Symbols = forward(Symbols); @@ -141,7 +136,7 @@ static bool getEnvFlag(char *name) { // http://en.wikipedia.org/wiki/Cheney%27s_algorithm void gc(void *root) { assert(!gc_running); - gc_running = true; // 開始垃圾蒐集 + gc_running = true; // Debug flags debug_gc = getEnvFlag("MINILISP_DEBUG_GC"); diff --git a/src/minilisp.c b/src/minilisp.c index 40094a4..43de35a 100644 --- a/src/minilisp.c +++ b/src/minilisp.c @@ -28,10 +28,10 @@ void error(char *fmt, int line_num, ...) { } // Constants -static Obj *True = &(Obj){ TTRUE }; -static Obj *Nil = &(Obj){ TNIL }; -static Obj *Dot = &(Obj){ TDOT }; -static Obj *Cparen = &(Obj){ TCPAREN }; +static Obj *True = &(Obj){ .type = TTRUE, .size = sizeof(Obj) }; +static Obj *Nil = &(Obj){ .type = TNIL, .size = sizeof(Obj) }; +static Obj *Dot = &(Obj){ .type = TDOT, .size = sizeof(Obj) }; +static Obj *Cparen = &(Obj){ .type = TCPAREN, .size = sizeof(Obj) }; //====================================================================== // Constructors @@ -263,15 +263,8 @@ static Obj *read_string(void *root) { static Obj *read_expr(void *root) { for (;;) { char c = read_char(); - if (c == '\n') { - if (peek() == '\r'); + if (c == ' ' || c == '\n' || c == '\r' || c == '\t') continue; - } - - if (c == ' ' || c == '\r' || c == '\t') - continue; - if (c == EOF) - return NULL; if (c == ';') { skip_line(); continue; @@ -292,6 +285,9 @@ static Obj *read_expr(void *root) { return make_int(root, -read_number(0)); if (isalpha(c) || strchr(symbol_chars, c)) return read_symbol(root, c); + if (c == EOF) + return NULL; + error("Don't know how to handle %c", filepos.line_num, c); } } @@ -808,9 +804,9 @@ static Obj *prim_macroexpand(void *root, Obj **env, Obj **list) { // (print ...) static Obj *prim_print(void *root, Obj **env, Obj **list) { - for (Obj *args = *list; args != Nil; args = args->cdr) { - print(eval(root, env, &(args->car))); - } + DEFINE1(root, tmp); + *tmp = (*list)->car; + print(eval(root, env, tmp)); return Nil; } @@ -1023,6 +1019,8 @@ static void define_primitives(void *root, Obj **env) { add_primitive(root, env, "while", prim_while); add_primitive(root, env, "gensym", prim_gensym); add_primitive(root, env, "not", prim_not); + add_primitive(root, env, "and", prim_and); + add_primitive(root, env, "or", prim_or); add_primitive(root, env, "+", prim_plus); add_primitive(root, env, "-", prim_minus); add_primitive(root, env, "*", prim_mult); @@ -1060,20 +1058,16 @@ static void define_primitives(void *root, Obj **env) { extern void *memory; -void reset_minilisp(Obj **env) { +void init_minilisp(Obj **env) { // Memory allocation - extern void *memory; extern void *alloc_semispace(); - extern void free_semispace(void *); - gc_root = NULL; - free_semispace(memory); memory = alloc_semispace(); // Constants and primitives Symbols = Nil; - *env = make_env(gc_root, &Nil, &Nil); - define_constants(gc_root, env); - define_primitives(gc_root, env); + *env = make_env(NULL, &Nil, &Nil); + define_constants(NULL, env); + define_primitives(NULL, env); } int eval_input(void *root, Obj **env, Obj **expr) { diff --git a/src/repl.c b/src/repl.c index c7aae7c..785bd49 100644 --- a/src/repl.c +++ b/src/repl.c @@ -75,6 +75,7 @@ void minilisp(char *text, size_t length, bool with_repl, Obj **env, Obj **expr) if (with_repl) for(int promptnum = 1;; promptnum++) { char prompt[15] = ""; + filepos = (filepos_t){ .filename = "", .file_len = 0, .line_num = 1 }; sprintf(prompt, "%d:", promptnum); char *line = bestline(prompt); if(line == NULL) continue; @@ -96,14 +97,9 @@ void minilisp(char *text, size_t length, bool with_repl, Obj **env, Obj **expr) extern size_t mem_nused; printf("Memory used: %ld / Total: %d\n", mem_nused, MEMORY_SIZE); } - else if (!strncmp(line, "/reset", 6)){ - DEFINE1(gc_root, env); - reset_minilisp(env); - } else if (!strncmp(line, "/help", 5)){ puts("Type Ctrl-C to quit."); puts("/memory to display the amount of memory used."); - puts("/reset to flush the interpreter objects."); } else { printf("Unreconized command: %s", line); @@ -220,7 +216,7 @@ int main(int argc, char **argv) { parse_args(argc, argv); DEFINE2(gc_root, env, expr); - reset_minilisp(env); + init_minilisp(env); for (int i = 0; i < num_files; i++) { printf("Loading %s\n", filenames[i]); diff --git a/test.sh b/test.sh index 5e47517..bf902e5 100755 --- a/test.sh +++ b/test.sh @@ -1,4 +1,4 @@ -#!/bin/bash +#!/bin/bash -x function fail() { echo -n -e '\e[1;31m[ERROR]\e[0m ' @@ -7,13 +7,13 @@ function fail() { } function do_run() { - error=$(echo "$3" | ./minilisp 2>&1 > /dev/null) + error=$(echo "$3" | ./minilisp -r -x "${3}" 2>&1 > /dev/null) if [ -n "$error" ]; then echo FAILED fail "$error" fi - result=$(echo "$3" | ./minilisp 2> /dev/null | tail -1) + result=$(echo "$3" | ./minilisp -r -x "${3}" 2> /dev/null | tail -1) if [ "$result" != "$2" ]; then echo FAILED fail "$2 expected, but got $result" @@ -136,9 +136,9 @@ run restargs '(3)' '(defun f (x . y) (cons x y)) (f 3)' # strings run 'string-concat' 'one & two and 3' '(string-concat "one" " & " "two" " and " 3)' -#run 'symbol->string' 'twelve' " -# (define twelve 12) -# (symbol->string 'twelve)" +run 'symbol->string' 'twelve' " + (define twelve 12) + (symbol->string 'twelve)" run 'string->symbol' 'twelve' '(string->symbol "twelve")' # Lexical closures @@ -154,9 +154,10 @@ run counter 3 ' (counter) (counter)' -#run progn 'I own 10 cents' '(progn (print "I own ") -# (defun add(x y)(+ x y)) -# (println (add 3 7) " cents"))' +run progn 'I own 10 cents()' '(progn (print "I own ") + (defun add(x y)(+ x y)) + (print (add 3 7)) + (print " cents"))' # While loop run while 45 "