├── .gitignore ├── build_settings.make ├── ch1 ├── .gitignore ├── doscript.c ├── eatyguy0.lua ├── loadmodule.c ├── makefile ├── mymodule.lua ├── runmodule.c └── script.lua ├── ch2 ├── .gitignore ├── eatyguy1.c ├── eatyguy1.lua ├── eatyguy2.c ├── eatyguy2.lua └── makefile ├── ch3 ├── .gitignore ├── eatyguy3.c ├── eatyguy3.lua ├── eatyguy4.c ├── eatyguy4.lua ├── eatyguy5.c ├── eatyguy5.lua ├── makefile └── util.lua ├── ch4 ├── .gitignore ├── Baddy.lua ├── Character.lua ├── Pair.c ├── Pair.h ├── cmodule.c ├── cmodule.h ├── eatyguy6.c ├── eatyguy6.lua ├── eatyguy7.c ├── eatyguy7.lua ├── eatyguy8.c ├── eatyguy8.lua ├── makefile ├── test_pair.lua └── util.lua ├── ch5 ├── .gitignore ├── Baddy.lua ├── Character.lua ├── Pair.c ├── Pair.h ├── eatyguy9.c ├── eatyguy9.lua ├── keep_calm.lua ├── lua_error.c ├── lua_pcall.c ├── makefile ├── panic.lua ├── strict.lua └── util.lua ├── ch6 ├── .gitignore ├── Baddy.lua ├── Character.lua ├── Pair.c ├── Pair.h ├── UserBaddy.lua ├── a_user_baddy.lua ├── eatyguy10.c ├── eatyguy10.lua ├── interpreter.c ├── interpreter.h ├── library_subset.c ├── limit_cpu.c ├── limit_memory.c ├── makefile ├── run_bash.lua ├── strict.lua ├── track_memory.c └── util.lua ├── img └── screenshot.png ├── lua ├── Makefile ├── lapi.c ├── lapi.h ├── lauxlib.c ├── lauxlib.h ├── lbaselib.c ├── lbitlib.c ├── lcode.c ├── lcode.h ├── lcorolib.c ├── lctype.c ├── lctype.h ├── ldblib.c ├── ldebug.c ├── ldebug.h ├── ldo.c ├── ldo.h ├── ldump.c ├── lfunc.c ├── lfunc.h ├── lgc.c ├── lgc.h ├── linit.c ├── liolib.c ├── llex.c ├── llex.h ├── llimits.h ├── lmathlib.c ├── lmem.c ├── lmem.h ├── loadlib.c ├── lobject.c ├── lobject.h ├── lopcodes.c ├── lopcodes.h ├── loslib.c ├── lparser.c ├── lparser.h ├── lprefix.h ├── lstate.c ├── lstate.h ├── lstring.c ├── lstring.h ├── lstrlib.c ├── ltable.c ├── ltable.h ├── ltablib.c ├── ltm.c ├── ltm.h ├── lua.c ├── lua.h ├── lua.hpp ├── luac.c ├── luaconf.h ├── lualib.h ├── lundump.c ├── lundump.h ├── lutf8lib.c ├── lvm.c ├── lvm.h ├── lzio.c └── lzio.h ├── makefile └── readme.md /.gitignore: -------------------------------------------------------------------------------- 1 | tmp.lua 2 | out 3 | tags 4 | lua-5.3.3* 5 | eatyguy 6 | *.exe 7 | *.swp 8 | liblua.a 9 | *.o 10 | scratch.md 11 | *.a 12 | *.so 13 | .vimrc 14 | -------------------------------------------------------------------------------- /build_settings.make: -------------------------------------------------------------------------------- 1 | # makefile_setup 2 | # 3 | # This makefile is meant to be included by chapter-specific 4 | # makefiles within each ch* directory. 5 | # 6 | # Here are the meanings of some key variables: 7 | # 8 | # Variable Meaning 9 | # ----------------------------- 10 | # 11 | # flags standard C compilation flags 12 | # 13 | # lua_lib name of Lua library file to build 14 | # (omitted on Windows as we assume it's prebuilt) 15 | # 16 | # lua_flags platform-specific flags for building $(lualib) 17 | # 18 | # so_ext filename extension for shared object files 19 | # (this is dll on windows, so on other systems) 20 | # 21 | # so_flags cc flags specific to building so files 22 | # (Windows is a 2-step process; see ch4/makefile) 23 | # 24 | 25 | cc = cc -std=c99 26 | 27 | # Platform-specific settings. 28 | ifeq ($(OS),Windows_NT) 29 | # Windows. 30 | flags = -llua 31 | so_ext = dll 32 | so_flags = -shared -llua 33 | so_dep = %.o 34 | so_make = $(cc) $< -o $@ $(so_flags) 35 | else ifeq ($(shell uname),Darwin) 36 | # macOS. 37 | cflags = -I../lua 38 | flags = -llua -L../lua $(cflags) 39 | lua_lib = ../lua/liblua.a 40 | lua_flags = SYSCFLAGS="-DLUA_USE_MACOSX" 41 | so_ext = so 42 | so_flags = -undefined dynamic_lookup -I../lua 43 | so_dep = %.c %.h 44 | so_make = $(cc) $< -o $@ $(so_flags) 45 | else 46 | # Guess Linux. 47 | cflags = -D_POSIX_C_SOURCE=199309L -I../lua 48 | flags = -llua -L../lua -lm -ldl $(cflags) 49 | lua_lib = ../lua/liblua.a 50 | lua_flags = SYSCFLAGS="-DLUA_USE_LINUX" 51 | so_ext = so 52 | so_flags = -I../lua -shared -fpic 53 | so_dep = %.c %.h 54 | so_make = $(cc) $< -o $@ $(so_flags) 55 | endif 56 | 57 | all = $(binaries) $(eatyguys) $(obj_files) $(so_files) $(interpreters) 58 | 59 | all: $(all) 60 | 61 | clean: 62 | rm -f $(all) *.o 63 | 64 | $(binaries) : % : %.c $(lua_lib) 65 | $(cc) $< -o $@ $(flags) 66 | 67 | $(eatyguys) : % : %.c $(lua_lib) Pair.o 68 | $(cc) $^ -o $@ $(flags) 69 | 70 | $(interpreters) : % : %.c $(lua_lib) interpreter.o 71 | $(cc) $^ -o $@ $(flags) 72 | 73 | $(obj_files) : %.o : %.c %.h 74 | $(cc) -c $< -o $@ $(cflags) 75 | 76 | $(so_files) : %.$(so_ext) : $(so_dep) 77 | $(cc) $< -o $@ $(so_flags) 78 | 79 | ../lua/liblua.a: 80 | make -C ../lua liblua.a $(lua_flags) 81 | -------------------------------------------------------------------------------- /ch1/.gitignore: -------------------------------------------------------------------------------- 1 | doscript 2 | loadmodule 3 | runmodule 4 | -------------------------------------------------------------------------------- /ch1/doscript.c: -------------------------------------------------------------------------------- 1 | // doscript.c 2 | 3 | #include "lauxlib.h" 4 | #include "lua.h" 5 | #include "lualib.h" 6 | 7 | int main() { 8 | lua_State *L = luaL_newstate(); 9 | luaL_openlibs(L); 10 | luaL_dofile(L, "script.lua"); 11 | 12 | return 0; 13 | } 14 | -------------------------------------------------------------------------------- /ch1/eatyguy0.lua: -------------------------------------------------------------------------------- 1 | -- eatyguy0.lua 2 | 3 | local eatyguy = {} 4 | 5 | 6 | -- Globals. 7 | 8 | local percent_extra_paths = 15 9 | local grid = nil -- grid[x][y]: falsy = wall. 10 | local grid_w, grid_h = nil, nil 11 | 12 | 13 | -- Internal functions. 14 | 15 | local function is_in_bounds(x, y) 16 | return (1 <= x and x <= grid_w and 17 | 1 <= y and y <= grid_h) 18 | end 19 | 20 | local function get_neighbor_directions(x, y, percent_extra) 21 | -- percent_extra is the percent chance of adding extra paths. 22 | percent_extra = percent_extra or 0 23 | local neighbor_directions = {} 24 | local all_directions = {{1, 0}, {-1, 0}, {0, 1}, {0, -1}} 25 | for _, direction in pairs(all_directions) do 26 | local nx, ny = x + 2 * direction[1], y + 2 * direction[2] 27 | local is_extra_ok = (math.random(100) <= percent_extra) 28 | -- Add `direction` if the neighbor is not yet in a path, or 29 | -- if we randomly got an extra ok using percent_extra. 30 | if is_in_bounds(nx, ny) and 31 | (not grid[nx][ny] or is_extra_ok) then 32 | table.insert(neighbor_directions, direction) 33 | end 34 | end 35 | return neighbor_directions 36 | end 37 | 38 | local function drill_path_from(x, y) 39 | grid[x][y] = 'open' 40 | local neighbor_directions = get_neighbor_directions(x, y) 41 | while #neighbor_directions > 0 do 42 | local direction = table.remove(neighbor_directions, 43 | math.random(#neighbor_directions)) 44 | grid[x + direction[1]][y + direction[2]] = 'open' 45 | drill_path_from(x + 2 * direction[1], y + 2 * direction[2]) 46 | neighbor_directions = get_neighbor_directions(x, y, 47 | percent_extra_paths) 48 | end 49 | end 50 | 51 | 52 | -- Public functions. 53 | 54 | function eatyguy.init() 55 | 56 | -- Set up the grid size and pseudorandom number generation. 57 | grid_w, grid_h = 39, 21 58 | math.randomseed(os.time()) 59 | 60 | -- Build the maze. 61 | grid = {} 62 | for x = 1, grid_w do grid[x] = {} end 63 | drill_path_from(1, 1) 64 | 65 | -- Draw the maze. 66 | for y = 0, grid_h + 1 do 67 | for x = 0, grid_w + 1 do 68 | -- This line is like: chars = (grid[x][y] ? ' ' : '##'). 69 | local chars = (grid[x] and grid[x][y]) and ' ' or '##' 70 | io.write(chars) 71 | end 72 | io.write('\n') -- Move cursor to next row. 73 | end 74 | end 75 | 76 | return eatyguy 77 | -------------------------------------------------------------------------------- /ch1/loadmodule.c: -------------------------------------------------------------------------------- 1 | // loadmodule.c 2 | // 3 | // Loads mymodule.lua but doesn't call any functions in it. 4 | // 5 | 6 | #include "lauxlib.h" 7 | #include "lua.h" 8 | #include "lualib.h" 9 | 10 | int main() { 11 | lua_State *L = luaL_newstate(); 12 | luaL_openlibs(L); 13 | luaL_dofile(L, "mymodule.lua"); 14 | lua_setglobal(L, "mymodule"); 15 | lua_settop(L, 0); 16 | 17 | return 0; 18 | } 19 | -------------------------------------------------------------------------------- /ch1/makefile: -------------------------------------------------------------------------------- 1 | binaries = doscript loadmodule runmodule 2 | include ../build_settings.make 3 | -------------------------------------------------------------------------------- /ch1/mymodule.lua: -------------------------------------------------------------------------------- 1 | -- mymodule.lua 2 | 3 | local mymodule = {} 4 | 5 | function mymodule.sayhi() 6 | print('Why hello from mymodule!') 7 | end 8 | 9 | return mymodule 10 | -------------------------------------------------------------------------------- /ch1/runmodule.c: -------------------------------------------------------------------------------- 1 | // runmodule.c 2 | // 3 | // Loads eatyguy0.lua and runs eatyguy.init(). 4 | // 5 | 6 | #include "lauxlib.h" 7 | #include "lua.h" 8 | #include "lualib.h" 9 | 10 | int main() { 11 | 12 | // Create a Lua state and load the module. 13 | lua_State *L = luaL_newstate(); 14 | luaL_openlibs(L); 15 | luaL_dofile(L, "eatyguy0.lua"); 16 | lua_setglobal(L, "eatyguy"); 17 | lua_settop(L, 0); 18 | 19 | // Run the init() function. 20 | lua_getglobal(L, "eatyguy"); 21 | lua_getfield(L, -1, "init"); // -1 means stack top. 22 | lua_call(L, 0, 0); // 0, 0 = #args, #retvals 23 | 24 | return 0; 25 | } 26 | -------------------------------------------------------------------------------- /ch1/script.lua: -------------------------------------------------------------------------------- 1 | -- script.lua 2 | print('Hi from script.lua!') 3 | -------------------------------------------------------------------------------- /ch2/.gitignore: -------------------------------------------------------------------------------- 1 | eatyguy1 2 | eatyguy2 3 | -------------------------------------------------------------------------------- /ch2/eatyguy1.c: -------------------------------------------------------------------------------- 1 | // eatyguy1.c 2 | // 3 | // Loads eatyguy1.lua and runs eatyguy.init(). 4 | // 5 | 6 | #include "lauxlib.h" 7 | #include "lua.h" 8 | #include "lualib.h" 9 | 10 | #include 11 | 12 | 13 | // Lua-visible functions. 14 | 15 | int set_color(lua_State *L) { 16 | int color = lua_tonumber(L, 1); 17 | char cmd[1024]; 18 | snprintf(cmd, 1024, "tput setab %d", color); 19 | system(cmd); 20 | return 0; // 0 is the number of Lua-visible return values. 21 | } 22 | 23 | 24 | // Main. 25 | 26 | int main() { 27 | 28 | // Create a Lua state and load the module. 29 | lua_State *L = luaL_newstate(); 30 | luaL_openlibs(L); 31 | luaL_dofile(L, "eatyguy1.lua"); 32 | lua_setglobal(L, "eatyguy"); 33 | lua_settop(L, 0); 34 | 35 | // Make the set_color function visible to Lua. 36 | lua_pushcfunction(L, set_color); 37 | lua_setglobal(L, "set_color"); 38 | 39 | // Run the init() function. 40 | lua_getglobal(L, "eatyguy"); 41 | lua_getfield(L, -1, "init"); // -1 means stack top. 42 | lua_call(L, 0, 0); // 0, 0 = #args, #retvals 43 | 44 | return 0; 45 | } 46 | -------------------------------------------------------------------------------- /ch2/eatyguy1.lua: -------------------------------------------------------------------------------- 1 | -- eatyguy1.lua 2 | 3 | local eatyguy = {} 4 | 5 | 6 | -- Globals. 7 | 8 | local percent_extra_paths = 15 9 | local grid = nil -- grid[x][y]: falsy = wall. 10 | local grid_w, grid_h = nil, nil 11 | 12 | 13 | -- Internal functions. 14 | 15 | local function is_in_bounds(x, y) 16 | return (1 <= x and x <= grid_w and 17 | 1 <= y and y <= grid_h) 18 | end 19 | 20 | local function get_neighbor_directions(x, y, percent_extra) 21 | -- percent_extra is the percent chance of adding extra paths. 22 | percent_extra = percent_extra or 0 23 | local neighbor_directions = {} 24 | local all_directions = {{1, 0}, {-1, 0}, {0, 1}, {0, -1}} 25 | for _, direction in pairs(all_directions) do 26 | local nx, ny = x + 2 * direction[1], y + 2 * direction[2] 27 | local is_extra_ok = (math.random(100) <= percent_extra) 28 | -- Add `direction` if the neighbor is not yet in a path, or 29 | -- if we randomly got an extra ok using percent_extra. 30 | if is_in_bounds(nx, ny) and 31 | (not grid[nx][ny] or is_extra_ok) then 32 | table.insert(neighbor_directions, direction) 33 | end 34 | end 35 | return neighbor_directions 36 | end 37 | 38 | local function drill_path_from(x, y) 39 | grid[x][y] = 'open' 40 | local neighbor_directions = get_neighbor_directions(x, y) 41 | while #neighbor_directions > 0 do 42 | local direction = table.remove(neighbor_directions, 43 | math.random(#neighbor_directions)) 44 | grid[x + direction[1]][y + direction[2]] = 'open' 45 | drill_path_from(x + 2 * direction[1], y + 2 * direction[2]) 46 | neighbor_directions = get_neighbor_directions(x, y, 47 | percent_extra_paths) 48 | end 49 | end 50 | 51 | 52 | -- Public functions. 53 | 54 | function eatyguy.init() 55 | 56 | -- Set up the grid size and pseudorandom number generation. 57 | grid_w, grid_h = 39, 21 58 | math.randomseed(os.time()) 59 | 60 | -- Build the maze. 61 | grid = {} 62 | for x = 1, grid_w do grid[x] = {} end 63 | drill_path_from(1, 1) 64 | 65 | -- Draw the maze. 66 | for y = 0, grid_h + 1 do 67 | for x = 0, grid_w + 1 do 68 | if grid[x] and grid[x][y] then 69 | set_color(0) -- Black; open space color. 70 | else 71 | set_color(4) -- Blue; wall color. 72 | end 73 | io.write(' ') 74 | io.flush() -- Needed for color output. 75 | end 76 | set_color(0) -- End the lines in black. 77 | io.write(' \r\n') -- Move cursor to next row. 78 | end 79 | end 80 | 81 | return eatyguy 82 | -------------------------------------------------------------------------------- /ch2/eatyguy2.c: -------------------------------------------------------------------------------- 1 | // eatyguy2.c 2 | // 3 | // Loads eatyguy2.lua and runs eatyguy.init(), 4 | // followed by eatyguy.loop() in a loop. 5 | // 6 | 7 | #include "lauxlib.h" 8 | #include "lua.h" 9 | #include "lualib.h" 10 | 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | 17 | // 27 is the decimal representation of Esc in ASCII. 18 | #define ESC_KEY 27 19 | 20 | // Internal functions. 21 | 22 | double gettime() { 23 | struct timeval tv; 24 | gettimeofday(&tv, NULL); 25 | return tv.tv_sec + 1e-6 * tv.tv_usec; 26 | } 27 | 28 | // This code is part of the file eatyguy2.c, which is the same 29 | // as eatyguy1.c except for lines in bold. 30 | 31 | int getkey() { 32 | 33 | // Make reading from stdin non-blocking. 34 | int flags = fcntl(STDIN_FILENO, F_GETFL); 35 | fcntl(STDIN_FILENO, F_SETFL, flags | O_NONBLOCK); 36 | 37 | int ch = getchar(); 38 | 39 | // Turn off non-blocking I/O. On some systems, leaving stdin 40 | // non-blocking will also leave stdout non-blocking, which 41 | // can cause printing errors. 42 | fcntl(STDIN_FILENO, F_SETFL, flags); 43 | return ch; 44 | } 45 | 46 | 47 | void start() { 48 | // Terminal setup. 49 | system("tput setab 0"); // Use a black background. 50 | system("tput clear"); // Clear the screen. 51 | system("tput civis"); // Hide the cursor. 52 | system("stty raw -echo"); // Improve access to keypresses. 53 | } 54 | 55 | void done() { 56 | 57 | // Put the terminal back into a decent state. 58 | system("stty cooked echo"); // Undo init call to "stty raw". 59 | system("tput reset"); // Reset colors and clear screen. 60 | 61 | exit(0); 62 | } 63 | 64 | 65 | // Lua-visible functions. 66 | 67 | // Lua: set_color('b' or 'f', ). 68 | int set_color(lua_State *L) { 69 | const char *b_or_f = lua_tostring(L, 1); 70 | int color = lua_tonumber(L, 2); 71 | char cmd[1024]; 72 | snprintf(cmd, 1024, "tput seta%s %d", b_or_f, color); 73 | system(cmd); 74 | return 0; 75 | } 76 | 77 | // Lua: set_pos(x, y). 78 | int set_pos(lua_State *L) { 79 | int x = lua_tonumber(L, 1); 80 | int y = lua_tonumber(L, 2); 81 | char cmd[1024]; 82 | // The 'tput cup' command accepts y before x; not a typo. 83 | snprintf(cmd, 1024, "tput cup %d %d", y, x); 84 | system(cmd); 85 | return 0; 86 | } 87 | 88 | // Lua: timestamp(). 89 | // Return a high-resolution timestamp in seconds. 90 | int timestamp(lua_State *L) { 91 | lua_pushnumber(L, gettime()); 92 | return 1; 93 | } 94 | 95 | 96 | // Main. 97 | 98 | int main() { 99 | 100 | start(); 101 | 102 | // Create a Lua state and load the module. 103 | lua_State *L = luaL_newstate(); 104 | luaL_openlibs(L); 105 | 106 | // Make our Lua-callable functions visible to Lua. 107 | lua_register(L, "set_color", set_color); 108 | lua_register(L, "set_pos", set_pos); 109 | lua_register(L, "timestamp", timestamp); 110 | 111 | // Load eatyguy2.lua and run the init() function. 112 | luaL_dofile(L, "eatyguy2.lua"); 113 | lua_setglobal(L, "eatyguy"); 114 | lua_settop(L, 0); 115 | 116 | lua_getglobal(L, "eatyguy"); 117 | lua_getfield(L, -1, "init"); // -1 means stack top. 118 | lua_call(L, 0, 0); // 0, 0 = #args, #retvals 119 | 120 | lua_getglobal(L, "eatyguy"); 121 | while (1) { 122 | // Call eatyguy.loop(). 123 | lua_getfield(L, -1, "loop"); 124 | lua_call(L, 0, 0); 125 | 126 | int c = getkey(); 127 | if (c == ESC_KEY || c == 'q' || c == 'Q') done(); 128 | } 129 | 130 | return 0; 131 | } 132 | -------------------------------------------------------------------------------- /ch2/eatyguy2.lua: -------------------------------------------------------------------------------- 1 | -- eatyguy2.lua 2 | 3 | local eatyguy = {} 4 | 5 | 6 | -- Globals. 7 | 8 | local percent_extra_paths = 15 9 | local grid = nil -- grid[x][y]: falsy = wall. 10 | local grid_w, grid_h = nil, nil 11 | local player = {pos = {1, 1}, 12 | dir = {1, 0}, 13 | next_dir = {1, 0}} 14 | 15 | 16 | -- Internal functions. 17 | 18 | local function is_in_bounds(x, y) 19 | return (1 <= x and x <= grid_w and 20 | 1 <= y and y <= grid_h) 21 | end 22 | 23 | local function get_neighbor_directions(x, y, percent_extra) 24 | -- percent_extra is the percent chance of adding extra paths. 25 | percent_extra = percent_extra or 0 26 | local neighbor_directions = {} 27 | local all_directions = {{1, 0}, {-1, 0}, {0, 1}, {0, -1}} 28 | for _, direction in pairs(all_directions) do 29 | local nx, ny = x + 2 * direction[1], y + 2 * direction[2] 30 | local is_extra_ok = (math.random(100) <= percent_extra) 31 | -- Add `direction` if the neighbor is not yet in a path, or 32 | -- if we randomly got an extra ok using percent_extra. 33 | if is_in_bounds(nx, ny) and 34 | (not grid[nx][ny] or is_extra_ok) then 35 | table.insert(neighbor_directions, direction) 36 | end 37 | end 38 | return neighbor_directions 39 | end 40 | 41 | local function drill_path_from(x, y) 42 | grid[x][y] = '. ' 43 | local neighbor_directions = get_neighbor_directions(x, y) 44 | while #neighbor_directions > 0 do 45 | local direction = table.remove(neighbor_directions, 46 | math.random(#neighbor_directions)) 47 | grid[x + direction[1]][y + direction[2]] = '. ' 48 | drill_path_from(x + 2 * direction[1], y + 2 * direction[2]) 49 | neighbor_directions = get_neighbor_directions(x, y, 50 | percent_extra_paths) 51 | end 52 | end 53 | 54 | -- Check whether a character can move in a given direction. 55 | -- Return can_move, new_pos. 56 | local function can_move_in_dir(character, dir) 57 | local pos = character.pos 58 | local grid_x, grid_y = pos[1] + dir[1], pos[2] + dir[2] 59 | local can_move = grid[grid_x] and grid[grid_x][grid_y] 60 | return can_move, {grid_x, grid_y} 61 | end 62 | 63 | local move_delta = 0.2 -- seconds 64 | local next_move_time = nil 65 | 66 | local function update() 67 | 68 | -- Ensure any dot under the player has been eaten. 69 | local cur_pos = player.pos 70 | grid[cur_pos[1]][cur_pos[2]] = ' ' 71 | 72 | -- Only move every move_delta seconds. 73 | if next_move_time == nil then 74 | next_move_time = timestamp() + move_delta 75 | end 76 | if timestamp() < next_move_time then return end 77 | next_move_time = next_move_time + move_delta 78 | 79 | -- Try to change direction; if we can't, next_dir will take 80 | -- effect at a corner where we can turn in that direction. 81 | local all_dirs = {{1, 0}, {-1, 0}, {0, 1}, {0, -1}} 82 | if can_move_in_dir(player, player.next_dir) then 83 | player.dir = player.next_dir 84 | player.next_dir = all_dirs[math.random(4)] 85 | end 86 | 87 | -- Move in direction player.dir if possible. 88 | local can_move, new_pos = can_move_in_dir(player, player.dir) 89 | while not can_move do 90 | player.dir = all_dirs[math.random(4)] 91 | can_move, new_pos = can_move_in_dir(player, player.dir) 92 | end 93 | player.old_pos = player.pos -- Save the old position. 94 | player.pos = new_pos 95 | end 96 | 97 | local function draw() 98 | 99 | -- Choose the sprite to draw. For example, a right-facing 100 | -- player is drawn as '< alternated with '- 101 | local draw_data = { 102 | [ '1,0'] = {"'<", "'-"}, 103 | ['-1,0'] = {">'", "-'"}, 104 | [ '0,1'] = {"^'", "|'"}, 105 | ['0,-1'] = {"v.", "'."} 106 | } 107 | local anim_timestep = 0.2 108 | local dirkey = ('%d,%d'):format(player.dir[1], 109 | player.dir[2]) 110 | -- framekey switches between 1 & 2; basic sprite animation. 111 | local time = timestamp() 112 | local framekey = math.floor(time / anim_timestep) % 2 + 1 113 | local chars = draw_data[dirkey][framekey] 114 | 115 | -- Draw the player. 116 | set_color('b', 3) -- Yellow background. 117 | set_color('f', 0) -- Black foreground. 118 | local x = 2 * player.pos[1] -- Each tile is 2 chars wide. 119 | local y = player.pos[2] 120 | set_pos(x, y) 121 | io.write(chars) 122 | io.flush() 123 | 124 | -- Erase the old player pos if appropriate. 125 | if player.old_pos then 126 | local x = 2 * player.old_pos[1] 127 | local y = player.old_pos[2] 128 | set_pos(x, y) 129 | set_color('b', 0) -- Black background. 130 | io.write(' ') 131 | io.flush() 132 | player.old_pos = nil 133 | end 134 | end 135 | 136 | -- Public functions. 137 | 138 | function eatyguy.init() 139 | 140 | -- Set up the grid size and pseudorandom number generation. 141 | grid_w, grid_h = 39, 21 142 | math.randomseed(os.time()) 143 | 144 | -- Build the maze. 145 | grid = {} 146 | for x = 1, grid_w do grid[x] = {} end 147 | drill_path_from(1, 1) 148 | 149 | -- Draw the maze. 150 | set_color('f', 7) -- White foreground. 151 | for y = 0, grid_h + 1 do 152 | for x = 0, grid_w + 1 do 153 | if grid[x] and grid[x][y] then 154 | set_color('b', 0) -- Black; open space color. 155 | io.write(grid[x][y]) 156 | else 157 | set_color('b', 4) -- Blue; wall color. 158 | io.write(' ') 159 | end 160 | io.flush() -- Needed for color output. 161 | end 162 | set_color('b', 0) -- End the lines in black. 163 | io.write(' \r\n') -- Move cursor to next row. 164 | end 165 | end 166 | 167 | function eatyguy.loop() 168 | update() 169 | draw() 170 | end 171 | 172 | return eatyguy 173 | -------------------------------------------------------------------------------- /ch2/makefile: -------------------------------------------------------------------------------- 1 | binaries = eatyguy1 eatyguy2 2 | include ../build_settings.make 3 | -------------------------------------------------------------------------------- /ch3/.gitignore: -------------------------------------------------------------------------------- 1 | eatyguy3 2 | eatyguy4 3 | eatyguy5 4 | -------------------------------------------------------------------------------- /ch3/eatyguy3.c: -------------------------------------------------------------------------------- 1 | // eatyguy3.c 2 | // 3 | // Load eatyguy3.lua and run it in the order below. 4 | // 5 | // -- Lua-ish pseudocode representing the order of events. 6 | // eatyguy.init() 7 | // while true do 8 | // eatyguy.loop(key) 9 | // sleep(0.016) 10 | // end 11 | // 12 | 13 | #include "lauxlib.h" 14 | #include "lua.h" 15 | #include "lualib.h" 16 | 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | 25 | // 27 is the decimal representation of Esc in ASCII. 26 | #define ESC_KEY 27 27 | 28 | 29 | // Internal functions. 30 | 31 | double gettime() { 32 | struct timeval tv; 33 | gettimeofday(&tv, NULL); 34 | return tv.tv_sec + 1e-6 * tv.tv_usec; 35 | } 36 | 37 | int getkey(int *is_end_of_seq) { 38 | 39 | // Make reading from stdin non-blocking. 40 | int flags = fcntl(STDIN_FILENO, F_GETFL); 41 | fcntl(STDIN_FILENO, F_SETFL, flags | O_NONBLOCK); 42 | 43 | // We care about two cases: 44 | // Case 1: A sequence of the form 27, 91, X; return X. 45 | // Case 2: For any other sequence, return each int separately. 46 | 47 | *is_end_of_seq = 0; 48 | int ch = getchar(); 49 | if (ch == 27) { 50 | int next = getchar(); 51 | if (next == 91) { 52 | *is_end_of_seq = 1; 53 | ch = getchar(); 54 | goto end; 55 | } 56 | // If we get here, then we're not in a 27, 91, X sequence. 57 | ungetc(next, stdin); 58 | } 59 | 60 | end: 61 | 62 | // Turn off non-blocking I/O. On some systems, leaving stdin 63 | // non-blocking will also leave stdout non-blocking, which 64 | // can cause printing errors. 65 | fcntl(STDIN_FILENO, F_SETFL, flags); 66 | return ch; 67 | } 68 | 69 | void sleephires(double sec) { 70 | long s = (long)floor(sec); 71 | long n = (long)floor((sec - s) * 1e9); 72 | struct timespec delay = { .tv_sec = s, .tv_nsec = n }; 73 | nanosleep(&delay, NULL); 74 | } 75 | 76 | void start() { 77 | // Terminal setup. 78 | system("tput setab 0"); // Use a black background. 79 | system("tput clear"); // Clear the screen. 80 | system("tput civis"); // Hide the cursor. 81 | system("stty raw -echo"); // Improve access to keypresses. 82 | } 83 | 84 | void done() { 85 | 86 | // Put the terminal back into a decent state. 87 | system("stty cooked echo"); // Undo init call to "stty raw". 88 | system("tput reset"); // Reset colors and clear screen. 89 | 90 | exit(0); 91 | } 92 | 93 | void push_keypress(lua_State *L, int key, int is_end_of_seq) { 94 | if (is_end_of_seq && 65 <= key && key <= 68) { 95 | // up, down, right, left = 65, 66, 67, 68 96 | static const char *arrow_names[] = {"up", "down", 97 | "right", "left"}; 98 | lua_pushstring(L, arrow_names[key - 65]); 99 | } else { 100 | lua_pushnumber(L, key); 101 | } 102 | } 103 | 104 | 105 | // Lua-visible functions. 106 | 107 | // Lua: set_color('b' or 'f', ). 108 | int set_color(lua_State *L) { 109 | const char *b_or_f = lua_tostring(L, 1); 110 | int color = lua_tonumber(L, 2); 111 | char cmd[1024]; 112 | snprintf(cmd, 1024, "tput seta%s %d", b_or_f, color); 113 | system(cmd); 114 | return 0; 115 | } 116 | 117 | // Lua: set_pos(x, y). 118 | int set_pos(lua_State *L) { 119 | int x = lua_tonumber(L, 1); 120 | int y = lua_tonumber(L, 2); 121 | char cmd[1024]; 122 | // The 'tput cup' command accepts y before x; not a typo. 123 | snprintf(cmd, 1024, "tput cup %d %d", y, x); 124 | system(cmd); 125 | return 0; 126 | } 127 | 128 | // Lua: timestamp(). 129 | // Return a high-resolution timestamp in seconds. 130 | int timestamp(lua_State *L) { 131 | lua_pushnumber(L, gettime()); 132 | return 1; 133 | } 134 | 135 | 136 | // Main. 137 | 138 | int main() { 139 | 140 | start(); 141 | 142 | // Create a Lua state and load the module. 143 | lua_State *L = luaL_newstate(); 144 | luaL_openlibs(L); 145 | 146 | // Make our Lua-callable functions visible to Lua. 147 | lua_register(L, "set_color", set_color); 148 | lua_register(L, "set_pos", set_pos); 149 | lua_register(L, "timestamp", timestamp); 150 | 151 | // Load eatyguy3 and run the init() function. 152 | luaL_dofile(L, "eatyguy3.lua"); 153 | lua_setglobal(L, "eatyguy"); 154 | lua_settop(L, 0); 155 | 156 | lua_getglobal(L, "eatyguy"); 157 | lua_getfield(L, -1, "init"); // -1 means stack top. 158 | lua_call(L, 0, 0); // 0, 0 = #args, #retvals 159 | 160 | lua_getglobal(L, "eatyguy"); 161 | while (1) { 162 | int is_end_of_seq; 163 | int key = getkey(&is_end_of_seq); 164 | if (key == ESC_KEY || key == 'q' || key == 'Q') done(); 165 | 166 | // Call eatyguy.loop(). 167 | lua_getfield(L, -1, "loop"); 168 | push_keypress(L, key, is_end_of_seq); 169 | lua_call(L, 1, 0); 170 | 171 | sleephires(0.016); // Sleep for 16ms. 172 | } 173 | 174 | return 0; 175 | } 176 | -------------------------------------------------------------------------------- /ch3/eatyguy3.lua: -------------------------------------------------------------------------------- 1 | -- eatyguy3.lua 2 | 3 | local eatyguy = {} 4 | 5 | 6 | -- Globals. 7 | 8 | local percent_extra_paths = 15 9 | local grid = nil -- grid[x][y]: falsy = wall. 10 | local grid_w, grid_h = nil, nil 11 | local player = {pos = {1, 1}, 12 | dir = {1, 0}, 13 | next_dir = {1, 0}} 14 | 15 | 16 | -- Internal functions. 17 | 18 | local function is_in_bounds(x, y) 19 | return (1 <= x and x <= grid_w and 20 | 1 <= y and y <= grid_h) 21 | end 22 | 23 | local function get_neighbor_directions(x, y, percent_extra) 24 | -- percent_extra is the percent chance of adding extra paths. 25 | percent_extra = percent_extra or 0 26 | local neighbor_directions = {} 27 | local all_directions = {{1, 0}, {-1, 0}, {0, 1}, {0, -1}} 28 | for _, direction in pairs(all_directions) do 29 | local nx, ny = x + 2 * direction[1], y + 2 * direction[2] 30 | local is_extra_ok = (math.random(100) <= percent_extra) 31 | -- Add `direction` if the neighbor is not yet in a path, or 32 | -- if we randomly got an extra ok using percent_extra. 33 | if is_in_bounds(nx, ny) and 34 | (not grid[nx][ny] or is_extra_ok) then 35 | table.insert(neighbor_directions, direction) 36 | end 37 | end 38 | return neighbor_directions 39 | end 40 | 41 | local function drill_path_from(x, y) 42 | grid[x][y] = '. ' 43 | local neighbor_directions = get_neighbor_directions(x, y) 44 | while #neighbor_directions > 0 do 45 | local direction = table.remove(neighbor_directions, 46 | math.random(#neighbor_directions)) 47 | grid[x + direction[1]][y + direction[2]] = '. ' 48 | drill_path_from(x + 2 * direction[1], y + 2 * direction[2]) 49 | neighbor_directions = get_neighbor_directions(x, y, 50 | percent_extra_paths) 51 | end 52 | end 53 | 54 | -- Check whether a character can move in a given direction. 55 | -- Return can_move, new_pos. 56 | local function can_move_in_dir(character, dir) 57 | local pos = character.pos 58 | local grid_x, grid_y = pos[1] + dir[1], pos[2] + dir[2] 59 | local can_move = grid[grid_x] and grid[grid_x][grid_y] 60 | return can_move, {grid_x, grid_y} 61 | end 62 | 63 | local move_delta = 0.2 -- seconds 64 | local next_move_time = nil 65 | 66 | local function update(key) 67 | 68 | -- Ensure any dot under the player has been eaten. 69 | local cur_pos = player.pos 70 | grid[cur_pos[1]][cur_pos[2]] = ' ' 71 | 72 | -- Update the next direction if an arrow key was pressed. 73 | local direction_of_key = {left = {-1, 0}, right = {1, 0}, 74 | up = {0, -1}, down = {0, 1}} 75 | local new_dir = direction_of_key[key] 76 | if new_dir then player.next_dir = new_dir end 77 | 78 | -- Only move every move_delta seconds. 79 | if next_move_time == nil then 80 | next_move_time = timestamp() + move_delta 81 | end 82 | if timestamp() < next_move_time then return end 83 | next_move_time = next_move_time + move_delta 84 | 85 | -- Try to change direction; if we can't, next_dir will take 86 | -- effect at a corner where we can turn in that direction. 87 | if can_move_in_dir(player, player.next_dir) then 88 | player.dir = player.next_dir 89 | end 90 | 91 | -- Move in direction player.dir if possible. 92 | local can_move, new_pos = can_move_in_dir(player, player.dir) 93 | if can_move then 94 | player.old_pos = player.pos -- Save the old position. 95 | player.pos = new_pos 96 | end 97 | end 98 | 99 | local function draw() 100 | 101 | -- Choose the sprite to draw. For example, a right-facing 102 | -- player is drawn as '< alternated with '- 103 | local draw_data = { 104 | [ '1,0'] = {"'<", "'-"}, 105 | ['-1,0'] = {">'", "-'"}, 106 | [ '0,1'] = {"^'", "|'"}, 107 | ['0,-1'] = {"v.", "'."} 108 | } 109 | local anim_timestep = 0.2 110 | local dirkey = ('%d,%d'):format(player.dir[1], 111 | player.dir[2]) 112 | -- framekey switches between 1 & 2; basic sprite animation. 113 | local time = timestamp() 114 | local framekey = math.floor(time / anim_timestep) % 2 + 1 115 | local chars = draw_data[dirkey][framekey] 116 | 117 | -- Draw the player. 118 | set_color('b', 3) -- Yellow background. 119 | set_color('f', 0) -- Black foreground. 120 | local x = 2 * player.pos[1] 121 | local y = player.pos[2] 122 | set_pos(x, y) 123 | io.write(chars) 124 | io.flush() 125 | 126 | -- Erase the old player pos if appropriate. 127 | if player.old_pos then 128 | local x = 2 * player.old_pos[1] 129 | local y = player.old_pos[2] 130 | set_pos(x, y) 131 | set_color('b', 0) -- Black background. 132 | io.write(' ') 133 | io.flush() 134 | player.old_pos = nil 135 | end 136 | end 137 | 138 | 139 | -- Public functions. 140 | 141 | function eatyguy.init() 142 | 143 | -- Set up the grid size and pseudorandom number generation. 144 | grid_w, grid_h = 39, 21 145 | math.randomseed(os.time()) 146 | 147 | -- Build the maze. 148 | grid = {} 149 | for x = 1, grid_w do grid[x] = {} end 150 | drill_path_from(1, 1) 151 | 152 | -- Draw the maze. 153 | set_color('f', 7) -- White foreground. 154 | for y = 0, grid_h + 1 do 155 | for x = 0, grid_w + 1 do 156 | if grid[x] and grid[x][y] then 157 | set_color('b', 0) -- Black; open space color. 158 | io.write(grid[x][y]) 159 | else 160 | set_color('b', 4) -- Blue; wall color. 161 | io.write(' ') 162 | end 163 | io.flush() -- Needed for color output. 164 | end 165 | set_color('b', 0) -- End the lines in black. 166 | io.write(' \r\n') -- Move cursor to next row. 167 | end 168 | end 169 | 170 | function eatyguy.loop(key) 171 | update(key) 172 | draw() 173 | end 174 | 175 | return eatyguy 176 | -------------------------------------------------------------------------------- /ch3/eatyguy4.c: -------------------------------------------------------------------------------- 1 | // eatyguy4.c 2 | // 3 | // Load eatyguy4.lua and run it in the order below. 4 | // 5 | // -- Lua-ish pseudocode representing the order of events. 6 | // eatyguy.init() 7 | // while true do 8 | // eatyguy.loop(key) 9 | // sleep(0.016) 10 | // end 11 | // 12 | 13 | #include "lauxlib.h" 14 | #include "lua.h" 15 | #include "lualib.h" 16 | 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | 25 | // 27 is the decimal representation of Esc in ASCII. 26 | #define ESC_KEY 27 27 | 28 | 29 | // Internal functions. 30 | 31 | double gettime() { 32 | struct timeval tv; 33 | gettimeofday(&tv, NULL); 34 | return tv.tv_sec + 1e-6 * tv.tv_usec; 35 | } 36 | 37 | int getkey(int *is_end_of_seq) { 38 | 39 | // Make reading from stdin non-blocking. 40 | int flags = fcntl(STDIN_FILENO, F_GETFL); 41 | fcntl(STDIN_FILENO, F_SETFL, flags | O_NONBLOCK); 42 | 43 | // We care about two cases: 44 | // Case 1: A sequence of the form 27, 91, X; return X. 45 | // Case 2: For any other sequence, return each int separately. 46 | 47 | *is_end_of_seq = 0; 48 | int ch = getchar(); 49 | if (ch == 27) { 50 | int next = getchar(); 51 | if (next == 91) { 52 | *is_end_of_seq = 1; 53 | ch = getchar(); 54 | goto end; 55 | } 56 | // If we get here, then we're not in a 27, 91, X sequence. 57 | ungetc(next, stdin); 58 | } 59 | 60 | end: 61 | 62 | // Turn off non-blocking I/O. On some systems, leaving stdin 63 | // non-blocking will also leave stdout non-blocking, which can 64 | // cause printing errors. 65 | fcntl(STDIN_FILENO, F_SETFL, flags); 66 | return ch; 67 | } 68 | 69 | void sleephires(double sec) { 70 | long s = (long)floor(sec); 71 | long n = (long)floor((sec - s) * 1e9); 72 | struct timespec delay = { .tv_sec = s, .tv_nsec = n }; 73 | nanosleep(&delay, NULL); 74 | } 75 | 76 | void start() { 77 | // Terminal setup. 78 | system("tput setab 0"); // Use a black background. 79 | system("tput clear"); // Clear the screen. 80 | system("tput civis"); // Hide the cursor. 81 | system("stty raw -echo"); // Improve access to keypresses. 82 | } 83 | 84 | void done() { 85 | 86 | // Put the terminal back into a decent state. 87 | system("stty cooked echo"); // Undo init call to "stty raw". 88 | system("tput reset"); // Reset colors and clear screen. 89 | 90 | exit(0); 91 | } 92 | 93 | void push_keypress(lua_State *L, int key, int is_end_of_seq) { 94 | if (is_end_of_seq && 65 <= key && key <= 68) { 95 | // up, down, right, left = 65, 66, 67, 68 96 | static const char *arrow_names[] = {"up", "down", 97 | "right", "left"}; 98 | lua_pushstring(L, arrow_names[key - 65]); 99 | } else { 100 | lua_pushnumber(L, key); 101 | } 102 | } 103 | 104 | 105 | // Lua-visible functions. 106 | 107 | // Lua: timestamp(). 108 | // Return a high-resolution timestamp in seconds. 109 | int timestamp(lua_State *L) { 110 | lua_pushnumber(L, gettime()); 111 | return 1; 112 | } 113 | 114 | 115 | // Main. 116 | 117 | int main() { 118 | 119 | start(); 120 | 121 | // Create a Lua state and load the module. 122 | lua_State *L = luaL_newstate(); 123 | luaL_openlibs(L); 124 | 125 | // Set up API functions written in C. 126 | lua_register(L, "timestamp", timestamp); 127 | 128 | // Set up API functions written in Lua. 129 | luaL_dofile(L, "util.lua"); 130 | 131 | // Load eatyguy4 and run the init() function. 132 | luaL_dofile(L, "eatyguy4.lua"); 133 | lua_setglobal(L, "eatyguy"); 134 | lua_settop(L, 0); 135 | 136 | lua_getglobal(L, "eatyguy"); 137 | lua_getfield(L, -1, "init"); // -1 means stack top. 138 | lua_call(L, 0, 0); // 0, 0 = #args, #retvals 139 | 140 | lua_getglobal(L, "eatyguy"); 141 | while (1) { 142 | int is_end_of_seq; 143 | int key = getkey(&is_end_of_seq); 144 | if (key == ESC_KEY || key == 'q' || key == 'Q') done(); 145 | 146 | // Call eatyguy.loop(). 147 | lua_getfield(L, -1, "loop"); 148 | push_keypress(L, key, is_end_of_seq); 149 | lua_call(L, 1, 0); 150 | 151 | sleephires(0.016); // Sleep for 16ms. 152 | } 153 | 154 | return 0; 155 | } 156 | -------------------------------------------------------------------------------- /ch3/eatyguy4.lua: -------------------------------------------------------------------------------- 1 | -- eatyguy4.lua 2 | 3 | local eatyguy = {} 4 | 5 | 6 | -- Globals. 7 | 8 | local percent_extra_paths = 15 9 | local grid = nil -- grid[x][y]: falsy = wall. 10 | local grid_w, grid_h = nil, nil 11 | local player = {pos = {1, 1}, 12 | dir = {1, 0}, 13 | next_dir = {1, 0}} 14 | 15 | 16 | -- Internal functions. 17 | 18 | local function is_in_bounds(x, y) 19 | return (1 <= x and x <= grid_w and 20 | 1 <= y and y <= grid_h) 21 | end 22 | 23 | local function get_neighbor_directions(x, y, percent_extra) 24 | -- percent_extra is the percent chance of adding extra paths. 25 | percent_extra = percent_extra or 0 26 | local neighbor_directions = {} 27 | local all_directions = {{1, 0}, {-1, 0}, {0, 1}, {0, -1}} 28 | for _, direction in pairs(all_directions) do 29 | local nx, ny = x + 2 * direction[1], y + 2 * direction[2] 30 | local is_extra_ok = (math.random(100) <= percent_extra) 31 | -- Add `direction` if the neighbor is not yet in a path, or 32 | -- if we randomly got an extra ok using percent_extra. 33 | if is_in_bounds(nx, ny) and 34 | (not grid[nx][ny] or is_extra_ok) then 35 | table.insert(neighbor_directions, direction) 36 | end 37 | end 38 | return neighbor_directions 39 | end 40 | 41 | local function drill_path_from(x, y) 42 | grid[x][y] = '. ' 43 | local neighbor_directions = get_neighbor_directions(x, y) 44 | while #neighbor_directions > 0 do 45 | local direction = table.remove(neighbor_directions, 46 | math.random(#neighbor_directions)) 47 | grid[x + direction[1]][y + direction[2]] = '. ' 48 | drill_path_from(x + 2 * direction[1], y + 2 * direction[2]) 49 | neighbor_directions = get_neighbor_directions(x, y, 50 | percent_extra_paths) 51 | end 52 | end 53 | 54 | -- Check whether a character can move in a given direction. 55 | -- Return can_move, new_pos. 56 | local function can_move_in_dir(character, dir) 57 | local pos = character.pos 58 | local grid_x, grid_y = pos[1] + dir[1], pos[2] + dir[2] 59 | local can_move = grid[grid_x] and grid[grid_x][grid_y] 60 | return can_move, {grid_x, grid_y} 61 | end 62 | 63 | local move_delta = 0.2 -- seconds 64 | local next_move_time = nil 65 | 66 | local function update(key) 67 | 68 | -- Ensure any dot under the player has been eaten. 69 | local cur_pos = player.pos 70 | grid[cur_pos[1]][cur_pos[2]] = ' ' 71 | 72 | -- Update the next direction if an arrow key was pressed. 73 | local direction_of_key = {left = {-1, 0}, right = {1, 0}, 74 | up = {0, -1}, down = {0, 1}} 75 | local new_dir = direction_of_key[key] 76 | if new_dir then player.next_dir = new_dir end 77 | 78 | -- Only move every move_delta seconds. 79 | if next_move_time == nil then 80 | next_move_time = timestamp() + move_delta 81 | end 82 | if timestamp() < next_move_time then return end 83 | next_move_time = next_move_time + move_delta 84 | 85 | -- Try to change direction; if we can't, next_dir will take 86 | -- effect at a corner where we can turn in that direction. 87 | if can_move_in_dir(player, player.next_dir) then 88 | player.dir = player.next_dir 89 | end 90 | 91 | -- Move in direction player.dir if possible. 92 | local can_move, new_pos = can_move_in_dir(player, player.dir) 93 | if can_move then 94 | player.old_pos = player.pos -- Save the old position. 95 | player.pos = new_pos 96 | end 97 | end 98 | 99 | local function draw() 100 | 101 | -- Choose the sprite to draw. For example, a right-facing 102 | -- player is drawn as '< alternated with '- 103 | local draw_data = { 104 | [ '1,0'] = {"'<", "'-"}, 105 | ['-1,0'] = {">'", "-'"}, 106 | [ '0,1'] = {"^'", "|'"}, 107 | ['0,-1'] = {"v.", "'."} 108 | } 109 | local anim_timestep = 0.2 110 | local dirkey = ('%d,%d'):format(player.dir[1], 111 | player.dir[2]) 112 | -- framekey switches between 1 & 2; basic sprite animation. 113 | local time = timestamp() 114 | local framekey = math.floor(time / anim_timestep) % 2 + 1 115 | local chars = draw_data[dirkey][framekey] 116 | 117 | -- Draw the player. 118 | set_color('b', 3) -- Yellow background. 119 | set_color('f', 0) -- Black foreground. 120 | local x = 2 * player.pos[1] 121 | local y = player.pos[2] 122 | set_pos(x, y) 123 | io.write(chars) 124 | io.flush() 125 | 126 | -- Erase the old player pos if appropriate. 127 | if player.old_pos then 128 | local x = 2 * player.old_pos[1] 129 | local y = player.old_pos[2] 130 | set_pos(x, y) 131 | set_color('b', 0) -- Black background. 132 | io.write(' ') 133 | io.flush() 134 | player.old_pos = nil 135 | end 136 | end 137 | 138 | 139 | -- Public functions. 140 | 141 | function eatyguy.init() 142 | 143 | -- Set up the grid size and pseudorandom number generation. 144 | grid_w, grid_h = 39, 21 145 | math.randomseed(os.time()) 146 | 147 | -- Build the maze. 148 | grid = {} 149 | for x = 1, grid_w do grid[x] = {} end 150 | drill_path_from(1, 1) 151 | 152 | -- Draw the maze. 153 | set_color('f', 7) -- White foreground. 154 | for y = 0, grid_h + 1 do 155 | for x = 0, grid_w + 1 do 156 | if grid[x] and grid[x][y] then 157 | set_color('b', 0) -- Black; open space color. 158 | io.write(grid[x][y]) 159 | else 160 | set_color('b', 4) -- Blue; wall color. 161 | io.write(' ') 162 | end 163 | io.flush() -- Needed for color output. 164 | end 165 | set_color('b', 0) -- End the lines in black. 166 | io.write(' \r\n') -- Move cursor to next row. 167 | end 168 | end 169 | 170 | function eatyguy.loop(key) 171 | update(key) 172 | draw() 173 | end 174 | 175 | return eatyguy 176 | -------------------------------------------------------------------------------- /ch3/eatyguy5.c: -------------------------------------------------------------------------------- 1 | // eatyguy5.c 2 | // 3 | // Load eatyguy5.lua and run it in the order below. 4 | // 5 | // This introduces the use of Lua tables via Lua's C API. 6 | // 7 | // -- Lua-ish pseudocode representing the order of events. 8 | // eatyguy.init() 9 | // while true do 10 | // eatyguy.loop(state) -- state has keys 'clock' and 'key'. 11 | // sleep(0.016) 12 | // end 13 | // 14 | 15 | #include "lauxlib.h" 16 | #include "lua.h" 17 | #include "lualib.h" 18 | 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | 27 | // 27 is the decimal representation of Esc in ASCII. 28 | #define ESC_KEY 27 29 | 30 | 31 | // Internal functions. 32 | 33 | double gettime() { 34 | struct timeval tv; 35 | gettimeofday(&tv, NULL); 36 | return tv.tv_sec + 1e-6 * tv.tv_usec; 37 | } 38 | 39 | int getkey(int *is_end_of_seq) { 40 | 41 | // Make reading from stdin non-blocking. 42 | int flags = fcntl(STDIN_FILENO, F_GETFL); 43 | fcntl(STDIN_FILENO, F_SETFL, flags | O_NONBLOCK); 44 | 45 | // We care about two cases: 46 | // Case 1: A sequence of the form 27, 91, X; return X. 47 | // Case 2: For any other sequence, return each int separately. 48 | 49 | *is_end_of_seq = 0; 50 | int ch = getchar(); 51 | if (ch == 27) { 52 | int next = getchar(); 53 | if (next == 91) { 54 | *is_end_of_seq = 1; 55 | ch = getchar(); 56 | goto end; 57 | } 58 | // If we get here, then we're not in a 27, 91, X sequence. 59 | ungetc(next, stdin); 60 | } 61 | 62 | end: 63 | 64 | // Turn off non-blocking I/O. On some systems, leaving stdin 65 | // non-blocking will also leave stdout non-blocking, which can 66 | // cause printing errors. 67 | fcntl(STDIN_FILENO, F_SETFL, flags); 68 | return ch; 69 | } 70 | 71 | void sleephires(double sec) { 72 | long s = (long)floor(sec); 73 | long n = (long)floor((sec - s) * 1e9); 74 | struct timespec delay = { .tv_sec = s, .tv_nsec = n }; 75 | nanosleep(&delay, NULL); 76 | } 77 | 78 | void start() { 79 | // Terminal setup. 80 | system("tput setab 0"); // Use a black background. 81 | system("tput clear"); // Clear the screen. 82 | system("tput civis"); // Hide the cursor. 83 | system("stty raw -echo"); // Improve access to keypresses. 84 | } 85 | 86 | void done() { 87 | 88 | // Put the terminal back into a decent state. 89 | system("stty cooked echo"); // Undo init call to "stty raw". 90 | system("tput reset"); // Reset colors and clear screen. 91 | 92 | exit(0); 93 | } 94 | 95 | void push_keypress(lua_State *L, int key, int is_end_of_seq) { 96 | if (is_end_of_seq && 65 <= key && key <= 68) { 97 | // up, down, right, left = 65, 66, 67, 68 98 | static const char *arrow_names[] = {"up", "down", 99 | "right", "left"}; 100 | lua_pushstring(L, arrow_names[key - 65]); 101 | } else { 102 | lua_pushnumber(L, key); 103 | } 104 | } 105 | 106 | void push_state_table(lua_State *L, 107 | int key, 108 | int is_end_of_seq) { 109 | 110 | // The state of the Lua stack is given in indented comments in 111 | // this function to clarify the actions of each function call. 112 | 113 | lua_newtable(L); 114 | 115 | // stack = [.., {}] 116 | 117 | push_keypress(L, key, is_end_of_seq); 118 | 119 | // stack = [.., {}, key] 120 | 121 | lua_setfield(L, -2, "key"); 122 | 123 | // stack = [.., {key = key}] 124 | 125 | lua_pushnumber(L, gettime()); 126 | 127 | // stack = [.., {key = key}, clock] 128 | 129 | lua_setfield(L, -2, "clock"); 130 | 131 | // stack = [.., {key = key, clock = clock}] 132 | } 133 | 134 | 135 | // Lua-visible functions. 136 | 137 | // Lua: timestamp(). 138 | // Return a high-resolution timestamp in seconds. 139 | int timestamp(lua_State *L) { 140 | lua_pushnumber(L, gettime()); 141 | return 1; 142 | } 143 | 144 | 145 | // Main. 146 | 147 | int main() { 148 | 149 | start(); 150 | 151 | // Create a Lua state and load the module. 152 | lua_State *L = luaL_newstate(); 153 | luaL_openlibs(L); 154 | 155 | // Set up API functions written in C. 156 | lua_register(L, "timestamp", timestamp); 157 | 158 | // Set up API functions written in Lua. 159 | luaL_dofile(L, "util.lua"); 160 | 161 | // Load eatyguy5 and run the init() function. 162 | luaL_dofile(L, "eatyguy5.lua"); 163 | lua_setglobal(L, "eatyguy"); 164 | lua_settop(L, 0); 165 | 166 | lua_getglobal(L, "eatyguy"); 167 | lua_getfield(L, -1, "init"); // -1 means stack top. 168 | lua_call(L, 0, 0); // 0, 0 = #args, #retvals 169 | 170 | lua_getglobal(L, "eatyguy"); 171 | while (1) { 172 | int is_end_of_seq; 173 | int key = getkey(&is_end_of_seq); 174 | if (key == ESC_KEY || key == 'q' || key == 'Q') done(); 175 | 176 | // Call eatyguy.loop(state). 177 | lua_getfield(L, -1, "loop"); 178 | // This replaces the previous call to push_keypress(). 179 | push_state_table(L, key, is_end_of_seq); 180 | lua_call(L, 1, 0); 181 | 182 | sleephires(0.016); // Sleep for 16ms. 183 | } 184 | 185 | return 0; 186 | } 187 | -------------------------------------------------------------------------------- /ch3/eatyguy5.lua: -------------------------------------------------------------------------------- 1 | -- eatyguy5.lua 2 | 3 | local eatyguy = {} 4 | 5 | 6 | -- Globals. 7 | 8 | local percent_extra_paths = 15 9 | local grid = nil -- grid[x][y]: falsy = wall. 10 | local grid_w, grid_h = nil, nil 11 | local player = {pos = {1, 1}, 12 | dir = {1, 0}, 13 | next_dir = {1, 0}} 14 | 15 | 16 | -- Internal functions. 17 | 18 | local function is_in_bounds(x, y) 19 | return (1 <= x and x <= grid_w and 20 | 1 <= y and y <= grid_h) 21 | end 22 | 23 | local function get_neighbor_directions(x, y, percent_extra) 24 | -- percent_extra is the percent chance of adding extra paths. 25 | percent_extra = percent_extra or 0 26 | local neighbor_directions = {} 27 | local all_directions = {{1, 0}, {-1, 0}, {0, 1}, {0, -1}} 28 | for _, direction in pairs(all_directions) do 29 | local nx, ny = x + 2 * direction[1], y + 2 * direction[2] 30 | local is_extra_ok = (math.random(100) <= percent_extra) 31 | -- Add `direction` if the neighbor is not yet in a path, or 32 | -- if we randomly got an extra ok using percent_extra. 33 | if is_in_bounds(nx, ny) and 34 | (not grid[nx][ny] or is_extra_ok) then 35 | table.insert(neighbor_directions, direction) 36 | end 37 | end 38 | return neighbor_directions 39 | end 40 | 41 | local function drill_path_from(x, y) 42 | grid[x][y] = '. ' 43 | local neighbor_directions = get_neighbor_directions(x, y) 44 | while #neighbor_directions > 0 do 45 | local direction = table.remove(neighbor_directions, 46 | math.random(#neighbor_directions)) 47 | grid[x + direction[1]][y + direction[2]] = '. ' 48 | drill_path_from(x + 2 * direction[1], y + 2 * direction[2]) 49 | neighbor_directions = get_neighbor_directions(x, y, 50 | percent_extra_paths) 51 | end 52 | end 53 | 54 | -- Check whether a character can move in a given direction. 55 | -- Return can_move, new_pos. 56 | local function can_move_in_dir(character, dir) 57 | local pos = character.pos 58 | local grid_x, grid_y = pos[1] + dir[1], pos[2] + dir[2] 59 | local can_move = grid[grid_x] and grid[grid_x][grid_y] 60 | return can_move, {grid_x, grid_y} 61 | end 62 | 63 | local move_delta = 0.2 -- seconds 64 | local next_move_time = nil 65 | 66 | local function update(state) 67 | 68 | -- Ensure any dot under the player has been eaten. 69 | local cur_pos = player.pos 70 | grid[cur_pos[1]][cur_pos[2]] = ' ' 71 | 72 | -- Update the next direction if an arrow key was pressed. 73 | local direction_of_key = {left = {-1, 0}, right = {1, 0}, 74 | up = {0, -1}, down = {0, 1}} 75 | local new_dir = direction_of_key[state.key] 76 | if new_dir then player.next_dir = new_dir end 77 | 78 | -- Only move every move_delta seconds. 79 | if next_move_time == nil then 80 | next_move_time = state.clock + move_delta 81 | end 82 | if state.clock < next_move_time then return end 83 | next_move_time = next_move_time + move_delta 84 | 85 | -- Try to change direction; if we can't, next_dir will take 86 | -- effect at a corner where we can turn in that direction. 87 | if can_move_in_dir(player, player.next_dir) then 88 | player.dir = player.next_dir 89 | end 90 | 91 | -- Move in direction player.dir if possible. 92 | local can_move, new_pos = can_move_in_dir(player, player.dir) 93 | if can_move then 94 | player.old_pos = player.pos -- Save the old position. 95 | player.pos = new_pos 96 | end 97 | end 98 | 99 | local function draw(clock) 100 | 101 | -- Choose the sprite to draw. For example, a right-facing 102 | -- player is drawn as '< alternated with '- 103 | local draw_data = { 104 | [ '1,0'] = {"'<", "'-"}, 105 | ['-1,0'] = {">'", "-'"}, 106 | [ '0,1'] = {"^'", "|'"}, 107 | ['0,-1'] = {"v.", "'."} 108 | } 109 | local anim_timestep = 0.2 110 | local dirkey = ('%d,%d'):format(player.dir[1], 111 | player.dir[2]) 112 | -- framekey switches between 1 & 2; basic sprite animation. 113 | -- We now use `clock` instead of calling timestamp(). 114 | local framekey = math.floor(clock / anim_timestep) % 2 + 1 115 | local chars = draw_data[dirkey][framekey] 116 | 117 | -- Draw the player. 118 | set_color('b', 3) -- Yellow background. 119 | set_color('f', 0) -- Black foreground. 120 | local x = 2 * player.pos[1] 121 | local y = player.pos[2] 122 | set_pos(x, y) 123 | io.write(chars) 124 | io.flush() 125 | 126 | -- Erase the old player pos if appropriate. 127 | if player.old_pos then 128 | local x = 2 * player.old_pos[1] 129 | local y = player.old_pos[2] 130 | set_pos(x, y) 131 | set_color('b', 0) -- Black background. 132 | io.write(' ') 133 | io.flush() 134 | player.old_pos = nil 135 | end 136 | end 137 | 138 | 139 | -- Public functions. 140 | 141 | function eatyguy.init() 142 | 143 | -- Set up the grid size and pseudorandom number generation. 144 | grid_w, grid_h = 39, 21 145 | math.randomseed(os.time()) 146 | 147 | -- Build the maze. 148 | grid = {} 149 | for x = 1, grid_w do grid[x] = {} end 150 | drill_path_from(1, 1) 151 | 152 | -- Draw the maze. 153 | set_color('f', 7) -- White foreground. 154 | for y = 0, grid_h + 1 do 155 | for x = 0, grid_w + 1 do 156 | if grid[x] and grid[x][y] then 157 | set_color('b', 0) -- Black; open space color. 158 | io.write(grid[x][y]) 159 | else 160 | set_color('b', 4) -- Blue; wall color. 161 | io.write(' ') 162 | end 163 | io.flush() -- Needed for color output. 164 | end 165 | set_color('b', 0) -- End the lines in black. 166 | io.write(' \r\n') -- Move cursor to next row. 167 | end 168 | end 169 | 170 | function eatyguy.loop(state) 171 | update(state) 172 | draw(state.clock) 173 | end 174 | 175 | return eatyguy 176 | -------------------------------------------------------------------------------- /ch3/makefile: -------------------------------------------------------------------------------- 1 | binaries = eatyguy3 eatyguy4 eatyguy5 2 | include ../build_settings.make 3 | -------------------------------------------------------------------------------- /ch3/util.lua: -------------------------------------------------------------------------------- 1 | -- util.lua 2 | -- 3 | -- Define the functions set_color and set_pos so that they cache 4 | -- the strings returned from tput in order to reduce the number 5 | -- of tput calls. 6 | 7 | 8 | -- Local functions like memoized_cmd won't be globally visible 9 | -- after this script completes. 10 | 11 | local memoized_strs = {} -- Maps cmd -> str. 12 | 13 | local function memoized_cmd(shell_cmd) 14 | if not memoized_strs[shell_cmd] then 15 | local pipe = io.popen(shell_cmd) 16 | memoized_strs[shell_cmd] = pipe:read() 17 | pipe:close() 18 | end 19 | io.write(memoized_strs[shell_cmd]) 20 | end 21 | 22 | 23 | -- Global functions will remain visible after this script 24 | -- completes. 25 | 26 | function set_color(b_or_f, color) 27 | assert(b_or_f == 'b' or b_or_f == 'f') 28 | memoized_cmd('tput seta' .. b_or_f .. ' ' .. color) 29 | end 30 | 31 | function set_pos(x, y) 32 | memoized_cmd(('tput cup %d %d'):format(y, x)) 33 | end 34 | -------------------------------------------------------------------------------- /ch4/.gitignore: -------------------------------------------------------------------------------- 1 | eatyguy6 2 | eatyguy7 3 | eatyguy8 4 | *.o 5 | *.so 6 | *.dll 7 | -------------------------------------------------------------------------------- /ch4/Baddy.lua: -------------------------------------------------------------------------------- 1 | -- Baddy.lua 2 | -- 3 | -- A subclass of Character to capture behavior 4 | -- specific to baddies. 5 | -- 6 | 7 | local Character = require 'Character' 8 | 9 | -- Set a new direction and simultaneously update baddy.next_dir 10 | -- to be a right or left turn from new_dir. 11 | -- This function cannot be seen outside the Baddy module. 12 | local function set_new_dir(baddy, new_dir) 13 | baddy.dir = new_dir 14 | local sign = math.random(2) * 2 - 3 -- Either -1 or +1. 15 | baddy.next_dir = {sign * baddy.dir[2], -sign * baddy.dir[1]} 16 | end 17 | 18 | Baddy = Character:new() 19 | 20 | -- Set up a new baddy. 21 | -- This expects a table with keys {home, color, chars}. 22 | function Baddy:new(b) 23 | assert(b) -- Require a table of initial values. 24 | b.pos = b.home 25 | b.dir = {-1, 0} 26 | b.next_dir = {-1, 0} 27 | self.__index = self 28 | return setmetatable(b, self) 29 | end 30 | 31 | function Baddy:move_if_possible(grid) 32 | local deltas = {{-1, 0}, {1, 0}, {0, -1}, {0, 1}} 33 | 34 | -- Try to change direction; otherwise the next_dir will take 35 | -- effect when we're at a turn in that direction. 36 | if self:can_move_in_dir(self.next_dir, grid) then 37 | set_new_dir(self, self.next_dir) 38 | end 39 | 40 | -- Try to move in self.dir; if that doesn't work, randomly 41 | -- change directions till we can move again. 42 | local can_move, new_pos = self:can_move_in_dir(self.dir, grid) 43 | while not can_move do 44 | set_new_dir(self, deltas[math.random(4)]) 45 | can_move, new_pos = self:can_move_in_dir(self.dir, grid) 46 | end 47 | self.old_pos = self.pos -- Save the old position. 48 | self.pos = new_pos 49 | end 50 | 51 | return Baddy 52 | -------------------------------------------------------------------------------- /ch4/Character.lua: -------------------------------------------------------------------------------- 1 | -- Character.lua 2 | -- 3 | -- A class to capture behavior of general 4 | -- characters. 5 | -- 6 | 7 | Character = {} 8 | 9 | function Character:new(c) 10 | c = c or {} 11 | self.__index = self 12 | return setmetatable(c, self) 13 | end 14 | 15 | function Character:can_move_in_dir(dir, grid) 16 | local pos = self.pos 17 | local grid_x, grid_y = pos[1] + dir[1], pos[2] + dir[2] 18 | local can_move = grid[grid_x] and grid[grid_x][grid_y] 19 | return can_move, {grid_x, grid_y} 20 | end 21 | 22 | function Character:move_if_possible(grid) 23 | -- Try to change direction; if we can't, next_dir will take 24 | -- effect at a corner where we can turn in that direction. 25 | if self:can_move_in_dir(self.next_dir, grid) then 26 | self.dir = self.next_dir 27 | end 28 | 29 | -- Move in direction self.dir if possible. 30 | local can_move, new_pos = self:can_move_in_dir(self.dir, grid) 31 | if can_move then 32 | self.old_pos = self.pos -- Save the old position. 33 | self.pos = new_pos 34 | end 35 | end 36 | 37 | function Character:draw(grid) 38 | 39 | -- Draw the character. 40 | local color = self.color or 3 -- Default to yellow. 41 | set_color('b', color) 42 | set_color('f', 0) -- Black foreground. 43 | local x = 2 * self.pos[1] 44 | local y = self.pos[2] 45 | set_pos(x, y) 46 | io.write(self.chars) 47 | io.flush() 48 | 49 | -- Erase the old character pos if appropriate. 50 | if self.old_pos then 51 | local op = self.old_pos 52 | local x = 2 * op[1] 53 | local y = op[2] 54 | set_pos(x, y) 55 | set_color('f', 7) -- White foreground. 56 | set_color('b', 0) -- Black background. 57 | io.write(grid[op[1]][op[2]]) 58 | io.flush() 59 | self.old_pos = nil 60 | end 61 | end 62 | 63 | return Character 64 | -------------------------------------------------------------------------------- /ch4/Pair.c: -------------------------------------------------------------------------------- 1 | // Pair.c 2 | // 3 | // When this module is loaded, it sets up two tables: 4 | // * Pair, which is the class itself, and 5 | // * Pair_mt, the metatable of Pair. 6 | // Pair_mt contains metamethods such as __index. 7 | // Pair contains the new() method so the user can create new 8 | // Pairs by using the familiar pattern: 9 | // 10 | // local p = Pair:new{12, 34}. 11 | // 12 | 13 | #include "Pair.h" 14 | 15 | #include "lauxlib.h" 16 | 17 | #include 18 | 19 | 20 | // -- Macros and types -- 21 | 22 | #define Pair_metatable "Pair" 23 | 24 | typedef struct { 25 | lua_Number x; 26 | lua_Number y; 27 | } Pair; 28 | 29 | 30 | // -- Methods -- 31 | 32 | // Pair:new(p) 33 | int Pair_new(lua_State *L) { 34 | 35 | // Extract the x, y data from the stack. 36 | luaL_checktype(L, 2, LUA_TTABLE); 37 | // stack = [self, p] 38 | lua_rawgeti(L, 2, 1); // 2, 1 = idx in stack, idx in table 39 | // stack = [self, p, p[1]] 40 | lua_rawgeti(L, 2, 2); 41 | // stack = [self, p, p[1], p[2]] 42 | lua_Number x = lua_tonumber(L, -2); // p[1] 43 | lua_Number y = lua_tonumber(L, -1); // p[2] 44 | lua_settop(L, 0); 45 | // stack = [] 46 | 47 | // Create a Pair instance and set its metatable. 48 | Pair *pair = (Pair *)lua_newuserdata(L, sizeof(Pair)); 49 | // stack = [pair] 50 | luaL_getmetatable(L, Pair_metatable); 51 | // stack = [pair, mt] 52 | lua_setmetatable(L, 1); 53 | // stack = [pair] 54 | 55 | // Set up the C data. 56 | pair->x = x; 57 | pair->y = y; 58 | 59 | return 1; 60 | } 61 | 62 | 63 | // -- Internal functions -- 64 | 65 | // Clear the stack and set up a new Pair instance on it. 66 | void push_new_pair(lua_State *L, lua_Number x, lua_Number y) { 67 | 68 | // Clear the stack. 69 | lua_settop(L, 0); 70 | // stack = [] 71 | 72 | // Set up the new resulting Pair instance. 73 | lua_newtable(L); 74 | // stack = [t (the new table)] 75 | lua_pushnumber(L, x); 76 | // stack = [t, x] 77 | lua_rawseti(L, -2, 1); // t[1] = x 78 | // stack = [t] 79 | lua_pushnumber(L, y); 80 | // stack = [t, y] 81 | lua_rawseti(L, -2, 2); // t[2] = y 82 | // stack = [t] 83 | lua_pushvalue(L, -1); 84 | // stack = [t, t] 85 | Pair_new(L); 86 | // stack = [new Pair from t] 87 | } 88 | 89 | 90 | // -- Metamethods -- 91 | 92 | // Pair_mt:index(key) 93 | // This is used to enable p.x and p.y dereferencing on Pair p. 94 | // This also supports p[1] and p[2] lookups. 95 | int Pair_mt_index(lua_State *L) { 96 | 97 | // Expected: stack = [self, key] 98 | Pair *pair = (Pair *)luaL_checkudata(L, 1, Pair_metatable); 99 | 100 | // Find the key value as either 1 (x) or 2 (y). 101 | int key = 0; 102 | int t = lua_type(L, 2); 103 | if (t == LUA_TNUMBER) { 104 | int ok; 105 | key = (int)lua_tointegerx(L, 2, &ok); 106 | if (!ok || (key != 1 && key != 2)) key = 0; 107 | } else if (t == LUA_TSTRING) { 108 | const char *str_key = lua_tostring(L, 2); 109 | if (strcmp(str_key, "x") == 0) key = 1; 110 | if (strcmp(str_key, "y") == 0) key = 2; 111 | } 112 | 113 | // Push the value. 114 | if (key) { 115 | lua_pushnumber(L, key == 1 ? pair->x : pair->y); 116 | } else { 117 | lua_pushnil(L); 118 | } 119 | 120 | return 1; 121 | } 122 | 123 | // Pair_mt:add(other) 124 | int Pair_mt_add(lua_State *L) { 125 | 126 | // Expected: stack = [self, other] 127 | // other may be a Pair or a table of the form {x, y}. 128 | 129 | // Extract the pairs p, q from the stack. 130 | Pair *pair = (Pair *)luaL_checkudata(L, 1, Pair_metatable); 131 | lua_Number p[2] = {pair->x, pair->y}; 132 | lua_Number q[2]; 133 | for (int i = 0; i < 2; ++i) { 134 | lua_pushnumber(L, i + 1); 135 | // stack = [self, other, i + 1] 136 | lua_gettable(L, 2); 137 | // stack = [self, other, other[i + 1]] 138 | if (lua_type(L, -1) != LUA_TNUMBER) { 139 | return luaL_argerror(L, 2, "bad 2nd addend"); 140 | } 141 | q[i] = lua_tonumber(L, -1); 142 | lua_pop(L, 1); 143 | // stack = [self, other] 144 | } 145 | lua_settop(L, 0); 146 | // stack = [] 147 | 148 | // Set up a new table with the sum. 149 | push_new_pair(L, p[0] + q[0], p[1] + q[1]); 150 | 151 | return 1; 152 | } 153 | 154 | // Pair_mt:mul(scalar) 155 | int Pair_mt_mul(lua_State *L) { 156 | 157 | // Expected: stack = [self, scalar] 158 | 159 | // Extract the needed values. 160 | Pair *pair = (Pair *)luaL_checkudata(L, 1, Pair_metatable); 161 | lua_Number scalar = luaL_checknumber(L, 2); 162 | 163 | // Set up the new resulting Pair instance. 164 | push_new_pair(L, pair->x * scalar, pair->y * scalar); 165 | 166 | return 1; 167 | } 168 | 169 | // Pair_mt:eq(other) 170 | int Pair_mt_eq(lua_State *L) { 171 | 172 | // Expected: stack = [self (p), other (q)] 173 | 174 | // Extract the pairs p, q from the stack. 175 | Pair *p = (Pair *)luaL_checkudata(L, 1, Pair_metatable); 176 | Pair *q = (Pair *)luaL_testudata(L, 2, Pair_metatable); 177 | 178 | // Push true or false onto the stack. 179 | if (q == NULL) { 180 | lua_pushboolean(L, 0); // If q is not a Pair, return false. 181 | } else { 182 | lua_pushboolean(L, p->x == q->x && p->y == q->y); 183 | } 184 | 185 | return 1; 186 | } 187 | 188 | 189 | // -- Luaopen function -- 190 | 191 | int luaopen_Pair(lua_State *L) { 192 | 193 | // The user may pass in values here, 194 | // but we'll ignore those values. 195 | lua_settop(L, 0); 196 | 197 | // stack = [] 198 | 199 | // If this metatable already exists, the library is already 200 | // loaded. 201 | if (luaL_newmetatable(L, Pair_metatable)) { 202 | 203 | // stack = [mt] 204 | 205 | static struct luaL_Reg metamethods[] = { 206 | {"__index", Pair_mt_index}, 207 | {"__add", Pair_mt_add}, 208 | {"__eq", Pair_mt_eq}, 209 | {"__mul", Pair_mt_mul}, 210 | {NULL, NULL} 211 | }; 212 | luaL_setfuncs(L, metamethods, 0); 213 | lua_pop(L, 1); // The table is saved in the Lua's registry. 214 | 215 | // stack = [] 216 | } 217 | 218 | static struct luaL_Reg fns[] = { 219 | {"new", Pair_new}, 220 | {NULL, NULL} 221 | }; 222 | 223 | luaL_newlib(L, fns); // Push a new table with fns key/vals. 224 | 225 | // stack = [Pair = {new = new}] 226 | 227 | return 1; // Return the top item, the Pair table. 228 | } 229 | -------------------------------------------------------------------------------- /ch4/Pair.h: -------------------------------------------------------------------------------- 1 | // Pair.h 2 | // 3 | // Pair is a simple userdata type, defined in C, which supports 4 | // basic operations on (x, y) pairs. 5 | // 6 | // Example usage: 7 | // 8 | // local Pair = require 'Pair' 9 | // 10 | // local p = Pair:new {1, 2} 11 | // local q = Pair:new {3, 4} 12 | // local r = p + q * 2 -- Now r = {7, 10}. 13 | // 14 | 15 | #include "lua.h" 16 | 17 | int luaopen_Pair(lua_State *L); 18 | -------------------------------------------------------------------------------- /ch4/cmodule.c: -------------------------------------------------------------------------------- 1 | // cmodule.c 2 | 3 | #include "cmodule.h" 4 | #include 5 | 6 | int luaopen_cmodule(lua_State *L) { 7 | printf("luaopen_cmodule() was called!\n"); 8 | lua_newtable(L); 9 | return 1; // Return the empty table. 10 | } 11 | -------------------------------------------------------------------------------- /ch4/cmodule.h: -------------------------------------------------------------------------------- 1 | // cmodule.h 2 | 3 | #include "lua.h" 4 | 5 | int luaopen_cmodule(lua_State *L); 6 | -------------------------------------------------------------------------------- /ch4/eatyguy6.c: -------------------------------------------------------------------------------- 1 | // eatyguy6.c 2 | // 3 | // Load eatyguy6.lua and run it in the order below. 4 | // 5 | // -- Lua-ish pseudocode representing the order of events. 6 | // eatyguy.init() 7 | // while true do 8 | // eatyguy.loop(state) -- state has keys 'clock' and 'key'. 9 | // sleep(0.016) 10 | // end 11 | // 12 | 13 | #include "lauxlib.h" 14 | #include "lua.h" 15 | #include "lualib.h" 16 | 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | 25 | // 27 is the decimal representation of Esc in ASCII. 26 | #define ESC_KEY 27 27 | 28 | 29 | // Internal functions. 30 | 31 | double gettime() { 32 | struct timeval tv; 33 | gettimeofday(&tv, NULL); 34 | return tv.tv_sec + 1e-6 * tv.tv_usec; 35 | } 36 | 37 | int getkey(int *is_end_of_seq) { 38 | 39 | // Make reading from stdin non-blocking. 40 | int flags = fcntl(STDIN_FILENO, F_GETFL); 41 | fcntl(STDIN_FILENO, F_SETFL, flags | O_NONBLOCK); 42 | 43 | // We care about two cases: 44 | // Case 1: A sequence of the form 27, 91, X; return X. 45 | // Case 2: For any other sequence, return each int separately. 46 | 47 | *is_end_of_seq = 0; 48 | int ch = getchar(); 49 | if (ch == 27) { 50 | int next = getchar(); 51 | if (next == 91) { 52 | *is_end_of_seq = 1; 53 | ch = getchar(); 54 | goto end; 55 | } 56 | // If we get here, then we're not in a 27, 91, X sequence. 57 | ungetc(next, stdin); 58 | } 59 | 60 | end: 61 | 62 | // Turn off non-blocking I/O. On some systems, leaving stdin 63 | // non-blocking will also leave stdout non-blocking, which can 64 | // cause printing errors. 65 | fcntl(STDIN_FILENO, F_SETFL, flags); 66 | return ch; 67 | } 68 | 69 | void sleephires(double sec) { 70 | long s = (long)floor(sec); 71 | long n = (long)floor((sec - s) * 1e9); 72 | struct timespec delay = { .tv_sec = s, .tv_nsec = n }; 73 | nanosleep(&delay, NULL); 74 | } 75 | 76 | void start() { 77 | // Terminal setup. 78 | system("tput setab 0"); // Use a black background. 79 | system("tput clear"); // Clear the screen. 80 | system("tput civis"); // Hide the cursor. 81 | system("stty raw -echo"); // Improve access to keypresses. 82 | } 83 | 84 | void done() { 85 | 86 | // Put the terminal back into a decent state. 87 | system("stty cooked echo"); // Undo init call to "stty raw". 88 | system("tput reset"); // Reset colors and clear screen. 89 | 90 | exit(0); 91 | } 92 | 93 | void push_keypress(lua_State *L, int key, int is_end_of_seq) { 94 | if (is_end_of_seq && 65 <= key && key <= 68) { 95 | // up, down, right, left = 65, 66, 67, 68 96 | static const char *arrow_names[] = {"up", "down", 97 | "right", "left"}; 98 | lua_pushstring(L, arrow_names[key - 65]); 99 | } else { 100 | lua_pushnumber(L, key); 101 | } 102 | } 103 | 104 | void push_state_table(lua_State *L, 105 | int key, 106 | int is_end_of_seq) { 107 | 108 | lua_newtable(L); 109 | 110 | // stack = [.., {}] 111 | 112 | push_keypress(L, key, is_end_of_seq); 113 | 114 | // stack = [.., {}, key] 115 | 116 | lua_setfield(L, -2, "key"); 117 | 118 | // stack = [.., {key = key}] 119 | 120 | lua_pushnumber(L, gettime()); 121 | 122 | // stack = [.., {key = key}, clock] 123 | 124 | lua_setfield(L, -2, "clock"); 125 | 126 | // stack = [.., {key = key, clock = clock}] 127 | } 128 | 129 | 130 | // Lua-visible functions. 131 | 132 | // Lua: timestamp(). 133 | // Return a high-resolution timestamp in seconds. 134 | int timestamp(lua_State *L) { 135 | lua_pushnumber(L, gettime()); 136 | return 1; 137 | } 138 | 139 | 140 | // Main. 141 | 142 | int main() { 143 | 144 | start(); 145 | 146 | // Create a Lua state and load the module. 147 | lua_State *L = luaL_newstate(); 148 | luaL_openlibs(L); 149 | 150 | // Set up API functions written in C. 151 | lua_register(L, "timestamp", timestamp); 152 | 153 | // Set up API functions written in Lua. 154 | luaL_dofile(L, "util.lua"); 155 | luaL_dofile(L, "Character.lua"); 156 | lua_setglobal(L, "Character"); 157 | 158 | // Load eatyguy6 and run the init() function. 159 | luaL_dofile(L, "eatyguy6.lua"); 160 | lua_setglobal(L, "eatyguy"); 161 | lua_settop(L, 0); 162 | 163 | lua_getglobal(L, "eatyguy"); 164 | lua_getfield(L, -1, "init"); // -1 means stack top. 165 | lua_call(L, 0, 0); // 0, 0 = #args, #retvals 166 | 167 | lua_getglobal(L, "eatyguy"); 168 | while (1) { 169 | int is_end_of_seq; 170 | int key = getkey(&is_end_of_seq); 171 | if (key == ESC_KEY || key == 'q' || key == 'Q') done(); 172 | 173 | // Call eatyguy.loop(state). 174 | lua_getfield(L, -1, "loop"); 175 | push_state_table(L, key, is_end_of_seq); 176 | lua_call(L, 1, 0); 177 | 178 | sleephires(0.016); // Sleep for 16ms. 179 | } 180 | 181 | return 0; 182 | } 183 | -------------------------------------------------------------------------------- /ch4/eatyguy6.lua: -------------------------------------------------------------------------------- 1 | -- eatyguy6.lua 2 | 3 | local eatyguy = {} 4 | 5 | 6 | -- Globals. 7 | 8 | local percent_extra_paths = 15 9 | local grid = nil -- grid[x][y]: falsy = wall. 10 | local grid_w, grid_h = nil, nil 11 | local player = Character:new({pos = {1, 1}, 12 | dir = {1, 0}, 13 | next_dir = {1, 0}}) 14 | 15 | 16 | -- Internal functions. 17 | 18 | local function is_in_bounds(x, y) 19 | return (1 <= x and x <= grid_w and 20 | 1 <= y and y <= grid_h) 21 | end 22 | 23 | local function get_neighbor_directions(x, y, percent_extra) 24 | -- percent_extra is the percent chance of adding extra paths. 25 | percent_extra = percent_extra or 0 26 | local neighbor_directions = {} 27 | local all_directions = {{1, 0}, {-1, 0}, {0, 1}, {0, -1}} 28 | for _, direction in pairs(all_directions) do 29 | local nx, ny = x + 2 * direction[1], y + 2 * direction[2] 30 | local is_extra_ok = (math.random(100) <= percent_extra) 31 | -- Add `direction` if the neighbor is not yet in a path, or 32 | -- if we randomly got an extra ok using percent_extra. 33 | if is_in_bounds(nx, ny) and 34 | (not grid[nx][ny] or is_extra_ok) then 35 | table.insert(neighbor_directions, direction) 36 | end 37 | end 38 | return neighbor_directions 39 | end 40 | 41 | local function drill_path_from(x, y) 42 | grid[x][y] = '. ' 43 | local neighbor_directions = get_neighbor_directions(x, y) 44 | while #neighbor_directions > 0 do 45 | local direction = table.remove(neighbor_directions, 46 | math.random(#neighbor_directions)) 47 | grid[x + direction[1]][y + direction[2]] = '. ' 48 | drill_path_from(x + 2 * direction[1], y + 2 * direction[2]) 49 | neighbor_directions = get_neighbor_directions(x, y, 50 | percent_extra_paths) 51 | end 52 | end 53 | 54 | local move_delta = 0.2 -- seconds 55 | local next_move_time = nil 56 | 57 | local function update(state) 58 | 59 | -- Ensure any dot under the player has been eaten. 60 | local cur_pos = player.pos 61 | grid[cur_pos[1]][cur_pos[2]] = ' ' 62 | 63 | -- Update the next direction if an arrow key was pressed. 64 | local direction_of_key = {left = {-1, 0}, right = {1, 0}, 65 | up = {0, -1}, down = {0, 1}} 66 | local new_dir = direction_of_key[state.key] 67 | if new_dir then player.next_dir = new_dir end 68 | 69 | -- Only move every move_delta seconds. 70 | if next_move_time == nil then 71 | next_move_time = state.clock + move_delta 72 | end 73 | if state.clock < next_move_time then return end 74 | next_move_time = next_move_time + move_delta 75 | 76 | -- It's been at least move_delta seconds since the last 77 | -- time things moved, so let's move them now! 78 | player:move_if_possible(grid) 79 | end 80 | 81 | local function draw(clock) 82 | 83 | -- Choose the sprite to draw. For example, a right-facing 84 | -- player is drawn as '< alternated with '- 85 | local draw_data = { 86 | [ '1,0'] = {"'<", "'-"}, 87 | ['-1,0'] = {">'", "-'"}, 88 | [ '0,1'] = {"^'", "|'"}, 89 | ['0,-1'] = {"v.", "'."} 90 | } 91 | local anim_timestep = 0.2 92 | local dirkey = ('%d,%d'):format(player.dir[1], 93 | player.dir[2]) 94 | -- framekey switches between 1 & 2; basic sprite animation. 95 | local framekey = math.floor(clock / anim_timestep) % 2 + 1 96 | player.chars = draw_data[dirkey][framekey] 97 | 98 | -- Draw the player and baddies. 99 | player:draw(grid) 100 | end 101 | 102 | 103 | -- Public functions. 104 | 105 | function eatyguy.init() 106 | 107 | -- Set up the grid size and pseudorandom number generation. 108 | grid_w, grid_h = 39, 21 109 | math.randomseed(os.time()) 110 | 111 | -- Build the maze. 112 | grid = {} 113 | for x = 1, grid_w do grid[x] = {} end 114 | drill_path_from(1, 1) 115 | 116 | -- Draw the maze. 117 | set_color('f', 7) -- White foreground. 118 | for y = 0, grid_h + 1 do 119 | for x = 0, grid_w + 1 do 120 | if grid[x] and grid[x][y] then 121 | set_color('b', 0) -- Black; open space color. 122 | io.write(grid[x][y]) 123 | else 124 | set_color('b', 4) -- Blue; wall color. 125 | io.write(' ') 126 | end 127 | io.flush() -- Needed for color output. 128 | end 129 | set_color('b', 0) -- End the lines in black. 130 | io.write(' \r\n') -- Move cursor to next row. 131 | end 132 | end 133 | 134 | function eatyguy.loop(state) 135 | update(state) 136 | draw(state.clock) 137 | end 138 | 139 | return eatyguy 140 | -------------------------------------------------------------------------------- /ch4/eatyguy7.c: -------------------------------------------------------------------------------- 1 | // eatyguy7.c 2 | // 3 | // Load eatyguy7.lua and run it in the order below. 4 | // 5 | // -- Lua-ish pseudocode representing the order of events. 6 | // eatyguy.init() 7 | // while true do 8 | // eatyguy.loop(state) -- state has keys 'clock' and 'key'. 9 | // sleep(0.016) 10 | // end 11 | // 12 | 13 | #include "lauxlib.h" 14 | #include "lua.h" 15 | #include "lualib.h" 16 | 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | 25 | // 27 is the decimal representation of Esc in ASCII. 26 | #define ESC_KEY 27 27 | 28 | 29 | // Internal functions. 30 | 31 | double gettime() { 32 | struct timeval tv; 33 | gettimeofday(&tv, NULL); 34 | return tv.tv_sec + 1e-6 * tv.tv_usec; 35 | } 36 | 37 | int getkey(int *is_end_of_seq) { 38 | 39 | // Make reading from stdin non-blocking. 40 | int flags = fcntl(STDIN_FILENO, F_GETFL); 41 | fcntl(STDIN_FILENO, F_SETFL, flags | O_NONBLOCK); 42 | 43 | // We care about two cases: 44 | // Case 1: A sequence of the form 27, 91, X; return X. 45 | // Case 2: For any other sequence, return each int separately. 46 | 47 | *is_end_of_seq = 0; 48 | int ch = getchar(); 49 | if (ch == 27) { 50 | int next = getchar(); 51 | if (next == 91) { 52 | *is_end_of_seq = 1; 53 | ch = getchar(); 54 | goto end; 55 | } 56 | // If we get here, then we're not in a 27, 91, X sequence. 57 | ungetc(next, stdin); 58 | } 59 | 60 | end: 61 | 62 | // Turn off non-blocking I/O. On some systems, leaving stdin 63 | // non-blocking will also leave stdout non-blocking, which can 64 | // cause printing errors. 65 | fcntl(STDIN_FILENO, F_SETFL, flags); 66 | return ch; 67 | } 68 | 69 | void sleephires(double sec) { 70 | long s = (long)floor(sec); 71 | long n = (long)floor((sec - s) * 1e9); 72 | struct timespec delay = { .tv_sec = s, .tv_nsec = n }; 73 | nanosleep(&delay, NULL); 74 | } 75 | 76 | void start() { 77 | // Terminal setup. 78 | system("tput setab 0"); // Use a black background. 79 | system("tput clear"); // Clear the screen. 80 | system("tput civis"); // Hide the cursor. 81 | system("stty raw -echo"); // Improve access to keypresses. 82 | } 83 | 84 | void done() { 85 | 86 | // Put the terminal back into a decent state. 87 | system("stty cooked echo"); // Undo init call to "stty raw". 88 | system("tput reset"); // Reset colors and clear screen. 89 | 90 | exit(0); 91 | } 92 | 93 | void push_keypress(lua_State *L, int key, int is_end_of_seq) { 94 | if (is_end_of_seq && 65 <= key && key <= 68) { 95 | // up, down, right, left = 65, 66, 67, 68 96 | static const char *arrow_names[] = {"up", "down", 97 | "right", "left"}; 98 | lua_pushstring(L, arrow_names[key - 65]); 99 | } else { 100 | lua_pushnumber(L, key); 101 | } 102 | } 103 | 104 | void push_state_table(lua_State *L, 105 | int key, 106 | int is_end_of_seq) { 107 | 108 | lua_newtable(L); 109 | 110 | // stack = [.., {}] 111 | 112 | push_keypress(L, key, is_end_of_seq); 113 | 114 | // stack = [.., {}, key] 115 | 116 | lua_setfield(L, -2, "key"); 117 | 118 | // stack = [.., {key = key}] 119 | 120 | lua_pushnumber(L, gettime()); 121 | 122 | // stack = [.., {key = key}, clock] 123 | 124 | lua_setfield(L, -2, "clock"); 125 | 126 | // stack = [.., {key = key, clock = clock}] 127 | } 128 | 129 | 130 | // Lua-visible functions. 131 | 132 | // Lua: timestamp(). 133 | // Return a high-resolution timestamp in seconds. 134 | int timestamp(lua_State *L) { 135 | lua_pushnumber(L, gettime()); 136 | return 1; 137 | } 138 | 139 | 140 | // Main. 141 | 142 | int main() { 143 | 144 | start(); 145 | 146 | // Create a Lua state and load the module. 147 | lua_State *L = luaL_newstate(); 148 | luaL_openlibs(L); 149 | 150 | // Set up API functions written in C. 151 | lua_register(L, "timestamp", timestamp); 152 | 153 | // Set up API functions written in Lua. 154 | luaL_dofile(L, "util.lua"); 155 | luaL_dofile(L, "Character.lua"); 156 | lua_setglobal(L, "Character"); 157 | 158 | // Load eatyguy7 and run the init() function. 159 | luaL_dofile(L, "eatyguy7.lua"); 160 | lua_setglobal(L, "eatyguy"); 161 | lua_settop(L, 0); 162 | 163 | lua_getglobal(L, "eatyguy"); 164 | lua_getfield(L, -1, "init"); // -1 means stack top. 165 | lua_call(L, 0, 0); // 0, 0 = #args, #retvals 166 | 167 | lua_getglobal(L, "eatyguy"); 168 | while (1) { 169 | int is_end_of_seq; 170 | int key = getkey(&is_end_of_seq); 171 | if (key == ESC_KEY || key == 'q' || key == 'Q') done(); 172 | 173 | // Call eatyguy.loop(state). 174 | lua_getfield(L, -1, "loop"); 175 | push_state_table(L, key, is_end_of_seq); 176 | lua_call(L, 1, 0); 177 | 178 | sleephires(0.016); // Sleep for 16ms. 179 | } 180 | 181 | return 0; 182 | } 183 | -------------------------------------------------------------------------------- /ch4/eatyguy7.lua: -------------------------------------------------------------------------------- 1 | -- eatyguy7.lua 2 | 3 | local eatyguy = {} 4 | 5 | 6 | -- Require modules. 7 | 8 | local Baddy = require 'Baddy' 9 | 10 | 11 | -- Globals. 12 | 13 | local percent_extra_paths = 15 14 | local grid = nil -- grid[x][y]: falsy = wall. 15 | local grid_w, grid_h = nil, nil 16 | local player = Character:new({pos = {1, 1}, 17 | dir = {1, 0}, 18 | next_dir = {1, 0}}) 19 | local baddies = {} 20 | 21 | 22 | -- Internal functions. 23 | 24 | local function is_in_bounds(x, y) 25 | return (1 <= x and x <= grid_w and 26 | 1 <= y and y <= grid_h) 27 | end 28 | 29 | local function get_neighbor_directions(x, y, percent_extra) 30 | -- percent_extra is the percent chance of adding extra paths. 31 | percent_extra = percent_extra or 0 32 | local neighbor_directions = {} 33 | local all_directions = {{1, 0}, {-1, 0}, {0, 1}, {0, -1}} 34 | for _, direction in pairs(all_directions) do 35 | local nx, ny = x + 2 * direction[1], y + 2 * direction[2] 36 | local is_extra_ok = (math.random(100) <= percent_extra) 37 | -- Add `direction` if the neighbor is not yet in a path, or 38 | -- if we randomly got an extra ok using percent_extra. 39 | if is_in_bounds(nx, ny) and 40 | (not grid[nx][ny] or is_extra_ok) then 41 | table.insert(neighbor_directions, direction) 42 | end 43 | end 44 | return neighbor_directions 45 | end 46 | 47 | local function drill_path_from(x, y) 48 | grid[x][y] = '. ' 49 | local neighbor_directions = get_neighbor_directions(x, y) 50 | while #neighbor_directions > 0 do 51 | local direction = table.remove(neighbor_directions, 52 | math.random(#neighbor_directions)) 53 | grid[x + direction[1]][y + direction[2]] = '. ' 54 | drill_path_from(x + 2 * direction[1], y + 2 * direction[2]) 55 | neighbor_directions = get_neighbor_directions(x, y, 56 | percent_extra_paths) 57 | end 58 | end 59 | 60 | local move_delta = 0.2 -- seconds 61 | local next_move_time = nil 62 | 63 | local function update(state) 64 | 65 | -- Ensure any dot under the player has been eaten. 66 | local cur_pos = player.pos 67 | grid[cur_pos[1]][cur_pos[2]] = ' ' 68 | 69 | -- Update the next direction if an arrow key was pressed. 70 | local direction_of_key = {left = {-1, 0}, right = {1, 0}, 71 | up = {0, -1}, down = {0, 1}} 72 | local new_dir = direction_of_key[state.key] 73 | if new_dir then player.next_dir = new_dir end 74 | 75 | -- Only move every move_delta seconds. 76 | if next_move_time == nil then 77 | next_move_time = state.clock + move_delta 78 | end 79 | if state.clock < next_move_time then return end 80 | next_move_time = next_move_time + move_delta 81 | 82 | -- It's been at least move_delta seconds since the last 83 | -- time things moved, so let's move them now! 84 | player:move_if_possible(grid) 85 | for _, baddy in pairs(baddies) do 86 | baddy:move_if_possible(grid) 87 | end 88 | end 89 | 90 | local function draw(clock) 91 | 92 | -- Choose the sprite to draw. For example, a right-facing 93 | -- player is drawn as '< alternated with '- 94 | local draw_data = { 95 | [ '1,0'] = {"'<", "'-"}, 96 | ['-1,0'] = {">'", "-'"}, 97 | [ '0,1'] = {"^'", "|'"}, 98 | ['0,-1'] = {"v.", "'."} 99 | } 100 | local anim_timestep = 0.2 101 | local dirkey = ('%d,%d'):format(player.dir[1], 102 | player.dir[2]) 103 | -- framekey switches between 1 & 2; basic sprite animation. 104 | local framekey = math.floor(clock / anim_timestep) % 2 + 1 105 | player.chars = draw_data[dirkey][framekey] 106 | 107 | -- Draw the player and baddies. 108 | player:draw(grid) 109 | for _, baddy in pairs(baddies) do 110 | baddy:draw(grid) 111 | end 112 | end 113 | 114 | 115 | -- Public functions. 116 | 117 | function eatyguy.init() 118 | 119 | -- Set up the grid size and pseudorandom number generation. 120 | grid_w, grid_h = 39, 21 121 | math.randomseed(os.time()) 122 | 123 | -- Set up the baddies. 124 | local baddy_info = { {color = 1, chars = 'oo', pos = {1, 1}}, 125 | {color = 2, chars = '@@', pos = {1, 0}}, 126 | {color = 5, chars = '^^', pos = {0, 1}} } 127 | for _, info in pairs(baddy_info) do 128 | info.home = {(grid_w - 1) * info.pos[1] + 1, 129 | (grid_h - 1) * info.pos[2] + 1} 130 | table.insert(baddies, Baddy:new(info)) 131 | end 132 | 133 | -- Build the maze. 134 | grid = {} 135 | for x = 1, grid_w do grid[x] = {} end 136 | drill_path_from(1, 1) 137 | 138 | -- Draw the maze. 139 | set_color('f', 7) -- White foreground. 140 | for y = 0, grid_h + 1 do 141 | for x = 0, grid_w + 1 do 142 | if grid[x] and grid[x][y] then 143 | set_color('b', 0) -- Black; open space color. 144 | io.write(grid[x][y]) 145 | else 146 | set_color('b', 4) -- Blue; wall color. 147 | io.write(' ') 148 | end 149 | io.flush() -- Needed for color output. 150 | end 151 | set_color('b', 0) -- End the lines in black. 152 | io.write(' \r\n') -- Move cursor to next row. 153 | end 154 | end 155 | 156 | function eatyguy.loop(state) 157 | update(state) 158 | draw(state.clock) 159 | end 160 | 161 | return eatyguy 162 | -------------------------------------------------------------------------------- /ch4/eatyguy8.c: -------------------------------------------------------------------------------- 1 | // eatyguy8.c 2 | // 3 | // Load eatyguy8.lua and run it in the order below. 4 | // 5 | // -- Lua-ish pseudocode representing the order of events. 6 | // eatyguy.init() 7 | // while true do 8 | // eatyguy.loop(state) -- state has keys 'clock' and 'key'. 9 | // sleep(0.016) 10 | // end 11 | // 12 | 13 | #include "Pair.h" 14 | 15 | #include "lauxlib.h" 16 | #include "lua.h" 17 | #include "lualib.h" 18 | 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | 27 | // 27 is the decimal representation of Esc in ASCII. 28 | #define ESC_KEY 27 29 | 30 | 31 | // Internal functions. 32 | 33 | double gettime() { 34 | struct timeval tv; 35 | gettimeofday(&tv, NULL); 36 | return tv.tv_sec + 1e-6 * tv.tv_usec; 37 | } 38 | 39 | int getkey(int *is_end_of_seq) { 40 | 41 | // Make reading from stdin non-blocking. 42 | int flags = fcntl(STDIN_FILENO, F_GETFL); 43 | fcntl(STDIN_FILENO, F_SETFL, flags | O_NONBLOCK); 44 | 45 | // We care about two cases: 46 | // Case 1: A sequence of the form 27, 91, X; return X. 47 | // Case 2: For any other sequence, return each int separately. 48 | 49 | *is_end_of_seq = 0; 50 | int ch = getchar(); 51 | if (ch == 27) { 52 | int next = getchar(); 53 | if (next == 91) { 54 | *is_end_of_seq = 1; 55 | ch = getchar(); 56 | goto end; 57 | } 58 | // If we get here, then we're not in a 27, 91, X sequence. 59 | ungetc(next, stdin); 60 | } 61 | 62 | end: 63 | 64 | // Turn off non-blocking I/O. On some systems, leaving stdin 65 | // non-blocking will also leave stdout non-blocking, which can 66 | // cause printing errors. 67 | fcntl(STDIN_FILENO, F_SETFL, flags); 68 | return ch; 69 | } 70 | 71 | void sleephires(double sec) { 72 | long s = (long)floor(sec); 73 | long n = (long)floor((sec - s) * 1e9); 74 | struct timespec delay = { .tv_sec = s, .tv_nsec = n }; 75 | nanosleep(&delay, NULL); 76 | } 77 | 78 | void start() { 79 | // Terminal setup. 80 | system("tput setab 0"); // Use a black background. 81 | system("tput clear"); // Clear the screen. 82 | system("tput civis"); // Hide the cursor. 83 | system("stty raw -echo"); // Improve access to keypresses. 84 | } 85 | 86 | void done() { 87 | 88 | // Put the terminal back into a decent state. 89 | system("stty cooked echo"); // Undo init call to "stty raw". 90 | system("tput reset"); // Reset colors and clear screen. 91 | 92 | exit(0); 93 | } 94 | 95 | void push_keypress(lua_State *L, int key, int is_end_of_seq) { 96 | if (is_end_of_seq && 65 <= key && key <= 68) { 97 | // up, down, right, left = 65, 66, 67, 68 98 | static const char *arrow_names[] = {"up", "down", 99 | "right", "left"}; 100 | lua_pushstring(L, arrow_names[key - 65]); 101 | } else { 102 | lua_pushnumber(L, key); 103 | } 104 | } 105 | 106 | void push_state_table(lua_State *L, 107 | int key, 108 | int is_end_of_seq) { 109 | 110 | lua_newtable(L); 111 | 112 | // stack = [.., {}] 113 | 114 | push_keypress(L, key, is_end_of_seq); 115 | 116 | // stack = [.., {}, key] 117 | 118 | lua_setfield(L, -2, "key"); 119 | 120 | // stack = [.., {key = key}] 121 | 122 | lua_pushnumber(L, gettime()); 123 | 124 | // stack = [.., {key = key}, clock] 125 | 126 | lua_setfield(L, -2, "clock"); 127 | 128 | // stack = [.., {key = key, clock = clock}] 129 | } 130 | 131 | 132 | // Lua-visible functions. 133 | 134 | // Lua: timestamp(). 135 | // Return a high-resolution timestamp in seconds. 136 | int timestamp(lua_State *L) { 137 | lua_pushnumber(L, gettime()); 138 | return 1; 139 | } 140 | 141 | 142 | // Main. 143 | 144 | int main() { 145 | 146 | start(); 147 | 148 | // Create a Lua state and load the module. 149 | lua_State *L = luaL_newstate(); 150 | luaL_openlibs(L); 151 | 152 | // Set up API functions written in C. 153 | lua_register(L, "timestamp", timestamp); 154 | luaopen_Pair(L); 155 | lua_setglobal(L, "Pair"); 156 | 157 | // Set up API functions written in Lua. 158 | luaL_dofile(L, "util.lua"); 159 | luaL_dofile(L, "Character.lua"); 160 | lua_setglobal(L, "Character"); 161 | 162 | // Load eatyguy8 and run the init() function. 163 | luaL_dofile(L, "eatyguy8.lua"); 164 | lua_setglobal(L, "eatyguy"); 165 | lua_settop(L, 0); 166 | 167 | lua_getglobal(L, "eatyguy"); 168 | lua_getfield(L, -1, "init"); // -1 means stack top. 169 | lua_call(L, 0, 0); // 0, 0 = #args, #retvals 170 | 171 | lua_getglobal(L, "eatyguy"); 172 | while (1) { 173 | int is_end_of_seq; 174 | int key = getkey(&is_end_of_seq); 175 | if (key == ESC_KEY || key == 'q' || key == 'Q') done(); 176 | 177 | // Call eatyguy.loop(state). 178 | lua_getfield(L, -1, "loop"); 179 | push_state_table(L, key, is_end_of_seq); 180 | lua_call(L, 1, 0); 181 | 182 | sleephires(0.016); // Sleep for 16ms. 183 | } 184 | 185 | return 0; 186 | } 187 | -------------------------------------------------------------------------------- /ch4/eatyguy8.lua: -------------------------------------------------------------------------------- 1 | -- eatyguy8.lua 2 | 3 | local eatyguy = {} 4 | 5 | 6 | -- Require modules. 7 | 8 | local Baddy = require 'Baddy' 9 | 10 | 11 | -- Convenience functions. 12 | 13 | -- Expect a Pair or a table; if it's a table, convert to a Pair. 14 | local function pair(t) 15 | -- This calls Pair:new() only if t is a table. 16 | return (type(t) == 'table') and Pair:new(t) or t 17 | end 18 | 19 | 20 | -- Globals. 21 | 22 | local percent_extra_paths = 15 23 | local grid = nil -- grid[x][y]: falsy = wall. 24 | local grid_w, grid_h = nil, nil 25 | local player = Character:new({pos = pair{1, 1}, 26 | dir = pair{1, 0}, 27 | next_dir = pair{1, 0}}) 28 | local baddies = {} 29 | 30 | 31 | -- Internal functions. 32 | 33 | local function is_in_bounds(pt) 34 | return (1 <= pt.x and pt.x <= grid_w and 35 | 1 <= pt.y and pt.y <= grid_h) 36 | end 37 | 38 | local function get_neighbor_directions(pt, percent_extra) 39 | -- percent_extra is the percent chance of adding extra paths. 40 | percent_extra = percent_extra or 0 41 | local neighbor_directions = {} 42 | local all_directions = {pair{1, 0}, pair{-1, 0}, 43 | pair{0, 1}, pair{0, -1}} 44 | for _, direction in pairs(all_directions) do 45 | local n_pt = pt + direction * 2 -- The neighbor point. 46 | local is_extra_ok = (math.random(100) <= percent_extra) 47 | -- Add `direction` if the neighbor is not yet in a path, or 48 | -- if we randomly got an extra ok using percent_extra. 49 | if is_in_bounds(n_pt) and 50 | (not grid[n_pt.x][n_pt.y] or is_extra_ok) then 51 | table.insert(neighbor_directions, direction) 52 | end 53 | end 54 | return neighbor_directions 55 | end 56 | 57 | local function drill_path_from(pt) 58 | grid[pt.x][pt.y] = '. ' 59 | local neighbor_directions = get_neighbor_directions(pt) 60 | while #neighbor_directions > 0 do 61 | local direction = table.remove(neighbor_directions, 62 | math.random(#neighbor_directions)) 63 | grid[pt.x + direction.x][pt.y + direction.y] = '. ' 64 | drill_path_from(pt + direction * 2) 65 | neighbor_directions = get_neighbor_directions(pt, 66 | percent_extra_paths) 67 | end 68 | end 69 | 70 | local move_delta = 0.2 -- seconds 71 | local next_move_time = nil 72 | 73 | local function update(state) 74 | 75 | -- Ensure any dot under the player has been eaten. 76 | local pt = pair(player.pos) 77 | grid[pt.x][pt.y] = ' ' 78 | 79 | -- Update the next direction if an arrow key was pressed. 80 | local direction_of_key = {left = pair{-1, 0}, 81 | right = pair{1, 0}, 82 | up = pair{0, -1}, 83 | down = pair{0, 1}} 84 | local new_dir = direction_of_key[state.key] 85 | if new_dir then player.next_dir = new_dir end 86 | 87 | -- Only move every move_delta seconds. 88 | if next_move_time == nil then 89 | next_move_time = state.clock + move_delta 90 | end 91 | if state.clock < next_move_time then return end 92 | next_move_time = next_move_time + move_delta 93 | 94 | -- It's been at least move_delta seconds since the last 95 | -- time things moved, so let's move them now! 96 | player:move_if_possible(grid) 97 | for _, baddy in pairs(baddies) do 98 | baddy:move_if_possible(grid) 99 | end 100 | end 101 | 102 | local function draw(clock) 103 | 104 | -- Choose the sprite to draw. For example, a right-facing 105 | -- player is drawn as '< alternated with '- 106 | local draw_data = { 107 | [ '1,0'] = {"'<", "'-"}, 108 | ['-1,0'] = {">'", "-'"}, 109 | [ '0,1'] = {"^'", "|'"}, 110 | ['0,-1'] = {"v.", "'."} 111 | } 112 | local anim_timestep = 0.2 113 | local dirkey = ('%d,%d'):format(player.dir[1], 114 | player.dir[2]) 115 | -- framekey switches between 1 & 2; basic sprite animation. 116 | local framekey = math.floor(clock / anim_timestep) % 2 + 1 117 | player.chars = draw_data[dirkey][framekey] 118 | 119 | -- Draw the player and baddies. 120 | player:draw(grid) 121 | for _, baddy in pairs(baddies) do 122 | baddy:draw(grid) 123 | end 124 | end 125 | 126 | 127 | -- Public functions. 128 | 129 | function eatyguy.init() 130 | 131 | -- Set up the grid size and pseudorandom number generation. 132 | grid_w, grid_h = 39, 21 133 | math.randomseed(os.time()) 134 | 135 | -- Set up the baddies. 136 | local baddy_info = { {color = 1, chars = 'oo', pos = {1, 1}}, 137 | {color = 2, chars = '@@', pos = {1, 0}}, 138 | {color = 5, chars = '^^', pos = {0, 1}} } 139 | for _, info in pairs(baddy_info) do 140 | info.home = pair{(grid_w - 1) * info.pos[1] + 1, 141 | (grid_h - 1) * info.pos[2] + 1} 142 | table.insert(baddies, Baddy:new(info)) 143 | end 144 | 145 | -- Build the maze. 146 | grid = {} 147 | for x = 1, grid_w do grid[x] = {} end 148 | drill_path_from(pair{1, 1}) 149 | 150 | -- Draw the maze. 151 | set_color('f', 7) -- White foreground. 152 | for y = 0, grid_h + 1 do 153 | for x = 0, grid_w + 1 do 154 | if grid[x] and grid[x][y] then 155 | set_color('b', 0) -- Black; open space color. 156 | io.write(grid[x][y]) 157 | else 158 | set_color('b', 4) -- Blue; wall color. 159 | io.write(' ') 160 | end 161 | io.flush() -- Needed for color output. 162 | end 163 | set_color('b', 0) -- End the lines in black. 164 | io.write(' \r\n') -- Move cursor to next row. 165 | end 166 | end 167 | 168 | function eatyguy.loop(state) 169 | update(state) 170 | draw(state.clock) 171 | end 172 | 173 | return eatyguy 174 | -------------------------------------------------------------------------------- /ch4/makefile: -------------------------------------------------------------------------------- 1 | # Chapter 4 makefile. 2 | # 3 | # To build all the binaries for this chapter, simply run: 4 | # $ make 5 | # 6 | # To remove the build products, including binaries, run: 7 | # $ make clean 8 | # 9 | 10 | eatyguys = eatyguy6 eatyguy7 eatyguy8 11 | obj_files = Pair.o cmodule.o 12 | so_files = Pair.$(so_ext) cmodule.$(so_ext) 13 | 14 | include ../build_settings.make 15 | -------------------------------------------------------------------------------- /ch4/test_pair.lua: -------------------------------------------------------------------------------- 1 | -- test_pair.lua 2 | -- 3 | -- A simple test to help verify that the Pair class is 4 | -- working as desired. 5 | -- 6 | 7 | local Pair = require 'Pair' 8 | 9 | local p = Pair:new{ 1, 2} 10 | local q = Pair:new{-1.5, 3} 11 | 12 | assert(p.x == 1) 13 | assert(q.x == -1.5) 14 | 15 | local r = p + q 16 | 17 | assert(r.x == -0.5) 18 | assert(r.y == 5) 19 | 20 | local s = Pair:new{-0.5, 5} 21 | 22 | assert(r == s) 23 | assert(p ~= q) 24 | 25 | -- If we get here, then no assert failed. 26 | print('Test passed!') 27 | -------------------------------------------------------------------------------- /ch4/util.lua: -------------------------------------------------------------------------------- 1 | -- util.lua 2 | -- 3 | -- Define the functions set_color and set_pos so that they cache 4 | -- the strings returned from tput in order to reduce the number 5 | -- of tput calls. 6 | 7 | 8 | -- Local functions won't be globally visible after this script 9 | -- completes. 10 | 11 | local cached_strs = {} -- Maps cmd -> str. 12 | 13 | local function cached_cmd(cmd) 14 | if not cached_strs[cmd] then 15 | local p = io.popen(cmd) 16 | cached_strs[cmd] = p:read() 17 | p:close() 18 | end 19 | io.write(cached_strs[cmd]) 20 | end 21 | 22 | 23 | -- Global functions will remain visible after this script 24 | -- completes. 25 | 26 | function set_color(b_or_f, color) 27 | assert(b_or_f == 'b' or b_or_f == 'f') 28 | cached_cmd('tput seta' .. b_or_f .. ' ' .. color) 29 | end 30 | 31 | function set_pos(x, y) 32 | cached_cmd(('tput cup %d %d'):format(y, x)) 33 | end 34 | -------------------------------------------------------------------------------- /ch5/.gitignore: -------------------------------------------------------------------------------- 1 | lua_error 2 | lua_pcall 3 | eatyguy9 4 | -------------------------------------------------------------------------------- /ch5/Baddy.lua: -------------------------------------------------------------------------------- 1 | -- Baddy.lua 2 | -- 3 | -- A subclass of Character to capture behavior 4 | -- specific to baddies. 5 | -- 6 | 7 | local Character = require 'Character' 8 | 9 | -- Set a new direction and simultaneously update baddy.next_dir 10 | -- to be a right or left turn from new_dir. 11 | -- This function cannot be seen outside the Baddy module. 12 | local function set_new_dir(baddy, new_dir) 13 | baddy.dir = new_dir 14 | local sign = math.random(2) * 2 - 3 -- Either -1 or +1. 15 | baddy.next_dir = {sign * baddy.dir[2], -sign * baddy.dir[1]} 16 | end 17 | 18 | local Baddy = Character:new() 19 | 20 | -- Set up a new baddy. 21 | -- This expects a table with keys {home, color, chars}. 22 | function Baddy:new(b) 23 | assert(b) -- Require a table of initial values. 24 | b.pos = b.home 25 | b.dir = {-1, 0} 26 | b.next_dir = {-1, 0} 27 | self.__index = self 28 | return setmetatable(b, self) 29 | end 30 | 31 | function Baddy:move_if_possible(grid) 32 | local deltas = {{-1, 0}, {1, 0}, {0, -1}, {0, 1}} 33 | 34 | -- Try to change direction; otherwise the next_dir will take 35 | -- effect when we're at a turn in that direction. 36 | if self:can_move_in_dir(self.next_dir, grid) then 37 | set_new_dir(self, self.next_dir) 38 | end 39 | 40 | -- Try to move in self.dir; if that doesn't work, randomly 41 | -- change directions till we can move again. 42 | local can_move, new_pos = self:can_move_in_dir(self.dir, grid) 43 | while not can_move do 44 | set_new_dir(self, deltas[math.random(4)]) 45 | can_move, new_pos = self:can_move_in_dir(self.dir, grid) 46 | end 47 | self.old_pos = self.pos -- Save the old position. 48 | self.pos = new_pos 49 | end 50 | 51 | return Baddy 52 | -------------------------------------------------------------------------------- /ch5/Character.lua: -------------------------------------------------------------------------------- 1 | -- Character.lua 2 | -- 3 | -- A class to capture behavior of general 4 | -- characters. 5 | -- 6 | 7 | Character = {} 8 | 9 | function Character:new(c) 10 | c = c or {} 11 | self.__index = self 12 | return setmetatable(c, self) 13 | end 14 | 15 | function Character:can_move_in_dir(dir, grid) 16 | local pos = self.pos 17 | local grid_x, grid_y = pos[1] + dir[1], pos[2] + dir[2] 18 | local can_move = grid[grid_x] and grid[grid_x][grid_y] 19 | return can_move, {grid_x, grid_y} 20 | end 21 | 22 | function Character:move_if_possible(grid) 23 | -- Try to change direction; if we can't, next_dir will take 24 | -- effect at a corner where we can turn in that direction. 25 | if self:can_move_in_dir(self.next_dir, grid) then 26 | self.dir = self.next_dir 27 | end 28 | 29 | -- Move in direction self.dir if possible. 30 | local can_move, new_pos = self:can_move_in_dir(self.dir, grid) 31 | if can_move then 32 | self.old_pos = self.pos -- Save the old position. 33 | self.pos = new_pos 34 | end 35 | end 36 | 37 | function Character:draw(grid) 38 | 39 | -- Draw the character. 40 | local color = self.color or 3 -- Default to yellow. 41 | set_color('b', color) 42 | set_color('f', 0) -- Black foreground. 43 | local x = 2 * self.pos[1] 44 | local y = self.pos[2] 45 | set_pos(x, y) 46 | io.write(self.chars) 47 | io.flush() 48 | 49 | -- Erase the old character pos if appropriate. 50 | if self.old_pos then 51 | local op = self.old_pos 52 | local x = 2 * op[1] 53 | local y = op[2] 54 | set_pos(x, y) 55 | set_color('f', 7) -- White foreground. 56 | set_color('b', 0) -- Black background. 57 | io.write(grid[op[1]][op[2]]) 58 | io.flush() 59 | self.old_pos = nil 60 | end 61 | end 62 | 63 | return Character 64 | -------------------------------------------------------------------------------- /ch5/Pair.h: -------------------------------------------------------------------------------- 1 | // Pair.h 2 | // 3 | // Pair is a simple userdata type, defined in C, which supports 4 | // basic operations on (x, y) pairs. 5 | // 6 | // Example usage: 7 | // 8 | // local Pair = require 'Pair' 9 | // 10 | // local p = Pair:new {1, 2} 11 | // local q = Pair:new {3, 4} 12 | // local r = p + q * 2 -- Now r = {7, 10}. 13 | // 14 | 15 | #include "lua.h" 16 | 17 | int luaopen_Pair(lua_State *L); 18 | -------------------------------------------------------------------------------- /ch5/eatyguy9.c: -------------------------------------------------------------------------------- 1 | // eatyguy9.c 2 | // 3 | // Load eatyguy9.lua and run it in the order below. 4 | // 5 | // -- Lua-ish pseudocode representing the order of events. 6 | // eatyguy.init() 7 | // while true do 8 | // eatyguy.loop(state) -- state has keys 'clock' and 'key'. 9 | // sleep(0.016) 10 | // end 11 | // 12 | 13 | #include "Pair.h" 14 | 15 | #include "lauxlib.h" 16 | #include "lua.h" 17 | #include "lualib.h" 18 | 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | 27 | // 27 is the decimal representation of Esc in ASCII. 28 | #define ESC_KEY 27 29 | 30 | 31 | // Internal functions. 32 | 33 | double gettime() { 34 | struct timeval tv; 35 | gettimeofday(&tv, NULL); 36 | return tv.tv_sec + 1e-6 * tv.tv_usec; 37 | } 38 | 39 | int getkey(int *is_end_of_seq) { 40 | 41 | // Make reading from stdin non-blocking. 42 | int flags = fcntl(STDIN_FILENO, F_GETFL); 43 | fcntl(STDIN_FILENO, F_SETFL, flags | O_NONBLOCK); 44 | 45 | // We care about two cases: 46 | // Case 1: A sequence of the form 27, 91, X; return X. 47 | // Case 2: For any other sequence, return each int separately. 48 | 49 | *is_end_of_seq = 0; 50 | int ch = getchar(); 51 | if (ch == 27) { 52 | int next = getchar(); 53 | if (next == 91) { 54 | *is_end_of_seq = 1; 55 | ch = getchar(); 56 | goto end; 57 | } 58 | // If we get here, then we're not in a 27, 91, X sequence. 59 | ungetc(next, stdin); 60 | } 61 | 62 | end: 63 | 64 | // Turn off non-blocking I/O. On some systems, leaving stdin 65 | // non-blocking will also leave stdout non-blocking, which can 66 | // cause printing errors. 67 | fcntl(STDIN_FILENO, F_SETFL, flags); 68 | return ch; 69 | } 70 | 71 | void sleephires(double sec) { 72 | long s = (long)floor(sec); 73 | long n = (long)floor((sec - s) * 1e9); 74 | struct timespec delay = { .tv_sec = s, .tv_nsec = n }; 75 | nanosleep(&delay, NULL); 76 | } 77 | 78 | void start() { 79 | // Terminal setup. 80 | system("tput setab 0"); // Use a black background. 81 | system("tput clear"); // Clear the screen. 82 | system("tput civis"); // Hide the cursor. 83 | system("stty raw -echo"); // Improve access to keypresses. 84 | } 85 | 86 | void done(const char *msg) { 87 | 88 | // Put the terminal back into a decent state. 89 | system("stty cooked echo"); // Undo init call to "stty raw". 90 | system("tput reset"); // Reset colors and clear screen. 91 | 92 | // Print the farewell message if there is one. 93 | if (msg) printf("%s\n", msg); 94 | 95 | exit(0); 96 | } 97 | 98 | void push_keypress(lua_State *L, int key, int is_end_of_seq) { 99 | if (is_end_of_seq && 65 <= key && key <= 68) { 100 | // up, down, right, left = 65, 66, 67, 68 101 | static const char *arrow_names[] = {"up", "down", 102 | "right", "left"}; 103 | lua_pushstring(L, arrow_names[key - 65]); 104 | } else { 105 | lua_pushnumber(L, key); 106 | } 107 | } 108 | 109 | void push_state_table(lua_State *L, 110 | int key, 111 | int is_end_of_seq) { 112 | 113 | lua_newtable(L); 114 | 115 | // stack = [.., {}] 116 | 117 | push_keypress(L, key, is_end_of_seq); 118 | 119 | // stack = [.., {}, key] 120 | 121 | lua_setfield(L, -2, "key"); 122 | 123 | // stack = [.., {key = key}] 124 | 125 | lua_pushnumber(L, gettime()); 126 | 127 | // stack = [.., {key = key}, clock] 128 | 129 | lua_setfield(L, -2, "clock"); 130 | 131 | // stack = [.., {key = key, clock = clock}] 132 | } 133 | 134 | 135 | // Lua-visible functions. 136 | 137 | // Lua: timestamp(). 138 | // Return a high-resolution timestamp in seconds. 139 | int timestamp(lua_State *L) { 140 | lua_pushnumber(L, gettime()); 141 | return 1; 142 | } 143 | 144 | 145 | // Main. 146 | 147 | int main() { 148 | 149 | start(); 150 | 151 | // Create a Lua state and load the module. 152 | lua_State *L = luaL_newstate(); 153 | luaL_openlibs(L); 154 | 155 | // Set up API functions written in C. 156 | lua_register(L, "timestamp", timestamp); 157 | luaopen_Pair(L); 158 | lua_setglobal(L, "Pair"); 159 | 160 | // Set up API functions written in Lua. 161 | luaL_dofile(L, "util.lua"); 162 | luaL_dofile(L, "Character.lua"); 163 | lua_setglobal(L, "Character"); 164 | 165 | // Load eatyguy9 and run the init() function. 166 | luaL_dofile(L, "eatyguy9.lua"); 167 | lua_setglobal(L, "eatyguy"); 168 | lua_settop(L, 0); 169 | 170 | lua_getglobal(L, "eatyguy"); 171 | lua_getfield(L, -1, "init"); // -1 means stack top. 172 | lua_call(L, 0, 0); // 0, 0 = #args, #retvals 173 | 174 | lua_getglobal(L, "eatyguy"); 175 | while (1) { 176 | int is_end_of_seq; 177 | int key = getkey(&is_end_of_seq); 178 | 179 | // Pass NULL to done() to print no ending message. 180 | if (key == ESC_KEY || key == 'q' || key == 'Q') done(NULL); 181 | 182 | // Call eatyguy.loop(state). 183 | lua_getfield(L, -1, "loop"); 184 | push_state_table(L, key, is_end_of_seq); 185 | lua_call(L, 1, 1); 186 | 187 | // Check to see if the game is over. 188 | if (lua_isstring(L, -1)) { 189 | const char *msg = lua_tostring(L, -1); 190 | done(msg); 191 | } 192 | 193 | // Pop the return value of eatyguy.loop() off the stack. 194 | lua_pop(L, 1); 195 | 196 | sleephires(0.016); // Sleep for 16ms. 197 | } 198 | 199 | return 0; 200 | } 201 | -------------------------------------------------------------------------------- /ch5/keep_calm.lua: -------------------------------------------------------------------------------- 1 | -- keep_calm.lua 2 | 3 | local function low() 4 | error('thrown from low') 5 | end 6 | 7 | local function middle() 8 | low() 9 | end 10 | 11 | local function high() 12 | middle() 13 | end 14 | 15 | print(pcall(high)) 16 | print('Done!') 17 | -------------------------------------------------------------------------------- /ch5/lua_error.c: -------------------------------------------------------------------------------- 1 | // lua_error.c 2 | 3 | #include "lauxlib.h" 4 | #include "lua.h" 5 | #include "lualib.h" 6 | 7 | #include 8 | 9 | int fn(lua_State *L) { 10 | lua_pushstring(L, "thrown from fn()!"); 11 | lua_error(L); 12 | return 1; 13 | } 14 | 15 | int main() { 16 | 17 | lua_State *L = luaL_newstate(); 18 | luaL_openlibs(L); 19 | lua_register(L, "fn", fn); 20 | 21 | // Call fn(). 22 | lua_getglobal(L, "fn"); 23 | lua_call(L, 0, 0); 24 | 25 | printf("Done!\n"); 26 | 27 | return 0; 28 | } 29 | -------------------------------------------------------------------------------- /ch5/lua_pcall.c: -------------------------------------------------------------------------------- 1 | // lua_pcall.c 2 | 3 | #include "lauxlib.h" 4 | #include "lua.h" 5 | #include "lualib.h" 6 | 7 | #include 8 | 9 | int my_error_handler(lua_State *L) { 10 | // Push a stack trace string onto the stack. 11 | // This augmented string will effectively replace the simpler 12 | // error message that comes directly from the Lua error. 13 | luaL_traceback(L, L, lua_tostring(L, -1), 1); 14 | return 1; 15 | } 16 | 17 | int fn_that_throws(lua_State *L) { 18 | lua_pushstring(L, "thrown from fn()!"); 19 | lua_error(L); 20 | return 1; 21 | } 22 | 23 | int middle_fn(lua_State *L) { 24 | lua_getglobal(L, "fn_that_throws"); 25 | lua_call(L, 0, 0); 26 | return 0; 27 | } 28 | 29 | int main() { 30 | 31 | lua_State *L = luaL_newstate(); 32 | luaL_openlibs(L); 33 | 34 | lua_register(L, "my_error_handler", my_error_handler); 35 | lua_register(L, "fn_that_throws", fn_that_throws); 36 | lua_register(L, "middle_fn", middle_fn); 37 | 38 | // Call middle_fn(). 39 | lua_pushcfunction(L, my_error_handler); 40 | lua_getglobal(L, "middle_fn"); 41 | int status = lua_pcall(L, 0, 0, -2); 42 | if (status != LUA_OK) { 43 | // Print the error. 44 | printf("Looks like lua_pcall() caught an error:\n%s\n", 45 | lua_tostring(L, -1)); 46 | // Pop the error message from the stack. 47 | lua_pop(L, 1); 48 | } 49 | 50 | printf("Done!\n"); 51 | 52 | return 0; 53 | } 54 | -------------------------------------------------------------------------------- /ch5/makefile: -------------------------------------------------------------------------------- 1 | # Chapter 5 makefile. 2 | # 3 | # To build all the binaries for this chapter, simply run: 4 | # $ make 5 | # 6 | # To remove the build products, including binaries, run: 7 | # $ make clean 8 | # 9 | 10 | eatyguys = eatyguy9 11 | binaries = lua_error lua_pcall 12 | obj_files = Pair.o 13 | 14 | include ../build_settings.make 15 | -------------------------------------------------------------------------------- /ch5/panic.lua: -------------------------------------------------------------------------------- 1 | -- panic.lua 2 | 3 | local function low() 4 | error('thrown from low') 5 | end 6 | 7 | local function middle() 8 | low() 9 | end 10 | 11 | local function high() 12 | middle() 13 | end 14 | 15 | high() 16 | print('Done!') 17 | -------------------------------------------------------------------------------- /ch5/strict.lua: -------------------------------------------------------------------------------- 1 | --[[ strict.lua 2 | 3 | Tested with Lua 5.2 and 5.3; not designed for 5.1 or earlier. 4 | 5 | After the user makes the following statement: 6 | 7 | local _ENV = strict.new_env() 8 | 9 | their code will throw an error on either of these conditions: 10 | 11 | * A global variable is created from a nested code block, such 12 | as within a function, or 13 | * An undeclared global is referenced. 14 | 15 | Alternatively, call: 16 | 17 | strict.add_checks(_G) 18 | 19 | to throw errors on all bad global name uses, not just for the 20 | current file. 21 | 22 | There are a number of files similar to this one available 23 | online, as well as described in the book Programming in Lua by 24 | Roberto Ierusalimschy. This happens by be a version I like. 25 | 26 | --]] 27 | 28 | local strict = {} 29 | 30 | -- This replaces the __index and __newindex metamethods on the 31 | -- given table; this is designed for use with env set to either 32 | -- _ENV or _G. 33 | function strict.add_checks(env) 34 | 35 | -- Get t's metatable, creating it if needed. 36 | local mt = getmetatable(env) 37 | if mt == nil then 38 | mt = {} 39 | setmetatable(env, mt) 40 | end 41 | 42 | -- Set up the declared table to be an upvalue for the __index 43 | -- and __newindex closures created below. 44 | local declared = {} 45 | 46 | -- Throw an error for global assignments in non-main Lua code. 47 | mt.__newindex = function (t, k, v) 48 | if not declared[k] then 49 | -- The values (2, 'S') ask for (S for) source info on the 50 | -- function at level 2 in the stack; the one making the 51 | -- assignment. 52 | local w = debug.getinfo(2, 'S').what 53 | if w ~= 'main' and w ~= 'C' then 54 | local fmt = 'Assignment to undeclared global "%s"' 55 | -- The value 2 will blame the error on the code making 56 | -- the bad assignment; i.e. the caller of __newindex(). 57 | error(fmt:format(k), 2) 58 | end 59 | declared[k] = true 60 | end 61 | -- Make the actual assignment in the table t. 62 | rawset(t, k, v) 63 | end 64 | 65 | -- Throw an error for references to undeclared global names. 66 | mt.__index = function (t, k) 67 | if not declared[k] then 68 | -- The parameter 2 will blame the error on the code making 69 | -- the bad lookup; i.e. the caller of __index(). 70 | error(('Use of undeclared global "%s"'):format(k), 2) 71 | end 72 | -- We won't always get an error; finish the lookup on key k. 73 | return rawget(t, k) 74 | end 75 | end 76 | 77 | -- This returns a replacement for _ENV that detects errors. 78 | function strict.new_env() 79 | 80 | -- Set up the new environment with the values from _ENV. 81 | local env = {} 82 | for key, value in pairs(_ENV) do 83 | env[key] = value 84 | end 85 | 86 | -- Add the checks and return the new environment. 87 | strict.add_checks(env) 88 | return env 89 | end 90 | 91 | return strict 92 | -------------------------------------------------------------------------------- /ch5/util.lua: -------------------------------------------------------------------------------- 1 | -- util.lua 2 | -- 3 | -- Define the functions set_color and set_pos so that they cache 4 | -- the strings returned from tput in order to reduce the number 5 | -- of tput calls. 6 | 7 | 8 | -- Local functions won't be globally visible after this script 9 | -- completes. 10 | 11 | local cached_strs = {} -- Maps cmd -> str. 12 | 13 | local function cached_cmd(cmd) 14 | if not cached_strs[cmd] then 15 | local p = io.popen(cmd) 16 | cached_strs[cmd] = p:read() 17 | p:close() 18 | end 19 | io.write(cached_strs[cmd]) 20 | end 21 | 22 | 23 | -- Global functions will remain visible after this script 24 | -- completes. 25 | 26 | function set_color(b_or_f, color) 27 | assert(b_or_f == 'b' or b_or_f == 'f') 28 | cached_cmd('tput seta' .. b_or_f .. ' ' .. color) 29 | end 30 | 31 | function set_pos(x, y) 32 | cached_cmd(('tput cup %d %d'):format(y, x)) 33 | end 34 | -------------------------------------------------------------------------------- /ch6/.gitignore: -------------------------------------------------------------------------------- 1 | eatyguy10 2 | track_memory 3 | limit_memory 4 | library_subset 5 | limit_cpu 6 | -------------------------------------------------------------------------------- /ch6/Baddy.lua: -------------------------------------------------------------------------------- 1 | -- Baddy.lua 2 | -- 3 | -- A subclass of Character to capture behavior 4 | -- specific to baddies. 5 | -- 6 | 7 | local Character = require 'Character' 8 | 9 | -- Set a new direction and simultaneously update baddy.next_dir 10 | -- to be a right or left turn from new_dir. 11 | -- This function cannot be seen outside the Baddy module. 12 | local function set_new_dir(baddy, new_dir) 13 | baddy.dir = new_dir 14 | local sign = math.random(2) * 2 - 3 -- Either -1 or +1. 15 | baddy.next_dir = {sign * baddy.dir[2], -sign * baddy.dir[1]} 16 | end 17 | 18 | local Baddy = Character:new() 19 | 20 | -- Set up a new baddy. 21 | -- This expects a table with keys {home, color, chars}. 22 | function Baddy:new(b) 23 | assert(b) -- Require a table of initial values. 24 | b.pos = b.home 25 | b.dir = {-1, 0} 26 | b.next_dir = {-1, 0} 27 | self.__index = self 28 | return setmetatable(b, self) 29 | end 30 | 31 | function Baddy:move_if_possible(grid) 32 | local deltas = {{-1, 0}, {1, 0}, {0, -1}, {0, 1}} 33 | 34 | -- Try to change direction; otherwise the next_dir will take 35 | -- effect when we're at a turn in that direction. 36 | if self:can_move_in_dir(self.next_dir, grid) then 37 | set_new_dir(self, self.next_dir) 38 | end 39 | 40 | -- Try to move in self.dir; if that doesn't work, randomly 41 | -- change directions till we can move again. 42 | local can_move, new_pos = self:can_move_in_dir(self.dir, grid) 43 | while not can_move do 44 | set_new_dir(self, deltas[math.random(4)]) 45 | can_move, new_pos = self:can_move_in_dir(self.dir, grid) 46 | end 47 | self.old_pos = self.pos -- Save the old position. 48 | self.pos = new_pos 49 | end 50 | 51 | return Baddy 52 | -------------------------------------------------------------------------------- /ch6/Character.lua: -------------------------------------------------------------------------------- 1 | -- Character.lua 2 | -- 3 | -- A class to capture behavior of general 4 | -- characters. 5 | -- 6 | 7 | Character = {} 8 | 9 | function Character:new(c) 10 | c = c or {} 11 | self.__index = self 12 | return setmetatable(c, self) 13 | end 14 | 15 | function Character:can_move_in_dir(dir, grid) 16 | local pos = self.pos 17 | local grid_x, grid_y = pos[1] + dir[1], pos[2] + dir[2] 18 | local can_move = grid[grid_x] and grid[grid_x][grid_y] 19 | return can_move, {grid_x, grid_y} 20 | end 21 | 22 | function Character:move_if_possible(grid) 23 | -- Try to change direction; if we can't, next_dir will take 24 | -- effect at a corner where we can turn in that direction. 25 | if self:can_move_in_dir(self.next_dir, grid) then 26 | self.dir = self.next_dir 27 | end 28 | 29 | -- Move in direction self.dir if possible. 30 | local can_move, new_pos = self:can_move_in_dir(self.dir, grid) 31 | if can_move then 32 | self.old_pos = self.pos -- Save the old position. 33 | self.pos = new_pos 34 | end 35 | end 36 | 37 | function Character:draw(grid) 38 | 39 | -- Draw the character. 40 | local color = self.color or 3 -- Default to yellow. 41 | set_color('b', color) 42 | set_color('f', 0) -- Black foreground. 43 | local x = 2 * self.pos[1] 44 | local y = self.pos[2] 45 | set_pos(x, y) 46 | io.write(self.chars) 47 | io.flush() 48 | 49 | -- Erase the old character pos if appropriate. 50 | if self.old_pos then 51 | local op = self.old_pos 52 | local x = 2 * op[1] 53 | local y = op[2] 54 | set_pos(x, y) 55 | set_color('f', 7) -- White foreground. 56 | set_color('b', 0) -- Black background. 57 | io.write(grid[op[1]][op[2]]) 58 | io.flush() 59 | self.old_pos = nil 60 | end 61 | end 62 | 63 | return Character 64 | -------------------------------------------------------------------------------- /ch6/Pair.h: -------------------------------------------------------------------------------- 1 | // Pair.h 2 | // 3 | // Pair is a simple userdata type, defined in C, which supports 4 | // basic operations on (x, y) pairs. 5 | // 6 | // Example usage: 7 | // 8 | // local Pair = require 'Pair' 9 | // 10 | // local p = Pair:new {1, 2} 11 | // local q = Pair:new {3, 4} 12 | // local r = p + q * 2 -- Now r = {7, 10}. 13 | // 14 | 15 | #include "lua.h" 16 | 17 | int luaopen_Pair(lua_State *L); 18 | -------------------------------------------------------------------------------- /ch6/UserBaddy.lua: -------------------------------------------------------------------------------- 1 | -- UserBaddy.lua 2 | -- 3 | -- A subclass of Baddy that users can provide scripts for. 4 | -- 5 | 6 | local Baddy = require 'Baddy' 7 | 8 | local function is_in_table(needle, haystack) 9 | for _, value in pairs(haystack) do 10 | if value == needle then 11 | return true 12 | end 13 | end 14 | return false 15 | end 16 | 17 | local UserBaddy = Baddy:new({}) 18 | 19 | -- Set up a new baddy. 20 | -- This expects a table with keys {home, color, chars, script}. 21 | function UserBaddy:new(b) 22 | assert(b) -- Require a table of initial values. 23 | b.pos = b.home 24 | b.dir = {-1, 0} 25 | b.get_direction = loadfile(b.script)() 26 | self.__index = self 27 | return setmetatable(b, self) 28 | end 29 | 30 | function UserBaddy:move_if_possible(grid, player) 31 | 32 | -- Determine which directions are possible to move in. 33 | local deltas = {{-1, 0}, {1, 0}, {0, -1}, {0, 1}} 34 | local possible_dirs = {} 35 | for _, delta in pairs(deltas) do 36 | if self:can_move_in_dir(delta, grid) then 37 | table.insert(possible_dirs, pair(delta)) 38 | end 39 | end 40 | 41 | -- Call the user-defined movement function. 42 | -- The `self` value will become the first parameter sent in. 43 | self.dir = self:get_direction(possible_dirs, grid, player) 44 | 45 | if not is_in_table(self.dir, possible_dirs) then 46 | self.dir = possible_dirs[1] 47 | end 48 | 49 | -- Update our position and saved old position. 50 | self.old_pos = self.pos 51 | _, self.pos = self:can_move_in_dir(self.dir, grid) 52 | end 53 | 54 | return UserBaddy 55 | -------------------------------------------------------------------------------- /ch6/a_user_baddy.lua: -------------------------------------------------------------------------------- 1 | -- a_user_baddy.lua 2 | 3 | -- Return the dot product of vectors a and b. 4 | local function dot(a, b) 5 | return a[1] * b[1] + a[2] * b[2] 6 | end 7 | 8 | local direction = pair {1, 0} 9 | local num_turns = 0 10 | 11 | -- The next function will return true when: 12 | -- * The player can turn left or right; or 13 | -- * the player can't go straight ahead. 14 | -- Intuitively, a "turning point" is any good place to consider 15 | -- moving in a new direction. 16 | local function is_turning_point(possible_dirs) 17 | 18 | local backwards = pair {-direction.x, -direction.y} 19 | local can_go_straight = false 20 | 21 | for i, possible_dir in pairs(possible_dirs) do 22 | if possible_dir == direction then 23 | can_go_straight = true 24 | elseif possible_dir ~= backwards then 25 | return true -- We can turn left or right. 26 | end 27 | end 28 | 29 | -- If we get here, then turning left or right is impossible. 30 | return not can_go_straight 31 | end 32 | 33 | local function choose_direction(baddy, possible_dirs, 34 | grid, player) 35 | 36 | -- If we can't turn and we can go straight, then go straight. 37 | if not is_turning_point(possible_dirs) then 38 | return direction 39 | end 40 | 41 | -- Every 5th turn is random. 42 | num_turns = num_turns + 1 43 | if num_turns % 5 == 0 then 44 | direction = possible_dirs[math.random(#possible_dirs)] 45 | return direction 46 | end 47 | 48 | -- Try to go toward the player in the other 4 out of 5 turns. 49 | 50 | local to_player = pair {player.pos[1] - baddy.pos[1], 51 | player.pos[2] - baddy.pos[2]} 52 | 53 | local max_dot_prod = -math.huge 54 | 55 | for i, possible_dir in pairs(possible_dirs) do 56 | local dot_prod = dot(possible_dir, to_player) 57 | if dot_prod > max_dot_prod then 58 | max_dot_prod = dot_prod 59 | direction = possible_dir 60 | end 61 | end 62 | 63 | return direction 64 | end 65 | 66 | return choose_direction 67 | -------------------------------------------------------------------------------- /ch6/eatyguy10.c: -------------------------------------------------------------------------------- 1 | // eatyguy10.c 2 | // 3 | // Load eatyguy10.lua and run it in the order below. 4 | // 5 | // -- Lua-ish pseudocode representing the order of events. 6 | // eatyguy.init() 7 | // while true do 8 | // eatyguy.loop(state) -- state has keys 'clock' and 'key'. 9 | // sleep(0.016) 10 | // end 11 | // 12 | 13 | #include "Pair.h" 14 | 15 | #include "lauxlib.h" 16 | #include "lua.h" 17 | #include "lualib.h" 18 | 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | 27 | // 27 is the decimal representation of Esc in ASCII. 28 | #define ESC_KEY 27 29 | 30 | 31 | // Internal functions. 32 | 33 | double gettime() { 34 | struct timeval tv; 35 | gettimeofday(&tv, NULL); 36 | return tv.tv_sec + 1e-6 * tv.tv_usec; 37 | } 38 | 39 | int getkey(int *is_end_of_seq) { 40 | 41 | // Make reading from stdin non-blocking. 42 | int flags = fcntl(STDIN_FILENO, F_GETFL); 43 | fcntl(STDIN_FILENO, F_SETFL, flags | O_NONBLOCK); 44 | 45 | // We care about two cases: 46 | // Case 1: A sequence of the form 27, 91, X; return X. 47 | // Case 2: For any other sequence, return each int separately. 48 | 49 | *is_end_of_seq = 0; 50 | int ch = getchar(); 51 | if (ch == 27) { 52 | int next = getchar(); 53 | if (next == 91) { 54 | *is_end_of_seq = 1; 55 | ch = getchar(); 56 | goto end; 57 | } 58 | // If we get here, then we're not in a 27, 91, X sequence. 59 | ungetc(next, stdin); 60 | } 61 | 62 | end: 63 | 64 | // Turn off non-blocking I/O. On some systems, leaving stdin 65 | // non-blocking will also leave stdout non-blocking, which can 66 | // cause printing errors. 67 | fcntl(STDIN_FILENO, F_SETFL, flags); 68 | return ch; 69 | } 70 | 71 | void sleephires(double sec) { 72 | long s = (long)floor(sec); 73 | long n = (long)floor((sec - s) * 1e9); 74 | struct timespec delay = { .tv_sec = s, .tv_nsec = n }; 75 | nanosleep(&delay, NULL); 76 | } 77 | 78 | void start() { 79 | // Terminal setup. 80 | system("tput setab 0"); // Use a black background. 81 | system("tput clear"); // Clear the screen. 82 | system("tput civis"); // Hide the cursor. 83 | system("stty raw -echo"); // Improve access to keypresses. 84 | } 85 | 86 | void done(const char *msg) { 87 | 88 | // Put the terminal back into a decent state. 89 | system("stty cooked echo"); // Undo init call to "stty raw". 90 | system("tput reset"); // Reset colors and clear screen. 91 | 92 | // Print the farewell message if there is one. 93 | if (msg) printf("%s\n", msg); 94 | 95 | exit(0); 96 | } 97 | 98 | void push_keypress(lua_State *L, int key, int is_end_of_seq) { 99 | if (is_end_of_seq && 65 <= key && key <= 68) { 100 | // up, down, right, left = 65, 66, 67, 68 101 | static const char *arrow_names[] = {"up", "down", 102 | "right", "left"}; 103 | lua_pushstring(L, arrow_names[key - 65]); 104 | } else { 105 | lua_pushnumber(L, key); 106 | } 107 | } 108 | 109 | void push_state_table(lua_State *L, 110 | int key, 111 | int is_end_of_seq) { 112 | 113 | lua_newtable(L); 114 | 115 | // stack = [.., {}] 116 | 117 | push_keypress(L, key, is_end_of_seq); 118 | 119 | // stack = [.., {}, key] 120 | 121 | lua_setfield(L, -2, "key"); 122 | 123 | // stack = [.., {key = key}] 124 | 125 | lua_pushnumber(L, gettime()); 126 | 127 | // stack = [.., {key = key}, clock] 128 | 129 | lua_setfield(L, -2, "clock"); 130 | 131 | // stack = [.., {key = key, clock = clock}] 132 | } 133 | 134 | 135 | // Lua-visible functions. 136 | 137 | // Lua: timestamp(). 138 | // Return a high-resolution timestamp in seconds. 139 | int timestamp(lua_State *L) { 140 | lua_pushnumber(L, gettime()); 141 | return 1; 142 | } 143 | 144 | 145 | // Main. 146 | 147 | int main() { 148 | 149 | start(); 150 | 151 | // Create a Lua state and load the module. 152 | lua_State *L = luaL_newstate(); 153 | luaL_openlibs(L); 154 | 155 | // Set up API functions written in C. 156 | lua_register(L, "timestamp", timestamp); 157 | luaopen_Pair(L); 158 | lua_setglobal(L, "Pair"); 159 | 160 | // Set up API functions written in Lua. 161 | luaL_dofile(L, "util.lua"); 162 | luaL_dofile(L, "Character.lua"); 163 | lua_setglobal(L, "Character"); 164 | 165 | // Load eatyguy10 and run the init() function. 166 | luaL_dofile(L, "eatyguy10.lua"); 167 | lua_setglobal(L, "eatyguy"); 168 | lua_settop(L, 0); 169 | 170 | lua_getglobal(L, "eatyguy"); 171 | lua_getfield(L, -1, "init"); // -1 means stack top. 172 | lua_call(L, 0, 0); // 0, 0 = #args, #retvals 173 | 174 | lua_getglobal(L, "eatyguy"); 175 | while (1) { 176 | int is_end_of_seq; 177 | int key = getkey(&is_end_of_seq); 178 | if (key == ESC_KEY || key == 'q' || key == 'Q') done(NULL); 179 | 180 | // Call eatyguy.loop(state). 181 | lua_getfield(L, -1, "loop"); 182 | push_state_table(L, key, is_end_of_seq); 183 | lua_call(L, 1, 1); 184 | 185 | // Check to see if the game is over. 186 | if (lua_isstring(L, -1)) { 187 | const char *msg = lua_tostring(L, -1); 188 | done(msg); 189 | } 190 | 191 | // Pop the return value of eatyguy.loop() off the stack. 192 | lua_pop(L, 1); 193 | 194 | sleephires(0.016); // Sleep for 16ms. 195 | } 196 | 197 | return 0; 198 | } 199 | -------------------------------------------------------------------------------- /ch6/interpreter.c: -------------------------------------------------------------------------------- 1 | // interpreter.c 2 | // 3 | 4 | #include "interpreter.h" 5 | 6 | #include "lauxlib.h" 7 | #include "lualib.h" 8 | 9 | #include 10 | #include 11 | 12 | int accept_and_run_a_line(lua_State *L) { 13 | 14 | char buff[2048]; 15 | 16 | // Read input and exit early if there is an end of stream. 17 | printf("> "); 18 | if (!fgets(buff, sizeof(buff), stdin)) { 19 | printf("\n"); 20 | return 0; 21 | } 22 | 23 | // Try to run the line, printing errors if there are any. 24 | int error = luaL_loadstring(L, buff); 25 | if (!error) error = lua_pcall(L, 0, 0, 0); 26 | if (error) { 27 | fprintf(stderr, "%s\n", lua_tostring(L, -1)); 28 | lua_pop(L, 1); 29 | } 30 | 31 | return 1; 32 | } 33 | -------------------------------------------------------------------------------- /ch6/interpreter.h: -------------------------------------------------------------------------------- 1 | // interpreter.h 2 | // 3 | 4 | #include "lua.h" 5 | 6 | // This returns 0 if the line indicated the end of input, 7 | // usually given by pressing control-D; otherwise returns 1. 8 | int accept_and_run_a_line(lua_State *L); 9 | -------------------------------------------------------------------------------- /ch6/library_subset.c: -------------------------------------------------------------------------------- 1 | // library_subset.c 2 | 3 | #include "lauxlib.h" 4 | #include "lua.h" 5 | #include "lualib.h" 6 | 7 | typedef enum { 8 | mode_all_libs, 9 | mode_some_libs, 10 | mode_base_only 11 | } Mode; 12 | 13 | int main() { 14 | 15 | lua_State *L = luaL_newstate(); 16 | int set_global = 1; 17 | 18 | // Choose how many libraries to work with. 19 | Mode mode = mode_all_libs; // Change this value to explore. 20 | switch (mode) { 21 | 22 | case mode_all_libs: 23 | luaL_openlibs(L); 24 | break; 25 | 26 | case mode_some_libs: 27 | luaL_requiref(L, "package", luaopen_package, set_global); 28 | luaL_requiref(L, "string", luaopen_string, set_global); 29 | luaL_requiref(L, "table", luaopen_table, set_global); 30 | luaL_requiref(L, "math", luaopen_math, set_global); 31 | // Fall through to also include the next case. 32 | 33 | case mode_base_only: 34 | luaL_requiref(L, "_G", luaopen_base, set_global); 35 | break; 36 | } 37 | 38 | luaL_dofile(L, "run_bash.lua"); 39 | 40 | return 0; 41 | } 42 | 43 | -------------------------------------------------------------------------------- /ch6/limit_cpu.c: -------------------------------------------------------------------------------- 1 | // limit_cpu.c 2 | // 3 | 4 | #include "interpreter.h" 5 | 6 | #include "lauxlib.h" 7 | #include "lua.h" 8 | #include "lualib.h" 9 | 10 | #include 11 | #include 12 | 13 | int do_limit_instructions = 1; 14 | long instructions_per_hook = 1; // 100+ recommended. 15 | long instruction_count = 0; 16 | long instruction_count_limit = 100; 17 | 18 | void hook(lua_State *L, lua_Debug *ar) { 19 | 20 | instruction_count += instructions_per_hook; 21 | 22 | if (!do_limit_instructions) return; 23 | 24 | if (instruction_count > instruction_count_limit) { 25 | lua_pushstring(L, "exceeded allowed cpu time"); 26 | lua_error(L); 27 | } 28 | } 29 | 30 | void print_status() { 31 | printf("%ld instructions run so far\n", instruction_count); 32 | } 33 | 34 | int main() { 35 | lua_State *L = luaL_newstate(); 36 | luaL_openlibs(L); 37 | lua_sethook(L, hook, LUA_MASKCOUNT, instructions_per_hook); 38 | 39 | print_status(); 40 | while (accept_and_run_a_line(L)) print_status(); 41 | 42 | lua_close(L); 43 | return 0; 44 | } 45 | -------------------------------------------------------------------------------- /ch6/limit_memory.c: -------------------------------------------------------------------------------- 1 | // limit_memory.c 2 | // 3 | 4 | #include "interpreter.h" 5 | 6 | #include "lauxlib.h" 7 | #include "lua.h" 8 | #include "lualib.h" 9 | 10 | #include 11 | #include 12 | #include 13 | 14 | long bytes_alloced = 0; 15 | long max_bytes = 30000; // You can choose this value. 16 | 17 | void *alloc(void *ud, 18 | void *ptr, 19 | size_t osize, 20 | size_t nsize) { 21 | 22 | // Compute the byte change requested. May be negative. 23 | long num_bytes_to_add = nsize - (ptr ? osize : 0); 24 | 25 | // Reject the change if it would exceed our limit. 26 | if (bytes_alloced + num_bytes_to_add > max_bytes) { 27 | errno = ENOMEM; 28 | return NULL; 29 | } 30 | 31 | // Otherwise, free or allocate memory as requested. 32 | bytes_alloced += num_bytes_to_add; 33 | if (nsize) return realloc(ptr, nsize); 34 | free(ptr); 35 | return NULL; 36 | } 37 | 38 | void print_status() { 39 | printf("%ld bytes allocated\n", bytes_alloced); 40 | } 41 | 42 | int main() { 43 | lua_State *L = lua_newstate(alloc, NULL); 44 | luaL_openlibs(L); 45 | 46 | print_status(); 47 | while (accept_and_run_a_line(L)) print_status(); 48 | 49 | lua_close(L); 50 | print_status(); 51 | return 0; 52 | } 53 | -------------------------------------------------------------------------------- /ch6/makefile: -------------------------------------------------------------------------------- 1 | # Chapter 6 makefile. 2 | # 3 | # To build all the binaries for this chapter, simply run: 4 | # $ make 5 | # 6 | # To remove the build products, including binaries, run: 7 | # $ make clean 8 | # 9 | 10 | eatyguys = eatyguy10 11 | interpreters = track_memory limit_memory limit_cpu 12 | binaries = library_subset 13 | obj_files = Pair.o interpreter.o 14 | 15 | include ../build_settings.make 16 | -------------------------------------------------------------------------------- /ch6/run_bash.lua: -------------------------------------------------------------------------------- 1 | print('Here are the global tables defined:') 2 | for k, v in pairs(_G) do 3 | if type(v) == 'table' then 4 | print(' ' .. k) 5 | end 6 | end 7 | 8 | if os then 9 | print('Hit control-D to exit this subshell.') 10 | os.execute('bash') 11 | else 12 | print('I could not run bash.') 13 | end 14 | -------------------------------------------------------------------------------- /ch6/strict.lua: -------------------------------------------------------------------------------- 1 | --[[ strict.lua 2 | 3 | Tested with Lua 5.2 and 5.3; not designed for 5.1 or earlier. 4 | 5 | After the user makes the following statement: 6 | 7 | local _ENV = strict.new_env() 8 | 9 | their code will throw an error on either of these conditions: 10 | 11 | * A global variable is created from a nested code block, such 12 | as within a function, or 13 | * An undeclared global is referenced. 14 | 15 | Alternatively, call: 16 | 17 | strict.add_checks(_G) 18 | 19 | to throw errors on all bad global name uses, not just for the 20 | current file. 21 | 22 | There are a number of files similar to this one available 23 | online, as well as described in the book Programming in Lua by 24 | Roberto Ierusalimschy. This happens by be a version I like. 25 | 26 | --]] 27 | 28 | local strict = {} 29 | 30 | -- This replaces the __index and __newindex metamethods on the 31 | -- given table; this is designed for use with env set to either 32 | -- _ENV or _G. 33 | function strict.add_checks(env) 34 | 35 | -- Get t's metatable, creating it if needed. 36 | local mt = getmetatable(env) 37 | if mt == nil then 38 | mt = {} 39 | setmetatable(env, mt) 40 | end 41 | 42 | -- Set up the declared table to be an upvalue for the __index 43 | -- and __newindex closures created below. 44 | local declared = {} 45 | 46 | -- Throw an error for global assignments in non-main Lua code. 47 | mt.__newindex = function (t, k, v) 48 | if not declared[k] then 49 | -- The values (2, 'S') ask for (S for) source info on the 50 | -- function at level 2 in the stack; the one making the 51 | -- assignment. 52 | local w = debug.getinfo(2, 'S').what 53 | if w ~= 'main' and w ~= 'C' then 54 | local fmt = 'Assignment to undeclared global "%s"' 55 | -- The value 2 will blame the error on the code making 56 | -- the bad assignment; i.e. the caller of __newindex(). 57 | error(fmt:format(k), 2) 58 | end 59 | declared[k] = true 60 | end 61 | -- Make the actual assignment in the table t. 62 | rawset(t, k, v) 63 | end 64 | 65 | -- Throw an error for references to undeclared global names. 66 | mt.__index = function (t, k) 67 | if not declared[k] then 68 | -- The parameter 2 will blame the error on the code making 69 | -- the bad lookup; i.e. the caller of __index(). 70 | error(('Use of undeclared global "%s"'):format(k), 2) 71 | end 72 | -- We won't always get an error; finish the lookup on key k. 73 | return rawget(t, k) 74 | end 75 | end 76 | 77 | -- This returns a replacement for _ENV that detects errors. 78 | function strict.new_env() 79 | 80 | -- Set up the new environment with the values from _ENV. 81 | local env = {} 82 | for key, value in pairs(_ENV) do 83 | env[key] = value 84 | end 85 | 86 | -- Add the checks and return the new environment. 87 | strict.add_checks(env) 88 | return env 89 | end 90 | 91 | return strict 92 | -------------------------------------------------------------------------------- /ch6/track_memory.c: -------------------------------------------------------------------------------- 1 | // track_memory.c 2 | // 3 | 4 | #include "interpreter.h" 5 | 6 | #include "lauxlib.h" 7 | #include "lua.h" 8 | #include "lualib.h" 9 | 10 | #include 11 | #include 12 | 13 | long bytes_alloced = 0; 14 | 15 | void *alloc(void *ud, 16 | void *ptr, 17 | size_t osize, 18 | size_t nsize) { 19 | bytes_alloced += nsize - (ptr ? osize : 0); 20 | if (nsize) return realloc(ptr, nsize); 21 | free(ptr); 22 | return NULL; 23 | } 24 | 25 | void print_status() { 26 | printf("%ld bytes allocated\n", bytes_alloced); 27 | } 28 | 29 | int main() { 30 | lua_State *L = lua_newstate(alloc, NULL); 31 | luaL_openlibs(L); 32 | 33 | print_status(); 34 | while (accept_and_run_a_line(L)) print_status(); 35 | 36 | lua_close(L); 37 | print_status(); 38 | return 0; 39 | } 40 | -------------------------------------------------------------------------------- /ch6/util.lua: -------------------------------------------------------------------------------- 1 | -- util.lua 2 | -- 3 | -- Define the functions set_color and set_pos so that they cache 4 | -- the strings returned from tput in order to reduce the number 5 | -- of tput calls. 6 | 7 | 8 | -- Local functions won't be globally visible after this script 9 | -- completes. 10 | 11 | local cached_strs = {} -- Maps cmd -> str. 12 | 13 | local function cached_cmd(cmd) 14 | if not cached_strs[cmd] then 15 | p = io.popen(cmd) 16 | cached_strs[cmd] = p:read() 17 | p:close() 18 | end 19 | io.write(cached_strs[cmd]) 20 | end 21 | 22 | 23 | -- Global functions will remain visible after this script 24 | -- completes. 25 | 26 | function set_color(b_or_f, color) 27 | assert(b_or_f == 'b' or b_or_f == 'f') 28 | cached_cmd('tput seta' .. b_or_f .. ' ' .. color) 29 | end 30 | 31 | function set_pos(x, y) 32 | cached_cmd(('tput cup %d %d'):format(y, x)) 33 | end 34 | 35 | -- Expect a Pair or a table; if it's a table, convert to a Pair. 36 | function pair(t) 37 | -- This calls Pair:new() only if t is a table. 38 | return (type(t) == 'table') and Pair:new(t) or t 39 | end 40 | 41 | 42 | -------------------------------------------------------------------------------- /img/screenshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tylerneylon/APIsWithLua/0b8e3f22211551b34d0950aa1de227e48b2df7e8/img/screenshot.png -------------------------------------------------------------------------------- /lua/lapi.h: -------------------------------------------------------------------------------- 1 | /* 2 | ** $Id: lapi.h,v 2.9 2015/03/06 19:49:50 roberto Exp $ 3 | ** Auxiliary functions from Lua API 4 | ** See Copyright Notice in lua.h 5 | */ 6 | 7 | #ifndef lapi_h 8 | #define lapi_h 9 | 10 | 11 | #include "llimits.h" 12 | #include "lstate.h" 13 | 14 | #define api_incr_top(L) {L->top++; api_check(L, L->top <= L->ci->top, \ 15 | "stack overflow");} 16 | 17 | #define adjustresults(L,nres) \ 18 | { if ((nres) == LUA_MULTRET && L->ci->top < L->top) L->ci->top = L->top; } 19 | 20 | #define api_checknelems(L,n) api_check(L, (n) < (L->top - L->ci->func), \ 21 | "not enough elements in the stack") 22 | 23 | 24 | #endif 25 | -------------------------------------------------------------------------------- /lua/lbitlib.c: -------------------------------------------------------------------------------- 1 | /* 2 | ** $Id: lbitlib.c,v 1.30 2015/11/11 19:08:09 roberto Exp $ 3 | ** Standard library for bitwise operations 4 | ** See Copyright Notice in lua.h 5 | */ 6 | 7 | #define lbitlib_c 8 | #define LUA_LIB 9 | 10 | #include "lprefix.h" 11 | 12 | 13 | #include "lua.h" 14 | 15 | #include "lauxlib.h" 16 | #include "lualib.h" 17 | 18 | 19 | #if defined(LUA_COMPAT_BITLIB) /* { */ 20 | 21 | 22 | #define pushunsigned(L,n) lua_pushinteger(L, (lua_Integer)(n)) 23 | #define checkunsigned(L,i) ((lua_Unsigned)luaL_checkinteger(L,i)) 24 | 25 | 26 | /* number of bits to consider in a number */ 27 | #if !defined(LUA_NBITS) 28 | #define LUA_NBITS 32 29 | #endif 30 | 31 | 32 | /* 33 | ** a lua_Unsigned with its first LUA_NBITS bits equal to 1. (Shift must 34 | ** be made in two parts to avoid problems when LUA_NBITS is equal to the 35 | ** number of bits in a lua_Unsigned.) 36 | */ 37 | #define ALLONES (~(((~(lua_Unsigned)0) << (LUA_NBITS - 1)) << 1)) 38 | 39 | 40 | /* macro to trim extra bits */ 41 | #define trim(x) ((x) & ALLONES) 42 | 43 | 44 | /* builds a number with 'n' ones (1 <= n <= LUA_NBITS) */ 45 | #define mask(n) (~((ALLONES << 1) << ((n) - 1))) 46 | 47 | 48 | 49 | static lua_Unsigned andaux (lua_State *L) { 50 | int i, n = lua_gettop(L); 51 | lua_Unsigned r = ~(lua_Unsigned)0; 52 | for (i = 1; i <= n; i++) 53 | r &= checkunsigned(L, i); 54 | return trim(r); 55 | } 56 | 57 | 58 | static int b_and (lua_State *L) { 59 | lua_Unsigned r = andaux(L); 60 | pushunsigned(L, r); 61 | return 1; 62 | } 63 | 64 | 65 | static int b_test (lua_State *L) { 66 | lua_Unsigned r = andaux(L); 67 | lua_pushboolean(L, r != 0); 68 | return 1; 69 | } 70 | 71 | 72 | static int b_or (lua_State *L) { 73 | int i, n = lua_gettop(L); 74 | lua_Unsigned r = 0; 75 | for (i = 1; i <= n; i++) 76 | r |= checkunsigned(L, i); 77 | pushunsigned(L, trim(r)); 78 | return 1; 79 | } 80 | 81 | 82 | static int b_xor (lua_State *L) { 83 | int i, n = lua_gettop(L); 84 | lua_Unsigned r = 0; 85 | for (i = 1; i <= n; i++) 86 | r ^= checkunsigned(L, i); 87 | pushunsigned(L, trim(r)); 88 | return 1; 89 | } 90 | 91 | 92 | static int b_not (lua_State *L) { 93 | lua_Unsigned r = ~checkunsigned(L, 1); 94 | pushunsigned(L, trim(r)); 95 | return 1; 96 | } 97 | 98 | 99 | static int b_shift (lua_State *L, lua_Unsigned r, lua_Integer i) { 100 | if (i < 0) { /* shift right? */ 101 | i = -i; 102 | r = trim(r); 103 | if (i >= LUA_NBITS) r = 0; 104 | else r >>= i; 105 | } 106 | else { /* shift left */ 107 | if (i >= LUA_NBITS) r = 0; 108 | else r <<= i; 109 | r = trim(r); 110 | } 111 | pushunsigned(L, r); 112 | return 1; 113 | } 114 | 115 | 116 | static int b_lshift (lua_State *L) { 117 | return b_shift(L, checkunsigned(L, 1), luaL_checkinteger(L, 2)); 118 | } 119 | 120 | 121 | static int b_rshift (lua_State *L) { 122 | return b_shift(L, checkunsigned(L, 1), -luaL_checkinteger(L, 2)); 123 | } 124 | 125 | 126 | static int b_arshift (lua_State *L) { 127 | lua_Unsigned r = checkunsigned(L, 1); 128 | lua_Integer i = luaL_checkinteger(L, 2); 129 | if (i < 0 || !(r & ((lua_Unsigned)1 << (LUA_NBITS - 1)))) 130 | return b_shift(L, r, -i); 131 | else { /* arithmetic shift for 'negative' number */ 132 | if (i >= LUA_NBITS) r = ALLONES; 133 | else 134 | r = trim((r >> i) | ~(trim(~(lua_Unsigned)0) >> i)); /* add signal bit */ 135 | pushunsigned(L, r); 136 | return 1; 137 | } 138 | } 139 | 140 | 141 | static int b_rot (lua_State *L, lua_Integer d) { 142 | lua_Unsigned r = checkunsigned(L, 1); 143 | int i = d & (LUA_NBITS - 1); /* i = d % NBITS */ 144 | r = trim(r); 145 | if (i != 0) /* avoid undefined shift of LUA_NBITS when i == 0 */ 146 | r = (r << i) | (r >> (LUA_NBITS - i)); 147 | pushunsigned(L, trim(r)); 148 | return 1; 149 | } 150 | 151 | 152 | static int b_lrot (lua_State *L) { 153 | return b_rot(L, luaL_checkinteger(L, 2)); 154 | } 155 | 156 | 157 | static int b_rrot (lua_State *L) { 158 | return b_rot(L, -luaL_checkinteger(L, 2)); 159 | } 160 | 161 | 162 | /* 163 | ** get field and width arguments for field-manipulation functions, 164 | ** checking whether they are valid. 165 | ** ('luaL_error' called without 'return' to avoid later warnings about 166 | ** 'width' being used uninitialized.) 167 | */ 168 | static int fieldargs (lua_State *L, int farg, int *width) { 169 | lua_Integer f = luaL_checkinteger(L, farg); 170 | lua_Integer w = luaL_optinteger(L, farg + 1, 1); 171 | luaL_argcheck(L, 0 <= f, farg, "field cannot be negative"); 172 | luaL_argcheck(L, 0 < w, farg + 1, "width must be positive"); 173 | if (f + w > LUA_NBITS) 174 | luaL_error(L, "trying to access non-existent bits"); 175 | *width = (int)w; 176 | return (int)f; 177 | } 178 | 179 | 180 | static int b_extract (lua_State *L) { 181 | int w; 182 | lua_Unsigned r = trim(checkunsigned(L, 1)); 183 | int f = fieldargs(L, 2, &w); 184 | r = (r >> f) & mask(w); 185 | pushunsigned(L, r); 186 | return 1; 187 | } 188 | 189 | 190 | static int b_replace (lua_State *L) { 191 | int w; 192 | lua_Unsigned r = trim(checkunsigned(L, 1)); 193 | lua_Unsigned v = trim(checkunsigned(L, 2)); 194 | int f = fieldargs(L, 3, &w); 195 | lua_Unsigned m = mask(w); 196 | r = (r & ~(m << f)) | ((v & m) << f); 197 | pushunsigned(L, r); 198 | return 1; 199 | } 200 | 201 | 202 | static const luaL_Reg bitlib[] = { 203 | {"arshift", b_arshift}, 204 | {"band", b_and}, 205 | {"bnot", b_not}, 206 | {"bor", b_or}, 207 | {"bxor", b_xor}, 208 | {"btest", b_test}, 209 | {"extract", b_extract}, 210 | {"lrotate", b_lrot}, 211 | {"lshift", b_lshift}, 212 | {"replace", b_replace}, 213 | {"rrotate", b_rrot}, 214 | {"rshift", b_rshift}, 215 | {NULL, NULL} 216 | }; 217 | 218 | 219 | 220 | LUAMOD_API int luaopen_bit32 (lua_State *L) { 221 | luaL_newlib(L, bitlib); 222 | return 1; 223 | } 224 | 225 | 226 | #else /* }{ */ 227 | 228 | 229 | LUAMOD_API int luaopen_bit32 (lua_State *L) { 230 | return luaL_error(L, "library 'bit32' has been deprecated"); 231 | } 232 | 233 | #endif /* } */ 234 | -------------------------------------------------------------------------------- /lua/lcode.h: -------------------------------------------------------------------------------- 1 | /* 2 | ** $Id: lcode.h,v 1.64 2016/01/05 16:22:37 roberto Exp $ 3 | ** Code generator for Lua 4 | ** See Copyright Notice in lua.h 5 | */ 6 | 7 | #ifndef lcode_h 8 | #define lcode_h 9 | 10 | #include "llex.h" 11 | #include "lobject.h" 12 | #include "lopcodes.h" 13 | #include "lparser.h" 14 | 15 | 16 | /* 17 | ** Marks the end of a patch list. It is an invalid value both as an absolute 18 | ** address, and as a list link (would link an element to itself). 19 | */ 20 | #define NO_JUMP (-1) 21 | 22 | 23 | /* 24 | ** grep "ORDER OPR" if you change these enums (ORDER OP) 25 | */ 26 | typedef enum BinOpr { 27 | OPR_ADD, OPR_SUB, OPR_MUL, OPR_MOD, OPR_POW, 28 | OPR_DIV, 29 | OPR_IDIV, 30 | OPR_BAND, OPR_BOR, OPR_BXOR, 31 | OPR_SHL, OPR_SHR, 32 | OPR_CONCAT, 33 | OPR_EQ, OPR_LT, OPR_LE, 34 | OPR_NE, OPR_GT, OPR_GE, 35 | OPR_AND, OPR_OR, 36 | OPR_NOBINOPR 37 | } BinOpr; 38 | 39 | 40 | typedef enum UnOpr { OPR_MINUS, OPR_BNOT, OPR_NOT, OPR_LEN, OPR_NOUNOPR } UnOpr; 41 | 42 | 43 | /* get (pointer to) instruction of given 'expdesc' */ 44 | #define getinstruction(fs,e) ((fs)->f->code[(e)->u.info]) 45 | 46 | #define luaK_codeAsBx(fs,o,A,sBx) luaK_codeABx(fs,o,A,(sBx)+MAXARG_sBx) 47 | 48 | #define luaK_setmultret(fs,e) luaK_setreturns(fs, e, LUA_MULTRET) 49 | 50 | #define luaK_jumpto(fs,t) luaK_patchlist(fs, luaK_jump(fs), t) 51 | 52 | LUAI_FUNC int luaK_codeABx (FuncState *fs, OpCode o, int A, unsigned int Bx); 53 | LUAI_FUNC int luaK_codeABC (FuncState *fs, OpCode o, int A, int B, int C); 54 | LUAI_FUNC int luaK_codek (FuncState *fs, int reg, int k); 55 | LUAI_FUNC void luaK_fixline (FuncState *fs, int line); 56 | LUAI_FUNC void luaK_nil (FuncState *fs, int from, int n); 57 | LUAI_FUNC void luaK_reserveregs (FuncState *fs, int n); 58 | LUAI_FUNC void luaK_checkstack (FuncState *fs, int n); 59 | LUAI_FUNC int luaK_stringK (FuncState *fs, TString *s); 60 | LUAI_FUNC int luaK_intK (FuncState *fs, lua_Integer n); 61 | LUAI_FUNC void luaK_dischargevars (FuncState *fs, expdesc *e); 62 | LUAI_FUNC int luaK_exp2anyreg (FuncState *fs, expdesc *e); 63 | LUAI_FUNC void luaK_exp2anyregup (FuncState *fs, expdesc *e); 64 | LUAI_FUNC void luaK_exp2nextreg (FuncState *fs, expdesc *e); 65 | LUAI_FUNC void luaK_exp2val (FuncState *fs, expdesc *e); 66 | LUAI_FUNC int luaK_exp2RK (FuncState *fs, expdesc *e); 67 | LUAI_FUNC void luaK_self (FuncState *fs, expdesc *e, expdesc *key); 68 | LUAI_FUNC void luaK_indexed (FuncState *fs, expdesc *t, expdesc *k); 69 | LUAI_FUNC void luaK_goiftrue (FuncState *fs, expdesc *e); 70 | LUAI_FUNC void luaK_goiffalse (FuncState *fs, expdesc *e); 71 | LUAI_FUNC void luaK_storevar (FuncState *fs, expdesc *var, expdesc *e); 72 | LUAI_FUNC void luaK_setreturns (FuncState *fs, expdesc *e, int nresults); 73 | LUAI_FUNC void luaK_setoneret (FuncState *fs, expdesc *e); 74 | LUAI_FUNC int luaK_jump (FuncState *fs); 75 | LUAI_FUNC void luaK_ret (FuncState *fs, int first, int nret); 76 | LUAI_FUNC void luaK_patchlist (FuncState *fs, int list, int target); 77 | LUAI_FUNC void luaK_patchtohere (FuncState *fs, int list); 78 | LUAI_FUNC void luaK_patchclose (FuncState *fs, int list, int level); 79 | LUAI_FUNC void luaK_concat (FuncState *fs, int *l1, int l2); 80 | LUAI_FUNC int luaK_getlabel (FuncState *fs); 81 | LUAI_FUNC void luaK_prefix (FuncState *fs, UnOpr op, expdesc *v, int line); 82 | LUAI_FUNC void luaK_infix (FuncState *fs, BinOpr op, expdesc *v); 83 | LUAI_FUNC void luaK_posfix (FuncState *fs, BinOpr op, expdesc *v1, 84 | expdesc *v2, int line); 85 | LUAI_FUNC void luaK_setlist (FuncState *fs, int base, int nelems, int tostore); 86 | 87 | 88 | #endif 89 | -------------------------------------------------------------------------------- /lua/lcorolib.c: -------------------------------------------------------------------------------- 1 | /* 2 | ** $Id: lcorolib.c,v 1.10 2016/04/11 19:19:55 roberto Exp $ 3 | ** Coroutine Library 4 | ** See Copyright Notice in lua.h 5 | */ 6 | 7 | #define lcorolib_c 8 | #define LUA_LIB 9 | 10 | #include "lprefix.h" 11 | 12 | 13 | #include 14 | 15 | #include "lua.h" 16 | 17 | #include "lauxlib.h" 18 | #include "lualib.h" 19 | 20 | 21 | static lua_State *getco (lua_State *L) { 22 | lua_State *co = lua_tothread(L, 1); 23 | luaL_argcheck(L, co, 1, "thread expected"); 24 | return co; 25 | } 26 | 27 | 28 | static int auxresume (lua_State *L, lua_State *co, int narg) { 29 | int status; 30 | if (!lua_checkstack(co, narg)) { 31 | lua_pushliteral(L, "too many arguments to resume"); 32 | return -1; /* error flag */ 33 | } 34 | if (lua_status(co) == LUA_OK && lua_gettop(co) == 0) { 35 | lua_pushliteral(L, "cannot resume dead coroutine"); 36 | return -1; /* error flag */ 37 | } 38 | lua_xmove(L, co, narg); 39 | status = lua_resume(co, L, narg); 40 | if (status == LUA_OK || status == LUA_YIELD) { 41 | int nres = lua_gettop(co); 42 | if (!lua_checkstack(L, nres + 1)) { 43 | lua_pop(co, nres); /* remove results anyway */ 44 | lua_pushliteral(L, "too many results to resume"); 45 | return -1; /* error flag */ 46 | } 47 | lua_xmove(co, L, nres); /* move yielded values */ 48 | return nres; 49 | } 50 | else { 51 | lua_xmove(co, L, 1); /* move error message */ 52 | return -1; /* error flag */ 53 | } 54 | } 55 | 56 | 57 | static int luaB_coresume (lua_State *L) { 58 | lua_State *co = getco(L); 59 | int r; 60 | r = auxresume(L, co, lua_gettop(L) - 1); 61 | if (r < 0) { 62 | lua_pushboolean(L, 0); 63 | lua_insert(L, -2); 64 | return 2; /* return false + error message */ 65 | } 66 | else { 67 | lua_pushboolean(L, 1); 68 | lua_insert(L, -(r + 1)); 69 | return r + 1; /* return true + 'resume' returns */ 70 | } 71 | } 72 | 73 | 74 | static int luaB_auxwrap (lua_State *L) { 75 | lua_State *co = lua_tothread(L, lua_upvalueindex(1)); 76 | int r = auxresume(L, co, lua_gettop(L)); 77 | if (r < 0) { 78 | if (lua_type(L, -1) == LUA_TSTRING) { /* error object is a string? */ 79 | luaL_where(L, 1); /* add extra info */ 80 | lua_insert(L, -2); 81 | lua_concat(L, 2); 82 | } 83 | return lua_error(L); /* propagate error */ 84 | } 85 | return r; 86 | } 87 | 88 | 89 | static int luaB_cocreate (lua_State *L) { 90 | lua_State *NL; 91 | luaL_checktype(L, 1, LUA_TFUNCTION); 92 | NL = lua_newthread(L); 93 | lua_pushvalue(L, 1); /* move function to top */ 94 | lua_xmove(L, NL, 1); /* move function from L to NL */ 95 | return 1; 96 | } 97 | 98 | 99 | static int luaB_cowrap (lua_State *L) { 100 | luaB_cocreate(L); 101 | lua_pushcclosure(L, luaB_auxwrap, 1); 102 | return 1; 103 | } 104 | 105 | 106 | static int luaB_yield (lua_State *L) { 107 | return lua_yield(L, lua_gettop(L)); 108 | } 109 | 110 | 111 | static int luaB_costatus (lua_State *L) { 112 | lua_State *co = getco(L); 113 | if (L == co) lua_pushliteral(L, "running"); 114 | else { 115 | switch (lua_status(co)) { 116 | case LUA_YIELD: 117 | lua_pushliteral(L, "suspended"); 118 | break; 119 | case LUA_OK: { 120 | lua_Debug ar; 121 | if (lua_getstack(co, 0, &ar) > 0) /* does it have frames? */ 122 | lua_pushliteral(L, "normal"); /* it is running */ 123 | else if (lua_gettop(co) == 0) 124 | lua_pushliteral(L, "dead"); 125 | else 126 | lua_pushliteral(L, "suspended"); /* initial state */ 127 | break; 128 | } 129 | default: /* some error occurred */ 130 | lua_pushliteral(L, "dead"); 131 | break; 132 | } 133 | } 134 | return 1; 135 | } 136 | 137 | 138 | static int luaB_yieldable (lua_State *L) { 139 | lua_pushboolean(L, lua_isyieldable(L)); 140 | return 1; 141 | } 142 | 143 | 144 | static int luaB_corunning (lua_State *L) { 145 | int ismain = lua_pushthread(L); 146 | lua_pushboolean(L, ismain); 147 | return 2; 148 | } 149 | 150 | 151 | static const luaL_Reg co_funcs[] = { 152 | {"create", luaB_cocreate}, 153 | {"resume", luaB_coresume}, 154 | {"running", luaB_corunning}, 155 | {"status", luaB_costatus}, 156 | {"wrap", luaB_cowrap}, 157 | {"yield", luaB_yield}, 158 | {"isyieldable", luaB_yieldable}, 159 | {NULL, NULL} 160 | }; 161 | 162 | 163 | 164 | LUAMOD_API int luaopen_coroutine (lua_State *L) { 165 | luaL_newlib(L, co_funcs); 166 | return 1; 167 | } 168 | 169 | -------------------------------------------------------------------------------- /lua/lctype.c: -------------------------------------------------------------------------------- 1 | /* 2 | ** $Id: lctype.c,v 1.12 2014/11/02 19:19:04 roberto Exp $ 3 | ** 'ctype' functions for Lua 4 | ** See Copyright Notice in lua.h 5 | */ 6 | 7 | #define lctype_c 8 | #define LUA_CORE 9 | 10 | #include "lprefix.h" 11 | 12 | 13 | #include "lctype.h" 14 | 15 | #if !LUA_USE_CTYPE /* { */ 16 | 17 | #include 18 | 19 | LUAI_DDEF const lu_byte luai_ctype_[UCHAR_MAX + 2] = { 20 | 0x00, /* EOZ */ 21 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0. */ 22 | 0x00, 0x08, 0x08, 0x08, 0x08, 0x08, 0x00, 0x00, 23 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 1. */ 24 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 25 | 0x0c, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, /* 2. */ 26 | 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 27 | 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, /* 3. */ 28 | 0x16, 0x16, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 29 | 0x04, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x05, /* 4. */ 30 | 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 31 | 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, /* 5. */ 32 | 0x05, 0x05, 0x05, 0x04, 0x04, 0x04, 0x04, 0x05, 33 | 0x04, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x05, /* 6. */ 34 | 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 35 | 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, /* 7. */ 36 | 0x05, 0x05, 0x05, 0x04, 0x04, 0x04, 0x04, 0x00, 37 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 8. */ 38 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 39 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 9. */ 40 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 41 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* a. */ 42 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 43 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* b. */ 44 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 45 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* c. */ 46 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 47 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* d. */ 48 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 49 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* e. */ 50 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 51 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* f. */ 52 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 53 | }; 54 | 55 | #endif /* } */ 56 | -------------------------------------------------------------------------------- /lua/lctype.h: -------------------------------------------------------------------------------- 1 | /* 2 | ** $Id: lctype.h,v 1.12 2011/07/15 12:50:29 roberto Exp $ 3 | ** 'ctype' functions for Lua 4 | ** See Copyright Notice in lua.h 5 | */ 6 | 7 | #ifndef lctype_h 8 | #define lctype_h 9 | 10 | #include "lua.h" 11 | 12 | 13 | /* 14 | ** WARNING: the functions defined here do not necessarily correspond 15 | ** to the similar functions in the standard C ctype.h. They are 16 | ** optimized for the specific needs of Lua 17 | */ 18 | 19 | #if !defined(LUA_USE_CTYPE) 20 | 21 | #if 'A' == 65 && '0' == 48 22 | /* ASCII case: can use its own tables; faster and fixed */ 23 | #define LUA_USE_CTYPE 0 24 | #else 25 | /* must use standard C ctype */ 26 | #define LUA_USE_CTYPE 1 27 | #endif 28 | 29 | #endif 30 | 31 | 32 | #if !LUA_USE_CTYPE /* { */ 33 | 34 | #include 35 | 36 | #include "llimits.h" 37 | 38 | 39 | #define ALPHABIT 0 40 | #define DIGITBIT 1 41 | #define PRINTBIT 2 42 | #define SPACEBIT 3 43 | #define XDIGITBIT 4 44 | 45 | 46 | #define MASK(B) (1 << (B)) 47 | 48 | 49 | /* 50 | ** add 1 to char to allow index -1 (EOZ) 51 | */ 52 | #define testprop(c,p) (luai_ctype_[(c)+1] & (p)) 53 | 54 | /* 55 | ** 'lalpha' (Lua alphabetic) and 'lalnum' (Lua alphanumeric) both include '_' 56 | */ 57 | #define lislalpha(c) testprop(c, MASK(ALPHABIT)) 58 | #define lislalnum(c) testprop(c, (MASK(ALPHABIT) | MASK(DIGITBIT))) 59 | #define lisdigit(c) testprop(c, MASK(DIGITBIT)) 60 | #define lisspace(c) testprop(c, MASK(SPACEBIT)) 61 | #define lisprint(c) testprop(c, MASK(PRINTBIT)) 62 | #define lisxdigit(c) testprop(c, MASK(XDIGITBIT)) 63 | 64 | /* 65 | ** this 'ltolower' only works for alphabetic characters 66 | */ 67 | #define ltolower(c) ((c) | ('A' ^ 'a')) 68 | 69 | 70 | /* two more entries for 0 and -1 (EOZ) */ 71 | LUAI_DDEC const lu_byte luai_ctype_[UCHAR_MAX + 2]; 72 | 73 | 74 | #else /* }{ */ 75 | 76 | /* 77 | ** use standard C ctypes 78 | */ 79 | 80 | #include 81 | 82 | 83 | #define lislalpha(c) (isalpha(c) || (c) == '_') 84 | #define lislalnum(c) (isalnum(c) || (c) == '_') 85 | #define lisdigit(c) (isdigit(c)) 86 | #define lisspace(c) (isspace(c)) 87 | #define lisprint(c) (isprint(c)) 88 | #define lisxdigit(c) (isxdigit(c)) 89 | 90 | #define ltolower(c) (tolower(c)) 91 | 92 | #endif /* } */ 93 | 94 | #endif 95 | 96 | -------------------------------------------------------------------------------- /lua/ldebug.h: -------------------------------------------------------------------------------- 1 | /* 2 | ** $Id: ldebug.h,v 2.14 2015/05/22 17:45:56 roberto Exp $ 3 | ** Auxiliary functions from Debug Interface module 4 | ** See Copyright Notice in lua.h 5 | */ 6 | 7 | #ifndef ldebug_h 8 | #define ldebug_h 9 | 10 | 11 | #include "lstate.h" 12 | 13 | 14 | #define pcRel(pc, p) (cast(int, (pc) - (p)->code) - 1) 15 | 16 | #define getfuncline(f,pc) (((f)->lineinfo) ? (f)->lineinfo[pc] : -1) 17 | 18 | #define resethookcount(L) (L->hookcount = L->basehookcount) 19 | 20 | 21 | LUAI_FUNC l_noret luaG_typeerror (lua_State *L, const TValue *o, 22 | const char *opname); 23 | LUAI_FUNC l_noret luaG_concaterror (lua_State *L, const TValue *p1, 24 | const TValue *p2); 25 | LUAI_FUNC l_noret luaG_opinterror (lua_State *L, const TValue *p1, 26 | const TValue *p2, 27 | const char *msg); 28 | LUAI_FUNC l_noret luaG_tointerror (lua_State *L, const TValue *p1, 29 | const TValue *p2); 30 | LUAI_FUNC l_noret luaG_ordererror (lua_State *L, const TValue *p1, 31 | const TValue *p2); 32 | LUAI_FUNC l_noret luaG_runerror (lua_State *L, const char *fmt, ...); 33 | LUAI_FUNC const char *luaG_addinfo (lua_State *L, const char *msg, 34 | TString *src, int line); 35 | LUAI_FUNC l_noret luaG_errormsg (lua_State *L); 36 | LUAI_FUNC void luaG_traceexec (lua_State *L); 37 | 38 | 39 | #endif 40 | -------------------------------------------------------------------------------- /lua/ldo.h: -------------------------------------------------------------------------------- 1 | /* 2 | ** $Id: ldo.h,v 2.29 2015/12/21 13:02:14 roberto Exp $ 3 | ** Stack and Call structure of Lua 4 | ** See Copyright Notice in lua.h 5 | */ 6 | 7 | #ifndef ldo_h 8 | #define ldo_h 9 | 10 | 11 | #include "lobject.h" 12 | #include "lstate.h" 13 | #include "lzio.h" 14 | 15 | 16 | /* 17 | ** Macro to check stack size and grow stack if needed. Parameters 18 | ** 'pre'/'pos' allow the macro to preserve a pointer into the 19 | ** stack across reallocations, doing the work only when needed. 20 | ** 'condmovestack' is used in heavy tests to force a stack reallocation 21 | ** at every check. 22 | */ 23 | #define luaD_checkstackaux(L,n,pre,pos) \ 24 | if (L->stack_last - L->top <= (n)) \ 25 | { pre; luaD_growstack(L, n); pos; } else { condmovestack(L,pre,pos); } 26 | 27 | /* In general, 'pre'/'pos' are empty (nothing to save) */ 28 | #define luaD_checkstack(L,n) luaD_checkstackaux(L,n,(void)0,(void)0) 29 | 30 | 31 | 32 | #define savestack(L,p) ((char *)(p) - (char *)L->stack) 33 | #define restorestack(L,n) ((TValue *)((char *)L->stack + (n))) 34 | 35 | 36 | /* type of protected functions, to be ran by 'runprotected' */ 37 | typedef void (*Pfunc) (lua_State *L, void *ud); 38 | 39 | LUAI_FUNC int luaD_protectedparser (lua_State *L, ZIO *z, const char *name, 40 | const char *mode); 41 | LUAI_FUNC void luaD_hook (lua_State *L, int event, int line); 42 | LUAI_FUNC int luaD_precall (lua_State *L, StkId func, int nresults); 43 | LUAI_FUNC void luaD_call (lua_State *L, StkId func, int nResults); 44 | LUAI_FUNC void luaD_callnoyield (lua_State *L, StkId func, int nResults); 45 | LUAI_FUNC int luaD_pcall (lua_State *L, Pfunc func, void *u, 46 | ptrdiff_t oldtop, ptrdiff_t ef); 47 | LUAI_FUNC int luaD_poscall (lua_State *L, CallInfo *ci, StkId firstResult, 48 | int nres); 49 | LUAI_FUNC void luaD_reallocstack (lua_State *L, int newsize); 50 | LUAI_FUNC void luaD_growstack (lua_State *L, int n); 51 | LUAI_FUNC void luaD_shrinkstack (lua_State *L); 52 | LUAI_FUNC void luaD_inctop (lua_State *L); 53 | 54 | LUAI_FUNC l_noret luaD_throw (lua_State *L, int errcode); 55 | LUAI_FUNC int luaD_rawrunprotected (lua_State *L, Pfunc f, void *ud); 56 | 57 | #endif 58 | 59 | -------------------------------------------------------------------------------- /lua/ldump.c: -------------------------------------------------------------------------------- 1 | /* 2 | ** $Id: ldump.c,v 2.37 2015/10/08 15:53:49 roberto Exp $ 3 | ** save precompiled Lua chunks 4 | ** See Copyright Notice in lua.h 5 | */ 6 | 7 | #define ldump_c 8 | #define LUA_CORE 9 | 10 | #include "lprefix.h" 11 | 12 | 13 | #include 14 | 15 | #include "lua.h" 16 | 17 | #include "lobject.h" 18 | #include "lstate.h" 19 | #include "lundump.h" 20 | 21 | 22 | typedef struct { 23 | lua_State *L; 24 | lua_Writer writer; 25 | void *data; 26 | int strip; 27 | int status; 28 | } DumpState; 29 | 30 | 31 | /* 32 | ** All high-level dumps go through DumpVector; you can change it to 33 | ** change the endianness of the result 34 | */ 35 | #define DumpVector(v,n,D) DumpBlock(v,(n)*sizeof((v)[0]),D) 36 | 37 | #define DumpLiteral(s,D) DumpBlock(s, sizeof(s) - sizeof(char), D) 38 | 39 | 40 | static void DumpBlock (const void *b, size_t size, DumpState *D) { 41 | if (D->status == 0 && size > 0) { 42 | lua_unlock(D->L); 43 | D->status = (*D->writer)(D->L, b, size, D->data); 44 | lua_lock(D->L); 45 | } 46 | } 47 | 48 | 49 | #define DumpVar(x,D) DumpVector(&x,1,D) 50 | 51 | 52 | static void DumpByte (int y, DumpState *D) { 53 | lu_byte x = (lu_byte)y; 54 | DumpVar(x, D); 55 | } 56 | 57 | 58 | static void DumpInt (int x, DumpState *D) { 59 | DumpVar(x, D); 60 | } 61 | 62 | 63 | static void DumpNumber (lua_Number x, DumpState *D) { 64 | DumpVar(x, D); 65 | } 66 | 67 | 68 | static void DumpInteger (lua_Integer x, DumpState *D) { 69 | DumpVar(x, D); 70 | } 71 | 72 | 73 | static void DumpString (const TString *s, DumpState *D) { 74 | if (s == NULL) 75 | DumpByte(0, D); 76 | else { 77 | size_t size = tsslen(s) + 1; /* include trailing '\0' */ 78 | const char *str = getstr(s); 79 | if (size < 0xFF) 80 | DumpByte(cast_int(size), D); 81 | else { 82 | DumpByte(0xFF, D); 83 | DumpVar(size, D); 84 | } 85 | DumpVector(str, size - 1, D); /* no need to save '\0' */ 86 | } 87 | } 88 | 89 | 90 | static void DumpCode (const Proto *f, DumpState *D) { 91 | DumpInt(f->sizecode, D); 92 | DumpVector(f->code, f->sizecode, D); 93 | } 94 | 95 | 96 | static void DumpFunction(const Proto *f, TString *psource, DumpState *D); 97 | 98 | static void DumpConstants (const Proto *f, DumpState *D) { 99 | int i; 100 | int n = f->sizek; 101 | DumpInt(n, D); 102 | for (i = 0; i < n; i++) { 103 | const TValue *o = &f->k[i]; 104 | DumpByte(ttype(o), D); 105 | switch (ttype(o)) { 106 | case LUA_TNIL: 107 | break; 108 | case LUA_TBOOLEAN: 109 | DumpByte(bvalue(o), D); 110 | break; 111 | case LUA_TNUMFLT: 112 | DumpNumber(fltvalue(o), D); 113 | break; 114 | case LUA_TNUMINT: 115 | DumpInteger(ivalue(o), D); 116 | break; 117 | case LUA_TSHRSTR: 118 | case LUA_TLNGSTR: 119 | DumpString(tsvalue(o), D); 120 | break; 121 | default: 122 | lua_assert(0); 123 | } 124 | } 125 | } 126 | 127 | 128 | static void DumpProtos (const Proto *f, DumpState *D) { 129 | int i; 130 | int n = f->sizep; 131 | DumpInt(n, D); 132 | for (i = 0; i < n; i++) 133 | DumpFunction(f->p[i], f->source, D); 134 | } 135 | 136 | 137 | static void DumpUpvalues (const Proto *f, DumpState *D) { 138 | int i, n = f->sizeupvalues; 139 | DumpInt(n, D); 140 | for (i = 0; i < n; i++) { 141 | DumpByte(f->upvalues[i].instack, D); 142 | DumpByte(f->upvalues[i].idx, D); 143 | } 144 | } 145 | 146 | 147 | static void DumpDebug (const Proto *f, DumpState *D) { 148 | int i, n; 149 | n = (D->strip) ? 0 : f->sizelineinfo; 150 | DumpInt(n, D); 151 | DumpVector(f->lineinfo, n, D); 152 | n = (D->strip) ? 0 : f->sizelocvars; 153 | DumpInt(n, D); 154 | for (i = 0; i < n; i++) { 155 | DumpString(f->locvars[i].varname, D); 156 | DumpInt(f->locvars[i].startpc, D); 157 | DumpInt(f->locvars[i].endpc, D); 158 | } 159 | n = (D->strip) ? 0 : f->sizeupvalues; 160 | DumpInt(n, D); 161 | for (i = 0; i < n; i++) 162 | DumpString(f->upvalues[i].name, D); 163 | } 164 | 165 | 166 | static void DumpFunction (const Proto *f, TString *psource, DumpState *D) { 167 | if (D->strip || f->source == psource) 168 | DumpString(NULL, D); /* no debug info or same source as its parent */ 169 | else 170 | DumpString(f->source, D); 171 | DumpInt(f->linedefined, D); 172 | DumpInt(f->lastlinedefined, D); 173 | DumpByte(f->numparams, D); 174 | DumpByte(f->is_vararg, D); 175 | DumpByte(f->maxstacksize, D); 176 | DumpCode(f, D); 177 | DumpConstants(f, D); 178 | DumpUpvalues(f, D); 179 | DumpProtos(f, D); 180 | DumpDebug(f, D); 181 | } 182 | 183 | 184 | static void DumpHeader (DumpState *D) { 185 | DumpLiteral(LUA_SIGNATURE, D); 186 | DumpByte(LUAC_VERSION, D); 187 | DumpByte(LUAC_FORMAT, D); 188 | DumpLiteral(LUAC_DATA, D); 189 | DumpByte(sizeof(int), D); 190 | DumpByte(sizeof(size_t), D); 191 | DumpByte(sizeof(Instruction), D); 192 | DumpByte(sizeof(lua_Integer), D); 193 | DumpByte(sizeof(lua_Number), D); 194 | DumpInteger(LUAC_INT, D); 195 | DumpNumber(LUAC_NUM, D); 196 | } 197 | 198 | 199 | /* 200 | ** dump Lua function as precompiled chunk 201 | */ 202 | int luaU_dump(lua_State *L, const Proto *f, lua_Writer w, void *data, 203 | int strip) { 204 | DumpState D; 205 | D.L = L; 206 | D.writer = w; 207 | D.data = data; 208 | D.strip = strip; 209 | D.status = 0; 210 | DumpHeader(&D); 211 | DumpByte(f->sizeupvalues, &D); 212 | DumpFunction(f, NULL, &D); 213 | return D.status; 214 | } 215 | 216 | -------------------------------------------------------------------------------- /lua/lfunc.c: -------------------------------------------------------------------------------- 1 | /* 2 | ** $Id: lfunc.c,v 2.45 2014/11/02 19:19:04 roberto Exp $ 3 | ** Auxiliary functions to manipulate prototypes and closures 4 | ** See Copyright Notice in lua.h 5 | */ 6 | 7 | #define lfunc_c 8 | #define LUA_CORE 9 | 10 | #include "lprefix.h" 11 | 12 | 13 | #include 14 | 15 | #include "lua.h" 16 | 17 | #include "lfunc.h" 18 | #include "lgc.h" 19 | #include "lmem.h" 20 | #include "lobject.h" 21 | #include "lstate.h" 22 | 23 | 24 | 25 | CClosure *luaF_newCclosure (lua_State *L, int n) { 26 | GCObject *o = luaC_newobj(L, LUA_TCCL, sizeCclosure(n)); 27 | CClosure *c = gco2ccl(o); 28 | c->nupvalues = cast_byte(n); 29 | return c; 30 | } 31 | 32 | 33 | LClosure *luaF_newLclosure (lua_State *L, int n) { 34 | GCObject *o = luaC_newobj(L, LUA_TLCL, sizeLclosure(n)); 35 | LClosure *c = gco2lcl(o); 36 | c->p = NULL; 37 | c->nupvalues = cast_byte(n); 38 | while (n--) c->upvals[n] = NULL; 39 | return c; 40 | } 41 | 42 | /* 43 | ** fill a closure with new closed upvalues 44 | */ 45 | void luaF_initupvals (lua_State *L, LClosure *cl) { 46 | int i; 47 | for (i = 0; i < cl->nupvalues; i++) { 48 | UpVal *uv = luaM_new(L, UpVal); 49 | uv->refcount = 1; 50 | uv->v = &uv->u.value; /* make it closed */ 51 | setnilvalue(uv->v); 52 | cl->upvals[i] = uv; 53 | } 54 | } 55 | 56 | 57 | UpVal *luaF_findupval (lua_State *L, StkId level) { 58 | UpVal **pp = &L->openupval; 59 | UpVal *p; 60 | UpVal *uv; 61 | lua_assert(isintwups(L) || L->openupval == NULL); 62 | while (*pp != NULL && (p = *pp)->v >= level) { 63 | lua_assert(upisopen(p)); 64 | if (p->v == level) /* found a corresponding upvalue? */ 65 | return p; /* return it */ 66 | pp = &p->u.open.next; 67 | } 68 | /* not found: create a new upvalue */ 69 | uv = luaM_new(L, UpVal); 70 | uv->refcount = 0; 71 | uv->u.open.next = *pp; /* link it to list of open upvalues */ 72 | uv->u.open.touched = 1; 73 | *pp = uv; 74 | uv->v = level; /* current value lives in the stack */ 75 | if (!isintwups(L)) { /* thread not in list of threads with upvalues? */ 76 | L->twups = G(L)->twups; /* link it to the list */ 77 | G(L)->twups = L; 78 | } 79 | return uv; 80 | } 81 | 82 | 83 | void luaF_close (lua_State *L, StkId level) { 84 | UpVal *uv; 85 | while (L->openupval != NULL && (uv = L->openupval)->v >= level) { 86 | lua_assert(upisopen(uv)); 87 | L->openupval = uv->u.open.next; /* remove from 'open' list */ 88 | if (uv->refcount == 0) /* no references? */ 89 | luaM_free(L, uv); /* free upvalue */ 90 | else { 91 | setobj(L, &uv->u.value, uv->v); /* move value to upvalue slot */ 92 | uv->v = &uv->u.value; /* now current value lives here */ 93 | luaC_upvalbarrier(L, uv); 94 | } 95 | } 96 | } 97 | 98 | 99 | Proto *luaF_newproto (lua_State *L) { 100 | GCObject *o = luaC_newobj(L, LUA_TPROTO, sizeof(Proto)); 101 | Proto *f = gco2p(o); 102 | f->k = NULL; 103 | f->sizek = 0; 104 | f->p = NULL; 105 | f->sizep = 0; 106 | f->code = NULL; 107 | f->cache = NULL; 108 | f->sizecode = 0; 109 | f->lineinfo = NULL; 110 | f->sizelineinfo = 0; 111 | f->upvalues = NULL; 112 | f->sizeupvalues = 0; 113 | f->numparams = 0; 114 | f->is_vararg = 0; 115 | f->maxstacksize = 0; 116 | f->locvars = NULL; 117 | f->sizelocvars = 0; 118 | f->linedefined = 0; 119 | f->lastlinedefined = 0; 120 | f->source = NULL; 121 | return f; 122 | } 123 | 124 | 125 | void luaF_freeproto (lua_State *L, Proto *f) { 126 | luaM_freearray(L, f->code, f->sizecode); 127 | luaM_freearray(L, f->p, f->sizep); 128 | luaM_freearray(L, f->k, f->sizek); 129 | luaM_freearray(L, f->lineinfo, f->sizelineinfo); 130 | luaM_freearray(L, f->locvars, f->sizelocvars); 131 | luaM_freearray(L, f->upvalues, f->sizeupvalues); 132 | luaM_free(L, f); 133 | } 134 | 135 | 136 | /* 137 | ** Look for n-th local variable at line 'line' in function 'func'. 138 | ** Returns NULL if not found. 139 | */ 140 | const char *luaF_getlocalname (const Proto *f, int local_number, int pc) { 141 | int i; 142 | for (i = 0; isizelocvars && f->locvars[i].startpc <= pc; i++) { 143 | if (pc < f->locvars[i].endpc) { /* is variable active? */ 144 | local_number--; 145 | if (local_number == 0) 146 | return getstr(f->locvars[i].varname); 147 | } 148 | } 149 | return NULL; /* not found */ 150 | } 151 | 152 | -------------------------------------------------------------------------------- /lua/lfunc.h: -------------------------------------------------------------------------------- 1 | /* 2 | ** $Id: lfunc.h,v 2.15 2015/01/13 15:49:11 roberto Exp $ 3 | ** Auxiliary functions to manipulate prototypes and closures 4 | ** See Copyright Notice in lua.h 5 | */ 6 | 7 | #ifndef lfunc_h 8 | #define lfunc_h 9 | 10 | 11 | #include "lobject.h" 12 | 13 | 14 | #define sizeCclosure(n) (cast(int, sizeof(CClosure)) + \ 15 | cast(int, sizeof(TValue)*((n)-1))) 16 | 17 | #define sizeLclosure(n) (cast(int, sizeof(LClosure)) + \ 18 | cast(int, sizeof(TValue *)*((n)-1))) 19 | 20 | 21 | /* test whether thread is in 'twups' list */ 22 | #define isintwups(L) (L->twups != L) 23 | 24 | 25 | /* 26 | ** maximum number of upvalues in a closure (both C and Lua). (Value 27 | ** must fit in a VM register.) 28 | */ 29 | #define MAXUPVAL 255 30 | 31 | 32 | /* 33 | ** Upvalues for Lua closures 34 | */ 35 | struct UpVal { 36 | TValue *v; /* points to stack or to its own value */ 37 | lu_mem refcount; /* reference counter */ 38 | union { 39 | struct { /* (when open) */ 40 | UpVal *next; /* linked list */ 41 | int touched; /* mark to avoid cycles with dead threads */ 42 | } open; 43 | TValue value; /* the value (when closed) */ 44 | } u; 45 | }; 46 | 47 | #define upisopen(up) ((up)->v != &(up)->u.value) 48 | 49 | 50 | LUAI_FUNC Proto *luaF_newproto (lua_State *L); 51 | LUAI_FUNC CClosure *luaF_newCclosure (lua_State *L, int nelems); 52 | LUAI_FUNC LClosure *luaF_newLclosure (lua_State *L, int nelems); 53 | LUAI_FUNC void luaF_initupvals (lua_State *L, LClosure *cl); 54 | LUAI_FUNC UpVal *luaF_findupval (lua_State *L, StkId level); 55 | LUAI_FUNC void luaF_close (lua_State *L, StkId level); 56 | LUAI_FUNC void luaF_freeproto (lua_State *L, Proto *f); 57 | LUAI_FUNC const char *luaF_getlocalname (const Proto *func, int local_number, 58 | int pc); 59 | 60 | 61 | #endif 62 | -------------------------------------------------------------------------------- /lua/lgc.h: -------------------------------------------------------------------------------- 1 | /* 2 | ** $Id: lgc.h,v 2.91 2015/12/21 13:02:14 roberto Exp $ 3 | ** Garbage Collector 4 | ** See Copyright Notice in lua.h 5 | */ 6 | 7 | #ifndef lgc_h 8 | #define lgc_h 9 | 10 | 11 | #include "lobject.h" 12 | #include "lstate.h" 13 | 14 | /* 15 | ** Collectable objects may have one of three colors: white, which 16 | ** means the object is not marked; gray, which means the 17 | ** object is marked, but its references may be not marked; and 18 | ** black, which means that the object and all its references are marked. 19 | ** The main invariant of the garbage collector, while marking objects, 20 | ** is that a black object can never point to a white one. Moreover, 21 | ** any gray object must be in a "gray list" (gray, grayagain, weak, 22 | ** allweak, ephemeron) so that it can be visited again before finishing 23 | ** the collection cycle. These lists have no meaning when the invariant 24 | ** is not being enforced (e.g., sweep phase). 25 | */ 26 | 27 | 28 | 29 | /* how much to allocate before next GC step */ 30 | #if !defined(GCSTEPSIZE) 31 | /* ~100 small strings */ 32 | #define GCSTEPSIZE (cast_int(100 * sizeof(TString))) 33 | #endif 34 | 35 | 36 | /* 37 | ** Possible states of the Garbage Collector 38 | */ 39 | #define GCSpropagate 0 40 | #define GCSatomic 1 41 | #define GCSswpallgc 2 42 | #define GCSswpfinobj 3 43 | #define GCSswptobefnz 4 44 | #define GCSswpend 5 45 | #define GCScallfin 6 46 | #define GCSpause 7 47 | 48 | 49 | #define issweepphase(g) \ 50 | (GCSswpallgc <= (g)->gcstate && (g)->gcstate <= GCSswpend) 51 | 52 | 53 | /* 54 | ** macro to tell when main invariant (white objects cannot point to black 55 | ** ones) must be kept. During a collection, the sweep 56 | ** phase may break the invariant, as objects turned white may point to 57 | ** still-black objects. The invariant is restored when sweep ends and 58 | ** all objects are white again. 59 | */ 60 | 61 | #define keepinvariant(g) ((g)->gcstate <= GCSatomic) 62 | 63 | 64 | /* 65 | ** some useful bit tricks 66 | */ 67 | #define resetbits(x,m) ((x) &= cast(lu_byte, ~(m))) 68 | #define setbits(x,m) ((x) |= (m)) 69 | #define testbits(x,m) ((x) & (m)) 70 | #define bitmask(b) (1<<(b)) 71 | #define bit2mask(b1,b2) (bitmask(b1) | bitmask(b2)) 72 | #define l_setbit(x,b) setbits(x, bitmask(b)) 73 | #define resetbit(x,b) resetbits(x, bitmask(b)) 74 | #define testbit(x,b) testbits(x, bitmask(b)) 75 | 76 | 77 | /* Layout for bit use in 'marked' field: */ 78 | #define WHITE0BIT 0 /* object is white (type 0) */ 79 | #define WHITE1BIT 1 /* object is white (type 1) */ 80 | #define BLACKBIT 2 /* object is black */ 81 | #define FINALIZEDBIT 3 /* object has been marked for finalization */ 82 | /* bit 7 is currently used by tests (luaL_checkmemory) */ 83 | 84 | #define WHITEBITS bit2mask(WHITE0BIT, WHITE1BIT) 85 | 86 | 87 | #define iswhite(x) testbits((x)->marked, WHITEBITS) 88 | #define isblack(x) testbit((x)->marked, BLACKBIT) 89 | #define isgray(x) /* neither white nor black */ \ 90 | (!testbits((x)->marked, WHITEBITS | bitmask(BLACKBIT))) 91 | 92 | #define tofinalize(x) testbit((x)->marked, FINALIZEDBIT) 93 | 94 | #define otherwhite(g) ((g)->currentwhite ^ WHITEBITS) 95 | #define isdeadm(ow,m) (!(((m) ^ WHITEBITS) & (ow))) 96 | #define isdead(g,v) isdeadm(otherwhite(g), (v)->marked) 97 | 98 | #define changewhite(x) ((x)->marked ^= WHITEBITS) 99 | #define gray2black(x) l_setbit((x)->marked, BLACKBIT) 100 | 101 | #define luaC_white(g) cast(lu_byte, (g)->currentwhite & WHITEBITS) 102 | 103 | 104 | /* 105 | ** Does one step of collection when debt becomes positive. 'pre'/'pos' 106 | ** allows some adjustments to be done only when needed. macro 107 | ** 'condchangemem' is used only for heavy tests (forcing a full 108 | ** GC cycle on every opportunity) 109 | */ 110 | #define luaC_condGC(L,pre,pos) \ 111 | { if (G(L)->GCdebt > 0) { pre; luaC_step(L); pos;}; \ 112 | condchangemem(L,pre,pos); } 113 | 114 | /* more often than not, 'pre'/'pos' are empty */ 115 | #define luaC_checkGC(L) luaC_condGC(L,(void)0,(void)0) 116 | 117 | 118 | #define luaC_barrier(L,p,v) ( \ 119 | (iscollectable(v) && isblack(p) && iswhite(gcvalue(v))) ? \ 120 | luaC_barrier_(L,obj2gco(p),gcvalue(v)) : cast_void(0)) 121 | 122 | #define luaC_barrierback(L,p,v) ( \ 123 | (iscollectable(v) && isblack(p) && iswhite(gcvalue(v))) ? \ 124 | luaC_barrierback_(L,p) : cast_void(0)) 125 | 126 | #define luaC_objbarrier(L,p,o) ( \ 127 | (isblack(p) && iswhite(o)) ? \ 128 | luaC_barrier_(L,obj2gco(p),obj2gco(o)) : cast_void(0)) 129 | 130 | #define luaC_upvalbarrier(L,uv) ( \ 131 | (iscollectable((uv)->v) && !upisopen(uv)) ? \ 132 | luaC_upvalbarrier_(L,uv) : cast_void(0)) 133 | 134 | LUAI_FUNC void luaC_fix (lua_State *L, GCObject *o); 135 | LUAI_FUNC void luaC_freeallobjects (lua_State *L); 136 | LUAI_FUNC void luaC_step (lua_State *L); 137 | LUAI_FUNC void luaC_runtilstate (lua_State *L, int statesmask); 138 | LUAI_FUNC void luaC_fullgc (lua_State *L, int isemergency); 139 | LUAI_FUNC GCObject *luaC_newobj (lua_State *L, int tt, size_t sz); 140 | LUAI_FUNC void luaC_barrier_ (lua_State *L, GCObject *o, GCObject *v); 141 | LUAI_FUNC void luaC_barrierback_ (lua_State *L, Table *o); 142 | LUAI_FUNC void luaC_upvalbarrier_ (lua_State *L, UpVal *uv); 143 | LUAI_FUNC void luaC_checkfinalizer (lua_State *L, GCObject *o, Table *mt); 144 | LUAI_FUNC void luaC_upvdeccount (lua_State *L, UpVal *uv); 145 | 146 | 147 | #endif 148 | -------------------------------------------------------------------------------- /lua/linit.c: -------------------------------------------------------------------------------- 1 | /* 2 | ** $Id: linit.c,v 1.38 2015/01/05 13:48:33 roberto Exp $ 3 | ** Initialization of libraries for lua.c and other clients 4 | ** See Copyright Notice in lua.h 5 | */ 6 | 7 | 8 | #define linit_c 9 | #define LUA_LIB 10 | 11 | /* 12 | ** If you embed Lua in your program and need to open the standard 13 | ** libraries, call luaL_openlibs in your program. If you need a 14 | ** different set of libraries, copy this file to your project and edit 15 | ** it to suit your needs. 16 | ** 17 | ** You can also *preload* libraries, so that a later 'require' can 18 | ** open the library, which is already linked to the application. 19 | ** For that, do the following code: 20 | ** 21 | ** luaL_getsubtable(L, LUA_REGISTRYINDEX, "_PRELOAD"); 22 | ** lua_pushcfunction(L, luaopen_modname); 23 | ** lua_setfield(L, -2, modname); 24 | ** lua_pop(L, 1); // remove _PRELOAD table 25 | */ 26 | 27 | #include "lprefix.h" 28 | 29 | 30 | #include 31 | 32 | #include "lua.h" 33 | 34 | #include "lualib.h" 35 | #include "lauxlib.h" 36 | 37 | 38 | /* 39 | ** these libs are loaded by lua.c and are readily available to any Lua 40 | ** program 41 | */ 42 | static const luaL_Reg loadedlibs[] = { 43 | {"_G", luaopen_base}, 44 | {LUA_LOADLIBNAME, luaopen_package}, 45 | {LUA_COLIBNAME, luaopen_coroutine}, 46 | {LUA_TABLIBNAME, luaopen_table}, 47 | {LUA_IOLIBNAME, luaopen_io}, 48 | {LUA_OSLIBNAME, luaopen_os}, 49 | {LUA_STRLIBNAME, luaopen_string}, 50 | {LUA_MATHLIBNAME, luaopen_math}, 51 | {LUA_UTF8LIBNAME, luaopen_utf8}, 52 | {LUA_DBLIBNAME, luaopen_debug}, 53 | #if defined(LUA_COMPAT_BITLIB) 54 | {LUA_BITLIBNAME, luaopen_bit32}, 55 | #endif 56 | {NULL, NULL} 57 | }; 58 | 59 | 60 | LUALIB_API void luaL_openlibs (lua_State *L) { 61 | const luaL_Reg *lib; 62 | /* "require" functions from 'loadedlibs' and set results to global table */ 63 | for (lib = loadedlibs; lib->func; lib++) { 64 | luaL_requiref(L, lib->name, lib->func, 1); 65 | lua_pop(L, 1); /* remove lib */ 66 | } 67 | } 68 | 69 | -------------------------------------------------------------------------------- /lua/llex.h: -------------------------------------------------------------------------------- 1 | /* 2 | ** $Id: llex.h,v 1.79 2016/05/02 14:02:12 roberto Exp $ 3 | ** Lexical Analyzer 4 | ** See Copyright Notice in lua.h 5 | */ 6 | 7 | #ifndef llex_h 8 | #define llex_h 9 | 10 | #include "lobject.h" 11 | #include "lzio.h" 12 | 13 | 14 | #define FIRST_RESERVED 257 15 | 16 | 17 | #if !defined(LUA_ENV) 18 | #define LUA_ENV "_ENV" 19 | #endif 20 | 21 | 22 | /* 23 | * WARNING: if you change the order of this enumeration, 24 | * grep "ORDER RESERVED" 25 | */ 26 | enum RESERVED { 27 | /* terminal symbols denoted by reserved words */ 28 | TK_AND = FIRST_RESERVED, TK_BREAK, 29 | TK_DO, TK_ELSE, TK_ELSEIF, TK_END, TK_FALSE, TK_FOR, TK_FUNCTION, 30 | TK_GOTO, TK_IF, TK_IN, TK_LOCAL, TK_NIL, TK_NOT, TK_OR, TK_REPEAT, 31 | TK_RETURN, TK_THEN, TK_TRUE, TK_UNTIL, TK_WHILE, 32 | /* other terminal symbols */ 33 | TK_IDIV, TK_CONCAT, TK_DOTS, TK_EQ, TK_GE, TK_LE, TK_NE, 34 | TK_SHL, TK_SHR, 35 | TK_DBCOLON, TK_EOS, 36 | TK_FLT, TK_INT, TK_NAME, TK_STRING 37 | }; 38 | 39 | /* number of reserved words */ 40 | #define NUM_RESERVED (cast(int, TK_WHILE-FIRST_RESERVED+1)) 41 | 42 | 43 | typedef union { 44 | lua_Number r; 45 | lua_Integer i; 46 | TString *ts; 47 | } SemInfo; /* semantics information */ 48 | 49 | 50 | typedef struct Token { 51 | int token; 52 | SemInfo seminfo; 53 | } Token; 54 | 55 | 56 | /* state of the lexer plus state of the parser when shared by all 57 | functions */ 58 | typedef struct LexState { 59 | int current; /* current character (charint) */ 60 | int linenumber; /* input line counter */ 61 | int lastline; /* line of last token 'consumed' */ 62 | Token t; /* current token */ 63 | Token lookahead; /* look ahead token */ 64 | struct FuncState *fs; /* current function (parser) */ 65 | struct lua_State *L; 66 | ZIO *z; /* input stream */ 67 | Mbuffer *buff; /* buffer for tokens */ 68 | Table *h; /* to avoid collection/reuse strings */ 69 | struct Dyndata *dyd; /* dynamic structures used by the parser */ 70 | TString *source; /* current source name */ 71 | TString *envn; /* environment variable name */ 72 | } LexState; 73 | 74 | 75 | LUAI_FUNC void luaX_init (lua_State *L); 76 | LUAI_FUNC void luaX_setinput (lua_State *L, LexState *ls, ZIO *z, 77 | TString *source, int firstchar); 78 | LUAI_FUNC TString *luaX_newstring (LexState *ls, const char *str, size_t l); 79 | LUAI_FUNC void luaX_next (LexState *ls); 80 | LUAI_FUNC int luaX_lookahead (LexState *ls); 81 | LUAI_FUNC l_noret luaX_syntaxerror (LexState *ls, const char *s); 82 | LUAI_FUNC const char *luaX_token2str (LexState *ls, int token); 83 | 84 | 85 | #endif 86 | -------------------------------------------------------------------------------- /lua/lmem.c: -------------------------------------------------------------------------------- 1 | /* 2 | ** $Id: lmem.c,v 1.91 2015/03/06 19:45:54 roberto Exp $ 3 | ** Interface to Memory Manager 4 | ** See Copyright Notice in lua.h 5 | */ 6 | 7 | #define lmem_c 8 | #define LUA_CORE 9 | 10 | #include "lprefix.h" 11 | 12 | 13 | #include 14 | 15 | #include "lua.h" 16 | 17 | #include "ldebug.h" 18 | #include "ldo.h" 19 | #include "lgc.h" 20 | #include "lmem.h" 21 | #include "lobject.h" 22 | #include "lstate.h" 23 | 24 | 25 | 26 | /* 27 | ** About the realloc function: 28 | ** void * frealloc (void *ud, void *ptr, size_t osize, size_t nsize); 29 | ** ('osize' is the old size, 'nsize' is the new size) 30 | ** 31 | ** * frealloc(ud, NULL, x, s) creates a new block of size 's' (no 32 | ** matter 'x'). 33 | ** 34 | ** * frealloc(ud, p, x, 0) frees the block 'p' 35 | ** (in this specific case, frealloc must return NULL); 36 | ** particularly, frealloc(ud, NULL, 0, 0) does nothing 37 | ** (which is equivalent to free(NULL) in ISO C) 38 | ** 39 | ** frealloc returns NULL if it cannot create or reallocate the area 40 | ** (any reallocation to an equal or smaller size cannot fail!) 41 | */ 42 | 43 | 44 | 45 | #define MINSIZEARRAY 4 46 | 47 | 48 | void *luaM_growaux_ (lua_State *L, void *block, int *size, size_t size_elems, 49 | int limit, const char *what) { 50 | void *newblock; 51 | int newsize; 52 | if (*size >= limit/2) { /* cannot double it? */ 53 | if (*size >= limit) /* cannot grow even a little? */ 54 | luaG_runerror(L, "too many %s (limit is %d)", what, limit); 55 | newsize = limit; /* still have at least one free place */ 56 | } 57 | else { 58 | newsize = (*size)*2; 59 | if (newsize < MINSIZEARRAY) 60 | newsize = MINSIZEARRAY; /* minimum size */ 61 | } 62 | newblock = luaM_reallocv(L, block, *size, newsize, size_elems); 63 | *size = newsize; /* update only when everything else is OK */ 64 | return newblock; 65 | } 66 | 67 | 68 | l_noret luaM_toobig (lua_State *L) { 69 | luaG_runerror(L, "memory allocation error: block too big"); 70 | } 71 | 72 | 73 | 74 | /* 75 | ** generic allocation routine. 76 | */ 77 | void *luaM_realloc_ (lua_State *L, void *block, size_t osize, size_t nsize) { 78 | void *newblock; 79 | global_State *g = G(L); 80 | size_t realosize = (block) ? osize : 0; 81 | lua_assert((realosize == 0) == (block == NULL)); 82 | #if defined(HARDMEMTESTS) 83 | if (nsize > realosize && g->gcrunning) 84 | luaC_fullgc(L, 1); /* force a GC whenever possible */ 85 | #endif 86 | newblock = (*g->frealloc)(g->ud, block, osize, nsize); 87 | if (newblock == NULL && nsize > 0) { 88 | lua_assert(nsize > realosize); /* cannot fail when shrinking a block */ 89 | if (g->version) { /* is state fully built? */ 90 | luaC_fullgc(L, 1); /* try to free some memory... */ 91 | newblock = (*g->frealloc)(g->ud, block, osize, nsize); /* try again */ 92 | } 93 | if (newblock == NULL) 94 | luaD_throw(L, LUA_ERRMEM); 95 | } 96 | lua_assert((nsize == 0) == (newblock == NULL)); 97 | g->GCdebt = (g->GCdebt + nsize) - realosize; 98 | return newblock; 99 | } 100 | 101 | -------------------------------------------------------------------------------- /lua/lmem.h: -------------------------------------------------------------------------------- 1 | /* 2 | ** $Id: lmem.h,v 1.43 2014/12/19 17:26:14 roberto Exp $ 3 | ** Interface to Memory Manager 4 | ** See Copyright Notice in lua.h 5 | */ 6 | 7 | #ifndef lmem_h 8 | #define lmem_h 9 | 10 | 11 | #include 12 | 13 | #include "llimits.h" 14 | #include "lua.h" 15 | 16 | 17 | /* 18 | ** This macro reallocs a vector 'b' from 'on' to 'n' elements, where 19 | ** each element has size 'e'. In case of arithmetic overflow of the 20 | ** product 'n'*'e', it raises an error (calling 'luaM_toobig'). Because 21 | ** 'e' is always constant, it avoids the runtime division MAX_SIZET/(e). 22 | ** 23 | ** (The macro is somewhat complex to avoid warnings: The 'sizeof' 24 | ** comparison avoids a runtime comparison when overflow cannot occur. 25 | ** The compiler should be able to optimize the real test by itself, but 26 | ** when it does it, it may give a warning about "comparison is always 27 | ** false due to limited range of data type"; the +1 tricks the compiler, 28 | ** avoiding this warning but also this optimization.) 29 | */ 30 | #define luaM_reallocv(L,b,on,n,e) \ 31 | (((sizeof(n) >= sizeof(size_t) && cast(size_t, (n)) + 1 > MAX_SIZET/(e)) \ 32 | ? luaM_toobig(L) : cast_void(0)) , \ 33 | luaM_realloc_(L, (b), (on)*(e), (n)*(e))) 34 | 35 | /* 36 | ** Arrays of chars do not need any test 37 | */ 38 | #define luaM_reallocvchar(L,b,on,n) \ 39 | cast(char *, luaM_realloc_(L, (b), (on)*sizeof(char), (n)*sizeof(char))) 40 | 41 | #define luaM_freemem(L, b, s) luaM_realloc_(L, (b), (s), 0) 42 | #define luaM_free(L, b) luaM_realloc_(L, (b), sizeof(*(b)), 0) 43 | #define luaM_freearray(L, b, n) luaM_realloc_(L, (b), (n)*sizeof(*(b)), 0) 44 | 45 | #define luaM_malloc(L,s) luaM_realloc_(L, NULL, 0, (s)) 46 | #define luaM_new(L,t) cast(t *, luaM_malloc(L, sizeof(t))) 47 | #define luaM_newvector(L,n,t) \ 48 | cast(t *, luaM_reallocv(L, NULL, 0, n, sizeof(t))) 49 | 50 | #define luaM_newobject(L,tag,s) luaM_realloc_(L, NULL, tag, (s)) 51 | 52 | #define luaM_growvector(L,v,nelems,size,t,limit,e) \ 53 | if ((nelems)+1 > (size)) \ 54 | ((v)=cast(t *, luaM_growaux_(L,v,&(size),sizeof(t),limit,e))) 55 | 56 | #define luaM_reallocvector(L, v,oldn,n,t) \ 57 | ((v)=cast(t *, luaM_reallocv(L, v, oldn, n, sizeof(t)))) 58 | 59 | LUAI_FUNC l_noret luaM_toobig (lua_State *L); 60 | 61 | /* not to be called directly */ 62 | LUAI_FUNC void *luaM_realloc_ (lua_State *L, void *block, size_t oldsize, 63 | size_t size); 64 | LUAI_FUNC void *luaM_growaux_ (lua_State *L, void *block, int *size, 65 | size_t size_elem, int limit, 66 | const char *what); 67 | 68 | #endif 69 | 70 | -------------------------------------------------------------------------------- /lua/lopcodes.c: -------------------------------------------------------------------------------- 1 | /* 2 | ** $Id: lopcodes.c,v 1.55 2015/01/05 13:48:33 roberto Exp $ 3 | ** Opcodes for Lua virtual machine 4 | ** See Copyright Notice in lua.h 5 | */ 6 | 7 | #define lopcodes_c 8 | #define LUA_CORE 9 | 10 | #include "lprefix.h" 11 | 12 | 13 | #include 14 | 15 | #include "lopcodes.h" 16 | 17 | 18 | /* ORDER OP */ 19 | 20 | LUAI_DDEF const char *const luaP_opnames[NUM_OPCODES+1] = { 21 | "MOVE", 22 | "LOADK", 23 | "LOADKX", 24 | "LOADBOOL", 25 | "LOADNIL", 26 | "GETUPVAL", 27 | "GETTABUP", 28 | "GETTABLE", 29 | "SETTABUP", 30 | "SETUPVAL", 31 | "SETTABLE", 32 | "NEWTABLE", 33 | "SELF", 34 | "ADD", 35 | "SUB", 36 | "MUL", 37 | "MOD", 38 | "POW", 39 | "DIV", 40 | "IDIV", 41 | "BAND", 42 | "BOR", 43 | "BXOR", 44 | "SHL", 45 | "SHR", 46 | "UNM", 47 | "BNOT", 48 | "NOT", 49 | "LEN", 50 | "CONCAT", 51 | "JMP", 52 | "EQ", 53 | "LT", 54 | "LE", 55 | "TEST", 56 | "TESTSET", 57 | "CALL", 58 | "TAILCALL", 59 | "RETURN", 60 | "FORLOOP", 61 | "FORPREP", 62 | "TFORCALL", 63 | "TFORLOOP", 64 | "SETLIST", 65 | "CLOSURE", 66 | "VARARG", 67 | "EXTRAARG", 68 | NULL 69 | }; 70 | 71 | 72 | #define opmode(t,a,b,c,m) (((t)<<7) | ((a)<<6) | ((b)<<4) | ((c)<<2) | (m)) 73 | 74 | LUAI_DDEF const lu_byte luaP_opmodes[NUM_OPCODES] = { 75 | /* T A B C mode opcode */ 76 | opmode(0, 1, OpArgR, OpArgN, iABC) /* OP_MOVE */ 77 | ,opmode(0, 1, OpArgK, OpArgN, iABx) /* OP_LOADK */ 78 | ,opmode(0, 1, OpArgN, OpArgN, iABx) /* OP_LOADKX */ 79 | ,opmode(0, 1, OpArgU, OpArgU, iABC) /* OP_LOADBOOL */ 80 | ,opmode(0, 1, OpArgU, OpArgN, iABC) /* OP_LOADNIL */ 81 | ,opmode(0, 1, OpArgU, OpArgN, iABC) /* OP_GETUPVAL */ 82 | ,opmode(0, 1, OpArgU, OpArgK, iABC) /* OP_GETTABUP */ 83 | ,opmode(0, 1, OpArgR, OpArgK, iABC) /* OP_GETTABLE */ 84 | ,opmode(0, 0, OpArgK, OpArgK, iABC) /* OP_SETTABUP */ 85 | ,opmode(0, 0, OpArgU, OpArgN, iABC) /* OP_SETUPVAL */ 86 | ,opmode(0, 0, OpArgK, OpArgK, iABC) /* OP_SETTABLE */ 87 | ,opmode(0, 1, OpArgU, OpArgU, iABC) /* OP_NEWTABLE */ 88 | ,opmode(0, 1, OpArgR, OpArgK, iABC) /* OP_SELF */ 89 | ,opmode(0, 1, OpArgK, OpArgK, iABC) /* OP_ADD */ 90 | ,opmode(0, 1, OpArgK, OpArgK, iABC) /* OP_SUB */ 91 | ,opmode(0, 1, OpArgK, OpArgK, iABC) /* OP_MUL */ 92 | ,opmode(0, 1, OpArgK, OpArgK, iABC) /* OP_MOD */ 93 | ,opmode(0, 1, OpArgK, OpArgK, iABC) /* OP_POW */ 94 | ,opmode(0, 1, OpArgK, OpArgK, iABC) /* OP_DIV */ 95 | ,opmode(0, 1, OpArgK, OpArgK, iABC) /* OP_IDIV */ 96 | ,opmode(0, 1, OpArgK, OpArgK, iABC) /* OP_BAND */ 97 | ,opmode(0, 1, OpArgK, OpArgK, iABC) /* OP_BOR */ 98 | ,opmode(0, 1, OpArgK, OpArgK, iABC) /* OP_BXOR */ 99 | ,opmode(0, 1, OpArgK, OpArgK, iABC) /* OP_SHL */ 100 | ,opmode(0, 1, OpArgK, OpArgK, iABC) /* OP_SHR */ 101 | ,opmode(0, 1, OpArgR, OpArgN, iABC) /* OP_UNM */ 102 | ,opmode(0, 1, OpArgR, OpArgN, iABC) /* OP_BNOT */ 103 | ,opmode(0, 1, OpArgR, OpArgN, iABC) /* OP_NOT */ 104 | ,opmode(0, 1, OpArgR, OpArgN, iABC) /* OP_LEN */ 105 | ,opmode(0, 1, OpArgR, OpArgR, iABC) /* OP_CONCAT */ 106 | ,opmode(0, 0, OpArgR, OpArgN, iAsBx) /* OP_JMP */ 107 | ,opmode(1, 0, OpArgK, OpArgK, iABC) /* OP_EQ */ 108 | ,opmode(1, 0, OpArgK, OpArgK, iABC) /* OP_LT */ 109 | ,opmode(1, 0, OpArgK, OpArgK, iABC) /* OP_LE */ 110 | ,opmode(1, 0, OpArgN, OpArgU, iABC) /* OP_TEST */ 111 | ,opmode(1, 1, OpArgR, OpArgU, iABC) /* OP_TESTSET */ 112 | ,opmode(0, 1, OpArgU, OpArgU, iABC) /* OP_CALL */ 113 | ,opmode(0, 1, OpArgU, OpArgU, iABC) /* OP_TAILCALL */ 114 | ,opmode(0, 0, OpArgU, OpArgN, iABC) /* OP_RETURN */ 115 | ,opmode(0, 1, OpArgR, OpArgN, iAsBx) /* OP_FORLOOP */ 116 | ,opmode(0, 1, OpArgR, OpArgN, iAsBx) /* OP_FORPREP */ 117 | ,opmode(0, 0, OpArgN, OpArgU, iABC) /* OP_TFORCALL */ 118 | ,opmode(0, 1, OpArgR, OpArgN, iAsBx) /* OP_TFORLOOP */ 119 | ,opmode(0, 0, OpArgU, OpArgU, iABC) /* OP_SETLIST */ 120 | ,opmode(0, 1, OpArgU, OpArgN, iABx) /* OP_CLOSURE */ 121 | ,opmode(0, 1, OpArgU, OpArgN, iABC) /* OP_VARARG */ 122 | ,opmode(0, 0, OpArgU, OpArgU, iAx) /* OP_EXTRAARG */ 123 | }; 124 | 125 | -------------------------------------------------------------------------------- /lua/lparser.h: -------------------------------------------------------------------------------- 1 | /* 2 | ** $Id: lparser.h,v 1.76 2015/12/30 18:16:13 roberto Exp $ 3 | ** Lua Parser 4 | ** See Copyright Notice in lua.h 5 | */ 6 | 7 | #ifndef lparser_h 8 | #define lparser_h 9 | 10 | #include "llimits.h" 11 | #include "lobject.h" 12 | #include "lzio.h" 13 | 14 | 15 | /* 16 | ** Expression and variable descriptor. 17 | ** Code generation for variables and expressions can be delayed to allow 18 | ** optimizations; An 'expdesc' structure describes a potentially-delayed 19 | ** variable/expression. It has a description of its "main" value plus a 20 | ** list of conditional jumps that can also produce its value (generated 21 | ** by short-circuit operators 'and'/'or'). 22 | */ 23 | 24 | /* kinds of variables/expressions */ 25 | typedef enum { 26 | VVOID, /* when 'expdesc' describes the last expression a list, 27 | this kind means an empty list (so, no expression) */ 28 | VNIL, /* constant nil */ 29 | VTRUE, /* constant true */ 30 | VFALSE, /* constant false */ 31 | VK, /* constant in 'k'; info = index of constant in 'k' */ 32 | VKFLT, /* floating constant; nval = numerical float value */ 33 | VKINT, /* integer constant; nval = numerical integer value */ 34 | VNONRELOC, /* expression has its value in a fixed register; 35 | info = result register */ 36 | VLOCAL, /* local variable; info = local register */ 37 | VUPVAL, /* upvalue variable; info = index of upvalue in 'upvalues' */ 38 | VINDEXED, /* indexed variable; 39 | ind.vt = whether 't' is register or upvalue; 40 | ind.t = table register or upvalue; 41 | ind.idx = key's R/K index */ 42 | VJMP, /* expression is a test/comparison; 43 | info = pc of corresponding jump instruction */ 44 | VRELOCABLE, /* expression can put result in any register; 45 | info = instruction pc */ 46 | VCALL, /* expression is a function call; info = instruction pc */ 47 | VVARARG /* vararg expression; info = instruction pc */ 48 | } expkind; 49 | 50 | 51 | #define vkisvar(k) (VLOCAL <= (k) && (k) <= VINDEXED) 52 | #define vkisinreg(k) ((k) == VNONRELOC || (k) == VLOCAL) 53 | 54 | typedef struct expdesc { 55 | expkind k; 56 | union { 57 | lua_Integer ival; /* for VKINT */ 58 | lua_Number nval; /* for VKFLT */ 59 | int info; /* for generic use */ 60 | struct { /* for indexed variables (VINDEXED) */ 61 | short idx; /* index (R/K) */ 62 | lu_byte t; /* table (register or upvalue) */ 63 | lu_byte vt; /* whether 't' is register (VLOCAL) or upvalue (VUPVAL) */ 64 | } ind; 65 | } u; 66 | int t; /* patch list of 'exit when true' */ 67 | int f; /* patch list of 'exit when false' */ 68 | } expdesc; 69 | 70 | 71 | /* description of active local variable */ 72 | typedef struct Vardesc { 73 | short idx; /* variable index in stack */ 74 | } Vardesc; 75 | 76 | 77 | /* description of pending goto statements and label statements */ 78 | typedef struct Labeldesc { 79 | TString *name; /* label identifier */ 80 | int pc; /* position in code */ 81 | int line; /* line where it appeared */ 82 | lu_byte nactvar; /* local level where it appears in current block */ 83 | } Labeldesc; 84 | 85 | 86 | /* list of labels or gotos */ 87 | typedef struct Labellist { 88 | Labeldesc *arr; /* array */ 89 | int n; /* number of entries in use */ 90 | int size; /* array size */ 91 | } Labellist; 92 | 93 | 94 | /* dynamic structures used by the parser */ 95 | typedef struct Dyndata { 96 | struct { /* list of active local variables */ 97 | Vardesc *arr; 98 | int n; 99 | int size; 100 | } actvar; 101 | Labellist gt; /* list of pending gotos */ 102 | Labellist label; /* list of active labels */ 103 | } Dyndata; 104 | 105 | 106 | /* control of blocks */ 107 | struct BlockCnt; /* defined in lparser.c */ 108 | 109 | 110 | /* state needed to generate code for a given function */ 111 | typedef struct FuncState { 112 | Proto *f; /* current function header */ 113 | struct FuncState *prev; /* enclosing function */ 114 | struct LexState *ls; /* lexical state */ 115 | struct BlockCnt *bl; /* chain of current blocks */ 116 | int pc; /* next position to code (equivalent to 'ncode') */ 117 | int lasttarget; /* 'label' of last 'jump label' */ 118 | int jpc; /* list of pending jumps to 'pc' */ 119 | int nk; /* number of elements in 'k' */ 120 | int np; /* number of elements in 'p' */ 121 | int firstlocal; /* index of first local var (in Dyndata array) */ 122 | short nlocvars; /* number of elements in 'f->locvars' */ 123 | lu_byte nactvar; /* number of active local variables */ 124 | lu_byte nups; /* number of upvalues */ 125 | lu_byte freereg; /* first free register */ 126 | } FuncState; 127 | 128 | 129 | LUAI_FUNC LClosure *luaY_parser (lua_State *L, ZIO *z, Mbuffer *buff, 130 | Dyndata *dyd, const char *name, int firstchar); 131 | 132 | 133 | #endif 134 | -------------------------------------------------------------------------------- /lua/lprefix.h: -------------------------------------------------------------------------------- 1 | /* 2 | ** $Id: lprefix.h,v 1.2 2014/12/29 16:54:13 roberto Exp $ 3 | ** Definitions for Lua code that must come before any other header file 4 | ** See Copyright Notice in lua.h 5 | */ 6 | 7 | #ifndef lprefix_h 8 | #define lprefix_h 9 | 10 | 11 | /* 12 | ** Allows POSIX/XSI stuff 13 | */ 14 | #if !defined(LUA_USE_C89) /* { */ 15 | 16 | #if !defined(_XOPEN_SOURCE) 17 | #define _XOPEN_SOURCE 600 18 | #elif _XOPEN_SOURCE == 0 19 | #undef _XOPEN_SOURCE /* use -D_XOPEN_SOURCE=0 to undefine it */ 20 | #endif 21 | 22 | /* 23 | ** Allows manipulation of large files in gcc and some other compilers 24 | */ 25 | #if !defined(LUA_32BITS) && !defined(_FILE_OFFSET_BITS) 26 | #define _LARGEFILE_SOURCE 1 27 | #define _FILE_OFFSET_BITS 64 28 | #endif 29 | 30 | #endif /* } */ 31 | 32 | 33 | /* 34 | ** Windows stuff 35 | */ 36 | #if defined(_WIN32) /* { */ 37 | 38 | #if !defined(_CRT_SECURE_NO_WARNINGS) 39 | #define _CRT_SECURE_NO_WARNINGS /* avoid warnings about ISO C functions */ 40 | #endif 41 | 42 | #endif /* } */ 43 | 44 | #endif 45 | 46 | -------------------------------------------------------------------------------- /lua/lstring.h: -------------------------------------------------------------------------------- 1 | /* 2 | ** $Id: lstring.h,v 1.61 2015/11/03 15:36:01 roberto Exp $ 3 | ** String table (keep all strings handled by Lua) 4 | ** See Copyright Notice in lua.h 5 | */ 6 | 7 | #ifndef lstring_h 8 | #define lstring_h 9 | 10 | #include "lgc.h" 11 | #include "lobject.h" 12 | #include "lstate.h" 13 | 14 | 15 | #define sizelstring(l) (sizeof(union UTString) + ((l) + 1) * sizeof(char)) 16 | 17 | #define sizeludata(l) (sizeof(union UUdata) + (l)) 18 | #define sizeudata(u) sizeludata((u)->len) 19 | 20 | #define luaS_newliteral(L, s) (luaS_newlstr(L, "" s, \ 21 | (sizeof(s)/sizeof(char))-1)) 22 | 23 | 24 | /* 25 | ** test whether a string is a reserved word 26 | */ 27 | #define isreserved(s) ((s)->tt == LUA_TSHRSTR && (s)->extra > 0) 28 | 29 | 30 | /* 31 | ** equality for short strings, which are always internalized 32 | */ 33 | #define eqshrstr(a,b) check_exp((a)->tt == LUA_TSHRSTR, (a) == (b)) 34 | 35 | 36 | LUAI_FUNC unsigned int luaS_hash (const char *str, size_t l, unsigned int seed); 37 | LUAI_FUNC unsigned int luaS_hashlongstr (TString *ts); 38 | LUAI_FUNC int luaS_eqlngstr (TString *a, TString *b); 39 | LUAI_FUNC void luaS_resize (lua_State *L, int newsize); 40 | LUAI_FUNC void luaS_clearcache (global_State *g); 41 | LUAI_FUNC void luaS_init (lua_State *L); 42 | LUAI_FUNC void luaS_remove (lua_State *L, TString *ts); 43 | LUAI_FUNC Udata *luaS_newudata (lua_State *L, size_t s); 44 | LUAI_FUNC TString *luaS_newlstr (lua_State *L, const char *str, size_t l); 45 | LUAI_FUNC TString *luaS_new (lua_State *L, const char *str); 46 | LUAI_FUNC TString *luaS_createlngstrobj (lua_State *L, size_t l); 47 | 48 | 49 | #endif 50 | -------------------------------------------------------------------------------- /lua/ltable.h: -------------------------------------------------------------------------------- 1 | /* 2 | ** $Id: ltable.h,v 2.21 2015/11/03 15:47:30 roberto Exp $ 3 | ** Lua tables (hash) 4 | ** See Copyright Notice in lua.h 5 | */ 6 | 7 | #ifndef ltable_h 8 | #define ltable_h 9 | 10 | #include "lobject.h" 11 | 12 | 13 | #define gnode(t,i) (&(t)->node[i]) 14 | #define gval(n) (&(n)->i_val) 15 | #define gnext(n) ((n)->i_key.nk.next) 16 | 17 | 18 | /* 'const' to avoid wrong writings that can mess up field 'next' */ 19 | #define gkey(n) cast(const TValue*, (&(n)->i_key.tvk)) 20 | 21 | /* 22 | ** writable version of 'gkey'; allows updates to individual fields, 23 | ** but not to the whole (which has incompatible type) 24 | */ 25 | #define wgkey(n) (&(n)->i_key.nk) 26 | 27 | #define invalidateTMcache(t) ((t)->flags = 0) 28 | 29 | 30 | /* returns the key, given the value of a table entry */ 31 | #define keyfromval(v) \ 32 | (gkey(cast(Node *, cast(char *, (v)) - offsetof(Node, i_val)))) 33 | 34 | 35 | LUAI_FUNC const TValue *luaH_getint (Table *t, lua_Integer key); 36 | LUAI_FUNC void luaH_setint (lua_State *L, Table *t, lua_Integer key, 37 | TValue *value); 38 | LUAI_FUNC const TValue *luaH_getshortstr (Table *t, TString *key); 39 | LUAI_FUNC const TValue *luaH_getstr (Table *t, TString *key); 40 | LUAI_FUNC const TValue *luaH_get (Table *t, const TValue *key); 41 | LUAI_FUNC TValue *luaH_newkey (lua_State *L, Table *t, const TValue *key); 42 | LUAI_FUNC TValue *luaH_set (lua_State *L, Table *t, const TValue *key); 43 | LUAI_FUNC Table *luaH_new (lua_State *L); 44 | LUAI_FUNC void luaH_resize (lua_State *L, Table *t, unsigned int nasize, 45 | unsigned int nhsize); 46 | LUAI_FUNC void luaH_resizearray (lua_State *L, Table *t, unsigned int nasize); 47 | LUAI_FUNC void luaH_free (lua_State *L, Table *t); 48 | LUAI_FUNC int luaH_next (lua_State *L, Table *t, StkId key); 49 | LUAI_FUNC int luaH_getn (Table *t); 50 | 51 | 52 | #if defined(LUA_DEBUG) 53 | LUAI_FUNC Node *luaH_mainposition (const Table *t, const TValue *key); 54 | LUAI_FUNC int luaH_isdummy (Node *n); 55 | #endif 56 | 57 | 58 | #endif 59 | -------------------------------------------------------------------------------- /lua/ltm.c: -------------------------------------------------------------------------------- 1 | /* 2 | ** $Id: ltm.c,v 2.37 2016/02/26 19:20:15 roberto Exp $ 3 | ** Tag methods 4 | ** See Copyright Notice in lua.h 5 | */ 6 | 7 | #define ltm_c 8 | #define LUA_CORE 9 | 10 | #include "lprefix.h" 11 | 12 | 13 | #include 14 | 15 | #include "lua.h" 16 | 17 | #include "ldebug.h" 18 | #include "ldo.h" 19 | #include "lobject.h" 20 | #include "lstate.h" 21 | #include "lstring.h" 22 | #include "ltable.h" 23 | #include "ltm.h" 24 | #include "lvm.h" 25 | 26 | 27 | static const char udatatypename[] = "userdata"; 28 | 29 | LUAI_DDEF const char *const luaT_typenames_[LUA_TOTALTAGS] = { 30 | "no value", 31 | "nil", "boolean", udatatypename, "number", 32 | "string", "table", "function", udatatypename, "thread", 33 | "proto" /* this last case is used for tests only */ 34 | }; 35 | 36 | 37 | void luaT_init (lua_State *L) { 38 | static const char *const luaT_eventname[] = { /* ORDER TM */ 39 | "__index", "__newindex", 40 | "__gc", "__mode", "__len", "__eq", 41 | "__add", "__sub", "__mul", "__mod", "__pow", 42 | "__div", "__idiv", 43 | "__band", "__bor", "__bxor", "__shl", "__shr", 44 | "__unm", "__bnot", "__lt", "__le", 45 | "__concat", "__call" 46 | }; 47 | int i; 48 | for (i=0; itmname[i] = luaS_new(L, luaT_eventname[i]); 50 | luaC_fix(L, obj2gco(G(L)->tmname[i])); /* never collect these names */ 51 | } 52 | } 53 | 54 | 55 | /* 56 | ** function to be used with macro "fasttm": optimized for absence of 57 | ** tag methods 58 | */ 59 | const TValue *luaT_gettm (Table *events, TMS event, TString *ename) { 60 | const TValue *tm = luaH_getshortstr(events, ename); 61 | lua_assert(event <= TM_EQ); 62 | if (ttisnil(tm)) { /* no tag method? */ 63 | events->flags |= cast_byte(1u<metatable; 75 | break; 76 | case LUA_TUSERDATA: 77 | mt = uvalue(o)->metatable; 78 | break; 79 | default: 80 | mt = G(L)->mt[ttnov(o)]; 81 | } 82 | return (mt ? luaH_getshortstr(mt, G(L)->tmname[event]) : luaO_nilobject); 83 | } 84 | 85 | 86 | /* 87 | ** Return the name of the type of an object. For tables and userdata 88 | ** with metatable, use their '__name' metafield, if present. 89 | */ 90 | const char *luaT_objtypename (lua_State *L, const TValue *o) { 91 | Table *mt; 92 | if ((ttistable(o) && (mt = hvalue(o)->metatable) != NULL) || 93 | (ttisfulluserdata(o) && (mt = uvalue(o)->metatable) != NULL)) { 94 | const TValue *name = luaH_getshortstr(mt, luaS_new(L, "__name")); 95 | if (ttisstring(name)) /* is '__name' a string? */ 96 | return getstr(tsvalue(name)); /* use it as type name */ 97 | } 98 | return ttypename(ttnov(o)); /* else use standard type name */ 99 | } 100 | 101 | 102 | void luaT_callTM (lua_State *L, const TValue *f, const TValue *p1, 103 | const TValue *p2, TValue *p3, int hasres) { 104 | ptrdiff_t result = savestack(L, p3); 105 | StkId func = L->top; 106 | setobj2s(L, func, f); /* push function (assume EXTRA_STACK) */ 107 | setobj2s(L, func + 1, p1); /* 1st argument */ 108 | setobj2s(L, func + 2, p2); /* 2nd argument */ 109 | L->top += 3; 110 | if (!hasres) /* no result? 'p3' is third argument */ 111 | setobj2s(L, L->top++, p3); /* 3rd argument */ 112 | /* metamethod may yield only when called from Lua code */ 113 | if (isLua(L->ci)) 114 | luaD_call(L, func, hasres); 115 | else 116 | luaD_callnoyield(L, func, hasres); 117 | if (hasres) { /* if has result, move it to its place */ 118 | p3 = restorestack(L, result); 119 | setobjs2s(L, p3, --L->top); 120 | } 121 | } 122 | 123 | 124 | int luaT_callbinTM (lua_State *L, const TValue *p1, const TValue *p2, 125 | StkId res, TMS event) { 126 | const TValue *tm = luaT_gettmbyobj(L, p1, event); /* try first operand */ 127 | if (ttisnil(tm)) 128 | tm = luaT_gettmbyobj(L, p2, event); /* try second operand */ 129 | if (ttisnil(tm)) return 0; 130 | luaT_callTM(L, tm, p1, p2, res, 1); 131 | return 1; 132 | } 133 | 134 | 135 | void luaT_trybinTM (lua_State *L, const TValue *p1, const TValue *p2, 136 | StkId res, TMS event) { 137 | if (!luaT_callbinTM(L, p1, p2, res, event)) { 138 | switch (event) { 139 | case TM_CONCAT: 140 | luaG_concaterror(L, p1, p2); 141 | /* call never returns, but to avoid warnings: *//* FALLTHROUGH */ 142 | case TM_BAND: case TM_BOR: case TM_BXOR: 143 | case TM_SHL: case TM_SHR: case TM_BNOT: { 144 | lua_Number dummy; 145 | if (tonumber(p1, &dummy) && tonumber(p2, &dummy)) 146 | luaG_tointerror(L, p1, p2); 147 | else 148 | luaG_opinterror(L, p1, p2, "perform bitwise operation on"); 149 | } 150 | /* calls never return, but to avoid warnings: *//* FALLTHROUGH */ 151 | default: 152 | luaG_opinterror(L, p1, p2, "perform arithmetic on"); 153 | } 154 | } 155 | } 156 | 157 | 158 | int luaT_callorderTM (lua_State *L, const TValue *p1, const TValue *p2, 159 | TMS event) { 160 | if (!luaT_callbinTM(L, p1, p2, L->top, event)) 161 | return -1; /* no metamethod */ 162 | else 163 | return !l_isfalse(L->top); 164 | } 165 | 166 | -------------------------------------------------------------------------------- /lua/ltm.h: -------------------------------------------------------------------------------- 1 | /* 2 | ** $Id: ltm.h,v 2.22 2016/02/26 19:20:15 roberto Exp $ 3 | ** Tag methods 4 | ** See Copyright Notice in lua.h 5 | */ 6 | 7 | #ifndef ltm_h 8 | #define ltm_h 9 | 10 | 11 | #include "lobject.h" 12 | 13 | 14 | /* 15 | * WARNING: if you change the order of this enumeration, 16 | * grep "ORDER TM" and "ORDER OP" 17 | */ 18 | typedef enum { 19 | TM_INDEX, 20 | TM_NEWINDEX, 21 | TM_GC, 22 | TM_MODE, 23 | TM_LEN, 24 | TM_EQ, /* last tag method with fast access */ 25 | TM_ADD, 26 | TM_SUB, 27 | TM_MUL, 28 | TM_MOD, 29 | TM_POW, 30 | TM_DIV, 31 | TM_IDIV, 32 | TM_BAND, 33 | TM_BOR, 34 | TM_BXOR, 35 | TM_SHL, 36 | TM_SHR, 37 | TM_UNM, 38 | TM_BNOT, 39 | TM_LT, 40 | TM_LE, 41 | TM_CONCAT, 42 | TM_CALL, 43 | TM_N /* number of elements in the enum */ 44 | } TMS; 45 | 46 | 47 | 48 | #define gfasttm(g,et,e) ((et) == NULL ? NULL : \ 49 | ((et)->flags & (1u<<(e))) ? NULL : luaT_gettm(et, e, (g)->tmname[e])) 50 | 51 | #define fasttm(l,et,e) gfasttm(G(l), et, e) 52 | 53 | #define ttypename(x) luaT_typenames_[(x) + 1] 54 | 55 | LUAI_DDEC const char *const luaT_typenames_[LUA_TOTALTAGS]; 56 | 57 | 58 | LUAI_FUNC const char *luaT_objtypename (lua_State *L, const TValue *o); 59 | 60 | LUAI_FUNC const TValue *luaT_gettm (Table *events, TMS event, TString *ename); 61 | LUAI_FUNC const TValue *luaT_gettmbyobj (lua_State *L, const TValue *o, 62 | TMS event); 63 | LUAI_FUNC void luaT_init (lua_State *L); 64 | 65 | LUAI_FUNC void luaT_callTM (lua_State *L, const TValue *f, const TValue *p1, 66 | const TValue *p2, TValue *p3, int hasres); 67 | LUAI_FUNC int luaT_callbinTM (lua_State *L, const TValue *p1, const TValue *p2, 68 | StkId res, TMS event); 69 | LUAI_FUNC void luaT_trybinTM (lua_State *L, const TValue *p1, const TValue *p2, 70 | StkId res, TMS event); 71 | LUAI_FUNC int luaT_callorderTM (lua_State *L, const TValue *p1, 72 | const TValue *p2, TMS event); 73 | 74 | 75 | 76 | #endif 77 | -------------------------------------------------------------------------------- /lua/lua.hpp: -------------------------------------------------------------------------------- 1 | // lua.hpp 2 | // Lua header files for C++ 3 | // <> not supplied automatically because Lua also compiles as C++ 4 | 5 | extern "C" { 6 | #include "lua.h" 7 | #include "lualib.h" 8 | #include "lauxlib.h" 9 | } 10 | -------------------------------------------------------------------------------- /lua/lualib.h: -------------------------------------------------------------------------------- 1 | /* 2 | ** $Id: lualib.h,v 1.44 2014/02/06 17:32:33 roberto Exp $ 3 | ** Lua standard libraries 4 | ** See Copyright Notice in lua.h 5 | */ 6 | 7 | 8 | #ifndef lualib_h 9 | #define lualib_h 10 | 11 | #include "lua.h" 12 | 13 | 14 | 15 | LUAMOD_API int (luaopen_base) (lua_State *L); 16 | 17 | #define LUA_COLIBNAME "coroutine" 18 | LUAMOD_API int (luaopen_coroutine) (lua_State *L); 19 | 20 | #define LUA_TABLIBNAME "table" 21 | LUAMOD_API int (luaopen_table) (lua_State *L); 22 | 23 | #define LUA_IOLIBNAME "io" 24 | LUAMOD_API int (luaopen_io) (lua_State *L); 25 | 26 | #define LUA_OSLIBNAME "os" 27 | LUAMOD_API int (luaopen_os) (lua_State *L); 28 | 29 | #define LUA_STRLIBNAME "string" 30 | LUAMOD_API int (luaopen_string) (lua_State *L); 31 | 32 | #define LUA_UTF8LIBNAME "utf8" 33 | LUAMOD_API int (luaopen_utf8) (lua_State *L); 34 | 35 | #define LUA_BITLIBNAME "bit32" 36 | LUAMOD_API int (luaopen_bit32) (lua_State *L); 37 | 38 | #define LUA_MATHLIBNAME "math" 39 | LUAMOD_API int (luaopen_math) (lua_State *L); 40 | 41 | #define LUA_DBLIBNAME "debug" 42 | LUAMOD_API int (luaopen_debug) (lua_State *L); 43 | 44 | #define LUA_LOADLIBNAME "package" 45 | LUAMOD_API int (luaopen_package) (lua_State *L); 46 | 47 | 48 | /* open all previous libraries */ 49 | LUALIB_API void (luaL_openlibs) (lua_State *L); 50 | 51 | 52 | 53 | #if !defined(lua_assert) 54 | #define lua_assert(x) ((void)0) 55 | #endif 56 | 57 | 58 | #endif 59 | -------------------------------------------------------------------------------- /lua/lundump.h: -------------------------------------------------------------------------------- 1 | /* 2 | ** $Id: lundump.h,v 1.45 2015/09/08 15:41:05 roberto Exp $ 3 | ** load precompiled Lua chunks 4 | ** See Copyright Notice in lua.h 5 | */ 6 | 7 | #ifndef lundump_h 8 | #define lundump_h 9 | 10 | #include "llimits.h" 11 | #include "lobject.h" 12 | #include "lzio.h" 13 | 14 | 15 | /* data to catch conversion errors */ 16 | #define LUAC_DATA "\x19\x93\r\n\x1a\n" 17 | 18 | #define LUAC_INT 0x5678 19 | #define LUAC_NUM cast_num(370.5) 20 | 21 | #define MYINT(s) (s[0]-'0') 22 | #define LUAC_VERSION (MYINT(LUA_VERSION_MAJOR)*16+MYINT(LUA_VERSION_MINOR)) 23 | #define LUAC_FORMAT 0 /* this is the official format */ 24 | 25 | /* load one chunk; from lundump.c */ 26 | LUAI_FUNC LClosure* luaU_undump (lua_State* L, ZIO* Z, const char* name); 27 | 28 | /* dump one chunk; from ldump.c */ 29 | LUAI_FUNC int luaU_dump (lua_State* L, const Proto* f, lua_Writer w, 30 | void* data, int strip); 31 | 32 | #endif 33 | -------------------------------------------------------------------------------- /lua/lvm.h: -------------------------------------------------------------------------------- 1 | /* 2 | ** $Id: lvm.h,v 2.40 2016/01/05 16:07:21 roberto Exp $ 3 | ** Lua virtual machine 4 | ** See Copyright Notice in lua.h 5 | */ 6 | 7 | #ifndef lvm_h 8 | #define lvm_h 9 | 10 | 11 | #include "ldo.h" 12 | #include "lobject.h" 13 | #include "ltm.h" 14 | 15 | 16 | #if !defined(LUA_NOCVTN2S) 17 | #define cvt2str(o) ttisnumber(o) 18 | #else 19 | #define cvt2str(o) 0 /* no conversion from numbers to strings */ 20 | #endif 21 | 22 | 23 | #if !defined(LUA_NOCVTS2N) 24 | #define cvt2num(o) ttisstring(o) 25 | #else 26 | #define cvt2num(o) 0 /* no conversion from strings to numbers */ 27 | #endif 28 | 29 | 30 | /* 31 | ** You can define LUA_FLOORN2I if you want to convert floats to integers 32 | ** by flooring them (instead of raising an error if they are not 33 | ** integral values) 34 | */ 35 | #if !defined(LUA_FLOORN2I) 36 | #define LUA_FLOORN2I 0 37 | #endif 38 | 39 | 40 | #define tonumber(o,n) \ 41 | (ttisfloat(o) ? (*(n) = fltvalue(o), 1) : luaV_tonumber_(o,n)) 42 | 43 | #define tointeger(o,i) \ 44 | (ttisinteger(o) ? (*(i) = ivalue(o), 1) : luaV_tointeger(o,i,LUA_FLOORN2I)) 45 | 46 | #define intop(op,v1,v2) l_castU2S(l_castS2U(v1) op l_castS2U(v2)) 47 | 48 | #define luaV_rawequalobj(t1,t2) luaV_equalobj(NULL,t1,t2) 49 | 50 | 51 | /* 52 | ** fast track for 'gettable': if 't' is a table and 't[k]' is not nil, 53 | ** return 1 with 'slot' pointing to 't[k]' (final result). Otherwise, 54 | ** return 0 (meaning it will have to check metamethod) with 'slot' 55 | ** pointing to a nil 't[k]' (if 't' is a table) or NULL (otherwise). 56 | ** 'f' is the raw get function to use. 57 | */ 58 | #define luaV_fastget(L,t,k,slot,f) \ 59 | (!ttistable(t) \ 60 | ? (slot = NULL, 0) /* not a table; 'slot' is NULL and result is 0 */ \ 61 | : (slot = f(hvalue(t), k), /* else, do raw access */ \ 62 | !ttisnil(slot))) /* result not nil? */ 63 | 64 | /* 65 | ** standard implementation for 'gettable' 66 | */ 67 | #define luaV_gettable(L,t,k,v) { const TValue *slot; \ 68 | if (luaV_fastget(L,t,k,slot,luaH_get)) { setobj2s(L, v, slot); } \ 69 | else luaV_finishget(L,t,k,v,slot); } 70 | 71 | 72 | /* 73 | ** Fast track for set table. If 't' is a table and 't[k]' is not nil, 74 | ** call GC barrier, do a raw 't[k]=v', and return true; otherwise, 75 | ** return false with 'slot' equal to NULL (if 't' is not a table) or 76 | ** 'nil'. (This is needed by 'luaV_finishget'.) Note that, if the macro 77 | ** returns true, there is no need to 'invalidateTMcache', because the 78 | ** call is not creating a new entry. 79 | */ 80 | #define luaV_fastset(L,t,k,slot,f,v) \ 81 | (!ttistable(t) \ 82 | ? (slot = NULL, 0) \ 83 | : (slot = f(hvalue(t), k), \ 84 | ttisnil(slot) ? 0 \ 85 | : (luaC_barrierback(L, hvalue(t), v), \ 86 | setobj2t(L, cast(TValue *,slot), v), \ 87 | 1))) 88 | 89 | 90 | #define luaV_settable(L,t,k,v) { const TValue *slot; \ 91 | if (!luaV_fastset(L,t,k,slot,luaH_get,v)) \ 92 | luaV_finishset(L,t,k,v,slot); } 93 | 94 | 95 | 96 | LUAI_FUNC int luaV_equalobj (lua_State *L, const TValue *t1, const TValue *t2); 97 | LUAI_FUNC int luaV_lessthan (lua_State *L, const TValue *l, const TValue *r); 98 | LUAI_FUNC int luaV_lessequal (lua_State *L, const TValue *l, const TValue *r); 99 | LUAI_FUNC int luaV_tonumber_ (const TValue *obj, lua_Number *n); 100 | LUAI_FUNC int luaV_tointeger (const TValue *obj, lua_Integer *p, int mode); 101 | LUAI_FUNC void luaV_finishget (lua_State *L, const TValue *t, TValue *key, 102 | StkId val, const TValue *slot); 103 | LUAI_FUNC void luaV_finishset (lua_State *L, const TValue *t, TValue *key, 104 | StkId val, const TValue *slot); 105 | LUAI_FUNC void luaV_finishOp (lua_State *L); 106 | LUAI_FUNC void luaV_execute (lua_State *L); 107 | LUAI_FUNC void luaV_concat (lua_State *L, int total); 108 | LUAI_FUNC lua_Integer luaV_div (lua_State *L, lua_Integer x, lua_Integer y); 109 | LUAI_FUNC lua_Integer luaV_mod (lua_State *L, lua_Integer x, lua_Integer y); 110 | LUAI_FUNC lua_Integer luaV_shiftl (lua_Integer x, lua_Integer y); 111 | LUAI_FUNC void luaV_objlen (lua_State *L, StkId ra, const TValue *rb); 112 | 113 | #endif 114 | -------------------------------------------------------------------------------- /lua/lzio.c: -------------------------------------------------------------------------------- 1 | /* 2 | ** $Id: lzio.c,v 1.37 2015/09/08 15:41:05 roberto Exp $ 3 | ** Buffered streams 4 | ** See Copyright Notice in lua.h 5 | */ 6 | 7 | #define lzio_c 8 | #define LUA_CORE 9 | 10 | #include "lprefix.h" 11 | 12 | 13 | #include 14 | 15 | #include "lua.h" 16 | 17 | #include "llimits.h" 18 | #include "lmem.h" 19 | #include "lstate.h" 20 | #include "lzio.h" 21 | 22 | 23 | int luaZ_fill (ZIO *z) { 24 | size_t size; 25 | lua_State *L = z->L; 26 | const char *buff; 27 | lua_unlock(L); 28 | buff = z->reader(L, z->data, &size); 29 | lua_lock(L); 30 | if (buff == NULL || size == 0) 31 | return EOZ; 32 | z->n = size - 1; /* discount char being returned */ 33 | z->p = buff; 34 | return cast_uchar(*(z->p++)); 35 | } 36 | 37 | 38 | void luaZ_init (lua_State *L, ZIO *z, lua_Reader reader, void *data) { 39 | z->L = L; 40 | z->reader = reader; 41 | z->data = data; 42 | z->n = 0; 43 | z->p = NULL; 44 | } 45 | 46 | 47 | /* --------------------------------------------------------------- read --- */ 48 | size_t luaZ_read (ZIO *z, void *b, size_t n) { 49 | while (n) { 50 | size_t m; 51 | if (z->n == 0) { /* no bytes in buffer? */ 52 | if (luaZ_fill(z) == EOZ) /* try to read more */ 53 | return n; /* no more input; return number of missing bytes */ 54 | else { 55 | z->n++; /* luaZ_fill consumed first byte; put it back */ 56 | z->p--; 57 | } 58 | } 59 | m = (n <= z->n) ? n : z->n; /* min. between n and z->n */ 60 | memcpy(b, z->p, m); 61 | z->n -= m; 62 | z->p += m; 63 | b = (char *)b + m; 64 | n -= m; 65 | } 66 | return 0; 67 | } 68 | 69 | -------------------------------------------------------------------------------- /lua/lzio.h: -------------------------------------------------------------------------------- 1 | /* 2 | ** $Id: lzio.h,v 1.31 2015/09/08 15:41:05 roberto Exp $ 3 | ** Buffered streams 4 | ** See Copyright Notice in lua.h 5 | */ 6 | 7 | 8 | #ifndef lzio_h 9 | #define lzio_h 10 | 11 | #include "lua.h" 12 | 13 | #include "lmem.h" 14 | 15 | 16 | #define EOZ (-1) /* end of stream */ 17 | 18 | typedef struct Zio ZIO; 19 | 20 | #define zgetc(z) (((z)->n--)>0 ? cast_uchar(*(z)->p++) : luaZ_fill(z)) 21 | 22 | 23 | typedef struct Mbuffer { 24 | char *buffer; 25 | size_t n; 26 | size_t buffsize; 27 | } Mbuffer; 28 | 29 | #define luaZ_initbuffer(L, buff) ((buff)->buffer = NULL, (buff)->buffsize = 0) 30 | 31 | #define luaZ_buffer(buff) ((buff)->buffer) 32 | #define luaZ_sizebuffer(buff) ((buff)->buffsize) 33 | #define luaZ_bufflen(buff) ((buff)->n) 34 | 35 | #define luaZ_buffremove(buff,i) ((buff)->n -= (i)) 36 | #define luaZ_resetbuffer(buff) ((buff)->n = 0) 37 | 38 | 39 | #define luaZ_resizebuffer(L, buff, size) \ 40 | ((buff)->buffer = luaM_reallocvchar(L, (buff)->buffer, \ 41 | (buff)->buffsize, size), \ 42 | (buff)->buffsize = size) 43 | 44 | #define luaZ_freebuffer(L, buff) luaZ_resizebuffer(L, buff, 0) 45 | 46 | 47 | LUAI_FUNC void luaZ_init (lua_State *L, ZIO *z, lua_Reader reader, 48 | void *data); 49 | LUAI_FUNC size_t luaZ_read (ZIO* z, void *b, size_t n); /* read next n bytes */ 50 | 51 | 52 | 53 | /* --------- Private Part ------------------ */ 54 | 55 | struct Zio { 56 | size_t n; /* bytes still unread */ 57 | const char *p; /* current position in buffer */ 58 | lua_Reader reader; /* reader function */ 59 | void *data; /* additional data */ 60 | lua_State *L; /* Lua state (for reader) */ 61 | }; 62 | 63 | 64 | LUAI_FUNC int luaZ_fill (ZIO *z); 65 | 66 | #endif 67 | -------------------------------------------------------------------------------- /makefile: -------------------------------------------------------------------------------- 1 | # This makefile will build all the binaries and libraries in this repo. 2 | # The built products will be in the same directory as their source. 3 | # 4 | # The code for each chapter can also be compiled on a per-chapter basis using 5 | # the makefile in the directory of that chapter. 6 | # 7 | 8 | subdirs = ch1 ch2 ch3 ch4 ch5 ch6 9 | 10 | all: 11 | for dir in $(subdirs); do make -C $$dir; done 12 | # 13 | # Built products are in the chapter directories (ch1, ch2, etc.) 14 | # 15 | 16 | clean: 17 | for dir in $(subdirs); do make -C $$dir clean; done 18 | -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | # Creating Solid APIs with Lua 2 | 3 | *Lua and C code examples that illustrate C/Lua integration.* 4 | 5 | These code samples were written as companions to the O'Reilly 6 | book 7 | [*Creating Solid APIs with Lua*](https://www.safaribooksonline.com/library/view/creating-solid-apis/9781491986301/). 8 | The samples are built up chapter-by-chapter, and focus on a particular 9 | game called *EatyGuy*, which looks like this: 10 | 11 | ![](https://github.com/tylerneylon/APIsWithLua/raw/master/img/screenshot.png) 12 | 13 | This game is text-based in order to enable simple graphics that work across 14 | platforms yet don't require any libraries. 15 | The code has runs on macOS, Ubuntu, 16 | and Windows. 17 | 18 | Run `make` from the root repo directory to build all the examples. 19 | The built products are created in each chapter's directory 20 | (`ch1`, `ch2`, and so on). The game examples can be run by, for 21 | example, in `ch6`, typing the command `./eatyguy10` (each chapter 22 | has its own version of the game). 23 | Use the arrow keys to move your character around, and press `q` to quit. 24 | 25 | Many more details are provided in the book! :) 26 | 27 | ## Table of Contents 28 | 29 | This section outlines the flow of content 30 | across the chapters of the book and corresponding 31 | subdirectories of this repo. 32 | Many of these topics center on learning Lua's 33 | C API. 34 | 35 | * **Chapter 1** Running a Lua Script from C 36 | 37 | *This chapter covers acquiring Lua's source as 38 | well as running Lua scripts from C.* 39 | 40 | * **Chapter 2** Calling C from Lua 41 | 42 | *Writing C functions that Lua can call.* 43 | 44 | * **Chapter 3** Using Lua Values in C 45 | 46 | *Manipulating Lua primitives and tables 47 | from C.* 48 | 49 | * **Chapter 4** Making Your API Classy 50 | 51 | *A review of Lua classes, and 52 | how C works with Lua's `userdata` type 53 | to make safe, class-like objects in Lua.* 54 | 55 | * **Chapter 5** Detecting Errors 56 | 57 | *Working with Lua errors (similar to exceptions) 58 | from C; making it easier to notice certain 59 | common mistakes.* 60 | 61 | * **Chapter 6** Sandboxing User Scripts 62 | 63 | *Limiting the API and resource access of user 64 | scripts, including fine-grained constraints 65 | on CPU and memory consumption.* 66 | --------------------------------------------------------------------------------