#!/usr/bin/env lua --[[------------------------------------------------------------------- TestRig.lua Tests for ChunkBake. Copyright (c) 2005 Kein-Hong Man The COPYRIGHT file describes the conditions under which this software may be distributed (basically a Lua 5-style license.) http://luaforge.net/projects/chunkbake/ http://www.geocities.com/keinhong/chunkbake.html See the ChangeLog for more information. --]]------------------------------------------------------------------- --[[------------------------------------------------------------------- -- Notes: -- * TODO some error messages, results, can be improved, but all tests -- are flagged as successful for now. To be fixed later. -- * NOTE some comments describe quirks, see below... -- * Test suite parser/code generator data ONLY for IA32/x86 platforms -- * This test suite takes control of ChunkBake using two variables: -- TEST (a flag) and WARN (to hold warning messages) -- * Invasive testing code in ChunkBake.lua is minimal. -- * Disassembly code generated by ChunkSpy 0.9.4/0.9.5. ----------------------------------------------------------------------- -- HOW TO CREATE PARSER TESTS -- * First, write the test case, then use Disp() to run the test. -- * Disp() takes the same arguments as Test() but it simply prints -- out what ChunkBake.lua generates. -- * Use WriteChunk() (requires ChunkSpy) to generate a disassembled -- listing, from which correctness can be checked, either visually -- or with ChkCode(). sample.out and sample.txt are generated. -- * ChkCode() is an easy-to-use function for verifying assembled -- code using textual output from ChunkSpy. -- * Fix any bugs in ChunkBake.lua or the test case so that the -- the result is what is expected or required. -- * Change Disp() to Test() to lock the test case in, with the -- appropriate expected result (OKAY or DIES) and optional error -- message and optional warning message. -- * NOTE subset matching is done for errors and warnings, see code. -- * DispChunk() is a hex dumper for the generated binary chunk, -- useful for examining the generated data. -- * ChkChunk(), IntChunk() and HexChunk() helps test parts of a -- binary chunk. ChkCode() is more convenient. --]]------------------------------------------------------------------- --[[------------------------------------------------------------------- -- set these verbosity flags to make testing easier... --]]------------------------------------------------------------------- --VERBOSE_LEX = true -- set to true to report everything --VERBOSE_PARSE = true --DONTTEST_LEX = true -- set to true to disable --DONTTEST_PARSE = true ----------------------------------------------------------------------- TEST = true -- always true to skip ChunkBase's main() require("../ChunkBake.lua") title = [[ TestRig for ChunkBake: Automatically test the assembler... Version 0.7.0 (20050512) Copyright (c) 2005 Kein-Hong Man The COPYRIGHT file describes the conditions under which this software may be distributed (basically a Lua 5-style license.) * You can set the verbosity flags to make testing easier.]] --[[------------------------------------------------------------------- -- support functions --]]------------------------------------------------------------------- ----------------------------------------------------------------------- -- escape control bytes in strings ----------------------------------------------------------------------- function EscapeString(s) s = s or "" local v = "" for i = 1, string.len(s) do local c = string.byte(s, i) -- other escapees with values > 31 are "(34), \(92) if c < 32 or c == 34 or c == 92 then if c >= 7 and c <= 13 then c = string.sub("abtnvfr", c - 6, c - 6) elseif c == 34 or c == 92 then c = string.char(c) end v = v.."\\"..c else-- 32 <= v <= 255 v = v..string.char(c) end end return "\""..v.."\"" end --[[------------------------------------------------------------------- -- test cases for lexical analyzer --]]------------------------------------------------------------------- function LexTest() local DIES, OKAY = false, true local crashed = false local total = 0 --------------------------------------------------------------- -- displays lex results (for composing test cases) --------------------------------------------------------------- local function Disp(outcome, src) local tt, tok, isint Lex:Init(src) repeat tt, tok, isint = Lex:Lex() if type(tok) == "string" then tok = EscapeString(tok) end print(tt, tok, type(tok)) until tok == Lex.EOF end --------------------------------------------------------------- -- test case tester -- * a failure case has to be the first one lexed --------------------------------------------------------------- local function Test(outcome, src, expected) local ok, tt, tok, isint, busted local idx = 1 total = total + 1 if VERBOSE_LEX then print("\n-------------------------------------\n".. "-- TEST: "..total.."\n".. "-------------------------------------") end Lex:Init(src) ------------------------------------------------------------- -- displays helpfile messages ------------------------------------------------------------- local function DispSrc() print("SOURCE: "..EscapeString(src)) end ------------------------------------------------------------- -- loop to read in and check lexed tokens ------------------------------------------------------------- repeat ok, tt, tok, isint = pcall(Lex.Lex, Lex) ----------------------------------------------------------- -- actual OKAY ----------------------------------------------------------- if ok and outcome == OKAY then local ett, etok = expected[idx], expected[idx + 1] idx = idx + 2 if not ett or not etok then DispSrc() print("ERROR: ran out of stuff to match in line "..Lex.line) busted = true; break elseif type(tok) ~= type(etok) then DispSrc() print("ERROR: token type different in line "..Lex.line) busted = true; break elseif tt ~= ett or tok ~= etok then DispSrc() print("ERROR: token did not match in line"..Lex.line) busted = true; break end -- don't do anything if okay elseif ok and outcome == DIES then DispSrc() print("ERROR: expected DIES but lexer OKAY in line "..Lex.line) busted = true; break ----------------------------------------------------------- -- actual DIES ----------------------------------------------------------- elseif not ok and outcome == DIES then local ett = expected[idx] if not ett then DispSrc() print("ERROR: ran out of stuff to match in line "..Lex.line) busted = true; break elseif not string.find(tt, ett, 1, 1) then DispSrc() print("ACTUAL: "..tt) print("EXPECTED: "..ett) print("ERROR: different error message in line "..Lex.line) busted = true; break end -- don't do anything if okay break else--if not ok and outcome == OKAY then DispSrc() print("ERROR: expected OKAY but lexer DIES") print("ERROR: '"..tt.."' in line "..Lex.line) busted = true; break end until tok == Lex.EOF if busted then crashed = true elseif VERBOSE_LEX then DispSrc() if outcome == OKAY then print("STATUS: lexer OKAY as expected") else--if outcome == DIES then print("STATUS: lexer DIES as expected") end end end --------------------------------------------------------------- -- lexer test cases --------------------------------------------------------------- Test(OKAY, nil, -- empty argument {"TK_EOF",-1,} ) Test(OKAY, "", -- empty input string {"TK_EOF",-1,} ) Test(OKAY, "\n\n\r\r", -- newlines {"TK_EOL","\n","TK_EOL","\n","TK_EOL","\n", "TK_EOL","\n","TK_EOF",-1,} ) Test(OKAY, "\r\n\n\r\n", -- different newline styles {"TK_EOL","\n","TK_EOL","\n","TK_EOL","\n","TK_EOF",-1,} ) Test(OKAY, ";\n \tblah ; comment\r\n;; \n", -- white space, ;comments {"TK_EOL","\n","TK_SYM","blah","TK_EOL","\n", "TK_EOL","\n","TK_EOF",-1,} ) --------------------------------------------------------------- Test(OKAY, "-- comment\n--comment", -- --comments {"TK_EOL","\n","TK_EOF",-1,} ) Test(OKAY, "--[[comment]]blah--[[blah\n\nblah]]\n", -- --[[comments]] {"TK_SYM","blah","TK_EOL","\n","TK_EOF",-1,} ) --------------------------------------------------------------- Test(OKAY, "123 123.456 12.e-34 12.3E+45", -- numbers {"TK_NUM",123,"TK_NUM",123.456, "TK_NUM",1.2e-033,"TK_NUM",1.23e+046,"TK_EOF",-1,} ) Test(OKAY, ".123 .12e-34", -- numbers, leading . {"TK_NUM",0.123,"TK_NUM",1.2e-035,"TK_EOF",-1,} ) Test(OKAY, "12..34 12.34.56 .12.34.56", -- not really numbers {"TK_NUM",12,"TK_OP","..","TK_NUM",34, "TK_NUM",12.34,"TK_NUM",0.56, "TK_NUM",0.12,"TK_NUM",0.34,"TK_NUM",0.56,"TK_EOF",-1,} ) Test(OKAY, "0x1234 0xBEEF 0XDEAD", -- hex numbers {"TK_NUM",4660,"TK_NUM",48879,"TK_NUM",57005,"TK_EOF",-1,} ) -- NOTE the following is DEPRECATED; the expression parser recognizes -- '-' as a unary prefix minus operator, so the following is no longer -- valid --[[ Disp(OKAY, "-1 -1.23 -1.23E45", -- negative numbers {"TK_EOF",-1,} ) --]] --------------------------------------------------------------- Test(OKAY, [["blah blah" 'boo hoo']], -- strings {"TK_STRING","blah blah","TK_STRING","boo hoo","TK_EOF",-1,} ) Test(DIES, [["]], -- unterminated string {"undelimited string",} ) Test(DIES, [["blah]], -- unterminated string {"undelimited string",} ) Test(OKAY, [["\a\b\f\n\r\t\v"]], -- string escapes {"TK_STRING","\a\b\f\n\r\t\v","TK_EOF",-1,} ) Test(OKAY, "\"blah\\\nblah\"", -- line continuation in string {"TK_STRING","blah\nblah","TK_EOF",-1,} ) Test(OKAY, [[ "\'\"\\" ]], -- punctuation escapes in string {"TK_STRING","'\"\\","TK_EOF",-1,} ) Test(OKAY, [['\0\12\123\045']], -- \ddd escapes {"TK_STRING","\0\12\123\45","TK_EOF",-1,} ) Test(DIES, [['\256']], -- \ddd escape overflow {"\\ddd escape overflow",} ) Test(OKAY, " [[]] [[blah]] [[\nblah\nblah]] ", -- [[]] long strings {"TK_STRING","","TK_STRING","blah", "TK_STRING","blah\nblah","TK_EOF",-1,} ) Test(DIES, "[[\n", -- unterminated long string {"incomplete long string",-1,} ) Test(OKAY, "[[[[]]]]\n[[-[[-[[-]]-]]-]]", -- nested long string {"TK_STRING","[[]]","TK_EOL","\n", "TK_STRING","-[[-[[-]]-]]-","TK_EOF",-1,} ) --------------------------------------------------------------- Test(OKAY, "ab..cd", -- concatenation {"TK_SYM","ab","TK_OP","..","TK_SYM","cd","TK_EOF",-1,} ) Test(OKAY, "blah\\\nblah \\ blah", -- line continuation {"TK_SYM","blah","TK_SYM","blah","TK_OP","\\", "TK_SYM","blah","TK_EOF",-1,} ) --------------------------------------------------------------- Test(OKAY, "abc123 _symbol _abc123 _123", -- symbols {"TK_SYM","abc123","TK_SYM","_symbol","TK_SYM","_abc123", "TK_SYM","_123","TK_EOF",-1,} ) Test(OKAY, "move GETGLOBAL NewTable", -- mnemonics {"TK_MNE","MOVE","TK_MNE","GETGLOBAL", "TK_MNE","NEWTABLE","TK_EOF",-1,} ) Test(OKAY, "nil true false and or", -- keywords {"TK_KEY","nil","TK_KEY","true", "TK_KEY","false","TK_KEY","and", "TK_KEY","or","TK_EOF",-1,} ) --------------------------------------------------------------- Test(OKAY, "label: _LABEL: ", -- labels {"TK_LABEL","label","TK_LABEL","_LABEL","TK_EOF",-1,} ) Test(OKAY, "R0 R12 R123 R12.34", -- registers {"TK_REG",0,"TK_REG",12,"TK_REG",123,"TK_REG",12, "TK_NUM",0.34,"TK_EOF",-1,} ) Test(OKAY, "$0 $ $12 $12.34", -- registers {"TK_REG",0,"TK_OP","$","TK_REG",12,"TK_REG",12, "TK_NUM",0.34,"TK_EOF",-1,} ) --------------------------------------------------------------- Test(OKAY, ".directive .abc123", -- directives {"TK_CMD",".directive","TK_CMD",".abc123","TK_EOF",-1,} ) -- NOTE the following is DEPRECATED; the expression parser uses -- '#' as a unary prefix operator, so this is no longer needed --[[ Disp(OKAY, "#123 #-123 #12.34 #12.34e-56 #0xDEAD", -- immediate numbers {"TK_IMM",123,"TK_IMM",-123,"TK_IMM",12.34, "TK_IMM",1.234e-055,"TK_IMM",57005,"TK_EOF",-1,} ) --]] Test(OKAY, "# #- #A", -- other instances of # symbol {"TK_OP","#","TK_OP","#","TK_OP","-","TK_OP","#", "TK_SYM","A","TK_EOF",-1,} ) Test(OKAY, "+ - / * = % | ", -- punctuation/operators {"TK_OP","+","TK_OP","-","TK_OP","/","TK_OP","*","TK_OP","=", "TK_OP","%","TK_OP","|","TK_EOF",-1,} ) Test(OKAY, "~= == <= >= +=", -- two-character operators {"TK_OP","~=","TK_OP","==","TK_OP","<=","TK_OP",">=", "TK_OP","+","TK_OP","=","TK_EOF",-1,} ) Test(OKAY, ".. ... .", -- three-character operators {"TK_OP","..","TK_OP","...","TK_OP",".","TK_EOF",-1,} ) Test(DIES, "\3", -- control characters {"unknown character or control character",} ) --------------------------------------------------------------- --Disp(OKAY, "", -- template for test cases, first examine -- {"TK_EOF",-1,} -- using Disp() then rewrite as a Test() --) --------------------------------------------------------------- -- summary of lexer testing --------------------------------------------------------------- print("\n-------------------------------------\n".. "STATUS: "..total.." lexer tests performed") if not crashed then print("STATUS: all lexer tests succeeded") else print("STATUS: errors were found; check the verbose output") end print("-------------------------------------") end --[[------------------------------------------------------------------- -- test cases for parser --]]------------------------------------------------------------------- function ParseTest() local DIES, OKAY, NONE = false, true, nil local crashed = false local total = 0 local lastsrc -- these are local to ParseTest so that further testing on the -- correctness of the processed output can be done local bindata, lstdata --------------------------------------------------------------- -- displays parse results (for composing test cases) --------------------------------------------------------------- local function Disp(outcome, src) local ok ok, bindata, lstdata = pcall(Parse.Parse, Parse, src, "(test)") if not ok then print(bindata) end end --------------------------------------------------------------- -- test case tester -- * caller is responsible for checking parsed data --------------------------------------------------------------- local function Test(outcome, src, errmsg, warning) local ok, busted lastsrc = src total = total + 1 if VERBOSE_PARSE then print("\n-------------------------------------\n".. "-- TEST: "..total.."\n".. "-------------------------------------") end ------------------------------------------------------------- -- displays helpfile messages ------------------------------------------------------------- local function DispSrc() print("SOURCE:\n"..src) end ------------------------------------------------------------- -- make the assembler call ------------------------------------------------------------- WARN = "" -- reset warning messages ok, bindata, lstdata = pcall(Parse.Parse, Parse, src, "(test)") ------------------------------------------------------------- -- handle warnings ------------------------------------------------------------- if WARN == "" then if warning then DispSrc() print("WARNING: "..warning) print("ACTUAL: --none--") print("ERROR: missing warning message\n") end elseif WARN ~= "" then if warning and warning ~= "" and not string.find(WARN, warning, 1, 1) then DispSrc() print("WARNING: "..warning) print("ACTUAL: "..WARN) print("ERROR: warning message different\n") elseif not warning or warning == "" then DispSrc() print("WARNING: --none expected--") print("ACTUAL: "..WARN) print("ERROR: unexpected warning message\n") end end ------------------------------------------------------------- -- actual OKAY ------------------------------------------------------------- if ok and outcome == OKAY then -- correct, don't do anything elseif ok and outcome == DIES then busted = true DispSrc() if not errmsg then errmsg = "(none)" end print("EXPECTED: "..errmsg) print("ERROR: expected DIES but lexer OKAY\n") ------------------------------------------------------------- -- actual DIES ------------------------------------------------------------- elseif not ok and outcome == DIES then -- verify error message is correct if errmsg and not string.find(bindata, errmsg, 1, 1) then busted = true DispSrc() print("EXPECTED: "..errmsg) print("ACTUAL: "..bindata) print("ERROR: different error message\n") elseif not errmsg or errmsg == "" then busted = true DispSrc() print("ACTUAL: "..bindata) print("ERROR: expected error for testing not specified\n") end -- otherwise correct, don't do anything else--if not ok and outcome == OKAY then busted = true DispSrc() print("ACTUAL: "..bindata) print("ERROR: expected OKAY but lexer DIES\n") end ------------------------------------------------------------- -- optional verbose messages ------------------------------------------------------------- if busted then crashed = true elseif VERBOSE_PARSE then DispSrc() if outcome == OKAY then print("STATUS: parser OKAY as expected") else--if outcome == DIES then print("STATUS: parser DIES as expected") end end end --------------------------------------------------------------- -- convenience display for binary chunk data --------------------------------------------------------------- local function DispChunk() print("-----------------------------------------\n".. "-- hex dump of binary chunk produced --\n".. "-----------------------------------------") local ln, asc, pos = "", "", 0 local function Line() print(string.format("%04X %s %s", pos, ln, asc)) end for i = 1, string.len(bindata) do local ch = string.byte(bindata, i) ln = ln..string.format("%02X ", ch) if ch >= 32 and ch <= 127 then asc = asc..string.char(ch) else asc = asc.."." end if math.mod(i, 8) == 0 then Line() pos = pos + string.len(ln) / 3 ln = ""; asc = "" end end if ln ~= "" then ln = ln..string.rep(" ", 8 - string.len(ln) / 3) Line() end print("-----------------------------------------") end --------------------------------------------------------------- -- convenience function for writing binary chunk to a file --------------------------------------------------------------- local function WriteChunk() local OUTF = io.open("sample.out", "wb") if not OUTF then error("cannot open \"sample.out\" for writing") end OUTF:write(bindata) io.close(OUTF) os.execute("chunkspy sample.out > sample.txt") print("STATUS: data written to sample.out & sample.txt") end --------------------------------------------------------------- -- checks a field in a binary chunk -- * note position is indexed from 0 --------------------------------------------------------------- local function ChkChunk(pos, expected) if type(pos) == "string" then pos = tonumber(pos, 16) end local len = string.len(expected) local actual = string.sub(bindata, pos + 1, pos + len) -- display hex values local function hexdata(data) local len, ln = string.len(data), "" for i = 1, len do ln = ln..string.format("%02X ", string.byte(data, i)) end return ln end if actual ~= expected then crashed = true print("SOURCE:\n"..lastsrc) print("ERROR: binary chunk field different") print("POSITION: "..pos) print("EXPECTED: "..hexdata(expected)) print("ACTUAL: "..hexdata(actual)) end end --------------------------------------------------------------- -- like ChkChunk but accepts an integer --------------------------------------------------------------- local function IntChunk(pos, intexp) local expected = "" for i = 1, 4 do expected = expected..string.char(math.mod(intexp, 256)) intexp = math.floor(intexp / 256) end ChkChunk(pos, expected) end --------------------------------------------------------------- -- like ChkChunk but accepts a hex string --------------------------------------------------------------- local function HexChunk(pos, hexexp) local sz = math.floor(string.len(hexexp) / 2) local expected = "" for i = 1, sz do local asc = tonumber(string.sub(hexexp, i * 2 - 1, i * 2), 16) expected = expected..string.char(asc) end ChkChunk(pos, expected) end --------------------------------------------------------------- -- accepts normal disassembled output from ChunkSpy and uses -- it to verify correctness of a binary chunk --------------------------------------------------------------- local function ChkCode(data) if not data or data == "" then return end local p, pmax = 1, string.len(data) while p <= pmax do -- find a non-empty line local q, r, l = string.find(data, "([^\r\n]*)[\r\n]*", p) if q then p = r + 1 -- parse line for data local _, _, hexpos, hexexp = string.find(l, "^(%x+)%s+(%x+)") if hexpos and hexexp then HexChunk(hexpos, hexexp) -- verify with bindata end else return end end end --------------------------------------------------------------- -- parser test cases --------------------------------------------------------------- Test(DIES, "", -- empty file "no valid function defined in source file" ) Test(DIES, "\n\n\r\n\n", -- empty file consisting of newlines "no valid function defined in source file" ) --------------------------------------------------------------- Test(DIES, [[ .foo ]], -- bad directive and some newlines "unknown directive .foo encountered" ) -- Test(DIES, [[ bar ]], -- unknown identifier "orphaned symbol or identifier" ) --------------------------------------------------------------- Test(DIES, [[ .header .header ]], -- empty directive "no valid function defined in source file" ) Test(DIES, [[ .header foo ]], -- incomplete key-value pair "'=' expected in key-value parameter pair" ) Test(DIES, [[ LABEL: .header ]], -- test LABEL: type labels ".header doesn't accept label prefixes" ) Test(DIES, [[ LABEL .header ]], -- test LABEL type labels ".header doesn't accept label prefixes" ) --------------------------------------------------------------- Test(DIES, [[ .header foo=bar ]], -- wrong value type "unknown symbol 'bar' in expression" ) Test(DIES, [[ .header foo=0 ]], -- unknown key name "unrecognized .header property foo" ) Test(DIES, [[ .header foo="bar" ]], -- unknown key name "unrecognized .header property foo" ) --------------------------------------------------------------- Test(DIES, [[ .header signature=0 ]], -- incorrect value type "string expected for property signature" ) Test(DIES, [[ .header signature="foo" ]], -- correct key-value pair, no function body "no valid function defined in source file", "signature declared in not of normal length (4)" -- warning ) assert(Code.header.signature == "foo", "incorrectly read signature" ) --------------------------------------------------------------- Test(OKAY, [[ .header .function return 0 0 .end ]] -- very basic function (check binary chunk later) ) assert(Code.header.signature == nil and Code.config.signature == "\27Lua", "incorrectly handled standard signature" ) Test(DIES, [[ .function return 0 0 .end .header signature="\27Bar" ]], -- illegal header declaration ".header declarations must be defined at the start" ) --------------------------------------------------------------- Test(OKAY, [[ .header signature="\27Foo" .function return 0 0 .end ]] -- custom signature ) assert(Code.header.signature == "\27Foo" and Code.config.signature == "\27Foo", "incorrectly handled custom signature" ) --------------------------------------------------------------- Test(DIES, [[ .header version=foo ]], -- bad version spec "unknown symbol 'foo' in expression" ) Test(DIES, [[ .header version="foo" ]], -- bad version spec "number expected for property version" ) --------------------------------------------------------------- Test(OKAY, [[ .header version=0x52 .function return 0 0 .end ]] -- custom version number ) assert(Code.header.version == 82 and Code.config.version == 82, "incorrectly handled version field" ) --------------------------------------------------------------- Test(DIES, [[ .header endianness=2 ]], -- illegal endianness range "integer out of range for property endianness" ) Test(DIES, [[ .header endianness="foo" ]], -- illegal endianness spec "number expected for property endianness" ) --------------------------------------------------------------- Test(OKAY, [[ .header version=0x58 endianness=0 .function return 0 0 .end ]] -- multiple key-value pairs ) assert(Code.header.version == 88, "incorrectly handled version field" ) assert(Code.header.endianness == 0 and Code.config.endianness == 0, "incorrectly handled endianness field" ) --------------------------------------------------------------- Test(OKAY, [[ .header version = 0x54, \ endianness = 1 .function return 0 0 .end ]] -- multiple key-value pairs ) assert(Code.header.version == 84, "incorrectly handled version field" ) assert(Code.header.endianness == 1 and Code.config.endianness == 1, "incorrectly handled endianness field" ) --------------------------------------------------------------- Test(DIES, [[ .header int="foo" ]], -- illegal int spec "number expected for property int" ) Test(DIES, [[ .header int=6, size_t = 2, \ InSTruCtION = 5 .function return 0 0 .end ]], -- custom field settings, case-insensitivity "instruction bit field sizes inconsistent with instruction size" ) assert(Code.header.int == 6 and Code.config.int == 6, "incorrectly handled int field" ) assert(Code.header.size_t == 2 and Code.config.size_t == 2, "incorrectly handled size_t field" ) assert(Code.header.Instruction == 5 and Code.config.Instruction == 5, "incorrectly handled Instruction field" ) --------------------------------------------------------------- Test(DIES, [[ .header size_OP =4, ]], -- invalid opcode bit field size range "integer out of range for property size_op" ) Test(OKAY, [[ .header Instruction=5, \ SIZE_OP=8, SIZE_A=10, \ SIZE_B=11, SIZE_C = 11 .function return 0 0 .end ]] -- valid custom bit field sizes ) assert(Code.header.SIZE_OP == 8 and Code.config.SIZE_OP == 8, "incorrectly handled SIZE_OP field" ) assert(Code.header.SIZE_A == 10 and Code.config.SIZE_A == 10, "incorrectly handled SIZE_A field" ) assert(Code.header.SIZE_B == 11 and Code.config.SIZE_B == 11, "incorrectly handled SIZE_B field" ) assert(Code.header.SIZE_C == 11 and Code.config.SIZE_C == 11, "incorrectly handled SIZE_C field" ) Test(DIES, [[ .header SIZE_A=9 .function return 0 0 .end ]], -- invalid opcode field relationship "bit field A must be smaller than" ) Test(DIES, [[ .header SIZE_B=10 .function return 0 0 .end ]], -- invalid opcode field relationship "bit field B must be the same as" ) --------------------------------------------------------------- Test(DIES, [[ .header MAXSTACK = 0, ]], -- invalid range "integer out of range for property maxstack" ) Test(OKAY, [[ .function return 0 0 .end ]] -- default sizes for basic constants ) assert(Code.config.MAXSTACK == 250 and Code.config.MAXVARS == 200 and Code.config.MAXUPVALUES == 32 and Code.config.MAXPARAMS == 100 and Code.config.FPF == 32, "invalid default value for basic constants" ) Test(OKAY, [[ .header MaxStack=150, \ MaxVars=100, \ MaxUpvalues=47, \ MaxParams=74 .function return 0 0 .end ]] -- valid sample sizes for basic constants ) assert(Code.header.MAXSTACK == 150 and Code.config.MAXSTACK == 150, "incorrectly handled MAXSTACK property" ) assert(Code.header.MAXVARS == 100 and Code.config.MAXVARS == 100, "incorrectly handled MAXVARS property" ) assert(Code.header.MAXUPVALUES == 47 and Code.config.MAXUPVALUES == 47, "incorrectly handled MAXUPVALUES property" ) assert(Code.header.MAXPARAMS == 74 and Code.config.MAXPARAMS == 74, "incorrectly handled MAXPARAMS property" ) --------------------------------------------------------------- Test(DIES, [[ .header MaxStack=10 .function return 0 0 .end ]], -- invalid MAXSTACK size "MAXSTACK must be greater than 20" ) Test(DIES, [[ .header MaxStack=500 .function return 0 0 .end ]], -- invalid MAXSTACK size "MAXSTACK beyond range of instruction field A" ) Test(DIES, [[ .header MaxUpvalues=262145 .function return 0 0 .end ]], -- invalid MAXUPVALUES size "MAXUPVALUES beyond range of instruction field Bx" ) Test(DIES, [[ .header MaxVars=300 .function return 0 0 .end ]], -- invalid MAXVARS size "MAXVARS cannot exceed or equal MAXSTACK" ) Test(DIES, [[ .header MaxParams=200 .function return 0 0 .end ]], -- invalid MAXPARAMS size "MAXPARAMS cannot exceed or equal MAXVARS" ) Test(OKAY, [[ .header FPF=10 .function return 0 0 .end ]] -- custom FPF value ) assert(Code.header.FPF == 10 and Code.config.FPF == 10, "incorrectly handled FPF property" ) --------------------------------------------------------------- Test(DIES, [[ .header number_type = foobar ]], -- invalid number type specification "unknown symbol 'foobar' in expression" ) Test(DIES, [[ .header number_type = 47 ]], -- invalid number type specification "string expected for property number_type" ) Test(DIES, [[ .header number_type = "long" ]], -- invalid number type specification "unrecognized number type for property number_type" ) Test(OKAY, [[ .function return 0 0 .end ]] ) -- default number type assert(Number.lua_Number == 8 and Number.test_number == "\182\9\147\104\231\245\125\65" and Number.Convert == Number["double"], "incorrectly handled number_type property" ) Test(OKAY, [[ .header number_type = "single" .function return 0 0 .end ]] ) -- custom number type assert(Number.lua_Number == 4 and Number.test_number == "\59\175\239\75" and Number.Convert == Number["single"], "incorrectly handled number_type property" ) --------------------------------------------------------------- Test(OKAY, [[ .header .function return 0 0 .end ]] -- check default header values ) --DispChunk() ChkChunk(0, "\27Lua") -- signature ChkChunk(4, "\80") -- version ChkChunk(5, "\1") -- endianness ChkChunk(6, "\4") -- sizeof int ChkChunk(7, "\4") -- sizeof size_t ChkChunk(8, "\4") -- sizeof Instruction ChkChunk(9, "\6") -- sizeof SIZE_OP ChkChunk(10, "\8") -- sizeof SIZE_A ChkChunk(11, "\9") -- sizeof SIZE_B ChkChunk(12, "\9") -- sizeof SIZE_C ChkChunk(13, "\8") -- sizeof lua_Number ChkChunk(14, "\182\9\147\104\231\245\125\65") -- sample number -- check by dump from ChunkSpy ChkCode([[ 0000 1B4C7561 header signature: "\27Lua" 0004 50 version (major:minor hex digits) 0005 01 endianness (1=little endian) 0006 04 size of int (bytes) 0007 04 size of size_t (bytes) 0008 04 size of Instruction (bytes) 0009 06 size of OP (bits) 000A 08 size of A (bits) 000B 09 size of B (bits) 000C 09 size of C (bits) 000D 08 size of number (bytes) 000E B6099368E7F57D41 sample number (double) ]]) --------------------------------------------------------------- Test(OKAY, [[ .header signature = "Dude", \ version = 0x57, \ endianness = 0, \ int = 6, size_t = 7, \ instruction = 5, \ SIZE_OP=8, SIZE_A=10, \ SIZE_B=11, SIZE_C = 11, \ number_type = "single" .function return 0 0 .end ]] -- check custom header values ) --DispChunk() ChkChunk(0, "Dude") -- signature ChkChunk(4, "\87") -- version ChkChunk(5, "\0") -- endianness ChkChunk(6, "\6") -- sizeof int ChkChunk(7, "\7") -- sizeof size_t ChkChunk(8, "\5") -- sizeof Instruction ChkChunk(9, "\8") -- sizeof SIZE_OP ChkChunk(10, "\10") -- sizeof SIZE_A ChkChunk(11, "\11") -- sizeof SIZE_B ChkChunk(12, "\11") -- sizeof SIZE_C ChkChunk(13, "\4") -- sizeof lua_Number -- this is big endian if endianness is 0!!! ChkChunk(14, "\75\239\175\59") -- sample number --------------------------------------------------------------- Test(DIES, [[ .header .function Trinity return 0 0 .end ]], "'=' expected in key-value parameter pair" ) -- incomplete key-value pair Test(DIES, [[ .header .function Cypher=99 return 0 0 .end ]], "unrecognized .function property cypher" ) -- invalid key Test(DIES, [[ .header Neo .function return 0 0 .end ]], "top-level function cannot have a label" ) -- invalid function label location Test(DIES, [[ .header Neo: .function return 0 0 .end ]], "top-level function cannot have a label" ) -- invalid function label: location Test(OKAY, [[ .header .func return 0 0 .end ]] ) -- alternate name for directive --------------------------------------------------------------- Test(DIES, [[ .header .function source_name=42 return 0 0 .end ]], "string expected for property source_name" ) -- invalid type for value Test(OKAY, [[ .header .function source_name = "Morpheus" return 0 0 .end ]] ) -- source_name for function --DispChunk() ChkChunk(22, "\9\0\0\0") -- source_name (string) ChkChunk(26, "Morpheus\0") --------------------------------------------------------------- Test(DIES, [[ .header .function line_defined = "Mr Anderson" return 0 0 .end ]], "number expected for property line_defined" ) -- invalid type for value Test(DIES, [[ .header .function line_defined = 0 return 0 0 .end ]], "integer out of range for property line_defined" ) -- invalid range for value Test(OKAY, [[ .header .function line_defined = 1234 return 0 0 .end ]] ) -- line_defined for function --DispChunk() ChkChunk(34, "\210\4\0\0") -- line_defined (integer) --------------------------------------------------------------- Test(DIES, [[ .header .function numparams=foo return 0 0 .end ]], "unknown symbol 'foo' in expression" ) -- invalid type for value Test(DIES, [[ .header .function numparams=65535 return 0 0 .end ]], "integer out of range for property numparams" ) -- invalid range for value Test(OKAY, [[ .header .function numparams=2 return 0 0 .end ]] ) -- numparams for function --DispChunk() ChkChunk(39, "\2") -- number of parameters (1 byte) --------------------------------------------------------------- Test(DIES, [[ .header .function is_vararg=njet return 0 0 .end ]], "unknown symbol 'njet' in expression" ) -- invalid type for value Test(DIES, [[ .header .function is_vararg=2 return 0 0 .end ]], "integer out of range for property is_vararg" ) -- invalid range for value Test(OKAY, [[ .header .function is_vararg=1 return 0 0 .end ]] ) -- is_vararg for function --DispChunk() ChkChunk(40, "\1") -- variable arguments flag (1 byte) --------------------------------------------------------------- Test(DIES, [[ .header .function maxstacksize="ja" return 0 0 .end ]], "number expected for property maxstacksize" ) -- invalid type for value Test(DIES, [[ .header .function maxstacksize=1 return 0 0 .end ]], "integer out of range for property maxstacksize" ) -- invalid range for value Test(OKAY, [[ .header .function maxstacksize=40 return 0 0 .end ]] ) -- maxstacksize for function --DispChunk() ChkChunk(41, "\40") -- maxstacksize (1 byte) --------------------------------------------------------------- Test(OKAY, [[ .function return 0 0 .end ]] ) -- default function header --DispChunk() IntChunk(22, 8) -- source_name (string) ChkChunk(26, "@(test)\0") IntChunk(34, 1) -- line_defined (integer) ChkChunk(39, "\0") -- number of parameters (1 byte) ChkChunk(40, "\0") -- variable arguments flag (1 byte) ChkChunk(41, "\2") -- maxstacksize (1 byte) -- -- these depend on assembled code; cannot be specified in .func ChkChunk(38, "\0") -- number of upvalues (1 byte) IntChunk(42, 1) -- sizelineinfo (int) IntChunk(46, 2) -- line[1] IntChunk(50, 0) -- sizelocvars (int) IntChunk(54, 0) -- sizeupvalues (int) IntChunk(58, 0) -- sizek (int) IntChunk(62, 0) -- sizep (int) IntChunk(66, 1) -- sizecode (int) HexChunk(70, "1B000000") -- [1] return 0 0 -- alternate check by dump from ChunkSpy ChkCode([[ 0016 08000000 string size (8) 001A 4028746573742900 "@(test)" 0022 01000000 line defined (3) 0026 00 nups (0) 0027 00 numparams (0) 0028 00 is_vararg (0) 0029 02 maxstacksize (2) 002A 01000000 sizelineinfo (1) 002E 02000000 [1] (2) 0032 00000000 sizelocvars (0) 0036 00000000 sizeupvalues (0) 003A 00000000 sizek (0) 003E 00000000 sizep (0) 0042 01000000 sizecode (1) 0046 1B000000 [1] return 0 0 ]]) --------------------------------------------------------------- Test(OKAY, [[ .header .function source_name = "Morpheus", \ line_defined = 1234, \ numparams=6, is_vararg=1, \ maxstacksize=47 return 0 0 .end ]] ) -- multiple customized values for a function --DispChunk() IntChunk(22, 9) -- source_name (string) ChkChunk(26, "Morpheus\0") IntChunk(35, 1234) -- line_defined (integer) ChkChunk(40, "\6") -- number of parameters (1 byte) ChkChunk(41, "\1") -- variable arguments flag (1 byte) ChkChunk(42, "\47") -- maxstacksize (1 byte) --------------------------------------------------------------- Test(OKAY, [[ .header .function source_name = "Trinity", \ line_defined = 47, \ numparams = 3, .function source_name = "Neo" \ line_defined = 74 \ numparams = 6 return 1 2 .end return 3 4 .end ]] ) -- basic nested function -- outer function IntChunk(22, 8) -- source name ChkChunk(26, "Trinity\0") IntChunk(34, 47) -- line defined ChkChunk(38, "\0") -- nups ChkChunk(39, "\3") -- numparams ChkChunk(40, "\0") -- is_vararg ChkChunk(41, "\4") -- maxstacksize (due to return 3 4) IntChunk(42, 1) -- sizelineinfo IntChunk(46, 10) -- line[1] IntChunk(50, 0) -- sizelocvars IntChunk(54, 0) -- sizeupvalues IntChunk(58, 0) -- sizek IntChunk(62, 1) -- sizep -- inner function IntChunk(66, 4) -- source name ChkChunk(70, "Neo\0") IntChunk(74, 74) -- line defined ChkChunk(78, "\0") -- nups ChkChunk(79, "\6") -- numparams ChkChunk(80, "\0") -- is_vararg ChkChunk(81, "\2") -- maxstacksize IntChunk(82, 1) -- sizelineinfo IntChunk(86, 8) -- line[1] IntChunk(90, 0) -- sizelocvars IntChunk(94, 0) -- sizeupvalues IntChunk(98, 0) -- sizek IntChunk(102, 0) -- sizep IntChunk(106, 1) -- sizecode HexChunk(110, "1B000101") -- [1] return 1 2 IntChunk(114, 1) -- sizecode HexChunk(118, "1B000203") -- [1] return 3 4 --------------------------------------------------------------- Test(OKAY, [[ .header .function .function .function maxstacksize=42 return 5 6 .end return 0 0 .end return 0 0 .end ]] ) -- 3 levels of nested functions IntChunk("66", 0) -- source name IntChunk("6A", 4) -- line defined ChkChunk("71", "\42") -- maxstacksize IntChunk("72", 1) -- sizelineinfo IntChunk("76", 5) -- line[1] IntChunk("7A", 0) -- sizelocvars IntChunk("7E", 0) -- sizeupvalues IntChunk("82", 0) -- sizek IntChunk("86", 0) -- sizep IntChunk("8A", 1) -- sizecode HexChunk("8E", "1B000305") -- [1] return 5 6 -- alternate check by dump from ChunkSpy ChkCode([[ 0066 00000000 string size (0) 006A 04000000 line defined (4) 006E 00 nups (0) 006F 00 numparams (0) 0070 00 is_vararg (0) 0071 2A maxstacksize (42) 0072 01000000 sizelineinfo (1) 0076 05000000 [1] (5) 007A 00000000 sizelocvars (0) 007E 00000000 sizeupvalues (0) 0082 00000000 sizek (0) 0086 00000000 sizep (0) 008A 01000000 sizecode (1) 008E 1B000305 [1] return 5 6 ]]) --------------------------------------------------------------- Test(OKAY, [[ .header .function FIRST .function source_name="first" return 4 7 .end SECOND: .function source_name="second" return 7 4 .end return 1 3 .end ]] ) -- multiple function definitions with labels --WriteChunk() ChkCode([[ 003E 02000000 sizep (2) ** has two prototypes 0042 ** first prototype 0042 06000000 string size (6) 0046 666972737400 "first" 004C 03000000 line defined (3) 0050 00 nups (0) 0051 00 numparams (0) 0052 00 is_vararg (0) 0053 05 maxstacksize (5) 0054 01000000 sizelineinfo (1) 0058 04000000 [1] (4) 005C 00000000 sizelocvars (0) 0060 00000000 sizeupvalues (0) 0064 00000000 sizek (0) 0068 00000000 sizep (0) 006C 01000000 sizecode (1) 0070 1B800304 [1] return 4 7 0074 ** second prototype 0074 07000000 string size (7) 0078 7365636F6E6400 "second" 007F 07000000 line defined (7) 0083 00 nups (0) 0084 00 numparams (0) 0085 00 is_vararg (0) 0086 08 maxstacksize (8) 0087 01000000 sizelineinfo (1) 008B 08000000 [1] (8) 008F 00000000 sizelocvars (0) 0093 00000000 sizeupvalues (0) 0097 00000000 sizek (0) 009B 00000000 sizep (0) 009F 01000000 sizecode (1) 00A3 1B000207 [1] return 7 4 ]]) --------------------------------------------------------------- Test(DIES, [[ .func .end ]], "a function cannot have no code" ) -- function with no code -- Test(DIES, [[ .func return 0 0 ]], "top-level function not yet closed by a .end" ) -- .function without .end -- Test(DIES, [[ .end ]], ".end directive without matching .function" ) -- .end without .function Test(DIES, [[ .func .func return 0 0 .end .func return 0 0 ]], "top-level function not yet closed by a .end" ) -- too many .function -- Test(DIES, [[ .func return 0 0 .end .end ]], ".end directive without matching .function" ) -- too many .end Test(DIES, [[ .function return 0 0 LABEL .end ]], ".end doesn't accept label prefixes" ) -- .end can't have a label --------------------------------------------------------------- Test(DIES, [[ .param ]], "declaration illegal outside a function" ) -- invalid declaration Test(DIES, [[ .function return 0 0 .end .local ]], "declaration illegal outside a function" ) -- invalid declaration Test(DIES, [[ .function move 0 1 .end .local ]], "last instruction must be RETURN or JMP" ) -- function must end with a RETURN or JMP --------------------------------------------------------------- Test(DIES, [[ .function .param return 0 0 .end ]], "Error: symbol expected in .param directive" ) -- missing symbol for operand Test(DIES, [[ .function .param .param return 0 0 .end ]], "Error: symbol expected in .param directive" ) -- invalid operand Test(DIES, [[ .function .param "FOO" return 0 0 .end ]], "Error: symbol expected in .param directive" ) -- invalid operand Test(DIES, [[ .function .param FOO BAR return 0 0 .end ]], "end of line expected, extra operands in statement" ) -- too many symbols Test(DIES, [[ .function .param FOO .param FOO return 0 0 .end ]], "symbol 'FOO' already defined" ) -- symbol already defined --------------------------------------------------------------- Test(OKAY, [[ .function .param FOO return 0 0 .end ]] ) -- basic parameter declaration assert(Code.root.numparams == 1 and Code.root.sizelocvars == 1 and Code.root.locvars[0] == "FOO" and Code.root.locvars[1] == nil, "numparams handled incorrectly for single .param" ) --WriteChunk() ChkCode([[ 0027 01 numparams (1) 0032 01000000 sizelocvars (1) 0036 04000000 string size (4) 003A 464F4F00 "FOO" 003E 00000000 startpc (0) 0042 01000000 endpc (2) ]]) Test(OKAY, [[ .function return 0 0 .param FOO return 0 0 .end ]] ) -- .param within instructions --WriteChunk() ChkCode([[ 0027 01 numparams (1) 0036 01000000 sizelocvars (1) 003A 04000000 string size (4) 003E 464F4F00 "FOO" 0042 00000000 startpc (0) 0046 02000000 endpc (2) ]]) --------------------------------------------------------------- Test(OKAY, [[ .function .param FOO .param BAR .param BAZ return 0 0 return 0 0 .end ]] ) -- multiple parameter declaration assert(Code.root.numparams == 3 and Code.root.sizelocvars == 3 and Code.root.locvars[0] == "FOO" and Code.root.locvars[1] == "BAR" and Code.root.locvars[2] == "BAZ" and Code.root.locvars[3] == nil, "numparams handled incorrectly for multiple .params" ) --WriteChunk() ChkCode([[ 0027 03 numparams (3) 0036 03000000 sizelocvars (3) 003A 04000000 string size (4) 003E 464F4F00 "FOO" 0042 00000000 startpc (0) 0046 02000000 endpc (2) 004A 04000000 string size (4) 004E 42415200 "BAR" 0052 00000000 startpc (0) 0056 02000000 endpc (2) 005A 04000000 string size (4) 005E 42415A00 "BAZ" 0062 00000000 startpc (0) 0066 02000000 endpc (2) ]]) --------------------------------------------------------------- Test(OKAY, [[ .function .function .param Rocky return 0 0 .end .function .param Sylvester return 0 0 .end .param Stallone return 0 0 .end ]] ) -- .param in nested and multiple function prototypes --WriteChunk() ChkCode([[ 0022 01000000 line defined (1) 0027 01 numparams (1) 0032 01000000 sizelocvars (1) 0036 09000000 string size (9) 003A 5374616C6C6F6E65+ "Stallone" 0042 00 "\0" 0043 00000000 startpc (0) 0047 01000000 endpc (1) 005B 02000000 line defined (2) 0060 01 numparams (1) 006B 01000000 sizelocvars (1) 006F 06000000 string size (6) 0073 526F636B7900 "Rocky" 0079 00000000 startpc (0) 007D 01000000 endpc (1) 0099 06000000 line defined (6) 009E 01 numparams (1) 00A9 01000000 sizelocvars (1) 00AD 0A000000 string size (10) 00B1 53796C7665737465+ "Sylveste" 00B9 7200 "r\0" 00BB 00000000 startpc (0) 00BF 01000000 endpc (1) ]]) --------------------------------------------------------------- Test(DIES, [[ .function .local return 0 0 .end ]], "symbol expected in .local directive" ) -- no operands Test(DIES, [[ .function .local return return 0 0 .end ]], "symbol expected in .local directive" ) -- invalid operand Test(DIES, [[ .function .local FOO, return 0 0 .end ]], "number expected after comma in .local directive" ) -- missing operand Test(DIES, [[ .function .local FOO, BAR return 0 0 .end ]], "unknown symbol 'BAR' in expression" ) -- invalid operand Test(DIES, [[ .function .local FOO, "bar" return 0 0 .end ]], "number expected after comma in .local directive" ) -- invalid operand Test(DIES, [[ .function .local FOO, 74 BAZ return 0 0 .end ]], "end of line expected, extra operands in statement" ) -- extra operand --------------------------------------------------------------- Test(OKAY, [[ .function .local FOO return 0 0 .end ]] ) -- basic local declaration assert(Code.root.numparams == 0 and Code.root.sizelocvars == 1 and Code.root.locvars[0] == "FOO" and Code.root.locvars[1] == nil, "sizelocvars handled incorrectly for single .local" ) --WriteChunk() ChkCode([[ 0027 00 numparams (0) 0032 01000000 sizelocvars (1) 0036 04000000 string size (4) 003A 464F4F00 "FOO" 003E 01000000 startpc (1) 0042 01000000 endpc (1) ]]) Test(OKAY, [[ .function return 0 0 .local FOO return 0 0 .end ]] ) -- test local declaration between instructions --WriteChunk() ChkCode([[ 0027 00 numparams (0) 0036 01000000 sizelocvars (1) 003A 04000000 string size (4) 003E 464F4F00 "FOO" 0042 01000000 startpc (1) 0046 02000000 endpc (2) ]]) Test(OKAY, [[ .function .local FOO .local BAR .local BAZ return 0 0 .end ]] ) -- multiple local declaration --WriteChunk() ChkCode([[ 0032 03000000 sizelocvars (3) 0036 04000000 string size (4) 003A 464F4F00 "FOO" 003E 01000000 startpc (1) 0042 01000000 endpc (1) 0046 04000000 string size (4) 004A 42415200 "BAR" 004E 01000000 startpc (1) 0052 01000000 endpc (1) 0056 04000000 string size (4) 005A 42415A00 "BAZ" 005E 01000000 startpc (1) 0062 01000000 endpc (1) ]]) --------------------------------------------------------------- Test(OKAY, [[ .function .local FOO, 4 return 0 0 .end ]] ) -- local declaration with explicit index assert(Code.root.sizelocvars == 5 and Code.root.locvars[0] == nil and Code.root.locvars[4] == "FOO", ".local with explicit index handled incorrectly" ) --WriteChunk() ChkCode([[ 0032 05000000 sizelocvars (5) 0036 07000000 string size (7) 003A 286E6F6E652900 "(none)" 0041 01000000 startpc (1) 0045 01000000 endpc (1) 0049 07000000 string size (7) 004D 286E6F6E652900 "(none)" 0054 01000000 startpc (1) 0058 01000000 endpc (1) 005C 07000000 string size (7) 0060 286E6F6E652900 "(none)" 0067 01000000 startpc (1) 006B 01000000 endpc (1) 006F 07000000 string size (7) 0073 286E6F6E652900 "(none)" 007A 01000000 startpc (1) 007E 01000000 endpc (1) 0082 04000000 string size (4) 0086 464F4F00 "FOO" 008A 01000000 startpc (1) 008E 01000000 endpc (1) ]]) Test(OKAY, [[ .function .local FOO 2 return 0 0 .end ]] ) -- local declaration with explicit index, no comma assert(Code.root.sizelocvars == 3 and Code.root.locvars[0] == nil and Code.root.locvars[2] == "FOO", ".local with explicit index handled incorrectly" ) --------------------------------------------------------------- Test(DIES, [[ .function .local FOO BAR return 0 0 .end ]], "unknown symbol 'BAR' in expression" ) -- invalid second operand Test(DIES, [[ .function .local FOO 99999 return 0 0 .end ]], "local index out of range" ) -- explicit local index out of range Test(DIES, [[ .function .local FOO 47 .local BAR 47 return 0 0 .end ]], "local with index 47 already defined" ) -- explicit local index duplicated Test(DIES, [[ .function .local FOO .local FOO return 0 0 .end ]], "symbol 'FOO' already defined" ) -- duplicate local name --------------------------------------------------------------- Test(OKAY, [[ .function .function .local Rocky return 0 0 .end .function .local Sylvester return 0 0 .end .local Stallone return 0 0 .end ]] ) -- .local in nested and multiple function prototypes --WriteChunk() ChkCode([[ 0032 01000000 sizelocvars (1) 0036 09000000 string size (9) 003A 5374616C6C6F6E65+ "Stallone" 0042 00 "\0" 0043 01000000 startpc (1) 0047 01000000 endpc (1) 006B 01000000 sizelocvars (1) 006F 06000000 string size (6) 0073 526F636B7900 "Rocky" 0079 01000000 startpc (1) 007D 01000000 endpc (1) 00A9 01000000 sizelocvars (1) 00AD 0A000000 string size (10) 00B1 53796C7665737465+ "Sylveste" 00B9 7200 "r\0" 00BB 01000000 startpc (1) 00BF 01000000 endpc (1) ]]) --------------------------------------------------------------- Test(DIES, [[ .function .upvalue return 0 0 .end ]], "symbol expected in .upvalue directive" ) -- no operands Test(DIES, [[ .function .upvalue "blackadder" return 0 0 .end ]], "symbol expected in .upvalue directive" ) -- invalid operand Test(DIES, [[ .function .upvalue FOO, return 0 0 .end ]], "number expected after comma in .upvalue directive" ) -- missing second operand Test(DIES, [[ .function .upvalue FOO, BAR return 0 0 .end ]], "unknown symbol 'BAR' in expression" ) -- invalid second operand Test(DIES, [[ .function .upvalue FOO, "bar" return 0 0 .end ]], "number expected after comma in .upvalue directive" ) -- invalid second operand Test(DIES, [[ .function .upvalue FOO, 13 BAZ return 0 0 .end ]], "end of line expected, extra operands in statement" ) -- extra operand --------------------------------------------------------------- Test(OKAY, [[ .function .upvalue FOO return 0 0 .end ]] ) -- basic upvalue declaration assert(Code.root.sizeupvalues == 1 and Code.root.upvalues[0] == "FOO" and Code.root.upvalues[1] == nil, "single .upvalue handled incorrectly" ) --WriteChunk() ChkCode([[ 0026 01 nups (1) 0036 01000000 sizeupvalues (1) 003A 04000000 string size (4) 003E 464F4F00 "FOO" ]]) Test(OKAY, [[ .function return 0 0 .upvalue FOO return 0 0 .end ]] ) -- test upvalue declaration between instructions --WriteChunk() ChkCode([[ 0026 01 nups (1) 003A 01000000 sizeupvalues (1) 003E 04000000 string size (4) 0042 464F4F00 "FOO" ]]) Test(OKAY, [[ .function .upvalue FOO .upvalue BAR .upvalue BAZ return 0 0 .end ]] ) -- multiple upvalue declaration --WriteChunk() ChkCode([[ 0026 03 nups (3) 0036 03000000 sizeupvalues (3) 003A 04000000 string size (4) 003E 464F4F00 "FOO" 0042 04000000 string size (4) 0046 42415200 "BAR" 004A 04000000 string size (4) 004E 42415A00 "BAZ" ]]) --------------------------------------------------------------- Test(OKAY, [[ .function .upvalue FOO, 3 return 0 0 .end ]] ) -- upvalue declaration with explicit index --WriteChunk() ChkCode([[ 0026 04 nups (4) 0036 04000000 sizeupvalues (4) 003A 07000000 string size (7) 003E 286E6F6E652900 "(none)" 0045 07000000 string size (7) 0049 286E6F6E652900 "(none)" 0050 07000000 string size (7) 0054 286E6F6E652900 "(none)" 005B 04000000 string size (4) 005F 464F4F00 "FOO" ]]) Test(OKAY, [[ .function .upvalue FOO 2 return 0 0 .end ]] ) -- upvalue declaration with explicit index, no comma --WriteChunk() ChkCode([[ 0026 03 nups (3) 0036 03000000 sizeupvalues (3) 003A 07000000 string size (7) 003E 286E6F6E652900 "(none)" 0045 07000000 string size (7) 0049 286E6F6E652900 "(none)" 0050 04000000 string size (4) 0054 464F4F00 "FOO" ]]) --------------------------------------------------------------- Test(DIES, [[ .function .upvalue FOO BAR return 0 0 .end ]], "unknown symbol 'BAR' in expression" ) -- invalid second operand Test(DIES, [[ .function .upvalue FOO .upvalue FOO return 0 0 .end ]], "symbol 'FOO' already defined" ) -- symbol name duplicated Test(DIES, [[ .function .upvalue FOO 999 return 0 0 .end ]], "upvalue index out of range" ) -- explicit upvalue index out of range Test(DIES, [[ .function .upvalue FOO 13 .upvalue BAR 13 return 0 0 .end ]], "upvalue with index 13 already defined" ) -- explicit upvalue index duplicated --------------------------------------------------------------- Test(OKAY, [[ .function .function .upvalue Rocky return 0 0 .end .function .upvalue Sylvester return 0 0 .end .upvalue Stallone return 0 0 .end ]] ) -- .upvalue in nested and multiple function prototypes --WriteChunk() ChkCode([[ 0026 01 nups (1) 0036 01000000 sizeupvalues (1) 003A 09000000 string size (9) 003E 5374616C6C6F6E65+ "Stallone" 0046 00 "\0" 0057 01 nups (1) 0067 01000000 sizeupvalues (1) 006B 06000000 string size (6) 006F 526F636B7900 "Rocky" 008D 01 nups (1) 009D 01000000 sizeupvalues (1) 00A1 0A000000 string size (10) 00A5 53796C7665737465+ "Sylveste" 00AD 7200 "r\0" ]]) --------------------------------------------------------------- Test(DIES, [[ .function .const return 0 0 .end ]], "number or string expected in .const directive" ) -- no operand Test(DIES, [[ .function .const Move return 0 0 .end ]], "illegal mnemonic 'MOVE' in expression" ) -- invalid operand Test(DIES, [[ .function .const FOO, return 0 0 .end ]], "number or string expected in .const directive" ) -- operand expected after comma Test(DIES, [[ .function .const FOO,BAR return 0 0 .end ]], "unknown symbol 'BAR' in expression" ) -- invalid operand Test(DIES, [[ .function .const FOO BAR return 0 0 .end ]], "unknown symbol 'BAR' in expression" ) -- invalid operand Test(DIES, [[ .function .const FOO 13 BAR return 0 0 .end ]], "unknown symbol 'BAR' in expression" ) -- invalid operand --------------------------------------------------------------- Test(OKAY, [[ .function .const FOO, 47 return 0 0 .end ]] ) -- basic .const definition assert(Code.root.sizek == 1 and Code.root.k[0] == 47 and Code.root.k[1] == nil, "invalid data structure handling .const" ) --WriteChunk() ChkCode([[ 003A 01000000 sizek (1) 003E 03 const type 3 003F 0000000000804740 const [0]: (47) ]]) Test(OKAY, [[ .function .const FOO, #47 return 0 0 .end ]] ) -- .const definition with immediate number assert(Code.root.sizek == 1 and Code.root.k[0] == 47, "invalid data structure handling .const" ) ChkCode([[ 003A 01000000 sizek (1) 003E 03 const type 3 003F 0000000000804740 const [0]: (47) ]]) Test(OKAY, [[ .function .const FOO, "bar" return 0 0 .end ]] ) -- .const definition with immediate string --WriteChunk() assert(Code.root.sizek == 1 and Code.root.k[0] == "bar", "invalid data structure handling .const" ) ChkCode([[ 003A 01000000 sizek (1) 003E 04 const type 4 003F 04000000 string size (4) 0043 62617200 "bar" ]]) Test(OKAY, [[ .function return 0 0 .const FOO, "bar" return 0 0 .end ]] ) -- .const definition between instructions --WriteChunk() assert(Code.root.sizek == 1 and Code.root.k[0] == "bar", "invalid data structure handling .const" ) ChkCode([[ 003E 01000000 sizek (1) 0042 04 const type 4 0043 04000000 string size (4) 0047 62617200 "bar" ]]) --------------------------------------------------------------- Test(OKAY, [[ .function .const FOO #12.34 return 0 0 .end ]] ) -- .const declaration without comma assert(Code.root.sizek == 1 and Code.root.k[0] == 12.34, "invalid data structure handling .const" ) Test(OKAY, [[ .function .const 7.8e-9 return 0 0 .end ]] ) -- .const declaration without named symbol assert(Code.root.sizek == 1 and Code.root.k[0] == 7.8e-9, "invalid data structure handling .const" ) Test(OKAY, [[ .function .const #12345 return 0 0 .end ]] ) -- .const declaration without named symbol assert(Code.root.sizek == 1 and Code.root.k[0] == 12345, "invalid data structure handling .const" ) Test(OKAY, [[ .function .const "Eastwood" return 0 0 .end ]] ) -- .const declaration without named symbol assert(Code.root.sizek == 1 and Code.root.k[0] == "Eastwood", "invalid data structure handling .const" ) Test(OKAY, [[ .function .const 123 .const #456 .const BAR, "Eastwood" return 0 0 .end ]] ) -- multiple .const declarations assert(Code.root.sizek == 3 and Code.root.k[0] == 123 and Code.root.k[1] == 456 and Code.root.k[2] == "Eastwood", "invalid data structure handling .const" ) --WriteChunk() ChkCode([[ 003A 03000000 sizek (3) 003E 03 const type 3 003F 0000000000C05E40 const [0]: (123) 0047 03 const type 3 0048 0000000000807C40 const [1]: (456) 0050 04 const type 4 0051 09000000 string size (9) 0055 45617374776F6F64+ "Eastwood" 005D 00 "\0" ]]) --------------------------------------------------------------- Test(OKAY, [[ .function .const FOO, 789, 2 return 0 0 .end ]] ) -- sample .const declarations ChkCode([[ 003A 03000000 sizek (3) 003E 00 const type 0 003F 00 const type 0 0040 03 const type 3 0041 0000000000A88840 const [2]: (789) ]]) Test(OKAY, [[ .function .const "Bar", 1 return 0 0 .end ]] ) -- sample .const declarations ChkCode([[ 003A 02000000 sizek (2) 003E 00 const type 0 003F 04 const type 4 0040 04000000 string size (4) 0044 42617200 "Bar" ]]) Test(OKAY, [[ .function .const #45e+67 3 return 0 0 .end ]] ) -- sample .const declarations ChkCode([[ 003A 04000000 sizek (4) 003E 00 const type 0 003F 00 const type 0 0040 00 const type 0 0041 03 const type 3 0042 DAF5806701B1304E const [3]: (4.5e+068) ]]) Test(OKAY, [[ .function .const BAR "baz" 2 return 0 0 .end ]] ) -- sample .const declarations ChkCode([[ 003A 03000000 sizek (3) 003E 00 const type 0 003F 00 const type 0 0040 04 const type 4 0041 04000000 string size (4) 0045 62617A00 "baz" ]]) Test(OKAY, [[ .function .const #45e+67 3 .const "Bar", 1 .const BAR "baz" 0 .const FOO, 789, 2 return 0 0 .end ]] ) -- sample .const declarations --WriteChunk() assert(Code.root.sizek == 4 and Code.root.k[0] == "baz" and Code.root.k[1] == "Bar" and Code.root.k[2] == 789 and Code.root.k[3] == 45e+67, "invalid data structure handling .const" ) ChkCode([[ 003A 04000000 sizek (4) 003E 04 const type 4 003F 04000000 string size (4) 0043 62617A00 "baz" 0047 04 const type 4 0048 04000000 string size (4) 004C 42617200 "Bar" 0050 03 const type 3 0051 0000000000A88840 const [2]: (789) 0059 03 const type 3 005A DAF5806701B1304E const [3]: (4.5e+068) ]]) --------------------------------------------------------------- Test(DIES, [[ .function .const true return 0 0 .end ]], "true cannot be used in .const directive" ) -- illegal constant: true Test(DIES, [[ .function .const false return 0 0 .end ]], "false cannot be used in .const directive" ) -- illegal constant: false --------------------------------------------------------------- Test(OKAY, [[ .function .const nil .const FOOBAR, nil return 0 0 .end ]]) -- nil constant declaration --WriteChunk() ChkCode([[ 003A 02000000 sizek (2) 003E 00 const type 0 003F 00 const type 0 ]]) --------------------------------------------------------------- Test(OKAY, [[ .function .const "bar" .const "bar" return 0 0 .end ]] ) -- same constant used ChkCode([[ 003A 02000000 sizek (2) 003E 04 const type 4 003F 04000000 string size (4) 0043 62617200 "bar" 0047 04 const type 4 0048 04000000 string size (4) 004C 62617200 "bar" ]]) Test(DIES, [[ .function .const FOO, #1 .const FOO, #2 return 0 0 .end ]], "symbol 'FOO' already defined" ) -- same symbol used --------------------------------------------------------------- Test(OKAY, [[ .function .function .const #0xDEAD .const #0xBEEF return 0 0 .end .function .const "Sam", 1 .const "Serious", 0 return 0 0 .end .const FirstName, "Thomas" .const LastName, "Anderson" return 0 0 .end ]] ) -- .const in nested and multiple function prototypes --WriteChunk() ChkCode([[ 003A 02000000 sizek (2) 003E 04 const type 4 003F 07000000 string size (7) 0043 54686F6D617300 "Thomas" 004A 04 const type 4 004B 09000000 string size (9) 004F 416E646572736F6E+ "Anderson" 0057 00 "\0" 0078 02000000 sizek (2) 007C 03 const type 3 007D 00000000A0D5EB40 const [0]: (57005) 0085 03 const type 3 0086 00000000E0DDE740 const [1]: (48879) 00B6 02000000 sizek (2) 00BA 04 const type 4 00BB 08000000 string size (8) 00BF 536572696F757300 "Serious" 00C7 04 const type 4 00C8 04000000 string size (4) 00CC 53616D00 "Sam" ]]) --------------------------------------------------------------- Test(DIES, [[ .func .func .local Morpheus .param Neo return 0 0 .end return 0 0 .end ]], "all parameters must be defined before locals" ) -- .param after .local is invalid Test(OKAY, [[ .func .param Neo .local Morpheus return 0 0 .end ]]) -- .param then .local --WriteChunk() ChkCode([[ 0027 01 numparams (1) 0032 02000000 sizelocvars (2) 0036 04000000 string size (4) 003A 4E656F00 "Neo" 003E 00000000 startpc (0) 0042 01000000 endpc (1) 0046 09000000 string size (9) 004A 4D6F727068657573+ "Morpheus" 0052 00 "\0" 0053 01000000 startpc (1) 0057 01000000 endpc (1) ]]) Test(DIES, [[ .function .local FOO .const FOO, #123 return 0 0 .end ]], "symbol 'FOO' already defined" ) -- same symbol used (different type) Test(DIES, [[ .function .const FOO, #123 .upvalue FOO return 0 0 .end ]], "symbol 'FOO' already defined" ) -- same symbol used (different type) --------------------------------------------------------------- -- MOVE R(A) [,] R(B) Test(DIES, [[ .function move .end ]], "R(x) operand expected" ) -- missing operand Test(DIES, [[ .function MOVE 0 .end ]], "R(x) operand expected" ) -- missing operand Test(DIES, [[ .function Move 0, .end ]], "R(x) operand expected" ) -- missing operand Test(DIES, [[ .function move R0 .end ]], "R(x) operand expected" ) -- missing operand Test(DIES, [[ .function move $0 .end ]], "R(x) operand expected" ) -- missing operand Test(DIES, [[ .function move #123 .end ]], "R(x) operand expected" ) -- invalid operand Test(DIES, [[ .function move "foo" .end ]], "R(x) operand expected" ) -- invalid operand Test(DIES, [[ .function move 666 0 .end ]], "R-type register operand out of range" ) -- operand out of range Test(DIES, [[ .function move 0 FOO .end ]], "unknown symbol 'FOO' in expression" ) -- unknown symbol Test(DIES, [[ .function move 0 R1 $2 .end ]], "end of line expected, extra operands in statement" ) -- extra operand Test(DIES, [[ .function .const BAZ, #888 move 1 #BAZ .end ]], "R(x) operand expected" ) -- invalid operand --------------------------------------------------------------- Test(OKAY, [[ .function MOVE 0 1 return 0 0 .end ]]) -- basic move instruction ChkCode([[ 0029 02 maxstacksize (2) 002A 02000000 sizelineinfo (2) 002E 02000000 [1] (2) 0032 03000000 [2] (3) 0046 02000000 sizecode (2) 004A 00800000 [1] move 0 1 004E 1B000000 [2] return 0 0 ]]) Test(OKAY, [[ .function move 0 0 return 0 0 .end ]], "", "MOVE to same register; does nothing" ) -- warning: do-nothing move Test(OKAY, [[ .function move 1 2 return 0 0 .end ]]) -- move instruction ChkCode([[ 0029 03 maxstacksize (3) 002A 02000000 sizelineinfo (2) 002E 02000000 [1] (2) 0046 02000000 sizecode (2) 004A 00000101 [1] move 1 2 ]]) Test(OKAY, [[ .function move R3 R4 move R3 4 move $5, $6 move $7, R8 return 0 0 .end ]]) -- multiple move instructions ChkCode([[ 0029 09 maxstacksize (9) 002A 05000000 sizelineinfo (5) 002E 02000000 [1] (2) 0032 03000000 [2] (3) 0036 04000000 [3] (4) 003A 05000000 [4] (5) 0052 05000000 sizecode (5) 0056 00000203 [1] move 3 4 005A 00000203 [2] move 3 4 005E 00000305 [3] move 5 6 0062 00000407 [4] move 7 8 ]]) --------------------------------------------------------------- Test(OKAY, [[ .function .local FOO, 1 move 0, FOO return 0 0 .end ]]) -- move instruction referencing a .local symbol ChkCode([[ 0029 02 maxstacksize (2) 002A 02000000 sizelineinfo (2) 002E 03000000 [1] (3) 0036 02000000 sizelocvars (2) 003A 07000000 string size (7) 003E 286E6F6E652900 "(none)" 0045 01000000 startpc (1) 0049 02000000 endpc (2) 004D 04000000 string size (4) 0051 464F4F00 "FOO" 0055 01000000 startpc (1) 0059 02000000 endpc (2) 0069 02000000 sizecode (2) 006D 00800000 [1] move 0 1 ]]) Test(OKAY, [[ .function .const BAZ, #888 move 1 BAZ return 0 0 .end ]], "", "potentially dangerous symbol mixing in expression" ) -- constant index used in an R(x) expression Test(OKAY, [[ .function .const BAZ, #888 move 1 (BAZ) return 0 0 .end ]]) -- constant index used in an R(x) expression ChkCode([[ 0029 02 maxstacksize (2) 002A 02000000 sizelineinfo (2) 002E 03000000 [1] (3) 0032 04000000 [2] (4) 003E 01000000 sizek (1) 0042 03 const type 3 0043 0000000000C08B40 const [0]: (888) 004F 02000000 sizecode (2) 0053 00000001 [1] move 1 0 0057 1B000000 [2] return 0 0 ]]) Test(OKAY, [[ .function .local FOO .local BAR move FOO, BAR return 0 0 .end ]]) -- test multiple local references with move instruction ChkCode([[ 0029 02 maxstacksize (2) 002A 02000000 sizelineinfo (2) 002E 04000000 [1] (4) 0036 02000000 sizelocvars (2) 003A 04000000 string size (4) 003E 464F4F00 "FOO" 0042 01000000 startpc (1) 0046 02000000 endpc (2) 004A 04000000 string size (4) 004E 42415200 "BAR" 0052 01000000 startpc (1) 0056 02000000 endpc (2) 0066 02000000 sizecode (2) 006A 00800000 [1] move 0 1 ]]) Test(OKAY, [[ .function Slot1 .local FOO Slot2: .local BAR move Slot2, Slot1 return 0 0 .end ]]) -- test move instruction with label references ChkCode([[ 0029 02 maxstacksize (2) 002A 02000000 sizelineinfo (2) 002E 04000000 [1] (4) 0036 02000000 sizelocvars (2) 003A 04000000 string size (4) 003E 464F4F00 "FOO" 0042 01000000 startpc (1) 0046 02000000 endpc (2) 004A 04000000 string size (4) 004E 42415200 "BAR" 0052 01000000 startpc (1) 0056 02000000 endpc (2) 0066 02000000 sizecode (2) 006A 00000001 [1] move 1 0 ]]) --------------------------------------------------------------- -- LOADK R(A) [,] Kst(Bx) Test(DIES, [[ .function loadk $1 .end ]], "Kst(x) operand expected" ) -- missing operand Test(DIES, [[ .function loadk 0, .end ]], "Kst(x) operand expected" ) -- missing operand Test(DIES, [[ .function loadk 1,1.1 .end ]], "integer Kst(x) constant operand expected" ) -- illegal constant number Test(DIES, [[ .function loadk R1, R2 .end ]], "constant number 2 not yet defined", "potentially dangerous symbol mixing in expression" ) -- incorrect operand type Test(DIES, [[ .function loadk R1, (R2) .end ]], "constant number 2 not yet defined" ) -- incorrect operand type without warning Test(DIES, [[ .function loadk 1, FOO .end ]], "unknown symbol 'FOO' in expression" ) -- unknown label Test(DIES, [[ .function loadk 1, 0 .end ]], "constant number 0 not yet defined" ) -- undefined constant Test(DIES, [[ .function loadk 1 2 3 .end ]], "constant number 2 not yet defined" ) -- undefined constant Test(DIES, [[ .function loadk 1, false .end ]], "'false' cannot be used as a Kst(x) operand" ) -- illegal keyword Test(DIES, [[ .function .local FOO loadk 1, FOO .end ]], "constant number 0 not yet defined", "potentially dangerous symbol mixing in expression" ) -- different symbol type --------------------------------------------------------------- Test(OKAY, [[ .function .const "foobar" loadk $1, 0 return 0 0 .end ]]) -- basic loadk instruction ChkCode([[ 003E 01000000 sizek (1) 0042 04 const type 4 0043 07000000 string size (7) 0047 666F6F62617200 "foobar" 0056 01000001 [1] loadk 1 0 ; "foobar" ]]) Test(OKAY, [[ .function .const FOO, #5678 loadk R1, FOO return 0 0 .end ]]) -- loadk using constant index ChkCode([[ 003E 01000000 sizek (1) 0042 03 const type 3 0043 00000000002EB640 const [0]: (5678) 0053 01000001 [1] loadk 1 0 ; 5678 ]]) Test(OKAY, [[ .function .const FOO, #5678 loadk R1, #FOO return 0 0 .end ]]) -- loadk using constant value ChkCode([[ 003E 01000000 sizek (1) 0042 03 const type 3 0043 00000000002EB640 const [0]: (5678) 0053 01000001 [1] loadk 1 0 ; 5678 ]]) Test(OKAY, [[ .function .const FOO nil 1 loadk R2, FOO loadk 3 nil return 0 0 .end ]]) -- more loadk usage (nils) ChkCode([[ 0042 02000000 sizek (2) 0046 00 const type 0 0047 00 const type 0 0050 41000002 [1] loadk 2 1 ; nil 0054 41000003 [2] loadk 3 1 ; nil ]]) Test(OKAY, [[ .function FOO: .const BAR, #1234 loadk R1, FOO loadk $0, #1234 loadk 2, "foobar" return 0 0 .end ]]) -- various loadk usage --WriteChunk() ChkCode([[ 0046 02000000 sizek (2) 004A 03 const type 3 004B 0000000000489340 const [0]: (1234) 0053 04 const type 4 0054 07000000 string size (7) 0058 666F6F62617200 "foobar" 0067 01000001 [1] loadk 1 0 ; 1234 006B 01000000 [2] loadk 0 0 ; 1234 006F 41000002 [3] loadk 2 1 ; "foobar" ]]) --------------------------------------------------------------- -- LOADBOOL R(A) [,] B(0|1|true|false) [,] C(0|1) Test(DIES, [[ .function loadbool 0 .end ]], "missing operand(s)" ) -- missing operand Test(DIES, [[ .function loadbool R0 1 .end ]], "missing operand(s)" ) -- missing operand Test(DIES, [[ .function loadbool 1 true .end ]], "missing operand(s)" ) -- missing operand Test(DIES, [[ .function loadbool 2 dunno .end ]], "unknown symbol 'dunno' in expression" ) -- illegal operand Test(DIES, [[ .function loadbool 2 false 2 .end ]], "operand C must be 0 or 1" ) -- illegal operand Test(DIES, [[ .function loadbool 1,1,1 return 0 0 .end ]], "jump out of bounds (to 3)" ) -- jump out of bounds --------------------------------------------------------------- Test(OKAY, [[ .function loadbool R0, 0, 0 loadbool $1, 1, 1 loadbool 2 true 0 loadbool 3 false, 1 return 0 0 return 0 0 .end ]]) -- basic loadbool instructions ChkCode([[ 0029 04 maxstacksize (4) 0056 06000000 sizecode (6) 005A 02000000 [1] loadbool 0 0 0 ; false 005E 42800001 [2] loadbool 1 1 1 ; true, to [4] 0062 02800002 [3] loadbool 2 1 0 ; true 0066 42000003 [4] loadbool 3 0 1 ; false, to [6] ]]) Test(OKAY, [[ .function .local FOO loadbool FOO, true, 0 return 0 0 .end ]]) -- loadbool with symbolic reference --WriteChunk() ChkCode([[ 0036 01000000 sizelocvars (1) 003A 04000000 string size (4) 003E 464F4F00 "FOO" 0042 01000000 startpc (1) 0046 02000000 endpc (2) 0056 02000000 sizecode (2) 005A 02800000 [1] loadbool 0 1 0 ; true ]]) --------------------------------------------------------------- -- LOADNIL R(A) [(,|...)] R(B) Test(DIES, [[ .function loadnil R0 .end ]], "R(x) operand expected" ) -- missing operand Test(DIES, [[ .function loadnil $1, .end ]], "R(x) operand expected" ) -- missing operand Test(DIES, [[ .function loadnil R2, $1 .end ]], "operand A must not exceed operand B" ) -- illegal operand Test(DIES, [[ .function loadnil 1 2 3 .end ]], "end of line expected, extra operands in statement" ) -- extra operand --------------------------------------------------------------- Test(OKAY, [[ .function loadnil 0,2 loadnil R1...R3 loadnil 2 $4 return 0 0 .end ]]) -- basic loadnil instruction ChkCode([[ 0029 05 maxstacksize (5) 004E 04000000 sizecode (4) 0052 03000100 [1] loadnil 0 2 0056 03800101 [2] loadnil 1 3 005A 03000202 [3] loadnil 2 4 ]]) Test(OKAY, [[ .function .local FOO .local BAR loadnil FOO...BAR return 0 0 .end ]]) -- loadnil with symbols --WriteChunk() ChkCode([[ 0036 02000000 sizelocvars (2) 003A 04000000 string size (4) 003E 464F4F00 "FOO" 0042 01000000 startpc (1) 0046 02000000 endpc (2) 004A 04000000 string size (4) 004E 42415200 "BAR" 0052 01000000 startpc (1) 0056 02000000 endpc (2) 0066 02000000 sizecode (2) 006A 03800000 [1] loadnil 0 1 ]]) --------------------------------------------------------------- -- GETUPVAL R(A) [,] Upvalue[B] -- SETUPVAL R(A) [,] Upvalue[B] Test(DIES, [[ .function getupval R1 .end ]], "missing operand(s)" ) -- missing operand Test(DIES, [[ .function getupval R1 R2 .end ]], "undefined upvalue number 2", "potentially dangerous symbol mixing in expression" ) -- invalid operand Test(DIES, [[ .function getupval R1 (R2) .end ]], "undefined upvalue number 2" ) -- invalid operand without warning Test(DIES, [[ .function setupval 1 2 3 .end ]], "undefined upvalue number 2" ) -- extra operand Test(DIES, [[ .function setupval 0, 1 .end ]], "undefined upvalue number 1" ) -- undefined upvalue Test(DIES, [[ .function .local BAR setupval 0, BAR .end ]], "undefined upvalue number 0", "potentially dangerous symbol mixing in expression" ) -- different symboltype Test(DIES, [[ .function .local BAR setupval 0, (BAR) .end ]], "undefined upvalue number 0" ) -- without warning --------------------------------------------------------------- Test(OKAY, [[ .function .upvalue FOO 1 .upvalue BAR getupval $1, 0 setupval R2 FOO return 0 0 .end ]]) -- basic getupval and setupval instructions ChkCode([[ 0026 02 nups (2) 003E 02000000 sizeupvalues (2) 0042 04000000 string size (4) 0046 42415200 "BAR" 004A 04000000 string size (4) 004E 464F4F00 "FOO" 005E 04000001 [1] getupval 1 0 ; BAR 0062 08800002 [2] setupval 2 1 ; FOO ]]) Test(OKAY, [[ .function .upvalue FOO .upvalue BAR getupval R0, 1 return 0 0 .end ]]) -- refer to upvalue by number ChkCode([[ 0026 02 nups (2) 003A 02000000 sizeupvalues (2) 003E 04000000 string size (4) 0042 464F4F00 "FOO" 0046 04000000 string size (4) 004A 42415200 "BAR" 005A 04800000 [1] getupval 0 1 ; BAR ]]) --------------------------------------------------------------- -- GETGLOBAL R(A) [,] Kst(Bx) -- SETGLOBAL R(A) [,] Kst(Bx) Test(DIES, [[ .function getglobal $1 .end ]], "Kst(x) operand expected" ) -- missing operand Test(DIES, [[ .function setglobal 0, .end ]], "Kst(x) operand expected" ) -- missing operand Test(DIES, [[ .function getglobal 0, BAR .end ]], "unknown symbol 'BAR' in expression" ) -- undefined symbol Test(DIES, [[ .function setglobal 0, 1048575 .end ]], "constant number 1048575 not yet defined" ) -- constant number out of range --------------------------------------------------------------- Test(OKAY, [[ .function .const FOO "bar" getglobal R0, FOO return 0 0 .end ]]) -- basic get/setglobal instruction ChkCode([[ 003E 01000000 sizek (1) 0042 04 const type 4 0043 04000000 string size (4) 0047 62617200 "bar" 0053 05000000 [1] getglobal 0 0 ; bar ]]) Test(OKAY, [[ .function FOO .const "foo" setglobal $1, FOO return 0 0 .end ]]) -- more get/setglobal instruction ChkCode([[ 003E 01000000 sizek (1) 0042 04 const type 4 0043 04000000 string size (4) 0047 666F6F00 "foo" 0053 07000001 [1] setglobal 1 0 ; foo ]]) Test(OKAY, [[ .function .const "foo" getglobal 1, 0 setglobal 2, "bar" getglobal 3 #0xBEEF return 0 0 .end ]]) -- more get/setglobal instruction --WriteChunk() ChkCode([[ 0046 03000000 sizek (3) 004A 04 const type 4 004B 04000000 string size (4) 004F 666F6F00 "foo" 0053 04 const type 4 0054 04000000 string size (4) 0058 62617200 "bar" 005C 03 const type 3 005D 00000000E0DDE740 const [2]: (48879) 006D 05000001 [1] getglobal 1 0 ; foo 0071 47000002 [2] setglobal 2 1 ; bar 0075 85000003 [3] getglobal 3 2 ; 48879 ]]) --------------------------------------------------------------- -- UNM R(A) [,] R(B) -- NOT R(A) [,] R(B) Test(DIES, [[ .function unm $1 .end ]], "R(x) operand expected" ) -- missing operand Test(DIES, [[ .function not 1, .end ]], "R(x) operand expected" ) -- missing operand Test(DIES, [[ .function unm 1 2 3 .end ]], "end of line expected, extra operands in statement" ) -- extra operand Test(DIES, [[ unm 0 1 ]], "instruction cannot live outside a function" ) -- instruction outside of a function --------------------------------------------------------------- Test(OKAY, [[ .function unm 1, $2 not R3 R4 return 0 0 .end ]]) -- basic unm and not instructions ChkCode([[ 004E 11000101 [1] unm 1 2 0052 12000203 [2] not 3 4 ]]) Test(OKAY, [[ .function .local FOO .local BAR unm FOO, BAR not BAR FOO return 0 0 .end ]]) -- more unm and not instructions --WriteChunk() ChkCode([[ 003A 02000000 sizelocvars (2) 003E 04000000 string size (4) 0042 464F4F00 "FOO" 0046 01000000 startpc (1) 004A 03000000 endpc (3) 004E 04000000 string size (4) 0052 42415200 "BAR" 0056 01000000 startpc (1) 005A 03000000 endpc (3) 006E 11800000 [1] unm 0 1 0072 12000001 [2] not 1 0 ]]) --------------------------------------------------------------- -- ADD R(A) [,] RK(B) [,] RK(C) -- SUB R(A) [,] RK(B) [,] RK(C) -- MUL R(A) [,] RK(B) [,] RK(C) -- DIV R(A) [,] RK(B) [,] RK(C) -- POW R(A) [,] RK(B) [,] RK(C) Test(DIES, [[ .func add 0 .end ]], "RK(x) operand expected" ) -- missing operand Test(DIES, [[ .func add $0, .end ]], "RK(x) operand expected" ) -- missing operand Test(DIES, [[ .func add R1, R2 .end ]], "RK(x) operand expected" ) -- missing operand Test(DIES, [[ .func add $1 $2 .end ]], "RK(x) operand expected" ) -- missing operand Test(DIES, [[ .func add 0 #1234 .end ]], "RK(x) operand expected" ) -- missing operand Test(DIES, [[ .func add 1,2, .end ]], "RK(x) operand expected" ) -- missing operand Test(DIES, [[ .func add 1 2 3 4 .end ]], "end of line expected, extra operands in statement" ) -- extra operand Test(DIES, [[ .func add 0 666 .end ]], "constant number 416 not yet defined" ) -- operand out of range Test(DIES, [[ .func add 0 FOO BAR .end ]], "unknown symbol 'FOO' in expression" ) -- undefined symbols Test(DIES, [[ .func add 0 250 251 .end ]], "constant number 0 not yet defined" ) -- undefined constants Test(DIES, [[ .function add 0 1 false .end ]], "'false' cannot be used as an RK(x) operand" ) -- illegal keyword --------------------------------------------------------------- Test(OKAY, [[ .func add 0,0,0 return 0 0 .end ]]) ChkCode([[ 004A 0C000000 [1] add 0 0 0 ]]) -- basic add instruction Test(OKAY, [[ .func add $0,R1,2 add 2 $1 0 return 0 0 .end ]]) -- more add variations ChkCode([[ 004E 8C800000 [1] add 0 1 2 0052 0C800002 [2] add 2 1 0 ]]) Test(OKAY, [[ .func add 0, R1, #123 add 1, #123, "foo" return 0 0 .end ]]) -- more add variations ChkCode([[ 0042 02000000 sizek (2) 0046 03 const type 3 0047 0000000000C05E40 const [0]: (123) 004F 04 const type 4 0050 04000000 string size (4) 0054 666F6F00 "foo" 0060 8CBE0000 [1] add 0 1 250 ; 123 0064 CC3E7D01 [2] add 1 250 251 ; 123 "foo" ]]) Test(OKAY, [[ .func add 0, #123, nil add 1, nil, "foo" return 0 0 .end ]]) -- nil variations ChkCode([[ 0042 03000000 sizek (3) 0046 03 const type 3 0047 0000000000C05E40 const [0]: (123) 004F 00 const type 0 0050 04 const type 4 0051 04000000 string size (4) 0055 666F6F00 "foo" 0061 CC3E7D00 [1] add 0 250 251 ; 123 nil 0065 0CBF7D01 [2] add 1 251 252 ; nil "foo" ]]) --------------------------------------------------------------- Test(OKAY, [[ .func FOO .local BAR, 2 add FOO, 0, BAR return 0 0 .end ]]) -- more add variations ChkCode([[ 0036 03000000 sizelocvars (3) 003A 07000000 string size (7) 003E 286E6F6E652900 "(none)" 004D 07000000 string size (7) 0051 286E6F6E652900 "(none)" 0060 04000000 string size (4) 0064 42415200 "BAR" 0080 8C000002 [1] add 2 0 2 ]]) Test(OKAY, [[ .func FOO .const BAR, #0xBEEF, 1 add 0, FOO, BAR return 0 0 .end ]]) -- more add variations ChkCode([[ 003E 02000000 sizek (2) 0042 00 const type 0 0043 03 const type 3 0044 00000000E0DDE740 const [1]: (48879) 0054 CCBE7D00 [1] add 0 251 251 ; 48879 48879 ]]) Test(OKAY, [[ .func .local FOO, 4 .const BAR, #42, 1 sub R1, #123, #456 mul $2, "foo", "bar" div 3, FOO, 250 pow 0, R1, BAR return 0 0 .end ]]) -- other similar arithmetic instructions --WriteChunk() ChkCode([[ 0029 05 maxstacksize (5) 0042 05000000 sizelocvars (5) 0046 07000000 string size (7) 004A 286E6F6E652900 "(none)" 0059 07000000 string size (7) 005D 286E6F6E652900 "(none)" 006C 07000000 string size (7) 0070 286E6F6E652900 "(none)" 007F 07000000 string size (7) 0083 286E6F6E652900 "(none)" 0092 04000000 string size (4) 0096 464F4F00 "FOO" 009A 01000000 startpc (1) 009E 05000000 endpc (5) 00A6 05000000 sizek (5) 00AA 03 const type 3 00AB 0000000000C05E40 const [0]: (123) 00B3 03 const type 3 00B4 0000000000004540 const [1]: (42) 00BC 03 const type 3 00BD 0000000000807C40 const [2]: (456) 00C5 04 const type 4 00C6 04000000 string size (4) 00CA 666F6F00 "foo" 00CE 04 const type 4 00CF 04000000 string size (4) 00D3 62617200 "bar" 00DF 0D3F7D01 [1] sub 1 250 252 ; 123 456 00E3 8EBF7E02 [2] mul 2 253 254 ; "foo" "bar" 00E7 8F3E0203 [3] div 3 4 250 ; 123 00EB D0BE0000 [4] pow 0 1 251 ; 42 ]]) Test(OKAY, [[ .func .const FOO, #666 add FOO, 1, 2 return 0 0 .end ]], "", "potentially dangerous symbol mixing in expression" ) -- dangerous symbol for operand 1 Test(OKAY, [[ .function .upvalue BAZ add 0 1 BAZ return 0 0 .end ]], "", "potentially dangerous symbol mixing in expression" ) -- different symbol type --------------------------------------------------------------- -- GETTABLE R(A) [,] R(B) [,] RK(C) -- GETTABLE R(A) [,] R(B) "[" RK(C) "]" Test(DIES, [[ .function gettable 1 0 .end ]], "RK(x) operand expected" ) -- missing operand Test(DIES, [[ .function gettable 1 2 3 4 .end ]], "end of line expected, extra operands in statement" ) -- extra operand Test(DIES, [[ .function gettable 1,2,3] .end ]], "end of line expected, extra operands in statement" ) -- extra ']' character, missing opening bracket Test(DIES, [[ .function gettable 1,2[3 .end ]], "closing square bracket expected" ) -- missing closing bracket Test(DIES, [[ .function gettable 1,2,666 .end ]], "constant number 416 not yet defined" ) -- operand out of range --------------------------------------------------------------- Test(OKAY, [[ .function gettable 1 0 2 return 0 0 .end ]]) -- basic gettable function ChkCode([[ 004A 86000001 [1] gettable 1 0 2 ]]) Test(OKAY, [[ .function .const larry, #0 gettable 1, R2, 250 gettable $2 3 #0xBEEF gettable R4 R5 "foobar" gettable R0, $1[251] gettable 1 2[3] gettable $3, 2["trinity"] return 0 0 .end ]]) -- some gettable variations --WriteChunk() ChkCode([[ 0029 06 maxstacksize (6) 0052 04000000 sizek (4) 0056 03 const type 3 0057 0000000000000000 const [0]: (0) 005F 03 const type 3 0060 00000000E0DDE740 const [1]: (48879) 0068 04 const type 4 0069 07000000 string size (7) 006D 666F6F62617200 "foobar" 0074 04 const type 4 0075 08000000 string size (8) 0079 7472696E69747900 "trinity" 0089 863E0101 [1] gettable 1 2 250 ; 0 008D C6BE0102 [2] gettable 2 3 251 ; 48879 0091 06BF0204 [3] gettable 4 5 252 ; "foobar" 0095 C6BE0000 [4] gettable 0 1 251 ; 48879 0099 C6000101 [5] gettable 1 2 3 009D 463F0103 [6] gettable 3 2 253 ; "trinity" ]]) --------------------------------------------------------------- -- SETTABLE R(A) [,] RK(B) [,] RK(C) -- SETTABLE R(A) "[" RK(B) "]" [,] RK(C) Test(DIES, [[ .function settable 1 0 .end ]], "RK(x) operand expected" ) -- missing operand Test(DIES, [[ .function settable 1 2 3 4 .end ]], "end of line expected, extra operands in statement" ) -- extra operand Test(DIES, [[ .function settable 1[2,3 .end ]], "closing square bracket expected" ) -- missing closing bracket Test(DIES, [[ .function settable 1,2],3 .end ]], "illegal symbol ']' in expression" ) -- missing opening bracket Test(DIES, [[ .function settable 1,666,2 .end ]], "constant number 416 not yet defined" ) -- operand out of range --------------------------------------------------------------- Test(OKAY, [[ .function settable 1 2 3 return 0 0 .end ]]) -- basic settable instruction ChkCode([[ 004A C9000101 [1] settable 1 2 3 ]]) Test(OKAY, [[ .function .const #0 settable R2, 250, $1 settable $1 #0xDEAD, 3 settable R4 "foobar" R5 settable R4 R2 "foobar" settable $1[251], R0 settable 2["trinity"] $3 return 0 0 .end ]]) -- more settable variations --WriteChunk() ChkCode([[ 0029 06 maxstacksize (6) 0052 04000000 sizek (4) 0056 03 const type 3 0057 0000000000000000 const [0]: (0) 005F 03 const type 3 0060 00000000A0D5EB40 const [1]: (57005) 0068 04 const type 4 0069 07000000 string size (7) 006D 666F6F62617200 "foobar" 0074 04 const type 4 0075 08000000 string size (8) 0079 7472696E69747900 "trinity" 0089 49007D02 [1] settable 2 250 1 ; 0 008D C9807D01 [2] settable 1 251 3 ; 57005 0091 49017E04 [3] settable 4 252 5 ; "foobar" 0095 093F0104 [4] settable 4 2 252 ; "foobar" 0099 09807D01 [5] settable 1 251 0 ; 57005 009D C9807E02 [6] settable 2 253 3 ; "trinity" ]]) --------------------------------------------------------------- -- NEWTABLE R(A) [,] (num|imm) [,] (num|imm) Test(DIES, [[ .function newtable R0 .end ]], "integer operand expected" ) -- missing operand Test(DIES, [[ .function newtable $1, FOO BAR .end ]], "unknown symbol 'FOO' in expressio" ) -- illegal operand Test(DIES, [[ .function newtable 1 2 3 4 .end ]], "end of line expected, extra operands in statement" ) -- extra operand Test(DIES, [[ .function newtable 1 0 #-1 return 0 0 .end ]], "positive integer operand expected" ) -- illegal hash size Test(DIES, [[ .function newtable 1 0 33 return 0 0 .end ]], "encoded table hash size out of range" ) -- illegal hash size Test(DIES, [[ .function newtable 1 -DUDE 0 return 0 0 .end ]], "unknown symbol 'DUDE' in expression" ) -- illegal array size Test(DIES, [[ .function newtable 1, -1, 0 return 0 0 .end ]], "positive integer operand expected" ) -- illegal array size Test(DIES, [[ .function newtable 1 1000 0 return 0 0 .end ]], "encoded table array size out of range" ) -- illegal array size --------------------------------------------------------------- Test(OKAY, [[ .function newtable 2 0 0 return 0 0 .end ]]) -- basic newtable instruction ChkCode([[ 004A 0A000002 [1] newtable 2 0 0 ; array=0, hash=0 ]]) Test(OKAY, [[ .function newtable R1, 10, 4 newtable 1 #200 #200 return 0 0 .end ]]) -- other newtable instructions --WriteChunk() ChkCode([[ 004E 0A010501 [1] newtable 1 10 4 ; array=4, hash=16 0052 0A821701 [2] newtable 1 47 8 ; array=224, hash=256 ]]) --------------------------------------------------------------- -- SELF R(A) [,] R(B) [,] RK(C) -- SELF R(A) [,] R(B) "[" RK(C) "]" Test(DIES, [[ .function self 0 .end ]], "R(x) operand expected" ) -- missing operand Test(DIES, [[ .function self 1, #2 .end ]], "R(x) operand expected" ) -- missing operand Test(DIES, [[ .function self 1 2 3 4 .end ]], "end of line expected, extra operands in statement" ) -- extra operand Test(DIES, [[ .function self 1 666 .end ]], "R-type register operand out of range" ) -- operand out of range --------------------------------------------------------------- Test(OKAY, [[ .function self R1 R2 R3 self 1,2,3 self $2 3[R4] return 0 0 .end ]]) -- basic self instructions ChkCode([[ 0029 05 maxstacksize (5) 0052 CB000101 [1] self 1 2 3 0056 CB000101 [2] self 1 2 3 005A 0B810102 [3] self 2 3 4 ]]) Test(OKAY, [[ .function .const FOO, #666 .local BAR, 3 self R1 R0[FOO] self R2 R1,250 self $1 BAR[BAR] self BAR $1[#0xBEEF] self BAR $1["morpheus"] return 0 0 .end ]]) -- some self instruction variations --WriteChunk() ChkCode([[ 0029 04 maxstacksize (4) 0046 04000000 sizelocvars (4) 004A 07000000 string size (7) 004E 286E6F6E652900 "(none)" 005D 07000000 string size (7) 0061 286E6F6E652900 "(none)" 0070 07000000 string size (7) 0074 286E6F6E652900 "(none)" 0083 04000000 string size (4) 0087 42415200 "BAR" 008B 01000000 startpc (1) 008F 06000000 endpc (6) 0097 03000000 sizek (3) 009B 03 const type 3 009C 0000000000D08440 const [0]: (666) 00A4 03 const type 3 00A5 00000000E0DDE740 const [1]: (48879) 00AD 04 const type 4 00AE 09000000 string size (9) 00B2 6D6F727068657573+ "morpheus" 00BA 00 "\0" 00C3 8B3E0001 [1] self 1 0 250 ; 666 00C7 8BBE0002 [2] self 2 1 250 ; 666 00CB CB800101 [3] self 1 3 3 00CF CBBE0003 [4] self 3 1 251 ; 48879 00D3 0BBF0003 [5] self 3 1 252 ; "morpheus" ]]) --------------------------------------------------------------- -- CONCAT R(A) [,] R(B) [(,|...)] R(C) Test(DIES, [[ .function concat 0 .end ]], "R(x) operand expected" ) -- missing operand Test(DIES, [[ .function concat 1,2... .end ]], "R(x) operand expected" ) -- missing operand Test(DIES, [[ .function concat 1 2 3 4 .end ]], "end of line expected, extra operands in statement" ) -- extra operand Test(DIES, [[ .function concat 666 .end ]], "R-type register operand out of range" ) -- operand out of range Test(DIES, [[ .function concat 1 3 2 .end ]], "operand B must not exceed operand C" ) -- operand B cannot exceed C --------------------------------------------------------------- Test(OKAY, [[ .function concat $0 $1 $3 concat 0,1,1 concat R1 R2...R4 .local FOO 1 .local BAR 3 concat 0, FOO...BAR return 0 0 .end ]]) -- some concat instruction variations --WriteChunk() ChkCode([[ 0029 05 maxstacksize (5) 0042 04000000 sizelocvars (4) 0046 07000000 string size (7) 004A 286E6F6E652900 "(none)" 0059 04000000 string size (4) 005D 464F4F00 "FOO" 0061 01000000 startpc (1) 0065 05000000 endpc (5) 0069 07000000 string size (7) 006D 286E6F6E652900 "(none)" 007C 04000000 string size (4) 0080 42415200 "BAR" 0084 01000000 startpc (1) 0088 05000000 endpc (5) 009C D3800000 [1] concat 0 1 3 00A0 53800000 [2] concat 0 1 1 00A4 13010101 [3] concat 1 2 4 00A8 D3800000 [4] concat 0 1 3 ]]) --------------------------------------------------------------- -- EQ (0|1) [,] RK(B) [,] RK(C) -- LT (0|1) [,] RK(B) [,] RK(C) -- LE (0|1) [,] RK(B) [,] RK(C) Test(DIES, [[ .function eq true .end ]], "operand A must be 0 or 1" ) -- invalid operand A Test(DIES, [[ .function eq 2 .end ]], "operand A must be 0 or 1" ) -- invalid operand A Test(DIES, [[ .function eq 0 1 .end ]], "RK(x) operand expected" ) -- missing operand Test(DIES, [[ .function eq 1 2 3 4 .end ]], "end of line expected, extra operands in statement" ) -- extra operand Test(DIES, [[ .function eq 0, 1, 666 .end ]], "constant number 416 not yet defined" ) -- operand out of range Test(DIES, [[ .function eq 0, 1, 2 return 0 0 .end ]], "jump out of bounds (to 3)" ) -- jump out of bounds --------------------------------------------------------------- Test(OKAY, [[ .function eq 0, 1, 2 eq 1 $2 $1 eq 0 R1 R2 .local FOO, 2 .local BAR, 1 eq 1 FOO BAR return 0 0 return 0 0 .end ]]) -- basic eq instructions ChkCode([[ 0029 03 maxstacksize (3) 0046 03000000 sizelocvars (3) 004A 07000000 string size (7) 004E 286E6F6E652900 "(none)" 005D 04000000 string size (4) 0061 42415200 "BAR" 0065 01000000 startpc (1) 0069 06000000 endpc (6) 006D 04000000 string size (4) 0071 464F4F00 "FOO" 0075 01000000 startpc (1) 0079 06000000 endpc (6) 008D 95800000 [1] eq 0 1 2 ; to [3] if true 0091 55000101 [2] eq 1 2 1 ; to [4] if false 0095 95800000 [3] eq 0 1 2 ; to [5] if true 0099 55000101 [4] eq 1 2 1 ; to [6] if false ]]) Test(OKAY, [[ .function .const "one" .const "two" eq 0 250 251 .const FOO #123 .const BAR "baz" lt 0, FOO, BAR return 0 0 return 0 0 .end ]]) -- more variations ChkCode([[ 0046 04000000 sizek (4) 004A 04 const type 4 004B 04000000 string size (4) 004F 6F6E6500 "one" 0053 04 const type 4 0054 04000000 string size (4) 0058 74776F00 "two" 005C 03 const type 3 005D 0000000000C05E40 const [2]: (123) 0065 04 const type 4 0066 04000000 string size (4) 006A 62617A00 "baz" 0076 D53E7D00 [1] eq 0 250 251 ; "one" "two", to [3] if true 007A 563F7E00 [2] lt 0 252 253 ; 123 "baz", to [4] if true ]]) Test(OKAY, [[ .function lt 1 #123 #456 le 0 "foo" "bar" le 1 #47 "47" return 0 0 return 0 0 .end ]]) -- more variations --WriteChunk() ChkCode([[ 004A 06000000 sizek (6) 004E 03 const type 3 004F 0000000000C05E40 const [0]: (123) 0057 03 const type 3 0058 0000000000807C40 const [1]: (456) 0060 04 const type 4 0061 04000000 string size (4) 0065 666F6F00 "foo" 0069 04 const type 4 006A 04000000 string size (4) 006E 62617200 "bar" 0072 03 const type 3 0073 0000000000804740 const [4]: (47) 007B 04 const type 4 007C 03000000 string size (3) 0080 343700 "47" 008B D63E7D01 [1] lt 1 250 251 ; 123 456, to [3] if false 008F 573F7E00 [2] le 0 252 253 ; "foo" "bar", to [4] if true 0093 D73F7F01 [3] le 1 254 255 ; 47 "47", to [5] if false ]]) --------------------------------------------------------------- -- TEST R(A) [,] R(B) [,] (0|1) Test(DIES, [[ .function test R0 .end ]], "R(x) operand expected" ) -- missing operand Test(DIES, [[ .function test 1 2 .end ]], "missing operand(s)" ) -- missing operand Test(DIES, [[ .function test 1 2 1 2 .end ]], "end of line expected, extra operands in statement" ) -- extra operand Test(DIES, [[ .function test 1 2 3 .end ]], "operand C must be 0 or 1" ) -- operand C invalid Test(DIES, [[ .function test 1 1 1 return 0 0 .end ]], "jump out of bounds (to 3)" ) -- jump out of bounds --------------------------------------------------------------- Test(OKAY, [[ .function test 1, 2, 0 test $2 $1, 1 test R1 R2 0 .local FOO .local BAR test FOO BAR 0 return 0 0 return 0 0 .end ]]) -- basic test instructions --WriteChunk() ChkCode([[ 0029 03 maxstacksize (3) 0046 02000000 sizelocvars (2) 004A 04000000 string size (4) 004E 464F4F00 "FOO" 0052 01000000 startpc (1) 0056 06000000 endpc (6) 005A 04000000 string size (4) 005E 42415200 "BAR" 0062 01000000 startpc (1) 0066 06000000 endpc (6) 007A 18000101 [1] test 1 2 0 ; to [3] if true 007E 58800002 [2] test 2 1 1 ; to [4] if false 0082 18000101 [3] test 1 2 0 ; to [5] if true 0086 18800000 [4] test 0 1 0 ; to [6] if true ]]) --------------------------------------------------------------- -- CALL R(A) [,] B [,] C Test(DIES, [[ .function call 0 .end ]], "integer operand expected" ) -- missing operands Test(DIES, [[ .function call 0 1 2 3 .end ]], "end of line expected, extra operands in statement" ) -- extra operands Test(DIES, [[ .function call 1 666 .end ]], "numeric operand out of range" ) -- operand out of range Test(DIES, [[ .function call 2 foo bar .end ]], "unknown symbol 'foo' in expression" ) -- invalid operands --------------------------------------------------------------- Test(OKAY, [[ .function call $0, 0, 0 call R1, 2, 3 call 2 3 1 return 0 0 .end ]]) --WriteChunk() ChkCode([[ 0029 03 maxstacksize (3) 0052 19000000 [1] call 0 0 0 0056 D9000101 [2] call 1 2 3 005A 59800102 [3] call 2 3 1 ]]) --------------------------------------------------------------- -- RETURN R(A) [,] B Test(DIES, [[ .function return .end ]], "R(x) operand expected" ) -- missing operands Test(DIES, [[ .function return 0 .end ]], "integer operand expected" ) -- missing operand Test(DIES, [[ .function return R1, .end ]], "integer operand expected" ) -- missing operand Test(DIES, [[ .function return 666 .end ]], "R-type register operand out of range" ) -- operand out of range Test(DIES, [[ .function return 1 2 3 .end ]], "end of line expected, extra operands in statement" ) -- extra operand Test(DIES, [[ .function return $2 #10 .end ]], "integer operand expected" ) -- illegal operand type --------------------------------------------------------------- Test(OKAY, [[ .function .local FOO return $1 2 return R2 20 return 3, 4 return FOO, 8 .end ]]) -- basic return instructions --WriteChunk() ChkCode([[ 0029 04 maxstacksize (4) 003E 01000000 sizelocvars (1) 0042 04000000 string size (4) 0046 464F4F00 "FOO" 004A 01000000 startpc (1) 004E 04000000 endpc (4) 0062 1B000101 [1] return 1 2 0066 1B000A02 [2] return 2 20 006A 1B000203 [3] return 3 4 006E 1B000400 [4] return 0 8 ]]) --------------------------------------------------------------- -- TAILCALL R(A) [,] B Test(DIES, [[ .function tailcall 0 .end ]], "integer operand expected" ) -- missing operand Test(DIES, [[ .function tailcall 666 .end ]], "R-type register operand out of range" ) -- operand out of range Test(DIES, [[ .function tailcall 1 2 3 .end ]], "end of line expected, extra operands in statement" ) -- extra operand --------------------------------------------------------------- Test(OKAY, [[ .function .local BAR tailcall $1 2 tailcall R2 20 tailcall 3, 4 tailcall BAR, 66 return 0 0 .end ]]) -- basic tailcall instructions --WriteChunk() ChkCode([[ 0029 04 maxstacksize (4) 0042 01000000 sizelocvars (1) 0046 04000000 string size (4) 004A 42415200 "BAR" 004E 01000000 startpc (1) 0052 05000000 endpc (5) 0066 1A000101 [1] tailcall 1 2 0 006A 1A000A02 [2] tailcall 2 20 0 006E 1A000203 [3] tailcall 3 4 0 0072 1A002100 [4] tailcall 0 66 0 ]]) --------------------------------------------------------------- -- TFORLOOP R(A) [,] C Test(DIES, [[ .function tforloop R0 .end ]], "integer operand expected" ) -- missing operand Test(DIES, [[ .function tforloop 1 2 3 .end ]], "end of line expected, extra operands in statement" ) -- extra operand Test(DIES, [[ .function tforloop 0 0 return 0 0 .end ]], "jump out of bounds (to 3)" ) -- jump out of bounds Test(OKAY, [[ .function tforloop 0, 0 tforloop $1, 4 tforloop R2, 10 return 0 0 return 0 0 .end ]]) -- basic tforloop instructions --WriteChunk() ChkCode([[ 0029 03 maxstacksize (3) 0056 1D000000 [1] tforloop 0 0 ; to [3] if exit 005A 1D010001 [2] tforloop 1 4 ; to [4] if exit 005E 9D020002 [3] tforloop 2 10 ; to [5] if exit ]]) --------------------------------------------------------------- -- SETLIST R(A) [,] Bx -- SETLIST R(A) [,] start [...] end -- SETLISTO R(A) [,] Bx -- SETLISTO R(A) [,] start [...] end Test(DIES, [[ .function setlist 0 .end ]], "integer operand expected" ) -- missing operand Test(DIES, [[ .function setlist 1,2,3 .end ]], "end of line expected, extra operands in statement" ) -- invalid format Test(DIES, [[ .function setlist 0 0...20 .end ]], "range must start with a multiple of FPF (32) plus 1" ) -- starting index invalid Test(DIES, [[ .function setlist 1,1...100 .end ]], "range must be not exceed FPF (32)" ) -- range too large Test(DIES, [[ .function setlist 1,32...52 .end ]], "range must start with a multiple of FPF (32) plus 1" ) -- starting index invalid --------------------------------------------------------------- Test(OKAY, [[ .function setlist $1, 10 setlist R2, 40 return 0 0 .end ]]) -- basic setlist instructions ChkCode([[ 0029 03 maxstacksize (3) 004E 9F020001 [1] setlist 1 10 ; index 1 to 11 0052 1F0A0002 [2] setlist 2 40 ; index 33 to 41 ]]) Test(OKAY, [[ .function setlist 0 1...10 setlist 1, 33...42 setlisto 1, 65...74 return 0 0 .end ]]) -- setlist instructions with ranges --WriteChunk() ChkCode([[ 0052 5F020000 [1] setlist 0 9 ; index 1 to 10 0056 5F0A0001 [2] setlist 1 41 ; index 33 to 42 005A 60120001 [3] setlisto 1 73 ; index 65 to top ]]) --TODO maybe let setlisto accept "top" as third prefix --------------------------------------------------------------- -- CLOSE R(A) Test(DIES, [[ .function close .end ]], "R(x) operand expected" ) -- missing operand Test(DIES, [[ .function close R1 2 .end ]], "end of line expected, extra operands in statement" ) -- extra operand Test(DIES, [[ .function close FOO .end ]], "unknown symbol 'FOO' in expression" ) -- undefined symbol --------------------------------------------------------------- Test(OKAY, [[ .function close 0 .local FOO, 1 close FOO return 0 0 .end ]]) -- basic close instruction --WriteChunk() ChkCode([[ 003A 02000000 sizelocvars (2) 003E 07000000 string size (7) 0042 286E6F6E652900 "(none)" 0051 04000000 string size (4) 0055 464F4F00 "FOO" 0059 01000000 startpc (1) 005D 03000000 endpc (3) 0071 21000000 [1] close 0 0075 21000001 [2] close 1 ]]) --------------------------------------------------------------- -- CLOSURE R(A) [,] Bx Test(DIES, [[ .function closure $0 .end ]], "missing operand(s)" ) -- missing operands Test(DIES, [[ .function .func return 0 0 .end closure 1 0 3 .end ]], "end of line expected, extra operands in statement" ) -- extra operands Test(DIES, [[ .function closure FOO 0 .end ]], "unknown symbol 'FOO' in expression" ) -- undefined symbol Test(DIES, [[ .function closure $2 0 .end ]], "function number 0 not yet defined" ) -- no prototype defined yet Test(DIES, [[ .function .local BAR closure R1 BAR .end ]], "function number 0 not yet defined", "potentially dangerous symbol mixing in expression" ) -- dangerous type, missing function --------------------------------------------------------------- Test(OKAY, [[ .function .func return 0 0 .end .func return 0 0 .end closure 1, 0 closure R2 1 .local FOO 2 .func return 0 0 .end closure FOO 2 return 0 0 .end ]]) -- basic closure instruction ChkCode([[ 0029 03 maxstacksize (3) 002A 04000000 sizelineinfo (4) 002E 08000000 [1] (8) 0032 09000000 [2] (9) 0036 0E000000 [3] (14) 003E 03000000 sizelocvars (3) 0042 07000000 string size (7) 0046 286E6F6E652900 "(none)" 0055 07000000 string size (7) 0059 286E6F6E652900 "(none)" 0068 04000000 string size (4) 006C 464F4F00 "FOO" 0070 01000000 startpc (1) 0074 04000000 endpc (4) 0080 03000000 sizep (3) 010C 22000001 [1] closure 1 0 ; 0 upvalues 0110 62000002 [2] closure 2 1 ; 0 upvalues 0114 A2000002 [3] closure 2 2 ; 0 upvalues ]]) Test(OKAY, [[ .function FOO .func return 0 0 .end BAR .func return 0 0 .end closure $3, BAR closure R4 FOO return 0 0 .end ]]) -- named prototypes --WriteChunk() ChkCode([[ 0029 05 maxstacksize (5) 0046 02000000 sizep (2) prototype 0 004A 00000000 string size (0) 004E 02000000 line defined (2) 0056 01000000 sizelineinfo (1) 005A 03000000 [1] (3) 006E 01000000 sizecode (1) 0072 1B000000 [1] return 0 0 prototype 1 0076 00000000 string size (0) 007A 05000000 line defined (5) 0082 01000000 sizelineinfo (1) 0086 06000000 [1] (6) 009A 01000000 sizecode (1) 009E 1B000000 [1] return 0 0 root prototype 00A2 03000000 sizecode (3) 00A6 62000003 [1] closure 3 1 ; 0 upvalues 00AA 22000004 [2] closure 4 0 ; 0 upvalues 00AE 1B000000 [3] return 0 0 ]]) --TODO further requirements to make closure correct (+upvalues) --------------------------------------------------------------- -- JMP label|disp Test(DIES, [[ .function jmp .end ]], "displacement or label expected" ) -- missing operand Test(DIES, [[ .function jmp 1 2 .end ]], "end of line expected, extra operands in statement" ) -- extra operand Test(DIES, [[ .function jmp FOO .end ]], "could not resolve label 'FOO'" ) -- undefined symbol Test(DIES, [[ .function .local BAR jmp BAR .end ]], "jump out of bounds (to 0)", "potentially dangerous symbol mixing in expression" ) -- dangerous symbol Test(DIES, [[ .function jmp 1.23 .end ]], "integer displacement absolute operand expected" ) -- invalid number specification Test(DIES, [[ .function jmp #1.23 .end ]], "integer displacement relative operand expected" ) -- invalid number specification Test(DIES, [[ .function jmp 1e+10 .end ]], "Displacement-type operand out of range" ) -- invalid number specification Test(DIES, [[ .function jmp #-1e-10 .end ]], "integer displacement relative operand expected" ) -- invalid number specification Test(DIES, [[ .function jmp #1 return 0 0 .end ]], "jump out of bounds (to 3)" ) -- jump out of bounds Test(DIES, [[ .function jmp #-2 return 0 0 .end ]], "jump out of bounds (to 0)" ) -- jump out of bounds Test(DIES, [[ .function BAR: return 0 0 BAR return 0 0 .end ]], "symbol 'BAR' already defined" ) -- duplicate symbol Test(DIES, [[ .function .local FOO FOO: return 0 0 .end ]], "symbol 'FOO' already defined" ) -- duplicate symbol (different type) Test(DIES, [[ .function .local FOO jmp FOO return 0 0 .end ]], "jump out of bounds (to 0)", "potentially dangerous symbol mixing in expression" ) -- different type --------------------------------------------------------------- Test(OKAY, [[ .function jmp #0 jmp #-2 jmp #2 jmp 3 jmp 2 jmp 1 .end ]]) -- basic jmp instructions ChkCode([[ 005A D4FF7F00 [1] jmp 0 ; to [2] 005E 54FF7F00 [2] jmp -2 ; to [1] 0062 54008000 [3] jmp 2 ; to [6] 0066 54FF7F00 [4] jmp -2 ; to [3] 006A D4FE7F00 [5] jmp -4 ; to [2] 006E 54FE7F00 [6] jmp -6 ; to [1] ]]) Test(OKAY, [[ .function jmp BAR FOO move 0 1 jmp FOO BAR: move 0 1 jmp BAR BAZ jmp BAZ .end ]], "", "instruction jumps to itself" ) -- jmp instructions with labels --WriteChunk() ChkCode([[ 005A 54008000 [1] jmp 2 ; to [4] 005E 00800000 [2] move 0 1 0062 54FF7F00 [3] jmp -2 ; to [2] 0066 00800000 [4] move 0 1 006A 54FF7F00 [5] jmp -2 ; to [4] 006E 94FF7F00 [6] jmp -1 ; to [6] ]]) --------------------------------------------------------------- -- FORLOOP R(A) [,] label|disp Test(DIES, [[ .function forloop $0, .end ]], "displacement or label expected" ) -- missing operand Test(DIES, [[ .function forloop R1, FOO .end ]], "could not resolve label 'FOO'" ) -- undefined label Test(DIES, [[ .function forloop 0, #1 return 0 0 .end ]], "jump out of bounds (to 3)" ) -- jump out of bounds --------------------------------------------------------------- Test(OKAY, [[ .function FOO: move 0 1 forloop 1 FOO return 0 0 .end ]]) -- basic forloop instruction ChkCode([[ 004E 00800000 [1] move 0 1 0052 5CFF7F01 [2] forloop 1 -2 ; to [1] if loop ]]) Test(OKAY, [[ .function FOO: forloop 1 FOO return 0 0 .end ]], "", "instruction jumps to itself" ) -- loop warning Test(OKAY, [[ .function forloop R1 BAR move 0 1 BAR return 0 0 .end ]]) -- another forloop instruction --WriteChunk() ChkCode([[ 004E 1C008001 [1] forloop 1 1 ; to [3] if loop 0052 00800000 [2] move 0 1 0056 1B000000 [3] return 0 0 ]]) --------------------------------------------------------------- -- TFORPREP R(A) [,] label|disp Test(DIES, [[ .function tforprep R0, .end ]], "displacement or label expected" ) -- missing operand Test(DIES, [[ .function tforprep $1, FOO .end ]], "could not resolve label 'FOO'" ) -- undefined symbol Test(DIES, [[ .function tforprep 1, #-2 return 0 0 .end ]], "jump out of bounds (to 0)" ) -- jump out of bounds --------------------------------------------------------------- Test(OKAY, [[ .function FOO: move 0 1 tforprep 1 FOO return 0 0 .end ]]) -- basic tforprep instruction ChkCode([[ 004E 00800000 [1] move 0 1 0052 5EFF7F01 [2] tforprep 1 -2 ; to [1] ]]) Test(OKAY, [[ .function FOO: tforprep 1 FOO return 0 0 .end ]], "", "instruction jumps to itself" ) -- loop warning Test(OKAY, [[ .function tforprep R1 BAR move 0 1 BAR return 0 0 .end ]]) -- another tforprep instruction --WriteChunk() ChkCode([[ 004E 1E008001 [1] tforprep 1 1 ; to [3] 0052 00800000 [2] move 0 1 0056 1B000000 [3] return 0 0 ]]) --------------------------------------------------------------- -- the following are tests that involve equates and the expression -- parser; those above mainly deal with single symbols, constants -- or numbers --------------------------------------------------------------- -- SYNTAX: .equ [,] TK_EOL Test(DIES, [[ .equ ]], "symbol expected in .equ directive" ) -- equate missing symbol Test(DIES, [[ .equ FOO ]], "expression expected in .equ directive" ) -- equate with missing operand Test(DIES, [[ .equ FOO, ]], "expression expected after comma in .equ directive" ) -- equate optional comma test Test(DIES, [[ .equ FOO, 1 2 ]], "end of line expected, extra operands in statement" ) -- equate with extra tokens --------------------------------------------------------------- Test(OKAY, [[ .equ FOO 123 .function move 0, FOO return 0 0 .end ]], "" ) -- basic valid equate ChkCode([[ 004A 00803D00 [1] move 0 123 ]]) Test(OKAY, [[ BAR: .equ FOO, #123 .function loadk 0, BAR loadk 0, FOO return 0 0 .end ]], "" ) -- equate with labels ChkCode([[ 0042 01000000 sizek (1) 0046 03 const type 3 0047 0000000000C05E40 const [0]: (123) 0057 01000000 [1] loadk 0 0 ; 123 005B 01000000 [2] loadk 0 0 ; 123 ]]) Test(OKAY, [[ .equ FOO 123 .equ FOO 47 .function move 0, FOO return 0 0 .end ]], "" ) -- equate redefinition ChkCode([[ 004A 00801700 [1] move 0 47 ]]) --------------------------------------------------------------- Test(DIES, [[ BAR: .equ FOO, 444 .equ BAR 666 ]], "symbol 'BAR' already defined" ) -- illegal attempt to redefine symbol Test(DIES, [[ .function .local FOO .equ FOO, 444 ]], "symbol 'FOO' already defined" ) -- illegal attempt to redefine symbol Test(DIES, [[ .function .local FOO FOO .equ BAR, 444 ]], "symbol 'FOO' already defined" ) -- illegal attempt to redefine symbol --------------------------------------------------------------- -- test cases for forward-reference labels Test(DIES, [[ .function jmp FOO+1 return 0 0 .end ]], "unknown symbol 'FOO' in expression" ) -- label in expression cannot be forward-referenced Test(DIES, [[ .function jmp FOO jmp FOO+1 FOO: return 0 0 .end ]], "forward referenced symbol illegal in expression" ) -- cannot use a forward-reference label in expression --------------------------------------------------------------- -- other failure cases in expression Test(DIES, [[ .equ FOO ( ]], "premature end of line encountered in expression" ) -- test early EOL in expression Test(DIES, [[ .equ FOO (47 ]], "')' expected as delimiter in expression" ) -- test missing parenthesis in expression Test(DIES, [[ .equ FOO 47% ]], "end of line expected, extra operands in statement" ) -- test incomplete expression Test(DIES, [[ .equ FOO 47+$ ]], "illegal symbol '$' in expression" ) -- test illegal symbol in expression Test(DIES, [[ .equ FOO 47+move ]], "illegal mnemonic 'MOVE' in expression" ) -- test illegal mnemonic expression Test(DIES, [[ .equ FOO 47+.equ ]], "illegal token type 'TK_CMD' in expression" ) -- test illegal token in expression Test(DIES, [[ .equ FOO 47+BAR ]], "unknown symbol 'BAR' in expression" ) -- test unknown symbol in expression --------------------------------------------------------------- -- data types in expressions Test(OKAY, [[ .equ FOO nil .function return 0 0 .end ]], "" ) -- test nil value in expression assert(Code.globtyp["FOO"] == "equ" and Code.globval["FOO"][1] == "nil" and Code.globval["FOO"][2] == "TK_KEY", "test nil in expression" ) Test(OKAY, [[ .equ BAR true .equ BAZ false .function return 0 0 .end ]], "" ) -- test boolean values in expression assert(Code.globtyp["BAR"] == "equ" and Code.globval["BAR"][1] == "true" and Code.globval["BAR"][2] == "TK_KEY", "test true in expression" ) assert(Code.globtyp["BAZ"] == "equ" and Code.globval["BAZ"][1] == "false" and Code.globval["BAZ"][2] == "TK_KEY", "test false in expression" ) Test(OKAY, [[ .equ FOO R8 .equ BAR $66 .function return 0 0 .end ]], "" ) -- test register values in expression assert(Code.globtyp["FOO"] == "equ" and Code.globval["FOO"][1] == 8 and Code.globval["FOO"][2] == "TK_NUM", "test register in expression" ) assert(Code.globtyp["BAR"] == "equ" and Code.globval["BAR"][1] == 66 and Code.globval["BAR"][2] == "TK_NUM", "test register in expression" ) Test(OKAY, [[ .equ FOO "thomas anderson" .equ BAR [[agent a. smith]] .function return 0 0 .end ]], "" ) -- test strings in expression assert(Code.globtyp["FOO"] == "equ" and Code.globval["FOO"][1] == "thomas anderson" and Code.globval["FOO"][2] == "TK_STRING", "test string in expression" ) assert(Code.globtyp["BAR"] == "equ" and Code.globval["BAR"][1] == "agent a. smith" and Code.globval["BAR"][2] == "TK_STRING", "test long string in expression" ) Test(OKAY, [[ .equ FOO 1234 .equ BAR #1234 .function return 0 0 .end ]], "" ) -- test number and immediate in expression assert(Code.globtyp["FOO"] == "equ" and Code.globval["FOO"][1] == 1234 and Code.globval["FOO"][2] == "TK_NUM", "test number in expression" ) assert(Code.globtyp["BAR"] == "equ" and Code.globval["BAR"][1] == 1234 and Code.globval["BAR"][2] == "TK_IMM", "test immediate in expression" ) Test(OKAY, [[ .function .local FOO, 5 .equ BAR FOO return 0 0 .end ]], "" ) -- test local in expression assert(Code.globtyp["BAR"] == "equ" and Code.globval["BAR"][1] == 5 and Code.globval["BAR"][2] == "TK_NUM", "test local in expression" ) Test(OKAY, [[ .function .upvalue FOO, 3 .equ BAR FOO return 0 0 .end ]], "" ) -- test upvalue in expression assert(Code.globtyp["BAR"] == "equ" and Code.globval["BAR"][1] == 3 and Code.globval["BAR"][2] == "TK_NUM", "test upvalue in expression" ) Test(OKAY, [[ .function .const FOO #888 20 .equ BAR FOO return 0 0 .end ]], "" ) -- test const in expression assert(Code.globtyp["BAR"] == "equ" and Code.globval["BAR"][1] == 20 and Code.globval["BAR"][2] == "TK_NUM", "test const in expression" ) Test(OKAY, [[ .function FOO: move 0 1 jmp FOO .equ BAR FOO return 0 0 .end ]], "" ) -- test code label in expression assert(Code.globtyp["BAR"] == "equ" and Code.globval["BAR"][1] == 1 and Code.globval["BAR"][2] == "TK_NUM", "test code label in expression" ) Test(OKAY, [[ .function FOO: .function return 0 0 .end .equ BAR FOO return 0 0 .end ]], "" ) -- test function label in expression assert(Code.globtyp["BAR"] == "equ" and Code.globval["BAR"][1] == 0 and Code.globval["BAR"][2] == "TK_NUM", "test function label in expression" ) --------------------------------------------------------------- -- parentheses Test(OKAY, [[ .equ FOO ((47)) .function return 0 0 .end ]], "" ) -- test multiple parentheses assert(Code.globtyp["FOO"] == "equ" and Code.globval["FOO"][1] == 47 and Code.globval["FOO"][2] == "TK_NUM", "test multiple parentheses" ) Test(OKAY, [[ .equ FOO 4*(5+6) .function return 0 0 .end ]], "" ) -- test parentheses priority assert(Code.globtyp["FOO"] == "equ" and Code.globval["FOO"][1] == 44 and Code.globval["FOO"][2] == "TK_NUM", "test parentheses priority" ) Test(DIES, [[ .equ FOO ((47) ]], "')' expected as delimiter in expression" ) -- missing parentheses Test(OKAY, [[ .equ FOO 2*((2+3)^3) .function return 0 0 .end ]], "" ) -- more complex use of parentheses assert(Code.globtyp["FOO"] == "equ" and Code.globval["FOO"][1] == 250 and Code.globval["FOO"][2] == "TK_NUM", "test more complex use of parentheses" ) --------------------------------------------------------------- -- immediate operator Test(OKAY, [[ .equ FOO #77 .function return 0 0 .end ]], "" ) -- test immediate operator assert(Code.globtyp["FOO"] == "equ" and Code.globval["FOO"][1] == 77 and Code.globval["FOO"][2] == "TK_IMM", "test immediate operator" ) Test(OKAY, [[ .equ FOO # 10 + 15 .function return 0 0 .end ]], "" ) -- test immediate operator assert(Code.globtyp["FOO"] == "equ" and Code.globval["FOO"][1] == 25 and Code.globval["FOO"][2] == "TK_IMM", "test immediate operator" ) Test(OKAY, [[ .equ FOO 10+#15 .function return 0 0 .end ]], "" ) -- test immediate operator assert(Code.globtyp["FOO"] == "equ" and Code.globval["FOO"][1] == 25 and Code.globval["FOO"][2] == "TK_IMM", "test immediate operator" ) Test(OKAY, [[ .equ FOO #"FOO" .function return 0 0 .end ]], "" ) -- test immediate operator assert(Code.globtyp["FOO"] == "equ" and Code.globval["FOO"][1] == "FOO" and Code.globval["FOO"][2] == "TK_STRING", "test immediate operator" ) Test(DIES, [[ .equ FOO #false ]], "'#' can only be applied to numbers or strings" ) -- test immediate operator Test(DIES, [[ .equ FOO #nil ]], "'#' can only be applied to numbers or strings" ) -- test immediate operator Test(OKAY, [[ .equ FOO ###123 .equ BAR #(#(#123)) .function return 0 0 .end ]], "" ) -- test immediate operator assert(Code.globtyp["FOO"] == "equ" and Code.globval["FOO"][1] == 123 and Code.globval["FOO"][2] == "TK_IMM", "test immediate operator" ) assert(Code.globtyp["BAR"] == "equ" and Code.globval["BAR"][1] == 123 and Code.globval["BAR"][2] == "TK_IMM", "test immediate operator" ) --------------------------------------------------------------- -- unary minus operator Test(OKAY, [[ .equ FOO 10+-6 .function return 0 0 .end ]], "" ) -- test unary minus operator assert(Code.globtyp["FOO"] == "equ" and Code.globval["FOO"][1] == 4 and Code.globval["FOO"][2] == "TK_NUM", "test unary minus operator" ) Test(OKAY, [[ -- note that 10--6 leaves 6 as a comment! .equ FOO 10- -6 .equ BAR #10- -2- -2 .function return 0 0 .end ]], "" ) -- test unary minus operator assert(Code.globtyp["FOO"] == "equ" and Code.globval["FOO"][1] == 16 and Code.globval["FOO"][2] == "TK_NUM", "test unary minus operator" ) assert(Code.globtyp["BAR"] == "equ" and Code.globval["BAR"][1] == 14 and Code.globval["BAR"][2] == "TK_IMM", "test immediate operator" ) --------------------------------------------------------------- -- unary not (~) operator Test(OKAY, [[ .equ FOO ~false .equ BAR ~true .function return 0 0 .end ]], "" ) -- test unary not operator assert(Code.globval["FOO"][1] == "true" and Code.globval["FOO"][2] == "TK_KEY", "test unary not operator" ) assert(Code.globval["BAR"][1] == "false" and Code.globval["BAR"][2] == "TK_KEY", "test unary not operator" ) Test(OKAY, [[ .equ FOO ~~10 .equ BAR ~"FOO" .equ BAZ ~nil .function return 0 0 .end ]], "" ) -- test unary not operator assert(Code.globval["FOO"][1] == "true" and Code.globval["FOO"][2] == "TK_KEY", "test unary not operator" ) assert(Code.globval["BAR"][1] == "false" and Code.globval["BAR"][2] == "TK_KEY", "test unary not operator" ) assert(Code.globval["BAZ"][1] == "true" and Code.globval["BAZ"][2] == "TK_KEY", "test unary not operator" ) --------------------------------------------------------------- -- binary operators (basic) Test(OKAY, [[ .equ FOO 2^4 .function return 0 0 .end ]], "" ) -- test binary operators assert(Code.globval["FOO"][1] == 16 and Code.globval["FOO"][2] == "TK_NUM", "test binary operators" ) Test(DIES, [[ .equ FOO 2^"FOO" ]], "failed to coerce string into a number in expression" ) -- test binary operators Test(DIES, [[ .equ FOO "FOO"^4 ]], "failed to coerce string into a number in expression" ) -- test binary operators Test(DIES, [[ .equ FOO "FOO"^"BAR" ]], "failed to coerce string into a number in expression" ) -- test binary operators Test(DIES, [[ .equ FOO 2^true ]], "illegal operation with nil or boolean in expression" ) -- test binary operators Test(OKAY, [[ .equ FOO "2"^"4" .function return 0 0 .end ]], "" ) -- test binary operators assert(Code.globval["FOO"][1] == 16 and Code.globval["FOO"][2] == "TK_IMM", "test binary operators" ) Test(OKAY, [[ .equ FOO 0 .function return 0 0 .end ]], "" ) -- test binary operators assert(Code.globval["FOO"][1] == 0 and Code.globval["FOO"][2] == "TK_NUM", "test binary operators" ) --------------------------------------------------------------- -- binary operators (arithmetic) Test(OKAY, [[ .equ FOO 15 * 8 .equ BAR 25/5 .function return 0 0 .end ]], "" ) -- test binary arithmetic operators assert(Code.globval["FOO"][1] == 120 and Code.globval["FOO"][2] == "TK_NUM", "test binary arithmetic operators" ) assert(Code.globval["BAR"][1] == 5 and Code.globval["BAR"][2] == "TK_NUM", "test binary arithmetic operators" ) Test(OKAY, [[ .equ FOO 47+74 .equ BAR 74-47 .function return 0 0 .end ]], "" ) -- test binary arithmetic operators assert(Code.globval["FOO"][1] == 121 and Code.globval["FOO"][2] == "TK_NUM", "test binary arithmetic operators" ) assert(Code.globval["BAR"][1] == 27 and Code.globval["BAR"][2] == "TK_NUM", "test binary arithmetic operators" ) --------------------------------------------------------------- -- string operator Test(OKAY, [[ .equ FOO 2..4 .function return 0 0 .end ]], "" ) -- test string operator assert(Code.globval["FOO"][1] == "24" and Code.globval["FOO"][2] == "TK_STRING", "test string operator" ) Test(OKAY, [[ .equ FOO "FOO".."BAR" .function return 0 0 .end ]], "" ) -- test string operator assert(Code.globval["FOO"][1] == "FOOBAR" and Code.globval["FOO"][2] == "TK_STRING", "test string operator" ) Test(OKAY, [[ .equ FOO "FOO" .. #888 .function return 0 0 .end ]], "" ) -- test string operator assert(Code.globval["FOO"][1] == "FOO888" and Code.globval["FOO"][2] == "TK_STRING", "test string operator" ) Test(DIES, [[ .equ FOO false.."BAR" ]], "illegal operation with nil or boolean in expression" ) -- test string operator --------------------------------------------------------------- -- binary operators (equality) Test(OKAY, [[ .equ FOO 2 ~= 4 .equ BAR 4 ~= 4 .function return 0 0 .end ]], "" ) -- test equality operators assert(Code.globval["FOO"][1] == "true" and Code.globval["FOO"][2] == "TK_KEY", "test equality operators" ) assert(Code.globval["BAR"][1] == "false" and Code.globval["BAR"][2] == "TK_KEY", "test equality operators" ) Test(OKAY, [[ .equ FOO 7 == 7 .equ BAR 7 == 4 .function return 0 0 .end ]], "" ) -- test equality operators assert(Code.globval["FOO"][1] == "true" and Code.globval["FOO"][2] == "TK_KEY", "test equality operators" ) assert(Code.globval["BAR"][1] == "false" and Code.globval["BAR"][2] == "TK_KEY", "test equality operators" ) Test(OKAY, [[ .equ FOO "FOO" ~= "BAR" .equ BAR "FOO" == "BAR" .equ BAZ "FOO" == "FOO" .function return 0 0 .end ]], "" ) -- test equality operators assert(Code.globval["FOO"][1] == "true" and Code.globval["FOO"][2] == "TK_KEY", "test equality operators" ) assert(Code.globval["BAR"][1] == "false" and Code.globval["BAR"][2] == "TK_KEY", "test equality operators" ) assert(Code.globval["BAZ"][1] == "true" and Code.globval["BAZ"][2] == "TK_KEY", "test equality operators" ) Test(OKAY, [[ .equ FOO 7 == "7" .equ BAR true == true .function return 0 0 .end ]], "" ) -- test equality operators assert(Code.globval["FOO"][1] == "false" and Code.globval["FOO"][2] == "TK_KEY", "test equality operators" ) assert(Code.globval["BAR"][1] == "true" and Code.globval["BAR"][2] == "TK_KEY", "test equality operators" ) --------------------------------------------------------------- -- binary operators (comparison) Test(DIES, [[ .equ FOO true < false ]], "illegal operation with nil or boolean in expression" ) -- test comparison operators Test(DIES, [[ .equ FOO true < nil ]], "illegal operation with nil or boolean in expression" ) -- test comparison operators Test(DIES, [[ .equ FOO "FOO" < 10 ]], "trying to compare different data types in expression" ) -- test comparison operators Test(OKAY, [[ .equ FOO 4 < 7 .equ BAR 7 < 4 .function return 0 0 .end ]], "" ) -- test comparison operators assert(Code.globval["FOO"][1] == "true" and Code.globval["FOO"][2] == "TK_KEY", "test comparison operators" ) assert(Code.globval["BAR"][1] == "false" and Code.globval["BAR"][2] == "TK_KEY", "test comparison operators" ) Test(OKAY, [[ .equ FOO 7 <= 4 .equ BAR 7 <= 7 .function return 0 0 .end ]], "" ) -- test comparison operators assert(Code.globval["FOO"][1] == "false" and Code.globval["FOO"][2] == "TK_KEY", "test comparison operators" ) assert(Code.globval["BAR"][1] == "true" and Code.globval["BAR"][2] == "TK_KEY", "test comparison operators" ) Test(OKAY, [[ .equ FOO 4 > 7 .equ BAR 7 > 4 .function return 0 0 .end ]], "" ) -- test comparison operators assert(Code.globval["FOO"][1] == "false" and Code.globval["FOO"][2] == "TK_KEY", "test comparison operators" ) assert(Code.globval["BAR"][1] == "true" and Code.globval["BAR"][2] == "TK_KEY", "test comparison operators" ) Test(OKAY, [[ .equ FOO 7 >= 4 .equ BAR 7 >= 7 .function return 0 0 .end ]], "" ) -- test comparison operators assert(Code.globval["FOO"][1] == "true" and Code.globval["FOO"][2] == "TK_KEY", "test comparison operators" ) assert(Code.globval["BAR"][1] == "true" and Code.globval["BAR"][2] == "TK_KEY", "test comparison operators" ) --------------------------------------------------------------- -- binary operators (logical) Test(OKAY, [[ .equ FOO "FOO" and "BAR" .equ BAR false and "FOO" .equ BAZ "FOO" and 555 .function return 0 0 .end ]], "" ) -- test logical binary operators assert(Code.globval["FOO"][1] == "BAR" and Code.globval["FOO"][2] == "TK_STRING", "test logical binary operators" ) assert(Code.globval["BAR"][1] == "false" and Code.globval["BAR"][2] == "TK_KEY", "test logical binary operators" ) assert(Code.globval["BAZ"][1] == 555 and Code.globval["BAZ"][2] == "TK_NUM", "test logical binary operators" ) Test(OKAY, [[ .equ FOO "FOO" or "BAR" .equ BAR false or "FOO" .equ BAZ 555 or "FOO" .function return 0 0 .end ]], "" ) -- test logical binary operators assert(Code.globval["FOO"][1] == "FOO" and Code.globval["FOO"][2] == "TK_STRING", "test logical binary operators" ) assert(Code.globval["BAR"][1] == "FOO" and Code.globval["BAR"][2] == "TK_STRING", "test logical binary operators" ) assert(Code.globval["BAZ"][1] == 555 and Code.globval["BAZ"][2] == "TK_NUM", "test logical binary operators" ) --------------------------------------------------------------- -- test Parameter parsing: .header, .function Test(OKAY, [[ .header signature = "FOO".."BAR", \ version = 0x50+4 .function return 0 0 .end ]], "", "signature declared in not of normal length" ) -- expressions in header values ChkChunk(0, "FOOBAR") -- signature ChkChunk(6, "\84") -- version Test(OKAY, [[ .equ TheOne, "Neo" .equ ID, 47 .header .function source_name = TheOne..ID, \ line_defined = ID*10, \ maxstacksize=100-ID return 0 0 .end ]] ) -- expression values for a function ChkCode([[ 0016 06000000 string size (6) 001A 4E656F343700 "Neo47" 0020 D6010000 line defined (470) 0027 35 maxstacksize (53) ]]) --------------------------------------------------------------- -- test expressions in directives: -- .local, .upvalue, .const, .equ Test(OKAY, [[ .equ FOO 47 .equ BAR FOO+20 .function .local BAZ FOO+40 .equ FOOBAR FOO+BAR+BAZ return 0 0 .end ]], "" ) -- multiple lookups of symbols assert(Code.globval["FOO"][1] == 47 and Code.globval["FOO"][2] == "TK_NUM", "test multiple lookups of symbols" ) assert(Code.globval["BAR"][1] == 67 and Code.globval["BAR"][2] == "TK_NUM", "test multiple lookups of symbols" ) assert(Code.globval["FOOBAR"][1] == 47+67+87 and Code.globval["FOOBAR"][2] == "TK_NUM", "test multiple lookups of symbols" ) Test(OKAY, [[ .function .local FOO2 2+3 .const BAR2 #999 4/2 .upvalue BAZ2 6+1 .equ FOO FOO2 .equ BAR BAR2 .equ BAZ BAZ2 return 0 0 .end ]], "" ) -- text expressions in directives assert(Code.globval["FOO"][1] == 5 and Code.globval["FOO"][2] == "TK_NUM", "text expressions in directives" ) assert(Code.globval["BAR"][1] == 2 and Code.globval["BAR"][2] == "TK_NUM", "text expressions in directives" ) assert(Code.globval["BAZ"][1] == 7 and Code.globval["BAZ"][2] == "TK_NUM", "text expressions in directives" ) --------------------------------------------------------------- -- test expression in operands Test(OKAY, [[ .equ FOO 24 .function .local BAR 2 move 10-10, 2*(4+1) move FOO BAR move R0, BAR+$2 return 0 0 .end ]], "" ) -- test OperandR ChkCode([[ 0088 00000500 [1] move 0 10 008C 00000118 [2] move 24 2 0090 00000200 [3] move 0 4 ]]) Test(OKAY, [[ .function .const FOO "Morpheus" 2 .upvalue BAR 3 move 0, (FOO+3) BAZ: move 1, 2*(BAR) move 2, (3^BAZ) return 0 0 .end ]], "" ) -- test OperandR ChkCode([[ 008B 00800200 [1] move 0 5 008F 00000301 [2] move 1 6 0093 00800402 [3] move 2 9 ]]) Test(OKAY, [[ .function .upvalue FOO 1 move 0, 10+FOO return 0 0 .end ]], "", "potentially dangerous symbol mixing in expression" ) -- test OperandR Test(OKAY, [[ .function .local FOO 2 .const BAR #0, 3 .upvalue BAZ 4 move 0, (BAR)+(BAZ)+FOO return 0 0 .end ]], "" ) -- test OperandR (multiple types) ChkCode([[ 00C0 00800400 [1] move 0 9 ]]) Test(OKAY, [[ .function .local FOO 2 .const BAR #0, 3 .upvalue BAZ 4 move 0, BAR+BAZ+FOO return 0 0 .end ]], "", "potentially dangerous symbol mixing in expression" ) -- test OperandR (multiple types) --------------------------------------------------------------- Test(OKAY, [[ .equ FOO #24 .function .const BAR #48 2 loadk R0, 1+1 loadk $1, FOO loadk 2, BAR return 0 0 .end ]], "" ) -- test OperandKst ChkCode([[ 0046 03000000 sizek (3) 004A 03 const type 3 004B 0000000000003840 const [0]: (24) 0053 00 const type 0 0054 03 const type 3 0055 0000000000004840 const [2]: (48) 005D 00000000 sizep (0) 0065 81000000 [1] loadk 0 2 ; 48 0069 01000001 [2] loadk 1 0 ; 24 006D 81000002 [3] loadk 2 2 ; 48 ]]) Test(OKAY, [[ .function .local FOO 2 .upvalue BAR 4 .const C0, #0, 7 .const C1, #0, 12 .const C2, #0, 16 loadk 0, (FOO+5) BAZ: loadk 1, 3*(BAR) loadk 2, (4^BAZ) return 0 0 .end ]], "" ) -- test OperandKst ChkCode([[ 00E5 C1010000 [1] loadk 0 7 ; 0 00E9 01030001 [2] loadk 1 12 ; 0 00ED 01040002 [3] loadk 2 16 ; 0 ]]) Test(OKAY, [[ .function .local FOO 2 .upvalue BAR 4 loadk 0, #FOO+5 BAZ: loadk 1, #3*BAR loadk 2, #4^BAZ return 0 0 .end ]], "" ) -- test OperandKst ChkCode([[ 00D7 01000000 [1] loadk 0 0 ; 7 00DB 41000001 [2] loadk 1 1 ; 12 00DF 81000002 [3] loadk 2 2 ; 16 ]]) Test(OKAY, [[ .function .upvalue FOO 1 .const C0, #0, 2 loadk 0, 1+FOO return 0 0 .end ]], "", "potentially dangerous symbol mixing in expression" ) -- test OperandKst --------------------------------------------------------------- Test(OKAY, [[ .equ FOO 24 .function .local BAR 2 .const CA #10 .const CB #10 add 0, 10-10, 2*(4+1) add 0, FOO BAR add 0, R0, BAR+$2 add 0, CA, #CB add FOO, BAR, CB return 0 0 .end ]], "" ) -- test OperandRK ChkCode([[ 00A2 8C020000 [1] add 0 0 10 00A6 8C000C00 [2] add 0 24 2 00AA 0C010000 [3] add 0 0 4 00AE CC3E7D00 [4] add 0 250 251 ; 10 10 00B2 CC3E0118 [5] add 24 2 251 ; 10 ]]) Test(OKAY, [[ .function .const FOO "Morpheus" 2 .upvalue BAR 3 add 0, 0, (FOO+3) BAZ: add 0, 1, 2*(BAR) add 0, 2, (3^BAZ) return 0 0 .end ]], "" ) -- test OperandRK ChkCode([[ 008B 4C010000 [1] add 0 0 5 008F 8C810000 [2] add 0 1 6 0093 4C020100 [3] add 0 2 9 ]]) Test(OKAY, [[ .function .upvalue FOO 1 add 0, 1, 10+FOO return 0 0 .end ]], "", "potentially dangerous symbol mixing in expression" ) -- test OperandRK Test(OKAY, [[ .function .upvalue FOO 1 BAR: move 0, 1 add 1, 2, BAR return 0 0 .end ]], "", "potentially dangerous symbol mixing in expression" ) -- test OperandRK --------------------------------------------------------------- Test(OKAY, [[ .equ FOO 24 .function .upvalue BAR 2 .upvalue BAZ 4 getupval 10-10, 2*(4+1)/5 getupval FOO BAR getupval R0, BAR+2 return 0 0 .end ]], "" ) -- test OperandUpval ChkCode([[ 0083 04000100 [1] getupval 0 2 ; BAR 0087 04000118 [2] getupval 24 2 ; BAR 008B 04000200 [3] getupval 0 4 ; BAZ ]]) Test(OKAY, [[ .function .const FOO "Morpheus" 2 .local BAR 3 .upvalue U0, 5 .upvalue U1, 6 .upvalue U2, 9 setupval 0, (FOO+3) BAZ: setupval 1, 2*(BAR) setupval 2, (3^BAZ) return 0 0 .end ]], "" ) -- test OperandUpval ChkCode([[ 010D 08800200 [1] setupval 0 5 ; U0 0111 08000301 [2] setupval 1 6 ; U1 0115 08800402 [3] setupval 2 9 ; U2 ]]) Test(OKAY, [[ .function .local FOO 1 .upvalue BAR, 11 getupval 0, 10+FOO return 0 0 .end ]], "", "potentially dangerous symbol mixing in expression" ) -- test OperandUpval --------------------------------------------------------------- Test(OKAY, [[ .equ FOO 24 .function .local BAR 4 newtable R0, 10-10, 2*(4+1) newtable $1, FOO, (BAR) newtable 2, #BAR, 2^#BAR return 0 0 .end ]], "" ) -- test OperandNum ChkCode([[ 00AE 8A020000 [1] newtable 0 0 10 ; array=0, hash=1024 00B2 0A010C01 [2] newtable 1 24 4 ; array=0, hash=16 00B6 4A010202 [3] newtable 2 4 5 ; array=4, hash=32 ]]) Test(OKAY, [[ .function .local FOO 1 newtable R0, 0, 10+FOO return 0 0 .end ]], "", "potentially dangerous symbol mixing in expression" ) -- test OperandNum --------------------------------------------------------------- Test(OKAY, [[ .equ FOO 2 .function .const BAR #3 .local BAZ 2 move 0, 1 BAT: move 0, 1 move 0, 1 jmp FOO+1 jmp BAT+2 jmp BAT-1 jmp -#BAR jmp (BAZ+1) return 0 0 .end ]], "" ) -- test OperandDisp ChkCode([[ 00B1 54FF7F00 [4] jmp -2 ; to [3] 00B5 54FF7F00 [5] jmp -2 ; to [4] 00B9 54FE7F00 [6] jmp -6 ; to [1] 00BD 14FF7F00 [7] jmp -3 ; to [5] 00C1 54FE7F00 [8] jmp -6 ; to [3] ]]) Test(OKAY, [[ .equ FOO 2 .function .const BAR #3 .local BAZ 2 move 0, 1 BAT: move 0, 1 jmp BAZ return 0 0 .end ]], "", "potentially dangerous symbol mixing in expression" ) -- test OperandDisp --------------------------------------------------------------- Test(OKAY, [[ .equ FOO 0 .function .local BAZ 1 .function return 0 0 .end BAR: .function return 0 0 .end closure R1, FOO closure $1+1, BAR closure 1+1+1, (BAZ) return 0 0 .end ]], "" ) -- test OperandProto ChkCode([[ 00CD 22000001 [1] closure 1 0 ; 0 upvalues 00D1 62000002 [2] closure 2 1 ; 0 upvalues 00D5 62000003 [3] closure 3 1 ; 0 upvalues ]]) Test(OKAY, [[ .function .local BAZ 0 .function return 0 0 .end closure R1, BAZ return 0 0 .end ]], "", "potentially dangerous symbol mixing in expression" ) -- test OperandProto --------------------------------------------------------------- Test(OKAY, [[ .equ YEP 1 .equ NJET 0 .function .local FOO 1 loadbool 0, YEP, 0 loadbool 0, NJET, 0 loadbool 1, (FOO), 0 loadbool 2, 2-1, 0 loadbool 2, 3+(-3), 0 loadbool 3, 2==2, 0 loadbool 3, 4==7, 0 return 0 0 .end ]], "" ) -- test OperandBool ChkCode([[ 0085 02800000 [1] loadbool 0 1 0 ; true 0089 02000000 [2] loadbool 0 0 0 ; false 008D 02800001 [3] loadbool 1 1 0 ; true 0091 02800002 [4] loadbool 2 1 0 ; true 0095 02000002 [5] loadbool 2 0 0 ; false 0099 02800003 [6] loadbool 3 1 0 ; true 009D 02000003 [7] loadbool 3 0 0 ; false ]]) Test(OKAY, [[ .function .local FOO 1 loadbool 1, FOO, 0 return 0 0 .end ]], "", "potentially dangerous symbol mixing in expression" ) -- test OperandBool --------------------------------------------------------------- Test(OKAY, [[ .equ SKIP 1 .equ NEXT 0 .function .local FOO 1 loadbool 0, 1, SKIP loadbool 0, 0, NEXT loadbool 1, 1, 2-1 loadbool 1, 0, 3+(-3) return 0 0 .end ]], "" ) -- test Operand01 ChkCode([[ 0079 42800000 [1] loadbool 0 1 1 ; true, to [3] 007D 02000000 [2] loadbool 0 0 0 ; false 0081 42800001 [3] loadbool 1 1 1 ; true, to [5] 0085 02000001 [4] loadbool 1 0 0 ; false ]]) Test(OKAY, [[ .function .local FOO 1 loadbool 1, 1, FOO move 0 1 return 0 0 .end ]], "", "potentially dangerous symbol mixing in expression" ) -- test Operand01 --------------------------------------------------------------- --[[ Disp(DIES, [[ .function .end ]], "" ) Test(OKAY, [[ .function .end ]]) --WriteChunk() ChkCode([[ ]]) --]] --------------------------------------------------------------- -- summary of lexer testing --------------------------------------------------------------- print("\n-------------------------------------\n".. "STATUS: "..total.." parser tests performed") if not crashed then print("STATUS: all parser tests succeeded") else print("STATUS: errors were found; check the verbose output") end print("-------------------------------------") end ----------------------------------------------------------------------- -- program entry point ----------------------------------------------------------------------- print(title) if not DONTTEST_LEX then LexTest() end if not DONTTEST_PARSE then ParseTest() end ----end-of-script----