Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- local language = {
- header = {
- {name="signature", val=0x1B4C7561, size=4},
- {name="version", val=0x51, size=1},
- {name="format", val=0x00, size=1},
- {name="endianness", val=0x00, size=1},
- {name="size_int", val=0x04, size=1},
- {name="size_t", val=0x04, size=1},
- {name="size_inst", val=0x04, size=1},
- {name="lua_Number", val=0x08, size=1},
- {name="integral", val=0x00, size=1}
- },
- symbols = {
- ["&"] = true
- },
- keywords = {
- [".local"] = true, --
- [".const"] = true, --
- [".param"] = true, --
- [".vararg"] = true, --
- [".upval"] = true, --
- [".end"] = true, --
- [".function"] = true, --
- [".stacksize"] = true
- },
- opcodes = {
- NOT = {
- type = "iABC",
- arguments = {
- {
- type = "R",
- index = "A"
- },
- {
- type = "R",
- index = "B"
- }
- },
- code = 19
- },
- LOADK = {
- type = "iABx",
- arguments = {
- {
- type = "R",
- index = "A"
- },
- {
- type = "C",
- index = "Bx"
- }
- },
- code = 1
- },
- SETTABLE = {
- type = "iABC",
- arguments = {
- {
- type = "R",
- index = "A"
- },
- {
- type = "RK",
- index = "B"
- },
- {
- type = "RK",
- index = "C"
- }
- },
- code = 9
- },
- CLOSE = {
- type = "iABC",
- arguments = {
- {
- type = "R",
- index = "A"
- }
- },
- code = 35
- },
- GETTABLE = {
- type = "iABC",
- arguments = {
- {
- type = "R",
- index = "A"
- },
- {
- type = "R",
- index = "B"
- },
- {
- type = "RK",
- index = "C"
- }
- },
- code = 6
- },
- DIV = {
- type = "iABC",
- arguments = {
- {
- type = "R",
- index = "A"
- },
- {
- type = "RK",
- index = "B"
- },
- {
- type = "RK",
- index = "C"
- }
- },
- code = 15
- },
- TEST = {
- type = "iABC",
- arguments = {
- {
- type = "R",
- index = "A"
- },
- {
- type = "N",
- index = "C"
- }
- },
- code = 26
- },
- ADD = {
- type = "iABC",
- arguments = {
- {
- type = "R",
- index = "A"
- },
- {
- type = "RK",
- index = "B"
- },
- {
- type = "RK",
- index = "C"
- }
- },
- code = 12
- },
- SETLIST = {
- type = "iABC",
- arguments = {
- {
- type = "R",
- index = "A"
- },
- {
- type = "N",
- index = "B"
- },
- {
- type = "N",
- index = "C"
- }
- },
- code = 34
- },
- TFORLOOP = {
- type = "iABC",
- arguments = {
- {
- type = "R",
- index = "A"
- },
- {
- type = "N",
- index = "C"
- }
- },
- code = 33
- },
- LOADBOOL = {
- type = "iABC",
- arguments = {
- {
- type = "R",
- index = "A"
- },
- {
- type = "N",
- index = "B"
- },
- {
- type = "N",
- index = "C"
- }
- },
- code = 2
- },
- TESTSET = {
- type = "iABC",
- arguments = {
- {
- type = "R",
- index = "A"
- },
- {
- type = "R",
- index = "B"
- },
- {
- type = "N",
- index = "C"
- }
- },
- code = 27
- },
- FORLOOP = {
- type = "iAsBx",
- arguments = {
- {
- type = "R",
- index = "A"
- },
- {
- type = "N",
- index = "sBx"
- }
- },
- code = 31
- },
- UNM = {
- type = "iABC",
- arguments = {
- {
- type = "R",
- index = "A"
- },
- {
- type = "R",
- index = "B"
- }
- },
- code = 18
- },
- CALL = {
- type = "iABC",
- arguments = {
- {
- type = "R",
- index = "A"
- },
- {
- type = "N",
- index = "B"
- },
- {
- type = "N",
- index = "C"
- }
- },
- code = 28
- },
- EQ = {
- type = "iABC",
- arguments = {
- {
- type = "R",
- index = "A"
- },
- {
- type = "RK",
- index = "B"
- },
- {
- type = "RK",
- index = "C"
- }
- },
- code = 23
- },
- LT = {
- type = "iABC",
- arguments = {
- {
- type = "R",
- index = "A"
- },
- {
- type = "RK",
- index = "B"
- },
- {
- type = "RK",
- index = "C"
- }
- },
- code = 24
- },
- RETURN = {
- type = "iABC",
- arguments = {
- {
- type = "R",
- index = "A"
- },
- {
- type = "N",
- index = "B"
- }
- },
- code = 30
- },
- JMP = {
- type = "iAsBx",
- arguments = {
- {
- type = "N",
- index = "sBx"
- }
- },
- code = 22
- },
- CLOSURE = {
- type = "iABx",
- arguments = {
- {
- type = "R",
- index = "A"
- },
- {
- type = "P",
- index = "Bx"
- }
- },
- code = 36
- },
- SETGLOBAL = {
- type = "iABx",
- arguments = {
- {
- type = "R",
- index = "A"
- },
- {
- type = "C",
- index = "Bx"
- }
- },
- code = 7
- },
- MUL = {
- type = "iABC",
- arguments = {
- {
- type = "R",
- index = "A"
- },
- {
- type = "RK",
- index = "B"
- },
- {
- type = "RK",
- index = "C"
- }
- },
- code = 14
- },
- TAILCALL = {
- type = "iABC",
- arguments = {
- {
- type = "R",
- index = "A"
- },
- {
- type = "N",
- index = "B"
- }
- },
- code = 29
- },
- POW = {
- type = "iABC",
- arguments = {
- {
- type = "R",
- index = "A"
- },
- {
- type = "RK",
- index = "B"
- },
- {
- type = "RK",
- index = "C"
- }
- },
- code = 17
- },
- CONCAT = {
- type = "iABC",
- arguments = {
- {
- type = "R",
- index = "A"
- },
- {
- type = "R",
- index = "B"
- },
- {
- type = "R",
- index = "C"
- }
- },
- code = 21
- },
- GETUPVAL = {
- type = "iABC",
- arguments = {
- {
- type = "R",
- index = "A"
- },
- {
- type = "U",
- index = "B"
- }
- },
- code = 4
- },
- NEWTABLE = {
- type = "iABC",
- arguments = {
- {
- type = "R",
- index = "A"
- },
- {
- type = "N",
- index = "B"
- },
- {
- type = "N",
- index = "C"
- }
- },
- code = 10
- },
- SETUPVAL = {
- type = "iABC",
- arguments = {
- {
- type = "R",
- index = "A"
- },
- {
- type = "U",
- index = "B"
- }
- },
- code = 8
- },
- SELF = {
- type = "iABC",
- arguments = {
- {
- type = "R",
- index = "A"
- },
- {
- type = "R",
- index = "B"
- },
- {
- type = "RK",
- index = "C"
- }
- },
- code = 11
- },
- SUB = {
- type = "iABC",
- arguments = {
- {
- type = "R",
- index = "A"
- },
- {
- type = "RK",
- index = "B"
- },
- {
- type = "RK",
- index = "C"
- }
- },
- code = 13
- },
- LOADNIL = {
- type = "iABC",
- arguments = {
- {
- type = "R",
- index = "A"
- },
- {
- type = "R",
- index = "B"
- }
- },
- code = 3
- },
- LEN = {
- type = "iABC",
- arguments = {
- {
- type = "R",
- index = "A"
- },
- {
- type = "R",
- index = "B"
- }
- },
- code = 20
- },
- FORPREP = {
- type = "iAsBx",
- arguments = {
- {
- type = "R",
- index = "A"
- },
- {
- type = "N",
- index = "sBx"
- }
- },
- code = 32
- },
- MOD = {
- type = "iABC",
- arguments = {
- {
- type = "R",
- index = "A"
- },
- {
- type = "RK",
- index = "B"
- },
- {
- type = "RK",
- index = "C"
- }
- },
- code = 16
- },
- VARARG = {
- type = "iABC",
- arguments = {
- {
- type = "R",
- index = "A"
- },
- {
- type = "N",
- index = "B"
- }
- },
- code = 37
- },
- MOVE = {
- type = "iABC",
- arguments = {
- {
- type = "R",
- index = "A"
- },
- {
- type = "R",
- index = "B"
- }
- },
- code = 0
- },
- LE = {
- type = "iABC",
- arguments = {
- {
- type = "R",
- index = "A"
- },
- {
- type = "RK",
- index = "B"
- },
- {
- type = "RK",
- index = "C"
- }
- },
- code = 25
- },
- GETGLOBAL = {
- type = "iABx",
- arguments = {
- {
- type = "R",
- index = "A"
- },
- {
- type = "C",
- index = "Bx"
- }
- },
- code = 5
- }
- }
- }
- local function compileparsed( ... )
- local parsed = ({ ... })[1]
- local lookup = {}
- local out = ""
- local compileFunction -- declare here so that all functions can access it. Weird local rules
- local function compile(s)
- out = out .. s
- end
- local function numToByteString(n, bytes, forceBigEndian)
- if lookup.endianness == 0 or forceBigEndian then
- for i=bytes-1, 0, -1 do
- local byte = bit.band(bit.brshift(n, i * 8), 0xff)
- compile(string.char(byte))
- end
- elseif lookup.endianness == 1 then
- for i=0, bytes - 1 do
- local byte = bit.band(bit.brshift(n, i * 8), 0xff)
- compile(string.char(byte))
- end
- else
- error("Unsupported endianness:" .. tostring(lookup.endianness))
- end
- end
- local function compileByte(b)
- compile(string.char(b))
- end
- local function compileInteger(n)
- numToByteString(n, lookup.size_int)
- end
- local function compileString(s)
- numToByteString(#s + 1, lookup.size_t)
- compile(s .. "\0")
- end
- local function compileNumber(n)
- assert(lookup.lua_Number == 8, "Unsupported lua_Number size")
- local frac = n - math.floor(n)
- local whole = n - frac
- local function wholeToBin(whole)
- local bWhole = ""
- repeat
- bWhole = bWhole .. (whole % 2)
- whole = math.floor(whole / 2)
- until whole == 0
- return bWhole:reverse()
- end
- local bWhole = wholeToBin(whole)
- local bFrac = ""
- while frac ~= 0 do
- frac = frac * 2
- if frac >= 1 then
- frac = frac - 1
- bFrac = bFrac .. "1"
- else
- bFrac = bFrac .. "0"
- end
- end
- bWhole = bWhole:gsub("^0*", "")
- bFrac = bFrac:gsub("0*$", "")
- if bWhole .. bFrac == "" then
- for i=1,lookup.lua_Number do compileByte(0) end
- return
- end
- local ex = #bWhole - 1
- local final
- if ex < 0 then
- ex = -bFrac:find("1")
- final = bFrac:sub(-ex + 1)
- else
- final = bWhole:sub(2) .. bFrac
- end
- local sign = (n >= 0 and "0" or "1")
- local sEx --[[ hehe ]] = wholeToBin(ex + 1023)
- sEx = ("0"):rep(11 - #sEx) .. sEx
- final = final .. ("0"):rep(52 - #final)
- local fBin = sign .. sEx .. final
- for i=1, lookup.lua_Number * 8, 8 do
- local byte = tonumber(fBin:sub(i, i + 7), 2)
- compileByte(byte)
- end
- end
- local function compileBoolean(b)
- compileByte(b)
- end
- local function compileInstructions(f)
- compileInteger(#(f.instructions))
- for i,v in ipairs(f.instructions) do
- local inst = 0
- inst = bit.bor(inst, v.code)
- if not v.A then v.A = 0 end
- inst = bit.bor(inst, bit.blshift(v.A, 6))
- if v.type == "iABC" then
- if not v.B then v.B = 0 end
- if not v.C then v.C = 0 end
- inst = bit.bor(inst, bit.blshift(v.B, 23))
- inst = bit.bor(inst, bit.blshift(v.C, 14))
- elseif v.type == "iABx" then
- if not v.Bx then v.Bx = 0 end
- inst = bit.bor(inst, bit.blshift(v.Bx, 14))
- elseif v.type == "iAsBx" then
- if not v.sBx then v.sBx = 0 end
- v.sBx = 131071 + v.sBx -- 131071 = bit.brshift(2^18, 1)
- inst = b.bor(inst, bit.blshift(v.sBx, 14))
- end
- numToByteString(inst, lookup.size_inst)
- end
- end
- local function compileConstants(f)
- compileInteger(#(f.constants))
- for i,v in ipairs(f.constants) do
- if v.type == "boolean" then
- compileByte(1)
- compileBoolean(v.data)
- elseif v.type == "number" then
- compileByte(3)
- compileNumber(v.data)
- elseif v.type == "string" then
- compileByte(4)
- compileString(v.data)
- end
- end
- end
- local function compileFunctionProtos(f)
- compileInteger(#(f.prototypes))
- for i,v in ipairs(f.prototypes) do
- compileFunction(v)
- end
- end
- local function compileLinePositions(f)
- compileInteger(#(f.instructions))
- for i,v in ipairs(f.instructions) do
- compileInteger(v.line)
- end
- end
- local function compileLocals(f)
- compileInteger(#(f.locals))
- for i,v in ipairs(f.locals) do
- compileString(v)
- if i <= f.numParams then
- compileInteger(0)
- else
- compileInteger(1)
- end
- compileInteger(#(f.instructions) - 1)
- end
- end
- local function compileUpvalues(f)
- compileInteger(#(f.upvalues))
- for i,v in ipairs(f.upvalues) do
- compileString(v)
- end
- end
- local function compileFunction(func)
- numToByteString(0, lookup.size_int)
- compileInteger(func.lineDefined)
- compileInteger(func.lastLineDefined)
- compileByte(#(func.upvalues))
- compileByte(func.numParams)
- compileByte(func.vararg)
- compileByte(func.stacksize)
- compileInstructions(func)
- compileConstants(func)
- compileFunctionProtos(func)
- compileLinePositions(func)
- compileLocals(func)
- compileUpvalues(func)
- end
- for i,v in ipairs(language.header) do
- numToByteString(v.val, v.size, true)
- lookup[v.name] = v.val
- end
- compileFunction(parsed)
- return out
- end
- local function lexstring( ... )
- ------------------------------------------------------------------
- -- lexstring.function --
- ------------------------------------------------------------------
- --[[
- lexstring.function takes a string argument and returns a table of tokens
- token = {
- type= type of data,
- data= the text of the data,
- base= if type is number, the base for the number system,
- quoteType= if type is string, the type of quote used
- }
- ]]
- ------------------------------------------------------------------ variables
- local src = ({ ... })[1]
- local tokens = {}
- local line = 1
- local function isWhite()
- return src:find("^%s") == 1 or src:find("^%-%-") == 1
- end
- local function isNumber(_src)
- return src:find("^[%d+-]") == 1
- end
- local function isString()
- return src:find("^['\"]") == 1 or src:find("^%[=*%[") == 1
- end
- local function isBoolean()
- return src:find("^true") == 1 or src:find("^false") == 1
- end
- local function isKeyword()
- local str = src:match("^.%a+")
- if not str then return false end
- return language.keywords[str:lower()] and true or false
- end
- local function isInstruction()
- local str = src:match("^%a+")
- if not str then return false end
- return language.opcodes[str:upper()] and true or false
- end
- local function isAssignment()
- return src:find("^%%%w+") == 1
- end
- local function isSymbol()
- return language.symbols[src:sub(1,1)]
- end
- while src ~= "" do
- local token = {line=line}
- -- leading whitespaces
- if isWhite() then
- local removed = ""
- src = src:gsub("^%s*", function(w)
- removed = removed .. w
- return ""
- end)
- src = src:gsub("^(%-%-%[(=*)%[.*%]%2%])", function(comment)
- removed = removed .. comment
- return ""
- end)
- src = src:gsub("^%-%-[^\n]*", function(comment)
- removed = removed .. comment
- return ""
- end)
- local _, removedLines = removed:gsub("\n", "")
- line = line + removedLines
- elseif isNumber() then
- local dat = ""
- src = src:gsub("^[+-]", function(sign)
- dat = dat .. sign
- return ""
- end)
- local base
- for k,v in pairs({[16] = "^0[xX]%x+", [2] = "^0[bB][01]+", [8] = "^0[oO][0-7]+"}) do
- src = src:gsub(v, function(num)
- dat = dat .. num
- base = k
- return ""
- end)
- end
- if not base then
- -- decimal number for sure
- src = src:gsub("^%d+%.?%d*", function(num)
- dat = dat .. num
- base = 10
- return ""
- end)
- end
- assert(base and src:find("^%w") ~= 1, "Malformed number at line: " .. line)
- token.type = "number"
- token.data = dat
- token.base = base
- elseif isString() then
- -- string
- local dat
- local quoteType
- src = src:gsub("^((['\"])[^\n]-%2)", function(str, _quoteType)
- dat = str
- quoteType = _quoteType
- return ""
- end)
- if not dat then
- src = src:gsub("^(%[(=*)%[.-%]%2%])", function(str, levels)
- dat = str
- local _, newLines = str:gsub("\n", "")
- line = line + newLines
- quoteType = "[" .. levels .. "["
- return ""
- end)
- end
- assert(dat, "Malformed string at line: " .. line)
- token.type = "string"
- token.data = dat
- token.quoteType = quoteType
- elseif isBoolean() then
- token.type = "boolean"
- if src:find("^true") then
- src = src:sub(5)
- token.data = "true"
- else
- src = src:sub(6)
- token.data = "false"
- end
- elseif isKeyword() then
- src = src:gsub("^.%a+", function(w)
- token.type = "keyword"
- token.data = w
- return ""
- end)
- elseif isInstruction() then
- src = src:gsub("^%a+", function(w)
- token.type = "instruction"
- token.data = w
- return ""
- end)
- elseif isAssignment() then
- src = src:gsub("^%%%w+", function(w)
- token.type = "assignment"
- token.data = w
- return ""
- end)
- elseif isSymbol() then
- token.type = "symbol"
- token.data = src:sub(1,1)
- src = src:sub(2)
- else
- error("Unexpected token: " .. src:match("^%.-%s?") .. " at line: " .. line)
- end
- if token.type then
- table.insert(tokens, token)
- end
- end
- return tokens
- end
- local function loadfile( ... )
- local path = ({ ... })[1]
- local f = assert(fs.open(path, "r"))
- return loadstring(f.readAll(), fs.getName(path))
- end
- local function loadstring( ... )
- local args = { ... }
- assert(type(args[1]) == "string" and type(args[2]) == "string", "Expected string, string", 2)
- local str
- local ok, err = pcall(function()
- str = compileparsed(parselexed(lexstring(args[1])))
- end)
- if not ok then
- return ok, err
- end
- local f
- f, err = _G.loadstring(str, args[2])
- return f, err
- end
- local function parselexed( ... )
- local loadstring = _G.loadstring -- would prefer to use this for string loading, since i have control over the strings.
- local tokens = ({ ... })[1]
- -- fix no function declaration for top level
- --table.insert(tokens, 1, {type="keyword", data=".function"})
- -- don't need .function because we just need the terminator
- if #tokens > 0 then
- local line = tokens[#tokens].line + 1
- table.insert(tokens, {type="keyword", data=".end", line=line})
- end
- local function parseFunction(parent, lineDefined)
- local func = {}
- func.assignments = {}
- func.lineDefined = lineDefined
- func.numParams = 0
- func.vararg = 2
- func.stacksize = nil
- func.instructions = {}
- func.constants = {}
- func.prototypes = {}
- func.linePositions = {}
- func.locals = {}
- func.upvalues = {}
- -- put parse functions here so they have proper assignment access.
- local function parseNumberToken(t) -- numbers
- assert(t.type == "number", "Number expected: " .. t.line)
- local sNum = t.data
- if t.base ~= 10 then
- sNum = sNum:sub(3)
- end
- return tonumber(sNum, t.base)
- end
- local function parseStringToken(t)
- assert(t.type == "string", "String expected: " .. t.line)
- return assert(_G.loadstring("return " .. t.data, "doesn'tmatter"))()
- end
- local function parseConstantToken(t)
- if t.type == "string" then
- return parseStringToken(t), "string"
- elseif t.type == "number" then
- return parseNumberToken(t), "number"
- elseif t.type == "boolean" then
- return (t.data == "true" and 1 or 0), "boolean"
- else
- error("Expected constant on line: " .. t.line)
- end
- end
- local function parseInstruction(inst) -- instructions
- local opInfo = language.opcodes[inst]
- local args = {code=opInfo.code, type=opInfo.type}
- for i=1, #(opInfo.arguments) do
- local t = table.remove(tokens, 1)
- assert(t.type == "number" or t.type == "assignment" or t.type == "symbol", "Expected argument on line: " .. t.line)
- local number
- if t.type == "assignment" then
- assert(func.assignments[t.data], "Nonexistent assignment used on line: " .. t.line)
- assert(func.assignments[t.data].type == opInfo.arguments[i].type, t.line .. ": expected " .. opInfo.arguments[i].type .. ", got " .. func.assignments[t.data].type)
- number = func.assignments[t.data].value
- elseif t.type == "symbol" and t.data == "&" and (opInfo.arguments[i].type == "C" or opInfo.arguments[i].type == "RK") then
- local c, cType = parseConstantToken(table.remove(tokens, 1))
- number = #(func.constants) + (opInfo.arguments[i].type == "RK" and 256 or 0)
- table.insert(func.constants, {type=cType, data=c})
- else
- number = parseNumberToken(t)
- end
- args[opInfo.arguments[i].index] = number
- end
- return args
- end
- for t in function() return table.remove(tokens, 1) end do
- if t.type == "instruction" then
- local inst = parseInstruction(t.data:upper())
- inst.line = t.line
- table.insert(func.instructions, inst)
- elseif t.type == "keyword" then
- if t.data == ".end" then
- func.lastLineDefined = t.line
- local returnInfo = language.opcodes.RETURN
- table.insert(func.instructions, {code=returnInfo.code, type=returnInfo.type, A=0, B=1, line=t.line})
- break
- elseif t.data == ".function" then
- if tokens[1] and tokens[1].type == "string" then
- func.assignments["%" .. parseStringToken(table.remove(tokens, 1))] = {value = #(func.prototypes), type = "P"}
- end
- table.insert(func.prototypes, parseFunction(func, t.line))
- elseif t.data == ".param" then
- assert(func.numParams == #(func.locals), "Local cannot be declared before parameter on line: " .. t.line)
- local name = parseStringToken(table.remove(tokens, 1))
- func.assignments["%" .. name] = {value = #(func.locals), type = "R"}
- table.insert(func.locals, name)
- func.numParams = func.numParams + 1
- elseif t.data == ".vararg" then
- func.vararg = parseNumberToken(table.remove(tokens, 1))
- elseif t.data == ".const" then
- local c, cType = parseConstantToken(table.remove(tokens, 1))
- table.insert(func.constants, {type=cType, data=c})
- elseif t.data == ".upval" then
- local name = parseStringToken(table.remove(tokens, 1))
- func.assignments["%" .. name] = {value = #(func.upvalues), type = "U"}
- table.insert(func.upvalues, name)
- elseif t.data == ".local" then
- local name = parseStringToken(table.remove(tokens, 1))
- func.assignments["%" .. name] = {value = #(func.locals), type = "R"}
- table.insert(func.locals, name)
- elseif t.data == ".stacksize" then
- func.stacksize = parseNumberToken(table.remove(tokens, 1))
- end
- else
- error("Unexpected token on line: " .. t.line)
- end
- end
- assert(func.stacksize, "Must specify stack size")
- return func
- end
- local top = parseFunction(nil, 0)
- top.lastLineDefined = 0 -- standard for top level func
- return top
- end
- local tArgs = { ... }
- assert(tArgs[1] ~= nil, "Input file expected")
- local path = fs.combine(shell.dir(), tArgs[1])
- local src = assert(fs.exists(path) and not fs.isDir(path) and fs.open(path, "r"), "Input file expected").readAll()
- local out = fs.open(fs.combine(shell.dir(), tArgs[2] or "a.out"), "wb")
- local lexed = lexstring(src, path and fs.getName(path) or "lasm")
- local parsed = parselexed(lexed)
- local compiled = compileparsed(parsed)
- local time = os.time()
- for w in compiled:gmatch(".") do
- if os.time() - time > 0.03 then
- sleep(0)
- time = os.time()
- end
- out.write(string.byte(w))
- end
- out.close()
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement