Finish basics of Lisp parser, add simple test
diff --git a/src/lisp/Jmk b/src/lisp/Jmk
index 5fc5fc4..7afda0c 100644
--- a/src/lisp/Jmk
+++ b/src/lisp/Jmk
@@ -13,6 +13,10 @@
run: lisp
status_log(RUN, ./lisp)
- @./lisp
+ @./lisp "$$(cat test.lisp)"
+
+format:
+ status_log(FORMAT, *)
+ @clang-format -i *.c *.h
finish
diff --git a/src/lisp/lisp b/src/lisp/lisp
index 5f8e81b..1732c3b 100755
--- a/src/lisp/lisp
+++ b/src/lisp/lisp
Binary files differ
diff --git a/src/lisp/lisp.c b/src/lisp/lisp.c
index be75adf..8be2b4c 100644
--- a/src/lisp/lisp.c
+++ b/src/lisp/lisp.c
@@ -1,18 +1,28 @@
#include "lisp.h"
-#include <stdlib.h>
-#include <string.h>
#include <ctype.h>
#include <stdbool.h>
#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
-#define MIN(a, b) (a)>(b)?(b):(a)
+#define MIN(a, b) (a) > (b) ? (b) : (a)
struct alloc_list *first_a = NULL, *last_a = NULL;
+struct value nil = {.tag = {.type = T_NIL}};
+
+void err (const char *msg)
+{
+ fprintf (stderr, "ERROR: %s\n", msg);
+}
+
struct value cons (struct value car, struct value cdr)
{
struct cons *c = malloc (sizeof (struct cons));
+ c->car = car;
+ c->cdr = cdr;
+
struct alloc_list *item = malloc (sizeof (struct alloc_list));
item->type = T_CONS;
item->data.cons_val = c;
@@ -44,9 +54,8 @@
bool isallowedchar (char c)
{
- return (c >= '#' && c <= '\'') ||
- (c >= '*' && c <= '/') ||
- (c >= '>' && c <= '@');
+ return (c >= '#' && c <= '\'') || (c >= '*' && c <= '/') ||
+ (c >= '>' && c <= '@');
}
bool issymstart (char c)
@@ -69,9 +78,9 @@
int size = 8;
char *s = malloc (size);
- s[0] = is->get (is);
+ s[ 0 ] = is->get (is);
- for ( int i = 1; ; i++ )
+ for ( int i = 1;; i++ )
{
if ( issym (is->peek (is)) )
{
@@ -81,11 +90,11 @@
s = realloc (s, size);
}
- s[i] = is->get (is);
+ s[ i ] = is->get (is);
}
else
{
- s[i] = 0;
+ s[ i ] = 0;
val->tag.type = T_SYMBOL;
val->tag.length = i - 1;
val->value.symbol_val = s;
@@ -106,18 +115,18 @@
int size = 8;
char *s = malloc (size);
- (void) is->get (is);
+ (void)is->get (is);
- for ( int i = 0; ; i++ )
+ for ( int i = 0;; i++ )
{
if ( is->peek (is) != '"' )
{
if ( i >= size )
{
- i *= 2;
- s = realloc (s, i);
+ size *= 2;
+ s = realloc (s, size);
}
-
+
char c = is->get (is);
if ( escape && c == 'n' )
@@ -133,7 +142,7 @@
else
{
escape = false;
- s[i] = c;
+ s[ i ] = c;
}
}
else
@@ -152,9 +161,9 @@
void printval (struct value v, int depth)
{
for ( int i = 0; i < depth; i++ )
- printf(" ");
+ printf (" ");
- switch (v.tag.type)
+ switch ( v.tag.type )
{
case T_SYMBOL:
printf ("'%s\n", v.value.symbol_val);
@@ -162,11 +171,53 @@
case T_STRING:
printf ("\"%s\"\n", v.value.string_val);
return;
+ case T_CONS:
+ if ( listp (v) )
+ {
+ printf ("list:\n");
+
+ for (struct value n = v; !nilp (n); n = cdr (n))
+ {
+ printval (car (n), depth + 1);
+ }
+ }
+ else
+ {
+ printf ("cons:\n");
+ printval (v.value.cons_val->car, depth + 1);
+ printval (v.value.cons_val->cdr, depth + 1);
+ }
+ break;
+ case T_NIL:
+ printf ("nil\n");
+ break;
default:
printf ("<unknown %d>\n", v.tag.type);
}
}
+bool readlist (struct istream *is, struct value *val)
+{
+ skipws (is);
+
+ if ( is->peek (is) != '(' )
+ return false;
+
+ is->get (is);
+
+ *val = readn (is);
+
+ if ( is->peek (is) != ')' )
+ {
+ is->showpos (is, stderr);
+ err ("Unterminated list");
+ return false;
+ }
+ is->get (is);
+
+ return true;
+}
+
bool read1 (struct istream *is, struct value *val)
{
if ( readsym (is, val) )
@@ -175,14 +226,36 @@
if ( readstr (is, val) )
return true;
+ if ( readlist (is, val) )
+ return true;
+
return false;
}
+struct value readn (struct istream *is)
+{
+ struct value first = nil;
+ struct value *last = &first;
+
+ struct value read_val;
+
+ while ( read1 (is, &read_val) )
+ {
+ *last = cons (read_val, nil);
+ last = &last->value.cons_val->cdr;
+ }
+
+ return first;
+}
+
struct stristream_private
{
char *val;
int i;
int length;
+ int line;
+ int fromleft;
+ int linestart;
};
int stristream_peek (struct istream *is)
@@ -200,10 +273,22 @@
struct stristream_private *p = is->data;
if ( p->i < p->length )
- return p->val[ p->i++ ];
+ {
+ char c = p->val[ p->i++ ];
+
+ p->fromleft++;
+
+ if ( c == '\n' )
+ {
+ p->fromleft = 1;
+ p->line++;
+ p->linestart = p->i;
+ }
+
+ return c;
+ }
else
return -1;
-
}
int stristream_read (struct istream *s, char *buffer, int size)
@@ -215,6 +300,31 @@
return len;
}
+void stristream_showpos (struct istream *s, FILE *out)
+{
+ struct stristream_private *p = s->data;
+
+ fprintf (out, "line: %d, char %d\n", p->line, p->fromleft);
+
+ int end = p->length;
+
+ for ( int i = p->linestart; i < p->length; i++ )
+ {
+ if ( p->val[ i ] == '\n' )
+ {
+ end = i;
+ break;
+ }
+ }
+
+ fprintf (out, " | %.*s\n", end - p->linestart, p->val + p->linestart);
+ fprintf (out, " | ");
+ for ( int i = 0; i < p->fromleft - 1; i++ )
+ fprintf (out, " ");
+
+ fprintf (out, "\033[31m^\033[0m\n");
+}
+
struct istream *new_stristream (char *str, int length)
{
struct istream *is = malloc (sizeof (struct istream));
@@ -223,16 +333,20 @@
p->val = strndup (str, length);
p->i = 0;
p->length = length;
+ p->line = 1;
+ p->fromleft = 1;
+ p->linestart = 0;
is->data = p;
is->get = stristream_get;
is->peek = stristream_peek;
is->read = stristream_read;
+ is->showpos = stristream_showpos;
return is;
}
-void del_stristream(struct istream *stristream)
+void del_stristream (struct istream *stristream)
{
struct stristream_private *p = stristream->data;
free (p->val);
@@ -242,12 +356,12 @@
struct istream *new_stristream_nt (char *str)
{
- return new_stristream(str, strlen(str));
+ return new_stristream (str, strlen (str));
}
-bool startswith (struct istream *s, const char *pattern)
+bool startswith (struct istream *s, char *pattern)
{
- const char *check = strdup (pattern);
+ char *check = strdup (pattern);
s->read (s, check, strlen (pattern));
bool res = strcmp (check, pattern) == 0;
@@ -255,3 +369,47 @@
return res;
}
+
+struct value strval (char *str)
+{
+ struct value v;
+ v.tag.type = T_STRING;
+ v.tag.length = strlen (str);
+ v.value.string_val = str;
+
+ return v;
+}
+
+bool listp (struct value v)
+{
+ struct value *next = &v;
+
+ while ( next->tag.type == T_CONS &&
+ cdr(*next).tag.type == T_CONS )
+ {
+ next = &next->value.cons_val->cdr;
+ }
+
+ return next->value.cons_val->cdr.tag.type == T_NIL;
+}
+
+struct value car (struct value v)
+{
+ if ( v.tag.type != T_CONS )
+ return nil;
+
+ return v.value.cons_val->car;
+}
+
+struct value cdr (struct value v)
+{
+ if ( v.tag.type != T_CONS )
+ return nil;
+
+ return v.value.cons_val->cdr;
+}
+
+bool nilp (struct value v)
+{
+ return v.tag.type == T_NIL;
+}
diff --git a/src/lisp/lisp.h b/src/lisp/lisp.h
index 016f8e5..a02b1b3 100644
--- a/src/lisp/lisp.h
+++ b/src/lisp/lisp.h
@@ -1,8 +1,7 @@
#pragma once
#include <stdbool.h>
-
-extern unsigned int cons_magic;
+#include <stdio.h>
enum type
{
@@ -61,21 +60,34 @@
int (*get) (struct istream *s);
int (*read) (struct istream *s, char *buffer, int size);
+
+ void (*showpos) (struct istream *s, FILE *out);
};
-bool startswith (struct istream *s, const char *pattern);
+bool startswith (struct istream *s, char *pattern);
bool readsym (struct istream *is, struct value *val);
bool readstr (struct istream *is, struct value *val);
+bool readlist (struct istream *is, struct value *val);
+struct value strval (char *str);
struct value cons (struct value car, struct value cdr);
bool read1 (struct istream *is, struct value *val);
-struct value read (struct istream);
-struct value readn (struct istream);
+struct value read (struct istream *is);
+struct value readn (struct istream *is);
+
+struct value car (struct value v);
+struct value cdr (struct value v);
+bool listp (struct value v);
+bool nilp (struct value v);
void printval (struct value v, int depth);
struct istream *new_stristream (char *str, int length);
// same as above but null terminated
struct istream *new_stristream_nt (char *str);
-void del_stristream(struct istream *stristream);
+void del_stristream (struct istream *stristream);
+
+void err (const char *msg);
+
+extern struct value nil;
diff --git a/src/lisp/main.c b/src/lisp/main.c
index 7c8224e..4ed48ea 100644
--- a/src/lisp/main.c
+++ b/src/lisp/main.c
@@ -2,7 +2,13 @@
int main (int argc, char **argv)
{
- struct istream *is = new_stristream_nt ("abcde \"asdf dsf sdf\"");
+ if ( argc < 2 )
+ {
+ puts ("pass the string you want to parse as the first argument please");
+ return 1;
+ }
+
+ struct istream *is = new_stristream_nt (argv[ 1 ]);
struct value val;
while ( read1 (is, &val) )
diff --git a/src/lisp/scratch.s b/src/lisp/scratch.s
new file mode 100644
index 0000000..1775a0a
--- /dev/null
+++ b/src/lisp/scratch.s
@@ -0,0 +1,43 @@
+;;; scratch.s -- ideas for how code generation will work
+
+ ;; (defun lisp-function (A B)
+ ;; (let ((local-var (whatever)))
+ ;; (whatever local-var (something-else 4))))
+ ;;
+ ;; This means the stack looks like this:
+ ;; B_value
+ ;; B_tag
+ ;; A_value
+ ;; A_tag
+ ;; result pointer
+ ;; return pointer <---- esp
+lisp_function:
+ push ebp ; Now ebp can be used as an offset
+ mov ebp, esp
+ sub esp, 16 ; 1 var, 1 temporary
+
+ ;; Now, call `whatever' with no arguments
+ ;; For now we will do no register allocation, so don't even
+ ;; bother saving anything.
+ lea eax, [ebp - 8]
+ push eax ; Return address
+ call whatever
+
+ ;; Now we need to evaluate (something-else 4) and store it in a
+ ;; temporary variable.
+ ;; First set up the literal 4
+ push 4 ; The value
+ push 0x20000000 ; Type tag and length
+ ;; Then set up the return address
+ lea eax, [ebp - 16]
+ push eax
+ call something_else ; Result stored in temporary
+
+ ;; Next function: `whatever'
+ push [ebp - 12] ; The temporary
+ push [ebp - 16]
+ push [ebp - 8] ; The variable
+ push [ebp - 4]
+ push [ebp + 4] ; The function's return address
+ call whatever
+ ret
diff --git a/src/lisp/test.lisp b/src/lisp/test.lisp
new file mode 100644
index 0000000..ee6773e
--- /dev/null
+++ b/src/lisp/test.lisp
@@ -0,0 +1,2 @@
+(defun my-fun (a b)
+ (display t "%a\n" (+ a b)))