monotonically accumulate versions of definitions

One old drawback now has a new look. Before, we loaded definitions in
order, so global definitions had to exist before other global
definitions that used them. See window and grid in life.tlv. Now we load
definitions in reverse order, so initialization needs to change. Worse,
if we update window, we need to edit grid just to fix the order.

This implies that we can't yet optimize away bindings where there are no
new changes.
This commit is contained in:
Kartik K. Agaram 2021-11-24 09:55:10 -08:00
parent b40ad26544
commit 5a63a5ca40
5 changed files with 261 additions and 87 deletions

View File

@ -1,10 +1,16 @@
teliva_program = { teliva_program = {
{
window = [==[ window = [==[
window = curses.stdscr() window = curses.stdscr()
-- animation-based app -- animation-based app
window:nodelay(true) window:nodelay(true)
lines, cols = window:getmaxyx()]==], lines, cols = window:getmaxyx()]==],
current_game = [==[current_game = {}]==], },
{
current_game = [==[
current_game = {}]==],
},
{
piece_glyph = [==[ piece_glyph = [==[
piece_glyph = { piece_glyph = {
-- for legibility, white pieces also use unicode glyphs for black pieces -- for legibility, white pieces also use unicode glyphs for black pieces
@ -22,6 +28,8 @@ piece_glyph = {
n = 0x265e, n = 0x265e,
p = 0x265f, p = 0x265f,
}]==], }]==],
},
{
top_player = [==[ top_player = [==[
function top_player(current_game) function top_player(current_game)
if current_game.players[1].color == "black" then if current_game.players[1].color == "black" then
@ -29,6 +37,8 @@ function top_player(current_game)
end end
return current_game.players[2] return current_game.players[2]
end]==], end]==],
},
{
bottom_player = [==[ bottom_player = [==[
function bottom_player(current_game) function bottom_player(current_game)
if current_game.players[1].color == "white" then if current_game.players[1].color == "white" then
@ -36,12 +46,16 @@ function bottom_player(current_game)
end end
return current_game.players[2] return current_game.players[2]
end]==], end]==],
},
{
render_player = [==[ render_player = [==[
function render_player(y, x, player) function render_player(y, x, player)
curses.mvaddstr(y, x, player.user.name) curses.mvaddstr(y, x, player.user.name)
curses.addstr(" · ") curses.addstr(" · ")
curses.addstr(tostring(player.rating)) curses.addstr(tostring(player.rating))
end]==], end]==],
},
{
render_square = [==[ render_square = [==[
function render_square(current_game, rank, file, highlighted_squares) function render_square(current_game, rank, file, highlighted_squares)
-- decide whether to highlight -- decide whether to highlight
@ -62,6 +76,8 @@ function render_square(current_game, rank, file, highlighted_squares)
curses.mvaddstr((8 - rank + 1)*3+2, file*5, " ") curses.mvaddstr((8 - rank + 1)*3+2, file*5, " ")
curses.attrset(curses.A_NORMAL) curses.attrset(curses.A_NORMAL)
end]==], end]==],
},
{
render_fen_rank = [==[ render_fen_rank = [==[
function render_fen_rank(rank, fen_rank, highlighted_squares) function render_fen_rank(rank, fen_rank, highlighted_squares)
local file = 1 local file = 1
@ -98,12 +114,16 @@ function render_fen_rank(rank, fen_rank, highlighted_squares)
end end
end end
end]==], end]==],
},
{
render_time = [==[ render_time = [==[
function render_time(y, x, seconds) function render_time(y, x, seconds)
if seconds == nil then return end if seconds == nil then return end
curses.mvaddstr(y, x, tostring(math.floor(seconds/60))) curses.mvaddstr(y, x, tostring(math.floor(seconds/60)))
curses.addstr(string.format(":%02d", seconds%60)) curses.addstr(string.format(":%02d", seconds%60))
end]==], end]==],
},
{
render_board = [==[ render_board = [==[
function render_board(current_game) function render_board(current_game)
--? curses.mvaddstr(1, 50, dump(current_game.fen)) --? curses.mvaddstr(1, 50, dump(current_game.fen))
@ -119,6 +139,8 @@ function render_board(current_game)
render_player(27, 5, bottom_player(current_game)) render_player(27, 5, bottom_player(current_game))
render_time(27, 35, current_game.wc) render_time(27, 35, current_game.wc)
end]==], end]==],
},
{
parse_lm = [==[ parse_lm = [==[
function parse_lm(move) function parse_lm(move)
--? curses.mvaddstr(4, 50, move) --? curses.mvaddstr(4, 50, move)
@ -129,6 +151,8 @@ function parse_lm(move)
--? curses.mvaddstr(5, 50, dump({{rank1, file1}, {rank2, file2}})) --? curses.mvaddstr(5, 50, dump({{rank1, file1}, {rank2, file2}}))
return {from={rank=rank1, file=file1}, to={rank=rank2, file=file2}} return {from={rank=rank1, file=file1}, to={rank=rank2, file=file2}}
end]==], end]==],
},
{
render = [==[ render = [==[
function render(chunk) function render(chunk)
local o = json.decode(chunk) local o = json.decode(chunk)
@ -149,6 +173,8 @@ function render(chunk)
render_board(current_game) render_board(current_game)
curses.refresh() curses.refresh()
end]==], end]==],
},
{
init_colors = [==[ init_colors = [==[
function init_colors() function init_colors()
-- colors -- colors
@ -168,6 +194,8 @@ function init_colors()
curses.init_pair(7, light_piece, dark_last_moved_square) curses.init_pair(7, light_piece, dark_last_moved_square)
curses.init_pair(8, dark_piece, dark_last_moved_square) curses.init_pair(8, dark_piece, dark_last_moved_square)
end]==], end]==],
},
{
main = [==[ main = [==[
function main() function main()
init_colors() init_colors()
@ -184,6 +212,8 @@ function main()
} }
http.request(request) http.request(request)
end]==], end]==],
},
{
utf8 = [==[ utf8 = [==[
-- https://stackoverflow.com/questions/7983574/how-to-write-a-unicode-symbol-in-lua -- https://stackoverflow.com/questions/7983574/how-to-write-a-unicode-symbol-in-lua
function utf8(decimal) function utf8(decimal)
@ -203,6 +233,8 @@ function utf8(decimal)
end end
return table.concat(charbytes) return table.concat(charbytes)
end]==], end]==],
},
{
split = [==[ split = [==[
function split(s, pat) function split(s, pat)
result = {} result = {}
@ -211,6 +243,8 @@ function split(s, pat)
end end
return result return result
end]==], end]==],
},
{
dump = [==[ dump = [==[
-- https://stackoverflow.com/questions/9168058/how-to-dump-a-table-to-console -- https://stackoverflow.com/questions/9168058/how-to-dump-a-table-to-console
function dump(o) function dump(o)
@ -225,4 +259,5 @@ function dump(o)
return tostring(o) return tostring(o)
end end
end]==], end]==],
},
} }

View File

@ -1,7 +1,14 @@
teliva_program = { teliva_program = {
window = [==[window = curses.stdscr()]==], {
n = [==[n = 0]==], window = [==[
render = [==[ window = curses.stdscr()]==],
},
{
n = [==[
n = 0]==],
},
{
render = [==[
function render(window) function render(window)
window:clear() window:clear()
window:attron(curses.A_BOLD) window:attron(curses.A_BOLD)
@ -12,15 +19,22 @@ function render(window)
window:attroff(curses.A_BOLD) window:attroff(curses.A_BOLD)
curses.refresh() curses.refresh()
end]==], end]==],
menu = [==[menu = {Enter="increment"}]==], },
update = [==[ {
menu = [==[
menu = {Enter="increment"}]==],
},
{
update = [==[
function update(window) function update(window)
local key = curses.getch() local key = curses.getch()
if key == 10 then if key == 10 then
n = n+1 n = n+1
end end
end]==], end]==],
main = [==[ },
{
main = [==[
function main() function main()
for i=1,7 do for i=1,7 do
curses.init_pair(i, 0, i) curses.init_pair(i, 0, i)
@ -31,4 +45,5 @@ function main()
update(window) update(window)
end end
end]==], end]==],
},
} }

View File

@ -1,4 +1,5 @@
teliva_program = { teliva_program = {
{
render = [==[ render = [==[
function render(window) function render(window)
window:clear() window:clear()
@ -10,16 +11,25 @@ function render(window)
end end
curses.refresh() curses.refresh()
end]==], end]==],
},
{
lines = [==[ lines = [==[
function lines(window) function lines(window)
local lines, cols = window:getmaxyx() local lines, cols = window:getmaxyx()
return lines return lines
end]==], end]==],
},
{
pop = [==[ pop = [==[
function pop(array) function pop(array)
return table.remove(array) return table.remove(array)
end]==], end]==],
window = [==[window = curses.stdscr()]==], },
{
window = [==[
window = curses.stdscr()]==],
},
{
render_tower = [==[ render_tower = [==[
function render_tower(window, line, col, tower_index, tower) function render_tower(window, line, col, tower_index, tower)
window:attron(curses.A_BOLD) window:attron(curses.A_BOLD)
@ -39,7 +49,12 @@ function render_tower(window, line, col, tower_index, tower)
line = line - 1 line = line - 1
end end
end]==], end]==],
tower = [==[tower = {{6, 5, 4, 3, 2}, {}, {}}]==], },
{
tower = [==[
tower = {{6, 5, 4, 3, 2}, {}, {}}]==],
},
{
render_disk = [==[ render_disk = [==[
function render_disk(window, line, col, size) function render_disk(window, line, col, size)
col = col-size+1 col = col-size+1
@ -50,6 +65,8 @@ function render_disk(window, line, col, size)
col = col+2 col = col+2
end end
end]==], end]==],
},
{
main = [==[ main = [==[
function main() function main()
for i=1,7 do for i=1,7 do
@ -62,6 +79,8 @@ function main()
end end
end end
]==], ]==],
},
{
len = [==[ len = [==[
function len(array) function len(array)
local result = 0 local result = 0
@ -70,6 +89,8 @@ function len(array)
end end
return result return result
end]==], end]==],
},
{
update = [==[ update = [==[
function update(window) function update(window)
window:mvaddstr(lines(window)-2, 5, "tower to remove top disk from? ") window:mvaddstr(lines(window)-2, 5, "tower to remove top disk from? ")
@ -78,14 +99,19 @@ function update(window)
local to = curses.getch() - 96 local to = curses.getch() - 96
make_move(from, to) make_move(from, to)
end]==], end]==],
},
{
make_move = [==[ make_move = [==[
function make_move(from, to) function make_move(from, to)
local disk = pop(tower[from]) local disk = pop(tower[from])
table.insert(tower[to], disk) table.insert(tower[to], disk)
end]==], end]==],
},
{
cols = [==[ cols = [==[
function cols(window) function cols(window)
local lines, cols = window:getmaxyx() local lines, cols = window:getmaxyx()
return cols return cols
end]==], end]==],
},
} }

View File

@ -1,9 +1,5 @@
teliva_program = { teliva_program = {
window = [==[ {
window = curses.stdscr()
-- animation-based app
window:nodelay(true)
lines, cols = window:getmaxyx()]==],
grid = [==[ grid = [==[
-- main data structure -- main data structure
grid = {} grid = {}
@ -14,6 +10,15 @@ for i=1,lines*4 do
end end
end end
]==], ]==],
},
{
window = [==[
window = curses.stdscr()
-- animation-based app
window:nodelay(true)
lines, cols = window:getmaxyx()]==],
},
{
grid_char = [==[ grid_char = [==[
-- grab a 4x2 chunk of grid -- grab a 4x2 chunk of grid
function grid_char(line, col) function grid_char(line, col)
@ -23,6 +28,8 @@ function grid_char(line, col)
end end
return result return result
end]==], end]==],
},
{
print_grid_char = [==[ print_grid_char = [==[
function print_grid_char(window, x) function print_grid_char(window, x)
result = {} result = {}
@ -33,6 +40,8 @@ function print_grid_char(window, x)
end end
return result return result
end]==], end]==],
},
{
glyph = [==[ glyph = [==[
-- look up the braille pattern corresponding to a 4x2 chunk of grid -- look up the braille pattern corresponding to a 4x2 chunk of grid
-- https://en.wikipedia.org/wiki/Braille_Patterns -- https://en.wikipedia.org/wiki/Braille_Patterns
@ -56,6 +65,8 @@ glyph = {
0x28b0, 0x28b1, 0x28b2, 0x28b3, 0x28b4, 0x28b5, 0x28b6, 0x28b7, 0x28f0, 0x28f1, 0x28f2, 0x28f3, 0x28f4, 0x28f5, 0x28f6, 0x28f7, 0x28b0, 0x28b1, 0x28b2, 0x28b3, 0x28b4, 0x28b5, 0x28b6, 0x28b7, 0x28f0, 0x28f1, 0x28f2, 0x28f3, 0x28f4, 0x28f5, 0x28f6, 0x28f7,
0x28b8, 0x28b9, 0x28ba, 0x28bb, 0x28bc, 0x28bd, 0x28be, 0x28bf, 0x28f8, 0x28f9, 0x28fa, 0x28fb, 0x28fc, 0x28fd, 0x28fe, 0x28ff, 0x28b8, 0x28b9, 0x28ba, 0x28bb, 0x28bc, 0x28bd, 0x28be, 0x28bf, 0x28f8, 0x28f9, 0x28fa, 0x28fb, 0x28fc, 0x28fd, 0x28fe, 0x28ff,
}]==], }]==],
},
{
utf8 = [==[ utf8 = [==[
-- https://stackoverflow.com/questions/7983574/how-to-write-a-unicode-symbol-in-lua -- https://stackoverflow.com/questions/7983574/how-to-write-a-unicode-symbol-in-lua
function utf8(decimal) function utf8(decimal)
@ -75,6 +86,8 @@ function utf8(decimal)
end end
return table.concat(charbytes) return table.concat(charbytes)
end]==], end]==],
},
{
grid_char_to_glyph_index = [==[ grid_char_to_glyph_index = [==[
-- convert a chunk of grid into a number -- convert a chunk of grid into a number
function grid_char_to_glyph_index(g) function grid_char_to_glyph_index(g)
@ -82,6 +95,8 @@ function grid_char_to_glyph_index(g)
g[1][2]*16 + g[2][2]*32 + g[3][2]*64 + g[4][2]*128 + g[1][2]*16 + g[2][2]*32 + g[3][2]*64 + g[4][2]*128 +
1 -- 1-indexing 1 -- 1-indexing
end]==], end]==],
},
{
render = [==[ render = [==[
function render(window) function render(window)
window:clear() window:clear()
@ -93,6 +108,8 @@ function render(window)
curses.refresh() curses.refresh()
end end
]==], ]==],
},
{
state = [==[ state = [==[
function state(line, col) function state(line, col)
if line < 1 or line > table.getn(grid) or col < 1 or col > table.getn(grid[1]) then if line < 1 or line > table.getn(grid) or col < 1 or col > table.getn(grid[1]) then
@ -100,12 +117,16 @@ function state(line, col)
end end
return grid[line][col] return grid[line][col]
end]==], end]==],
},
{
num_live_neighbors = [==[ num_live_neighbors = [==[
function num_live_neighbors(line, col) function num_live_neighbors(line, col)
return state(line-1, col-1) + state(line-1, col) + state(line-1, col+1) + return state(line-1, col-1) + state(line-1, col) + state(line-1, col+1) +
state(line, col-1) + state(line, col+1) + state(line, col-1) + state(line, col+1) +
state(line+1, col-1) + state(line+1, col) + state(line+1, col+1) state(line+1, col-1) + state(line+1, col) + state(line+1, col+1)
end]==], end]==],
},
{
step = [==[ step = [==[
function step() function step()
local new_grid = {} local new_grid = {}
@ -124,12 +145,16 @@ function step()
end end
grid = new_grid grid = new_grid
end]==], end]==],
},
{
sleep = [==[ sleep = [==[
function sleep(a) function sleep(a)
local sec = tonumber(os.clock() + a); local sec = tonumber(os.clock() + a);
while (os.clock() < sec) do while (os.clock() < sec) do
end end
end]==], end]==],
},
{
file_exists = [==[ file_exists = [==[
function file_exists(filename) function file_exists(filename)
local f = io.open(filename, "r") local f = io.open(filename, "r")
@ -140,6 +165,8 @@ function file_exists(filename)
return false return false
end end
end]==], end]==],
},
{
load_file = [==[ load_file = [==[
function load_file(window, filename) function load_file(window, filename)
io.input(filename) io.input(filename)
@ -160,6 +187,8 @@ function load_file(window, filename)
end end
end end
end]==], end]==],
},
{
update = [==[ update = [==[
menu = {arrow="pan"} menu = {arrow="pan"}
@ -198,6 +227,8 @@ function update(window, c)
end end
end end
end]==], end]==],
},
{
main = [==[ main = [==[
function main() function main()
for i=1,7 do for i=1,7 do
@ -274,4 +305,5 @@ function main()
step() step()
end end
end]==], end]==],
},
} }

212
src/lua.c
View File

@ -280,27 +280,64 @@ void stack_dump (lua_State *L) {
} }
static int binding_exists (lua_State *L, const char *name) {
int result = 0;
lua_getglobal(L, name);
result = !lua_isnil(L, -1);
lua_pop(L, 1);
return result;
}
static const char *look_up_definition (lua_State *L, const char *name) {
lua_getglobal(L, "teliva_program");
int history_array = lua_gettop(L);
/* iterate over mutations in teliva_program history in reverse order */
int history_array_size = luaL_getn(L, history_array);
for (int i = history_array_size; i > 0; --i) {
lua_rawgeti(L, history_array, i);
int table = lua_gettop(L);
/* iterate over bindings */
/* really we expect only one */
for (lua_pushnil(L); lua_next(L, table) != 0; lua_pop(L, 1)) {
const char* key = lua_tostring(L, -2);
if (strcmp(key, name) == 0)
return lua_tostring(L, -1);
}
}
lua_pop(L, 1);
return NULL;
}
char *Image_name = NULL; char *Image_name = NULL;
static int handle_image (lua_State *L, char **argv, int n) { static int handle_image (lua_State *L, char **argv, int n) {
int status; int status;
int narg = getargs(L, argv, n); /* collect arguments */ int narg = getargs(L, argv, n); /* collect arguments */
lua_setglobal(L, "arg"); lua_setglobal(L, "arg");
/* parse and load file contents (teliva_program table) */ /* parse and load file contents (teliva_program array) */
Image_name = argv[n]; Image_name = argv[n];
status = luaL_loadfile(L, Image_name); status = luaL_loadfile(L, Image_name);
lua_insert(L, -(narg+1)); lua_insert(L, -(narg+1));
if (status != 0) { if (status != 0) return status;
return status;
}
status = docall(L, narg, 0); status = docall(L, narg, 0);
lua_getglobal(L, "teliva_program"); lua_getglobal(L, "teliva_program");
int table = lua_gettop(L); int history_array = lua_gettop(L);
/* parse and load each binding in teliva_program */ /* iterate over mutations in teliva_program history in reverse order */
for (lua_pushnil(L); lua_next(L, table) != 0; lua_pop(L, 1)) { int history_array_size = luaL_getn(L, history_array);
const char* key = lua_tostring(L, -2); for (int i = history_array_size; i > 0; --i) {
const char* value = lua_tostring(L, -1); lua_rawgeti(L, history_array, i);
status = dostring(L, value, key); int table = lua_gettop(L);
if (status != 0) return report(L, status); /* iterate over bindings */
/* really we expect only one */
for (lua_pushnil(L); lua_next(L, table) != 0; lua_pop(L, 1)) {
const char* key = lua_tostring(L, -2);
if (binding_exists(L, key))
continue; // most recent binding trumps older ones
const char* value = lua_tostring(L, -1);
status = dostring(L, value, key);
if (status != 0) return report(L, status);
}
} }
/* call main() */ /* call main() */
lua_getglobal(L, "main"); lua_getglobal(L, "main");
@ -314,10 +351,7 @@ static int handle_image (lua_State *L, char **argv, int n) {
char Current_definition[CURRENT_DEFINITION_LEN+1] = {0}; char Current_definition[CURRENT_DEFINITION_LEN+1] = {0};
void save_to_current_definition_and_editor_buffer (lua_State *L, const char *definition) { void save_to_current_definition_and_editor_buffer (lua_State *L, const char *definition) {
strncpy(Current_definition, definition, CURRENT_DEFINITION_LEN); strncpy(Current_definition, definition, CURRENT_DEFINITION_LEN);
lua_getglobal(L, "teliva_program"); const char *contents = look_up_definition(L, Current_definition);
lua_getfield(L, -1, Current_definition);
const char *contents = lua_tostring(L, -1);
lua_pop(L, 1);
FILE *out = fopen("teliva_editbuffer", "w"); FILE *out = fopen("teliva_editbuffer", "w");
if (contents != NULL) if (contents != NULL)
fprintf(out, "%s", contents); fprintf(out, "%s", contents);
@ -333,27 +367,42 @@ static void read_editor_buffer (char *out) {
} }
/* table to update is at top of stack */ static void update_definition (lua_State *L, const char *name, char *new_contents) {
static void update_definition (lua_State *L, const char *name, char *out) { assert(lua_gettop(L) == 0);
lua_getglobal(L, "teliva_program"); lua_getglobal(L, "teliva_program");
lua_pushstring(L, out); int history_array = 1;
/* create a new table containing a single binding */
lua_createtable(L, /*number of fields per mutation*/2, 0);
lua_pushstring(L, new_contents);
assert(strlen(name) > 0); assert(strlen(name) > 0);
lua_setfield(L, -2, name); lua_setfield(L, -2, name);
/* append the new table to the history of mutations */
int history_array_size = luaL_getn(L, history_array);
++history_array_size;
lua_rawseti(L, history_array, history_array_size);
lua_settop(L, 0); lua_settop(L, 0);
} }
static void save_image (lua_State *L) { static void save_image (lua_State *L) {
lua_getglobal(L, "teliva_program"); lua_getglobal(L, "teliva_program");
int table = lua_gettop(L); int history_array = lua_gettop(L);
FILE *out = fopen(Image_name, "w"); int history_array_size = luaL_getn(L, history_array);
FILE* out = fopen(Image_name, "w");
fprintf(out, "teliva_program = {\n"); fprintf(out, "teliva_program = {\n");
for (lua_pushnil(L); lua_next(L, table) != 0; lua_pop(L, 1)) { for (int i = 1; i <= history_array_size; ++i) {
const char *key = lua_tostring(L, -2); lua_rawgeti(L, history_array, i);
const char *value = lua_tostring(L, -1); int table = lua_gettop(L);
fprintf(out, " %s = [==[", key); fprintf(out, " {\n");
fprintf(out, "%s", value); for (lua_pushnil(L); lua_next(L, table) != 0; lua_pop(L, 1)) {
fprintf(out, "]==],\n"); const char* key = lua_tostring(L, -2);
const char* value = lua_tostring(L, -1);
fprintf(out, " %s = [==[\n", key);
fprintf(out, "%s", value);
fprintf(out, "]==],\n");
}
fprintf(out, " },\n");
lua_pop(L, 1);
} }
fprintf(out, "}\n"); fprintf(out, "}\n");
fclose(out); fclose(out);
@ -434,46 +483,55 @@ int browse_image (lua_State *L) {
clear(); clear();
luaL_newmetatable(L, "__teliva_call_graph_depth"); luaL_newmetatable(L, "__teliva_call_graph_depth");
int cgt = lua_gettop(L); int cgt = lua_gettop(L);
// special-case: we don't instrument the call to main, but it's always 1 // special-case: we don't instrument the call to main, but it's always at depth 1
lua_pushinteger(L, 1); lua_pushinteger(L, 1);
lua_setfield(L, cgt, "main"); lua_setfield(L, cgt, "main");
// segment definitions by depth // segment definitions by depth
lua_getglobal(L, "teliva_program"); lua_getglobal(L, "teliva_program");
int t = lua_gettop(L); int history_array = lua_gettop(L);
int history_array_size = luaL_getn(L, history_array);
int y = 2; int y = 2;
mvaddstr(y, 0, "data: "); mvaddstr(y, 0, "data: ");
// first: data (non-functions) that's not the Teliva menu or curses variables // first: data (non-functions) that's not the Teliva menu or curses variables
for (lua_pushnil(L); lua_next(L, t) != 0;) { for (int i = history_array_size; i > 0; --i) {
const char *definition_name = lua_tostring(L, -2); lua_rawgeti(L, history_array, i);
lua_getglobal(L, definition_name); int t = lua_gettop(L);
int is_userdata = lua_isuserdata(L, -1); for (lua_pushnil(L); lua_next(L, t) != 0;) {
int is_function = lua_isfunction(L, -1); const char *definition_name = lua_tostring(L, -2);
lua_pop(L, 1); lua_getglobal(L, definition_name);
if (strcmp(definition_name, "menu") != 0 // required by all Teliva programs int is_userdata = lua_isuserdata(L, -1);
&& !is_function // functions are not data int is_function = lua_isfunction(L, -1);
&& !is_userdata // including curses window objects lua_pop(L, 1);
// (unlikely to have an interesting definition) if (strcmp(definition_name, "menu") != 0 // required by all Teliva programs
) { && !is_function // functions are not data
browse_definition(definition_name); && !is_userdata // including curses window objects
// (unlikely to have an interesting definition)
) {
browse_definition(definition_name);
}
lua_pop(L, 1); // value
// leave key on stack for next iteration
} }
lua_pop(L, 1); // value
// leave key on stack for next iteration
} }
// second: menu and other userdata // second: menu and other userdata
for (lua_pushnil(L); lua_next(L, t) != 0;) { for (int i = history_array_size; i > 0; --i) {
const char* definition_name = lua_tostring(L, -2); lua_rawgeti(L, history_array, i);
lua_getglobal(L, definition_name); int t = lua_gettop(L);
int is_userdata = lua_isuserdata(L, -1); for (lua_pushnil(L); lua_next(L, t) != 0;) {
lua_pop(L, 1); const char* definition_name = lua_tostring(L, -2);
if (strcmp(definition_name, "menu") == 0 lua_getglobal(L, definition_name);
|| is_userdata // including curses window objects int is_userdata = lua_isuserdata(L, -1);
) { lua_pop(L, 1);
browse_definition(definition_name); if (strcmp(definition_name, "menu") == 0
|| is_userdata // including curses window objects
) {
browse_definition(definition_name);
}
lua_pop(L, 1); // value
// leave key on stack for next iteration
} }
lua_pop(L, 1); // value
// leave key on stack for next iteration
} }
// functions by level // functions by level
@ -482,32 +540,40 @@ int browse_image (lua_State *L) {
y++; y++;
for (int level = 1; level < 5; ++level) { for (int level = 1; level < 5; ++level) {
mvaddstr(y, 0, " "); mvaddstr(y, 0, " ");
for (lua_pushnil(L); lua_next(L, t) != 0;) { for (int i = history_array_size; i > 0; --i) {
const char* definition_name = lua_tostring(L, -2); lua_rawgeti(L, history_array, i);
lua_getfield(L, cgt, definition_name); int t = lua_gettop(L);
int depth = lua_tointeger(L, -1); for (lua_pushnil(L); lua_next(L, t) != 0;) {
if (depth == level) const char* definition_name = lua_tostring(L, -2);
browse_definition(definition_name); lua_getfield(L, cgt, definition_name);
lua_pop(L, 1); // depth of value int depth = lua_tointeger(L, -1);
lua_pop(L, 1); // value if (depth == level)
// leave key on stack for next iteration browse_definition(definition_name);
lua_pop(L, 1); // depth of value
lua_pop(L, 1); // value
// leave key on stack for next iteration
}
} }
y += 2; y += 2;
} }
// unused functions // unused functions
mvaddstr(y, 0, " "); mvaddstr(y, 0, " ");
for (lua_pushnil(L); lua_next(L, t) != 0;) { for (int i = history_array_size; i > 0; --i) {
const char* definition_name = lua_tostring(L, -2); lua_rawgeti(L, history_array, i);
lua_getglobal(L, definition_name); int t = lua_gettop(L);
int is_function = lua_isfunction(L, -1); for (lua_pushnil(L); lua_next(L, t) != 0;) {
lua_pop(L, 1); const char* definition_name = lua_tostring(L, -2);
lua_getfield(L, cgt, definition_name); lua_getglobal(L, definition_name);
if (is_function && lua_isnoneornil(L, -1)) int is_function = lua_isfunction(L, -1);
browse_definition(definition_name); lua_pop(L, 1);
lua_pop(L, 1); // depth of value lua_getfield(L, cgt, definition_name);
lua_pop(L, 1); // value if (is_function && lua_isnoneornil(L, -1))
// leave key on stack for next iteration browse_definition(definition_name);
lua_pop(L, 1); // depth of value
lua_pop(L, 1); // value
// leave key on stack for next iteration
}
} }
lua_settop(L, 0); lua_settop(L, 0);