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
71 - __teliva_timestamp: original
73 >-- helper for debug by print; overlay debug information towards the right
74 >-- reset debugy every time you refresh screen
75 >function dbg(window, s)
78 > oldy, oldx = window:getyx()
79 > window:mvaddstr(debugy, 60, s)
81 > window:mvaddstr(oldy, oldx, '')
83 - __teliva_timestamp: original
85 >function check(x, msg)
90 > print(' '..str(x)..' is false/nil')
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
100 >function check_eq(x, expected, msg)
101 > if eq(x, expected) then
105 > print(' expected '..str(expected)..' but got '..str(x))
106 > teliva_num_test_failures = teliva_num_test_failures + 1
107 > -- overlay first test failure on editors
108 > if teliva_first_failure == nil then
109 > teliva_first_failure = msg
113 - __teliva_timestamp: original
116 > if type(a) ~= type(b) then return false end
117 > if type(a) == 'table' then
118 > if #a ~= #b then return false end
119 > for k, v in pairs(a) do
124 > for k, v in pairs(b) do
133 - __teliva_timestamp: original
136 >-- slow; used only for debugging
138 > if type(x) == 'table' then
140 > result = result..#x..'{'
141 > for k, v in pairs(x) do
142 > result = result..str(k)..'='..str(v)..', '
144 > result = result..'}'
146 > elseif type(x) == 'string' then
151 - __teliva_timestamp: original
153 >function find_index(arr, x)
154 > for n, y in ipairs(arr) do
160 - __teliva_timestamp: original
163 > return s:gsub('^%s*', ''):gsub('%s*$', '')
165 - __teliva_timestamp: original
167 >function split(s, d)
169 > for match in (s..d):gmatch("(.-)"..d) do
170 > table.insert(result, match);
174 - __teliva_timestamp: original
179 > for _, x in ipairs(l) do
180 > table.insert(result, f(x))
184 - __teliva_timestamp: original
187 >function reduce(l, f, init)
189 > for _, x in ipairs(l) do
190 > result = f(result, x)
194 - __teliva_timestamp: original
196 >function filter(h, f)
198 > for k, v in pairs(h) do
205 - __teliva_timestamp: original
208 >function ifilter(l, f)
210 > for _, x in ipairs(l) do
212 > table.insert(result, x)
217 - __teliva_timestamp: original
219 >function sort_letters(s)
222 > table.insert(tmp, s[i])
226 > for _, c in pairs(tmp) do
232 >function test_sort_letters(s)
233 > check_eq(sort_letters(''), '', 'test_sort_letters: empty')
234 > check_eq(sort_letters('ba'), 'ab', 'test_sort_letters: non-empty')
235 > check_eq(sort_letters('abba'), 'aabb', 'test_sort_letters: duplicates')
237 - __teliva_timestamp: original
239 >-- TODO: handle unicode
240 >function count_letters(s)
244 > if result[c] == nil then
247 > result[c] = result[c] + 1
252 - __teliva_timestamp: original
254 >-- turn an array of elements into a map from elements to their frequency
255 >-- analogous to count_letters for non-strings
258 > for i, v in ipairs(a) do
259 > if result[v] == nil then
262 > result[v] = result[v] + 1
267 - __teliva_timestamp: original
269 >function union(a, b)
270 > for k, v in pairs(b) do
275 - __teliva_timestamp: original
278 >function subtract(a, b)
279 > for k, v in pairs(b) do
284 - __teliva_timestamp: original
286 >-- universal quantifier on sets
288 > for k, v in pairs(s) do
289 > if not f(k, v) then
295 - __teliva_timestamp: original
297 >-- turn a set into an array
299 >function to_array(h)
301 > for k, _ in pairs(h) do
302 > table.insert(result, k)
306 - __teliva_timestamp: original
308 >-- concatenate list 'elems' into 'l', modifying 'l' in the process
309 >function append(l, elems)
311 > table.insert(l, elems[i])
314 - __teliva_timestamp: original
316 >-- concatenate list 'elems' into the start of 'l', modifying 'l' in the process
317 >function prepend(l, elems)
319 > table.insert(l, i, elems[i])
322 - __teliva_timestamp: original
324 >function all_but(x, idx)
325 > if type(x) == 'table' then
327 > for i, elem in ipairs(x) do
329 > table.insert(result,elem)
333 > elseif type(x) == 'string' then
334 > if idx < 1 then return x:sub(1) end
335 > return x:sub(1, idx-1) .. x:sub(idx+1)
337 > error('all_but: unsupported type '..type(x))
341 >function test_all_but()
342 > check_eq(all_but('', 0), '', 'all_but: empty')
343 > check_eq(all_but('abc', 0), 'abc', 'all_but: invalid low index')
344 > check_eq(all_but('abc', 4), 'abc', 'all_but: invalid high index')
345 > check_eq(all_but('abc', 1), 'bc', 'all_but: first index')
346 > check_eq(all_but('abc', 3), 'ab', 'all_but: final index')
347 > check_eq(all_but('abc', 2), 'ac', 'all_but: middle index')
349 - __teliva_timestamp: original
353 > for i, elem in ipairs(l) do
354 > result[elem] = true
358 - __teliva_timestamp: original
360 >function set_eq(l1, l2)
361 > return eq(set(l1), set(l2))
364 >function test_set_eq()
365 > check(set_eq({1}, {1}), 'set_eq: identical')
366 > check(not set_eq({1, 2}, {1, 3}), 'set_eq: different')
367 > check(set_eq({1, 2}, {2, 1}), 'set_eq: order')
368 > check(set_eq({1, 2, 2}, {2, 1}), 'set_eq: duplicates')
370 - __teliva_timestamp: original
372 >function clear(lines)
373 > while #lines > 0 do
374 > table.remove(lines)
377 - __teliva_timestamp: original
379 >function zap(target, src)
381 > append(target, src)
383 - __teliva_timestamp: original
385 >-- memoized version of factorial
386 >-- doesn't memoize recursive calls, but may be good enough
387 >mfactorial = memo1(factorial)
388 - __teliva_timestamp: original
390 >function factorial(n)
397 - __teliva_timestamp: original
399 >-- a higher-order function that takes a function of a single arg
400 >-- (that never returns nil)
401 >-- and returns a memoized version of it
405 > if memo[x] == nil then
412 >-- mfactorial doesn't seem noticeably faster
413 >function test_memo1()
415 > check_eq(mfactorial(i), factorial(i), 'memo1 over factorial: '..str(i))
418 - __teliva_timestamp: original
420 >-- number of permutations of n distinct objects, taken r at a time
421 >function num_permutations(n, r)
422 > return factorial(n)/factorial(n-r)
425 >-- mfactorial doesn't seem noticeably faster
426 >function test_memo1()
429 > check_eq(num_permutations(i, j), mfactorial(i)/mfactorial(i-j), 'num_permutations memoizes: '..str(i)..'P'..str(j))
433 - __teliva_timestamp: original
435 >-- To show app-specific hotkeys in the menu bar, add hotkey/command
436 >-- arrays of strings to the menu array.
438 - __teliva_timestamp: original
440 >Window = curses.stdscr()
441 - __teliva_timestamp: original
443 >-- constructor for fake screen and window
444 >-- call it like this:
445 >-- local w = window{
447 >-- scr=scr{h=5, w=4},
449 >-- eventually it'll do everything a real ncurses window can
453 > h.__index = function(table, key)
454 > return rawget(h, key)
456 > h.attrset = function(self, x)
459 > h.attron = function(self, x)
460 > -- currently same as attrset since Lua 5.1 doesn't have bitwise operators
461 > -- doesn't support multiple attrs at once
462 >-- local old = self.scr.attrs
463 >-- self.scr.attrs = old|x
466 > h.attroff = function(self, x)
467 > -- currently borked since Lua 5.1 doesn't have bitwise operators
468 > -- doesn't support multiple attrs at once
469 >-- local old = self.scr.attrs
470 >-- self.scr.attrs = old & (~x)
471 > self.scr.attrs = curses.A_NORMAL
473 > h.getch = function(self)
474 > local c = table.remove(h.kbd, 1)
475 > if c == nil then return c end
476 > return string.byte(c) -- for verisimilitude with ncurses
478 > h.addch = function(self, c)
479 > local scr = self.scr
481 > scr.cursy = scr.cursy+1
485 > if scr.cursy <= scr.h then
486 > scr[scr.cursy][scr.cursx] = {data=c, attrs=scr.attrs}
487 > scr.cursx = scr.cursx+1
488 > if scr.cursx > scr.w then
489 > scr.cursy = scr.cursy+1
494 > h.addstr = function(self, s)
499 > h.mvaddch = function(self, y, x, c)
504 > h.mvaddstr = function(self, y, x, s)
509 > h.clear = function(self)
510 > clear_scr(self.scr)
512 > h.refresh = function(self)
517 - __teliva_timestamp: original
521 > for i=1,keys:len() do
522 > table.insert(result, keys[i])
526 - __teliva_timestamp: original
534 - __teliva_timestamp: original
536 >function clear_scr(props)
542 > props[y][x] = {data=' ', attrs=curses.A_NORMAL}
547 - __teliva_timestamp: original
549 >function check_screen(window, contents, message)
551 > for i=1,contents:len() do
552 > check_eq(window.scr[y][x].data, contents[i], message..'/'..y..','..x)
554 > if x > window.scr.w then
561 >-- putting it all together, an example test of both keyboard and screen
562 >function test_check_screen()
575 > local b = w:getch()
576 > if b == nil then break end
577 > w:mvaddstr(y, 1, lines[string.char(b)])
580 > check_screen(w, '345 '..
583 > 'test_check_screen')
585 - __teliva_timestamp: original
587 >function check_reverse(window, contents, message)
589 > for i=1,contents:len() do
590 > if contents[i] ~= ' ' then
591 > -- hacky version while we're without bitwise operators on Lua 5.1
592 >-- check(window.scr[y][x].attrs & curses.A_REVERSE, message..'/'..y..','..x)
593 > check_eq(window.scr[y][x].attrs, curses.A_REVERSE, message..'/'..y..','..x)
595 > -- hacky version while we're without bitwise operators on Lua 5.1
596 >-- check(window.scr[y][x].attrs & (~curses.A_REVERSE), message..'/'..y..','..x)
597 > check(window.scr[y][x].attrs ~= curses.A_REVERSE, message..'/'..y..','..x)
600 > if x > window.scr.w then
606 - __teliva_timestamp: original
608 >function check_bold(window, contents, message)
610 > for i=1,contents:len() do
611 > if contents[i] ~= ' ' then
612 > -- hacky version while we're without bitwise operators on Lua 5.1
613 >-- check(window.scr[y][x].attrs & curses.A_BOLD, message..'/'..y..','..x)
614 > check_eq(window.scr[y][x].attrs, curses.A_BOLD, message..'/'..y..','..x)
616 > -- hacky version while we're without bitwise operators on Lua 5.1
617 >-- check(window.scr[y][x].attrs & (~curses.A_BOLD), message..'/'..y..','..x)
618 > check(window.scr[y][x].attrs ~= curses.A_BOLD, message..'/'..y..','..x)
621 > if x > window.scr.w then
627 - __teliva_timestamp: original
629 >-- check which parts of a screen have the given color_pair
630 >function check_color(window, cp, contents, message)
632 > for i=1,contents:len() do
633 > if contents[i] ~= ' ' then
634 > -- hacky version while we're without bitwise operators on Lua 5.1
635 >-- check(window.scr[y][x].attrs & curses.color_pair(cp), message..'/'..y..','..x)
636 > check_eq(window.scr[y][x].attrs, curses.color_pair(cp), message..'/'..y..','..x)
638 > -- hacky version while we're without bitwise operators on Lua 5.1
639 >-- check(window.scr[y][x].attrs & (~curses.A_BOLD), message..'/'..y..','..x)
640 > check(window.scr[y][x].attrs ~= curses.color_pair(cp), message..'/'..y..','..x)
643 > if x > window.scr.w then
649 - __teliva_timestamp: original
651 >-- horizontal separator
652 >function sep(window)
653 > local y, _ = window:getyx()
654 > window:mvaddstr(y+1, 0, '')
655 > local _, cols = window:getmaxyx()
660 - __teliva_timestamp: original
662 >function render(window)
664 > -- draw stuff to screen here
665 > window:attron(curses.A_BOLD)
666 > window:mvaddstr(1, 5, "example app")
667 > window:attrset(curses.A_NORMAL)
669 > window:attrset(curses.color_pair(i))
670 > window:mvaddstr(3+i, 5, "========================")
674 - __teliva_timestamp: original
676 >function update(window)
677 > local key = window:getch()
678 > -- process key here
680 - __teliva_timestamp: original
682 >function init_colors()
684 > curses.init_pair(i, i, -1)
686 > curses.init_pair(8, 7, 0)
687 > curses.init_pair(9, 7, 1)
688 > curses.init_pair(10, 7, 2)
689 > curses.init_pair(11, 7, 3)
690 > curses.init_pair(12, 7, 4)
691 > curses.init_pair(13, 7, 5)
692 > curses.init_pair(14, 7, 6)
693 > curses.init_pair(15, -1, 15)
695 - __teliva_timestamp: original
705 - __teliva_timestamp:
706 >Thu Mar 17 21:21:29 2022
708 >program = {lines = nil, cursor = nil}
710 >-- cursor: int, index into lines
711 >-- line: {[word], cursor}
712 >-- word: {string, cursor}
713 - __teliva_timestamp:
714 >Thu Mar 17 21:27:38 2022
716 >function render(window)
717 > local key = window:getch()
718 > for row, line in ipair(program.lines) do
719 > for col, word in ipair(line.words) do
720 > window:addstr(word)
723 > window:addstr('\n')
726 - __teliva_timestamp:
727 >Thu Mar 17 21:28:39 2022
729 >function render(window)
730 > local key = window:getch()
731 > for row, line in ipairs(program.lines) do
732 > for col, word in pairs(line.words) do
733 > window:addstr(word)
736 > window:addstr('\n')
739 - __teliva_timestamp:
740 >Thu Mar 17 21:31:33 2022
761 - __teliva_timestamp:
762 >Thu Mar 17 21:32:05 2022
764 >function render(window)
766 > for row, line in ipairs(program.lines) do
767 > for col, word in pairs(line.words) do
768 > window:addstr(word)
771 > window:addstr('\n')
775 - __teliva_timestamp:
776 >Thu Mar 17 21:32:22 2022
778 >function render(window)
780 > for row, line in ipairs(program.lines) do
781 > for col, word in pairs(line.words) do
782 > window:addstr(word.data)
785 > window:addstr('\n')
789 - __teliva_timestamp:
790 >Thu Mar 17 21:33:00 2022
792 >function update(window)
793 > local key = window:getch()
799 - __teliva_timestamp:
800 >Thu Mar 17 21:33:17 2022
802 >function update(window)
803 > local key = window:getch()
804 > if key == string.byte(' ') then
809 - __teliva_timestamp:
810 >Thu Mar 17 21:33:31 2022
812 >function update(window)
813 > local key = string.char(window:getch())
819 - __teliva_timestamp:
820 >Thu Mar 17 21:36:14 2022
822 >very initial skeleton
823 >- just render, no eval
824 >- no stack rendering
825 >- no cursor (just appends)
827 >function update(window)
828 > local key = string.char(window:getch())
830 > table.insert(program.lines[1].words, {data='', cursor=0})
832 > local words = program.lines[1].words
833 > words[#words].data = words[#words].data .. key
836 - __teliva_timestamp:
837 >Thu Mar 17 21:39:24 2022
848 >function test_basic_render()
850 - __teliva_timestamp:
851 >Thu Mar 17 21:43:05 2022
857 > render(Window, Program)
858 > update(Window, Program)
861 - __teliva_timestamp:
862 >Thu Mar 17 21:43:11 2022
864 >function render(window, program)
866 > for row, line in ipairs(program.lines) do
867 > for col, word in pairs(line.words) do
868 > window:addstr(word.data)
871 > window:addstr('\n')
876 >function test_render()
882 - __teliva_timestamp:
883 >Thu Mar 17 21:43:20 2022
885 >function update(window, program, key)
887 > table.insert(program.lines[1].words, {data='', cursor=0})
889 > local words = program.lines[1].words
890 > words[#words].data = words[#words].data .. key
893 - __teliva_timestamp:
894 >Thu Mar 17 21:58:10 2022
896 >Program = empty_program()
897 - __teliva_timestamp:
898 >Thu Mar 17 21:58:10 2022
900 >function empty_program()
916 - __teliva_timestamp:
917 >Thu Mar 17 22:36:24 2022
919 >function render(window, program)
921 > for row, line in ipairs(program.lines) do
922 > for col, word in pairs(line.words) do
923 > window:addstr(word.data)
926 > window:addstr('\n')
930 - __teliva_timestamp:
931 >Thu Mar 17 22:41:01 2022
939 > render(Window, Program)
940 > local key = string.char(Window:getch()) -- nodelay can't be set
941 > update(Window, Program, key)
945 >function test_incomplete()
950 > local prog = empty_program()
953 > local key = w:getch()
954 > if key == nil then break end
955 > update(w, prog, string.char(key))
957 > check_screen(w, 'ab c '..
960 > 'test_main_incomplete')
962 - __teliva_timestamp: original
964 >A concatenative programming language with a live-updating debug UI.
965 >by Kartik Agaram and Sumeet Agarwal
969 >Previous prototypes:
970 > - https://archive.org/details/akkartik-2min-2020-12-06
971 > - https://merveilles.town/@akkartik/105759816342173743