Merge branch 'master' of git.sr.ht:~swisschili/bluejay
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/README.md b/README.md
index 7f93e10..f80c12a 100644
--- a/README.md
+++ b/README.md
@@ -1,9 +1,9 @@
# Bluejay
-[![builds.sr.ht status](https://builds.sr.ht/~swisschili/bluejay/commits/.build.yml.svg)](https://builds.sr.ht/~swisschili/bluejay/commits/.build.yml?)
-
<img src="share/branding/bluejay-unsplash.jpg" align="right" width="200">
+[![builds.sr.ht status](https://builds.sr.ht/~swisschili/bluejay/commits/.build.yml.svg)](https://builds.sr.ht/~swisschili/bluejay/commits/.build.yml?)
+
Bluejay is a preemptive kernel for x86. It is inspired by modern day
UNIX-like systems and 80's Lisp machines. The goal is to create a
fully usable kernel and graphical Lisp environment.
@@ -24,19 +24,19 @@
- [x] Efficient kernel virtual allocator
- [ ] Preemptive multitasking
- [x] Multi-threading
- - [ ] Multi-process support
+ - [ ] Multi-process support (waiting on FS)
- [ ] Device drivers
- [x] PCI
- [ ] USB
- [ ] Mouse + keyboard drivers
+ - [ ] Storage device drivers
+ - [x] ATA PIO (broken)
+ - [ ] SATA
- [ ] Filesystem
- [x] Virtual file system
- [x] Initial ramdisk
- - [ ] Storage device drivers
- - [x] ATA PIO
- - [ ] SATA
- [ ] Filesystem drivers
- - [ ] EXT2
+ - [ ] EXT2 (in progress)
- [ ] FAT32
- [ ] System call API
- [ ] Filesystem API
@@ -45,9 +45,9 @@
- [ ] Lisp compiler
- [ ] JIT compiler using dynasm
- [x] Basic compilation
+ - [x] GC
- [ ] Lexical closures
- - [ ] GC
- - [ ] Standard library
+ - [ ] Standard library (in progress)
- [ ] Lisp integrated into kernel
- [ ] User-space driver API
- [ ] Lisp API
@@ -62,5 +62,5 @@
## Documentation
-Manual pages are available in `doc/`, and additional documentation is
-available [on the wiki](https://wiki.swisschili.sh/Bluejay).
+The [Bluejay manual](https://bluejay.readthedocs.io) contains up to date
+documentation.
diff --git a/doc/_static/custom.css b/doc/_static/custom.css
new file mode 100644
index 0000000..f474a3e
--- /dev/null
+++ b/doc/_static/custom.css
@@ -0,0 +1,3 @@
+.pre {
+ font-family:SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,Courier,monospace;
+}
diff --git a/doc/conf.py b/doc/conf.py
index dd82eb7..39e52ea 100644
--- a/doc/conf.py
+++ b/doc/conf.py
@@ -30,3 +30,6 @@
html_theme = 'sphinx_rtd_theme'
html_static_path = ['_static']
+
+def setup(app):
+ app.add_stylesheet('custom.css')
diff --git a/doc/lisp-std.rst b/doc/lisp-std.rst
index 261f388..9f9dbd6 100644
--- a/doc/lisp-std.rst
+++ b/doc/lisp-std.rst
@@ -8,20 +8,59 @@
In general every user-facing API in the standard library should be documented
here.
-Built-in "functions"
+- ``(x ...)`` represents a list ``x``.
+- ``& body`` means that the rest of the list is represented by ``body``.
+- ``[something]`` means that ``something`` is optional.
+
+Top-level primitives
--------------------
-.. function:: (defun function-name (arg1 ... argN) & body)
+These are "functions" that can only appear at the top-level of the program. This
+means they can't be nested in any other expressions.
- Define a function ``function-name`` that takes N arguments with names
- ``arg1`` ... ``argN``. ``body`` is evaluated in order, with the whole
- function evaluating to the last expression.
+.. function:: (defun function-name (args ...) & body)
+
+ Defines a function ``function-name`` that takes ``args`` and evaluates
+ ``body``. ``function-name`` is quoted, not evaluated.
.. code-block:: lisp
+
+ (defun say-hi (name)
+ (print "Hi, ")
+ (print name))
+
+ (say-hi "Joe")
+ ; "Hi,"
+ ; "Joe"
+
+.. function:: (defmacro macro-name (args ...) & body)
+
+ ``defmacro`` is to macros as ``defun`` is to functions. When ``macro-name``
+ is called, whatever it evaluates to will be compiled.
+
+ Note that internally this compiles a function the same way all other
+ functions are compiled, meaning you can call **any** lisp function from a
+ macro definition and it will work as expected.
+
+ .. code-block:: Lisp
+
+ (defun double (n)
+ (+ n n))
- (defun greet (name)
- (string-concat "Hello, " name))
- ; string-concat isn't even implemented yet, but you get the picture.
+ (defmacro call-with-4 (whatever)
+ (print "this was run at **compile time**")
+ (print whatever)
+ ;; ``whatever`` expands to the form passed to this macro, in this case
+ ;; ``double``.
+ (list whatever 4))
+
+ (print (call-with-4 double))
+ ; "this was run at **compile time**"
+ ; 'double
+ ; 8
+
+Functions
+---------
.. function:: (if condition true-condition [false-condition])
@@ -53,17 +92,22 @@
Force the garbage collector (GC) to run.
-Functions
----------
-
.. function:: (car pair)
Return the first item in ``pair``.
+ .. code-block:: lisp
+
+ (car (cons 'a 'b)) ;=> 'a
+
.. function:: (cdr pair)
Return the second (last) item in ``pair``.
+ .. code-block:: lisp
+
+ (cdr (cons 'a 'b)) ;=> 'b
+
.. function:: (cons a b)
Return a cons-pair containing ``a`` and ``b``.
@@ -73,3 +117,24 @@
Print out ``val`` to standard output. This will not be formatted as an
s-expression, but in a manner more similar to the internal representation.
+.. function:: (list & items)
+
+ Returns a cons-list of items.
+
+ .. code-block:: lisp
+
+ (list 1 2 3)
+ ; is the same as
+ (cons 1 (cons 2 (cons 3 nil)))
+
+.. function:: (quote form)
+
+ Returns form without evaluating it.
+
+ .. code-block:: lisp
+
+ '(cons a b)
+ ; or
+ (quote cons a b)
+ ; is the same as
+ (list 'cons 'a 'b)
diff --git a/include/kernel/dri/ata_pio/ata_pio.h b/include/kernel/dri/ata_pio/ata_pio.h
index 667ed3b..2fd8623 100644
--- a/include/kernel/dri/ata_pio/ata_pio.h
+++ b/include/kernel/dri/ata_pio/ata_pio.h
@@ -35,9 +35,12 @@
// Commands
enum
{
- ATA_CMD_READ = 0x20,
- ATA_CMD_WRITE = 0x30,
+ /// Do not retry
+ ATA_CMD_READ = 0x21,
+ /// Do not retry
+ ATA_CMD_WRITE = 0x31,
ATA_CMD_IDENTIFY = 0xec,
+ ATA_CMD_FLUSH_CACHE = 0xe7,
};
void ata_pio_wait_bsy();
@@ -48,4 +51,4 @@
void test_ata_pio();
-void init_ata_pio();
\ No newline at end of file
+void init_ata_pio();
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/kernel/dri/ata_pio/ata_pio.c b/src/kernel/dri/ata_pio/ata_pio.c
index cd426ef..54db487 100644
--- a/src/kernel/dri/ata_pio/ata_pio.c
+++ b/src/kernel/dri/ata_pio/ata_pio.c
@@ -39,17 +39,27 @@
void ata_pio_read_sectors(void *buffer, uint lba, uchar num_sectors)
{
- ata_pio_wait_bsy();
+ ushort *word_buffer = buffer;
+ ata_pio_wait_bsy();
ata_pio_send_init(lba, num_sectors);
outb(ATA_PORT_CMD, ATA_CMD_READ);
ata_pio_wait_bsy();
+
+ if (inb(ATA_PORT_CMD) & ATA_ERR)
+ {
+ kprintf(ERROR "ATA_ERR on read\n");
+ kpanic("Failed to read");
+ }
- asm volatile("rep insw" ::"c"(num_sectors * 256), "d"(ATA_PORT_DATA),
- "D"(buffer));
+ for (int i = 0; i < num_sectors * 256; i++)
+ {
+ word_buffer[i] = inw(ATA_PORT_DATA);
+ }
ata_pio_wait_bsy();
+ outw(ATA_PORT_CMD, ATA_CMD_FLUSH_CACHE);
}
void ata_pio_write_sectors(uint lba, uchar num_sectors, ushort *buffer)
@@ -65,6 +75,9 @@
{
outw(ATA_PORT_DATA, buffer[i]);
}
+
+ ata_pio_wait_bsy();
+ outw(ATA_PORT_CMD, ATA_CMD_FLUSH_CACHE);
}
static void print_buffer()
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 bc5aec8..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);
@@ -224,6 +229,23 @@
return n;
}
+void compile_backquote(struct environment *env, struct local *local,
+ value_t val, dasm_State **Dst)
+{
+ if (!listp(val))
+ {
+ | mov eax, (val);
+ }
+ else
+ {
+ value_t fsym = car(val),
+ args = cdr(val);
+ int nargs = length(args);
+
+ // TODO
+ }
+}
+
void compile_expression(struct environment *env, struct local *local,
value_t val, dasm_State **Dst)
{
@@ -311,6 +333,42 @@
| run_gc;
}
+ else if (symstreq(fsym, "quote"))
+ {
+ if (nargs != 1)
+ err("quote should take exactly 1 argument");
+
+ // Simple!
+ | mov eax, (car(args));
+ }
+ else if (symstreq(fsym, "backquote"))
+ {
+ if (nargs != 1)
+ err("backquote should take exactly 1 argument");
+
+ compile_backquote(env, local, car(args), Dst);
+ }
+ else if (symstreq(fsym, "list"))
+ {
+ | push (nil);
+
+ for (int i = nargs - 1; i >= 0; i--)
+ {
+ compile_expression(env, local, elt(args, i), Dst);
+
+ // push the ith item
+ | push eax;
+ // cons the top two stack items
+ | mov ebx, (cons);
+ | call ebx;
+ // remove the stack items from use
+ | add esp, (2 * value_size);
+ // put the new thing on the stack
+ | push eax;
+ }
+
+ | pop eax;
+ }
else
{
struct function *func =
@@ -322,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))
@@ -369,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,
@@ -405,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 465c9fb..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);
@@ -56,8 +64,16 @@
void compile_expression(struct environment *env, struct local *local,
value_t val, dasm_State **Dst);
+
+/**
+ * Compile a backquoted expression
+ */
+void compile_backquote(struct environment *env, struct local *local,
+ value_t val, dasm_State **Dst);
+
void compile_expr_to_func(struct environment *env, char *name, value_t val,
dasm_State **Dst);
+
int nextpc(struct local *local, dasm_State **Dst);
// Local utilities
@@ -74,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/gc.c b/src/lisp/gc.c
index b9e9574..16ec471 100644
--- a/src/lisp/gc.c
+++ b/src/lisp/gc.c
@@ -20,8 +20,6 @@
void *pointer = (void *)(value & ~HEAP_MASK);
struct alloc *alloc = pointer - sizeof(struct alloc);
- fprintf(stderr, "[ GC ] Marking 0x%p\n", pointer);
-
// Only recursively mark if this hasn't been marked yet. i.e. prevent
// marking circular references twice
if (alloc->mark != gc_mark)
@@ -54,17 +52,15 @@
void _sweep()
{
- for (struct alloc *a = first_a; a; a = a->next)
+ for (struct alloc *a = first_a; a;)
{
if (pool_alive(a->pool) || a->mark == gc_mark)
{
// Marked or in living pool
+ a = a->next;
}
else
{
- printf("Freeing:\n");
- printval(alloc_to_value(a), 2);
-
// Free and remove from allocation list
struct alloc *p = a->prev, *n = a->next;
free_aligned(a);
@@ -89,8 +85,6 @@
gc_mark++;
- fprintf(stderr, "[ GC ] %d (esp 0x%p, ebp 0x%p)\n", gc_mark, esp_p, ebp_p);
-
// For every stack frame until the base of the stack
while (esp_p < gc_base)
{
@@ -110,8 +104,6 @@
esp_p += 2;
}
- fprintf(stderr, "Marked %d\n", num_marked);
-
_sweep();
}
diff --git a/src/lisp/istream.c b/src/lisp/istream.c
index 9351adf..957b2b5 100644
--- a/src/lisp/istream.c
+++ b/src/lisp/istream.c
@@ -81,6 +81,14 @@
fprintf(out, "\033[31m^\033[0m\n");
}
+void stristream_getpos(struct istream *is, int *line, char **name)
+{
+ struct stristream_private *p = is->data;
+
+ *name = "<input literal>";
+ *line = p->line;
+}
+
struct istream *new_stristream(char *str, int length)
{
struct istream *is = malloc(sizeof(struct istream));
@@ -98,6 +106,7 @@
is->peek = stristream_peek;
is->read = stristream_read;
is->showpos = stristream_showpos;
+ is->getpos = stristream_getpos;
return is;
}
@@ -120,6 +129,7 @@
FILE *file;
int next;
bool has_next;
+ int line;
};
int fistream_peek(struct istream *is)
@@ -138,13 +148,20 @@
{
struct fistream_private *p = is->data;
+ char c;
+
if (p->has_next)
{
p->has_next = false;
- return p->next;
+ c = p->next;
}
+ else
+ c = fgetc(p->file);
- return fgetc(p->file);
+ if (c == '\n')
+ p->line++;
+
+ return c;
}
int fistream_read(struct istream *is, char *buffer, int size)
@@ -170,6 +187,14 @@
// TODO: implement
}
+void fistream_getpos(struct istream *is, int *line, char **name)
+{
+ struct fistream_private *p = is->data;
+
+ *line = p->line;
+ *name = "<FILE *>";
+}
+
struct istream *new_fistream(char *path, bool binary)
{
struct istream *is = malloc(sizeof(struct istream));
@@ -187,12 +212,14 @@
p->has_next = false;
p->file = fp;
+ p->line = 1;
is->data = p;
is->get = fistream_get;
is->peek = fistream_peek;
is->read = fistream_read;
is->showpos = fistream_showpos;
+ is->getpos = fistream_getpos;
return is;
}
diff --git a/src/lisp/istream.h b/src/lisp/istream.h
index aceada8..882a740 100644
--- a/src/lisp/istream.h
+++ b/src/lisp/istream.h
@@ -5,17 +5,22 @@
#define MIN(a, b) (a) > (b) ? (b) : (a)
+/// Virtual class representing an input stream. Subclasses must implement every
+/// function.
struct istream
{
void *data;
- // These two return -1 on error
+ /// Returns -1 on error
int (*peek)(struct istream *s);
+ /// Returns -1 on error
int (*get)(struct istream *s);
int (*read)(struct istream *s, char *buffer, int size);
void (*showpos)(struct istream *s, FILE *out);
+
+ void (*getpos)(struct istream *s, int *line, char **name);
};
struct istream *new_stristream(char *str, int length);
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 2d920ea..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;
@@ -248,6 +250,8 @@
bool readint(struct istream *is, value_t *val)
{
+ skipws(is);
+
int number = 0;
if (!isdigit(is->peek(is)))
@@ -263,8 +267,70 @@
return true;
}
+bool readquote(struct istream *is, value_t *val)
+{
+ skipws(is);
+
+ char c = is->peek(is);
+
+ if (c == '\'' || c == '`' || c == ',')
+ {
+ is->get(is);
+
+ if (c == '`' && is->peek(is) == '@')
+ {
+ // This is actually a splice
+ is->get(is);
+ c = '@';
+ }
+
+ // Read the next form and wrap it in the appropriate function
+
+ value_t wrapped;
+ bool has_next = read1(is, &wrapped);
+
+ if (!has_next)
+ {
+ fprintf(stderr, "Expected a form after reader macro char %c\n", c);
+ is->showpos(is, stderr);
+ err("Invalid reader macro");
+ return false;
+ }
+
+ value_t symbol = nil;
+
+ switch (c)
+ {
+ case '\'':
+ symbol = symval("quote");
+ break;
+ case '`':
+ symbol = symval("backquote");
+ break;
+ case ',':
+ symbol = symval("unquote");
+ break;
+ case '@':
+ symbol = symval("unquote-splice");
+ break;
+ }
+
+ *val = cons(symbol, cons(wrapped, nil));
+
+ return true;
+ }
+ else
+ {
+ return false;
+ }
+}
+
bool read1(struct istream *is, value_t *val)
{
+ // This could all be one big short-circuiting || but that is ugly.
+ if (readquote(is, val))
+ return true;
+
if (readsym(is, val))
return true;
@@ -280,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;
@@ -289,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);
}
@@ -312,12 +395,25 @@
value_t v;
char *a = malloc_aligned(strlen(str) + 1);
+ strcpy(a, str);
v = (value_t)a;
v |= STRING_TAG;
return v;
}
+value_t symval(char *str)
+{
+ value_t v;
+
+ char *a = malloc_aligned(strlen(str) + 1);
+ strcpy(a, str);
+ v = (value_t)a;
+ v |= SYMBOL_TAG;
+
+ return v;
+}
+
bool integerp(value_t v)
{
return (v & INT_MASK) == INT_TAG;
diff --git a/src/lisp/lisp.h b/src/lisp/lisp.h
index 9078b45..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)
@@ -100,8 +109,15 @@
bool readlist(struct istream *is, value_t *val);
bool readint(struct istream *is, value_t *val);
+/**
+ * Read a quoted form, including `'` (quote) `\`` (backquote) and `,` (unquote)
+ * @returns true if read successfully, and sets `val`.
+ */
+bool readquote(struct istream *is, value_t *val);
+
value_t intval(int i);
value_t strval(char *str);
+value_t symval(char *str);
value_t cons(value_t car, value_t cdr);
bool read1(struct istream *is, value_t *val);
value_t read(struct istream *is);
diff --git a/src/lisp/plat/linux.c b/src/lisp/plat/linux.c
index 83cc39b..2777950 100644
--- a/src/lisp/plat/linux.c
+++ b/src/lisp/plat/linux.c
@@ -18,7 +18,7 @@
void **aligned_ptr = (void **)((uintptr_t)(mem + 8 + sizeof(void *)) & ~7);
aligned_ptr[-1] = mem;
- memcpy(aligned_ptr, addr, (uintptr_t)aligned_ptr[-2]);
+ memcpy(aligned_ptr, addr, ((uintptr_t *)addr)[-2]);
return aligned_ptr;
}
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 9cfb8b7..45c8be4 100644
--- a/src/lisp/test.lisp
+++ b/src/lisp/test.lisp
@@ -5,15 +5,23 @@
(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
- (cons 12 (cons 34 (cons 45 nil)))
+ (list 12 34 56)
+ (list "a" "b" "c" "d")
; But these should not
(let1 (unused-but-bound (cons 5 6))
- (let1 (val (cons 1 (cons 2 (cons 3 nil))))
+ (let1 (val '(a b c d e))
(calls-gc val))))