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 > {'^h', 'backspace'},
440 - __teliva_timestamp: original
442 >Window = curses.stdscr()
443 - __teliva_timestamp: original
445 >Show all anagrams of a given word
446 - __teliva_timestamp: original
449 - __teliva_timestamp: original
452 - __teliva_timestamp: original
455 > Window:nodelay(true)
461 - __teliva_timestamp: original
463 >function update(window)
466 > key = window:getch()
467 > if key then break end
469 > if key == curses.KEY_LEFT then
473 > elseif key == curses.KEY_RIGHT then
474 > if cursor <= #word then
477 > elseif key == curses.KEY_BACKSPACE or key == 8 or key == 127 then -- ctrl-h, ctrl-?, delete
480 > word = word:remove(cursor)
482 > elseif key >= 32 and key < 127 then
483 > word = word:insert(string.char(key), cursor-1)
487 - __teliva_timestamp: original
489 >function render(window)
492 > local prompt_str = ' what is your name? '
493 > window:attron(curses.A_REVERSE)
494 > window:mvaddstr(0, 0, prompt_str)
495 > window:attroff(curses.A_REVERSE)
497 > window:attron(curses.A_BOLD)
498 > window:addstr(word)
499 > window:attroff(curses.A_BOLD)
500 > window:mvaddstr(2, 0, '')
501 > local results = anagrams(word)
502 > if #results > 0 then
503 > window:attron(curses.A_REVERSE)
504 > print(#results..' anagrams')
505 > window:attroff(curses.A_REVERSE)
506 > for i, w in ipairs(results) do
512 > window:mvaddstr(0, string.len(prompt_str)+cursor, '')
515 - __teliva_timestamp:
516 >Mon Feb 21 17:42:28 2022
518 >function anagrams(word)
519 > return gather(sort_letters(word))
521 - __teliva_timestamp:
522 >Mon Feb 21 18:18:07 2022
525 > if s == '' then return {} end
528 > if i == 1 or s[i] ~= s[i-1] then
529 > append(result, combine(s[i], gather(all_but(s, i))))
534 - __teliva_timestamp: original
538 >-- return 'l' with each element prefixed with 'prefix'
539 >function combine(prefix, l)
540 > if #l == 0 then return {prefix} end
542 > for _, elem in ipairs(l) do
543 > table.insert(result, prefix..elem)
548 >function test_combine()
549 > check_eq(combine('abc', {}), {'abc'}, 'test_combine: empty list')
551 - __teliva_timestamp:
552 >Sat Mar 5 15:24:00 2022
554 >function count_anagrams(s)
555 > local result = factorial(s:len())
556 > local letter_counts = count_letters(s)
557 > for l, cnt in pairs(letter_counts) do
558 > result = result / factorial(cnt)
562 - __teliva_timestamp:
563 >Sat Mar 5 15:53:23 2022
565 >-- only works when nodelay (non-blocking keyboard)
566 >function key_pressed(window)
567 > local c = window:getch()
568 > if c == nil then return false end
572 - __teliva_timestamp:
573 >Sat Mar 5 15:55:34 2022
575 >function render(window)
578 > local prompt_str = ' what is your name? '
579 > window:attron(curses.A_REVERSE)
580 > window:mvaddstr(0, 0, prompt_str)
581 > window:attroff(curses.A_REVERSE)
583 > window:attron(curses.A_BOLD)
584 > window:addstr(word)
585 > window:attroff(curses.A_BOLD)
586 > window:mvaddstr(2, 0, '')
588 > local num_anagrams = count_anagrams(word)
589 > window:attron(curses.A_REVERSE)
590 > print(num_anagrams..' anagrams')
591 > window:attroff(curses.A_REVERSE)
592 > local results = anagrams(word)
593 > if results == nil then -- interrupted
594 > window:addstr('...')
596 > assert(#results == num_anagrams, "something's wrong; the count is unexpected")
597 > for i, w in ipairs(results) do
600 > if key_pressed(window) then
607 > window:mvaddstr(0, string.len(prompt_str)+cursor, '')
610 - __teliva_timestamp:
611 >Sat Mar 5 15:56:35 2022
613 >restart computation when a key is pressed
615 >-- return a list of unique permutations of a sorted string 's'
616 >-- the letters in 's' must be in alphabetical order, so that duplicates are adjacent
617 >-- this function can take a long time for long strings, so we make it interruptible
618 >-- if a key is pressed, it returns nil
619 >-- since it's recursive, we also need to handle recursive calls returning nil
621 > if s == '' then return {} end
624 > if i == 1 or s[i] ~= s[i-1] then
625 > local subresult = gather(all_but(s, i))
626 > if subresult == nil then return nil end -- interrupted
627 > append(result, combine(s[i], subresult))
629 > if key_pressed(Window) then return nil end -- interrupted