Skip to content

Instantly share code, notes, and snippets.

@MCJack123
Created October 30, 2023 00:34
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save MCJack123/48999076fef81d0a7b9fcc7fd6acedf6 to your computer and use it in GitHub Desktop.
Save MCJack123/48999076fef81d0a7b9fcc7fd6acedf6 to your computer and use it in GitHub Desktop.
AOT Brainfuck compiler to bytecode for Lua 5.1
--[[
registers: 0 = data table, 1 = data pointer, 2 = function temp, 3 = current data (4 total)
expects `get(): number` and `put(n: number)` global functions
init code
GETGLOBAL R(0), K("setmetatable")
NEWTABLE R(1), 0, 0
NEWTABLE R(2), 0, 1
CLOSURE R(3), KP(0)
SETTABLE R(2), K("__index"), R(3)
CALL R(0), 3, 2
LOADK R(1), K(0)
LOADK R(3), K(0)
>, increment data pointer
SETTABLE R(0), R(1), R(3)
ADD R(1), R(1), K(1)
GETTABLE R(3), R(0), R(1)
<, decrement data pointer
SETTABLE R(0), R(1), R(3)
SUB R(1), R(1), K(1)
GETTABLE R(3), R(0), R(1)
+, increment data
ADD R(3), R(3), K(1)
-, decrement data
SUB R(3), R(3), K(1)
., output byte
GETGLOBAL R(2), K("put")
CALL R(2), 2, 1
,, input byte
GETGLOBAL R(3), K("get")
CALL R(3), 1, 2
[, jump forward if zero
EQ 1, R(3), K(0)
JMP $displacement*3
], jump backward if not zero
EQ 0, R(3), K(0)
JMP $displacement*-3
]]
assert(_VERSION == "Lua 5.1")
local header = string.dump(function() end):sub(1, 12)
local ENDIAN = header:byte(7) == 0 and ">" or "<"
local INT = "i" .. header:byte(8)
local STRING = "s" .. header:byte(9)
local INSTRUCTION = "I" .. header:byte(10)
local NUMBER = (header:byte(12) == 0 and (header:byte(11) == 4 and "f" or "d") or ("i" .. header:byte(11)))
local HEADER = ENDIAN .. STRING .. INT .. INT .. "BBBB"
local FOOTER = (ENDIAN .. INT .. "B" .. NUMBER .. "B" .. NUMBER .. "B" .. STRING .. "B" .. STRING .. "B" .. STRING .. "B" .. STRING .. INT .. STRING .. INT .. INT .. "BBBB" .. INT .. INSTRUCTION:rep(2) .. INT .. "B" .. NUMBER .. INT:rep(7)):pack(6, 3, 0, 3, 1, 4, "put\0", 4, "get\0", 4, "setmetatable\0", 4, "__index\0", 1, "\0", 0, 0, 0, 0, 0, 1, 2, 0x00000001, 0x0100001E, 1, 3, 0, 0, 0, 0, 0, 0, 0, 0)
local function encode(c) return (INT .. INSTRUCTION:rep(#c)):pack(#c, table.unpack(c)) end
local instructions = {
[">"] = {
0x0080C009,
0x00C0404C,
0x000040C6
},
["<"] = {
0x0080C009,
0x00C0404D,
0x000040C6
},
["+"] = {
0x01C040CC
},
["-"] = {
0x01C040CD
},
["."] = {
0x00008085,
0x0100409C
},
[","] = {
0x0000C0C5,
0x008080DC
},
["["] = {
0x01C00057,
0x00000016
},
["]"] = {
0x01C00017,
0x00000016
}
}
local function bfcompile(src)
local insts, stack = {
0x00010005,
0x0000004A,
0x0000408A,
0x000000E4,
0x8280C089,
0x0180801C,
0x00000041,
0x000000C1
}, {}
for c in src:gmatch "[<>%+%-%.,%[%]]" do
local i = instructions[c]
insts[#insts+1] = i[1]
insts[#insts+1] = i[2]
insts[#insts+1] = i[3]
if c == "[" then stack[#stack+1] = #insts
elseif c == "]" then
local p = stack[#stack]
local d = #insts - p
stack[#stack] = nil
insts[p] = 0x16 + (d + 0x1FFFF) * 0x4000
insts[#insts] = 0x16 + (0x1FFFF - d) * 0x4000
end
end
insts[#insts+1] = 0x0080001E -- RETURN 0, 1
local chunk = header .. HEADER:pack(src .. "\0", 0, 0, 0, 0, 0, 4) .. encode(insts) .. FOOTER
--[[print(chunk:gsub(".", function(c) return ("%02X "):format(c:byte()) end))
local file = fs.open("bfcompile.tmp", "wb")
file.write(chunk)
file.close()]]
return load(chunk, src, "b", _ENV)
end
function get() return select(2, os.pullEvent("char")):byte() end
function put(c) write(string.char(c)) end
assert(bfcompile(read()))()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment