Skip to content

Instantly share code, notes, and snippets.

@wfrsk
Created March 18, 2023 21:15
Embed
What would you like to do?
paintfuck interpreter written in pure lua
--[[
This file is licensed under the license `CC BY-NC-ND 4.0`, visit "https://creativecommons.org/licenses/by-nc-nd/4.0/" for additional information.
author: wfrsk @ github.com
]]
local instruction_type = { up = 1,
down = 3,
right = 2,
left = 4,
loop_open = 5,
loop_close = 7,
flip_bit = 6 }
local character_map = { ["n"] = instruction_type.up,
["s"] = instruction_type.down,
["e"] = instruction_type.right,
["w"] = instruction_type.left,
["["] = instruction_type.loop_open,
["]"] = instruction_type.loop_close,
["*"] = instruction_type.flip_bit }
local function make_bitmap( x_bounds )
local integers_required = math.ceil( x_bounds / 64 )
local internal_bitmap, external_adapter do
internal_bitmap = { } do
for index = 1, integers_required do
internal_bitmap[ index ] = 0
end
end
local function index_routine( _, bitspace_index )
assert( type( bitspace_index ) == "number", "index must be an integer" )
local integer_index, bit_index = math.ceil( bitspace_index / 64 ), bitspace_index % 64
return ( assert( internal_bitmap[ integer_index ], "index given out of bounds for bitmap" ) & ( 1 << ( bit_index - 1 ) ) ) == ( 1 << ( bit_index - 1 ) ) and 1 or 0
end
local function newindex_routine( _, bitspace_index, new_value )
assert( type( bitspace_index ) == "number", "index must be an integer" )
local integer_index, bit_index = math.ceil( bitspace_index / 64 ), bitspace_index % 64
internal_bitmap[ integer_index ] = ( new_value & 1 ) == 1 and assert( internal_bitmap[ integer_index ], "index given out of bounds for bitmap" ) | ( 1 << ( bit_index - 1 ) ) or assert( internal_bitmap[ integer_index ], "index given out of bounds for bitmap" ) & ~( 1 << ( bit_index - 1 ) )
end
external_adapter = setmetatable( { }, { __index = index_routine, __newindex = newindex_routine } )
end
return external_adapter
end
local function serialize_bitmap_2d( target_bitmap_2d, x_bounds, y_bounds )
local string_repr = { } do
for y_index = 1, y_bounds do
string_repr[ y_index ] = { }
for x_index = 1, x_bounds do
string_repr[ y_index ][ x_index ] = tostring( target_bitmap_2d[ x_index ][ y_index ] )
end
string_repr[ y_index ] = table.concat( string_repr[ y_index ], "" )
end
end
return table.concat( string_repr, "\r\n" )
end
local function paintfuck_vm_routine( source_code, step_limit, x_bounds, y_bounds )
local vm_state = { } do
vm_state.instruction_pointer = 0
vm_state.instruction_list = { } do
for character in string.gmatch( source_code, "[%[%]nswe*]" ) do
vm_state.instruction_list[ #vm_state.instruction_list + 1 ] = character_map[ character ]
end
end
vm_state.memory_location = { x = 1, y = 1 }
vm_state.loop_stack = { }
vm_state.memory_bitmap = { } do
for index = 1, x_bounds do
vm_state.memory_bitmap[ index ] = make_bitmap( y_bounds )
end
end
end
while vm_state.instruction_pointer <= #vm_state.instruction_list and step_limit ~= 0 do
vm_state.instruction_pointer = vm_state.instruction_pointer + 1
step_limit = step_limit - 1
local current_instruction = vm_state.instruction_list[ vm_state.instruction_pointer ]
if current_instruction == instruction_type.flip_bit then
vm_state.memory_bitmap[ vm_state.memory_location.x ][ vm_state.memory_location.y ] = vm_state.memory_bitmap[ vm_state.memory_location.x ][ vm_state.memory_location.y ] == 0 and 1 or 0
elseif current_instruction == instruction_type.loop_open and vm_state.memory_bitmap[ vm_state.memory_location.x ][ vm_state.memory_location.y ] == 0 then
local ongoing_loops = 1
repeat
vm_state.instruction_pointer = vm_state.instruction_pointer + 1
if vm_state.instruction_list[ vm_state.instruction_pointer ] == instruction_type.loop_open then
ongoing_loops = ongoing_loops + 1
elseif vm_state.instruction_list[ vm_state.instruction_pointer ] == instruction_type.loop_close then
ongoing_loops = ongoing_loops - 1
end
until vm_state.instruction_list[ vm_state.instruction_pointer ] == instruction_type.loop_close and ongoing_loops == 0
elseif current_instruction == instruction_type.loop_close and vm_state.memory_bitmap[ vm_state.memory_location.x ][ vm_state.memory_location.y ] ~= 0 then
local ongoing_loops = 1
repeat
vm_state.instruction_pointer = vm_state.instruction_pointer - 1
if vm_state.instruction_list[ vm_state.instruction_pointer ] == instruction_type.loop_open then
ongoing_loops = ongoing_loops - 1
elseif vm_state.instruction_list[ vm_state.instruction_pointer ] == instruction_type.loop_close then
ongoing_loops = ongoing_loops + 1
end
until vm_state.instruction_list[ vm_state.instruction_pointer ] == instruction_type.loop_open and ongoing_loops == 0
elseif current_instruction == instruction_type.right then
vm_state.memory_location.x = vm_state.memory_location.x + 1 <= x_bounds and vm_state.memory_location.x + 1 or vm_state.memory_location.x + 1 - x_bounds
elseif current_instruction == instruction_type.left then
vm_state.memory_location.x = vm_state.memory_location.x - 1 > 0 and vm_state.memory_location.x - 1 or x_bounds
elseif current_instruction == instruction_type.up then
vm_state.memory_location.y = vm_state.memory_location.y - 1 > 0 and vm_state.memory_location.y - 1 or y_bounds
elseif current_instruction == instruction_type.down then
vm_state.memory_location.y = vm_state.memory_location.y + 1 <= y_bounds and vm_state.memory_location.y + 1 or vm_state.memory_location.y + 1 - y_bounds
end
end
return serialize_bitmap_2d( vm_state.memory_bitmap, x_bounds, y_bounds )
end
return paintfuck_vm_routine
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment