Add beginning of Lips compiler, DynASM
diff --git a/.gitmodules b/.gitmodules
new file mode 100644
index 0000000..b997271
--- /dev/null
+++ b/.gitmodules
@@ -0,0 +1,3 @@
+[submodule "src/lisp/vendor/luajit"]
+ path = src/lisp/vendor/luajit
+ url = https://luajit.org/git/luajit.git
diff --git a/src/lisp/.clang-format b/src/lisp/.clang-format
index bfc16c1..419e428 100644
--- a/src/lisp/.clang-format
+++ b/src/lisp/.clang-format
@@ -8,3 +8,5 @@
UseTab: ForIndentation
TabWidth: 4
ColumnLimit: 80
+
+ForEachMacros: ['FOREACH']
\ No newline at end of file
diff --git a/src/lisp/.gitignore b/src/lisp/.gitignore
new file mode 100644
index 0000000..a21650e
--- /dev/null
+++ b/src/lisp/.gitignore
@@ -0,0 +1 @@
+compiler.c
\ No newline at end of file
diff --git a/src/lisp/Jmk b/src/lisp/Jmk
index 7afda0c..42a0e51 100644
--- a/src/lisp/Jmk
+++ b/src/lisp/Jmk
@@ -7,7 +7,21 @@
archetype(c)
-OBJECTS = main.o lisp.o
+CFLAGS += -Ivendor/luajit/dynasm
+
+OBJECTS = main.o \
+ lisp.o \
+ compiler.o
+
+LUA = vendor/luajit/src/host/minilua
+
+vendor/luajit/src/host/minilua: vendor/luajit/src/host/minilua.c
+ status_log(CC, $<)
+ @$(CC) $< -o $@ -lm
+
+compiler.c: compiler.dasc
+ status_log(DYNASM, $<)
+ @$(LUA) vendor/luajit/dynasm/dynasm.lua -o $@ $<
type(executable)
@@ -17,6 +31,6 @@
format:
status_log(FORMAT, *)
- @clang-format -i *.c *.h
+ @clang-format -i *.c *.h *.dasc
finish
diff --git a/src/lisp/compiler.dasc b/src/lisp/compiler.dasc
new file mode 100644
index 0000000..b6d23e7
--- /dev/null
+++ b/src/lisp/compiler.dasc
@@ -0,0 +1,67 @@
+#include "compiler.h"
+
+#include <dasm_proto.h>
+#include <dasm_x86.h>
+
+#define value_size sizeof (struct value)
+
+|.arch x86;
+
+|.macro setup, nvars;
+| push ebp;
+| mov ebp, esp;
+| sub esp, value_size *nvars;
+|.endmacro;
+
+|.macro cleanup;
+| mov esp, ebp;
+| pop ebp;
+| ret;
+|.endmacro;
+
+dasm_State *d;
+unsigned int npc = 8;
+
+struct function *find_function (struct environment *env, char *name)
+{
+ struct function *f = env->first;
+
+ while ( strcmp (f->name, name) != 0 )
+ {
+ if ( f->prev )
+ f = f->prev;
+ else
+ return NULL;
+ }
+
+ return f;
+}
+
+void compile (struct istream *is)
+{
+ |.section code;
+ dasm_init (&d, DASM_MAXSECTION);
+
+ |.globals lbl_;
+ void *labels[ lbl__MAX ];
+ dasm_setupglobal (&d, labels, lbl__MAX);
+
+ |.actionlist lisp_actions;
+ dasm_setup (&d, lisp_actions);
+
+ dasm_growpc (&d, npc);
+}
+
+// First pass populates local
+void firstpass (struct value val, struct environment *env, struct local *local)
+{
+}
+
+// Second pass generates code
+void secondpass (struct value val, struct environment *env, struct local *local)
+{
+}
+
+void toplevel (struct value val, struct environment *env)
+{
+}
diff --git a/src/lisp/compiler.h b/src/lisp/compiler.h
new file mode 100644
index 0000000..9342a12
--- /dev/null
+++ b/src/lisp/compiler.h
@@ -0,0 +1,51 @@
+#pragma once
+
+#include "lisp.h"
+#include <stdbool.h>
+
+struct function
+{
+ char *name;
+ int nargs; // number of arguments
+
+ union {
+ struct value (*def0) ();
+ struct value (*def1) (struct value);
+ struct value (*def2) (struct value, struct value);
+ struct value (*def3) (struct value, struct value, struct value);
+ void *code_ptr;
+ unsigned long code_addr;
+ };
+
+ struct function *prev;
+};
+
+struct environment
+{
+ struct function *first;
+};
+
+struct variable
+{
+ char *name;
+ int number;
+ struct variable *prev;
+};
+
+// local environment
+struct local
+{
+ // temps are accessed at ebp - 8 * (num_vars + temp)
+ bool temps[ 64 ];
+ int num_vars;
+ struct variable *first;
+};
+
+// First pass populates local
+void firstpass (struct value val, struct environment *env, struct local *local);
+// Second pass generates code
+void secondpass (struct value val, struct environment *env,
+ struct local *local);
+void toplevel (struct value val, struct environment *env);
+void compile (struct istream *is);
+struct function *find_function (struct environment *env, char *name);
diff --git a/src/lisp/lisp b/src/lisp/lisp
index 1732c3b..9f20723 100755
--- a/src/lisp/lisp
+++ b/src/lisp/lisp
Binary files differ
diff --git a/src/lisp/lisp-notes.org b/src/lisp/lisp-notes.org
new file mode 100644
index 0000000..614ac8f
--- /dev/null
+++ b/src/lisp/lisp-notes.org
@@ -0,0 +1,36 @@
+#+TITLE: Lisp Notes
+#+AUTHOR: swissChili
+
+* Compiler
+
+ The compiler will using DynASM to generate code at runtime. It won’t
+ be a JIT (no interpreter), strictly a dynamic compiler.
+
+ For now I won’t even have a register allocator, all variables and
+ temporaries will be stored on the stack. This makes more or less
+ sense considering they will need to be put on the stack anyway when
+ a function is called.
+
+ An example assembly is in =scratch.s=.
+
+** First Pass
+
+ The first pass will involve finding all the local variables
+ (i.e. anything defined with =let=) and all the temporary values
+ necessary. Once a variable is out of scope, its stack space becomes
+ usable by other variables. Similarly, once a temporary is used, its
+ space becomes available. Variables are addressable by name but
+ temporaries are not.
+
+** Second Pass
+
+ The second pass will actually generate assembly. First enough space
+ will be reserved on the stack for the variables and temporaries,
+ then the AST will be walked as before to generate all the
+ appropriate function calls.
+
+ When a function call is generated, first temporaries are allocated
+ for all its arguments. Then the sub-expressions are compiled left to
+ right given these temporary locations as the outputs. For now we
+ will assume that everything is either a variable or a function
+ call, there will be no literals yet.
diff --git a/src/lisp/lisp.c b/src/lisp/lisp.c
index 8be2b4c..51a3270 100644
--- a/src/lisp/lisp.c
+++ b/src/lisp/lisp.c
@@ -176,7 +176,7 @@
{
printf ("list:\n");
- for (struct value n = v; !nilp (n); n = cdr (n))
+ for ( struct value n = v; !nilp (n); n = cdr (n) )
{
printval (car (n), depth + 1);
}
@@ -384,8 +384,7 @@
{
struct value *next = &v;
- while ( next->tag.type == T_CONS &&
- cdr(*next).tag.type == T_CONS )
+ while ( next->tag.type == T_CONS && cdr (*next).tag.type == T_CONS )
{
next = &next->value.cons_val->cdr;
}
@@ -413,3 +412,13 @@
{
return v.tag.type == T_NIL;
}
+
+int length (struct value v)
+{
+ int i = 0;
+
+ FOREACH (item, v)
+ i++;
+
+ return i;
+}
diff --git a/src/lisp/lisp.h b/src/lisp/lisp.h
index a02b1b3..d8bc0e4 100644
--- a/src/lisp/lisp.h
+++ b/src/lisp/lisp.h
@@ -80,6 +80,7 @@
struct value cdr (struct value v);
bool listp (struct value v);
bool nilp (struct value v);
+int length (struct value v);
void printval (struct value v, int depth);
@@ -91,3 +92,10 @@
void err (const char *msg);
extern struct value nil;
+
+#define FOREACH(item, list) \
+ for ( ; listp (list); ) \
+ for ( struct value item = car (list), _foreach_current = list; \
+ !nilp (_foreach_current); \
+ _foreach_current = cdr (_foreach_current), \
+ item = car (_foreach_current) )
diff --git a/src/lisp/scratch.s b/src/lisp/scratch.s
index 1775a0a..5428dce 100644
--- a/src/lisp/scratch.s
+++ b/src/lisp/scratch.s
@@ -40,4 +40,7 @@
push [ebp - 4]
push [ebp + 4] ; The function's return address
call whatever
+
+ mov esp, ebp ; Finally clean up
+ pop ebp
ret
diff --git a/src/lisp/test.lisp b/src/lisp/test.lisp
index ee6773e..2ddedd8 100644
--- a/src/lisp/test.lisp
+++ b/src/lisp/test.lisp
@@ -1,2 +1,5 @@
(defun my-fun (a b)
(display t "%a\n" (+ a b)))
+
+(defun main ()
+ (my-fun pi four))
diff --git a/src/lisp/vendor/luajit b/src/lisp/vendor/luajit
new file mode 160000
index 0000000..20f556e
--- /dev/null
+++ b/src/lisp/vendor/luajit
@@ -0,0 +1 @@
+Subproject commit 20f556e53190ab9a735b932f5d868d45ec536a70