Add macros
diff --git a/.vscode/launch.json b/.vscode/launch.json
index 2fd02c2..b438376 100644
--- a/.vscode/launch.json
+++ b/.vscode/launch.json
@@ -9,7 +9,7 @@
"type": "cppdbg",
"request": "launch",
"program": "${workspaceFolder}/src/lisp/lisp",
- "args": ["test.lisp"],
+ "args": ["test-macros.lisp"],
"stopAtEntry": false,
"cwd": "${workspaceFolder}/src/lisp",
"environment": [],
diff --git a/.vscode/settings.json b/.vscode/settings.json
index 19972d2..54186f5 100644
--- a/.vscode/settings.json
+++ b/.vscode/settings.json
@@ -1,4 +1,10 @@
{
"restructuredtext.languageServer.disabled": true,
- "restructuredtext.confPath": "${workspaceFolder}/doc"
+ "restructuredtext.confPath": "${workspaceFolder}/doc",
+ "files.associations": {
+ "Jmk.options": "makefile",
+ "Jmk": "makefile",
+ "*.dasc": "c",
+ "typeinfo": "c"
+ }
}
\ No newline at end of file
diff --git a/share/jmk/jmk.m4 b/share/jmk/jmk.m4
index aacc7bf..0b2c96c 100644
--- a/share/jmk/jmk.m4
+++ b/share/jmk/jmk.m4
@@ -99,7 +99,7 @@
define(finish,
`clean: $(jmk_clean_libs)
- @rm -f *.o *.a *.so $(jmk_target)
+ @rm -f **/*.o **/*.a *.so $(jmk_target) $(OBJECTS)
Makefile: Jmk
status_log(JMK, jmk_build_dir)
diff --git a/src/lisp/Jmk b/src/lisp/Jmk
index ec4e5da..394e077 100644
--- a/src/lisp/Jmk
+++ b/src/lisp/Jmk
@@ -4,14 +4,17 @@
option(PLAT, "`platform to build for: either linux or bluejay'", linux)
-preset(optimize)
+# preset(optimize)
preset(32)
preset(debug)
preset(warn)
+preset(nasm)
-archetype(c, asm)
+archetype(c)
+archetype(asm)
CFLAGS += -Ivendor/luajit/dynasm
+ASMFLAGS += -felf -Fdwarf
OBJECTS = main.o \
lisp.o \
@@ -19,7 +22,8 @@
lib/std.o \
plat/linux.o \
istream.o \
- gc.o
+ gc.o \
+ call_list.o
LUA = vendor/luajit/src/host/minilua
@@ -33,9 +37,11 @@
type(executable)
+F ?= test.lisp
+
run: lisp
- status_log(LISP, test.lisp)
- @./lisp ./test.lisp
+ status_log(LISP, $(F))
+ @./lisp $(F)
format:
status_log(FORMAT, *)
diff --git a/src/lisp/call_list.s b/src/lisp/call_list.s
new file mode 100644
index 0000000..da0c00c
--- /dev/null
+++ b/src/lisp/call_list.s
@@ -0,0 +1,57 @@
+;;; TODO: figure out if I need to do something special with the GC here.
+
+ [bits 32]
+ [global _call_list]
+ [extern length]
+ [extern elt]
+ ;;; This function should call it's first argument with the arguments from
+ ;;; the cons-list passed as its second argument.
+
+ ;;; _call_list(function pointer, cons list)
+_call_list:
+ push ebp
+ mov ebp, esp
+
+ mov edi, [ebp + 12] ; Cons list
+
+ push edi
+ call length ; Length of cons list in eax
+ add esp, 4
+
+ mov ecx, eax ; Store length in counter
+
+ ;; Reserve space for all the stack items
+ shl eax, 2
+ sub esp, eax
+
+ mov esi, esp ; Pointer to top of stack
+
+ ;; Skip all of this if there are no arguments
+ cmp ecx, 0
+ je .done
+
+.loop:
+ ;; Get the previous item. At the start ecx = the length so to get the last
+ ;; index we need to subtract 1
+ dec ecx
+
+ push ecx
+ push edi
+ call elt
+ add esp, 4
+ pop ecx ; This is a scratch register, remember
+
+ ;; We now have the ecx-th item in eax
+ ;; Remember esi is the top of the stack area reserved, so
+ mov [esi + 4 * ecx], eax
+
+ jcxz .done
+ jmp .loop
+
+.done:
+ mov ebx, [ebp + 8] ; Function pointer
+ call ebx
+
+ mov esp, ebp
+ pop ebp
+ ret
diff --git a/src/lisp/compiler.dasc b/src/lisp/compiler.dasc
index 32e2713..ec14078 100644
--- a/src/lisp/compiler.dasc
+++ b/src/lisp/compiler.dasc
@@ -95,8 +95,13 @@
value_t form = car(val);
value_t args = cdr(val);
- if (symstreq(form, "defun"))
+ if (symstreq(form, "defun") || symstreq(form, "defmacro"))
{
+ enum namespace namespace = NS_FUNCTION;
+
+ if (symstreq(form, "defmacro"))
+ namespace = NS_MACRO;
+
dasm_State *d;
dasm_State **Dst = &d;
@@ -162,7 +167,7 @@
| cleanup;
add_function(env, (char *)(name ^ SYMBOL_TAG), link(Dst),
- length(arglist));
+ length(arglist), namespace);
dasm_free(&d);
free(local.stack_slots);
@@ -375,16 +380,28 @@
if (nargs != func->nargs)
err("wrong number of args");
- for (int i = length(args) - 1; i >= 0; i--)
+ if (func->namespace == NS_FUNCTION)
{
- compile_expression(env, local, elt(args, i), Dst);
- | push eax;
- }
+ for (int i = length(args) - 1; i >= 0; i--)
+ {
+ compile_expression(env, local, elt(args, i), Dst);
+ | push eax;
+ }
- | mov ebx, (func->code_addr);
- | call ebx;
- | add esp, (nargs * value_size);
- // result in eax
+ | mov ebx, (func->code_addr);
+ | call ebx;
+ | add esp, (nargs * value_size);
+ // result in eax
+ }
+ else if (func->namespace == NS_MACRO)
+ {
+ value_t expanded_to = call_list(func, args);
+
+ printf("Macro expanded to:\n");
+ printval(expanded_to, 2);
+
+ compile_expression(env, local, expanded_to, Dst);
+ }
}
}
else if (symbolp(val))
@@ -422,7 +439,7 @@
| cleanup;
- add_function(env, name, link(Dst), 0);
+ add_function(env, name, link(Dst), 0, NS_FUNCTION);
}
struct variable *add_variable(struct local *local, enum var_type type,
@@ -458,3 +475,10 @@
return v;
}
+
+extern value_t _call_list(void *addr, value_t list);
+
+value_t call_list(struct function *func, value_t list)
+{
+ return _call_list(func->code_ptr, list);
+}
diff --git a/src/lisp/compiler.h b/src/lisp/compiler.h
index 1cbd92d..ddf7ea0 100644
--- a/src/lisp/compiler.h
+++ b/src/lisp/compiler.h
@@ -5,12 +5,20 @@
#include <stdbool.h>
#include <stdint.h>
+enum namespace
+{
+ NS_FUNCTION,
+ NS_MACRO,
+};
+
struct function
{
char *name;
int nargs; // number of arguments
+ enum namespace namespace;
- union {
+ union
+ {
value_t (*def0)();
value_t (*def1)(value_t);
value_t (*def2)(value_t, value_t);
@@ -82,3 +90,8 @@
// Might return null
struct variable *find_variable(struct local *local, char *name);
void destroy_local(struct local *local);
+
+/**
+ * Like `apply` in lisp, calls func with list args and returns the result.
+ */
+value_t call_list(struct function *func, value_t list);
diff --git a/src/lisp/lib/std.c b/src/lisp/lib/std.c
index 03e17fc..485bf49 100644
--- a/src/lisp/lib/std.c
+++ b/src/lisp/lib/std.c
@@ -39,7 +39,7 @@
return nil;
}
-void add_function(struct environment *env, char *name, void *func, int nargs)
+void add_function(struct environment *env, char *name, void *func, int nargs, enum namespace ns)
{
struct function *last, *new = malloc(sizeof(struct function));
@@ -48,20 +48,21 @@
new->name = name;
new->nargs = nargs;
new->code_ptr = func;
+ new->namespace = ns;
env->first = new;
}
void load_std(struct environment *env)
{
- add_function(env, "+", l_plus, 2);
- add_function(env, "-", l_minus, 2);
- add_function(env, "*", l_times, 2);
- add_function(env, "/", l_divide, 2);
+ add_function(env, "+", l_plus, 2, NS_FUNCTION);
+ add_function(env, "-", l_minus, 2, NS_FUNCTION);
+ add_function(env, "*", l_times, 2, NS_FUNCTION);
+ add_function(env, "/", l_divide, 2, NS_FUNCTION);
- add_function(env, "car", car, 1);
- add_function(env, "cdr", cdr, 1);
- add_function(env, "cons", cons, 2);
+ add_function(env, "car", car, 1, NS_FUNCTION);
+ add_function(env, "cdr", cdr, 1, NS_FUNCTION);
+ add_function(env, "cons", cons, 2, NS_FUNCTION);
- add_function(env, "print", l_printval, 1);
+ add_function(env, "print", l_printval, 1, NS_FUNCTION);
}
diff --git a/src/lisp/lib/std.h b/src/lisp/lib/std.h
index 000129e..5162bab 100644
--- a/src/lisp/lib/std.h
+++ b/src/lisp/lib/std.h
@@ -5,5 +5,5 @@
value_t l_plus(value_t a, value_t b);
-void add_function(struct environment *env, char *name, void *func, int nargs);
+void add_function(struct environment *env, char *name, void *func, int nargs, enum namespace ns);
void load_std(struct environment *env);
diff --git a/src/lisp/lisp.c b/src/lisp/lisp.c
index 30b2861..0d305f2 100644
--- a/src/lisp/lisp.c
+++ b/src/lisp/lisp.c
@@ -34,6 +34,8 @@
c->car = car;
c->cdr = cdr;
+ c->line = 0;
+ c->name = NULL;
item->alloc.type_tag = CONS_TAG;
item->alloc.pool = current_pool;
@@ -344,6 +346,17 @@
return false;
}
+void set_cons_info(value_t cons, int line, char *name)
+{
+ if (!consp(cons))
+ return;
+
+ struct cons *ca = (void *)(cons ^ CONS_TAG);
+
+ ca->line = line;
+ ca->name = name;
+}
+
value_t readn(struct istream *is)
{
value_t first = nil;
@@ -353,7 +366,13 @@
while (read1(is, &read_val))
{
+ int line;
+ char *file;
+
+ is->getpos(is, &line, &file);
*last = cons(read_val, nil);
+ set_cons_info(*last, line, file);
+
last = cdrref(*last);
}
diff --git a/src/lisp/lisp.h b/src/lisp/lisp.h
index 56fa09f..eb9e5f6 100644
--- a/src/lisp/lisp.h
+++ b/src/lisp/lisp.h
@@ -23,11 +23,20 @@
struct cons;
+/// Represents a Lisp value
typedef unsigned int value_t;
struct cons
{
value_t car, cdr;
+
+ /// Line of the input file from where this was parsed, 0 if it was created
+ /// in Lisp.
+ int line;
+
+ /// Description of where the cons was parsed from, or NULL if generated in
+ /// code.
+ char *name;
};
/// Default pool (no pool)
diff --git a/src/lisp/test-macros.lisp b/src/lisp/test-macros.lisp
new file mode 100644
index 0000000..a075a9b
--- /dev/null
+++ b/src/lisp/test-macros.lisp
@@ -0,0 +1,6 @@
+(defmacro weird-const (a b)
+ a)
+
+(defun main ()
+ (let1 (var "this is var")
+ (print (weird-const var 13))))
diff --git a/src/lisp/test.lisp b/src/lisp/test.lisp
index ea78d4f..45c8be4 100644
--- a/src/lisp/test.lisp
+++ b/src/lisp/test.lisp
@@ -5,10 +5,17 @@
(print whatever)
(gc))
+(defmacro weird-identity (a)
+ a)
+
+(defmacro weird-const (a b)
+ a)
+
(defun main ()
(let1 (a (add-two 3))
(print "a is")
- (print a))
+ (print (weird-identity a))
+ (print (weird-const a 4)))
; These allocations should be freed
(list 12 34 56)