1 # .tlv file generated by https://github.com/akkartik/teliva
2 # You may edit it if you are careful; however, you may see cryptic errors if you
3 # violate Teliva's assumptions.
5 # .tlv files are representations of Teliva programs. Teliva programs consist of
6 # sequences of definitions. Each definition is a table of key/value pairs. Keys
7 # and values are both strings.
9 # Lines in .tlv files always follow exactly one of the following forms:
10 # - comment lines at the top of the file starting with '#' at column 0
11 # - beginnings of definitions starting with '- ' at column 0, followed by a
13 # - key/value pairs consisting of ' ' at column 0, containing either a
14 # spaceless value on the same line, or a multi-line value
15 # - multiline values indented by more than 2 spaces, starting with a '>'
17 # If these constraints are violated, Teliva may unceremoniously crash. Please
18 # report bugs at http://akkartik.name/contact
19 - __teliva_timestamp: original
21 >-- some string helpers from http://lua-users.org/wiki/StringIndexing
23 >-- index characters using []
24 >getmetatable('').__index = function(str,i)
25 > if type(i) == 'number' then
32 >-- ranges using (), selected bytes using {}
33 >getmetatable('').__call = function(str,i,j)
34 > if type(i)~='table' then
38 > for k,v in ipairs(i) do
41 > return table.concat(t)
45 >-- iterate over an ordered sequence
47 > if type(x) == 'string' then
48 > return x:gmatch('.')
54 >-- insert within string
55 >function string.insert(str1, str2, pos)
56 > return str1:sub(1,pos)..str2..str1:sub(pos+1)
59 >function string.remove(s, pos)
60 > return s:sub(1,pos-1)..s:sub(pos+1)
63 >function string.pos(s, sub)
64 > return string.find(s, sub, 1, true) -- plain=true to disable regular expressions
67 >-- TODO: backport utf-8 support from Lua 5.3
68 - __teliva_timestamp: original
70 >function check(x, msg)
75 > print(' '..str(x)..' is false/nil')
76 > teliva_num_test_failures = teliva_num_test_failures + 1
77 > -- overlay first test failure on editors
78 > if teliva_first_failure == nil then
79 > teliva_first_failure = msg
83 - __teliva_timestamp: original
85 >function check_eq(x, expected, msg)
86 > if eq(x, expected) then
90 > print(' expected '..str(expected)..' but got '..str(x))
91 > teliva_num_test_failures = teliva_num_test_failures + 1
92 > -- overlay first test failure on editors
93 > if teliva_first_failure == nil then
94 > teliva_first_failure = msg
98 - __teliva_timestamp: original
101 > if type(a) ~= type(b) then return false end
102 > if type(a) == 'table' then
103 > if #a ~= #b then return false end
104 > for k, v in pairs(a) do
109 > for k, v in pairs(b) do
118 - __teliva_timestamp: original
121 >-- slow; used only for debugging
123 > if type(x) == 'table' then
125 > result = result..#x..'{'
126 > for k, v in pairs(x) do
127 > result = result..str(k)..'='..str(v)..', '
129 > result = result..'}'
131 > elseif type(x) == 'string' then
136 - __teliva_timestamp: original
138 >-- To show app-specific hotkeys in the menu bar, add hotkey/command
139 >-- arrays of strings to the menu array.
141 - __teliva_timestamp: original
143 >Window = curses.stdscr()
144 - __teliva_timestamp: original
146 >function render(window)
148 > -- draw stuff to screen here
149 > window:attron(curses.A_BOLD)
150 > window:mvaddstr(1, 5, "example app")
151 > window:attrset(curses.A_NORMAL)
153 > window:attrset(curses.color_pair(i))
154 > window:mvaddstr(3+i, 5, "========================")
158 - __teliva_timestamp: original
160 >-- To show app-specific hotkeys in the menu bar, add hotkey/command
161 >-- arrays of strings to the menu array.
163 - __teliva_timestamp: original
165 >function update(window)
166 > local key = window:getch()
167 > -- process key here
169 - __teliva_timestamp: original
171 >function init_colors()
173 > curses.init_pair(i, i, -1)
175 > curses.init_pair(8, 7, 0)
176 > curses.init_pair(9, 7, 1)
177 > curses.init_pair(10, 7, 2)
178 > curses.init_pair(11, 7, 3)
179 > curses.init_pair(12, 7, 4)
180 > curses.init_pair(13, 7, 5)
181 > curses.init_pair(14, 7, 6)
182 > curses.init_pair(15, -1, 15)
184 - __teliva_timestamp: original
194 - __teliva_timestamp: original
196 >function eval(x, env)
197 > function symeq(x, s)
198 > return x and x.sym == s
201 > return lookup(env, x.sym)
202 > elseif atom(x) then
204 > -- otherwise x is a pair
205 > elseif symeq(x.car, 'quote') then
207 > elseif unary_functions[x.car.sym] then
208 > return eval_unary(x, env)
209 > elseif binary_functions[x.car.sym] then
210 > return eval_binary(x, env)
211 > -- special forms that don't always eval all their args
212 > elseif symeq(x.car, 'if') then
213 > return eval_if(x, env)
214 > elseif symeq(x.car.car, 'fn') then
215 > return eval_fn(x, env)
216 > elseif symeq(x.car.car, 'label') then
217 > return eval_label(x, env)
220 - __teliva_timestamp: original
222 >function eval_unary(x, env)
223 > return unary_functions[x.car.sym](eval(x.cdr.car, env))
225 - __teliva_timestamp: original
227 >function eval_binary(x, env)
228 > return binary_functions[x.car.sym](eval(x.cdr.car, env))
230 - __teliva_timestamp: original
232 >-- format: lisp name = lua function that implements it
238 - __teliva_timestamp: original
240 >-- format: lisp name = lua function that implements it
241 >binary_functions = {
245 - __teliva_timestamp: original
247 >function lookup(env, s)
248 > if env[s] then return env[s] end
249 > if env.next then return lookup(env.next, s) end
251 - __teliva_timestamp: original
253 >function eval_if(x, env)
254 > -- syntax: (if check b1 b2)
255 > local check = x.cdr.car
256 > local b1 = x.cdr.cdr.car
257 > local b2 = x.cdr.cdr.cdr.car
258 > if eval(check, env) then
259 > return eval(b1, env)
261 > return eval(b2, env)
264 - __teliva_timestamp: original
266 >function eval_fn(x, env)
267 > -- syntax: ((fn params body*) args*)
268 > local callee = x.car
270 > local params = callee.cdr.car
271 > local body = callee.cdr.cdr
272 > return eval_exprs(body,
273 > bind_env(params, args, env))
275 - __teliva_timestamp: original
277 >function bind_env(params, args, env)
278 > if params == nil then return env end
279 > local result = {next=env}
281 > result[params.car.sym] = eval(args.car, env)
282 > params = params.cdr
284 > if params == nil then break end
288 - __teliva_timestamp: original
290 >function eval_exprs(xs, env)
293 > result = eval(xs.car, env)
298 - __teliva_timestamp: original
300 >function eval_label(x, env)
301 > -- syntax: ((label f (fn params body*)) args*)
302 > local callee = x.car
304 > local f = callee.cdr.car
305 > local fn = callee.cdr.cdr.car
306 > return eval({car=fn, cdr=args},
307 > bind_env({f}, {callee}, env))
309 - __teliva_timestamp: original
312 > return x == nil or x.num or x.char or x.str or x.sym
314 - __teliva_timestamp: original
316 >function car(x) return x.car end
317 - __teliva_timestamp: original
319 >function cdr(x) return x.cdr end
320 - __teliva_timestamp: original
322 >function cons(x, y) return {car=x, cdr=y} end
323 - __teliva_timestamp: original
325 >John McCarthy's Lisp -- without the metacircularity
326 >If you know Lua, this version might be easier to understand.
328 >Words highlighted like [[this]] are suggestions for places to jump to using ctrl-g (see the menu below).
329 >You can always jump back here using ctrl-b (for 'big picture').
331 >Lisp is a programming language that manipulates objects of a few different types.
332 >There are a few _atomic_ types, and one type that can combine them.
333 >The atomic types are what you would expect: numbers, characters, strings, symbols (variables). You can add others.
335 >The way to combine them is the [[cons]] table which has just two keys: a [[car]] and a [[cdr]]. Both can hold objects, either atoms or other cons tables.
337 >We'll now build an interpreter that can run programs constructed out of cons tables.
339 >One thing we'll need for an interpreter is a symbol table (env) that maps symbols to values (objects).
340 >We'll just use a Lua table for this purpose, but with one tweak: a _next_ pointer that allows us to combine tables together.
341 >See [[lookup]] now to get a sense for how we'll use envs.
343 >Lisp programs are just cons tables and atoms nested to arbitrary depths, constructing trees. A Lisp interpreter walks the tree of code,
344 >performing computations. Since cons tables can point to other cons tables, the tree-walker interpreter [[eval]] is recursive.
345 >As the interpreter gets complex, we'll extract parts of it into their own helper functions: [[eval_unary]], [[eval_binary]], [[eval_if]], and so on.
346 >The helper functions contain recursive calls to [[eval]], so that [[eval]] becomes indirectly recursive, and [[eval]] together with its helpers
347 >is mutually recursive. I sometimes find it helpful to think of them all as just one big function.
349 >All these mutually recursive functions take the same arguments: a current expression 'x' and the symbol table 'env'.
350 >But really, most of the interpreter is just walking the tree of expressions. Only two functions care about the internals of 'env':
351 > - [[lookup]] which reads within env as we saw before
352 > - [[bind_env]] which creates a new _scope_ of symbols for each new function call.
353 >More complex Lisps add even more arguments to every. single. helper. Each arg will still only really matter to a couple of functions.
354 >But we still have to pass them around all over the place.
356 >Hopefully this quick overview will help you get a sense for this codebase.
358 >Here's a reference list of eval helpers: [[eval_unary]], [[eval_binary]], [[eval_if]], [[eval_fn]], [[eval_exprs]], [[eval_label]]
359 >More complex Lisps with more features will likely add helpers for lumpy bits of the language.
360 >Here's a list of primitives implemented in Lua: [[atom]], [[car]], [[cdr]], [[cons]], [[iso]] (for 'isomorphic'; comparing trees all the way down to the leaves)
361 >Here's a list of _constructors_ for creating objects of different types: [[num]], [[char]], [[str]], [[sym]] (and of course [[cons]])
362 >I should probably add more primitives for operating on numbers, characters and strings..
363 - __teliva_timestamp: original
366 > if x == nil then return y == nil end
368 > -- watch out for the rare cyclical expression
369 > if done[x] then return done[x] == y end
372 > if not atom(y) then return nil end
373 > for k, v in pairs(x) do
374 > if y[k] ~= v then return nil end
378 > for k, v in pairs(x) do
379 > if not iso(y[k], v) then return nil end
381 > for k, v in pairs(y) do
382 > if not iso(x[k], v) then return nil end