Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- -- Gui to Lua
- -- Version: 3.2
- -- Instances:
- local SS = Instance.new("ScreenGui")
- local Frame = Instance.new("Frame")
- local Frame_2 = Instance.new("Frame")
- local TextLabel = Instance.new("TextLabel")
- local execute = Instance.new("TextButton")
- local clear = Instance.new("TextButton")
- local source = Instance.new("TextBox")
- local close = Instance.new("TextButton")
- local open = Instance.new("TextButton")
- --Properties:
- SS.Name = "SS"
- SS.Parent = game.Workspace
- SS.ZIndexBehavior = Enum.ZIndexBehavior.Sibling
- Frame.Parent = SS
- Frame.BackgroundColor3 = Color3.fromRGB(0, 170, 0)
- Frame.BorderSizePixel = 0
- Frame.Position = UDim2.new(0.328465194, 0, 0.198740721, 0)
- Frame.Size = UDim2.new(0, 419, 0, 282)
- Frame.Visible = false
- Frame_2.Parent = Frame
- Frame_2.BackgroundColor3 = Color3.fromRGB(170, 255, 127)
- Frame_2.BorderSizePixel = 0
- Frame_2.Position = UDim2.new(-0.00231534243, 0, -0.00259640813, 0)
- Frame_2.Size = UDim2.new(0, 419, 0, 41)
- TextLabel.Parent = Frame_2
- TextLabel.BackgroundColor3 = Color3.fromRGB(255, 255, 255)
- TextLabel.BackgroundTransparency = 1.000
- TextLabel.BorderSizePixel = 0
- TextLabel.Position = UDim2.new(-0.00238663494, 0, 0, 0)
- TextLabel.Size = UDim2.new(0, 418, 0, 41)
- TextLabel.Font = Enum.Font.Gotham
- TextLabel.Text = "Tutorial SS"
- TextLabel.TextColor3 = Color3.fromRGB(0, 0, 0)
- TextLabel.TextScaled = true
- TextLabel.TextSize = 14.000
- TextLabel.TextWrapped = true
- execute.Name = "execute"
- execute.Parent = Frame
- execute.BackgroundColor3 = Color3.fromRGB(170, 255, 127)
- execute.BorderSizePixel = 0
- execute.Position = UDim2.new(0.0214797128, 0, 0.872340441, 0)
- execute.Size = UDim2.new(0, 185, 0, 28)
- execute.Font = Enum.Font.Gotham
- execute.Text = "Execute"
- execute.TextColor3 = Color3.fromRGB(0, 0, 0)
- execute.TextScaled = true
- execute.TextSize = 14.000
- execute.TextWrapped = true
- clear.Name = "clear"
- clear.Parent = Frame
- clear.BackgroundColor3 = Color3.fromRGB(170, 255, 127)
- clear.BorderSizePixel = 0
- clear.Position = UDim2.new(0.529832959, 0, 0.872340441, 0)
- clear.Size = UDim2.new(0, 185, 0, 28)
- clear.Font = Enum.Font.Gotham
- clear.Text = "Clear"
- clear.TextColor3 = Color3.fromRGB(0, 0, 0)
- clear.TextScaled = true
- clear.TextSize = 14.000
- clear.TextWrapped = true
- source.Name = "source"
- source.Parent = Frame
- source.BackgroundColor3 = Color3.fromRGB(170, 255, 127)
- source.BorderSizePixel = 0
- source.Position = UDim2.new(0.0214797128, 0, 0.184397161, 0)
- source.Size = UDim2.new(0, 398, 0, 185)
- source.Font = Enum.Font.SourceSans
- source.PlaceholderText = "Put script here!"
- source.Text = ""
- source.TextColor3 = Color3.fromRGB(0, 0, 0)
- source.TextSize = 14.000
- source.TextXAlignment = Enum.TextXAlignment.Left
- source.TextYAlignment = Enum.TextYAlignment.Top
- close.Name = "close"
- close.Parent = Frame
- close.BackgroundColor3 = Color3.fromRGB(255, 47, 6)
- close.BorderColor3 = Color3.fromRGB(255, 47, 6)
- close.Position = UDim2.new(0.899761319, 0, 0, 0)
- close.Size = UDim2.new(0, 41, 0, 40)
- close.Font = Enum.Font.SourceSans
- close.Text = "X"
- close.TextColor3 = Color3.fromRGB(0, 0, 0)
- close.TextSize = 53.000
- open.Name = "open"
- open.Parent = SS
- open.BackgroundColor3 = Color3.fromRGB(0, 170, 0)
- open.Position = UDim2.new(0, 0, 0.492569, 0)
- open.Size = UDim2.new(0, 75, 0, 32)
- open.Font = Enum.Font.SourceSans
- open.Text = "OPEN"
- open.TextColor3 = Color3.fromRGB(0, 0, 0)
- open.TextSize = 14.000
- -- Module Scripts:
- local fake_module_scripts = {}
- do -- nil.Loadstring
- local script = Instance.new('ModuleScript', nil)
- script.Name = "Loadstring"
- local function module_script()
- --[[
- Credit to einsteinK.
- Credit to Stravant for LBI.
- Credit to the creators of all the other modules used in this.
- Sceleratis was here and decided modify some things.
- einsteinK was here again to fix a bug in LBI for if-statements
- --]]
- local waitDeps = {
- 'Rerubi';
- 'LuaK';
- 'LuaP';
- 'LuaU';
- 'LuaX';
- 'LuaY';
- 'LuaZ';
- }
- for i,v in pairs(waitDeps) do script:WaitForChild(v) end
- local luaX = require(script.LuaX)
- local luaY = require(script.LuaY)
- local luaZ = require(script.LuaZ)
- local luaU = require(script.LuaU)
- local rerubi = require(script.Rerubi)
- luaX:init()
- local LuaState = {}
- getfenv().script = nil
- return function(str,env)
- local f,writer,buff,name
- local env = env or getfenv(2)
- local name = (env.script and env.script:GetFullName())
- local ran,error = pcall(function()
- local zio = luaZ:init(luaZ:make_getS(str), nil)
- if not zio then return error() end
- local func = luaY:parser(LuaState, zio, nil, name or "nil")
- writer, buff = luaU:make_setS()
- luaU:dump(LuaState, func, writer, buff)
- f = rerubi(buff.data, env)
- end)
- if ran then
- return f,buff.data
- else
- return nil,error
- end
- end
- end
- fake_module_scripts[script] = module_script
- end
- do -- nil.LuaZ
- local script = Instance.new('ModuleScript', nil)
- script.Name = "LuaZ"
- local function module_script()
- --[[--------------------------------------------------------------------
- lzio.lua
- Lua buffered streams in Lua
- This file is part of Yueliang.
- Copyright (c) 2005-2006 Kein-Hong Man <khman@users.sf.net>
- The COPYRIGHT file describes the conditions
- under which this software may be distributed.
- See the ChangeLog for more information.
- ----------------------------------------------------------------------]]
- --[[--------------------------------------------------------------------
- -- Notes:
- -- * EOZ is implemented as a string, "EOZ"
- -- * Format of z structure (ZIO)
- -- z.n -- bytes still unread
- -- z.p -- last read position position in buffer
- -- z.reader -- chunk reader function
- -- z.data -- additional data
- -- * Current position, p, is now last read index instead of a pointer
- --
- -- Not implemented:
- -- * luaZ_lookahead: used only in lapi.c:lua_load to detect binary chunk
- -- * luaZ_read: used only in lundump.c:ezread to read +1 bytes
- -- * luaZ_openspace: dropped; let Lua handle buffers as strings (used in
- -- lundump.c:LoadString & lvm.c:luaV_concat)
- -- * luaZ buffer macros: dropped; buffers are handled as strings
- -- * lauxlib.c:getF reader implementation has an extraline flag to
- -- skip over a shbang (#!) line, this is not implemented here
- --
- -- Added:
- -- (both of the following are vaguely adapted from lauxlib.c)
- -- * luaZ:make_getS: create Reader from a string
- -- * luaZ:make_getF: create Reader that reads from a file
- --
- -- Changed in 5.1.x:
- -- * Chunkreader renamed to Reader (ditto with Chunkwriter)
- -- * Zio struct: no more name string, added Lua state for reader
- -- (however, Yueliang readers do not require a Lua state)
- ----------------------------------------------------------------------]]
- local luaZ = {}
- ------------------------------------------------------------------------
- -- * reader() should return a string, or nil if nothing else to parse.
- -- Additional data can be set only during stream initialization
- -- * Readers are handled in lauxlib.c, see luaL_load(file|buffer|string)
- -- * LUAL_BUFFERSIZE=BUFSIZ=512 in make_getF() (located in luaconf.h)
- -- * Original Reader typedef:
- -- const char * (*lua_Reader) (lua_State *L, void *ud, size_t *sz);
- -- * This Lua chunk reader implementation:
- -- returns string or nil, no arguments to function
- ------------------------------------------------------------------------
- ------------------------------------------------------------------------
- -- create a chunk reader from a source string
- ------------------------------------------------------------------------
- function luaZ:make_getS(buff)
- local b = buff
- return function() -- chunk reader anonymous function here
- if not b then return nil end
- local data = b
- b = nil
- return data
- end
- end
- ------------------------------------------------------------------------
- -- create a chunk reader from a source file
- ------------------------------------------------------------------------
- --[[
- function luaZ:make_getF(filename)
- local LUAL_BUFFERSIZE = 512
- local h = io.open(filename, "r")
- if not h then return nil end
- return function() -- chunk reader anonymous function here
- if not h or io.type(h) == "closed file" then return nil end
- local buff = h:read(LUAL_BUFFERSIZE)
- if not buff then h:close(); h = nil end
- return buff
- end
- end
- --]]
- ------------------------------------------------------------------------
- -- creates a zio input stream
- -- returns the ZIO structure, z
- ------------------------------------------------------------------------
- function luaZ:init(reader, data, name)
- if not reader then return end
- local z = {}
- z.reader = reader
- z.data = data or ""
- z.name = name
- -- set up additional data for reading
- if not data or data == "" then z.n = 0 else z.n = #data end
- z.p = 0
- return z
- end
- ------------------------------------------------------------------------
- -- fill up input buffer
- ------------------------------------------------------------------------
- function luaZ:fill(z)
- local buff = z.reader()
- z.data = buff
- if not buff or buff == "" then return "EOZ" end
- z.n, z.p = #buff - 1, 1
- return string.sub(buff, 1, 1)
- end
- ------------------------------------------------------------------------
- -- get next character from the input stream
- -- * local n, p are used to optimize code generation
- ------------------------------------------------------------------------
- function luaZ:zgetc(z)
- local n, p = z.n, z.p + 1
- if n > 0 then
- z.n, z.p = n - 1, p
- return string.sub(z.data, p, p)
- else
- return self:fill(z)
- end
- end
- return luaZ
- end
- fake_module_scripts[script] = module_script
- end
- do -- nil.LuaX
- local script = Instance.new('ModuleScript', nil)
- script.Name = "LuaX"
- local function module_script()
- --[[--------------------------------------------------------------------
- llex.lua
- Lua lexical analyzer in Lua
- This file is part of Yueliang.
- Copyright (c) 2005-2006 Kein-Hong Man <khman@users.sf.net>
- The COPYRIGHT file describes the conditions
- under which this software may be distributed.
- See the ChangeLog for more information.
- ----------------------------------------------------------------------]]
- --[[--------------------------------------------------------------------
- -- Notes:
- -- * intended to 'imitate' llex.c code; performance is not a concern
- -- * tokens are strings; code structure largely retained
- -- * deleted stuff (compared to llex.c) are noted, comments retained
- -- * nextc() returns the currently read character to simplify coding
- -- here; next() in llex.c does not return anything
- -- * compatibility code is marked with "--#" comments
- --
- -- Added:
- -- * luaX:chunkid (function luaO_chunkid from lobject.c)
- -- * luaX:str2d (function luaO_str2d from lobject.c)
- -- * luaX.LUA_QS used in luaX:lexerror (from luaconf.h)
- -- * luaX.LUA_COMPAT_LSTR in luaX:read_long_string (from luaconf.h)
- -- * luaX.MAX_INT used in luaX:inclinenumber (from llimits.h)
- --
- -- To use the lexer:
- -- (1) luaX:init() to initialize the lexer
- -- (2) luaX:setinput() to set the input stream to lex
- -- (3) call luaX:next() or luaX:luaX:lookahead() to get tokens,
- -- until "TK_EOS": luaX:next()
- -- * since EOZ is returned as a string, be careful when regexp testing
- --
- -- Not implemented:
- -- * luaX_newstring: not required by this Lua implementation
- -- * buffer MAX_SIZET size limit (from llimits.h) test not implemented
- -- in the interest of performance
- -- * locale-aware number handling is largely redundant as Lua's
- -- tonumber() function is already capable of this
- --
- -- Changed in 5.1.x:
- -- * TK_NAME token order moved down
- -- * string representation for TK_NAME, TK_NUMBER, TK_STRING changed
- -- * token struct renamed to lower case (LS -> ls)
- -- * LexState struct: removed nestlevel, added decpoint
- -- * error message functions have been greatly simplified
- -- * token2string renamed to luaX_tokens, exposed in llex.h
- -- * lexer now handles all kinds of newlines, including CRLF
- -- * shbang first line handling removed from luaX:setinput;
- -- it is now done in lauxlib.c (luaL_loadfile)
- -- * next(ls) macro renamed to nextc(ls) due to new luaX_next function
- -- * EXTRABUFF and MAXNOCHECK removed due to lexer changes
- -- * checkbuffer(ls, len) macro deleted
- -- * luaX:read_numeral now has 3 support functions: luaX:trydecpoint,
- -- luaX:buffreplace and (luaO_str2d from lobject.c) luaX:str2d
- -- * luaX:read_numeral is now more promiscuous in slurping characters;
- -- hexadecimal numbers was added, locale-aware decimal points too
- -- * luaX:skip_sep is new; used by luaX:read_long_string
- -- * luaX:read_long_string handles new-style long blocks, with some
- -- optional compatibility code
- -- * luaX:llex: parts changed to support new-style long blocks
- -- * luaX:llex: readname functionality has been folded in
- -- * luaX:llex: removed test for control characters
- --
- --------------------------------------------------------------------]]
- local luaZ = require(script.Parent.LuaZ)
- local luaX = {}
- -- FIRST_RESERVED is not required as tokens are manipulated as strings
- -- TOKEN_LEN deleted; maximum length of a reserved word not needed
- ------------------------------------------------------------------------
- -- "ORDER RESERVED" deleted; enumeration in one place: luaX.RESERVED
- ------------------------------------------------------------------------
- -- terminal symbols denoted by reserved words: TK_AND to TK_WHILE
- -- other terminal symbols: TK_NAME to TK_EOS
- luaX.RESERVED = [[
- TK_AND and
- TK_BREAK break
- TK_DO do
- TK_ELSE else
- TK_ELSEIF elseif
- TK_END end
- TK_FALSE false
- TK_FOR for
- TK_FUNCTION function
- TK_IF if
- TK_IN in
- TK_LOCAL local
- TK_NIL nil
- TK_NOT not
- TK_OR or
- TK_REPEAT repeat
- TK_RETURN return
- TK_THEN then
- TK_TRUE true
- TK_UNTIL until
- TK_WHILE while
- TK_CONCAT ..
- TK_DOTS ...
- TK_EQ ==
- TK_GE >=
- TK_LE <=
- TK_NE ~=
- TK_NAME <name>
- TK_NUMBER <number>
- TK_STRING <string>
- TK_EOS <eof>]]
- -- NUM_RESERVED is not required; number of reserved words
- --[[--------------------------------------------------------------------
- -- Instead of passing seminfo, the Token struct (e.g. ls.t) is passed
- -- so that lexer functions can use its table element, ls.t.seminfo
- --
- -- SemInfo (struct no longer needed, a mixed-type value is used)
- --
- -- Token (struct of ls.t and ls.lookahead):
- -- token -- token symbol
- -- seminfo -- semantics information
- --
- -- LexState (struct of ls; ls is initialized by luaX:setinput):
- -- current -- current character (charint)
- -- linenumber -- input line counter
- -- lastline -- line of last token 'consumed'
- -- t -- current token (table: struct Token)
- -- lookahead -- look ahead token (table: struct Token)
- -- fs -- 'FuncState' is private to the parser
- -- L -- LuaState
- -- z -- input stream
- -- buff -- buffer for tokens
- -- source -- current source name
- -- decpoint -- locale decimal point
- -- nestlevel -- level of nested non-terminals
- ----------------------------------------------------------------------]]
- -- luaX.tokens (was luaX_tokens) is now a hash; see luaX:init
- luaX.MAXSRC = 80
- luaX.MAX_INT = 2147483645 -- constants from elsewhere (see above)
- luaX.LUA_QS = "'%s'"
- luaX.LUA_COMPAT_LSTR = 1
- --luaX.MAX_SIZET = 4294967293
- ------------------------------------------------------------------------
- -- initialize lexer
- -- * original luaX_init has code to create and register token strings
- -- * luaX.tokens: TK_* -> token
- -- * luaX.enums: token -> TK_* (used in luaX:llex)
- ------------------------------------------------------------------------
- function luaX:init()
- local tokens, enums = {}, {}
- for v in string.gmatch(self.RESERVED, "[^\n]+") do
- local _, _, tok, str = string.find(v, "(%S+)%s+(%S+)")
- tokens[tok] = str
- enums[str] = tok
- end
- self.tokens = tokens
- self.enums = enums
- end
- ------------------------------------------------------------------------
- -- returns a suitably-formatted chunk name or id
- -- * from lobject.c, used in llex.c and ldebug.c
- -- * the result, out, is returned (was first argument)
- ------------------------------------------------------------------------
- function luaX:chunkid(source, bufflen)
- local out
- local first = string.sub(source, 1, 1)
- if first == "=" then
- out = string.sub(source, 2, bufflen) -- remove first char
- else -- out = "source", or "...source"
- if first == "@" then
- source = string.sub(source, 2) -- skip the '@'
- bufflen = bufflen - #" '...' "
- local l = #source
- out = ""
- if l > bufflen then
- source = string.sub(source, 1 + l - bufflen) -- get last part of file name
- out = out.."..."
- end
- out = out..source
- else -- out = [string "string"]
- local len = string.find(source, "[\n\r]") -- stop at first newline
- len = len and (len - 1) or #source
- bufflen = bufflen - #(" [string \"...\"] ")
- if len > bufflen then len = bufflen end
- out = "[string \""
- if len < #source then -- must truncate?
- out = out..string.sub(source, 1, len).."..."
- else
- out = out..source
- end
- out = out.."\"]"
- end
- end
- return out
- end
- --[[--------------------------------------------------------------------
- -- Support functions for lexer
- -- * all lexer errors eventually reaches lexerror:
- syntaxerror -> lexerror
- ----------------------------------------------------------------------]]
- ------------------------------------------------------------------------
- -- look up token and return keyword if found (also called by parser)
- ------------------------------------------------------------------------
- function luaX:token2str(ls, token)
- if string.sub(token, 1, 3) ~= "TK_" then
- if string.find(token, "%c") then
- return string.format("char(%d)", string.byte(token))
- end
- return token
- else
- end
- return self.tokens[token]
- end
- ------------------------------------------------------------------------
- -- throws a lexer error
- -- * txtToken has been made local to luaX:lexerror
- -- * can't communicate LUA_ERRSYNTAX, so it is unimplemented
- ------------------------------------------------------------------------
- function luaX:lexerror(ls, msg, token)
- local function txtToken(ls, token)
- if token == "TK_NAME" or
- token == "TK_STRING" or
- token == "TK_NUMBER" then
- return ls.buff
- else
- return self:token2str(ls, token)
- end
- end
- local buff = self:chunkid(ls.source, self.MAXSRC)
- local msg = string.format("%s:%d: %s", buff, ls.linenumber, msg)
- if token then
- msg = string.format("%s near "..self.LUA_QS, msg, txtToken(ls, token))
- end
- -- luaD_throw(ls->L, LUA_ERRSYNTAX)
- error(msg)
- end
- ------------------------------------------------------------------------
- -- throws a syntax error (mainly called by parser)
- -- * ls.t.token has to be set by the function calling luaX:llex
- -- (see luaX:next and luaX:lookahead elsewhere in this file)
- ------------------------------------------------------------------------
- function luaX:syntaxerror(ls, msg)
- self:lexerror(ls, msg, ls.t.token)
- end
- ------------------------------------------------------------------------
- -- move on to next line
- ------------------------------------------------------------------------
- function luaX:currIsNewline(ls)
- return ls.current == "\n" or ls.current == "\r"
- end
- function luaX:inclinenumber(ls)
- local old = ls.current
- -- lua_assert(currIsNewline(ls))
- self:nextc(ls) -- skip '\n' or '\r'
- if self:currIsNewline(ls) and ls.current ~= old then
- self:nextc(ls) -- skip '\n\r' or '\r\n'
- end
- ls.linenumber = ls.linenumber + 1
- if ls.linenumber >= self.MAX_INT then
- self:syntaxerror(ls, "chunk has too many lines")
- end
- end
- ------------------------------------------------------------------------
- -- initializes an input stream for lexing
- -- * if ls (the lexer state) is passed as a table, then it is filled in,
- -- otherwise it has to be retrieved as a return value
- -- * LUA_MINBUFFER not used; buffer handling not required any more
- ------------------------------------------------------------------------
- function luaX:setinput(L, ls, z, source)
- if not ls then ls = {} end -- create struct
- if not ls.lookahead then ls.lookahead = {} end
- if not ls.t then ls.t = {} end
- ls.decpoint = "."
- ls.L = L
- ls.lookahead.token = "TK_EOS" -- no look-ahead token
- ls.z = z
- ls.fs = nil
- ls.linenumber = 1
- ls.lastline = 1
- ls.source = source
- self:nextc(ls) -- read first char
- end
- --[[--------------------------------------------------------------------
- -- LEXICAL ANALYZER
- ----------------------------------------------------------------------]]
- ------------------------------------------------------------------------
- -- checks if current character read is found in the set 'set'
- ------------------------------------------------------------------------
- function luaX:check_next(ls, set)
- if not string.find(set, ls.current, 1, 1) then
- return false
- end
- self:save_and_next(ls)
- return true
- end
- ------------------------------------------------------------------------
- -- retrieve next token, checking the lookahead buffer if necessary
- -- * note that the macro next(ls) in llex.c is now luaX:nextc
- -- * utilized used in lparser.c (various places)
- ------------------------------------------------------------------------
- function luaX:next(ls)
- ls.lastline = ls.linenumber
- if ls.lookahead.token ~= "TK_EOS" then -- is there a look-ahead token?
- -- this must be copy-by-value
- ls.t.seminfo = ls.lookahead.seminfo -- use this one
- ls.t.token = ls.lookahead.token
- ls.lookahead.token = "TK_EOS" -- and discharge it
- else
- ls.t.token = self:llex(ls, ls.t) -- read next token
- end
- end
- ------------------------------------------------------------------------
- -- fill in the lookahead buffer
- -- * utilized used in lparser.c:constructor
- ------------------------------------------------------------------------
- function luaX:lookahead(ls)
- -- lua_assert(ls.lookahead.token == "TK_EOS")
- ls.lookahead.token = self:llex(ls, ls.lookahead)
- end
- ------------------------------------------------------------------------
- -- gets the next character and returns it
- -- * this is the next() macro in llex.c; see notes at the beginning
- ------------------------------------------------------------------------
- function luaX:nextc(ls)
- local c = luaZ:zgetc(ls.z)
- ls.current = c
- return c
- end
- ------------------------------------------------------------------------
- -- saves the given character into the token buffer
- -- * buffer handling code removed, not used in this implementation
- -- * test for maximum token buffer length not used, makes things faster
- ------------------------------------------------------------------------
- function luaX:save(ls, c)
- local buff = ls.buff
- -- if you want to use this, please uncomment luaX.MAX_SIZET further up
- --if #buff > self.MAX_SIZET then
- -- self:lexerror(ls, "lexical element too long")
- --end
- ls.buff = buff..c
- end
- ------------------------------------------------------------------------
- -- save current character into token buffer, grabs next character
- -- * like luaX:nextc, returns the character read for convenience
- ------------------------------------------------------------------------
- function luaX:save_and_next(ls)
- self:save(ls, ls.current)
- return self:nextc(ls)
- end
- ------------------------------------------------------------------------
- -- LUA_NUMBER
- -- * luaX:read_numeral is the main lexer function to read a number
- -- * luaX:str2d, luaX:buffreplace, luaX:trydecpoint are support functions
- ------------------------------------------------------------------------
- ------------------------------------------------------------------------
- -- string to number converter (was luaO_str2d from lobject.c)
- -- * returns the number, nil if fails (originally returns a boolean)
- -- * conversion function originally lua_str2number(s,p), a macro which
- -- maps to the strtod() function by default (from luaconf.h)
- ------------------------------------------------------------------------
- function luaX:str2d(s)
- local result = tonumber(s)
- if result then return result end
- -- conversion failed
- if string.lower(string.sub(s, 1, 2)) == "0x" then -- maybe an hexadecimal constant?
- result = tonumber(s, 16)
- if result then return result end -- most common case
- -- Was: invalid trailing characters?
- -- In C, this function then skips over trailing spaces.
- -- true is returned if nothing else is found except for spaces.
- -- If there is still something else, then it returns a false.
- -- All this is not necessary using Lua's tonumber.
- end
- return nil
- end
- ------------------------------------------------------------------------
- -- single-character replacement, for locale-aware decimal points
- ------------------------------------------------------------------------
- function luaX:buffreplace(ls, from, to)
- local result, buff = "", ls.buff
- for p = 1, #buff do
- local c = string.sub(buff, p, p)
- if c == from then c = to end
- result = result..c
- end
- ls.buff = result
- end
- ------------------------------------------------------------------------
- -- Attempt to convert a number by translating '.' decimal points to
- -- the decimal point character used by the current locale. This is not
- -- needed in Yueliang as Lua's tonumber() is already locale-aware.
- -- Instead, the code is here in case the user implements localeconv().
- ------------------------------------------------------------------------
- function luaX:trydecpoint(ls, Token)
- -- format error: try to update decimal point separator
- local old = ls.decpoint
- -- translate the following to Lua if you implement localeconv():
- -- struct lconv *cv = localeconv();
- -- ls->decpoint = (cv ? cv->decimal_point[0] : '.');
- self:buffreplace(ls, old, ls.decpoint) -- try updated decimal separator
- local seminfo = self:str2d(ls.buff)
- Token.seminfo = seminfo
- if not seminfo then
- -- format error with correct decimal point: no more options
- self:buffreplace(ls, ls.decpoint, ".") -- undo change (for error message)
- self:lexerror(ls, "malformed number", "TK_NUMBER")
- end
- end
- ------------------------------------------------------------------------
- -- main number conversion function
- -- * "^%w$" needed in the scan in order to detect "EOZ"
- ------------------------------------------------------------------------
- function luaX:read_numeral(ls, Token)
- -- lua_assert(string.find(ls.current, "%d"))
- repeat
- self:save_and_next(ls)
- until string.find(ls.current, "%D") and ls.current ~= "."
- if self:check_next(ls, "Ee") then -- 'E'?
- self:check_next(ls, "+-") -- optional exponent sign
- end
- while string.find(ls.current, "^%w$") or ls.current == "_" do
- self:save_and_next(ls)
- end
- self:buffreplace(ls, ".", ls.decpoint) -- follow locale for decimal point
- local seminfo = self:str2d(ls.buff)
- Token.seminfo = seminfo
- if not seminfo then -- format error?
- self:trydecpoint(ls, Token) -- try to update decimal point separator
- end
- end
- ------------------------------------------------------------------------
- -- count separators ("=") in a long string delimiter
- -- * used by luaX:read_long_string
- ------------------------------------------------------------------------
- function luaX:skip_sep(ls)
- local count = 0
- local s = ls.current
- -- lua_assert(s == "[" or s == "]")
- self:save_and_next(ls)
- while ls.current == "=" do
- self:save_and_next(ls)
- count = count + 1
- end
- return (ls.current == s) and count or (-count) - 1
- end
- ------------------------------------------------------------------------
- -- reads a long string or long comment
- ------------------------------------------------------------------------
- function luaX:read_long_string(ls, Token, sep)
- local cont = 0
- self:save_and_next(ls) -- skip 2nd '['
- if self:currIsNewline(ls) then -- string starts with a newline?
- self:inclinenumber(ls) -- skip it
- end
- while true do
- local c = ls.current
- if c == "EOZ" then
- self:lexerror(ls, Token and "unfinished long string" or
- "unfinished long comment", "TK_EOS")
- elseif c == "[" then
- --# compatibility code start
- if self.LUA_COMPAT_LSTR then
- if self:skip_sep(ls) == sep then
- self:save_and_next(ls) -- skip 2nd '['
- cont = cont + 1
- --# compatibility code start
- if self.LUA_COMPAT_LSTR == 1 then
- if sep == 0 then
- self:lexerror(ls, "nesting of [[...]] is deprecated", "[")
- end
- end
- --# compatibility code end
- end
- end
- --# compatibility code end
- elseif c == "]" then
- if self:skip_sep(ls) == sep then
- self:save_and_next(ls) -- skip 2nd ']'
- --# compatibility code start
- if self.LUA_COMPAT_LSTR and self.LUA_COMPAT_LSTR == 2 then
- cont = cont - 1
- if sep == 0 and cont >= 0 then break end
- end
- --# compatibility code end
- break
- end
- elseif self:currIsNewline(ls) then
- self:save(ls, "\n")
- self:inclinenumber(ls)
- if not Token then ls.buff = "" end -- avoid wasting space
- else -- default
- if Token then
- self:save_and_next(ls)
- else
- self:nextc(ls)
- end
- end--if c
- end--while
- if Token then
- local p = 3 + sep
- Token.seminfo = string.sub(ls.buff, p, -p)
- end
- end
- ------------------------------------------------------------------------
- -- reads a string
- -- * has been restructured significantly compared to the original C code
- ------------------------------------------------------------------------
- function luaX:read_string(ls, del, Token)
- self:save_and_next(ls)
- while ls.current ~= del do
- local c = ls.current
- if c == "EOZ" then
- self:lexerror(ls, "unfinished string", "TK_EOS")
- elseif self:currIsNewline(ls) then
- self:lexerror(ls, "unfinished string", "TK_STRING")
- elseif c == "\\" then
- c = self:nextc(ls) -- do not save the '\'
- if self:currIsNewline(ls) then -- go through
- self:save(ls, "\n")
- self:inclinenumber(ls)
- elseif c ~= "EOZ" then -- will raise an error next loop
- -- escapes handling greatly simplified here:
- local i = string.find("abfnrtv", c, 1, 1)
- if i then
- self:save(ls, string.sub("\a\b\f\n\r\t\v", i, i))
- self:nextc(ls)
- elseif not string.find(c, "%d") then
- self:save_and_next(ls) -- handles \\, \", \', and \?
- else -- \xxx
- c, i = 0, 0
- repeat
- c = 10 * c + ls.current
- self:nextc(ls)
- i = i + 1
- until i >= 3 or not string.find(ls.current, "%d")
- if c > 255 then -- UCHAR_MAX
- self:lexerror(ls, "escape sequence too large", "TK_STRING")
- end
- self:save(ls, string.char(c))
- end
- end
- else
- self:save_and_next(ls)
- end--if c
- end--while
- self:save_and_next(ls) -- skip delimiter
- Token.seminfo = string.sub(ls.buff, 2, -2)
- end
- ------------------------------------------------------------------------
- -- main lexer function
- ------------------------------------------------------------------------
- function luaX:llex(ls, Token)
- ls.buff = ""
- while true do
- local c = ls.current
- ----------------------------------------------------------------
- if self:currIsNewline(ls) then
- self:inclinenumber(ls)
- ----------------------------------------------------------------
- elseif c == "-" then
- c = self:nextc(ls)
- if c ~= "-" then return "-" end
- -- else is a comment
- local sep = -1
- if self:nextc(ls) == '[' then
- sep = self:skip_sep(ls)
- ls.buff = "" -- 'skip_sep' may dirty the buffer
- end
- if sep >= 0 then
- self:read_long_string(ls, nil, sep) -- long comment
- ls.buff = ""
- else -- else short comment
- while not self:currIsNewline(ls) and ls.current ~= "EOZ" do
- self:nextc(ls)
- end
- end
- ----------------------------------------------------------------
- elseif c == "[" then
- local sep = self:skip_sep(ls)
- if sep >= 0 then
- self:read_long_string(ls, Token, sep)
- return "TK_STRING"
- elseif sep == -1 then
- return "["
- else
- self:lexerror(ls, "invalid long string delimiter", "TK_STRING")
- end
- ----------------------------------------------------------------
- elseif c == "=" then
- c = self:nextc(ls)
- if c ~= "=" then return "="
- else self:nextc(ls); return "TK_EQ" end
- ----------------------------------------------------------------
- elseif c == "<" then
- c = self:nextc(ls)
- if c ~= "=" then return "<"
- else self:nextc(ls); return "TK_LE" end
- ----------------------------------------------------------------
- elseif c == ">" then
- c = self:nextc(ls)
- if c ~= "=" then return ">"
- else self:nextc(ls); return "TK_GE" end
- ----------------------------------------------------------------
- elseif c == "~" then
- c = self:nextc(ls)
- if c ~= "=" then return "~"
- else self:nextc(ls); return "TK_NE" end
- ----------------------------------------------------------------
- elseif c == "\"" or c == "'" then
- self:read_string(ls, c, Token)
- return "TK_STRING"
- ----------------------------------------------------------------
- elseif c == "." then
- c = self:save_and_next(ls)
- if self:check_next(ls, ".") then
- if self:check_next(ls, ".") then
- return "TK_DOTS" -- ...
- else return "TK_CONCAT" -- ..
- end
- elseif not string.find(c, "%d") then
- return "."
- else
- self:read_numeral(ls, Token)
- return "TK_NUMBER"
- end
- ----------------------------------------------------------------
- elseif c == "EOZ" then
- return "TK_EOS"
- ----------------------------------------------------------------
- else -- default
- if string.find(c, "%s") then
- -- lua_assert(self:currIsNewline(ls))
- self:nextc(ls)
- elseif string.find(c, "%d") then
- self:read_numeral(ls, Token)
- return "TK_NUMBER"
- elseif string.find(c, "[_%a]") then
- -- identifier or reserved word
- repeat
- c = self:save_and_next(ls)
- until c == "EOZ" or not string.find(c, "[_%w]")
- local ts = ls.buff
- local tok = self.enums[ts]
- if tok then return tok end -- reserved word?
- Token.seminfo = ts
- return "TK_NAME"
- else
- self:nextc(ls)
- return c -- single-char tokens (+ - / ...)
- end
- ----------------------------------------------------------------
- end--if c
- end--while
- end
- return luaX
- end
- fake_module_scripts[script] = module_script
- end
- do -- nil.LuaY
- local script = Instance.new('ModuleScript', nil)
- script.Name = "LuaY"
- local function module_script()
- --[[--------------------------------------------------------------------
- lparser.lua
- Lua 5 parser in Lua
- This file is part of Yueliang.
- Copyright (c) 2005-2007 Kein-Hong Man <khman@users.sf.net>
- The COPYRIGHT file describes the conditions
- under which this software may be distributed.
- See the ChangeLog for more information.
- ----------------------------------------------------------------------]]
- --[[--------------------------------------------------------------------
- -- Notes:
- -- * some unused C code that were not converted are kept as comments
- -- * LUA_COMPAT_VARARG option changed into a comment block
- -- * for value/size specific code added, look for 'NOTE: '
- --
- -- Not implemented:
- -- * luaX_newstring not needed by this Lua implementation
- -- * luaG_checkcode() in assert is not currently implemented
- --
- -- Added:
- -- * some constants added from various header files
- -- * luaY.LUA_QS used in error_expected, check_match (from luaconf.h)
- -- * luaY:LUA_QL needed for error messages (from luaconf.h)
- -- * luaY:growvector (from lmem.h) -- skeleton only, limit checking
- -- * luaY.SHRT_MAX (from <limits.h>) for registerlocalvar
- -- * luaY:newproto (from lfunc.c)
- -- * luaY:int2fb (from lobject.c)
- -- * NOTE: HASARG_MASK, for implementing a VARARG_HASARG bit operation
- -- * NOTE: value-specific code for VARARG_NEEDSARG to replace a bitop
- --
- -- Changed in 5.1.x:
- -- * various code changes are not detailed...
- -- * names of constants may have changed, e.g. added a LUAI_ prefix
- -- * struct expkind: added VKNUM, VVARARG; VCALL's info changed?
- -- * struct expdesc: added nval
- -- * struct FuncState: upvalues data type changed to upvaldesc
- -- * macro hasmultret is new
- -- * function checklimit moved to parser from lexer
- -- * functions anchor_token, errorlimit, checknext are new
- -- * checknext is new, equivalent to 5.0.x's check, see check too
- -- * luaY:next and luaY:lookahead moved to lexer
- -- * break keyword no longer skipped in luaY:breakstat
- -- * function new_localvarstr replaced by new_localvarliteral
- -- * registerlocalvar limits local variables to SHRT_MAX
- -- * create_local deleted, new_localvarliteral used instead
- -- * constant LUAI_MAXUPVALUES increased to 60
- -- * constants MAXPARAMS, LUA_MAXPARSERLEVEL, MAXSTACK removed
- -- * function interface changed: singlevaraux, singlevar
- -- * enterlevel and leavelevel uses nCcalls to track call depth
- -- * added a name argument to main entry function, luaY:parser
- -- * function luaY_index changed to yindex
- -- * luaY:int2fb()'s table size encoding format has been changed
- -- * luaY:log2() no longer needed for table constructors
- -- * function code_params deleted, functionality folded in parlist
- -- * vararg flags handling (is_vararg) changes; also see VARARG_*
- -- * LUA_COMPATUPSYNTAX section for old-style upvalues removed
- -- * repeatstat() calls chunk() instead of block()
- -- * function interface changed: cond, test_then_block
- -- * while statement implementation considerably simplified; MAXEXPWHILE
- -- and EXTRAEXP no longer required, no limits to the complexity of a
- -- while condition
- -- * repeat, forbody statement implementation has major changes,
- -- mostly due to new scoping behaviour of local variables
- -- * OPR_MULT renamed to OPR_MUL
- ----------------------------------------------------------------------]]
- --requires luaP, luaX, luaK
- local luaY = {}
- local luaX = require(script.Parent.LuaX)
- local luaK = require(script.Parent.LuaK)(luaY)
- local luaP = require(script.Parent.LuaP)
- --[[--------------------------------------------------------------------
- -- Expression descriptor
- -- * expkind changed to string constants; luaY:assignment was the only
- -- function to use a relational operator with this enumeration
- -- VVOID -- no value
- -- VNIL -- no value
- -- VTRUE -- no value
- -- VFALSE -- no value
- -- VK -- info = index of constant in 'k'
- -- VKNUM -- nval = numerical value
- -- VLOCAL -- info = local register
- -- VUPVAL, -- info = index of upvalue in 'upvalues'
- -- VGLOBAL -- info = index of table; aux = index of global name in 'k'
- -- VINDEXED -- info = table register; aux = index register (or 'k')
- -- VJMP -- info = instruction pc
- -- VRELOCABLE -- info = instruction pc
- -- VNONRELOC -- info = result register
- -- VCALL -- info = instruction pc
- -- VVARARG -- info = instruction pc
- } ----------------------------------------------------------------------]]
- --[[--------------------------------------------------------------------
- -- * expdesc in Lua 5.1.x has a union u and another struct s; this Lua
- -- implementation ignores all instances of u and s usage
- -- struct expdesc:
- -- k -- (enum: expkind)
- -- info, aux -- (int, int)
- -- nval -- (lua_Number)
- -- t -- patch list of 'exit when true'
- -- f -- patch list of 'exit when false'
- ----------------------------------------------------------------------]]
- --[[--------------------------------------------------------------------
- -- struct upvaldesc:
- -- k -- (lu_byte)
- -- info -- (lu_byte)
- ----------------------------------------------------------------------]]
- --[[--------------------------------------------------------------------
- -- state needed to generate code for a given function
- -- struct FuncState:
- -- f -- current function header (table: Proto)
- -- h -- table to find (and reuse) elements in 'k' (table: Table)
- -- prev -- enclosing function (table: FuncState)
- -- ls -- lexical state (table: LexState)
- -- L -- copy of the Lua state (table: lua_State)
- -- bl -- chain of current blocks (table: BlockCnt)
- -- pc -- next position to code (equivalent to 'ncode')
- -- lasttarget -- 'pc' of last 'jump target'
- -- jpc -- list of pending jumps to 'pc'
- -- freereg -- first free register
- -- nk -- number of elements in 'k'
- -- np -- number of elements in 'p'
- -- nlocvars -- number of elements in 'locvars'
- -- nactvar -- number of active local variables
- -- upvalues[LUAI_MAXUPVALUES] -- upvalues (table: upvaldesc)
- -- actvar[LUAI_MAXVARS] -- declared-variable stack
- ----------------------------------------------------------------------]]
- ------------------------------------------------------------------------
- -- constants used by parser
- -- * picks up duplicate values from luaX if required
- ------------------------------------------------------------------------
- luaY.LUA_QS = luaX.LUA_QS or "'%s'" -- (from luaconf.h)
- luaY.SHRT_MAX = 32767 -- (from <limits.h>)
- luaY.LUAI_MAXVARS = 200 -- (luaconf.h)
- luaY.LUAI_MAXUPVALUES = 60 -- (luaconf.h)
- luaY.MAX_INT = luaX.MAX_INT or 2147483645 -- (from llimits.h)
- -- * INT_MAX-2 for 32-bit systems
- luaY.LUAI_MAXCCALLS = 200 -- (from luaconf.h)
- luaY.VARARG_HASARG = 1 -- (from lobject.h)
- -- NOTE: HASARG_MASK is value-specific
- luaY.HASARG_MASK = 2 -- this was added for a bitop in parlist()
- luaY.VARARG_ISVARARG = 2
- -- NOTE: there is some value-specific code that involves VARARG_NEEDSARG
- luaY.VARARG_NEEDSARG = 4
- luaY.LUA_MULTRET = -1 -- (lua.h)
- --[[--------------------------------------------------------------------
- -- other functions
- ----------------------------------------------------------------------]]
- ------------------------------------------------------------------------
- -- LUA_QL describes how error messages quote program elements.
- -- CHANGE it if you want a different appearance. (from luaconf.h)
- ------------------------------------------------------------------------
- function luaY:LUA_QL(x)
- return "'"..x.."'"
- end
- ------------------------------------------------------------------------
- -- this is a stripped-down luaM_growvector (from lmem.h) which is a
- -- macro based on luaM_growaux (in lmem.c); all the following does is
- -- reproduce the size limit checking logic of the original function
- -- so that error behaviour is identical; all arguments preserved for
- -- convenience, even those which are unused
- -- * set the t field to nil, since this originally does a sizeof(t)
- -- * size (originally a pointer) is never updated, their final values
- -- are set by luaY:close_func(), so overall things should still work
- ------------------------------------------------------------------------
- function luaY:growvector(L, v, nelems, size, t, limit, e)
- if nelems >= limit then
- error(e) -- was luaG_runerror
- end
- end
- ------------------------------------------------------------------------
- -- initialize a new function prototype structure (from lfunc.c)
- -- * used only in open_func()
- ------------------------------------------------------------------------
- function luaY:newproto(L)
- local f = {} -- Proto
- -- luaC_link(L, obj2gco(f), LUA_TPROTO); /* GC */
- f.k = {}
- f.sizek = 0
- f.p = {}
- f.sizep = 0
- f.code = {}
- f.sizecode = 0
- f.sizelineinfo = 0
- f.sizeupvalues = 0
- f.nups = 0
- f.upvalues = {}
- f.numparams = 0
- f.is_vararg = 0
- f.maxstacksize = 0
- f.lineinfo = {}
- f.sizelocvars = 0
- f.locvars = {}
- f.lineDefined = 0
- f.lastlinedefined = 0
- f.source = nil
- return f
- end
- ------------------------------------------------------------------------
- -- converts an integer to a "floating point byte", represented as
- -- (eeeeexxx), where the real value is (1xxx) * 2^(eeeee - 1) if
- -- eeeee != 0 and (xxx) otherwise.
- ------------------------------------------------------------------------
- function luaY:int2fb(x)
- local e = 0 -- exponent
- while x >= 16 do
- x = math.floor((x + 1) / 2)
- e = e + 1
- end
- if x < 8 then
- return x
- else
- return ((e + 1) * 8) + (x - 8)
- end
- end
- --[[--------------------------------------------------------------------
- -- parser functions
- ----------------------------------------------------------------------]]
- ------------------------------------------------------------------------
- -- true of the kind of expression produces multiple return values
- ------------------------------------------------------------------------
- function luaY:hasmultret(k)
- return k == "VCALL" or k == "VVARARG"
- end
- ------------------------------------------------------------------------
- -- convenience function to access active local i, returns entry
- ------------------------------------------------------------------------
- function luaY:getlocvar(fs, i)
- return fs.f.locvars[ fs.actvar[i] ]
- end
- ------------------------------------------------------------------------
- -- check a limit, string m provided as an error message
- ------------------------------------------------------------------------
- function luaY:checklimit(fs, v, l, m)
- if v > l then self:errorlimit(fs, l, m) end
- end
- --[[--------------------------------------------------------------------
- -- nodes for block list (list of active blocks)
- -- struct BlockCnt:
- -- previous -- chain (table: BlockCnt)
- -- breaklist -- list of jumps out of this loop
- -- nactvar -- # active local variables outside the breakable structure
- -- upval -- true if some variable in the block is an upvalue (boolean)
- -- isbreakable -- true if 'block' is a loop (boolean)
- ----------------------------------------------------------------------]]
- ------------------------------------------------------------------------
- -- prototypes for recursive non-terminal functions
- ------------------------------------------------------------------------
- -- prototypes deleted; not required in Lua
- ------------------------------------------------------------------------
- -- reanchor if last token is has a constant string, see close_func()
- -- * used only in close_func()
- ------------------------------------------------------------------------
- function luaY:anchor_token(ls)
- if ls.t.token == "TK_NAME" or ls.t.token == "TK_STRING" then
- -- not relevant to Lua implementation of parser
- -- local ts = ls.t.seminfo
- -- luaX_newstring(ls, getstr(ts), ts->tsv.len); /* C */
- end
- end
- ------------------------------------------------------------------------
- -- throws a syntax error if token expected is not there
- ------------------------------------------------------------------------
- function luaY:error_expected(ls, token)
- luaX:syntaxerror(ls,
- string.format(self.LUA_QS.." expected", luaX:token2str(ls, token)))
- end
- ------------------------------------------------------------------------
- -- prepares error message for display, for limits exceeded
- -- * used only in checklimit()
- ------------------------------------------------------------------------
- function luaY:errorlimit(fs, limit, what)
- local msg = (fs.f.linedefined == 0) and
- string.format("main function has more than %d %s", limit, what) or
- string.format("function at line %d has more than %d %s",
- fs.f.linedefined, limit, what)
- luaX:lexerror(fs.ls, msg, 0)
- end
- ------------------------------------------------------------------------
- -- tests for a token, returns outcome
- -- * return value changed to boolean
- ------------------------------------------------------------------------
- function luaY:testnext(ls, c)
- if ls.t.token == c then
- luaX:next(ls)
- return true
- else
- return false
- end
- end
- ------------------------------------------------------------------------
- -- check for existence of a token, throws error if not found
- ------------------------------------------------------------------------
- function luaY:check(ls, c)
- if ls.t.token ~= c then
- self:error_expected(ls, c)
- end
- end
- ------------------------------------------------------------------------
- -- verify existence of a token, then skip it
- ------------------------------------------------------------------------
- function luaY:checknext(ls, c)
- self:check(ls, c)
- luaX:next(ls)
- end
- ------------------------------------------------------------------------
- -- throws error if condition not matched
- ------------------------------------------------------------------------
- function luaY:check_condition(ls, c, msg)
- if not c then luaX:syntaxerror(ls, msg) end
- end
- ------------------------------------------------------------------------
- -- verifies token conditions are met or else throw error
- ------------------------------------------------------------------------
- function luaY:check_match(ls, what, who, where)
- if not self:testnext(ls, what) then
- if where == ls.linenumber then
- self:error_expected(ls, what)
- else
- luaX:syntaxerror(ls, string.format(
- self.LUA_QS.." expected (to close "..self.LUA_QS.." at line %d)",
- luaX:token2str(ls, what), luaX:token2str(ls, who), where))
- end
- end
- end
- ------------------------------------------------------------------------
- -- expect that token is a name, return the name
- ------------------------------------------------------------------------
- function luaY:str_checkname(ls)
- self:check(ls, "TK_NAME")
- local ts = ls.t.seminfo
- luaX:next(ls)
- return ts
- end
- ------------------------------------------------------------------------
- -- initialize a struct expdesc, expression description data structure
- ------------------------------------------------------------------------
- function luaY:init_exp(e, k, i)
- e.f, e.t = luaK.NO_JUMP, luaK.NO_JUMP
- e.k = k
- e.info = i
- end
- ------------------------------------------------------------------------
- -- adds given string s in string pool, sets e as VK
- ------------------------------------------------------------------------
- function luaY:codestring(ls, e, s)
- self:init_exp(e, "VK", luaK:stringK(ls.fs, s))
- end
- ------------------------------------------------------------------------
- -- consume a name token, adds it to string pool, sets e as VK
- ------------------------------------------------------------------------
- function luaY:checkname(ls, e)
- self:codestring(ls, e, self:str_checkname(ls))
- end
- ------------------------------------------------------------------------
- -- creates struct entry for a local variable
- -- * used only in new_localvar()
- ------------------------------------------------------------------------
- function luaY:registerlocalvar(ls, varname)
- local fs = ls.fs
- local f = fs.f
- self:growvector(ls.L, f.locvars, fs.nlocvars, f.sizelocvars,
- nil, self.SHRT_MAX, "too many local variables")
- -- loop to initialize empty f.locvar positions not required
- f.locvars[fs.nlocvars] = {} -- LocVar
- f.locvars[fs.nlocvars].varname = varname
- -- luaC_objbarrier(ls.L, f, varname) /* GC */
- local nlocvars = fs.nlocvars
- fs.nlocvars = fs.nlocvars + 1
- return nlocvars
- end
- ------------------------------------------------------------------------
- -- creates a new local variable given a name and an offset from nactvar
- -- * used in fornum(), forlist(), parlist(), body()
- ------------------------------------------------------------------------
- function luaY:new_localvarliteral(ls, v, n)
- self:new_localvar(ls, v, n)
- end
- ------------------------------------------------------------------------
- -- register a local variable, set in active variable list
- ------------------------------------------------------------------------
- function luaY:new_localvar(ls, name, n)
- local fs = ls.fs
- self:checklimit(fs, fs.nactvar + n + 1, self.LUAI_MAXVARS, "local variables")
- fs.actvar[fs.nactvar + n] = self:registerlocalvar(ls, name)
- end
- ------------------------------------------------------------------------
- -- adds nvars number of new local variables, set debug information
- ------------------------------------------------------------------------
- function luaY:adjustlocalvars(ls, nvars)
- local fs = ls.fs
- fs.nactvar = fs.nactvar + nvars
- for i = nvars, 1, -1 do
- self:getlocvar(fs, fs.nactvar - i).startpc = fs.pc
- end
- end
- ------------------------------------------------------------------------
- -- removes a number of locals, set debug information
- ------------------------------------------------------------------------
- function luaY:removevars(ls, tolevel)
- local fs = ls.fs
- while fs.nactvar > tolevel do
- fs.nactvar = fs.nactvar - 1
- self:getlocvar(fs, fs.nactvar).endpc = fs.pc
- end
- end
- ------------------------------------------------------------------------
- -- returns an existing upvalue index based on the given name, or
- -- creates a new upvalue struct entry and returns the new index
- -- * used only in singlevaraux()
- ------------------------------------------------------------------------
- function luaY:indexupvalue(fs, name, v)
- local f = fs.f
- for i = 0, f.nups - 1 do
- if fs.upvalues[i].k == v.k and fs.upvalues[i].info == v.info then
- assert(f.upvalues[i] == name)
- return i
- end
- end
- -- new one
- self:checklimit(fs, f.nups + 1, self.LUAI_MAXUPVALUES, "upvalues")
- self:growvector(fs.L, f.upvalues, f.nups, f.sizeupvalues,
- nil, self.MAX_INT, "")
- -- loop to initialize empty f.upvalues positions not required
- f.upvalues[f.nups] = name
- -- luaC_objbarrier(fs->L, f, name); /* GC */
- assert(v.k == "VLOCAL" or v.k == "VUPVAL")
- -- this is a partial copy; only k & info fields used
- fs.upvalues[f.nups] = { k = v.k, info = v.info }
- local nups = f.nups
- f.nups = f.nups + 1
- return nups
- end
- ------------------------------------------------------------------------
- -- search the local variable namespace of the given fs for a match
- -- * used only in singlevaraux()
- ------------------------------------------------------------------------
- function luaY:searchvar(fs, n)
- for i = fs.nactvar - 1, 0, -1 do
- if n == self:getlocvar(fs, i).varname then
- return i
- end
- end
- return -1 -- not found
- end
- ------------------------------------------------------------------------
- -- * mark upvalue flags in function states up to a given level
- -- * used only in singlevaraux()
- ------------------------------------------------------------------------
- function luaY:markupval(fs, level)
- local bl = fs.bl
- while bl and bl.nactvar > level do bl = bl.previous end
- if bl then bl.upval = true end
- end
- ------------------------------------------------------------------------
- -- handle locals, globals and upvalues and related processing
- -- * search mechanism is recursive, calls itself to search parents
- -- * used only in singlevar()
- ------------------------------------------------------------------------
- function luaY:singlevaraux(fs, n, var, base)
- if fs == nil then -- no more levels?
- self:init_exp(var, "VGLOBAL", luaP.NO_REG) -- default is global variable
- return "VGLOBAL"
- else
- local v = self:searchvar(fs, n) -- look up at current level
- if v >= 0 then
- self:init_exp(var, "VLOCAL", v)
- if base == 0 then
- self:markupval(fs, v) -- local will be used as an upval
- end
- return "VLOCAL"
- else -- not found at current level; try upper one
- if self:singlevaraux(fs.prev, n, var, 0) == "VGLOBAL" then
- return "VGLOBAL"
- end
- var.info = self:indexupvalue(fs, n, var) -- else was LOCAL or UPVAL
- var.k = "VUPVAL" -- upvalue in this level
- return "VUPVAL"
- end--if v
- end--if fs
- end
- ------------------------------------------------------------------------
- -- consume a name token, creates a variable (global|local|upvalue)
- -- * used in prefixexp(), funcname()
- ------------------------------------------------------------------------
- function luaY:singlevar(ls, var)
- local varname = self:str_checkname(ls)
- local fs = ls.fs
- if self:singlevaraux(fs, varname, var, 1) == "VGLOBAL" then
- var.info = luaK:stringK(fs, varname) -- info points to global name
- end
- end
- ------------------------------------------------------------------------
- -- adjust RHS to match LHS in an assignment
- -- * used in assignment(), forlist(), localstat()
- ------------------------------------------------------------------------
- function luaY:adjust_assign(ls, nvars, nexps, e)
- local fs = ls.fs
- local extra = nvars - nexps
- if self:hasmultret(e.k) then
- extra = extra + 1 -- includes call itself
- if extra <= 0 then extra = 0 end
- luaK:setreturns(fs, e, extra) -- last exp. provides the difference
- if extra > 1 then luaK:reserveregs(fs, extra - 1) end
- else
- if e.k ~= "VVOID" then luaK:exp2nextreg(fs, e) end -- close last expression
- if extra > 0 then
- local reg = fs.freereg
- luaK:reserveregs(fs, extra)
- luaK:_nil(fs, reg, extra)
- end
- end
- end
- ------------------------------------------------------------------------
- -- tracks and limits parsing depth, assert check at end of parsing
- ------------------------------------------------------------------------
- function luaY:enterlevel(ls)
- ls.L.nCcalls = ls.L.nCcalls + 1
- if ls.L.nCcalls > self.LUAI_MAXCCALLS then
- luaX:lexerror(ls, "chunk has too many syntax levels", 0)
- end
- end
- ------------------------------------------------------------------------
- -- tracks parsing depth, a pair with luaY:enterlevel()
- ------------------------------------------------------------------------
- function luaY:leavelevel(ls)
- ls.L.nCcalls = ls.L.nCcalls - 1
- end
- ------------------------------------------------------------------------
- -- enters a code unit, initializes elements
- ------------------------------------------------------------------------
- function luaY:enterblock(fs, bl, isbreakable)
- bl.breaklist = luaK.NO_JUMP
- bl.isbreakable = isbreakable
- bl.nactvar = fs.nactvar
- bl.upval = false
- bl.previous = fs.bl
- fs.bl = bl
- assert(fs.freereg == fs.nactvar)
- end
- ------------------------------------------------------------------------
- -- leaves a code unit, close any upvalues
- ------------------------------------------------------------------------
- function luaY:leaveblock(fs)
- local bl = fs.bl
- fs.bl = bl.previous
- self:removevars(fs.ls, bl.nactvar)
- if bl.upval then
- luaK:codeABC(fs, "OP_CLOSE", bl.nactvar, 0, 0)
- end
- -- a block either controls scope or breaks (never both)
- assert(not bl.isbreakable or not bl.upval)
- assert(bl.nactvar == fs.nactvar)
- fs.freereg = fs.nactvar -- free registers
- luaK:patchtohere(fs, bl.breaklist)
- end
- ------------------------------------------------------------------------
- -- implement the instantiation of a function prototype, append list of
- -- upvalues after the instantiation instruction
- -- * used only in body()
- ------------------------------------------------------------------------
- function luaY:pushclosure(ls, func, v)
- local fs = ls.fs
- local f = fs.f
- self:growvector(ls.L, f.p, fs.np, f.sizep, nil,
- luaP.MAXARG_Bx, "constant table overflow")
- -- loop to initialize empty f.p positions not required
- f.p[fs.np] = func.f
- fs.np = fs.np + 1
- -- luaC_objbarrier(ls->L, f, func->f); /* C */
- self:init_exp(v, "VRELOCABLE", luaK:codeABx(fs, "OP_CLOSURE", 0, fs.np - 1))
- for i = 0, func.f.nups - 1 do
- local o = (func.upvalues[i].k == "VLOCAL") and "OP_MOVE" or "OP_GETUPVAL"
- luaK:codeABC(fs, o, 0, func.upvalues[i].info, 0)
- end
- end
- ------------------------------------------------------------------------
- -- opening of a function
- ------------------------------------------------------------------------
- function luaY:open_func(ls, fs)
- local L = ls.L
- local f = self:newproto(ls.L)
- fs.f = f
- fs.prev = ls.fs -- linked list of funcstates
- fs.ls = ls
- fs.L = L
- ls.fs = fs
- fs.pc = 0
- fs.lasttarget = -1
- fs.jpc = luaK.NO_JUMP
- fs.freereg = 0
- fs.nk = 0
- fs.np = 0
- fs.nlocvars = 0
- fs.nactvar = 0
- fs.bl = nil
- f.source = ls.source
- f.maxstacksize = 2 -- registers 0/1 are always valid
- fs.h = {} -- constant table; was luaH_new call
- -- anchor table of constants and prototype (to avoid being collected)
- -- sethvalue2s(L, L->top, fs->h); incr_top(L); /* C */
- -- setptvalue2s(L, L->top, f); incr_top(L);
- end
- ------------------------------------------------------------------------
- -- closing of a function
- ------------------------------------------------------------------------
- function luaY:close_func(ls)
- local L = ls.L
- local fs = ls.fs
- local f = fs.f
- self:removevars(ls, 0)
- luaK:ret(fs, 0, 0) -- final return
- -- luaM_reallocvector deleted for f->code, f->lineinfo, f->k, f->p,
- -- f->locvars, f->upvalues; not required for Lua table arrays
- f.sizecode = fs.pc
- f.sizelineinfo = fs.pc
- f.sizek = fs.nk
- f.sizep = fs.np
- f.sizelocvars = fs.nlocvars
- f.sizeupvalues = f.nups
- --assert(luaG_checkcode(f)) -- currently not implemented
- assert(fs.bl == nil)
- ls.fs = fs.prev
- -- the following is not required for this implementation; kept here
- -- for completeness
- -- L->top -= 2; /* remove table and prototype from the stack */
- -- last token read was anchored in defunct function; must reanchor it
- if fs then self:anchor_token(ls) end
- end
- ------------------------------------------------------------------------
- -- parser initialization function
- -- * note additional sub-tables needed for LexState, FuncState
- ------------------------------------------------------------------------
- function luaY:parser(L, z, buff, name)
- local lexstate = {} -- LexState
- lexstate.t = {}
- lexstate.lookahead = {}
- local funcstate = {} -- FuncState
- funcstate.upvalues = {}
- funcstate.actvar = {}
- -- the following nCcalls initialization added for convenience
- L.nCcalls = 0
- lexstate.buff = buff
- luaX:setinput(L, lexstate, z, name)
- self:open_func(lexstate, funcstate)
- funcstate.f.is_vararg = self.VARARG_ISVARARG -- main func. is always vararg
- luaX:next(lexstate) -- read first token
- self:chunk(lexstate)
- self:check(lexstate, "TK_EOS")
- self:close_func(lexstate)
- assert(funcstate.prev == nil)
- assert(funcstate.f.nups == 0)
- assert(lexstate.fs == nil)
- return funcstate.f
- end
- --[[--------------------------------------------------------------------
- -- GRAMMAR RULES
- ----------------------------------------------------------------------]]
- ------------------------------------------------------------------------
- -- parse a function name suffix, for function call specifications
- -- * used in primaryexp(), funcname()
- ------------------------------------------------------------------------
- function luaY:field(ls, v)
- -- field -> ['.' | ':'] NAME
- local fs = ls.fs
- local key = {} -- expdesc
- luaK:exp2anyreg(fs, v)
- luaX:next(ls) -- skip the dot or colon
- self:checkname(ls, key)
- luaK:indexed(fs, v, key)
- end
- ------------------------------------------------------------------------
- -- parse a table indexing suffix, for constructors, expressions
- -- * used in recfield(), primaryexp()
- ------------------------------------------------------------------------
- function luaY:yindex(ls, v)
- -- index -> '[' expr ']'
- luaX:next(ls) -- skip the '['
- self:expr(ls, v)
- luaK:exp2val(ls.fs, v)
- self:checknext(ls, "]")
- end
- --[[--------------------------------------------------------------------
- -- Rules for Constructors
- ----------------------------------------------------------------------]]
- --[[--------------------------------------------------------------------
- -- struct ConsControl:
- -- v -- last list item read (table: struct expdesc)
- -- t -- table descriptor (table: struct expdesc)
- -- nh -- total number of 'record' elements
- -- na -- total number of array elements
- -- tostore -- number of array elements pending to be stored
- ----------------------------------------------------------------------]]
- ------------------------------------------------------------------------
- -- parse a table record (hash) field
- -- * used in constructor()
- ------------------------------------------------------------------------
- function luaY:recfield(ls, cc)
- -- recfield -> (NAME | '['exp1']') = exp1
- local fs = ls.fs
- local reg = ls.fs.freereg
- local key, val = {}, {} -- expdesc
- if ls.t.token == "TK_NAME" then
- self:checklimit(fs, cc.nh, self.MAX_INT, "items in a constructor")
- self:checkname(ls, key)
- else -- ls->t.token == '['
- self:yindex(ls, key)
- end
- cc.nh = cc.nh + 1
- self:checknext(ls, "=")
- local rkkey = luaK:exp2RK(fs, key)
- self:expr(ls, val)
- luaK:codeABC(fs, "OP_SETTABLE", cc.t.info, rkkey, luaK:exp2RK(fs, val))
- fs.freereg = reg -- free registers
- end
- ------------------------------------------------------------------------
- -- emit a set list instruction if enough elements (LFIELDS_PER_FLUSH)
- -- * used in constructor()
- ------------------------------------------------------------------------
- function luaY:closelistfield(fs, cc)
- if cc.v.k == "VVOID" then return end -- there is no list item
- luaK:exp2nextreg(fs, cc.v)
- cc.v.k = "VVOID"
- if cc.tostore == luaP.LFIELDS_PER_FLUSH then
- luaK:setlist(fs, cc.t.info, cc.na, cc.tostore) -- flush
- cc.tostore = 0 -- no more items pending
- end
- end
- ------------------------------------------------------------------------
- -- emit a set list instruction at the end of parsing list constructor
- -- * used in constructor()
- ------------------------------------------------------------------------
- function luaY:lastlistfield(fs, cc)
- if cc.tostore == 0 then return end
- if self:hasmultret(cc.v.k) then
- luaK:setmultret(fs, cc.v)
- luaK:setlist(fs, cc.t.info, cc.na, self.LUA_MULTRET)
- cc.na = cc.na - 1 -- do not count last expression (unknown number of elements)
- else
- if cc.v.k ~= "VVOID" then
- luaK:exp2nextreg(fs, cc.v)
- end
- luaK:setlist(fs, cc.t.info, cc.na, cc.tostore)
- end
- end
- ------------------------------------------------------------------------
- -- parse a table list (array) field
- -- * used in constructor()
- ------------------------------------------------------------------------
- function luaY:listfield(ls, cc)
- self:expr(ls, cc.v)
- self:checklimit(ls.fs, cc.na, self.MAX_INT, "items in a constructor")
- cc.na = cc.na + 1
- cc.tostore = cc.tostore + 1
- end
- ------------------------------------------------------------------------
- -- parse a table constructor
- -- * used in funcargs(), simpleexp()
- ------------------------------------------------------------------------
- function luaY:constructor(ls, t)
- -- constructor -> '{' [ field { fieldsep field } [ fieldsep ] ] '}'
- -- field -> recfield | listfield
- -- fieldsep -> ',' | ';'
- local fs = ls.fs
- local line = ls.linenumber
- local pc = luaK:codeABC(fs, "OP_NEWTABLE", 0, 0, 0)
- local cc = {} -- ConsControl
- cc.v = {}
- cc.na, cc.nh, cc.tostore = 0, 0, 0
- cc.t = t
- self:init_exp(t, "VRELOCABLE", pc)
- self:init_exp(cc.v, "VVOID", 0) -- no value (yet)
- luaK:exp2nextreg(ls.fs, t) -- fix it at stack top (for gc)
- self:checknext(ls, "{")
- repeat
- assert(cc.v.k == "VVOID" or cc.tostore > 0)
- if ls.t.token == "}" then break end
- self:closelistfield(fs, cc)
- local c = ls.t.token
- if c == "TK_NAME" then -- may be listfields or recfields
- luaX:lookahead(ls)
- if ls.lookahead.token ~= "=" then -- expression?
- self:listfield(ls, cc)
- else
- self:recfield(ls, cc)
- end
- elseif c == "[" then -- constructor_item -> recfield
- self:recfield(ls, cc)
- else -- constructor_part -> listfield
- self:listfield(ls, cc)
- end
- until not self:testnext(ls, ",") and not self:testnext(ls, ";")
- self:check_match(ls, "}", "{", line)
- self:lastlistfield(fs, cc)
- luaP:SETARG_B(fs.f.code[pc], self:int2fb(cc.na)) -- set initial array size
- luaP:SETARG_C(fs.f.code[pc], self:int2fb(cc.nh)) -- set initial table size
- end
- -- }======================================================================
- ------------------------------------------------------------------------
- -- parse the arguments (parameters) of a function declaration
- -- * used in body()
- ------------------------------------------------------------------------
- function luaY:parlist(ls)
- -- parlist -> [ param { ',' param } ]
- local fs = ls.fs
- local f = fs.f
- local nparams = 0
- f.is_vararg = 0
- if ls.t.token ~= ")" then -- is 'parlist' not empty?
- repeat
- local c = ls.t.token
- if c == "TK_NAME" then -- param -> NAME
- self:new_localvar(ls, self:str_checkname(ls), nparams)
- nparams = nparams + 1
- elseif c == "TK_DOTS" then -- param -> `...'
- luaX:next(ls)
- -- [[
- -- #if defined(LUA_COMPAT_VARARG)
- -- use `arg' as default name
- self:new_localvarliteral(ls, "arg", nparams)
- nparams = nparams + 1
- f.is_vararg = self.VARARG_HASARG + self.VARARG_NEEDSARG
- -- #endif
- --]]
- f.is_vararg = f.is_vararg + self.VARARG_ISVARARG
- else
- luaX:syntaxerror(ls, "<name> or "..self:LUA_QL("...").." expected")
- end
- until f.is_vararg ~= 0 or not self:testnext(ls, ",")
- end--if
- self:adjustlocalvars(ls, nparams)
- -- NOTE: the following works only when HASARG_MASK is 2!
- f.numparams = fs.nactvar - (f.is_vararg % self.HASARG_MASK)
- luaK:reserveregs(fs, fs.nactvar) -- reserve register for parameters
- end
- ------------------------------------------------------------------------
- -- parse function declaration body
- -- * used in simpleexp(), localfunc(), funcstat()
- ------------------------------------------------------------------------
- function luaY:body(ls, e, needself, line)
- -- body -> '(' parlist ')' chunk END
- local new_fs = {} -- FuncState
- new_fs.upvalues = {}
- new_fs.actvar = {}
- self:open_func(ls, new_fs)
- new_fs.f.lineDefined = line
- self:checknext(ls, "(")
- if needself then
- self:new_localvarliteral(ls, "self", 0)
- self:adjustlocalvars(ls, 1)
- end
- self:parlist(ls)
- self:checknext(ls, ")")
- self:chunk(ls)
- new_fs.f.lastlinedefined = ls.linenumber
- self:check_match(ls, "TK_END", "TK_FUNCTION", line)
- self:close_func(ls)
- self:pushclosure(ls, new_fs, e)
- end
- ------------------------------------------------------------------------
- -- parse a list of comma-separated expressions
- -- * used is multiple locations
- ------------------------------------------------------------------------
- function luaY:explist1(ls, v)
- -- explist1 -> expr { ',' expr }
- local n = 1 -- at least one expression
- self:expr(ls, v)
- while self:testnext(ls, ",") do
- luaK:exp2nextreg(ls.fs, v)
- self:expr(ls, v)
- n = n + 1
- end
- return n
- end
- ------------------------------------------------------------------------
- -- parse the parameters of a function call
- -- * contrast with parlist(), used in function declarations
- -- * used in primaryexp()
- ------------------------------------------------------------------------
- function luaY:funcargs(ls, f)
- local fs = ls.fs
- local args = {} -- expdesc
- local nparams
- local line = ls.linenumber
- local c = ls.t.token
- if c == "(" then -- funcargs -> '(' [ explist1 ] ')'
- if line ~= ls.lastline then
- luaX:syntaxerror(ls, "ambiguous syntax (function call x new statement)")
- end
- luaX:next(ls)
- if ls.t.token == ")" then -- arg list is empty?
- args.k = "VVOID"
- else
- self:explist1(ls, args)
- luaK:setmultret(fs, args)
- end
- self:check_match(ls, ")", "(", line)
- elseif c == "{" then -- funcargs -> constructor
- self:constructor(ls, args)
- elseif c == "TK_STRING" then -- funcargs -> STRING
- self:codestring(ls, args, ls.t.seminfo)
- luaX:next(ls) -- must use 'seminfo' before 'next'
- else
- luaX:syntaxerror(ls, "function arguments expected")
- return
- end
- assert(f.k == "VNONRELOC")
- local base = f.info -- base register for call
- if self:hasmultret(args.k) then
- nparams = self.LUA_MULTRET -- open call
- else
- if args.k ~= "VVOID" then
- luaK:exp2nextreg(fs, args) -- close last argument
- end
- nparams = fs.freereg - (base + 1)
- end
- self:init_exp(f, "VCALL", luaK:codeABC(fs, "OP_CALL", base, nparams + 1, 2))
- luaK:fixline(fs, line)
- fs.freereg = base + 1 -- call remove function and arguments and leaves
- -- (unless changed) one result
- end
- --[[--------------------------------------------------------------------
- -- Expression parsing
- ----------------------------------------------------------------------]]
- ------------------------------------------------------------------------
- -- parses an expression in parentheses or a single variable
- -- * used in primaryexp()
- ------------------------------------------------------------------------
- function luaY:prefixexp(ls, v)
- -- prefixexp -> NAME | '(' expr ')'
- local c = ls.t.token
- if c == "(" then
- local line = ls.linenumber
- luaX:next(ls)
- self:expr(ls, v)
- self:check_match(ls, ")", "(", line)
- luaK:dischargevars(ls.fs, v)
- elseif c == "TK_NAME" then
- self:singlevar(ls, v)
- else
- luaX:syntaxerror(ls, "unexpected symbol")
- end--if c
- return
- end
- ------------------------------------------------------------------------
- -- parses a prefixexp (an expression in parentheses or a single variable)
- -- or a function call specification
- -- * used in simpleexp(), assignment(), exprstat()
- ------------------------------------------------------------------------
- function luaY:primaryexp(ls, v)
- -- primaryexp ->
- -- prefixexp { '.' NAME | '[' exp ']' | ':' NAME funcargs | funcargs }
- local fs = ls.fs
- self:prefixexp(ls, v)
- while true do
- local c = ls.t.token
- if c == "." then -- field
- self:field(ls, v)
- elseif c == "[" then -- '[' exp1 ']'
- local key = {} -- expdesc
- luaK:exp2anyreg(fs, v)
- self:yindex(ls, key)
- luaK:indexed(fs, v, key)
- elseif c == ":" then -- ':' NAME funcargs
- local key = {} -- expdesc
- luaX:next(ls)
- self:checkname(ls, key)
- luaK:_self(fs, v, key)
- self:funcargs(ls, v)
- elseif c == "(" or c == "TK_STRING" or c == "{" then -- funcargs
- luaK:exp2nextreg(fs, v)
- self:funcargs(ls, v)
- else
- return
- end--if c
- end--while
- end
- ------------------------------------------------------------------------
- -- parses general expression types, constants handled here
- -- * used in subexpr()
- ------------------------------------------------------------------------
- function luaY:simpleexp(ls, v)
- -- simpleexp -> NUMBER | STRING | NIL | TRUE | FALSE | ... |
- -- constructor | FUNCTION body | primaryexp
- local c = ls.t.token
- if c == "TK_NUMBER" then
- self:init_exp(v, "VKNUM", 0)
- v.nval = ls.t.seminfo
- elseif c == "TK_STRING" then
- self:codestring(ls, v, ls.t.seminfo)
- elseif c == "TK_NIL" then
- self:init_exp(v, "VNIL", 0)
- elseif c == "TK_TRUE" then
- self:init_exp(v, "VTRUE", 0)
- elseif c == "TK_FALSE" then
- self:init_exp(v, "VFALSE", 0)
- elseif c == "TK_DOTS" then -- vararg
- local fs = ls.fs
- self:check_condition(ls, fs.f.is_vararg ~= 0,
- "cannot use "..self:LUA_QL("...").." outside a vararg function");
- -- NOTE: the following substitutes for a bitop, but is value-specific
- local is_vararg = fs.f.is_vararg
- if is_vararg >= self.VARARG_NEEDSARG then
- fs.f.is_vararg = is_vararg - self.VARARG_NEEDSARG -- don't need 'arg'
- end
- self:init_exp(v, "VVARARG", luaK:codeABC(fs, "OP_VARARG", 0, 1, 0))
- elseif c == "{" then -- constructor
- self:constructor(ls, v)
- return
- elseif c == "TK_FUNCTION" then
- luaX:next(ls)
- self:body(ls, v, false, ls.linenumber)
- return
- else
- self:primaryexp(ls, v)
- return
- end--if c
- luaX:next(ls)
- end
- ------------------------------------------------------------------------
- -- Translates unary operators tokens if found, otherwise returns
- -- OPR_NOUNOPR. getunopr() and getbinopr() are used in subexpr().
- -- * used in subexpr()
- ------------------------------------------------------------------------
- function luaY:getunopr(op)
- if op == "TK_NOT" then
- return "OPR_NOT"
- elseif op == "-" then
- return "OPR_MINUS"
- elseif op == "#" then
- return "OPR_LEN"
- else
- return "OPR_NOUNOPR"
- end
- end
- ------------------------------------------------------------------------
- -- Translates binary operator tokens if found, otherwise returns
- -- OPR_NOBINOPR. Code generation uses OPR_* style tokens.
- -- * used in subexpr()
- ------------------------------------------------------------------------
- luaY.getbinopr_table = {
- ["+"] = "OPR_ADD",
- ["-"] = "OPR_SUB",
- ["*"] = "OPR_MUL",
- ["/"] = "OPR_DIV",
- ["%"] = "OPR_MOD",
- ["^"] = "OPR_POW",
- ["TK_CONCAT"] = "OPR_CONCAT",
- ["TK_NE"] = "OPR_NE",
- ["TK_EQ"] = "OPR_EQ",
- ["<"] = "OPR_LT",
- ["TK_LE"] = "OPR_LE",
- [">"] = "OPR_GT",
- ["TK_GE"] = "OPR_GE",
- ["TK_AND"] = "OPR_AND",
- ["TK_OR"] = "OPR_OR",
- }
- function luaY:getbinopr(op)
- local opr = self.getbinopr_table[op]
- if opr then return opr else return "OPR_NOBINOPR" end
- end
- ------------------------------------------------------------------------
- -- the following priority table consists of pairs of left/right values
- -- for binary operators (was a static const struct); grep for ORDER OPR
- -- * the following struct is replaced:
- -- static const struct {
- -- lu_byte left; /* left priority for each binary operator */
- -- lu_byte right; /* right priority */
- -- } priority[] = { /* ORDER OPR */
- ------------------------------------------------------------------------
- luaY.priority = {
- {6, 6}, {6, 6}, {7, 7}, {7, 7}, {7, 7}, -- `+' `-' `/' `%'
- {10, 9}, {5, 4}, -- power and concat (right associative)
- {3, 3}, {3, 3}, -- equality
- {3, 3}, {3, 3}, {3, 3}, {3, 3}, -- order
- {2, 2}, {1, 1} -- logical (and/or)
- }
- luaY.UNARY_PRIORITY = 8 -- priority for unary operators
- ------------------------------------------------------------------------
- -- Parse subexpressions. Includes handling of unary operators and binary
- -- operators. A subexpr is given the rhs priority level of the operator
- -- immediately left of it, if any (limit is -1 if none,) and if a binop
- -- is found, limit is compared with the lhs priority level of the binop
- -- in order to determine which executes first.
- ------------------------------------------------------------------------
- ------------------------------------------------------------------------
- -- subexpr -> (simpleexp | unop subexpr) { binop subexpr }
- -- where 'binop' is any binary operator with a priority higher than 'limit'
- -- * for priority lookups with self.priority[], 1=left and 2=right
- -- * recursively called
- -- * used in expr()
- ------------------------------------------------------------------------
- function luaY:subexpr(ls, v, limit)
- self:enterlevel(ls)
- local uop = self:getunopr(ls.t.token)
- if uop ~= "OPR_NOUNOPR" then
- luaX:next(ls)
- self:subexpr(ls, v, self.UNARY_PRIORITY)
- luaK:prefix(ls.fs, uop, v)
- else
- self:simpleexp(ls, v)
- end
- -- expand while operators have priorities higher than 'limit'
- local op = self:getbinopr(ls.t.token)
- while op ~= "OPR_NOBINOPR" and self.priority[luaK.BinOpr[op] + 1][1] > limit do
- local v2 = {} -- expdesc
- luaX:next(ls)
- luaK:infix(ls.fs, op, v)
- -- read sub-expression with higher priority
- local nextop = self:subexpr(ls, v2, self.priority[luaK.BinOpr[op] + 1][2])
- luaK:posfix(ls.fs, op, v, v2)
- op = nextop
- end
- self:leavelevel(ls)
- return op -- return first untreated operator
- end
- ------------------------------------------------------------------------
- -- Expression parsing starts here. Function subexpr is entered with the
- -- left operator (which is non-existent) priority of -1, which is lower
- -- than all actual operators. Expr information is returned in parm v.
- -- * used in multiple locations
- ------------------------------------------------------------------------
- function luaY:expr(ls, v)
- self:subexpr(ls, v, 0)
- end
- -- }====================================================================
- --[[--------------------------------------------------------------------
- -- Rules for Statements
- ----------------------------------------------------------------------]]
- ------------------------------------------------------------------------
- -- checks next token, used as a look-ahead
- -- * returns boolean instead of 0|1
- -- * used in retstat(), chunk()
- ------------------------------------------------------------------------
- function luaY:block_follow(token)
- if token == "TK_ELSE" or token == "TK_ELSEIF" or token == "TK_END"
- or token == "TK_UNTIL" or token == "TK_EOS" then
- return true
- else
- return false
- end
- end
- ------------------------------------------------------------------------
- -- parse a code block or unit
- -- * used in multiple functions
- ------------------------------------------------------------------------
- function luaY:block(ls)
- -- block -> chunk
- local fs = ls.fs
- local bl = {} -- BlockCnt
- self:enterblock(fs, bl, false)
- self:chunk(ls)
- assert(bl.breaklist == luaK.NO_JUMP)
- self:leaveblock(fs)
- end
- ------------------------------------------------------------------------
- -- structure to chain all variables in the left-hand side of an
- -- assignment
- -- struct LHS_assign:
- -- prev -- (table: struct LHS_assign)
- -- v -- variable (global, local, upvalue, or indexed) (table: expdesc)
- ------------------------------------------------------------------------
- ------------------------------------------------------------------------
- -- check whether, in an assignment to a local variable, the local variable
- -- is needed in a previous assignment (to a table). If so, save original
- -- local value in a safe place and use this safe copy in the previous
- -- assignment.
- -- * used in assignment()
- ------------------------------------------------------------------------
- function luaY:check_conflict(ls, lh, v)
- local fs = ls.fs
- local extra = fs.freereg -- eventual position to save local variable
- local conflict = false
- while lh do
- if lh.v.k == "VINDEXED" then
- if lh.v.info == v.info then -- conflict?
- conflict = true
- lh.v.info = extra -- previous assignment will use safe copy
- end
- if lh.v.aux == v.info then -- conflict?
- conflict = true
- lh.v.aux = extra -- previous assignment will use safe copy
- end
- end
- lh = lh.prev
- end
- if conflict then
- luaK:codeABC(fs, "OP_MOVE", fs.freereg, v.info, 0) -- make copy
- luaK:reserveregs(fs, 1)
- end
- end
- ------------------------------------------------------------------------
- -- parse a variable assignment sequence
- -- * recursively called
- -- * used in exprstat()
- ------------------------------------------------------------------------
- function luaY:assignment(ls, lh, nvars)
- local e = {} -- expdesc
- -- test was: VLOCAL <= lh->v.k && lh->v.k <= VINDEXED
- local c = lh.v.k
- self:check_condition(ls, c == "VLOCAL" or c == "VUPVAL" or c == "VGLOBAL"
- or c == "VINDEXED", "syntax error")
- if self:testnext(ls, ",") then -- assignment -> ',' primaryexp assignment
- local nv = {} -- LHS_assign
- nv.v = {}
- nv.prev = lh
- self:primaryexp(ls, nv.v)
- if nv.v.k == "VLOCAL" then
- self:check_conflict(ls, lh, nv.v)
- end
- self:checklimit(ls.fs, nvars, self.LUAI_MAXCCALLS - ls.L.nCcalls,
- "variables in assignment")
- self:assignment(ls, nv, nvars + 1)
- else -- assignment -> '=' explist1
- self:checknext(ls, "=")
- local nexps = self:explist1(ls, e)
- if nexps ~= nvars then
- self:adjust_assign(ls, nvars, nexps, e)
- if nexps > nvars then
- ls.fs.freereg = ls.fs.freereg - (nexps - nvars) -- remove extra values
- end
- else
- luaK:setoneret(ls.fs, e) -- close last expression
- luaK:storevar(ls.fs, lh.v, e)
- return -- avoid default
- end
- end
- self:init_exp(e, "VNONRELOC", ls.fs.freereg - 1) -- default assignment
- luaK:storevar(ls.fs, lh.v, e)
- end
- ------------------------------------------------------------------------
- -- parse condition in a repeat statement or an if control structure
- -- * used in repeatstat(), test_then_block()
- ------------------------------------------------------------------------
- function luaY:cond(ls)
- -- cond -> exp
- local v = {} -- expdesc
- self:expr(ls, v) -- read condition
- if v.k == "VNIL" then v.k = "VFALSE" end -- 'falses' are all equal here
- luaK:goiftrue(ls.fs, v)
- return v.f
- end
- ------------------------------------------------------------------------
- -- parse a break statement
- -- * used in statements()
- ------------------------------------------------------------------------
- function luaY:breakstat(ls)
- -- stat -> BREAK
- local fs = ls.fs
- local bl = fs.bl
- local upval = false
- while bl and not bl.isbreakable do
- if bl.upval then upval = true end
- bl = bl.previous
- end
- if not bl then
- luaX:syntaxerror(ls, "no loop to break")
- end
- if upval then
- luaK:codeABC(fs, "OP_CLOSE", bl.nactvar, 0, 0)
- end
- bl.breaklist = luaK:concat(fs, bl.breaklist, luaK:jump(fs))
- end
- ------------------------------------------------------------------------
- -- parse a while-do control structure, body processed by block()
- -- * with dynamic array sizes, MAXEXPWHILE + EXTRAEXP limits imposed by
- -- the function's implementation can be removed
- -- * used in statements()
- ------------------------------------------------------------------------
- function luaY:whilestat(ls, line)
- -- whilestat -> WHILE cond DO block END
- local fs = ls.fs
- local bl = {} -- BlockCnt
- luaX:next(ls) -- skip WHILE
- local whileinit = luaK:getlabel(fs)
- local condexit = self:cond(ls)
- self:enterblock(fs, bl, true)
- self:checknext(ls, "TK_DO")
- self:block(ls)
- luaK:patchlist(fs, luaK:jump(fs), whileinit)
- self:check_match(ls, "TK_END", "TK_WHILE", line)
- self:leaveblock(fs)
- luaK:patchtohere(fs, condexit) -- false conditions finish the loop
- end
- ------------------------------------------------------------------------
- -- parse a repeat-until control structure, body parsed by chunk()
- -- * used in statements()
- ------------------------------------------------------------------------
- function luaY:repeatstat(ls, line)
- -- repeatstat -> REPEAT block UNTIL cond
- local fs = ls.fs
- local repeat_init = luaK:getlabel(fs)
- local bl1, bl2 = {}, {} -- BlockCnt
- self:enterblock(fs, bl1, true) -- loop block
- self:enterblock(fs, bl2, false) -- scope block
- luaX:next(ls) -- skip REPEAT
- self:chunk(ls)
- self:check_match(ls, "TK_UNTIL", "TK_REPEAT", line)
- local condexit = self:cond(ls) -- read condition (inside scope block)
- if not bl2.upval then -- no upvalues?
- self:leaveblock(fs) -- finish scope
- luaK:patchlist(ls.fs, condexit, repeat_init) -- close the loop
- else -- complete semantics when there are upvalues
- self:breakstat(ls) -- if condition then break
- luaK:patchtohere(ls.fs, condexit) -- else...
- self:leaveblock(fs) -- finish scope...
- luaK:patchlist(ls.fs, luaK:jump(fs), repeat_init) -- and repeat
- end
- self:leaveblock(fs) -- finish loop
- end
- ------------------------------------------------------------------------
- -- parse the single expressions needed in numerical for loops
- -- * used in fornum()
- ------------------------------------------------------------------------
- function luaY:exp1(ls)
- local e = {} -- expdesc
- self:expr(ls, e)
- local k = e.k
- luaK:exp2nextreg(ls.fs, e)
- return k
- end
- ------------------------------------------------------------------------
- -- parse a for loop body for both versions of the for loop
- -- * used in fornum(), forlist()
- ------------------------------------------------------------------------
- function luaY:forbody(ls, base, line, nvars, isnum)
- -- forbody -> DO block
- local bl = {} -- BlockCnt
- local fs = ls.fs
- self:adjustlocalvars(ls, 3) -- control variables
- self:checknext(ls, "TK_DO")
- local prep = isnum and luaK:codeAsBx(fs, "OP_FORPREP", base, luaK.NO_JUMP)
- or luaK:jump(fs)
- self:enterblock(fs, bl, false) -- scope for declared variables
- self:adjustlocalvars(ls, nvars)
- luaK:reserveregs(fs, nvars)
- self:block(ls)
- self:leaveblock(fs) -- end of scope for declared variables
- luaK:patchtohere(fs, prep)
- local endfor = isnum and luaK:codeAsBx(fs, "OP_FORLOOP", base, luaK.NO_JUMP)
- or luaK:codeABC(fs, "OP_TFORLOOP", base, 0, nvars)
- luaK:fixline(fs, line) -- pretend that `OP_FOR' starts the loop
- luaK:patchlist(fs, isnum and endfor or luaK:jump(fs), prep + 1)
- end
- ------------------------------------------------------------------------
- -- parse a numerical for loop, calls forbody()
- -- * used in forstat()
- ------------------------------------------------------------------------
- function luaY:fornum(ls, varname, line)
- -- fornum -> NAME = exp1,exp1[,exp1] forbody
- local fs = ls.fs
- local base = fs.freereg
- self:new_localvarliteral(ls, "(for index)", 0)
- self:new_localvarliteral(ls, "(for limit)", 1)
- self:new_localvarliteral(ls, "(for step)", 2)
- self:new_localvar(ls, varname, 3)
- self:checknext(ls, '=')
- self:exp1(ls) -- initial value
- self:checknext(ls, ",")
- self:exp1(ls) -- limit
- if self:testnext(ls, ",") then
- self:exp1(ls) -- optional step
- else -- default step = 1
- luaK:codeABx(fs, "OP_LOADK", fs.freereg, luaK:numberK(fs, 1))
- luaK:reserveregs(fs, 1)
- end
- self:forbody(ls, base, line, 1, true)
- end
- ------------------------------------------------------------------------
- -- parse a generic for loop, calls forbody()
- -- * used in forstat()
- ------------------------------------------------------------------------
- function luaY:forlist(ls, indexname)
- -- forlist -> NAME {,NAME} IN explist1 forbody
- local fs = ls.fs
- local e = {} -- expdesc
- local nvars = 0
- local base = fs.freereg
- -- create control variables
- self:new_localvarliteral(ls, "(for generator)", nvars)
- nvars = nvars + 1
- self:new_localvarliteral(ls, "(for state)", nvars)
- nvars = nvars + 1
- self:new_localvarliteral(ls, "(for control)", nvars)
- nvars = nvars + 1
- -- create declared variables
- self:new_localvar(ls, indexname, nvars)
- nvars = nvars + 1
- while self:testnext(ls, ",") do
- self:new_localvar(ls, self:str_checkname(ls), nvars)
- nvars = nvars + 1
- end
- self:checknext(ls, "TK_IN")
- local line = ls.linenumber
- self:adjust_assign(ls, 3, self:explist1(ls, e), e)
- luaK:checkstack(fs, 3) -- extra space to call generator
- self:forbody(ls, base, line, nvars - 3, false)
- end
- ------------------------------------------------------------------------
- -- initial parsing for a for loop, calls fornum() or forlist()
- -- * used in statements()
- ------------------------------------------------------------------------
- function luaY:forstat(ls, line)
- -- forstat -> FOR (fornum | forlist) END
- local fs = ls.fs
- local bl = {} -- BlockCnt
- self:enterblock(fs, bl, true) -- scope for loop and control variables
- luaX:next(ls) -- skip `for'
- local varname = self:str_checkname(ls) -- first variable name
- local c = ls.t.token
- if c == "=" then
- self:fornum(ls, varname, line)
- elseif c == "," or c == "TK_IN" then
- self:forlist(ls, varname)
- else
- luaX:syntaxerror(ls, self:LUA_QL("=").." or "..self:LUA_QL("in").." expected")
- end
- self:check_match(ls, "TK_END", "TK_FOR", line)
- self:leaveblock(fs) -- loop scope (`break' jumps to this point)
- end
- ------------------------------------------------------------------------
- -- parse part of an if control structure, including the condition
- -- * used in ifstat()
- ------------------------------------------------------------------------
- function luaY:test_then_block(ls)
- -- test_then_block -> [IF | ELSEIF] cond THEN block
- luaX:next(ls) -- skip IF or ELSEIF
- local condexit = self:cond(ls)
- self:checknext(ls, "TK_THEN")
- self:block(ls) -- `then' part
- return condexit
- end
- ------------------------------------------------------------------------
- -- parse an if control structure
- -- * used in statements()
- ------------------------------------------------------------------------
- function luaY:ifstat(ls, line)
- -- ifstat -> IF cond THEN block {ELSEIF cond THEN block} [ELSE block] END
- local fs = ls.fs
- local escapelist = luaK.NO_JUMP
- local flist = self:test_then_block(ls) -- IF cond THEN block
- while ls.t.token == "TK_ELSEIF" do
- escapelist = luaK:concat(fs, escapelist, luaK:jump(fs))
- luaK:patchtohere(fs, flist)
- flist = self:test_then_block(ls) -- ELSEIF cond THEN block
- end
- if ls.t.token == "TK_ELSE" then
- escapelist = luaK:concat(fs, escapelist, luaK:jump(fs))
- luaK:patchtohere(fs, flist)
- luaX:next(ls) -- skip ELSE (after patch, for correct line info)
- self:block(ls) -- 'else' part
- else
- escapelist = luaK:concat(fs, escapelist, flist)
- end
- luaK:patchtohere(fs, escapelist)
- self:check_match(ls, "TK_END", "TK_IF", line)
- end
- ------------------------------------------------------------------------
- -- parse a local function statement
- -- * used in statements()
- ------------------------------------------------------------------------
- function luaY:localfunc(ls)
- local v, b = {}, {} -- expdesc
- local fs = ls.fs
- self:new_localvar(ls, self:str_checkname(ls), 0)
- self:init_exp(v, "VLOCAL", fs.freereg)
- luaK:reserveregs(fs, 1)
- self:adjustlocalvars(ls, 1)
- self:body(ls, b, false, ls.linenumber)
- luaK:storevar(fs, v, b)
- -- debug information will only see the variable after this point!
- self:getlocvar(fs, fs.nactvar - 1).startpc = fs.pc
- end
- ------------------------------------------------------------------------
- -- parse a local variable declaration statement
- -- * used in statements()
- ------------------------------------------------------------------------
- function luaY:localstat(ls)
- -- stat -> LOCAL NAME {',' NAME} ['=' explist1]
- local nvars = 0
- local nexps
- local e = {} -- expdesc
- repeat
- self:new_localvar(ls, self:str_checkname(ls), nvars)
- nvars = nvars + 1
- until not self:testnext(ls, ",")
- if self:testnext(ls, "=") then
- nexps = self:explist1(ls, e)
- else
- e.k = "VVOID"
- nexps = 0
- end
- self:adjust_assign(ls, nvars, nexps, e)
- self:adjustlocalvars(ls, nvars)
- end
- ------------------------------------------------------------------------
- -- parse a function name specification
- -- * used in funcstat()
- ------------------------------------------------------------------------
- function luaY:funcname(ls, v)
- -- funcname -> NAME {field} [':' NAME]
- local needself = false
- self:singlevar(ls, v)
- while ls.t.token == "." do
- self:field(ls, v)
- end
- if ls.t.token == ":" then
- needself = true
- self:field(ls, v)
- end
- return needself
- end
- ------------------------------------------------------------------------
- -- parse a function statement
- -- * used in statements()
- ------------------------------------------------------------------------
- function luaY:funcstat(ls, line)
- -- funcstat -> FUNCTION funcname body
- local v, b = {}, {} -- expdesc
- luaX:next(ls) -- skip FUNCTION
- local needself = self:funcname(ls, v)
- self:body(ls, b, needself, line)
- luaK:storevar(ls.fs, v, b)
- luaK:fixline(ls.fs, line) -- definition 'happens' in the first line
- end
- ------------------------------------------------------------------------
- -- parse a function call with no returns or an assignment statement
- -- * used in statements()
- ------------------------------------------------------------------------
- function luaY:exprstat(ls)
- -- stat -> func | assignment
- local fs = ls.fs
- local v = {} -- LHS_assign
- v.v = {}
- self:primaryexp(ls, v.v)
- if v.v.k == "VCALL" then -- stat -> func
- luaP:SETARG_C(luaK:getcode(fs, v.v), 1) -- call statement uses no results
- else -- stat -> assignment
- v.prev = nil
- self:assignment(ls, v, 1)
- end
- end
- ------------------------------------------------------------------------
- -- parse a return statement
- -- * used in statements()
- ------------------------------------------------------------------------
- function luaY:retstat(ls)
- -- stat -> RETURN explist
- local fs = ls.fs
- local e = {} -- expdesc
- local first, nret -- registers with returned values
- luaX:next(ls) -- skip RETURN
- if self:block_follow(ls.t.token) or ls.t.token == ";" then
- first, nret = 0, 0 -- return no values
- else
- nret = self:explist1(ls, e) -- optional return values
- if self:hasmultret(e.k) then
- luaK:setmultret(fs, e)
- if e.k == "VCALL" and nret == 1 then -- tail call?
- luaP:SET_OPCODE(luaK:getcode(fs, e), "OP_TAILCALL")
- assert(luaP:GETARG_A(luaK:getcode(fs, e)) == fs.nactvar)
- end
- first = fs.nactvar
- nret = self.LUA_MULTRET -- return all values
- else
- if nret == 1 then -- only one single value?
- first = luaK:exp2anyreg(fs, e)
- else
- luaK:exp2nextreg(fs, e) -- values must go to the 'stack'
- first = fs.nactvar -- return all 'active' values
- assert(nret == fs.freereg - first)
- end
- end--if
- end--if
- luaK:ret(fs, first, nret)
- end
- ------------------------------------------------------------------------
- -- initial parsing for statements, calls a lot of functions
- -- * returns boolean instead of 0|1
- -- * used in chunk()
- ------------------------------------------------------------------------
- function luaY:statement(ls)
- local line = ls.linenumber -- may be needed for error messages
- local c = ls.t.token
- if c == "TK_IF" then -- stat -> ifstat
- self:ifstat(ls, line)
- return false
- elseif c == "TK_WHILE" then -- stat -> whilestat
- self:whilestat(ls, line)
- return false
- elseif c == "TK_DO" then -- stat -> DO block END
- luaX:next(ls) -- skip DO
- self:block(ls)
- self:check_match(ls, "TK_END", "TK_DO", line)
- return false
- elseif c == "TK_FOR" then -- stat -> forstat
- self:forstat(ls, line)
- return false
- elseif c == "TK_REPEAT" then -- stat -> repeatstat
- self:repeatstat(ls, line)
- return false
- elseif c == "TK_FUNCTION" then -- stat -> funcstat
- self:funcstat(ls, line)
- return false
- elseif c == "TK_LOCAL" then -- stat -> localstat
- luaX:next(ls) -- skip LOCAL
- if self:testnext(ls, "TK_FUNCTION") then -- local function?
- self:localfunc(ls)
- else
- self:localstat(ls)
- end
- return false
- elseif c == "TK_RETURN" then -- stat -> retstat
- self:retstat(ls)
- return true -- must be last statement
- elseif c == "TK_BREAK" then -- stat -> breakstat
- luaX:next(ls) -- skip BREAK
- self:breakstat(ls)
- return true -- must be last statement
- else
- self:exprstat(ls)
- return false -- to avoid warnings
- end--if c
- end
- ------------------------------------------------------------------------
- -- parse a chunk, which consists of a bunch of statements
- -- * used in parser(), body(), block(), repeatstat()
- ------------------------------------------------------------------------
- function luaY:chunk(ls)
- -- chunk -> { stat [';'] }
- local islast = false
- self:enterlevel(ls)
- while not islast and not self:block_follow(ls.t.token) do
- islast = self:statement(ls)
- self:testnext(ls, ";")
- assert(ls.fs.f.maxstacksize >= ls.fs.freereg and
- ls.fs.freereg >= ls.fs.nactvar)
- ls.fs.freereg = ls.fs.nactvar -- free registers
- end
- self:leavelevel(ls)
- end
- -- }======================================================================
- return luaY
- end
- fake_module_scripts[script] = module_script
- end
- do -- nil.LuaK
- local script = Instance.new('ModuleScript', nil)
- script.Name = "LuaK"
- local function module_script()
- --[[--------------------------------------------------------------------
- lcode.lua
- Lua 5 code generator in Lua
- This file is part of Yueliang.
- Copyright (c) 2005-2007 Kein-Hong Man <khman@users.sf.net>
- The COPYRIGHT file describes the conditions
- under which this software may be distributed.
- See the ChangeLog for more information.
- ----------------------------------------------------------------------]]
- --[[--------------------------------------------------------------------
- -- Notes:
- -- * one function manipulate a pointer argument with a simple data type
- -- (can't be emulated by a table, ambiguous), now returns that value:
- -- luaK:concat(fs, l1, l2)
- -- * luaM_growvector uses the faux luaY:growvector, for limit checking
- -- * some function parameters changed to boolean, additional code
- -- translates boolean back to 1/0 for instruction fields
- --
- -- Not implemented:
- -- * NOTE there is a failed assert in luaK:addk, a porting problem
- --
- -- Added:
- -- * constant MAXSTACK from llimits.h
- -- * luaK:ttisnumber(o) (from lobject.h)
- -- * luaK:nvalue(o) (from lobject.h)
- -- * luaK:setnilvalue(o) (from lobject.h)
- -- * luaK:setnvalue(o, x) (from lobject.h)
- -- * luaK:setbvalue(o, x) (from lobject.h)
- -- * luaK:sethvalue(o, x) (from lobject.h), parameter L deleted
- -- * luaK:setsvalue(o, x) (from lobject.h), parameter L deleted
- -- * luaK:numadd, luaK:numsub, luaK:nummul, luaK:numdiv, luaK:nummod,
- -- luaK:numpow, luaK:numunm, luaK:numisnan (from luaconf.h)
- -- * copyexp(e1, e2) added in luaK:posfix to copy expdesc struct
- --
- -- Changed in 5.1.x:
- -- * enum BinOpr has a new entry, OPR_MOD
- -- * enum UnOpr has a new entry, OPR_LEN
- -- * binopistest, unused in 5.0.x, has been deleted
- -- * macro setmultret is new
- -- * functions isnumeral, luaK_ret, boolK are new
- -- * funcion nilK was named nil_constant in 5.0.x
- -- * function interface changed: need_value, patchtestreg, concat
- -- * TObject now a TValue
- -- * functions luaK_setreturns, luaK_setoneret are new
- -- * function luaK:setcallreturns deleted, to be replaced by:
- -- luaK:setmultret, luaK:ret, luaK:setreturns, luaK:setoneret
- -- * functions constfolding, codearith, codecomp are new
- -- * luaK:codebinop has been deleted
- -- * function luaK_setlist is new
- -- * OPR_MULT renamed to OPR_MUL
- ----------------------------------------------------------------------]]
- -- requires luaP, luaX, luaY
- local luaK = {}
- local luaP = require(script.Parent.LuaP)
- local luaX = require(script.Parent.LuaX)
- ------------------------------------------------------------------------
- -- constants used by code generator
- ------------------------------------------------------------------------
- -- maximum stack for a Lua function
- luaK.MAXSTACK = 250 -- (from llimits.h)
- --[[--------------------------------------------------------------------
- -- other functions
- ----------------------------------------------------------------------]]
- ------------------------------------------------------------------------
- -- emulation of TValue macros (these are from lobject.h)
- -- * TValue is a table since lcode passes references around
- -- * tt member field removed, using Lua's type() instead
- -- * for setsvalue, sethvalue, parameter L (deleted here) in lobject.h
- -- is used in an assert for testing, see checkliveness(g,obj)
- ------------------------------------------------------------------------
- function luaK:ttisnumber(o)
- if o then return type(o.value) == "number" else return false end
- end
- function luaK:nvalue(o) return o.value end
- function luaK:setnilvalue(o) o.value = nil end
- function luaK:setsvalue(o, x) o.value = x end
- luaK.setnvalue = luaK.setsvalue
- luaK.sethvalue = luaK.setsvalue
- luaK.setbvalue = luaK.setsvalue
- ------------------------------------------------------------------------
- -- The luai_num* macros define the primitive operations over numbers.
- -- * this is not the entire set of primitive operations from luaconf.h
- -- * used in luaK:constfolding()
- ------------------------------------------------------------------------
- function luaK:numadd(a, b) return a + b end
- function luaK:numsub(a, b) return a - b end
- function luaK:nummul(a, b) return a * b end
- function luaK:numdiv(a, b) return a / b end
- function luaK:nummod(a, b) return a % b end
- -- ((a) - floor((a)/(b))*(b)) /* actual, for reference */
- function luaK:numpow(a, b) return a ^ b end
- function luaK:numunm(a) return -a end
- function luaK:numisnan(a) return not a == a end
- -- a NaN cannot equal another NaN
- --[[--------------------------------------------------------------------
- -- code generator functions
- ----------------------------------------------------------------------]]
- ------------------------------------------------------------------------
- -- Marks the end of a patch list. It is an invalid value both as an absolute
- -- address, and as a list link (would link an element to itself).
- ------------------------------------------------------------------------
- luaK.NO_JUMP = -1
- ------------------------------------------------------------------------
- -- grep "ORDER OPR" if you change these enums
- ------------------------------------------------------------------------
- luaK.BinOpr = {
- OPR_ADD = 0, OPR_SUB = 1, OPR_MUL = 2, OPR_DIV = 3, OPR_MOD = 4, OPR_POW = 5,
- OPR_CONCAT = 6,
- OPR_NE = 7, OPR_EQ = 8,
- OPR_LT = 9, OPR_LE = 10, OPR_GT = 11, OPR_GE = 12,
- OPR_AND = 13, OPR_OR = 14,
- OPR_NOBINOPR = 15,
- }
- -- * UnOpr is used by luaK:prefix's op argument, but not directly used
- -- because the function receives the symbols as strings, e.g. "OPR_NOT"
- luaK.UnOpr = {
- OPR_MINUS = 0, OPR_NOT = 1, OPR_LEN = 2, OPR_NOUNOPR = 3
- }
- ------------------------------------------------------------------------
- -- returns the instruction object for given e (expdesc), was a macro
- ------------------------------------------------------------------------
- function luaK:getcode(fs, e)
- return fs.f.code[e.info]
- end
- ------------------------------------------------------------------------
- -- codes an instruction with a signed Bx (sBx) field, was a macro
- -- * used in luaK:jump(), (lparser) luaY:forbody()
- ------------------------------------------------------------------------
- function luaK:codeAsBx(fs, o, A, sBx)
- return self:codeABx(fs, o, A, sBx + luaP.MAXARG_sBx)
- end
- ------------------------------------------------------------------------
- -- set the expdesc e instruction for multiple returns, was a macro
- ------------------------------------------------------------------------
- function luaK:setmultret(fs, e)
- self:setreturns(fs, e, luaY.LUA_MULTRET)
- end
- ------------------------------------------------------------------------
- -- there is a jump if patch lists are not identical, was a macro
- -- * used in luaK:exp2reg(), luaK:exp2anyreg(), luaK:exp2val()
- ------------------------------------------------------------------------
- function luaK:hasjumps(e)
- return e.t ~= e.f
- end
- ------------------------------------------------------------------------
- -- true if the expression is a constant number (for constant folding)
- -- * used in constfolding(), infix()
- ------------------------------------------------------------------------
- function luaK:isnumeral(e)
- return e.k == "VKNUM" and e.t == self.NO_JUMP and e.f == self.NO_JUMP
- end
- ------------------------------------------------------------------------
- -- codes loading of nil, optimization done if consecutive locations
- -- * used in luaK:discharge2reg(), (lparser) luaY:adjust_assign()
- ------------------------------------------------------------------------
- function luaK:_nil(fs, from, n)
- if fs.pc > fs.lasttarget then -- no jumps to current position?
- if fs.pc == 0 then -- function start?
- if from >= fs.nactvar then
- return -- positions are already clean
- end
- else
- local previous = fs.f.code[fs.pc - 1]
- if luaP:GET_OPCODE(previous) == "OP_LOADNIL" then
- local pfrom = luaP:GETARG_A(previous)
- local pto = luaP:GETARG_B(previous)
- if pfrom <= from and from <= pto + 1 then -- can connect both?
- if from + n - 1 > pto then
- luaP:SETARG_B(previous, from + n - 1)
- end
- return
- end
- end
- end
- end
- self:codeABC(fs, "OP_LOADNIL", from, from + n - 1, 0) -- else no optimization
- end
- ------------------------------------------------------------------------
- --
- -- * used in multiple locations
- ------------------------------------------------------------------------
- function luaK:jump(fs)
- local jpc = fs.jpc -- save list of jumps to here
- fs.jpc = self.NO_JUMP
- local j = self:codeAsBx(fs, "OP_JMP", 0, self.NO_JUMP)
- j = self:concat(fs, j, jpc) -- keep them on hold
- return j
- end
- ------------------------------------------------------------------------
- -- codes a RETURN instruction
- -- * used in luaY:close_func(), luaY:retstat()
- ------------------------------------------------------------------------
- function luaK:ret(fs, first, nret)
- self:codeABC(fs, "OP_RETURN", first, nret + 1, 0)
- end
- ------------------------------------------------------------------------
- --
- -- * used in luaK:jumponcond(), luaK:codecomp()
- ------------------------------------------------------------------------
- function luaK:condjump(fs, op, A, B, C)
- self:codeABC(fs, op, A, B, C)
- return self:jump(fs)
- end
- ------------------------------------------------------------------------
- --
- -- * used in luaK:patchlistaux(), luaK:concat()
- ------------------------------------------------------------------------
- function luaK:fixjump(fs, pc, dest)
- local jmp = fs.f.code[pc]
- local offset = dest - (pc + 1)
- assert(dest ~= self.NO_JUMP)
- if math.abs(offset) > luaP.MAXARG_sBx then
- luaX:syntaxerror(fs.ls, "control structure too long")
- end
- luaP:SETARG_sBx(jmp, offset)
- end
- ------------------------------------------------------------------------
- -- returns current 'pc' and marks it as a jump target (to avoid wrong
- -- optimizations with consecutive instructions not in the same basic block).
- -- * used in multiple locations
- -- * fs.lasttarget tested only by luaK:_nil() when optimizing OP_LOADNIL
- ------------------------------------------------------------------------
- function luaK:getlabel(fs)
- fs.lasttarget = fs.pc
- return fs.pc
- end
- ------------------------------------------------------------------------
- --
- -- * used in luaK:need_value(), luaK:removevalues(), luaK:patchlistaux(),
- -- luaK:concat()
- ------------------------------------------------------------------------
- function luaK:getjump(fs, pc)
- local offset = luaP:GETARG_sBx(fs.f.code[pc])
- if offset == self.NO_JUMP then -- point to itself represents end of list
- return self.NO_JUMP -- end of list
- else
- return (pc + 1) + offset -- turn offset into absolute position
- end
- end
- ------------------------------------------------------------------------
- --
- -- * used in luaK:need_value(), luaK:patchtestreg(), luaK:invertjump()
- ------------------------------------------------------------------------
- function luaK:getjumpcontrol(fs, pc)
- local pi = fs.f.code[pc]
- local ppi = fs.f.code[pc - 1]
- if pc >= 1 and luaP:testTMode(luaP:GET_OPCODE(ppi)) ~= 0 then
- return ppi
- else
- return pi
- end
- end
- ------------------------------------------------------------------------
- -- check whether list has any jump that do not produce a value
- -- (or produce an inverted value)
- -- * return value changed to boolean
- -- * used only in luaK:exp2reg()
- ------------------------------------------------------------------------
- function luaK:need_value(fs, list)
- while list ~= self.NO_JUMP do
- local i = self:getjumpcontrol(fs, list)
- if luaP:GET_OPCODE(i) ~= "OP_TESTSET" then return true end
- list = self:getjump(fs, list)
- end
- return false -- not found
- end
- ------------------------------------------------------------------------
- --
- -- * used in luaK:removevalues(), luaK:patchlistaux()
- ------------------------------------------------------------------------
- function luaK:patchtestreg(fs, node, reg)
- local i = self:getjumpcontrol(fs, node)
- if luaP:GET_OPCODE(i) ~= "OP_TESTSET" then
- return false -- cannot patch other instructions
- end
- if reg ~= luaP.NO_REG and reg ~= luaP:GETARG_B(i) then
- luaP:SETARG_A(i, reg)
- else -- no register to put value or register already has the value
- -- due to use of a table as i, i cannot be replaced by another table
- -- so the following is required; there is no change to ARG_C
- luaP:SET_OPCODE(i, "OP_TEST")
- local b = luaP:GETARG_B(i)
- luaP:SETARG_A(i, b)
- luaP:SETARG_B(i, 0)
- -- *i = CREATE_ABC(OP_TEST, GETARG_B(*i), 0, GETARG_C(*i)); /* C */
- end
- return true
- end
- ------------------------------------------------------------------------
- --
- -- * used only in luaK:codenot()
- ------------------------------------------------------------------------
- function luaK:removevalues(fs, list)
- while list ~= self.NO_JUMP do
- self:patchtestreg(fs, list, luaP.NO_REG)
- list = self:getjump(fs, list)
- end
- end
- ------------------------------------------------------------------------
- --
- -- * used in luaK:dischargejpc(), luaK:patchlist(), luaK:exp2reg()
- ------------------------------------------------------------------------
- function luaK:patchlistaux(fs, list, vtarget, reg, dtarget)
- while list ~= self.NO_JUMP do
- local _next = self:getjump(fs, list)
- if self:patchtestreg(fs, list, reg) then
- self:fixjump(fs, list, vtarget)
- else
- self:fixjump(fs, list, dtarget) -- jump to default target
- end
- list = _next
- end
- end
- ------------------------------------------------------------------------
- --
- -- * used only in luaK:code()
- ------------------------------------------------------------------------
- function luaK:dischargejpc(fs)
- self:patchlistaux(fs, fs.jpc, fs.pc, luaP.NO_REG, fs.pc)
- fs.jpc = self.NO_JUMP
- end
- ------------------------------------------------------------------------
- --
- -- * used in (lparser) luaY:whilestat(), luaY:repeatstat(), luaY:forbody()
- ------------------------------------------------------------------------
- function luaK:patchlist(fs, list, target)
- if target == fs.pc then
- self:patchtohere(fs, list)
- else
- assert(target < fs.pc)
- self:patchlistaux(fs, list, target, luaP.NO_REG, target)
- end
- end
- ------------------------------------------------------------------------
- --
- -- * used in multiple locations
- ------------------------------------------------------------------------
- function luaK:patchtohere(fs, list)
- self:getlabel(fs)
- fs.jpc = self:concat(fs, fs.jpc, list)
- end
- ------------------------------------------------------------------------
- -- * l1 was a pointer, now l1 is returned and callee assigns the value
- -- * used in multiple locations
- ------------------------------------------------------------------------
- function luaK:concat(fs, l1, l2)
- if l2 == self.NO_JUMP then return l1
- elseif l1 == self.NO_JUMP then
- return l2
- else
- local list = l1
- local _next = self:getjump(fs, list)
- while _next ~= self.NO_JUMP do -- find last element
- list = _next
- _next = self:getjump(fs, list)
- end
- self:fixjump(fs, list, l2)
- end
- return l1
- end
- ------------------------------------------------------------------------
- --
- -- * used in luaK:reserveregs(), (lparser) luaY:forlist()
- ------------------------------------------------------------------------
- function luaK:checkstack(fs, n)
- local newstack = fs.freereg + n
- if newstack > fs.f.maxstacksize then
- if newstack >= self.MAXSTACK then
- luaX:syntaxerror(fs.ls, "function or expression too complex")
- end
- fs.f.maxstacksize = newstack
- end
- end
- ------------------------------------------------------------------------
- --
- -- * used in multiple locations
- ------------------------------------------------------------------------
- function luaK:reserveregs(fs, n)
- self:checkstack(fs, n)
- fs.freereg = fs.freereg + n
- end
- ------------------------------------------------------------------------
- --
- -- * used in luaK:freeexp(), luaK:dischargevars()
- ------------------------------------------------------------------------
- function luaK:freereg(fs, reg)
- if not luaP:ISK(reg) and reg >= fs.nactvar then
- fs.freereg = fs.freereg - 1
- assert(reg == fs.freereg)
- end
- end
- ------------------------------------------------------------------------
- --
- -- * used in multiple locations
- ------------------------------------------------------------------------
- function luaK:freeexp(fs, e)
- if e.k == "VNONRELOC" then
- self:freereg(fs, e.info)
- end
- end
- ------------------------------------------------------------------------
- -- * TODO NOTE implementation is not 100% correct, since the assert fails
- -- * luaH_set, setobj deleted; direct table access used instead
- -- * used in luaK:stringK(), luaK:numberK(), luaK:boolK(), luaK:nilK()
- ------------------------------------------------------------------------
- function luaK:addk(fs, k, v)
- local L = fs.L
- local idx = fs.h[k.value]
- --TValue *idx = luaH_set(L, fs->h, k); /* C */
- local f = fs.f
- if self:ttisnumber(idx) then
- --TODO this assert currently FAILS (last tested for 5.0.2)
- --assert(fs.f.k[self:nvalue(idx)] == v)
- --assert(luaO_rawequalObj(&fs->f->k[cast_int(nvalue(idx))], v)); /* C */
- return self:nvalue(idx)
- else -- constant not found; create a new entry
- idx = {}
- self:setnvalue(idx, fs.nk)
- fs.h[k.value] = idx
- -- setnvalue(idx, cast_num(fs->nk)); /* C */
- luaY:growvector(L, f.k, fs.nk, f.sizek, nil,
- luaP.MAXARG_Bx, "constant table overflow")
- -- loop to initialize empty f.k positions not required
- f.k[fs.nk] = v
- -- setobj(L, &f->k[fs->nk], v); /* C */
- -- luaC_barrier(L, f, v); /* GC */
- local nk = fs.nk
- fs.nk = fs.nk + 1
- return nk
- end
- end
- ------------------------------------------------------------------------
- -- creates and sets a string object
- -- * used in (lparser) luaY:codestring(), luaY:singlevar()
- ------------------------------------------------------------------------
- function luaK:stringK(fs, s)
- local o = {} -- TValue
- self:setsvalue(o, s)
- return self:addk(fs, o, o)
- end
- ------------------------------------------------------------------------
- -- creates and sets a number object
- -- * used in luaK:prefix() for negative (or negation of) numbers
- -- * used in (lparser) luaY:simpleexp(), luaY:fornum()
- ------------------------------------------------------------------------
- function luaK:numberK(fs, r)
- local o = {} -- TValue
- self:setnvalue(o, r)
- return self:addk(fs, o, o)
- end
- ------------------------------------------------------------------------
- -- creates and sets a boolean object
- -- * used only in luaK:exp2RK()
- ------------------------------------------------------------------------
- function luaK:boolK(fs, b)
- local o = {} -- TValue
- self:setbvalue(o, b)
- return self:addk(fs, o, o)
- end
- ------------------------------------------------------------------------
- -- creates and sets a nil object
- -- * used only in luaK:exp2RK()
- ------------------------------------------------------------------------
- function luaK:nilK(fs)
- local k, v = {}, {} -- TValue
- self:setnilvalue(v)
- -- cannot use nil as key; instead use table itself to represent nil
- self:sethvalue(k, fs.h)
- return self:addk(fs, k, v)
- end
- ------------------------------------------------------------------------
- --
- -- * used in luaK:setmultret(), (lparser) luaY:adjust_assign()
- ------------------------------------------------------------------------
- function luaK:setreturns(fs, e, nresults)
- if e.k == "VCALL" then -- expression is an open function call?
- luaP:SETARG_C(self:getcode(fs, e), nresults + 1)
- elseif e.k == "VVARARG" then
- luaP:SETARG_B(self:getcode(fs, e), nresults + 1);
- luaP:SETARG_A(self:getcode(fs, e), fs.freereg);
- luaK:reserveregs(fs, 1)
- end
- end
- ------------------------------------------------------------------------
- --
- -- * used in luaK:dischargevars(), (lparser) luaY:assignment()
- ------------------------------------------------------------------------
- function luaK:setoneret(fs, e)
- if e.k == "VCALL" then -- expression is an open function call?
- e.k = "VNONRELOC"
- e.info = luaP:GETARG_A(self:getcode(fs, e))
- elseif e.k == "VVARARG" then
- luaP:SETARG_B(self:getcode(fs, e), 2)
- e.k = "VRELOCABLE" -- can relocate its simple result
- end
- end
- ------------------------------------------------------------------------
- --
- -- * used in multiple locations
- ------------------------------------------------------------------------
- function luaK:dischargevars(fs, e)
- local k = e.k
- if k == "VLOCAL" then
- e.k = "VNONRELOC"
- elseif k == "VUPVAL" then
- e.info = self:codeABC(fs, "OP_GETUPVAL", 0, e.info, 0)
- e.k = "VRELOCABLE"
- elseif k == "VGLOBAL" then
- e.info = self:codeABx(fs, "OP_GETGLOBAL", 0, e.info)
- e.k = "VRELOCABLE"
- elseif k == "VINDEXED" then
- self:freereg(fs, e.aux)
- self:freereg(fs, e.info)
- e.info = self:codeABC(fs, "OP_GETTABLE", 0, e.info, e.aux)
- e.k = "VRELOCABLE"
- elseif k == "VVARARG" or k == "VCALL" then
- self:setoneret(fs, e)
- else
- -- there is one value available (somewhere)
- end
- end
- ------------------------------------------------------------------------
- --
- -- * used only in luaK:exp2reg()
- ------------------------------------------------------------------------
- function luaK:code_label(fs, A, b, jump)
- self:getlabel(fs) -- those instructions may be jump targets
- return self:codeABC(fs, "OP_LOADBOOL", A, b, jump)
- end
- ------------------------------------------------------------------------
- --
- -- * used in luaK:discharge2anyreg(), luaK:exp2reg()
- ------------------------------------------------------------------------
- function luaK:discharge2reg(fs, e, reg)
- self:dischargevars(fs, e)
- local k = e.k
- if k == "VNIL" then
- self:_nil(fs, reg, 1)
- elseif k == "VFALSE" or k == "VTRUE" then
- self:codeABC(fs, "OP_LOADBOOL", reg, (e.k == "VTRUE") and 1 or 0, 0)
- elseif k == "VK" then
- self:codeABx(fs, "OP_LOADK", reg, e.info)
- elseif k == "VKNUM" then
- self:codeABx(fs, "OP_LOADK", reg, self:numberK(fs, e.nval))
- elseif k == "VRELOCABLE" then
- local pc = self:getcode(fs, e)
- luaP:SETARG_A(pc, reg)
- elseif k == "VNONRELOC" then
- if reg ~= e.info then
- self:codeABC(fs, "OP_MOVE", reg, e.info, 0)
- end
- else
- assert(e.k == "VVOID" or e.k == "VJMP")
- return -- nothing to do...
- end
- e.info = reg
- e.k = "VNONRELOC"
- end
- ------------------------------------------------------------------------
- --
- -- * used in luaK:jumponcond(), luaK:codenot()
- ------------------------------------------------------------------------
- function luaK:discharge2anyreg(fs, e)
- if e.k ~= "VNONRELOC" then
- self:reserveregs(fs, 1)
- self:discharge2reg(fs, e, fs.freereg - 1)
- end
- end
- ------------------------------------------------------------------------
- --
- -- * used in luaK:exp2nextreg(), luaK:exp2anyreg(), luaK:storevar()
- ------------------------------------------------------------------------
- function luaK:exp2reg(fs, e, reg)
- self:discharge2reg(fs, e, reg)
- if e.k == "VJMP" then
- e.t = self:concat(fs, e.t, e.info) -- put this jump in 't' list
- end
- if self:hasjumps(e) then
- local final -- position after whole expression
- local p_f = self.NO_JUMP -- position of an eventual LOAD false
- local p_t = self.NO_JUMP -- position of an eventual LOAD true
- if self:need_value(fs, e.t) or self:need_value(fs, e.f) then
- local fj = (e.k == "VJMP") and self.NO_JUMP or self:jump(fs)
- p_f = self:code_label(fs, reg, 0, 1)
- p_t = self:code_label(fs, reg, 1, 0)
- self:patchtohere(fs, fj)
- end
- final = self:getlabel(fs)
- self:patchlistaux(fs, e.f, final, reg, p_f)
- self:patchlistaux(fs, e.t, final, reg, p_t)
- end
- e.f, e.t = self.NO_JUMP, self.NO_JUMP
- e.info = reg
- e.k = "VNONRELOC"
- end
- ------------------------------------------------------------------------
- --
- -- * used in multiple locations
- ------------------------------------------------------------------------
- function luaK:exp2nextreg(fs, e)
- self:dischargevars(fs, e)
- self:freeexp(fs, e)
- self:reserveregs(fs, 1)
- self:exp2reg(fs, e, fs.freereg - 1)
- end
- ------------------------------------------------------------------------
- --
- -- * used in multiple locations
- ------------------------------------------------------------------------
- function luaK:exp2anyreg(fs, e)
- self:dischargevars(fs, e)
- if e.k == "VNONRELOC" then
- if not self:hasjumps(e) then -- exp is already in a register
- return e.info
- end
- if e.info >= fs.nactvar then -- reg. is not a local?
- self:exp2reg(fs, e, e.info) -- put value on it
- return e.info
- end
- end
- self:exp2nextreg(fs, e) -- default
- return e.info
- end
- ------------------------------------------------------------------------
- --
- -- * used in luaK:exp2RK(), luaK:prefix(), luaK:posfix()
- -- * used in (lparser) luaY:yindex()
- ------------------------------------------------------------------------
- function luaK:exp2val(fs, e)
- if self:hasjumps(e) then
- self:exp2anyreg(fs, e)
- else
- self:dischargevars(fs, e)
- end
- end
- ------------------------------------------------------------------------
- --
- -- * used in multiple locations
- ------------------------------------------------------------------------
- function luaK:exp2RK(fs, e)
- self:exp2val(fs, e)
- local k = e.k
- if k == "VKNUM" or k == "VTRUE" or k == "VFALSE" or k == "VNIL" then
- if fs.nk <= luaP.MAXINDEXRK then -- constant fit in RK operand?
- -- converted from a 2-deep ternary operator expression
- if e.k == "VNIL" then
- e.info = self:nilK(fs)
- else
- e.info = (e.k == "VKNUM") and self:numberK(fs, e.nval)
- or self:boolK(fs, e.k == "VTRUE")
- end
- e.k = "VK"
- return luaP:RKASK(e.info)
- end
- elseif k == "VK" then
- if e.info <= luaP.MAXINDEXRK then -- constant fit in argC?
- return luaP:RKASK(e.info)
- end
- else
- -- default
- end
- -- not a constant in the right range: put it in a register
- return self:exp2anyreg(fs, e)
- end
- ------------------------------------------------------------------------
- --
- -- * used in (lparser) luaY:assignment(), luaY:localfunc(), luaY:funcstat()
- ------------------------------------------------------------------------
- function luaK:storevar(fs, var, ex)
- local k = var.k
- if k == "VLOCAL" then
- self:freeexp(fs, ex)
- self:exp2reg(fs, ex, var.info)
- return
- elseif k == "VUPVAL" then
- local e = self:exp2anyreg(fs, ex)
- self:codeABC(fs, "OP_SETUPVAL", e, var.info, 0)
- elseif k == "VGLOBAL" then
- local e = self:exp2anyreg(fs, ex)
- self:codeABx(fs, "OP_SETGLOBAL", e, var.info)
- elseif k == "VINDEXED" then
- local e = self:exp2RK(fs, ex)
- self:codeABC(fs, "OP_SETTABLE", var.info, var.aux, e)
- else
- assert(0) -- invalid var kind to store
- end
- self:freeexp(fs, ex)
- end
- ------------------------------------------------------------------------
- --
- -- * used only in (lparser) luaY:primaryexp()
- ------------------------------------------------------------------------
- function luaK:_self(fs, e, key)
- self:exp2anyreg(fs, e)
- self:freeexp(fs, e)
- local func = fs.freereg
- self:reserveregs(fs, 2)
- self:codeABC(fs, "OP_SELF", func, e.info, self:exp2RK(fs, key))
- self:freeexp(fs, key)
- e.info = func
- e.k = "VNONRELOC"
- end
- ------------------------------------------------------------------------
- --
- -- * used in luaK:goiftrue(), luaK:codenot()
- ------------------------------------------------------------------------
- function luaK:invertjump(fs, e)
- local pc = self:getjumpcontrol(fs, e.info)
- assert(luaP:testTMode(luaP:GET_OPCODE(pc)) ~= 0 and
- luaP:GET_OPCODE(pc) ~= "OP_TESTSET" and
- luaP:GET_OPCODE(pc) ~= "OP_TEST")
- luaP:SETARG_A(pc, (luaP:GETARG_A(pc) == 0) and 1 or 0)
- end
- ------------------------------------------------------------------------
- --
- -- * used in luaK:goiftrue(), luaK:goiffalse()
- ------------------------------------------------------------------------
- function luaK:jumponcond(fs, e, cond)
- if e.k == "VRELOCABLE" then
- local ie = self:getcode(fs, e)
- if luaP:GET_OPCODE(ie) == "OP_NOT" then
- fs.pc = fs.pc - 1 -- remove previous OP_NOT
- return self:condjump(fs, "OP_TEST", luaP:GETARG_B(ie), 0, cond and 0 or 1)
- end
- -- else go through
- end
- self:discharge2anyreg(fs, e)
- self:freeexp(fs, e)
- return self:condjump(fs, "OP_TESTSET", luaP.NO_REG, e.info, cond and 1 or 0)
- end
- ------------------------------------------------------------------------
- --
- -- * used in luaK:infix(), (lparser) luaY:cond()
- ------------------------------------------------------------------------
- function luaK:goiftrue(fs, e)
- local pc -- pc of last jump
- self:dischargevars(fs, e)
- local k = e.k
- if k == "VK" or k == "VKNUM" or k == "VTRUE" then
- pc = self.NO_JUMP -- always true; do nothing
- elseif k == "VFALSE" then
- pc = self:jump(fs) -- always jump
- elseif k == "VJMP" then
- self:invertjump(fs, e)
- pc = e.info
- else
- pc = self:jumponcond(fs, e, false)
- end
- e.f = self:concat(fs, e.f, pc) -- insert last jump in `f' list
- self:patchtohere(fs, e.t)
- e.t = self.NO_JUMP
- end
- ------------------------------------------------------------------------
- --
- -- * used in luaK:infix()
- ------------------------------------------------------------------------
- function luaK:goiffalse(fs, e)
- local pc -- pc of last jump
- self:dischargevars(fs, e)
- local k = e.k
- if k == "VNIL" or k == "VFALSE"then
- pc = self.NO_JUMP -- always false; do nothing
- elseif k == "VTRUE" then
- pc = self:jump(fs) -- always jump
- elseif k == "VJMP" then
- pc = e.info
- else
- pc = self:jumponcond(fs, e, true)
- end
- e.t = self:concat(fs, e.t, pc) -- insert last jump in `t' list
- self:patchtohere(fs, e.f)
- e.f = self.NO_JUMP
- end
- ------------------------------------------------------------------------
- --
- -- * used only in luaK:prefix()
- ------------------------------------------------------------------------
- function luaK:codenot(fs, e)
- self:dischargevars(fs, e)
- local k = e.k
- if k == "VNIL" or k == "VFALSE" then
- e.k = "VTRUE"
- elseif k == "VK" or k == "VKNUM" or k == "VTRUE" then
- e.k = "VFALSE"
- elseif k == "VJMP" then
- self:invertjump(fs, e)
- elseif k == "VRELOCABLE" or k == "VNONRELOC" then
- self:discharge2anyreg(fs, e)
- self:freeexp(fs, e)
- e.info = self:codeABC(fs, "OP_NOT", 0, e.info, 0)
- e.k = "VRELOCABLE"
- else
- assert(0) -- cannot happen
- end
- -- interchange true and false lists
- e.f, e.t = e.t, e.f
- self:removevalues(fs, e.f)
- self:removevalues(fs, e.t)
- end
- ------------------------------------------------------------------------
- --
- -- * used in (lparser) luaY:field(), luaY:primaryexp()
- ------------------------------------------------------------------------
- function luaK:indexed(fs, t, k)
- t.aux = self:exp2RK(fs, k)
- t.k = "VINDEXED"
- end
- ------------------------------------------------------------------------
- --
- -- * used only in luaK:codearith()
- ------------------------------------------------------------------------
- function luaK:constfolding(op, e1, e2)
- local r
- if not self:isnumeral(e1) or not self:isnumeral(e2) then return false end
- local v1 = e1.nval
- local v2 = e2.nval
- if op == "OP_ADD" then
- r = self:numadd(v1, v2)
- elseif op == "OP_SUB" then
- r = self:numsub(v1, v2)
- elseif op == "OP_MUL" then
- r = self:nummul(v1, v2)
- elseif op == "OP_DIV" then
- if v2 == 0 then return false end -- do not attempt to divide by 0
- r = self:numdiv(v1, v2)
- elseif op == "OP_MOD" then
- if v2 == 0 then return false end -- do not attempt to divide by 0
- r = self:nummod(v1, v2)
- elseif op == "OP_POW" then
- r = self:numpow(v1, v2)
- elseif op == "OP_UNM" then
- r = self:numunm(v1)
- elseif op == "OP_LEN" then
- return false -- no constant folding for 'len'
- else
- assert(0)
- r = 0
- end
- if self:numisnan(r) then return false end -- do not attempt to produce NaN
- e1.nval = r
- return true
- end
- ------------------------------------------------------------------------
- --
- -- * used in luaK:prefix(), luaK:posfix()
- ------------------------------------------------------------------------
- function luaK:codearith(fs, op, e1, e2)
- if self:constfolding(op, e1, e2) then
- return
- else
- local o2 = (op ~= "OP_UNM" and op ~= "OP_LEN") and self:exp2RK(fs, e2) or 0
- local o1 = self:exp2RK(fs, e1)
- if o1 > o2 then
- self:freeexp(fs, e1)
- self:freeexp(fs, e2)
- else
- self:freeexp(fs, e2)
- self:freeexp(fs, e1)
- end
- e1.info = self:codeABC(fs, op, 0, o1, o2)
- e1.k = "VRELOCABLE"
- end
- end
- ------------------------------------------------------------------------
- --
- -- * used only in luaK:posfix()
- ------------------------------------------------------------------------
- function luaK:codecomp(fs, op, cond, e1, e2)
- local o1 = self:exp2RK(fs, e1)
- local o2 = self:exp2RK(fs, e2)
- self:freeexp(fs, e2)
- self:freeexp(fs, e1)
- if cond == 0 and op ~= "OP_EQ" then
- -- exchange args to replace by `<' or `<='
- o1, o2 = o2, o1 -- o1 <==> o2
- cond = 1
- end
- e1.info = self:condjump(fs, op, cond, o1, o2)
- e1.k = "VJMP"
- end
- ------------------------------------------------------------------------
- --
- -- * used only in (lparser) luaY:subexpr()
- ------------------------------------------------------------------------
- function luaK:prefix(fs, op, e)
- local e2 = {} -- expdesc
- e2.t, e2.f = self.NO_JUMP, self.NO_JUMP
- e2.k = "VKNUM"
- e2.nval = 0
- if op == "OPR_MINUS" then
- if not self:isnumeral(e) then
- self:exp2anyreg(fs, e) -- cannot operate on non-numeric constants
- end
- self:codearith(fs, "OP_UNM", e, e2)
- elseif op == "OPR_NOT" then
- self:codenot(fs, e)
- elseif op == "OPR_LEN" then
- self:exp2anyreg(fs, e) -- cannot operate on constants
- self:codearith(fs, "OP_LEN", e, e2)
- else
- assert(0)
- end
- end
- ------------------------------------------------------------------------
- --
- -- * used only in (lparser) luaY:subexpr()
- ------------------------------------------------------------------------
- function luaK:infix(fs, op, v)
- if op == "OPR_AND" then
- self:goiftrue(fs, v)
- elseif op == "OPR_OR" then
- self:goiffalse(fs, v)
- elseif op == "OPR_CONCAT" then
- self:exp2nextreg(fs, v) -- operand must be on the 'stack'
- elseif op == "OPR_ADD" or op == "OPR_SUB" or
- op == "OPR_MUL" or op == "OPR_DIV" or
- op == "OPR_MOD" or op == "OPR_POW" then
- if not self:isnumeral(v) then self:exp2RK(fs, v) end
- else
- self:exp2RK(fs, v)
- end
- end
- ------------------------------------------------------------------------
- --
- -- * used only in (lparser) luaY:subexpr()
- ------------------------------------------------------------------------
- -- table lookups to simplify testing
- luaK.arith_op = {
- OPR_ADD = "OP_ADD", OPR_SUB = "OP_SUB", OPR_MUL = "OP_MUL",
- OPR_DIV = "OP_DIV", OPR_MOD = "OP_MOD", OPR_POW = "OP_POW",
- }
- luaK.comp_op = {
- OPR_EQ = "OP_EQ", OPR_NE = "OP_EQ", OPR_LT = "OP_LT",
- OPR_LE = "OP_LE", OPR_GT = "OP_LT", OPR_GE = "OP_LE",
- }
- luaK.comp_cond = {
- OPR_EQ = 1, OPR_NE = 0, OPR_LT = 1,
- OPR_LE = 1, OPR_GT = 0, OPR_GE = 0,
- }
- function luaK:posfix(fs, op, e1, e2)
- -- needed because e1 = e2 doesn't copy values...
- -- * in 5.0.x, only k/info/aux/t/f copied, t for AND, f for OR
- -- but here, all elements are copied for completeness' sake
- local function copyexp(e1, e2)
- e1.k = e2.k
- e1.info = e2.info; e1.aux = e2.aux
- e1.nval = e2.nval
- e1.t = e2.t; e1.f = e2.f
- end
- if op == "OPR_AND" then
- assert(e1.t == self.NO_JUMP) -- list must be closed
- self:dischargevars(fs, e2)
- e2.f = self:concat(fs, e2.f, e1.f)
- copyexp(e1, e2)
- elseif op == "OPR_OR" then
- assert(e1.f == self.NO_JUMP) -- list must be closed
- self:dischargevars(fs, e2)
- e2.t = self:concat(fs, e2.t, e1.t)
- copyexp(e1, e2)
- elseif op == "OPR_CONCAT" then
- self:exp2val(fs, e2)
- if e2.k == "VRELOCABLE" and luaP:GET_OPCODE(self:getcode(fs, e2)) == "OP_CONCAT" then
- assert(e1.info == luaP:GETARG_B(self:getcode(fs, e2)) - 1)
- self:freeexp(fs, e1)
- luaP:SETARG_B(self:getcode(fs, e2), e1.info)
- e1.k = "VRELOCABLE"
- e1.info = e2.info
- else
- self:exp2nextreg(fs, e2) -- operand must be on the 'stack'
- self:codearith(fs, "OP_CONCAT", e1, e2)
- end
- else
- -- the following uses a table lookup in place of conditionals
- local arith = self.arith_op[op]
- if arith then
- self:codearith(fs, arith, e1, e2)
- else
- local comp = self.comp_op[op]
- if comp then
- self:codecomp(fs, comp, self.comp_cond[op], e1, e2)
- else
- assert(0)
- end
- end--if arith
- end--if op
- end
- ------------------------------------------------------------------------
- -- adjusts debug information for last instruction written, in order to
- -- change the line where item comes into existence
- -- * used in (lparser) luaY:funcargs(), luaY:forbody(), luaY:funcstat()
- ------------------------------------------------------------------------
- function luaK:fixline(fs, line)
- fs.f.lineinfo[fs.pc - 1] = line
- end
- ------------------------------------------------------------------------
- -- general function to write an instruction into the instruction buffer,
- -- sets debug information too
- -- * used in luaK:codeABC(), luaK:codeABx()
- -- * called directly by (lparser) luaY:whilestat()
- ------------------------------------------------------------------------
- function luaK:code(fs, i, line)
- local f = fs.f
- self:dischargejpc(fs) -- 'pc' will change
- -- put new instruction in code array
- luaY:growvector(fs.L, f.code, fs.pc, f.sizecode, nil,
- luaY.MAX_INT, "code size overflow")
- f.code[fs.pc] = i
- -- save corresponding line information
- luaY:growvector(fs.L, f.lineinfo, fs.pc, f.sizelineinfo, nil,
- luaY.MAX_INT, "code size overflow")
- f.lineinfo[fs.pc] = line
- local pc = fs.pc
- fs.pc = fs.pc + 1
- return pc
- end
- ------------------------------------------------------------------------
- -- writes an instruction of type ABC
- -- * calls luaK:code()
- ------------------------------------------------------------------------
- function luaK:codeABC(fs, o, a, b, c)
- assert(luaP:getOpMode(o) == luaP.OpMode.iABC)
- assert(luaP:getBMode(o) ~= luaP.OpArgMask.OpArgN or b == 0)
- assert(luaP:getCMode(o) ~= luaP.OpArgMask.OpArgN or c == 0)
- return self:code(fs, luaP:CREATE_ABC(o, a, b, c), fs.ls.lastline)
- end
- ------------------------------------------------------------------------
- -- writes an instruction of type ABx
- -- * calls luaK:code(), called by luaK:codeAsBx()
- ------------------------------------------------------------------------
- function luaK:codeABx(fs, o, a, bc)
- assert(luaP:getOpMode(o) == luaP.OpMode.iABx or
- luaP:getOpMode(o) == luaP.OpMode.iAsBx)
- assert(luaP:getCMode(o) == luaP.OpArgMask.OpArgN)
- return self:code(fs, luaP:CREATE_ABx(o, a, bc), fs.ls.lastline)
- end
- ------------------------------------------------------------------------
- --
- -- * used in (lparser) luaY:closelistfield(), luaY:lastlistfield()
- ------------------------------------------------------------------------
- function luaK:setlist(fs, base, nelems, tostore)
- local c = math.floor((nelems - 1)/luaP.LFIELDS_PER_FLUSH) + 1
- local b = (tostore == luaY.LUA_MULTRET) and 0 or tostore
- assert(tostore ~= 0)
- if c <= luaP.MAXARG_C then
- self:codeABC(fs, "OP_SETLIST", base, b, c)
- else
- self:codeABC(fs, "OP_SETLIST", base, b, 0)
- self:code(fs, luaP:CREATE_Inst(c), fs.ls.lastline)
- end
- fs.freereg = base + 1 -- free registers with list values
- end
- return function(a) luaY = a return luaK end
- end
- fake_module_scripts[script] = module_script
- end
- do -- nil.LuaU
- local script = Instance.new('ModuleScript', nil)
- script.Name = "LuaU"
- local function module_script()
- --[[--------------------------------------------------------------------
- ldump.lua
- Save precompiled Lua chunks
- This file is part of Yueliang.
- Copyright (c) 2006 Kein-Hong Man <khman@users.sf.net>
- The COPYRIGHT file describes the conditions
- under which this software may be distributed.
- See the ChangeLog for more information.
- ----------------------------------------------------------------------]]
- --[[--------------------------------------------------------------------
- -- Notes:
- -- * WARNING! byte order (little endian) and data type sizes for header
- -- signature values hard-coded; see luaU:header
- -- * chunk writer generators are included, see below
- -- * one significant difference is that instructions are still in table
- -- form (with OP/A/B/C/Bx fields) and luaP:Instruction() is needed to
- -- convert them into 4-char strings
- --
- -- Not implemented:
- -- * DumpVar, DumpMem has been removed
- -- * DumpVector folded into folded into DumpDebug, DumpCode
- --
- -- Added:
- -- * for convenience, the following two functions have been added:
- -- luaU:make_setS: create a chunk writer that writes to a string
- -- luaU:make_setF: create a chunk writer that writes to a file
- -- (lua.h contains a typedef for lua_Writer/lua_Chunkwriter, and
- -- a Lua-based implementation exists, writer() in lstrlib.c)
- -- * luaU:ttype(o) (from lobject.h)
- -- * for converting number types to its binary equivalent:
- -- luaU:from_double(x): encode double value for writing
- -- luaU:from_int(x): encode integer value for writing
- -- (error checking is limited for these conversion functions)
- -- (double conversion does not support denormals or NaNs)
- --
- -- Changed in 5.1.x:
- -- * the dumper was mostly rewritten in Lua 5.1.x, so notes on the
- -- differences between 5.0.x and 5.1.x is limited
- -- * LUAC_VERSION bumped to 0x51, LUAC_FORMAT added
- -- * developer is expected to adjust LUAC_FORMAT in order to identify
- -- non-standard binary chunk formats
- -- * header signature code is smaller, has been simplified, and is
- -- tested as a single unit; its logic is shared with the undumper
- -- * no more endian conversion, invalid endianness mean rejection
- -- * opcode field sizes are no longer exposed in the header
- -- * code moved to front of a prototype, followed by constants
- -- * debug information moved to the end of the binary chunk, and the
- -- relevant functions folded into a single function
- -- * luaU:dump returns a writer status code
- -- * chunk writer now implements status code because dumper uses it
- -- * luaU:endianness removed
- ----------------------------------------------------------------------]]
- --requires luaP
- local luaU = {}
- local luaP = require(script.Parent.LuaP)
- -- mark for precompiled code ('<esc>Lua') (from lua.h)
- luaU.LUA_SIGNATURE = "\27Lua"
- -- constants used by dumper (from lua.h)
- luaU.LUA_TNUMBER = 3
- luaU.LUA_TSTRING = 4
- luaU.LUA_TNIL = 0
- luaU.LUA_TBOOLEAN = 1
- luaU.LUA_TNONE = -1
- -- constants for header of binary files (from lundump.h)
- luaU.LUAC_VERSION = 0x51 -- this is Lua 5.1
- luaU.LUAC_FORMAT = 0 -- this is the official format
- luaU.LUAC_HEADERSIZE = 12 -- size of header of binary files
- --[[--------------------------------------------------------------------
- -- Additional functions to handle chunk writing
- -- * to use make_setS and make_setF, see test_ldump.lua elsewhere
- ----------------------------------------------------------------------]]
- ------------------------------------------------------------------------
- -- create a chunk writer that writes to a string
- -- * returns the writer function and a table containing the string
- -- * to get the final result, look in buff.data
- ------------------------------------------------------------------------
- function luaU:make_setS()
- local buff = {}
- buff.data = ""
- local writer =
- function(s, buff) -- chunk writer
- if not s then return 0 end
- buff.data = buff.data..s
- return 0
- end
- return writer, buff
- end
- ------------------------------------------------------------------------
- -- create a chunk writer that writes to a file
- -- * returns the writer function and a table containing the file handle
- -- * if a nil is passed, then writer should close the open file
- ------------------------------------------------------------------------
- --[[
- function luaU:make_setF(filename)
- local buff = {}
- buff.h = io.open(filename, "wb")
- if not buff.h then return nil end
- local writer =
- function(s, buff) -- chunk writer
- if not buff.h then return 0 end
- if not s then
- if buff.h:close() then return 0 end
- else
- if buff.h:write(s) then return 0 end
- end
- return 1
- end
- return writer, buff
- end--]]
- ------------------------------------------------------------------------
- -- works like the lobject.h version except that TObject used in these
- -- scripts only has a 'value' field, no 'tt' field (native types used)
- ------------------------------------------------------------------------
- function luaU:ttype(o)
- local tt = type(o.value)
- if tt == "number" then return self.LUA_TNUMBER
- elseif tt == "string" then return self.LUA_TSTRING
- elseif tt == "nil" then return self.LUA_TNIL
- elseif tt == "boolean" then return self.LUA_TBOOLEAN
- else
- return self.LUA_TNONE -- the rest should not appear
- end
- end
- -----------------------------------------------------------------------
- -- converts a IEEE754 double number to an 8-byte little-endian string
- -- * luaU:from_double() and luaU:from_int() are adapted from ChunkBake
- -- * supports +/- Infinity, but not denormals or NaNs
- -----------------------------------------------------------------------
- function luaU:from_double(x)
- local function grab_byte(v)
- local c = v % 256
- return (v - c) / 256, string.char(c)
- end
- local sign = 0
- if x < 0 then sign = 1; x = -x end
- local mantissa, exponent = math.frexp(x)
- if x == 0 then -- zero
- mantissa, exponent = 0, 0
- elseif x == 1/0 then
- mantissa, exponent = 0, 2047
- else
- mantissa = (mantissa * 2 - 1) * math.ldexp(0.5, 53)
- exponent = exponent + 1022
- end
- local v, byte = "" -- convert to bytes
- x = math.floor(mantissa)
- for i = 1,6 do
- x, byte = grab_byte(x); v = v..byte -- 47:0
- end
- x, byte = grab_byte(exponent * 16 + x); v = v..byte -- 55:48
- x, byte = grab_byte(sign * 128 + x); v = v..byte -- 63:56
- return v
- end
- -----------------------------------------------------------------------
- -- converts a number to a little-endian 32-bit integer string
- -- * input value assumed to not overflow, can be signed/unsigned
- -----------------------------------------------------------------------
- function luaU:from_int(x)
- local v = ""
- x = math.floor(x)
- if x < 0 then x = 4294967296 + x end -- ULONG_MAX+1
- for i = 1, 4 do
- local c = x % 256
- v = v..string.char(c); x = math.floor(x / 256)
- end
- return v
- end
- --[[--------------------------------------------------------------------
- -- Functions to make a binary chunk
- -- * many functions have the size parameter removed, since output is
- -- in the form of a string and some sizes are implicit or hard-coded
- ----------------------------------------------------------------------]]
- --[[--------------------------------------------------------------------
- -- struct DumpState:
- -- L -- lua_State (not used in this script)
- -- writer -- lua_Writer (chunk writer function)
- -- data -- void* (chunk writer context or data already written)
- -- strip -- if true, don't write any debug information
- -- status -- if non-zero, an error has occured
- ----------------------------------------------------------------------]]
- ------------------------------------------------------------------------
- -- dumps a block of bytes
- -- * lua_unlock(D.L), lua_lock(D.L) unused
- ------------------------------------------------------------------------
- function luaU:DumpBlock(b, D)
- if D.status == 0 then
- -- lua_unlock(D->L);
- D.status = D.write(b, D.data)
- -- lua_lock(D->L);
- end
- end
- ------------------------------------------------------------------------
- -- dumps a char
- ------------------------------------------------------------------------
- function luaU:DumpChar(y, D)
- self:DumpBlock(string.char(y), D)
- end
- ------------------------------------------------------------------------
- -- dumps a 32-bit signed or unsigned integer (for int) (hard-coded)
- ------------------------------------------------------------------------
- function luaU:DumpInt(x, D)
- self:DumpBlock(self:from_int(x), D)
- end
- ------------------------------------------------------------------------
- -- dumps a lua_Number (hard-coded as a double)
- ------------------------------------------------------------------------
- function luaU:DumpNumber(x, D)
- self:DumpBlock(self:from_double(x), D)
- end
- ------------------------------------------------------------------------
- -- dumps a Lua string (size type is hard-coded)
- ------------------------------------------------------------------------
- function luaU:DumpString(s, D)
- if s == nil then
- self:DumpInt(0, D)
- else
- s = s.."\0" -- include trailing '\0'
- self:DumpInt(#s, D)
- self:DumpBlock(s, D)
- end
- end
- ------------------------------------------------------------------------
- -- dumps instruction block from function prototype
- ------------------------------------------------------------------------
- function luaU:DumpCode(f, D)
- local n = f.sizecode
- --was DumpVector
- self:DumpInt(n, D)
- for i = 0, n - 1 do
- self:DumpBlock(luaP:Instruction(f.code[i]), D)
- end
- end
- ------------------------------------------------------------------------
- -- dump constant pool from function prototype
- -- * bvalue(o), nvalue(o) and rawtsvalue(o) macros removed
- ------------------------------------------------------------------------
- function luaU:DumpConstants(f, D)
- local n = f.sizek
- self:DumpInt(n, D)
- for i = 0, n - 1 do
- local o = f.k[i] -- TValue
- local tt = self:ttype(o)
- self:DumpChar(tt, D)
- if tt == self.LUA_TNIL then
- elseif tt == self.LUA_TBOOLEAN then
- self:DumpChar(o.value and 1 or 0, D)
- elseif tt == self.LUA_TNUMBER then
- self:DumpNumber(o.value, D)
- elseif tt == self.LUA_TSTRING then
- self:DumpString(o.value, D)
- else
- --lua_assert(0) -- cannot happen
- end
- end
- n = f.sizep
- self:DumpInt(n, D)
- for i = 0, n - 1 do
- self:DumpFunction(f.p[i], f.source, D)
- end
- end
- ------------------------------------------------------------------------
- -- dump debug information
- ------------------------------------------------------------------------
- function luaU:DumpDebug(f, D)
- local n
- n = D.strip and 0 or f.sizelineinfo -- dump line information
- --was DumpVector
- self:DumpInt(n, D)
- for i = 0, n - 1 do
- self:DumpInt(f.lineinfo[i], D)
- end
- n = D.strip and 0 or f.sizelocvars -- dump local information
- self:DumpInt(n, D)
- for i = 0, n - 1 do
- self:DumpString(f.locvars[i].varname, D)
- self:DumpInt(f.locvars[i].startpc, D)
- self:DumpInt(f.locvars[i].endpc, D)
- end
- n = D.strip and 0 or f.sizeupvalues -- dump upvalue information
- self:DumpInt(n, D)
- for i = 0, n - 1 do
- self:DumpString(f.upvalues[i], D)
- end
- end
- ------------------------------------------------------------------------
- -- dump child function prototypes from function prototype
- ------------------------------------------------------------------------
- function luaU:DumpFunction(f, p, D)
- local source = f.source
- if source == p or D.strip then source = nil end
- self:DumpString(source, D)
- self:DumpInt(f.lineDefined, D)
- self:DumpInt(f.lastlinedefined, D)
- self:DumpChar(f.nups, D)
- self:DumpChar(f.numparams, D)
- self:DumpChar(f.is_vararg, D)
- self:DumpChar(f.maxstacksize, D)
- self:DumpCode(f, D)
- self:DumpConstants(f, D)
- self:DumpDebug(f, D)
- end
- ------------------------------------------------------------------------
- -- dump Lua header section (some sizes hard-coded)
- ------------------------------------------------------------------------
- function luaU:DumpHeader(D)
- local h = self:header()
- assert(#h == self.LUAC_HEADERSIZE) -- fixed buffer now an assert
- self:DumpBlock(h, D)
- end
- ------------------------------------------------------------------------
- -- make header (from lundump.c)
- -- returns the header string
- ------------------------------------------------------------------------
- function luaU:header()
- local x = 1
- return self.LUA_SIGNATURE..
- string.char(
- self.LUAC_VERSION,
- self.LUAC_FORMAT,
- x, -- endianness (1=little)
- 4, -- sizeof(int)
- 4, -- sizeof(size_t)
- 4, -- sizeof(Instruction)
- 8, -- sizeof(lua_Number)
- 0) -- is lua_Number integral?
- end
- ------------------------------------------------------------------------
- -- dump Lua function as precompiled chunk
- -- (lua_State* L, const Proto* f, lua_Writer w, void* data, int strip)
- -- * w, data are created from make_setS, make_setF
- ------------------------------------------------------------------------
- function luaU:dump(L, f, w, data, strip)
- local D = {} -- DumpState
- D.L = L
- D.write = w
- D.data = data
- D.strip = strip
- D.status = 0
- self:DumpHeader(D)
- self:DumpFunction(f, nil, D)
- -- added: for a chunk writer writing to a file, this final call with
- -- nil data is to indicate to the writer to close the file
- D.write(nil, D.data)
- return D.status
- end
- return luaU
- end
- fake_module_scripts[script] = module_script
- end
- do -- nil.LuaP
- local script = Instance.new('ModuleScript', nil)
- script.Name = "LuaP"
- local function module_script()
- --[[--------------------------------------------------------------------
- lopcodes.lua
- Lua 5 virtual machine opcodes in Lua
- This file is part of Yueliang.
- Copyright (c) 2006 Kein-Hong Man <khman@users.sf.net>
- The COPYRIGHT file describes the conditions
- under which this software may be distributed.
- See the ChangeLog for more information.
- ----------------------------------------------------------------------]]
- --[[--------------------------------------------------------------------
- -- Notes:
- -- * an Instruction is a table with OP, A, B, C, Bx elements; this
- -- makes the code easy to follow and should allow instruction handling
- -- to work with doubles and ints
- -- * WARNING luaP:Instruction outputs instructions encoded in little-
- -- endian form and field size and positions are hard-coded
- --
- -- Not implemented:
- -- *
- --
- -- Added:
- -- * luaP:CREATE_Inst(c): create an inst from a number (for OP_SETLIST)
- -- * luaP:Instruction(i): convert field elements to a 4-char string
- -- * luaP:DecodeInst(x): convert 4-char string into field elements
- --
- -- Changed in 5.1.x:
- -- * POS_OP added, instruction field positions changed
- -- * some symbol names may have changed, e.g. LUAI_BITSINT
- -- * new operators for RK indices: BITRK, ISK(x), INDEXK(r), RKASK(x)
- -- * OP_MOD, OP_LEN is new
- -- * OP_TEST is now OP_TESTSET, OP_TEST is new
- -- * OP_FORLOOP, OP_TFORLOOP adjusted, OP_FORPREP is new
- -- * OP_TFORPREP deleted
- -- * OP_SETLIST and OP_SETLISTO merged and extended
- -- * OP_VARARG is new
- -- * many changes to implementation of OpMode data
- ----------------------------------------------------------------------]]
- local luaP = {}
- --[[
- ===========================================================================
- We assume that instructions are unsigned numbers.
- All instructions have an opcode in the first 6 bits.
- Instructions can have the following fields:
- 'A' : 8 bits
- 'B' : 9 bits
- 'C' : 9 bits
- 'Bx' : 18 bits ('B' and 'C' together)
- 'sBx' : signed Bx
- A signed argument is represented in excess K; that is, the number
- value is the unsigned value minus K. K is exactly the maximum value
- for that argument (so that -max is represented by 0, and +max is
- represented by 2*max), which is half the maximum for the corresponding
- unsigned argument.
- ===========================================================================
- --]]
- luaP.OpMode = { iABC = 0, iABx = 1, iAsBx = 2 } -- basic instruction format
- ------------------------------------------------------------------------
- -- size and position of opcode arguments.
- -- * WARNING size and position is hard-coded elsewhere in this script
- ------------------------------------------------------------------------
- luaP.SIZE_C = 9
- luaP.SIZE_B = 9
- luaP.SIZE_Bx = luaP.SIZE_C + luaP.SIZE_B
- luaP.SIZE_A = 8
- luaP.SIZE_OP = 6
- luaP.POS_OP = 0
- luaP.POS_A = luaP.POS_OP + luaP.SIZE_OP
- luaP.POS_C = luaP.POS_A + luaP.SIZE_A
- luaP.POS_B = luaP.POS_C + luaP.SIZE_C
- luaP.POS_Bx = luaP.POS_C
- ------------------------------------------------------------------------
- -- limits for opcode arguments.
- -- we use (signed) int to manipulate most arguments,
- -- so they must fit in LUAI_BITSINT-1 bits (-1 for sign)
- ------------------------------------------------------------------------
- -- removed "#if SIZE_Bx < BITS_INT-1" test, assume this script is
- -- running on a Lua VM with double or int as LUA_NUMBER
- luaP.MAXARG_Bx = math.ldexp(1, luaP.SIZE_Bx) - 1
- luaP.MAXARG_sBx = math.floor(luaP.MAXARG_Bx / 2) -- 'sBx' is signed
- luaP.MAXARG_A = math.ldexp(1, luaP.SIZE_A) - 1
- luaP.MAXARG_B = math.ldexp(1, luaP.SIZE_B) - 1
- luaP.MAXARG_C = math.ldexp(1, luaP.SIZE_C) - 1
- -- creates a mask with 'n' 1 bits at position 'p'
- -- MASK1(n,p) deleted, not required
- -- creates a mask with 'n' 0 bits at position 'p'
- -- MASK0(n,p) deleted, not required
- --[[--------------------------------------------------------------------
- Visual representation for reference:
- 31 | | | 0 bit position
- +-----+-----+-----+----------+
- | B | C | A | Opcode | iABC format
- +-----+-----+-----+----------+
- - 9 - 9 - 8 - 6 - field sizes
- +-----+-----+-----+----------+
- | [s]Bx | A | Opcode | iABx | iAsBx format
- +-----+-----+-----+----------+
- ----------------------------------------------------------------------]]
- ------------------------------------------------------------------------
- -- the following macros help to manipulate instructions
- -- * changed to a table object representation, very clean compared to
- -- the [nightmare] alternatives of using a number or a string
- -- * Bx is a separate element from B and C, since there is never a need
- -- to split Bx in the parser or code generator
- ------------------------------------------------------------------------
- -- these accept or return opcodes in the form of string names
- function luaP:GET_OPCODE(i) return self.ROpCode[i.OP] end
- function luaP:SET_OPCODE(i, o) i.OP = self.OpCode[o] end
- function luaP:GETARG_A(i) return i.A end
- function luaP:SETARG_A(i, u) i.A = u end
- function luaP:GETARG_B(i) return i.B end
- function luaP:SETARG_B(i, b) i.B = b end
- function luaP:GETARG_C(i) return i.C end
- function luaP:SETARG_C(i, b) i.C = b end
- function luaP:GETARG_Bx(i) return i.Bx end
- function luaP:SETARG_Bx(i, b) i.Bx = b end
- function luaP:GETARG_sBx(i) return i.Bx - self.MAXARG_sBx end
- function luaP:SETARG_sBx(i, b) i.Bx = b + self.MAXARG_sBx end
- function luaP:CREATE_ABC(o,a,b,c)
- return {OP = self.OpCode[o], A = a, B = b, C = c}
- end
- function luaP:CREATE_ABx(o,a,bc)
- return {OP = self.OpCode[o], A = a, Bx = bc}
- end
- ------------------------------------------------------------------------
- -- create an instruction from a number (for OP_SETLIST)
- ------------------------------------------------------------------------
- function luaP:CREATE_Inst(c)
- local o = c % 64
- c = (c - o) / 64
- local a = c % 256
- c = (c - a) / 256
- return self:CREATE_ABx(o, a, c)
- end
- ------------------------------------------------------------------------
- -- returns a 4-char string little-endian encoded form of an instruction
- ------------------------------------------------------------------------
- function luaP:Instruction(i)
- if i.Bx then
- -- change to OP/A/B/C format
- i.C = i.Bx % 512
- i.B = (i.Bx - i.C) / 512
- end
- local I = i.A * 64 + i.OP
- local c0 = I % 256
- I = i.C * 64 + (I - c0) / 256 -- 6 bits of A left
- local c1 = I % 256
- I = i.B * 128 + (I - c1) / 256 -- 7 bits of C left
- local c2 = I % 256
- local c3 = (I - c2) / 256
- return string.char(c0, c1, c2, c3)
- end
- ------------------------------------------------------------------------
- -- decodes a 4-char little-endian string into an instruction struct
- ------------------------------------------------------------------------
- function luaP:DecodeInst(x)
- local byte = string.byte
- local i = {}
- local I = byte(x, 1)
- local op = I % 64
- i.OP = op
- I = byte(x, 2) * 4 + (I - op) / 64 -- 2 bits of c0 left
- local a = I % 256
- i.A = a
- I = byte(x, 3) * 4 + (I - a) / 256 -- 2 bits of c1 left
- local c = I % 512
- i.C = c
- i.B = byte(x, 4) * 2 + (I - c) / 512 -- 1 bits of c2 left
- local opmode = self.OpMode[tonumber(string.sub(self.opmodes[op + 1], 7, 7))]
- if opmode ~= "iABC" then
- i.Bx = i.B * 512 + i.C
- end
- return i
- end
- ------------------------------------------------------------------------
- -- Macros to operate RK indices
- -- * these use arithmetic instead of bit ops
- ------------------------------------------------------------------------
- -- this bit 1 means constant (0 means register)
- luaP.BITRK = math.ldexp(1, luaP.SIZE_B - 1)
- -- test whether value is a constant
- function luaP:ISK(x) return x >= self.BITRK end
- -- gets the index of the constant
- function luaP:INDEXK(x) return x - self.BITRK end
- luaP.MAXINDEXRK = luaP.BITRK - 1
- -- code a constant index as a RK value
- function luaP:RKASK(x) return x + self.BITRK end
- ------------------------------------------------------------------------
- -- invalid register that fits in 8 bits
- ------------------------------------------------------------------------
- luaP.NO_REG = luaP.MAXARG_A
- ------------------------------------------------------------------------
- -- R(x) - register
- -- Kst(x) - constant (in constant table)
- -- RK(x) == if ISK(x) then Kst(INDEXK(x)) else R(x)
- ------------------------------------------------------------------------
- ------------------------------------------------------------------------
- -- grep "ORDER OP" if you change these enums
- ------------------------------------------------------------------------
- --[[--------------------------------------------------------------------
- Lua virtual machine opcodes (enum OpCode):
- ------------------------------------------------------------------------
- name args description
- ------------------------------------------------------------------------
- OP_MOVE A B R(A) := R(B)
- OP_LOADK A Bx R(A) := Kst(Bx)
- OP_LOADBOOL A B C R(A) := (Bool)B; if (C) pc++
- OP_LOADNIL A B R(A) := ... := R(B) := nil
- OP_GETUPVAL A B R(A) := UpValue[B]
- OP_GETGLOBAL A Bx R(A) := Gbl[Kst(Bx)]
- OP_GETTABLE A B C R(A) := R(B)[RK(C)]
- OP_SETGLOBAL A Bx Gbl[Kst(Bx)] := R(A)
- OP_SETUPVAL A B UpValue[B] := R(A)
- OP_SETTABLE A B C R(A)[RK(B)] := RK(C)
- OP_NEWTABLE A B C R(A) := {} (size = B,C)
- OP_SELF A B C R(A+1) := R(B); R(A) := R(B)[RK(C)]
- OP_ADD A B C R(A) := RK(B) + RK(C)
- OP_SUB A B C R(A) := RK(B) - RK(C)
- OP_MUL A B C R(A) := RK(B) * RK(C)
- OP_DIV A B C R(A) := RK(B) / RK(C)
- OP_MOD A B C R(A) := RK(B) % RK(C)
- OP_POW A B C R(A) := RK(B) ^ RK(C)
- OP_UNM A B R(A) := -R(B)
- OP_NOT A B R(A) := not R(B)
- OP_LEN A B R(A) := length of R(B)
- OP_CONCAT A B C R(A) := R(B).. ... ..R(C)
- OP_JMP sBx pc+=sBx
- OP_EQ A B C if ((RK(B) == RK(C)) ~= A) then pc++
- OP_LT A B C if ((RK(B) < RK(C)) ~= A) then pc++
- OP_LE A B C if ((RK(B) <= RK(C)) ~= A) then pc++
- OP_TEST A C if not (R(A) <=> C) then pc++
- OP_TESTSET A B C if (R(B) <=> C) then R(A) := R(B) else pc++
- OP_CALL A B C R(A), ... ,R(A+C-2) := R(A)(R(A+1), ... ,R(A+B-1))
- OP_TAILCALL A B C return R(A)(R(A+1), ... ,R(A+B-1))
- OP_RETURN A B return R(A), ... ,R(A+B-2) (see note)
- OP_FORLOOP A sBx R(A)+=R(A+2);
- if R(A) <?= R(A+1) then { pc+=sBx; R(A+3)=R(A) }
- OP_FORPREP A sBx R(A)-=R(A+2); pc+=sBx
- OP_TFORLOOP A C R(A+3), ... ,R(A+2+C) := R(A)(R(A+1), R(A+2));
- if R(A+3) ~= nil then R(A+2)=R(A+3) else pc++
- OP_SETLIST A B C R(A)[(C-1)*FPF+i] := R(A+i), 1 <= i <= B
- OP_CLOSE A close all variables in the stack up to (>=) R(A)
- OP_CLOSURE A Bx R(A) := closure(KPROTO[Bx], R(A), ... ,R(A+n))
- OP_VARARG A B R(A), R(A+1), ..., R(A+B-1) = vararg
- ----------------------------------------------------------------------]]
- luaP.opnames = {} -- opcode names
- luaP.OpCode = {} -- lookup name -> number
- luaP.ROpCode = {} -- lookup number -> name
- ------------------------------------------------------------------------
- -- ORDER OP
- ------------------------------------------------------------------------
- local i = 0
- for v in string.gmatch([[
- MOVE LOADK LOADBOOL LOADNIL GETUPVAL
- GETGLOBAL GETTABLE SETGLOBAL SETUPVAL SETTABLE
- NEWTABLE SELF ADD SUB MUL
- DIV MOD POW UNM NOT
- LEN CONCAT JMP EQ LT
- LE TEST TESTSET CALL TAILCALL
- RETURN FORLOOP FORPREP TFORLOOP SETLIST
- CLOSE CLOSURE VARARG
- ]], "%S+") do
- local n = "OP_"..v
- luaP.opnames[i] = v
- luaP.OpCode[n] = i
- luaP.ROpCode[i] = n
- i = i + 1
- end
- luaP.NUM_OPCODES = i
- --[[
- ===========================================================================
- Notes:
- (*) In OP_CALL, if (B == 0) then B = top. C is the number of returns - 1,
- and can be 0: OP_CALL then sets 'top' to last_result+1, so
- next open instruction (OP_CALL, OP_RETURN, OP_SETLIST) may use 'top'.
- (*) In OP_VARARG, if (B == 0) then use actual number of varargs and
- set top (like in OP_CALL with C == 0).
- (*) In OP_RETURN, if (B == 0) then return up to 'top'
- (*) In OP_SETLIST, if (B == 0) then B = 'top';
- if (C == 0) then next 'instruction' is real C
- (*) For comparisons, A specifies what condition the test should accept
- (true or false).
- (*) All 'skips' (pc++) assume that next instruction is a jump
- ===========================================================================
- --]]
- --[[--------------------------------------------------------------------
- masks for instruction properties. The format is:
- bits 0-1: op mode
- bits 2-3: C arg mode
- bits 4-5: B arg mode
- bit 6: instruction set register A
- bit 7: operator is a test
- for OpArgMask:
- OpArgN - argument is not used
- OpArgU - argument is used
- OpArgR - argument is a register or a jump offset
- OpArgK - argument is a constant or register/constant
- ----------------------------------------------------------------------]]
- -- was enum OpArgMask
- luaP.OpArgMask = { OpArgN = 0, OpArgU = 1, OpArgR = 2, OpArgK = 3 }
- ------------------------------------------------------------------------
- -- e.g. to compare with symbols, luaP:getOpMode(...) == luaP.OpCode.iABC
- -- * accepts opcode parameter as strings, e.g. "OP_MOVE"
- ------------------------------------------------------------------------
- function luaP:getOpMode(m)
- return self.opmodes[self.OpCode[m]] % 4
- end
- function luaP:getBMode(m)
- return math.floor(self.opmodes[self.OpCode[m]] / 16) % 4
- end
- function luaP:getCMode(m)
- return math.floor(self.opmodes[self.OpCode[m]] / 4) % 4
- end
- function luaP:testAMode(m)
- return math.floor(self.opmodes[self.OpCode[m]] / 64) % 2
- end
- function luaP:testTMode(m)
- return math.floor(self.opmodes[self.OpCode[m]] / 128)
- end
- -- luaP_opnames[] is set above, as the luaP.opnames table
- -- number of list items to accumulate before a SETLIST instruction
- luaP.LFIELDS_PER_FLUSH = 50
- ------------------------------------------------------------------------
- -- build instruction properties array
- -- * deliberately coded to look like the C equivalent
- ------------------------------------------------------------------------
- local function opmode(t, a, b, c, m)
- local luaP = luaP
- return t * 128 + a * 64 +
- luaP.OpArgMask[b] * 16 + luaP.OpArgMask[c] * 4 + luaP.OpMode[m]
- end
- -- ORDER OP
- luaP.opmodes = {
- -- T A B C mode opcode
- opmode(0, 1, "OpArgK", "OpArgN", "iABx"), -- OP_LOADK
- opmode(0, 1, "OpArgU", "OpArgU", "iABC"), -- OP_LOADBOOL
- opmode(0, 1, "OpArgR", "OpArgN", "iABC"), -- OP_LOADNIL
- opmode(0, 1, "OpArgU", "OpArgN", "iABC"), -- OP_GETUPVAL
- opmode(0, 1, "OpArgK", "OpArgN", "iABx"), -- OP_GETGLOBAL
- opmode(0, 1, "OpArgR", "OpArgK", "iABC"), -- OP_GETTABLE
- opmode(0, 0, "OpArgK", "OpArgN", "iABx"), -- OP_SETGLOBAL
- opmode(0, 0, "OpArgU", "OpArgN", "iABC"), -- OP_SETUPVAL
- opmode(0, 0, "OpArgK", "OpArgK", "iABC"), -- OP_SETTABLE
- opmode(0, 1, "OpArgU", "OpArgU", "iABC"), -- OP_NEWTABLE
- opmode(0, 1, "OpArgR", "OpArgK", "iABC"), -- OP_SELF
- opmode(0, 1, "OpArgK", "OpArgK", "iABC"), -- OP_ADD
- opmode(0, 1, "OpArgK", "OpArgK", "iABC"), -- OP_SUB
- opmode(0, 1, "OpArgK", "OpArgK", "iABC"), -- OP_MUL
- opmode(0, 1, "OpArgK", "OpArgK", "iABC"), -- OP_DIV
- opmode(0, 1, "OpArgK", "OpArgK", "iABC"), -- OP_MOD
- opmode(0, 1, "OpArgK", "OpArgK", "iABC"), -- OP_POW
- opmode(0, 1, "OpArgR", "OpArgN", "iABC"), -- OP_UNM
- opmode(0, 1, "OpArgR", "OpArgN", "iABC"), -- OP_NOT
- opmode(0, 1, "OpArgR", "OpArgN", "iABC"), -- OP_LEN
- opmode(0, 1, "OpArgR", "OpArgR", "iABC"), -- OP_CONCAT
- opmode(0, 0, "OpArgR", "OpArgN", "iAsBx"), -- OP_JMP
- opmode(1, 0, "OpArgK", "OpArgK", "iABC"), -- OP_EQ
- opmode(1, 0, "OpArgK", "OpArgK", "iABC"), -- OP_LT
- opmode(1, 0, "OpArgK", "OpArgK", "iABC"), -- OP_LE
- opmode(1, 1, "OpArgR", "OpArgU", "iABC"), -- OP_TEST
- opmode(1, 1, "OpArgR", "OpArgU", "iABC"), -- OP_TESTSET
- opmode(0, 1, "OpArgU", "OpArgU", "iABC"), -- OP_CALL
- opmode(0, 1, "OpArgU", "OpArgU", "iABC"), -- OP_TAILCALL
- opmode(0, 0, "OpArgU", "OpArgN", "iABC"), -- OP_RETURN
- opmode(0, 1, "OpArgR", "OpArgN", "iAsBx"), -- OP_FORLOOP
- opmode(0, 1, "OpArgR", "OpArgN", "iAsBx"), -- OP_FORPREP
- opmode(1, 0, "OpArgN", "OpArgU", "iABC"), -- OP_TFORLOOP
- opmode(0, 0, "OpArgU", "OpArgU", "iABC"), -- OP_SETLIST
- opmode(0, 0, "OpArgN", "OpArgN", "iABC"), -- OP_CLOSE
- opmode(0, 1, "OpArgU", "OpArgN", "iABx"), -- OP_CLOSURE
- opmode(0, 1, "OpArgU", "OpArgN", "iABC"), -- OP_VARARG
- }
- -- an awkward way to set a zero-indexed table...
- luaP.opmodes[0] =
- opmode(0, 1, "OpArgR", "OpArgN", "iABC") -- OP_MOVE
- return luaP
- end
- fake_module_scripts[script] = module_script
- end
- do -- nil.Rerubi
- local script = Instance.new('ModuleScript', nil)
- script.Name = "Rerubi"
- local function module_script()
- local Concat = table.concat;
- local Select = select;
- local _Byte = string.byte;
- local Sub = string.sub;
- local Opcode = { -- Opcode types.
- 'ABC', 'ABx', 'ABC', 'ABC';
- 'ABC', 'ABx', 'ABC', 'ABx';
- 'ABC', 'ABC', 'ABC', 'ABC';
- 'ABC', 'ABC', 'ABC', 'ABC';
- 'ABC', 'ABC', 'ABC', 'ABC';
- 'ABC', 'ABC', 'AsBx', 'ABC';
- 'ABC', 'ABC', 'ABC', 'ABC';
- 'ABC', 'ABC', 'ABC', 'AsBx';
- 'AsBx', 'ABC', 'ABC', 'ABC';
- 'ABx', 'ABC';
- };
- -- rlbi author -> Rerumu
- --[[
- Features;
- * Almost complete rework/rewrite
- * Fast and performant
- * Fixes to upvalues
- * C Stack overflow fixes in opcodes
- * Fixed missing/broken returns
- * Numeric constant 0 is properly handled
- * Formatted in a more readable manner
- * Tailcalls and stack issues have been fixed
- * CLOSE implemented
- * SETLIST (extended) implemented
- * VARARG fixes
- --]]
- local function gBit(Bit, Start, End) -- No tail-calls, yay.
- if End then -- Thanks to cntkillme for giving input on this shorter, better approach.
- local Res = (Bit / 2 ^ (Start - 1)) % 2 ^ ((End - 1) - (Start - 1) + 1);
- return Res - Res % 1;
- else
- local Plc = 2 ^ (Start - 1);
- if (Bit % (Plc + Plc) >= Plc) then
- return 1;
- else
- return 0;
- end;
- end;
- end;
- local function GetMeaning(ByteString)
- local Pos = 1;
- local gSizet;
- local gInt;
- local function gBits8() -- Get the next byte in the stream.
- local F = _Byte(ByteString, Pos, Pos);
- Pos = Pos + 1;
- return F;
- end;
- local function gBits32()
- local W, X, Y, Z = _Byte(ByteString, Pos, Pos + 3);
- Pos = Pos + 4;
- return (Z * 16777216) + (Y * 65536) + (X * 256) + W;
- end;
- local function gBits64()
- return gBits32() * 4294967296 + gBits32();
- end;
- local function gFloat()
- local A, B = gBits32(), gBits32();
- if ((A + B) == 0) then
- return 0; -- Float 0 tends to be very messy, so this is a temp fix until I figure out what's up.
- else
- return (-2 * gBit(B, 32) + 1) * (2 ^ (gBit(B, 21, 31) - 1023)) * ((gBit(B, 1, 20) * (2^32) + A) / (2 ^ 52) + 1);
- end;
- end;
- local function gString(Len)
- local Str;
- if Len then
- Str = Sub(ByteString, Pos, Pos + Len - 1);
- Pos = Pos + Len;
- else
- Len = gSizet();
- if (Len == 0) then return; end;
- Str = Sub(ByteString, Pos, Pos + Len - 1);
- Pos = Pos + Len;
- end;
- return Str;
- end;
- local function ChunkDecode()
- local Instr = {};
- local Const = {};
- local Proto = {};
- local Chunk = {
- Instr = Instr; -- Instructions
- Const = Const; -- Constants
- Proto = Proto; -- Prototypes
- Lines = {}; -- Lines
- Name = gString(); -- Grab name string.
- FirstL = gInt(); -- First line.
- LastL = gInt(); -- Last line.
- Upvals = gBits8(); -- Upvalue count.
- Args = gBits8(); -- Arg count.
- Vargs = gBits8(); -- Vararg type.
- Stack = gBits8(); -- Stack.
- };
- if Chunk.Name then
- Chunk.Name = Sub(Chunk.Name, 1, -2);
- end;
- for Idx = 1, gInt() do -- Loading instructions to the chunk.
- local Data = gBits32();
- local Opco = gBit(Data, 1, 6);
- local Type = Opcode[Opco + 1];
- local Inst;
- if Type then
- Inst = {
- Enum = Opco;
- gBit(Data, 7, 14); -- Register A.
- };
- if (Type == 'ABC') then -- Most common, basic instruction type.
- Inst[2] = gBit(Data, 24, 32);
- Inst[3] = gBit(Data, 15, 23);
- elseif (Type == 'ABx') then
- Inst[2] = gBit(Data, 15, 32);
- elseif (Type == 'AsBx') then
- Inst[2] = gBit(Data, 15, 32) - 131071;
- end;
- else
- Inst = Data; -- Extended SETLIST
- end;
- Instr[Idx] = Inst;
- end;
- for Idx = 1, gInt() do -- Load constants.
- local Type = gBits8();
- local Cons;
- if (Type == 1) then -- Boolean
- Cons = (gBits8() ~= 0);
- elseif (Type == 3) then -- Float/Double
- Cons = gFloat();
- elseif (Type == 4) then
- Cons = Sub(gString(), 1, -2);
- end;
- Const[Idx - 1] = Cons;
- end;
- for Idx = 1, gInt() do -- Nested function prototypes.
- Proto[Idx - 1] = ChunkDecode();
- end;
- do -- Debugging
- local Lines = Chunk.Lines;
- for Idx = 1, gInt() do
- Lines[Idx] = gBits32();
- end;
- for Idx = 1, gInt() do -- Locals in stack.
- gString(); -- Name of local.
- gBits32(); -- Starting point.
- gBits32(); -- End point.
- end;
- for Idx = 1, gInt() do -- Upvalues.
- gString(); -- Name of upvalue.
- end;
- end;
- return Chunk; -- Finished chunk.
- end;
- do -- Most of this chunk I was too lazy to reformat or change
- assert(gString(4) == "\27Lua", "Lua bytecode expected.");
- assert(gBits8() == 0x51, "Only Lua 5.1 is supported.");
- gBits8(); -- Probably version control.
- gBits8(); -- Is small endians.
- local IntSize = gBits8(); -- Int size
- local Sizet = gBits8(); -- size_t
- if (IntSize == 4) then
- gInt = gBits32;
- elseif (IntSize == 8) then
- gInt = gBits64;
- else
- error('Integer size not supported', 2);
- end;
- if (Sizet == 4) then
- gSizet = gBits32;
- elseif (Sizet == 8) then
- gSizet = gBits64;
- else
- error('Sizet size not supported', 2);
- end;
- assert(gString(3) == "\4\8\0", "Unsupported bytecode target platform");
- end;
- return ChunkDecode();
- end;
- local function _Returns(...)
- return Select('#', ...), {...};
- end;
- local function Wrap(Chunk, Env, Upvalues)
- local Instr = Chunk.Instr;
- local Const = Chunk.Const;
- local Proto = Chunk.Proto;
- local function OnError(Err, Position) -- Handle your errors in whatever way.
- local Name = Chunk.Name or 'Code';
- local Line = Chunk.Lines[Position] or '?';
- local Err = Err:match'^.+:%s*(.+)' or Err;
- error(string.format('%s (%s): %s', Name, Line, Err), 0);
- end;
- return function(...) -- Returned function to run bytecode chunk (Don't be stupid, you can't setfenv this to work your way).
- local Upvalues = Upvalues;
- local Instr = Instr;
- local Const = Const;
- local Proto = Proto;
- local InstrPoint, Top = 1, -1;
- local Vararg, Varargsz = {}, Select('#', ...) - 1;
- local GStack = {};
- local Lupvals = {};
- local Stack = setmetatable({}, {
- __index = GStack;
- __newindex = function(_, Key, Value)
- if (Key > Top) and Value then
- Top = Key;
- end;
- GStack[Key] = Value;
- end;
- });
- local function Loop()
- local Instr = Instr;
- local Inst, Enum, A, B;
- while true do
- Inst = Instr[InstrPoint];
- Enum = Inst.Enum;
- InstrPoint = InstrPoint + 1;
- if (Enum == 0) then -- MOVE
- Stack[Inst[1]] = Stack[Inst[2]];
- elseif (Enum == 1) then -- LOADK
- Stack[Inst[1]] = Const[Inst[2]];
- elseif (Enum == 2) then -- LOADBOOL
- Stack[Inst[1]] = (Inst[2] ~= 0);
- if (Inst[3] ~= 0) then
- InstrPoint = InstrPoint + 1;
- end;
- elseif (Enum == 3) then -- LOADNIL
- local Stk = Stack;
- for Idx = Inst[1], Inst[2] do
- Stk[Idx] = nil;
- end;
- elseif (Enum == 4) then -- GETUPVAL
- Stack[Inst[1]] = Upvalues[Inst[2]];
- elseif (Enum == 5) then -- GETGLOBAL
- Stack[Inst[1]] = Env[Const[Inst[2]]];
- elseif (Enum == 6) then -- GETTABLE
- local C = Inst[3];
- local Stk = Stack;
- if (C > 255) then
- C = Const[C - 256];
- else
- C = Stk[C];
- end;
- Stk[Inst[1]] = Stk[Inst[2]][C];
- elseif (Enum == 7) then -- SETGLOBAL
- Env[Const[Inst[2]]] = Stack[Inst[1]];
- elseif (Enum == 8) then -- SETUPVAL
- Upvalues[Inst[2]] = Stack[Inst[1]];
- elseif (Enum == 9) then -- SETTABLE
- local B, C = Inst[2], Inst[3];
- local Stk = Stack;
- if (B > 255) then
- B = Const[B - 256];
- else
- B = Stk[B];
- end;
- if (C > 255) then
- C = Const[C - 256];
- else
- C = Stk[C];
- end;
- Stk[Inst[1]][B] = C;
- elseif (Enum == 10) then -- NEWTABLE
- Stack[Inst[1]] = {};
- elseif (Enum == 11) then -- SELF
- local A = Inst[1];
- local B = Inst[2];
- local C = Inst[3];
- local Stk = Stack;
- B = Stk[B];
- if (C > 255) then
- C = Const[C - 256];
- else
- C = Stk[C];
- end;
- Stk[A + 1] = B;
- Stk[A] = B[C];
- elseif (Enum == 12) then -- ADD
- local B = Inst[2];
- local C = Inst[3];
- local Stk, Con = Stack, Const;
- if (B > 255) then
- B = Const[B - 256];
- else
- B = Stk[B];
- end;
- if (C > 255) then
- C = Const[C - 256];
- else
- C = Stk[C];
- end;
- Stk[Inst[1]] = B + C;
- elseif (Enum == 13) then -- SUB
- local B = Inst[2];
- local C = Inst[3];
- local Stk, Con = Stack, Const;
- if (B > 255) then
- B = Const[B - 256];
- else
- B = Stk[B];
- end;
- if (C > 255) then
- C = Const[C - 256];
- else
- C = Stk[C];
- end;
- Stk[Inst[1]] = B - C;
- elseif (Enum == 14) then -- MUL
- local B = Inst[2];
- local C = Inst[3];
- local Stk, Con = Stack, Const;
- if (B > 255) then
- B = Const[B - 256];
- else
- B = Stk[B];
- end;
- if (C > 255) then
- C = Const[C - 256];
- else
- C = Stk[C];
- end;
- Stk[Inst[1]] = B * C;
- elseif (Enum == 15) then -- DIV
- local B = Inst[2];
- local C = Inst[3];
- local Stk, Con = Stack, Const;
- if (B > 255) then
- B = Const[B - 256];
- else
- B = Stk[B];
- end;
- if (C > 255) then
- C = Const[C - 256];
- else
- C = Stk[C];
- end;
- Stk[Inst[1]] = B / C;
- elseif (Enum == 16) then -- MOD
- local B = Inst[2];
- local C = Inst[3];
- local Stk, Con = Stack, Const;
- if (B > 255) then
- B = Const[B - 256];
- else
- B = Stk[B];
- end;
- if (C > 255) then
- C = Const[C - 256];
- else
- C = Stk[C];
- end;
- Stk[Inst[1]] = B % C;
- elseif (Enum == 17) then -- POW
- local B = Inst[2];
- local C = Inst[3];
- local Stk, Con = Stack, Const;
- if (B > 255) then
- B = Const[B - 256];
- else
- B = Stk[B];
- end;
- if (C > 255) then
- C = Const[C - 256];
- else
- C = Stk[C];
- end;
- Stk[Inst[1]] = B ^ C;
- elseif (Enum == 18) then -- UNM
- Stack[Inst[1]] = -Stack[Inst[2]];
- elseif (Enum == 19) then -- NOT
- Stack[Inst[1]] = (not Stack[Inst[2]]);
- elseif (Enum == 20) then -- LEN
- Stack[Inst[1]] = #Stack[Inst[2]];
- elseif (Enum == 21) then -- CONCAT
- local Stk = Stack;
- local B = Inst[2];
- local K = {Stack[B]};
- for Idx = B + 1, Inst[3] do
- K[#K + 1] = Stk[Idx];
- end;
- Stack[Inst[1]] = Concat(K);
- elseif (Enum == 22) then -- JUMP
- InstrPoint = InstrPoint + Inst[2];
- elseif (Enum == 23) then -- EQ
- local A = Inst[1] ~= 0;
- local B = Inst[2];
- local C = Inst[3];
- local Stk, Con = Stack, Const;
- if (B > 255) then
- B = Const[B - 256];
- else
- B = Stk[B];
- end;
- if (C > 255) then
- C = Const[C - 256];
- else
- C = Stk[C];
- end;
- if (B == C) ~= A then
- InstrPoint = InstrPoint + 1;
- end;
- elseif (Enum == 24) then -- LT
- local A = Inst[1] ~= 0;
- local B = Inst[2];
- local C = Inst[3];
- local Stk, Con = Stack, Const;
- if (B > 255) then
- B = Const[B - 256];
- else
- B = Stk[B];
- end;
- if (C > 255) then
- C = Const[C - 256];
- else
- C = Stk[C];
- end;
- if (B < C) ~= A then
- InstrPoint = InstrPoint + 1;
- end;
- elseif (Enum == 25) then -- LE
- local A = Inst[1] ~= 0;
- local B = Inst[2];
- local C = Inst[3];
- local Stk, Con = Stack, Const;
- if (B > 255) then
- B = Const[B - 256];
- else
- B = Stk[B];
- end;
- if (C > 255) then
- C = Const[C - 256];
- else
- C = Stk[C];
- end;
- if (B <= C) ~= A then
- InstrPoint = InstrPoint + 1;
- end;
- elseif (Enum == 26) then -- TEST
- if (not not Stack[Inst[1]]) == (Inst[3] == 0) then
- InstrPoint = InstrPoint + 1;
- end;
- elseif (Enum == 27) then -- TESTSET
- local B = Stack[Inst[2]];
- if (not not B) == (Inst[3] == 0) then
- InstrPoint = InstrPoint + 1;
- else
- Stack[Inst[1]] = B;
- end;
- elseif (Enum == 28) then -- CALL
- local A = Inst[1];
- local B = Inst[2];
- local C = Inst[3];
- local Stk = Stack;
- local Args, Results;
- local Limit, Loop;
- Args = {};
- if (B ~= 1) then
- if (B ~= 0) then
- Limit = A + B - 1;
- else
- Limit = Top;
- end;
- Loop = 0;
- for Idx = A + 1, Limit do
- Loop = Loop + 1;
- Args[Loop] = Stk[Idx];
- end;
- Limit, Results = _Returns(Stk[A](unpack(Args, 1, Limit - A)));
- else
- Limit, Results = _Returns(Stk[A]());
- end;
- Top = A - 1;
- if (C ~= 1) then
- if (C ~= 0) then
- Limit = A + C - 2;
- else
- Limit = Limit + A;
- end;
- Loop = 0;
- for Idx = A, Limit do
- Loop = Loop + 1;
- Stk[Idx] = Results[Loop];
- end;
- end;
- elseif (Enum == 29) then -- TAILCALL
- local A = Inst[1];
- local B = Inst[2];
- local C = Inst[3];
- local Stk = Stack;
- local Args, Results;
- local Limit, Loop;
- Args = {};
- if (B ~= 1) then
- if (B ~= 0) then
- Limit = A + B - 1;
- else
- Limit = Top;
- end
- Loop = 0;
- for Idx = A + 1, Limit do
- Loop = Loop + 1;
- Args[#Args + 1] = Stk[Idx];
- end
- Results = {Stk[A](unpack(Args, 1, Limit - A))};
- else
- Results = {Stk[A]()};
- end;
- return Results;
- elseif (Enum == 30) then -- RETURN
- local A = Inst[1];
- local B = Inst[2];
- local Stk = Stack;
- local Loop, Output;
- local Limit;
- if (B == 1) then
- return;
- elseif (B == 0) then
- Limit = Top;
- else
- Limit = A + B - 2;
- end;
- Output = {};
- local Loop = 0;
- for Idx = A, Limit do
- Loop = Loop + 1;
- Output[Loop] = Stk[Idx];
- end;
- return Output;
- elseif (Enum == 31) then -- FORLOOP
- local A = Inst[1];
- local Stk = Stack;
- local Step = Stk[A + 2];
- local Index = Stk[A] + Step;
- Stk[A] = Index;
- if (Step > 0) then
- if Index <= Stk[A + 1] then
- InstrPoint = InstrPoint + Inst[2];
- Stk[A + 3] = Index;
- end;
- else
- if Index >= Stk[A + 1] then
- InstrPoint = InstrPoint + Inst[2];
- Stk[A + 3] = Index;
- end
- end
- elseif (Enum == 32) then -- FORPREP
- local A = Inst[1];
- local Stk = Stack;
- Stk[A] = Stk[A] - Stk[A + 2];
- InstrPoint = InstrPoint + Inst[2];
- elseif (Enum == 33) then -- TFORLOOP
- local A = Inst[1];
- local B = Inst[2];
- local C = Inst[3];
- local Stk = Stack;
- local Offset = A + 2;
- local Result = {Stk[A](Stk[A + 1], Stk[A + 2])};
- for Idx = 1, C do
- Stack[Offset + Idx] = Result[Idx];
- end;
- if (Stk[A + 3] ~= nil) then
- Stk[A + 2] = Stk[A + 3];
- else
- InstrPoint = InstrPoint + 1;
- end;
- elseif (Enum == 34) then -- SETLIST
- local A = Inst[1];
- local B = Inst[2];
- local C = Inst[3];
- local Stk = Stack;
- if (C == 0) then
- InstrPoint = InstrPoint + 1;
- C = Instr[InstrPoint]; -- This implementation was ambiguous! Will eventually re-test.
- end;
- local Offset = (C - 1) * 50;
- local T = Stk[A]; -- Assuming T is the newly created table.
- if (B == 0) then
- B = Top;
- end;
- for Idx = 1, B do
- T[Offset + Idx] = Stk[A + Idx];
- end;
- elseif (Enum == 35) then -- CLOSE
- local A = Inst[1];
- local Cls = {}; -- Slight doubts on any issues this may cause
- for Idx = 1, #Lupvals do
- local List = Lupvals[Idx];
- for Idz = 0, #List do
- local Upv = List[Idz];
- local Stk = Upv[1];
- local Pos = Upv[2];
- if (Stk == Stack) and (Pos >= A) then
- Cls[Pos] = Stk[Pos];
- Upv[1] = Cls; -- @memcorrupt credit me for the spoonfeed
- end;
- end;
- end;
- elseif (Enum == 36) then -- CLOSURE
- local Proto = Proto[Inst[2]];
- local Instr = Instr;
- local Stk = Stack;
- local Indexes;
- local NewUvals;
- if (Proto.Upvals ~= 0) then
- Indexes = {};
- NewUvals = setmetatable({}, {
- __index = function(_, Key)
- local Val = Indexes[Key];
- return Val[1][Val[2]];
- end,
- __newindex = function(_, Key, Value)
- local Val = Indexes[Key];
- Val[1][Val[2]] = Value;
- end;
- }
- );
- for Idx = 1, Proto.Upvals do
- local Mvm = Instr[InstrPoint];
- if (Mvm.Enum == 0) then -- MOVE
- Indexes[Idx - 1] = {Stk, Mvm[2]};
- elseif (Mvm.Enum == 4) then -- GETUPVAL
- Indexes[Idx - 1] = {Upvalues, Mvm[2]};
- end;
- InstrPoint = InstrPoint + 1;
- end;
- Lupvals[#Lupvals + 1] = Indexes;
- end;
- Stk[Inst[1]] = Wrap(Proto, Env, NewUvals);
- elseif (Enum == 37) then -- VARARG
- local A = Inst[1];
- local B = Inst[2];
- local Stk, Vararg = Stack, Vararg;
- for Idx = A, A + (B > 0 and B - 1 or Varargsz) do
- Stk[Idx] = Vararg[Idx - A];
- end;
- end;
- end;
- end;
- local Args = {...};
- for Idx = 0, Varargsz do
- Stack[Idx] = Args[Idx + 1];
- if (Idx >= Chunk.Args) then
- Vararg[Idx - Chunk.Args] = Args[Idx + 1];
- end;
- end;
- local A, B = pcall(Loop); -- Pcalling to allow yielding
- if A then -- We're always expecting this to come out true (because errorless code)
- if B then -- So I flipped the conditions.
- return unpack(B);
- end;
- return;
- else
- OnError(B, InstrPoint - 1); -- Didn't get time to test the `-1` honestly, but I assume it works properly
- end;
- end;
- end;
- return function(BCode, Env) -- lua_function LoadBytecode (string BCode, table Env)
- local Buffer = GetMeaning(BCode);
- return Wrap(Buffer, Env or getfenv(0)), Buffer;
- end;
- end
- fake_module_scripts[script] = module_script
- end
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement