2022-01-27 16:30:50 +00:00
# . tlv file generated by https : // github.com / akkartik / teliva
# You may edit it if you are careful ; however , you may see cryptic errors if you
# violate Teliva ' s assumptions.
#
# . tlv files are representations of Teliva programs . Teliva programs consist of
# sequences of definitions . Each definition is a table of key / value pairs . Keys
# and values are both strings .
#
# Lines in . tlv files always follow exactly one of the following forms :
# - comment lines at the top of the file starting with ' # ' at column 0
# - beginnings of definitions starting with ' - ' at column 0 , followed by a
# key / value pair
# - key / value pairs consisting of ' ' at column 0 , containing either a
# spaceless value on the same line , or a multi - line value
# - multiline values indented by more than 2 spaces , starting with a ' > '
#
# If these constraints are violated , Teliva may unceremoniously crash . Please
# report bugs at http : // akkartik.name / contact
- __teliva_timestamp : original
str_helpers :
>-- some string helpers from http : // lua - users.org / wiki / StringIndexing
>
>-- index characters using [ ]
> getmetatable ( ' ' ) . __index = function ( str , i )
> if type ( i ) == ' number ' then
2022-03-07 00:38:49 +00:00
> return str : sub ( i , i )
2022-01-27 16:30:50 +00:00
> else
> return string [ i ]
> end
> end
>
>-- ranges using ( ) , selected bytes using { }
> getmetatable ( ' ' ) . __call = function ( str , i , j )
> if type ( i ) ~= ' table ' then
2022-03-07 00:38:49 +00:00
> return str : sub ( i , j )
2022-01-27 16:30:50 +00:00
> else
> local t = { }
> for k , v in ipairs ( i ) do
2022-03-07 00:38:49 +00:00
> t [ k ] = str : sub ( v , v )
2022-01-27 16:30:50 +00:00
> end
> return table.concat ( t )
> end
> end
>
>-- iterate over an ordered sequence
> function q ( x )
> if type ( x ) == ' string ' then
> return x : gmatch ( ' . ' )
> else
> return ipairs ( x )
> end
> end
>
>-- insert within string
> function string . insert ( str1 , str2 , pos )
> return str1 : sub ( 1 , pos ) .. str2 .. str1 : sub ( pos + 1 )
> end
>
> function string . remove ( s , pos )
> return s : sub ( 1 , pos - 1 ) .. s : sub ( pos + 1 )
> end
>
2022-03-18 17:37:29 +00:00
> function string . pos ( s , sub )
> return string.find ( s , sub , 1 , true ) -- plain=true to disable regular expressions
> end
>
2022-01-27 16:30:50 +00:00
>-- TODO : backport utf - 8 support from Lua 5.3
2022-03-19 07:19:58 +00:00
- __teliva_timestamp : original
check :
> function check ( x , msg )
> if x then
> Window : addch ( ' . ' )
> else
> print ( ' F - ' .. msg )
> print ( ' ' .. str ( x ) .. ' is false/nil ' )
> teliva_num_test_failures = teliva_num_test_failures + 1
> -- overlay first test failure on editors
> if teliva_first_failure == nil then
> teliva_first_failure = msg
> end
> end
> end
- __teliva_timestamp : original
check_eq :
> function check_eq ( x , expected , msg )
> if eq ( x , expected ) then
> Window : addch ( ' . ' )
> else
> print ( ' F - ' .. msg )
> print ( ' expected ' .. str ( expected ) .. ' but got ' .. str ( x ) )
> teliva_num_test_failures = teliva_num_test_failures + 1
> -- overlay first test failure on editors
> if teliva_first_failure == nil then
> teliva_first_failure = msg
> end
> end
> end
- __teliva_timestamp : original
eq :
> function eq ( a , b )
> if type ( a ) ~= type ( b ) then return false end
> if type ( a ) == ' table ' then
> if # a ~= # b then return false end
> for k , v in pairs ( a ) do
> if b [ k ] ~= v then
> return false
> end
> end
> for k , v in pairs ( b ) do
> if a [ k ] ~= v then
> return false
> end
> end
> return true
> end
> return a == b
> end
- __teliva_timestamp : original
str :
>-- smarter tostring
>-- slow ; used only for debugging
> function str ( x )
> if type ( x ) == ' table ' then
> local result = ' '
> result = result .. # x .. ' { '
> for k , v in pairs ( x ) do
> result = result .. str ( k ) .. ' = ' .. str ( v ) .. ' , '
> end
> result = result .. ' } '
> return result
> elseif type ( x ) == ' string ' then
> return ' " ' .. x .. ' " '
> end
> return tostring ( x )
> end
- __teliva_timestamp : original
menu :
>-- To show app - specific hotkeys in the menu bar , add hotkey / command
>-- arrays of strings to the menu array .
> menu = { }
2022-01-27 16:30:50 +00:00
- __teliva_timestamp : original
2022-03-03 06:15:01 +00:00
Window :
> Window = curses.stdscr ( )
2022-01-27 16:30:50 +00:00
- __teliva_timestamp : original
render :
> function render ( window )
> window : clear ( )
> -- draw stuff to screen here
> window : attron ( curses.A_BOLD )
> window : mvaddstr ( 1 , 5 , " example app " )
> window : attrset ( curses.A_NORMAL )
> for i = 0 , 15 do
> window : attrset ( curses.color_pair ( i ) )
> window : mvaddstr ( 3 + i , 5 , " ======================== " )
> end
2022-02-27 16:38:48 +00:00
> window : refresh ( )
2022-01-27 16:30:50 +00:00
> end
- __teliva_timestamp : original
menu :
>-- To show app - specific hotkeys in the menu bar , add hotkey / command
>-- arrays of strings to the menu array .
> menu = { }
- __teliva_timestamp : original
update :
> function update ( window )
2022-02-27 16:38:48 +00:00
> local key = window : getch ( )
2022-01-27 16:30:50 +00:00
> -- process key here
> end
- __teliva_timestamp : original
init_colors :
> function init_colors ( )
> for i = 0 , 7 do
> curses.init_pair ( i , i , - 1 )
> end
> curses.init_pair ( 8 , 7 , 0 )
> curses.init_pair ( 9 , 7 , 1 )
> curses.init_pair ( 10 , 7 , 2 )
> curses.init_pair ( 11 , 7 , 3 )
> curses.init_pair ( 12 , 7 , 4 )
> curses.init_pair ( 13 , 7 , 5 )
> curses.init_pair ( 14 , 7 , 6 )
> curses.init_pair ( 15 , - 1 , 15 )
> end
2022-02-08 00:22:04 +00:00
- __teliva_timestamp : original
main :
2022-01-27 16:30:50 +00:00
> function main ( )
> init_colors ( )
>
> while true do
2022-03-03 06:15:01 +00:00
> render ( Window )
> update ( Window )
2022-01-27 16:30:50 +00:00
> end
> end
2022-02-08 00:22:04 +00:00
- __teliva_timestamp : original
2022-01-27 16:30:50 +00:00
eval :
> function eval ( x , env )
> function symeq ( x , s )
> return x and x.sym == s
> end
> if x.sym then
> return lookup ( env , x.sym )
> elseif atom ( x ) then
> return x
> -- otherwise x is a pair
> elseif symeq ( x.car , ' quote ' ) then
> return x.cdr
> elseif unary_functions [ x.car . sym ] then
> return eval_unary ( x , env )
> elseif binary_functions [ x.car . sym ] then
> return eval_binary ( x , env )
> -- special forms that don't always eval all their args
> elseif symeq ( x.car , ' if ' ) then
> return eval_if ( x , env )
> elseif symeq ( x.car . car , ' fn ' ) then
> return eval_fn ( x , env )
> elseif symeq ( x.car . car , ' label ' ) then
> return eval_label ( x , env )
> end
> end
2022-02-08 00:22:04 +00:00
- __teliva_timestamp : original
2022-01-27 16:30:50 +00:00
eval_unary :
> function eval_unary ( x , env )
> return unary_functions [ x.car . sym ] ( eval ( x.cdr . car , env ) )
> end
2022-02-08 00:22:04 +00:00
- __teliva_timestamp : original
2022-01-27 16:30:50 +00:00
eval_binary :
> function eval_binary ( x , env )
> return binary_functions [ x.car . sym ] ( eval ( x.cdr . car , env ) )
> end
2022-02-08 00:22:04 +00:00
- __teliva_timestamp : original
2022-01-27 16:30:50 +00:00
unary_functions :
>-- format : lisp name = lua function that implements it
> unary_functions = {
> atom = atom ,
> car = car ,
> cdr = cdr ,
> }
2022-02-08 00:22:04 +00:00
- __teliva_timestamp : original
2022-01-27 16:30:50 +00:00
binary_functions :
>-- format : lisp name = lua function that implements it
> binary_functions = {
> cons = cons ,
> iso = iso ,
> }
2022-02-08 00:22:04 +00:00
- __teliva_timestamp : original
2022-01-27 16:30:50 +00:00
lookup :
> function lookup ( env , s )
> if env [ s ] then return env [ s ] end
> if env.next then return lookup ( env.next , s ) end
> end
2022-02-08 00:22:04 +00:00
- __teliva_timestamp : original
2022-01-27 16:30:50 +00:00
eval_if :
> function eval_if ( x , env )
> -- syntax: (if check b1 b2)
> local check = x.cdr . car
> local b1 = x.cdr . cdr.car
> local b2 = x.cdr . cdr.cdr . car
> if eval ( check , env ) then
> return eval ( b1 , env )
> else
> return eval ( b2 , env )
> end
> end
2022-02-08 00:22:04 +00:00
- __teliva_timestamp : original
2022-01-27 16:30:50 +00:00
eval_fn :
> function eval_fn ( x , env )
> -- syntax: ((fn params body*) args*)
> local callee = x.car
> local args = x.cdr
> local params = callee.cdr . car
> local body = callee.cdr . cdr
> return eval_exprs ( body ,
> bind_env ( params , args , env ) )
> end
2022-02-08 00:22:04 +00:00
- __teliva_timestamp : original
2022-01-27 16:30:50 +00:00
bind_env :
> function bind_env ( params , args , env )
> if params == nil then return env end
> local result = { next = env }
> while true do
> result [ params.car . sym ] = eval ( args.car , env )
> params = params.cdr
> args = args.cdr
> if params == nil then break end
> end
> return result
> end
2022-02-08 00:22:04 +00:00
- __teliva_timestamp : original
2022-01-27 16:30:50 +00:00
eval_exprs :
> function eval_exprs ( xs , env )
> local result = nil
> while xs do
> result = eval ( xs.car , env )
> xs = xs.cdr
> end
> return result
> end
2022-02-08 00:22:04 +00:00
- __teliva_timestamp : original
eval_label :
2022-01-27 16:30:50 +00:00
> function eval_label ( x , env )
> -- syntax: ((label f (fn params body*)) args*)
> local callee = x.car
> local args = x.cdr
> local f = callee.cdr . car
> local fn = callee.cdr . cdr.car
> return eval ( { car = fn , cdr = args } ,
> bind_env ( { f } , { callee } , env ) )
> end
2022-02-08 00:22:04 +00:00
- __teliva_timestamp : original
2022-01-27 16:30:50 +00:00
atom :
> function atom ( x )
> return x == nil or x.num or x.char or x.str or x.sym
2022-02-08 00:22:04 +00:00
> end
- __teliva_timestamp : original
car :
2022-01-27 16:30:50 +00:00
> function car ( x ) return x.car end
2022-02-08 00:22:04 +00:00
- __teliva_timestamp : original
cdr :
2022-01-27 16:30:50 +00:00
> function cdr ( x ) return x.cdr end
2022-02-08 00:22:04 +00:00
- __teliva_timestamp : original
2022-01-27 16:30:50 +00:00
cons :
> function cons ( x , y ) return { car = x , cdr = y } end
2022-02-08 00:22:04 +00:00
- __teliva_timestamp : original
doc : main :
2022-01-27 16:30:50 +00:00
> John McCarthy ' s Lisp -- without the metacircularity
> If you know Lua , this version might be easier to understand .
>
> Words highlighted like [[this]] are suggestions for places to jump to using ctrl - g ( see the menu below ) .
> You can always jump back here using ctrl - b ( for ' big picture ' ) .
>
> Lisp is a programming language that manipulates objects of a few different types .
> There are a few _atomic_ types , and one type that can combine them .
> The atomic types are what you would expect : numbers , characters , strings , symbols ( variables ) . You can add others .
>
> 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 .
>
> We ' ll now build an interpreter that can run programs constructed out of cons tables.
>
> One thing we ' ll need for an interpreter is a symbol table (env) that maps symbols to values (objects).
> We ' ll just use a Lua table for this purpose, but with one tweak: a _next_ pointer that allows us to combine tables together.
> See [[lookup]] now to get a sense for how we ' ll use envs.
>
> Lisp programs are just cons tables and atoms nested to arbitrary depths , constructing trees . A Lisp interpreter walks the tree of code ,
> performing computations . Since cons tables can point to other cons tables , the tree - walker interpreter [[eval]] is recursive .
> 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.
> The helper functions contain recursive calls to [[eval]] , so that [[eval]] becomes indirectly recursive , and [[eval]] together with its helpers
> is mutually recursive . I sometimes find it helpful to think of them all as just one big function .
>
> All these mutually recursive functions take the same arguments : a current expression ' x ' and the symbol table ' env ' .
> But really , most of the interpreter is just walking the tree of expressions . Only two functions care about the internals of ' env ' :
> - [[lookup]] which reads within env as we saw before
> - [[bind_env]] which creates a new _scope_ of symbols for each new function call .
> More complex Lisps add even more arguments to every . single . helper . Each arg will still only really matter to a couple of functions .
2022-02-08 00:22:04 +00:00
> But we still have to pass them around all over the place .
>
2022-01-27 16:30:50 +00:00
> Hopefully this quick overview will help you get a sense for this codebase .
>
> Here ' s a reference list of eval helpers: [[eval_unary]], [[eval_binary]], [[eval_if]], [[eval_fn]], [[eval_exprs]], [[eval_label]]
> More complex Lisps with more features will likely add helpers for lumpy bits of the language .
> 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)
> Here ' s a list of _constructors_ for creating objects of different types: [[num]], [[char]], [[str]], [[sym]] (and of course [[cons]])
> I should probably add more primitives for operating on numbers , characters and strings ..
2022-02-08 00:22:04 +00:00
- __teliva_timestamp : original
2022-01-27 16:30:50 +00:00
iso :
> function iso ( x , y )
> if x == nil then return y == nil end
> local done = { }
> -- watch out for the rare cyclical expression
> if done [ x ] then return done [ x ] == y end
> done [ x ] = y
> if atom ( x ) then
> if not atom ( y ) then return nil end
> for k , v in pairs ( x ) do
> if y [ k ] ~= v then return nil end
> end
> return true
> end
> for k , v in pairs ( x ) do
> if not iso ( y [ k ] , v ) then return nil end
> end
> for k , v in pairs ( y ) do
> if not iso ( x [ k ] , v ) then return nil end
> end
> return true
> end