commit 88910e0

jack  ·  2026-06-23 22:45:46 +0000 UTC
parent 88910e0
First commit, includes a crappy C interpreter plus a much more pretty lua one
4 files changed,  +564, -0
+0, -0
+358, -0
  1@@ -0,0 +1,358 @@
  2+#include <stdio.h>
  3+#include <stdlib.h>
  4+#include <string.h>
  5+
  6+#define DBGS(s) printf("DBG LINE %d: %s\n", __LINE__, s)
  7+#define DBGF(n) printf("DBG LINE %d: %f\n", __LINE__, n)
  8+#define DBGN(n) printf("DBG LINE %d: %d\n", __LINE__, n)
  9+
 10+struct Cons;
 11+
 12+typedef struct Expr {
 13+  enum {T_NMBR, T_PRIM, T_CONS, T_SYMB, T_NIL} tag;
 14+  union {
 15+	double number;
 16+	char* symbol;
 17+	struct Cons* cons;
 18+	//rest of fn( prog, stack, binds )
 19+	struct Expr (*fn)(struct Expr, struct Expr, struct Expr*);
 20+  } value;
 21+} Expr;
 22+
 23+typedef struct Cons {
 24+  Expr car;
 25+  Expr cdr;
 26+} Cons;
 27+
 28+Cons* cons(Expr car, Expr cdr) {
 29+  Cons* c = malloc(sizeof(Cons));
 30+  c->car = car;
 31+  c->cdr = cdr;
 32+  return c;
 33+}
 34+
 35+#define DELIMS " \n\t"
 36+
 37+char* getnext() {
 38+  char* next = strtok(NULL, DELIMS);
 39+  if (next == NULL) {
 40+	printf("Unexpected EOF");
 41+  }
 42+  return next;
 43+}
 44+
 45+int isnum(char* tok) {
 46+  return (strspn(tok, "0123456789") == strlen(tok));
 47+}
 48+
 49+void makesymb(Expr* e, char* tok) {
 50+  e->tag = T_SYMB;
 51+  int toklen = strlen(tok);
 52+  e->value.symbol = malloc((toklen + 1) * sizeof(char));
 53+  strcpy(e->value.symbol, tok);
 54+}
 55+
 56+void makenum(Expr* e, double f) {
 57+  e->tag = T_NMBR;
 58+  e->value.number = f;
 59+}
 60+
 61+void makecons(Expr* e, Expr car, Expr cdr) {
 62+  e->value.cons = cons(car, cdr);
 63+  e->tag = T_CONS;
 64+}
 65+
 66+void parseatom(Expr* e, char* tok) {
 67+  if (isnum(tok)) {
 68+	return makenum(e, atof(tok));
 69+  }
 70+  return makesymb(e, tok);
 71+}
 72+
 73+void parselist(Expr*);
 74+
 75+void parsetok(Expr* e, char* tok) {
 76+  if (strcmp(tok, ")") == 0) {
 77+	exit(1);
 78+  }
 79+  if (strcmp(tok, "(") == 0) {
 80+	return parselist(e);
 81+  }
 82+  return parseatom(e, tok);
 83+}
 84+
 85+void parselist(Expr* e) {
 86+  char* tok;
 87+  tok = getnext();
 88+  if (strcmp(tok, ")") == 0) {
 89+	e->tag = T_NIL;
 90+	return;
 91+  }
 92+  if (strcmp(tok, ".") == 0) {
 93+	tok = getnext();
 94+	parsetok(e, tok);
 95+	tok = getnext();
 96+	if (strcmp(tok, ")") != 0) {
 97+	  exit(1);
 98+	}
 99+	return;
100+  }
101+  Expr head;
102+  parsetok(&head, tok);
103+  Expr tail;
104+  parselist(&tail);
105+  makecons(e, head, tail);
106+}
107+
108+struct Expr parse(char* progstr) {
109+  int proglen = strlen(progstr);
110+  char* prog = malloc(sizeof(char) * (proglen + 1));
111+  strcpy(prog, progstr);
112+  char* tok;
113+  tok = strtok(prog, DELIMS);
114+  Expr r;
115+  parsetok(&r, tok);
116+  free(prog);
117+  return r;
118+}
119+
120+void printexp(Expr);
121+
122+void printlist(Expr e) {
123+  Expr car = e.value.cons->car;
124+  Expr cdr = e.value.cons->cdr;
125+  printexp(car);
126+  printf(" ");
127+  if (cdr.tag == T_CONS) {
128+	printlist(cdr);
129+	return;
130+  }
131+  if (cdr.tag == T_NIL) {
132+	printf(")");
133+	return;
134+  }
135+  printf(". ");
136+  printexp(cdr);
137+  printf(")");
138+}
139+
140+void printexp(Expr e) {
141+  switch (e.tag) {
142+  case T_NMBR:
143+	printf("%f", e.value.number);
144+	break;
145+  case T_SYMB:
146+	printf("%s", e.value.symbol);
147+	break;
148+  case T_NIL:
149+	printf("()");
150+	break;
151+  case T_CONS:
152+	printf("( ");
153+	printlist(e);
154+	break;
155+  case T_PRIM:
156+	printf("PRIMITIVE");
157+	break;
158+  }
159+}
160+
161+void freeexpr(Expr* tofree) {
162+  if (tofree->tag == T_CONS) {
163+	freeexpr(&(tofree->value.cons->car));
164+	Expr cdr = tofree->value.cons->cdr;
165+	free(tofree->value.cons);
166+	return freeexpr(&cdr);
167+  }
168+  if (tofree->tag == T_SYMB) {
169+	free(tofree->value.symbol);
170+  }
171+}
172+
173+void dup(Expr* into, Expr* e) {
174+  switch (e->tag) {
175+  case T_SYMB:
176+	return makesymb(into, e->value.symbol);
177+	break;
178+  case T_CONS:
179+	Expr car;
180+	Expr cdr;
181+	makecons(into, car, cdr);
182+	dup(&(into->value.cons->car), &(e->value.cons->car));
183+	// We make the recursive call on the cdr for lists
184+	return dup(&(into->value.cons->cdr), &(e->value.cons->cdr));
185+	break;
186+  case T_NIL:
187+	into->tag = T_NIL;
188+	return;
189+	break;
190+  }
191+  into->tag = e->tag;
192+  into->value = e->value;
193+}
194+
195+// Checks if two expressions are equal, Non recursive
196+int expsequal(Expr* e1, Expr* e2) {
197+  if (e1->tag != e2->tag) {return 0;}
198+  switch (e1->tag) {
199+  case T_SYMB:
200+	return strcmp(e1->value.symbol, e2->value.symbol) == 0;
201+  case T_NMBR:
202+	return e1->value.number == e2->value.number;
203+  case T_CONS:
204+	return e1->value.cons == e2->value.cons;
205+  }
206+  return 0;
207+}
208+
209+// Adds a bind onto the list, returns pointer to the new list
210+void makebind(Expr* newbinds, char* sym, Expr value, Expr binds) {
211+  Expr symb;
212+  Expr bind;
213+  makesymb(&symb, sym);
214+  makecons(&bind, symb, value);
215+  makecons(newbinds, bind, binds);
216+}
217+
218+Expr* findbind(char* sym, Expr* binds) {
219+  if (binds == NULL) {
220+	return NULL;
221+  }
222+  if (strcmp(binds->value.cons->car.value.cons->car.value.symbol, sym) == 0) {
223+	return &(binds->value.cons->car.value.cons->cdr);
224+  }
225+  return findbind(sym, &(binds->value.cons->cdr));
226+}
227+
228+void evalatom(Expr* val, Expr* binds) {
229+  if (val->tag == T_SYMB) {
230+	dup(val, findbind(val->value.symbol, binds));
231+	return;
232+  }
233+}
234+
235+void uncons(Expr* car, Expr* cdr, Expr pair) {
236+  *car = pair.value.cons->car;
237+  *cdr = pair.value.cons->cdr;
238+  free(pair.value.cons);
239+}
240+
241+Expr evallist(Expr val, Expr stack, Expr* binds) {
242+  if (val.tag == T_NIL) {return stack;}
243+  Expr head;
244+  Expr tail;
245+  uncons(&head, &tail, val);
246+  DBGS("CURRENT EVAL:");
247+  printexp(head);
248+  printf("\n");
249+  evalatom(&head, binds);
250+  if (head.tag == T_PRIM) {
251+	return head.value.fn(tail, stack, binds);
252+  }
253+  Expr newstack;
254+  makecons(&newstack, head, stack);
255+  return evallist(tail, newstack, binds);
256+}
257+
258+void popstack(Expr* top, Expr* stack) {
259+  *top = stack->value.cons->car;
260+  Expr cdr = stack->value.cons->cdr;
261+  free(stack->value.cons);
262+  *stack = cdr;
263+}
264+
265+void pushstack(Expr val, Expr* stack) {
266+  Expr newstack;
267+  makecons(&newstack, val, *stack);
268+  *stack = newstack;
269+}
270+
271+// Builtins
272+
273+Expr add2(Expr prog, Expr stack, Expr* binds) {
274+  Expr arg1;
275+  Expr arg2;
276+  popstack(&arg1, &stack);
277+  popstack(&arg2, &stack);
278+  Expr res;
279+  makenum(&res, arg1.value.number + arg2.value.number);
280+  pushstack(res, &stack);
281+  return evallist(prog, stack, binds);
282+}
283+
284+Expr execmacro(Expr prog, Expr stack, Expr* binds) {
285+  Expr macro;
286+  popstack(&macro, &stack);
287+  pushstack(prog, &stack);
288+  return evallist(macro, stack, binds);
289+}
290+
291+Expr unconsbuiltin(Expr prog, Expr stack, Expr* binds) {
292+  Expr arg1;
293+  popstack(&arg1, &stack);
294+  Expr car;
295+  Expr cdr;
296+  uncons(&car, &cdr, arg1);
297+  pushstack(cdr, &stack);
298+  pushstack(car, &stack);
299+  return evallist(prog, stack, binds);
300+}
301+
302+Expr bindbuiltin(Expr prog, Expr stack, Expr* binds) {
303+  Expr tobind;
304+  Expr sym;
305+  Expr restprog;
306+  popstack(&tobind, &stack);
307+  uncons(&sym, &restprog, prog);
308+  makebind(binds, sym.value.symbol, tobind, *binds);
309+  return evallist(restprog, stack, binds);
310+}
311+
312+Expr evalbuiltin(Expr prog, Expr stack, Expr* binds) {
313+  Expr top;
314+  popstack(&top, &stack);
315+  Expr newbinds;
316+  dup(&newbinds, binds);
317+  Expr nstack = evallist(top, stack, &newbinds);
318+  freeexpr(&newbinds);
319+  return evallist(prog, nstack, binds);
320+}
321+
322+// End Builtins
323+
324+Expr createpriminst(Expr (*prim)(Expr, Expr, Expr*)) {
325+  Expr p;
326+  p.tag = T_PRIM;
327+  p.value.fn = prim;
328+  return p;
329+}
330+
331+void makebase(Expr* binds) {
332+  makebind(binds, "+", createpriminst(add2), *binds);
333+  makebind(binds, ",", createpriminst(evalbuiltin), *binds);
334+  makebind(binds, "$", createpriminst(bindbuiltin), *binds);
335+  makebind(binds, "uncons", createpriminst(unconsbuiltin), *binds);
336+  makebind(binds, "execmacro", createpriminst(execmacro), *binds);
337+}
338+
339+Expr evalprog(char* progstr) {
340+  Expr p = parse(progstr);
341+  DBGS("Finished Parsing");
342+  Expr binds;
343+  binds.tag = T_NIL;
344+  makebase(&binds);
345+  DBGS("Finished Making Binds");
346+  printexp(binds);
347+  printf("\n");
348+  DBGS("Now Evaluating Prog");
349+  Expr s;
350+  s.tag = T_NIL;
351+  Expr e = evallist(p, s, &binds);
352+  return e;
353+}
354+
355+int main() {
356+  Expr s = evalprog("( ( $ x $ y y x ) $ swap ( uncons , ) execmacro x )");
357+  printf("Result: ");
358+  printexp(s);
359+}
+199, -0
  1@@ -0,0 +1,199 @@
  2+local EXPTYPES = {}
  3+EXPTYPES.SYMB = 1
  4+EXPTYPES.CONS = 2
  5+EXPTYPES.PRIM = 3
  6+EXPTYPES.NMBR = 4
  7+EXPTYPES.RUNE = 5
  8+EXPTYPES.NIL  = 6
  9+
 10+local NILOBJ = {type=EXPTYPES.NIL}
 11+
 12+function cons(x, y)
 13+	return {car=x,cdr=y}
 14+end
 15+
 16+function makesymbol(s)
 17+	return {value=s, type=EXPTYPES.SYMB}
 18+end
 19+
 20+function makenumber(n)
 21+	return {value=n, type=EXPTYPES.NMBR}
 22+end
 23+
 24+function makecons(car, cdr)
 25+	return {value=cons(car,cdr), type=EXPTYPES.CONS}
 26+end
 27+
 28+function makeprim(fn)
 29+	return {value=fn, type=EXPTYPES.PRIM}
 30+end
 31+
 32+function getnexttok(p)
 33+	if p ~= nil then
 34+		GETNEXTBUFF = string.gmatch(p, "%S+")
 35+	end
 36+	return GETNEXTBUFF()
 37+end
 38+
 39+function parsetok(tok)
 40+	if tok == "(" then
 41+		return parselist()
 42+	end
 43+	if tonumber(tok) ~= nil then
 44+		return makenumber(tonumber(tok))
 45+	end
 46+	return makesymbol(tok)
 47+end
 48+
 49+function parselist()
 50+	local tok = getnexttok()
 51+	if tok == nil then 
 52+		error("Unmatched Parens") 
 53+	end
 54+	if tok == ")" then
 55+		return NILOBJ
 56+	end
 57+	car = parsetok(tok)
 58+	return makecons(car, parselist())
 59+end
 60+
 61+function parse(prog)
 62+	local tok = getnexttok(prog)
 63+	return parsetok(tok)
 64+end
 65+
 66+function fmtlist(e)
 67+	if e.type == EXPTYPES.NIL then
 68+		return ")"
 69+	end
 70+	if e.type ~= EXPTYPES.CONS then
 71+		return ". " .. fmtexp(e) .. " )"
 72+	end
 73+	return fmtexp(e.value.car) .. " " .. fmtlist(e.value.cdr)
 74+end
 75+
 76+function fmtexp(e)
 77+	local t = e.type
 78+	if t == EXPTYPES.CONS then
 79+		return "( " .. fmtlist(e)
 80+	end
 81+	return tostring(e.value)
 82+end
 83+
 84+local function printtable(t)
 85+	if type(t) == "table" then
 86+		print("{")
 87+		for k, v in pairs(t) do
 88+			print(tostring(k) .. ":")
 89+			printtable(v)
 90+		end
 91+		print("}")
 92+	elseif type(t) == "nil" then print(nil)
 93+	else
 94+		print(t)
 95+	end
 96+end
 97+
 98+-- Binds are in the form ((name, value) (name value) (name value) ...)
 99+
100+local function addbind(sym, value, oldbinds)
101+	return makecons(makecons(sym, value), oldbinds)
102+end
103+
104+local function findbind(sym, binds)
105+	if binds.type == EXPTYPES.NIL then error("bind doesn't exist for " .. sym.value) end
106+	local curbind = binds.value.car
107+	if curbind.value.car.value == sym.value then
108+		return curbind.value.cdr
109+	end
110+	return findbind(sym, binds.value.cdr)
111+end
112+
113+local function evallist(prog, stack, binds)
114+	if prog.type == EXPTYPES.NIL then return stack end
115+	local top = prog.value.car
116+	local prog = prog.value.cdr
117+	if top.type == EXPTYPES.SYMB then
118+		top = findbind(top, binds)
119+	end
120+	if top.type == EXPTYPES.PRIM then
121+		return top.value(prog, stack, binds)
122+	end
123+	return evallist(prog, makecons(top, stack), binds)
124+end
125+
126+
127+local function uncons(c)
128+	return c.value.car, c.value.cdr
129+end
130+
131+-- Start Builtins
132+
133+local function add2(prog, stack, binds)
134+	local arg1, stack = uncons(stack)
135+	local arg2, stack = uncons(stack)
136+	local res = makenumber(arg1.value + arg2.value)
137+	return evallist(prog, makecons(res, stack), binds)
138+end
139+
140+local function evalbuiltin(prog, stack, binds)
141+	local toeval, stack = uncons(stack)
142+	stack = evallist(toeval, stack, binds)
143+	return evallist(prog, stack, binds)
144+end
145+
146+local function bindbuiltin(prog, stack, binds)
147+	local sym, prog = uncons(prog)
148+	local val, stack = uncons(stack)
149+	return evallist(prog, stack, addbind(sym, val, binds))
150+end
151+
152+local function execmacro(prog, stack, binds)
153+	local mac, stack = uncons(stack)
154+	stack = makecons(prog, stack)
155+	return evallist(mac, stack, binds)
156+end
157+
158+local function unconsbuiltin(prog, stack, binds)
159+	local top, stack = uncons(stack)
160+	stack = makecons(top.value.cdr, stack)
161+	stack = makecons(top.value.car, stack)
162+	return evallist(prog, stack, binds)
163+end
164+
165+local function consbuiltin(prog, stack, binds)
166+	local car, stack = uncons(stack)
167+	local cdr, stack = uncons(stack)
168+	return evallist(prog, makecons(makecons(car, cdr), stack), binds)
169+end
170+
171+-- End Builtins
172+-- Register Builtins
173+
174+local function getbase()
175+	local base = NILOBJ
176+	local function addprim(s, f)
177+		base = addbind(makesymbol(s), makeprim(f), base)
178+	end
179+	addprim("+", add2)
180+	addprim(",", evalbuiltin)
181+	addprim("$", bindbuiltin)
182+	addprim("uncons", unconsbuiltin)
183+	addprim("cons", consbuiltin)
184+	addprim("!", execmacro)
185+	return base
186+end
187+
188+-- End Register Builtins
189+
190+local function readfile(file) -- from stackoverflow
191+	local f = assert(io.open(file, "rb"))
192+	local content = f:read("*all")
193+	f:close()
194+	return content
195+end
196+
197+local testterm = readfile(arg[1])
198+local parsed = parse(testterm)
199+local evalled = evallist(parsed, NILOBJ, getbase())
200+print(fmtexp(evalled))
+7, -0
1@@ -0,0 +1,7 @@
2+(
3+
4+( $ x $ y x y ) $ swap
5+
6+( uncons swap , , ) ! quoted_symbol 1 2 3
7+
8+)