main potoo.lua
  1local EXPTYPES = {}
  2EXPTYPES.SYMB = 1
  3EXPTYPES.CONS = 2
  4EXPTYPES.PRIM = 3
  5EXPTYPES.NMBR = 4
  6EXPTYPES.RUNE = 5
  7EXPTYPES.NIL  = 6
  8
  9local NILOBJ = {type=EXPTYPES.NIL}
 10
 11function cons(x, y)
 12	return {car=x,cdr=y}
 13end
 14
 15function makesymbol(s)
 16	return {value=s, type=EXPTYPES.SYMB}
 17end
 18
 19function makenumber(n)
 20	return {value=n, type=EXPTYPES.NMBR}
 21end
 22
 23function makecons(car, cdr)
 24	return {value=cons(car,cdr), type=EXPTYPES.CONS}
 25end
 26
 27function makeprim(fn)
 28	return {value=fn, type=EXPTYPES.PRIM}
 29end
 30
 31function getnexttok(p)
 32	if p ~= nil then
 33		GETNEXTBUFF = string.gmatch(p, "%S+")
 34	end
 35	return GETNEXTBUFF()
 36end
 37
 38function parsetok(tok)
 39	if tok == "(" then
 40		return parselist()
 41	end
 42	if tonumber(tok) ~= nil then
 43		return makenumber(tonumber(tok))
 44	end
 45	return makesymbol(tok)
 46end
 47
 48function parselist()
 49	local tok = getnexttok()
 50	if tok == nil then 
 51		error("Unmatched Parens") 
 52	end
 53	if tok == ")" then
 54		return NILOBJ
 55	end
 56	car = parsetok(tok)
 57	return makecons(car, parselist())
 58end
 59
 60function parse(prog)
 61	local tok = getnexttok(prog)
 62	return parsetok(tok)
 63end
 64
 65function fmtlist(e)
 66	if e.type == EXPTYPES.NIL then
 67		return ")"
 68	end
 69	if e.type ~= EXPTYPES.CONS then
 70		return ". " .. fmtexp(e) .. " )"
 71	end
 72	return fmtexp(e.value.car) .. " " .. fmtlist(e.value.cdr)
 73end
 74
 75function fmtexp(e)
 76	local t = e.type
 77	if t == EXPTYPES.CONS then
 78		return "( " .. fmtlist(e)
 79	end
 80	return tostring(e.value)
 81end
 82
 83local function printtable(t)
 84	if type(t) == "table" then
 85		print("{")
 86		for k, v in pairs(t) do
 87			print(tostring(k) .. ":")
 88			printtable(v)
 89		end
 90		print("}")
 91	elseif type(t) == "nil" then print(nil)
 92	else
 93		print(t)
 94	end
 95end
 96
 97-- Binds are in the form ((name, value) (name value) (name value) ...)
 98
 99local function addbind(sym, value, oldbinds)
100	return makecons(makecons(sym, value), oldbinds)
101end
102
103local function findbind(sym, binds)
104	if binds.type == EXPTYPES.NIL then error("bind doesn't exist for " .. sym.value) end
105	local curbind = binds.value.car
106	if curbind.value.car.value == sym.value then
107		return curbind.value.cdr
108	end
109	return findbind(sym, binds.value.cdr)
110end
111
112local function evallist(prog, stack, binds)
113	if prog.type == EXPTYPES.NIL then return stack end
114	local top = prog.value.car
115	local prog = prog.value.cdr
116	if top.type == EXPTYPES.SYMB then
117		top = findbind(top, binds)
118	end
119	if top.type == EXPTYPES.PRIM then
120		return top.value(prog, stack, binds)
121	end
122	return evallist(prog, makecons(top, stack), binds)
123end
124
125
126local function uncons(c)
127	return c.value.car, c.value.cdr
128end
129
130-- Start Builtins
131
132local function add2(prog, stack, binds)
133	local arg1, stack = uncons(stack)
134	local arg2, stack = uncons(stack)
135	local res = makenumber(arg1.value + arg2.value)
136	return evallist(prog, makecons(res, stack), binds)
137end
138
139local function evalbuiltin(prog, stack, binds)
140	local toeval, stack = uncons(stack)
141	stack = evallist(toeval, stack, binds)
142	return evallist(prog, stack, binds)
143end
144
145local function bindbuiltin(prog, stack, binds)
146	local sym, prog = uncons(prog)
147	local val, stack = uncons(stack)
148	return evallist(prog, stack, addbind(sym, val, binds))
149end
150
151local function execmacro(prog, stack, binds)
152	local mac, stack = uncons(stack)
153	stack = makecons(prog, stack)
154	return evallist(mac, stack, binds)
155end
156
157local function unconsbuiltin(prog, stack, binds)
158	local top, stack = uncons(stack)
159	stack = makecons(top.value.cdr, stack)
160	stack = makecons(top.value.car, stack)
161	return evallist(prog, stack, binds)
162end
163
164local function consbuiltin(prog, stack, binds)
165	local car, stack = uncons(stack)
166	local cdr, stack = uncons(stack)
167	return evallist(prog, makecons(makecons(car, cdr), stack), binds)
168end
169
170-- End Builtins
171-- Register Builtins
172
173local function getbase()
174	local base = NILOBJ
175	local function addprim(s, f)
176		base = addbind(makesymbol(s), makeprim(f), base)
177	end
178	addprim("+", add2)
179	addprim(",", evalbuiltin)
180	addprim("$", bindbuiltin)
181	addprim("uncons", unconsbuiltin)
182	addprim("cons", consbuiltin)
183	addprim("!", execmacro)
184	return base
185end
186
187-- End Register Builtins
188
189local function readfile(file) -- from stackoverflow
190	local f = assert(io.open(file, "rb"))
191	local content = f:read("*all")
192	f:close()
193	return content
194end
195
196local testterm = readfile(arg[1])
197local parsed = parse(testterm)
198local evalled = evallist(parsed, NILOBJ, getbase())
199print(fmtexp(evalled))