Add low-level class support, stub of high level OOP wrapper
diff --git a/doc/lisp_reference/lisp_reference.tex b/doc/lisp_reference/lisp_reference.tex
index 78c6255..fa07548 100644
--- a/doc/lisp_reference/lisp_reference.tex
+++ b/doc/lisp_reference/lisp_reference.tex
@@ -398,6 +398,42 @@
   empty.
 \end{defin}
 
+\section{Strings \& Symbols}
+
+\begin{defin}{(\optlist{\func{string->symbol}\\\func{symbol->string}} \param{value})}%
+  \index{string->symbol}\index{symbol->string} Return a new
+  \ret{symbol} or \ret{string} representing \param{value},
+  respectively; or \ret{\nil} if \param{value} is not of the specified
+  type.
+\end{defin}
+
+\begin{defin}{(\func{concat} \param{strings} \more)}\index{concat}
+  Return \ret{a new string} containing the content of each of the
+  strings \param{strings} one after the other.
+\end{defin}
+
+\begin{defin}{(\func{gensym})}\index{gensym}
+  Return a \ret{new, unique symbol}. Useful in macros.
+\end{defin}
+
+\section{Object Oriented Programming}
+
+\begin{defin}{(\func{make-class} \param{type} \param{num-members})}\index{make-class}
+  Create a \ret{new class instance} of type \param{type} with slots
+  for \param{num-members} members. This is a low-level function that
+  should not be often used.
+\end{defin}
+
+\begin{defin}{(\func{class-member} \param{class} \param{index})}\index{class-member}
+  Get the \ret{member variable at index \param{index}} in the class
+  instance \param{class}, or \ret{\nil} if \param{index} out of range.
+\end{defin}
+
+\begin{defin}{(\func{set-class-member} \param{class} \param{index} \param{value})}\index{set-class-member}
+  Set the member variable at index \param{index} in the class instance
+  \param{class} to \param{value}.
+\end{defin}
+
 \section{Input \& Output}
 
 \begin{defin}{
diff --git a/lib/lisp/std/list-functions.lisp b/lib/lisp/std/list-functions.lisp
index f25ceb5..bba2835 100644
--- a/lib/lisp/std/list-functions.lisp
+++ b/lib/lisp/std/list-functions.lisp
@@ -34,3 +34,16 @@
 	  (reduce fun (cdr list)
 			  (funcall fun initial-value
 					   (car list)))))
+
+(defmacro dolist (bind & body)
+  "(dolist (var list) body ...)"
+  (let ((var (car bind))
+        (list (cadr bind))
+        (rest-sym (gensym)))
+    `(funcall
+      (lambda (,rest-sym)
+        (let ((,var (car ,rest-sym)))
+          (progn ,@body)
+          (if (cdr ,rest-sym)
+              (recurse (cdr ,rest-sym)))))
+      ,list)))
diff --git a/lib/lisp/std/oop.lisp b/lib/lisp/std/oop.lisp
new file mode 100644
index 0000000..fd9bc1b
--- /dev/null
+++ b/lib/lisp/std/oop.lisp
@@ -0,0 +1,11 @@
+;;; oop.lisp
+
+;; TODO
+(defmacro defclass (name members)
+  "(defclass person (name age (occupation nil)))"
+  (let ((makefn-name (string->symbol (concat "make-" (symbol->string name))))
+        (class-sym (gensym))
+        (nmemb (length members)))
+    `(defun ,makefn-name ,members
+       (let ((,class-sym (make-class ,name ,nmemb)))
+         ,class-sym))))
diff --git a/lib/lisp/std/std.lisp b/lib/lisp/std/std.lisp
index a199516..dfd70a2 100644
--- a/lib/lisp/std/std.lisp
+++ b/lib/lisp/std/std.lisp
@@ -71,3 +71,4 @@
   (funcall let- bindings body))
 
 (load "list-functions.lisp")
+(load "oop.lisp")
diff --git a/src/lisp/Jmk2 b/src/lisp/Jmk2
index a54cd87..242708f 100644
--- a/src/lisp/Jmk2
+++ b/src/lisp/Jmk2
@@ -40,6 +40,6 @@
 }
 
 srcs main.c lisp.c compiler.c lib/std.c plat/linux.c istream.c gc.c \
-	call_list.s error.c
+	call_list.s error.c lib/classes.c
 
 type executable
diff --git a/src/lisp/bugs b/src/lisp/bugs
new file mode 100644
index 0000000..100594f
--- /dev/null
+++ b/src/lisp/bugs
@@ -0,0 +1 @@
+(funcall #'dolist '(a (list 1 2 3 4)) '(print a)) -> segfault
\ No newline at end of file
diff --git a/src/lisp/compiler.dasc b/src/lisp/compiler.dasc
index 0a01079..331193d 100644
--- a/src/lisp/compiler.dasc
+++ b/src/lisp/compiler.dasc
@@ -545,7 +545,7 @@
 	struct error err;
 
 	if (!IS_OKAY((err = compile_function(function, NS_ANONYMOUS, env, &local, NULL,
-										 &args, NULL, "/", &d))))
+										 &args, "", "/", &d))))
 	{
 		ereport(err);
 		return nil;
diff --git a/src/lisp/gc.c b/src/lisp/gc.c
index 826cd02..e3fc231 100644
--- a/src/lisp/gc.c
+++ b/src/lisp/gc.c
@@ -95,6 +95,16 @@
 
 				break;
 			}
+			case CLASS_TAG: {
+				struct class_alloc *class = (void *)alloc;
+
+				for (int i = 0; i < class->class.num_members; i++)
+				{
+					_mark(class->class.members[i], marked);
+				}
+
+				break;
+			}
 			}
 		}
 	}
diff --git a/src/lisp/lib/classes.c b/src/lisp/lib/classes.c
new file mode 100644
index 0000000..11ed7f6
--- /dev/null
+++ b/src/lisp/lib/classes.c
@@ -0,0 +1,75 @@
+#include "classes.h"
+#include "../plat/plat.h"
+#include "std.h"
+
+value_t *class_member_ref(value_t class, int index)
+{
+	if (!classp(class))
+		return NULL;
+
+	struct class *c = (struct class *)(class ^ CLASS_TAG);
+
+	if (index >= c->num_members)
+		return NULL;
+
+	return &c->members[index];
+}
+
+value_t l_class_member(value_t class, value_t index)
+{
+	if (!integerp(index))
+		return nil;
+
+	value_t *member = class_member_ref(class, valint(index));
+
+	if (member)
+		return *member;
+	else
+		return nil;
+}
+
+value_t l_set_class_member(value_t class, value_t index, value_t value)
+{
+	if (!integerp(index))
+		return nil;
+
+	value_t *member = class_member_ref(class, valint(index));
+
+	if (member)
+		*member = value;
+
+	return nil;
+}
+
+// type = symbol representing this instances type
+// members = list of members
+value_t l_make_class(value_t type, value_t members)
+{
+	if (!integerp(members) || !symbolp(type))
+		return nil;
+
+	int nmemb = valint(members);
+	struct class_alloc *item = malloc_aligned(sizeof(struct class_alloc) +
+											  sizeof(value_t) * nmemb);
+	struct class *c = &item->class;
+
+	c->type = type;
+	c->num_members = nmemb;
+	c->cdata = NULL;
+
+	for (int i = 0; i < nmemb; i++)
+	{
+		c->members[i] = nil;
+	}
+
+	add_this_alloc(&item->alloc, CLASS_TAG);
+
+	return (value_t)c | CLASS_TAG;
+}
+
+void load_classes(struct environment *env)
+{
+	add_c_function(env, "set-class-member", l_set_class_member, 3);
+	add_c_function(env, "class-member", l_class_member, 2);
+	add_c_function(env, "make-class", l_make_class, 2);
+}
diff --git a/src/lisp/lib/classes.h b/src/lisp/lib/classes.h
new file mode 100644
index 0000000..7ebdc02
--- /dev/null
+++ b/src/lisp/lib/classes.h
@@ -0,0 +1,6 @@
+#pragma once
+
+#include "../lisp.h"
+#include "../compiler.h"
+
+void load_classes(struct environment *env);
diff --git a/src/lisp/lib/std.c b/src/lisp/lib/std.c
index a0b7c78..0acafe8 100644
--- a/src/lisp/lib/std.c
+++ b/src/lisp/lib/std.c
@@ -1,4 +1,5 @@
 #include "std.h"
+#include "classes.h"
 #include "../gc.h"
 #include "../compiler.h"
 #include "../plat/plat.h"
@@ -217,6 +218,64 @@
 	return list;
 }
 
+value_t l_string_to_symbol(value_t string)
+{
+	if (!stringp(string))
+		return nil;
+
+	return symval((char *)(string ^ STRING_TAG));
+}
+
+value_t l_symbol_to_string(value_t string)
+{
+	if (!symbolp(string))
+		return nil;
+
+	return strval((char *)(string ^ SYMBOL_TAG));
+}
+
+value_t l_string_length(value_t string)
+{
+	if (!stringp(string))
+		return intval(0);
+
+	return intval(strlen((char *)(string ^ STRING_TAG)));
+}
+
+value_t l_concat(value_t strings)
+{
+	struct alloc *string_alloc = malloc_aligned(sizeof(struct alloc));
+	int lengths = 0;
+
+	for (value_t str = strings; !nilp(str); str = cdr(str))
+	{
+		if (!stringp(car(str)))
+			continue;
+
+		int len = strlen((char *)(car(str) ^ STRING_TAG));
+		string_alloc = realloc_aligned(string_alloc,
+									   sizeof(struct alloc) + lengths + len);
+
+		memcpy((void *)string_alloc + sizeof(struct alloc) + lengths,
+			   (char *)(car(str) ^ STRING_TAG),
+			   len);
+
+		lengths += len;
+	}
+
+	add_this_alloc(string_alloc, STRING_TAG);
+
+	return (value_t)(string_alloc + 1) | STRING_TAG;
+}
+
+value_t l_gensym()
+{
+	static int symcnt = 0;
+	char buffer[32] = {0};
+	snprintf(buffer, 32, "-sym-%d", symcnt++);
+	return symval(buffer);
+}
+
 #define LISP_PREDICATE(name) value_t l_##name(value_t v) { return name(v) ? t : nil; }
 
 LISP_PREDICATE(listp)
@@ -224,6 +283,7 @@
 LISP_PREDICATE(symbolp)
 LISP_PREDICATE(closurep)
 LISP_PREDICATE(consp)
+LISP_PREDICATE(classp)
 
 #undef LISP_PREDICATE
 
@@ -259,9 +319,16 @@
 	add_c_function(env, "elt", l_elt, 2);
 
 	add_c_function(env, "gc-stats", l_gc_stats, 0);
-
 	add_c_function(env, "env-functions", l_list_functions, 1);
 
+	add_c_varargs(env, "concat", l_concat, 0);
+	add_c_function(env, "string-length", l_string_length, 1);
+	add_c_function(env, "string->symbol", l_string_to_symbol, 1);
+	add_c_function(env, "symbol->string", l_symbol_to_string, 1);
+	add_c_function(env, "gensym", l_gensym, 0);
+
+	load_classes(env);
+
 	if (!load_library(env, "std"))
 	{
 		fprintf(stderr, "Not found std\n");
diff --git a/src/lisp/lisp.c b/src/lisp/lisp.c
index 8002319..a305430 100644
--- a/src/lisp/lisp.c
+++ b/src/lisp/lisp.c
@@ -16,6 +16,14 @@
 
 unsigned char max_pool = 0, current_pool = 0;
 
+int valint(value_t i)
+{
+	if (!integerp(i))
+		return 0;
+
+	return i >> 2;
+}
+
 value_t intval(int i)
 {
 	i <<= 2;
@@ -243,7 +251,7 @@
 	}
 	else if (nilp(v))
 	{
-		printf("nil\n");
+		printf("nil");
 	}
 	else if (closurep(v))
 	{
@@ -251,6 +259,19 @@
 		printf("<closure %p (%d) %d>",
 		       c->function, c->args->num_required, c->num_captured);
 	}
+	else if (classp(v))
+	{
+		struct class *c = (void *)(v ^ CLASS_TAG);
+		printf("<class %s", (char *)(c->type ^ SYMBOL_TAG));
+
+		for (int i = 0; i < c->num_members; i++)
+		{
+			printf(" ");
+			printval_ol(c->members[i]);
+		}
+
+		printf(">");
+	}
 	else
 	{
 		printf("<unknown %d>", v);
@@ -481,6 +502,11 @@
 	return (v & HEAP_MASK) == CONS_TAG;
 }
 
+bool classp(value_t v)
+{
+	return (v & HEAP_MASK) == CLASS_TAG;
+}
+
 bool heapp(value_t v)
 {
 	return consp(v) || stringp(v) || symbolp(v) || closurep(v);
diff --git a/src/lisp/lisp.h b/src/lisp/lisp.h
index c5cb398..6035dfd 100644
--- a/src/lisp/lisp.h
+++ b/src/lisp/lisp.h
@@ -168,6 +168,7 @@
 void add_to_pool(value_t form);
 
 void del_alloc(struct alloc *alloc);
+void add_this_alloc(struct alloc *a, int tag);
 
 /**
  * @returns true if pool is still alive (in scope).
@@ -186,6 +187,8 @@
  */
 struct error readquote(struct istream *is, value_t *val) WARN_UNUSED;
 
+int valint(value_t i);
+
 value_t intval(int i);
 value_t strval(char *str);
 value_t symval(char *str);
@@ -216,6 +219,7 @@
 bool symbolp(value_t v);
 bool stringp(value_t v);
 bool consp(value_t v);
+bool classp(value_t v);
 bool listp(value_t v);
 bool nilp(value_t v);
 bool heapp(value_t v);
diff --git a/src/lisp/main.c b/src/lisp/main.c
index 6205bd1..f6c999c 100644
--- a/src/lisp/main.c
+++ b/src/lisp/main.c
@@ -36,8 +36,6 @@
 		}
 	}
 
-	
-
 	struct function *lisp_main_f = find_function(env, "main");
 
 	if (lisp_main_f)
diff --git a/src/lisp/test-oop.lisp b/src/lisp/test-oop.lisp
new file mode 100644
index 0000000..40c7b9b
--- /dev/null
+++ b/src/lisp/test-oop.lisp
@@ -0,0 +1,4 @@
+(defclass person (name age))
+
+(defun main ()
+  (print (make-person "john" 30)))