Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merge Extend #6

Merged
merged 4 commits into from
Nov 29, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
@@ -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
Expand Down
29 changes: 7 additions & 22 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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

Expand Down Expand Up @@ -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).
7 changes: 1 addition & 6 deletions src/gc.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand All @@ -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");
Expand Down
40 changes: 17 additions & 23 deletions src/minilisp.c
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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;
Expand All @@ -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);
}
}
Expand Down Expand Up @@ -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;
}

Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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) {
Expand Down
8 changes: 2 additions & 6 deletions src/repl.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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);
Expand Down Expand Up @@ -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]);
Expand Down
19 changes: 10 additions & 9 deletions test.sh
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
#!/bin/bash
#!/bin/bash -x

function fail() {
echo -n -e '\e[1;31m[ERROR]\e[0m '
Expand All @@ -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"
Expand Down Expand Up @@ -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
Expand All @@ -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 "
Expand Down