local pprint = require "pprint" love.graphics.setDefaultFilter("nearest", "nearest") local read, write = 1, 1 local active_buffer, back_buffer, rule_book = {}, {}, {} local function parse(str) local symbols = {} for symbol in str:gmatch("%S+") do table.insert(symbols, symbol) end return symbols end local function queue_up(string) for _, symbol in ipairs(parse(string)) do table.insert(active_buffer, symbol) end end local function add_rule(wants) return function(gives) table.insert(rule_book, {parse(wants), type(gives) == "string" and parse(gives) or gives}) end end local function match(wants) for i = 1, #wants do if wants[i] ~= active_buffer[i + read - 1] then return false end end return true end local function consume(n) read = read + n end local function peek(n) local symbols = {} for i = 0, n - 1 do table.insert(symbols, active_buffer[read + i]) end return symbols end local function take_next(n) n = n or 1 local symbol = active_buffer[read] read = read + 1 return symbol end local function produce(gives) for _, symbol in ipairs(gives) do back_buffer[write] = symbol write = write + 1 end end local function advance() back_buffer[write], active_buffer[read] = active_buffer[read], nil write, read = write + 1, read + 1 end local function run_cycle() local rule_matched = false repeat local rule_fired = false for _, rule in ipairs(rule_book) do local wants, gives = rule[1], rule[2] if match(wants) then consume(#wants) if type(gives) == "function" then gives(active_buffer, read) else produce(gives) end rule_fired, rule_matched = true, true break end end if not rule_fired then advance() end until not active_buffer[read] return rule_matched end local function exchange_buffers() active_buffer, back_buffer = back_buffer, {} read, write = 1, 1 end local cycles, MAX_CYCLES = 0, 10000 local function run() while run_cycle() and cycles < MAX_CYCLES do print(table.concat(back_buffer, " ")) exchange_buffers() cycles = cycles + 1 end exchange_buffers() end local current_sprite local sprites, state = {}, {} local screen = love.graphics.newCanvas() add_rule "@ sprite [" (function() current_sprite = { data = {{}} } produce {"sprite>"} end) add_rule "sprite> name is" (function() current_sprite.name = take_next() produce { "sprite>" } end) add_rule "sprite> symbol is" (function() current_sprite.symbol = take_next() produce { "sprite>" } end) add_rule "sprite> ;" (function() table.insert(current_sprite.data, {}) produce { "sprite>" } end) add_rule "sprite> ]" (function() -- find longest row in data local longest_row = -1 for _, row in ipairs(current_sprite.data) do if #row > longest_row then longest_row = #row end end -- pad out other rows for _, row in ipairs(current_sprite.data) do while #row < longest_row do table.insert(row, ".") end end -- construct a canvas for our sprite local sprite = love.graphics.newCanvas(longest_row, #current_sprite.data) love.graphics.setCanvas(sprite) local x, y = 0, 0 for _, row in ipairs(current_sprite.data) do for _, data in ipairs(row) do if data == "#" then love.graphics.points(x + 0.5, y + 0.5) end x = x + 1 end x, y = 0, y + 1 end love.graphics.setCanvas() sprites[current_sprite.symbol] = sprite produce {"@"} end) add_rule "sprite>" (function() table.insert(current_sprite.data[#current_sprite.data], take_next()) produce { "sprite>" } end) add_rule "@ draw sprite [" (function() local symbol = take_next() consume(2) local x = tonumber(take_next()) consume(1) local y = tonumber(take_next()) consume(1) love.graphics.setCanvas(screen) love.graphics.draw(sprites[symbol], x * 16, y * 16) love.graphics.setCanvas() produce {"@"} end) add_rule "@ clear screen" (function() love.graphics.setCanvas(screen) love.graphics.clear() love.graphics.setCanvas() produce {"@"} end) -- @ set to add_rule "@ set" (function () -- local field = {} local symbol = take_next() print(symbol) while symbol ~= "to" do table.insert(field, symbol) symbol = take_next() end pprint(symbol) -- local value = take_next() -- try casting value to a number if value:sub(1,1) == "#" then local numerical_part = tonumber(value:sub(2)) value = numerical_part or value end print() state[table.concat(field, " ")] = value produce {"@"} end) queue_up [[ @ sprite [ symbol is p . . . . . # # # # # . . . . . . ; . . . . . . . . . . . . . . . . ; . . . . . # # # # # . . . . . . ; . . # # # # # # . # # . . . . . ; . . . . # # # # . # # # # # . . ; . . . . . # # # # # # # # # . . ; . . . . . . # # # # . . . . . . ; . . . . . # # # # # # . . . . . ; . . . . # . # # # # . # . . . . ; . . . . # . # # # # . # . . . . ; . . . . # . # # # # . # . . . . ; . . . . . . # . . # . . . . . . ; . . . . . . # . . # . . . . . . ; . . . . . . # . . # . . . . . . ; . . . . . . # . . # . . . . . . ; . . . . . . # . . # . . . . . . ] sprite [ symbol is w . . # # # # # # # # # # # # . . ; . # . # . # . # . # . # . # # . ; # . # . # . # . # . # . # . # # ; # # . # . # . # . # . # . # . # ; # . # . # . # . # . # . # . # # ; # # . # . # . # . # . # . # . # ; # . # . # . # . # . # . # . # # ; # # . # . # . # . # . # . # . # ; # . # . # . # . # . # . # . # # ; # # . # . # . # . # . # . # . # ; # . # . # . # . # . # . # . # # ; # # . # . # . # . # . # . # . # ; # . # . # . # . # . # . # . # # ; # # . # . # . # . # . # . # . # ; . # # . # . # . # . # . # . # . ; . . # # # # # # # # # # # # . . ] set player x to #5 set player y to #4 ]] local function draw_map() queue_up [[ draw sprite [ w at x 5 y 5 ] draw sprite [ w at x 4 y 6 ] draw sprite [ w at x 5 y 6 ] draw sprite [ w at x 6 y 6 ] draw sprite [ w at x 3 y 7 ] draw sprite [ w at x 4 y 7 ] draw sprite [ w at x 5 y 7 ] draw sprite [ w at x 6 y 7 ] draw sprite [ w at x 7 y 7 ] draw sprite [ w at x 2 y 8 ] draw sprite [ w at x 3 y 8 ] draw sprite [ w at x 4 y 8 ] draw sprite [ w at x 5 y 8 ] draw sprite [ w at x 6 y 8 ] draw sprite [ w at x 7 y 8 ] draw sprite [ w at x 8 y 8 ] draw sprite [ w at x 1 y 9 ] draw sprite [ w at x 2 y 9 ] draw sprite [ w at x 3 y 9 ] draw sprite [ w at x 4 y 9 ] draw sprite [ w at x 5 y 9 ] draw sprite [ w at x 6 y 9 ] draw sprite [ w at x 7 y 9 ] draw sprite [ w at x 8 y 9 ] draw sprite [ w at x 9 y 9 ] draw sprite [ w at x 0 y 10 ] draw sprite [ w at x 1 y 10 ] draw sprite [ w at x 2 y 10 ] draw sprite [ w at x 3 y 10 ] draw sprite [ w at x 4 y 10 ] draw sprite [ w at x 5 y 10 ] draw sprite [ w at x 6 y 10 ] draw sprite [ w at x 7 y 10 ] draw sprite [ w at x 8 y 10 ] draw sprite [ w at x 9 y 10 ] draw sprite [ w at x 10 y 10 ] ]] end run() local function draw_player() local draw_command = ("draw sprite [ p at x %d y %d ]"):format(state["player x"], state["player y"]) queue_up(draw_command) end function love.draw() queue_up "clear screen" draw_player() pprint(active_buffer) run() love.graphics.draw(screen, 0, 0, 0, 4, 4) love.graphics.print(tostring(love.timer.getFPS()), 10, 10) end function love.keypressed(_, scancode) local move_x_command = "set player x to #%d" local move_y_command = "set player y to #%d" if scancode == "w" then queue_up(move_y_command:format(state["player y"] - 1)) elseif scancode == "s" then queue_up(move_y_command:format(state["player y"] + 1)) end if scancode == "a" then queue_up(move_x_command:format(state["player x"] - 1)) elseif scancode == "d" then queue_up(move_x_command:format(state["player x"] + 1)) end end