Add argument variable binding
diff --git a/src/lisp/Jmk b/src/lisp/Jmk
index aa75bf6..4d0825f 100644
--- a/src/lisp/Jmk
+++ b/src/lisp/Jmk
@@ -17,7 +17,8 @@
 			lisp.o \
 			compiler.o \
 			lib/std.o \
-			plat/linux.o
+			plat/linux.o \
+			istream.o
 
 LUA = vendor/luajit/src/host/minilua
 
@@ -33,7 +34,7 @@
 
 run: lisp
 	status_log(RUN, ./lisp)
-	@./lisp "$$(cat test.lisp)"
+	@./lisp ./test.lisp
 
 format:
 	status_log(FORMAT, *)
diff --git a/src/lisp/compiler.dasc b/src/lisp/compiler.dasc
index 2ac21f3..3040e45 100644
--- a/src/lisp/compiler.dasc
+++ b/src/lisp/compiler.dasc
@@ -7,6 +7,9 @@
 #include <dasm_proto.h>
 #include <dasm_x86.h>
 
+#include <stdlib.h>
+#include <string.h>
+
 #define value_size sizeof(value_t)
 
 |.arch x86;
@@ -73,6 +76,7 @@
 		dasm_growpc(&d, local.npc);
 
 		// Generate code
+		// TODO: first pass, extract bound and free variables
 
 		| setup 0;
 
@@ -84,6 +88,17 @@
 		if ((name & HEAP_MASK) != SYMBOL_TAG)
 			err("function name must be a symbol");
 
+		value_t a = arglist;
+		for (int i = 0; !nilp(a); a = cdr(a), i++)
+		{
+			if (!symbolp(car(a)))
+			{
+				err("defun argument must be a symbol");
+			}
+
+			add_variable(&local, V_ARGUMENT, (char *)(car(a) ^ SYMBOL_TAG), i);
+		}
+
 		for (; !nilp(body); body = cdr(body))
 		{
 			compile_expression(env, &local, car(body), Dst);
@@ -131,7 +146,11 @@
 	{
 		| mov eax, (nil);
 	}
-	else if (integerp(val) || stringp(val) || symbolp(val))
+	else if (symstreq(val, "t"))
+	{
+		| mov eax, (t);
+	}
+	else if (integerp(val) || stringp(val))
 	{
 		| mov eax, val;
 	}
@@ -161,7 +180,7 @@
 
 			compile_expression(env, local, elt(args, 1), Dst);
 			| jmp =>after_label;
-			|=>false_label:
+			|=>false_label:;
 			if (nargs == 3)
 			    compile_expression(env, local, elt(args, 2), Dst);
 			|=>after_label:
@@ -171,6 +190,9 @@
 			struct function *func =
 			    find_function(env, (char *)(fsym ^ SYMBOL_TAG));
 
+			if (func == NULL)
+				err("Function undefined");
+
 			if (nargs != func->nargs)
 				err("wrong number of args");
 
@@ -182,10 +204,27 @@
 
 			| mov ebx, (func->code_addr);
 			| call ebx;
-			| add esp, (nargs * 4);
+			| add esp, (nargs * value_size);
 			// result in eax
 		}
 	}
+	else if (symbolp(val))
+	{
+		// For now ignore global variables, only search locally
+		struct variable *v = find_variable(local, (char *)(val ^ SYMBOL_TAG));
+
+		if (!v)
+			err("Variable unbound");
+
+		switch (v->type)
+		{
+		case V_ARGUMENT:
+			| mov eax, dword [ebp + value_size * (v->number + 2)];
+			break;
+		default:
+			err("Sorry, can only access V_ARGUMENT variables for now :(");
+		}
+	}
 }
 
 void compile_expr_to_func(struct environment *env, char *name, value_t val,
@@ -200,3 +239,37 @@
 
 	add_function(env, name, link(Dst), 0);
 }
+
+struct variable *add_variable(struct local *local, enum var_type type,
+                              char *name, int number)
+{
+	struct variable *var = malloc(sizeof(struct variable));
+	var->prev = local->first;
+	var->type = type;
+	var->name = name;
+	var->number = number;
+
+	local->first = var;
+
+	return var;
+}
+
+void destroy_local(struct local *local)
+{
+	for (struct variable *v = local->first; v;)
+	{
+		struct variable *t = v;
+		v = v->prev;
+		free(t);
+	}
+}
+
+struct variable *find_variable(struct local *local, char *name)
+{
+	struct variable *v = local->first;
+
+	for (; v && strcmp(v->name, name) != 0; v = v->prev)
+	{}
+
+	return v;
+}
diff --git a/src/lisp/compiler.h b/src/lisp/compiler.h
index ae66001..4d8ffba 100644
--- a/src/lisp/compiler.h
+++ b/src/lisp/compiler.h
@@ -27,10 +27,19 @@
 	struct function *first;
 };
 
+enum var_type
+{
+	V_BOUND,    // Bound local variable
+	V_ARGUMENT, // Bound function argument
+	V_GLOBAL,   // Global variable
+	V_FREE      // Free (lexical) variable
+};
+
 struct variable
 {
 	char *name;
-	int number;
+	uintptr_t number;
+	enum var_type type;
 	struct variable *prev;
 };
 
@@ -52,3 +61,8 @@
 void compile_tl(value_t val, struct environment *env);
 struct environment compile_all(struct istream *is);
 struct function *find_function(struct environment *env, char *name);
+struct variable *add_variable(struct local *local, enum var_type type,
+                              char *name, int number);
+// Might return null
+struct variable *find_variable(struct local *local, char *name);
+void destroy_local(struct local *local);
diff --git a/src/lisp/istream.c b/src/lisp/istream.c
new file mode 100644
index 0000000..9351adf
--- /dev/null
+++ b/src/lisp/istream.c
@@ -0,0 +1,208 @@
+#include "istream.h"
+
+#include <stdbool.h>
+#include <stdlib.h>
+#include <string.h>
+
+struct stristream_private
+{
+	char *val;
+	int i;
+	int length;
+	int line;
+	int fromleft;
+	int linestart;
+};
+
+int stristream_peek(struct istream *is)
+{
+	struct stristream_private *p = is->data;
+
+	if (p->i < p->length)
+		return p->val[p->i];
+	else
+		return -1;
+}
+
+int stristream_get(struct istream *is)
+{
+	struct stristream_private *p = is->data;
+
+	if (p->i < p->length)
+	{
+		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)
+{
+	struct stristream_private *p = s->data;
+
+	int len = MIN(size, p->length - p->i);
+	memcpy(buffer, p->val, len);
+	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));
+	struct stristream_private *p = malloc(sizeof(struct stristream_private));
+
+	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)
+{
+	struct stristream_private *p = stristream->data;
+	free(p->val);
+	free(p);
+	free(stristream);
+}
+
+struct istream *new_stristream_nt(char *str)
+{
+	return new_stristream(str, strlen(str));
+}
+
+struct fistream_private
+{
+	FILE *file;
+	int next;
+	bool has_next;
+};
+
+int fistream_peek(struct istream *is)
+{
+	struct fistream_private *p = is->data;
+
+	if (p->has_next)
+		return p->next;
+
+	p->next = fgetc(p->file);
+	p->has_next = true;
+	return p->next;
+}
+
+int fistream_get(struct istream *is)
+{
+	struct fistream_private *p = is->data;
+
+	if (p->has_next)
+	{
+		p->has_next = false;
+		return p->next;
+	}
+
+	return fgetc(p->file);
+}
+
+int fistream_read(struct istream *is, char *buffer, int size)
+{
+	struct fistream_private *p = is->data;
+
+	int offset = 0;
+
+	if (p->has_next)
+	{
+		*buffer = p->next;
+		p->has_next = false;
+		buffer++;
+		size--;
+		offset = 1;
+	}
+
+	return (int)fread(buffer, 1, size, p->file) + offset;
+}
+
+void fistream_showpos(struct istream *s, FILE *out)
+{
+	// TODO: implement
+}
+
+struct istream *new_fistream(char *path, bool binary)
+{
+	struct istream *is = malloc(sizeof(struct istream));
+
+	FILE *fp = fopen(path, binary ? "rb" : "r");
+
+	if (fp == NULL)
+	{
+		free(is);
+		return NULL;
+	}
+
+	struct fistream_private *p = is->data =
+	    malloc(sizeof(struct fistream_private));
+
+	p->has_next = false;
+	p->file = fp;
+
+	is->data = p;
+	is->get = fistream_get;
+	is->peek = fistream_peek;
+	is->read = fistream_read;
+	is->showpos = fistream_showpos;
+
+	return is;
+}
+
+void del_fistream(struct istream *is)
+{
+	struct fistream_private *p = is->data;
+
+	fclose(p->file);
+
+	free(is->data);
+	free(is);
+}
diff --git a/src/lisp/istream.h b/src/lisp/istream.h
new file mode 100644
index 0000000..aceada8
--- /dev/null
+++ b/src/lisp/istream.h
@@ -0,0 +1,27 @@
+#pragma once
+
+#include <stdbool.h>
+#include <stdio.h>
+
+#define MIN(a, b) (a) > (b) ? (b) : (a)
+
+struct istream
+{
+	void *data;
+
+	// These two return -1 on error
+	int (*peek)(struct istream *s);
+	int (*get)(struct istream *s);
+
+	int (*read)(struct istream *s, char *buffer, int size);
+
+	void (*showpos)(struct istream *s, FILE *out);
+};
+
+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);
+
+struct istream *new_fistream(char *path, bool binary);
+void del_fistream(struct istream *fistream);
\ No newline at end of file
diff --git a/src/lisp/lisp.c b/src/lisp/lisp.c
index 7a7d6cb..58e58e7 100644
--- a/src/lisp/lisp.c
+++ b/src/lisp/lisp.c
@@ -7,11 +7,10 @@
 #include <stdlib.h>
 #include <string.h>
 
-#define MIN(a, b) (a) > (b) ? (b) : (a)
-
 struct alloc_list *first_a = NULL, *last_a = NULL;
 
 value_t nil = 0b00101111; // magic ;)
+value_t t = 1 << 3;
 
 void err(const char *msg)
 {
@@ -282,117 +281,6 @@
 	return first;
 }
 
-struct stristream_private
-{
-	char *val;
-	int i;
-	int length;
-	int line;
-	int fromleft;
-	int linestart;
-};
-
-int stristream_peek(struct istream *is)
-{
-	struct stristream_private *p = is->data;
-
-	if (p->i < p->length)
-		return p->val[p->i];
-	else
-		return -1;
-}
-
-int stristream_get(struct istream *is)
-{
-	struct stristream_private *p = is->data;
-
-	if (p->i < p->length)
-	{
-		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)
-{
-	struct stristream_private *p = s->data;
-
-	int len = MIN(size, p->length - p->i);
-	memcpy(buffer, p->val, len);
-	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));
-	struct stristream_private *p = malloc(sizeof(struct stristream_private));
-
-	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)
-{
-	struct stristream_private *p = stristream->data;
-	free(p->val);
-	free(p);
-	free(stristream);
-}
-
-struct istream *new_stristream_nt(char *str)
-{
-	return new_stristream(str, strlen(str));
-}
-
 bool startswith(struct istream *s, char *pattern)
 {
 	char *check = strdup(pattern);
diff --git a/src/lisp/lisp.h b/src/lisp/lisp.h
index d3a0a29..48de7d4 100644
--- a/src/lisp/lisp.h
+++ b/src/lisp/lisp.h
@@ -1,5 +1,6 @@
 #pragma once
 
+#include "istream.h"
 #include <stdbool.h>
 #include <stdio.h>
 
@@ -52,19 +53,6 @@
 	struct alloc_list *next, *prev;
 };
 
-struct istream
-{
-	void *data;
-
-	// These two return -1 on error
-	int (*peek)(struct istream *s);
-	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, char *pattern);
 
 bool readsym(struct istream *is, value_t *val);
@@ -95,13 +83,9 @@
 
 void printval(value_t 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 err(const char *msg);
 
 bool symstreq(value_t sym, char *str);
 
 extern value_t nil;
+extern value_t t;
diff --git a/src/lisp/main.c b/src/lisp/main.c
index 5679572..b2c7590 100644
--- a/src/lisp/main.c
+++ b/src/lisp/main.c
@@ -9,11 +9,17 @@
 		return 1;
 	}
 
-	struct istream *is = new_stristream_nt(argv[1]);
+	struct istream *is = new_fistream(argv[1], false);
+
+	if (is == NULL)
+	{
+		fprintf(stderr, "Could not open %s\n", argv[1]);
+		return 1;
+	}
 
 	struct environment env = compile_all(is);
 	value_t (*lisp_main)() = find_function(&env, "main")->def0;
 	lisp_main();
 
-	//	del_stristream (is);
+	del_fistream(is);
 }
diff --git a/src/lisp/test.lisp b/src/lisp/test.lisp
index 6f4abb8..08b96ba 100644
--- a/src/lisp/test.lisp
+++ b/src/lisp/test.lisp
@@ -1,12 +1,7 @@
-(defun two-plus-two ()
-  (+ 2 2))
-
-(defun hmm-main ()
-  (print "64 / (2 + 2) =")
-  (print (/ 64 (two-plus-two)))
-  (print nil))
+(defun add-two (a)
+  (+ a 2))
 
 (defun main ()
   (if t
-    (print 1)
-    (print 2)))
+    (print (add-two (* 4 3)))
+    (print (- 3 6))))