Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- <roblox xmlns:xmime="http://www.w3.org/2005/05/xmlmime" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="http://www.roblox.com/roblox.xsd" version="4">
- <Meta name="ExplicitAutoJoints">true</Meta>
- <External>null</External>
- <External>nil</External>
- <Item class="ModuleScript" referent="RBX368AFF5C67F045749198DD239F0720E5">
- <Properties>
- <Content name="LinkedSource"><null></null></Content>
- <string name="Name">CmdrClient</string>
- <string name="ScriptGuid">{2396E93D-6873-4D5D-B171-B5778E9572D0}</string>
- <ProtectedString name="Source"><![CDATA[-- A fully extensible and type-safe admin-commands console
- -- @documentation https://github.com/evaera/Cmdr/blob/master/README.md
- -- @source https://github.com/evaera/Cmdr
- -- @rostrap Cmdr
- -- @author evaera
- local RunService = game:GetService("RunService")
- local StarterGui = game:GetService("StarterGui")
- local Players = game:GetService("Players")
- local Player = Players.LocalPlayer
- local Shared = script:WaitForChild("Shared")
- local Util = require(Shared:WaitForChild("Util"))
- local Loadstring = require(script:WaitForChild('Loadstring'))
- local Cmdr do
- Cmdr = setmetatable({
- ReplicatedRoot = script;
- RemoteFunction = Instance.new('BindableFunction');
- RemoteEvent = Instance.new('BindableEvent');
- ActivationKeys = {[Enum.KeyCode.Semicolon] = true};
- Enabled = true;
- MashToEnable = false;
- ActivationUnlocksMouse = true;
- PlaceName = "Cmdr";
- Util = Util;
- Events = {};
- Loadstring = Loadstring
- }, {
- -- This sucks, and may be redone or removed
- -- Proxies dispatch methods on to main Cmdr object
- __index = function (self, k)
- local r = self.Dispatcher[k]
- if r and type(r) == "function" then
- return function (_, ...)
- return r(self.Dispatcher, ...)
- end
- end
- end
- })
- Cmdr.Registry = require(Shared.Registry)(Cmdr)
- Cmdr.Dispatcher = require(Shared.Dispatcher)(Cmdr)
- end
- require(script.CreateGui)()
- local Interface = require(script.CmdrInterface)(Cmdr)
- --- Sets a list of keyboard keys (Enum.KeyCode) that can be used to open the commands menu
- function Cmdr:SetActivationKeys (keysArray)
- self.ActivationKeys = Util.MakeDictionary(keysArray)
- end
- --- Sets the place name label on the interface
- function Cmdr:SetPlaceName (name)
- self.PlaceName = name
- Interface.Window:UpdateLabel()
- end
- --- Sets whether or not the console is enabled
- function Cmdr:SetEnabled (enabled)
- self.Enabled = enabled
- end
- --- Sets if activation will free the mouse.
- function Cmdr:SetActivationUnlocksMouse (enabled)
- self.ActivationUnlocksMouse = enabled
- end
- --- Shows Cmdr window
- function Cmdr:Show ()
- if not self.Enabled then
- return
- end
- Interface.Window:Show()
- end
- --- Hides Cmdr window
- function Cmdr:Hide ()
- Interface.Window:Hide()
- end
- --- Toggles Cmdr window
- function Cmdr:Toggle ()
- if not self.Enabled then
- return self:Hide()
- end
- Interface.Window:SetVisible(not Interface.Window:IsVisible())
- end
- --- Enables the "Mash to open" feature
- function Cmdr:SetMashToEnable(isEnabled)
- self.MashToEnable = isEnabled
- if isEnabled then
- self:SetEnabled(false)
- end
- end
- --- Sets the handler for a certain event type
- function Cmdr:HandleEvent(name, callback)
- self.Events[name] = callback
- end
- -- Only register when we aren't in studio because don't want to overwrite what the server portion did
- Cmdr.Registry:RegisterTypesIn(script:WaitForChild("Types"))
- local Cmds = script:WaitForChild("Commands")
- for i,v in pairs(Cmds:GetChildren()) do
- if v:IsA('ModuleScript') then
- if require(v)() then
- Cmdr.Registry:RegisterCommandsIn(v)
- end
- else
- Cmdr.Registry:RegisterCommandsIn(v)
- end
- end
- Player.Chatted:Connect(function(msg)
- if msg:sub(1,2) == '--' then
- Cmdr.Dispatcher:EvaluateAndRun(msg:sub(3), Player)
- end
- end)
- -- Hook up event listener
- Cmdr.RemoteEvent.Event:Connect(function(name, ...)
- if Cmdr.Events[name] then
- Cmdr.Events[name](...)
- end
- end)
- require(script.DefaultEventHandlers)(Cmdr)
- return Cmdr]]></ProtectedString>
- <BinaryString name="Tags"></BinaryString>
- </Properties>
- <Item class="ModuleScript" referent="RBX8EDF08B92E914B60BDE7F1DD43F8D97E">
- <Properties>
- <Content name="LinkedSource"><null></null></Content>
- <string name="Name">DefaultEventHandlers</string>
- <string name="ScriptGuid">{235F4510-3809-4E94-99F5-E7D5EF9E85AF}</string>
- <ProtectedString name="Source"><![CDATA[-- A fully extensible and type-safe admin-commands console
- -- @documentation https://github.com/evaera/Cmdr/blob/master/README.md
- -- @source https://github.com/evaera/Cmdr
- -- @rostrap Cmdr
- -- @author evaera
- local StarterGui = game:GetService("StarterGui")
- local Window = require(script.Parent.CmdrInterface.Window)
- return function (Cmdr)
- Cmdr:HandleEvent("Message", function (text)
- StarterGui:SetCore("ChatMakeSystemMessage", {
- Text = ("[Announcement] %s"):format(text);
- Color = Color3.fromRGB(249, 217, 56);
- })
- end)
- Cmdr:HandleEvent("AddLine", function (...)
- Window:AddLine(...)
- end)
- end]]></ProtectedString>
- <BinaryString name="Tags"></BinaryString>
- </Properties>
- </Item>
- <Item class="ModuleScript" referent="RBX326A7781248546519AAD866FC9257B72">
- <Properties>
- <Content name="LinkedSource"><null></null></Content>
- <string name="Name">CmdrInterface</string>
- <string name="ScriptGuid">{7E21D3D2-A7E4-4D31-9C6D-63FCF89BBB81}</string>
- <ProtectedString name="Source"><![CDATA[-- A fully extensible and type-safe admin-commands console
- -- @documentation https://github.com/evaera/Cmdr/blob/master/README.md
- -- @source https://github.com/evaera/Cmdr
- -- @rostrap Cmdr
- -- @author evaera
- -- Here be dragons
- local Players = game:GetService("Players")
- local Player = Players.LocalPlayer
- return function (Cmdr)
- local Util = Cmdr.Util
- local Window = require(script:WaitForChild("Window"))
- Window.Cmdr = Cmdr
- local AutoComplete = require(script:WaitForChild("AutoComplete"))(Cmdr)
- Window.AutoComplete = AutoComplete
- -- Sets the Window.ProcessEntry callback so that we can dispatch our commands out
- function Window.ProcessEntry(text)
- text = Util.TrimString(text)
- if #text == 0 then return end
- Window:AddLine(Window:GetLabel() .. " " .. text, Color3.fromRGB(255, 223, 93))
- Window:AddLine(Cmdr.Dispatcher:EvaluateAndRun(text, Player, {
- IsHuman = true
- }))
- end
- -- Sets the Window.OnTextChanged callback so we can update the auto complete
- function Window.OnTextChanged (text)
- local command = Cmdr.Dispatcher:Evaluate(text, Player, true)
- local arguments = Util.SplitString(text)
- local commandText = table.remove(arguments, 1)
- local atEnd = false
- if command then
- arguments = Util.MashExcessArguments(arguments, #command.Object.Args)
- atEnd = #arguments == #command.Object.Args
- end
- local entryComplete = commandText and #arguments > 0
- if text:sub(#text, #text):match("%s") and not atEnd then
- entryComplete = true
- arguments[#arguments + 1] = ""
- end
- if command and entryComplete then
- local commandValid, errorText = command:Validate()
- Window:SetIsValidInput(commandValid, ("Validation errors: %s"):format(errorText or ""))
- local acItems = {}
- local lastArgument = command:GetArgument(#arguments)
- if lastArgument then
- local typedText = lastArgument.RawSegments[#lastArgument.RawSegments]
- local items = lastArgument:GetAutocomplete()
- for i, item in pairs(items) do
- acItems[i] = {typedText, item}
- end
- local valid = true
- if #typedText > 0 then
- valid, errorText = lastArgument:Validate()
- end
- return AutoComplete:Show(acItems, {
- at = atEnd and #text - #typedText + (text:sub(#text, #text):match("%s") and -1 or 0);
- prefix = #lastArgument.RawSegments == 1 and lastArgument.Prefix or "";
- isLast = #command.Arguments == #command.ArgumentDefinitions and #typedText > 0;
- numArgs = #arguments;
- command = command;
- arg = lastArgument;
- name = lastArgument.Name .. (lastArgument.Required and "" or "?");
- type = lastArgument.Type.DisplayName;
- description = (valid == false and errorText) or lastArgument.Object.Description;
- invalid = not valid;
- })
- end
- elseif commandText and #arguments == 0 then
- Window:SetIsValidInput(true)
- local exactCommand = Cmdr.Registry:GetCommand(commandText)
- local exactMatch
- if exactCommand then
- exactMatch = {exactCommand.Name, exactCommand.Name, options = {
- name = exactCommand.Name;
- description = exactCommand.Description;
- }}
- end
- local acItems = {exactMatch}
- for _, cmd in pairs(Cmdr.Registry:GetCommandsAsStrings()) do
- if commandText:lower() == cmd:lower():sub(1, #commandText) and (exactMatch == nil or exactMatch[1] ~= commandText) then
- local commandObject = Cmdr.Registry:GetCommand(cmd)
- acItems[#acItems + 1] = {commandText, cmd, options = {
- name = commandObject.Name;
- description = commandObject.Description;
- }}
- end
- end
- return AutoComplete:Show(acItems)
- end
- Window:SetIsValidInput(false, "Invalid command. Use the help command to see all available commands.")
- AutoComplete:Hide()
- end
- Window:UpdateLabel()
- Window:UpdateWindowHeight()
- return {
- Window = Window;
- AutoComplete = AutoComplete;
- }
- end
- ]]></ProtectedString>
- <BinaryString name="Tags"></BinaryString>
- </Properties>
- <Item class="ModuleScript" referent="RBX240D210F33274C2EB2EA753E4EDE69C9">
- <Properties>
- <Content name="LinkedSource"><null></null></Content>
- <string name="Name">AutoComplete</string>
- <string name="ScriptGuid">{E9F50325-6C3B-45B6-BDF0-44C5C331798A}</string>
- <ProtectedString name="Source"><![CDATA[-- A fully extensible and type-safe admin-commands console
- -- @documentation https://github.com/evaera/Cmdr/blob/master/README.md
- -- @source https://github.com/evaera/Cmdr
- -- @rostrap Cmdr
- -- @author evaera
- -- luacheck: ignore 212
- local Players = game:GetService("Players")
- local Player = Players.LocalPlayer
- return function (Cmdr)
- local AutoComplete = {
- Items = {};
- ItemOptions = {};
- SelectedItem = 0;
- }
- local Util = Cmdr.Util
- local Shorthands = Util.MakeDictionary({"me", "all", ".", "*", "others"})
- local Gui = Player:WaitForChild("PlayerGui"):WaitForChild("Cmdr"):WaitForChild("Autocomplete")
- local AutoItem = Gui:WaitForChild("TextButton")
- local Title = Gui:WaitForChild("Title")
- local Description = Gui:WaitForChild("Description")
- local Entry = Gui.Parent:WaitForChild("Frame"):WaitForChild("Entry")
- AutoItem.Parent = nil
- -- Helper function that sets text and resizes labels
- local function SetText(obj, textObj, text, sizeFromContents)
- obj.Visible = text ~= nil
- textObj.Text = text or ""
- if sizeFromContents then
- textObj.Size = UDim2.new(0, Util.GetTextSize(text or "", textObj, Vector2.new(1000, 1000), 1, 0).X, obj.Size.Y.Scale, obj.Size.Y.Offset)
- end
- end
- -- Update the info display (Name, type, and description) based on given options.
- local function UpdateInfoDisplay (options)
- -- Update the objects' text and sizes
- SetText(Title, Title.Field, options.name, true)
- SetText(Title.Field.Type, Title.Field.Type, options.type and ": " .. options.type:sub(1, 1):upper() .. options.type:sub(2))
- SetText(Description, Description.Label, options.description)
- Description.Label.TextColor3 = options.invalid and Color3.fromRGB(255, 73, 73) or Color3.fromRGB(255, 255, 255)
- -- Calculate needed width and height
- local infoWidth = Title.Field.TextBounds.X + Title.Field.Type.TextBounds.X
- local guiWidth = math.max(infoWidth, Gui.Size.X.Offset)
- Description.Size = UDim2.new(1, 0, 0, 40)
- -- Flow description text
- while not Description.Label.TextFits do
- Description.Size = Description.Size + UDim2.new(0, 0, 0, 2)
- if Description.Size.Y.Offset > 500 then
- break
- end
- end
- -- Update container
- wait()
- Gui.UIListLayout:ApplyLayout()
- Gui.Size = UDim2.new(0, guiWidth, 0, Gui.UIListLayout.AbsoluteContentSize.Y)
- end
- --- Shows the auto complete menu with the given list and possible options
- -- item = {typedText, suggestedText, options?=options}
- -- The options table is optional. `at` should only be passed into AutoComplete::Show
- -- name, type, and description may be passed in an options dictionary inside the items as well
- -- options.at?: the character index at which to show the menu
- -- options.name?: The name to display in the info box
- -- options.type?: The type to display in the info box
- -- options.prefix?: The current type prefix (%Team)
- -- options.description?: The description for the currently active info box
- -- options.invalid?: If true, description is shown in red.
- -- options.isLast?: If true, auto complete won't keep going after this argument.
- function AutoComplete:Show (items, options)
- options = options or {}
- -- Remove old options.
- for _, item in pairs(self.Items) do
- if item.gui then
- item.gui:Destroy()
- end
- end
- -- Reset state
- self.SelectedItem = 1
- self.Items = items
- self.Prefix = options.prefix or ""
- self.LastItem = options.isLast or false
- self.Command = options.command
- self.Arg = options.arg
- self.NumArgs = options.numArgs
- -- Generate the new option labels
- local autocompleteWidth = 200
- for i, item in pairs(self.Items) do
- local leftText = item[1]
- local rightText = item[2]
- if Shorthands[leftText] then
- leftText = rightText
- end
- local btn = AutoItem:Clone()
- btn.Name = leftText .. rightText
- btn.BackgroundTransparency = i == self.SelectedItem and 0.5 or 1
- btn.Typed.Text = leftText
- btn.Suggest.Text = string.rep(" ", #leftText) .. rightText:sub(#leftText + 1)
- btn.Parent = Gui
- btn.LayoutOrder = i
- local maxBounds = math.max(btn.Typed.TextBounds.X, btn.Suggest.TextBounds.X) + 20
- if maxBounds > autocompleteWidth then
- autocompleteWidth = maxBounds
- end
- item.gui = btn
- end
- Gui.UIListLayout:ApplyLayout()
- -- Todo: Use TextService to find accurate position for auto complete box
- local text = Entry.TextBox.Text
- local words = Util.SplitString(text)
- if text:sub(#text, #text) == " " and not options.at then
- words[#words+1] = "e"
- end
- table.remove(words, #words)
- local extra = (options.at and options.at or (#table.concat(words, " ") + 1)) * 7
- -- Update the auto complete container
- Gui.Position = UDim2.new(0, Entry.TextBox.AbsolutePosition.X - 10 + extra, 0, Entry.TextBox.AbsolutePosition.Y + 30)
- Gui.Size = UDim2.new(0, autocompleteWidth, 0, Gui.UIListLayout.AbsoluteContentSize.Y)
- Gui.Visible = true
- -- Finally, update thge info display
- UpdateInfoDisplay(self.Items[1] and self.Items[1].options or options)
- end
- --- Returns the selected item in the auto complete
- function AutoComplete:GetSelectedItem ()
- if Gui.Visible == false then
- return nil
- end
- return AutoComplete.Items[AutoComplete.SelectedItem]
- end
- --- Hides the auto complete
- function AutoComplete:Hide ()
- Gui.Visible = false
- end
- --- Returns if the menu is visible
- function AutoComplete:IsVisible ()
- return Gui.Visible
- end
- --- Changes the user's item selection by the given delta
- function AutoComplete:Select (delta)
- if not Gui.Visible then return end
- self.SelectedItem = self.SelectedItem + delta
- if self.SelectedItem > #self.Items then
- self.SelectedItem = 1
- elseif self.SelectedItem < 1 then
- self.SelectedItem = #self.Items
- end
- for i, item in pairs(self.Items) do
- item.gui.BackgroundTransparency = i == self.SelectedItem and 0.5 or 1
- end
- if self.Items[self.SelectedItem] and self.Items[self.SelectedItem].options then
- UpdateInfoDisplay(self.Items[self.SelectedItem].options or {})
- end
- end
- return AutoComplete
- end
- ]]></ProtectedString>
- <BinaryString name="Tags"></BinaryString>
- </Properties>
- </Item>
- <Item class="ModuleScript" referent="RBX608CDE2293BD49E3B78C34371D6C8E65">
- <Properties>
- <Content name="LinkedSource"><null></null></Content>
- <string name="Name">Window</string>
- <string name="ScriptGuid">{6D536A57-C13E-4C1A-9E42-10CA9207B1CA}</string>
- <ProtectedString name="Source"><![CDATA[-- A fully extensible and type-safe admin-commands console
- -- @documentation https://github.com/evaera/Cmdr/blob/master/README.md
- -- @source https://github.com/evaera/Cmdr
- -- @rostrap Cmdr
- -- @author evaera
- -- Here be dragons
- -- luacheck: ignore 212
- local GuiService = game:GetService("GuiService")
- local UserInputService = game:GetService("UserInputService")
- local TextService = game:GetService("TextService")
- local Players = game:GetService("Players")
- local Player = Players.LocalPlayer
- local LINE_HEIGHT = 20
- local WINDOW_MAX_HEIGHT = 300
- --- Window handles the command bar GUI
- local Window = {
- Valid = true,
- AutoComplete = nil,
- ProcessEntry = nil,
- OnTextChanged = nil,
- Cmdr = nil,
- HistoryState = nil,
- }
- local Gui = Player:WaitForChild("PlayerGui"):WaitForChild("Cmdr"):WaitForChild("Frame")
- local Line = Gui:WaitForChild("Line")
- local Entry = Gui:WaitForChild("Entry")
- Line.Parent = nil
- --- Update the text entry label
- function Window:UpdateLabel()
- Entry.TextLabel.Text = Player.Name .. "@" .. self.Cmdr.PlaceName .. "$"
- Entry.TextLabel.Size = UDim2.new(0, Entry.TextLabel.TextBounds.X, 0, 20)
- Entry.TextBox.Position = UDim2.new(0, Entry.TextLabel.Size.X.Offset + 7, 0, 0)
- end
- --- Get the text entry label
- function Window:GetLabel()
- return Entry.TextLabel.Text
- end
- --- Recalculate the window height
- function Window:UpdateWindowHeight()
- local windowHeight = LINE_HEIGHT
- for _, child in pairs(Gui:GetChildren()) do
- if child:IsA("GuiObject") then
- windowHeight = windowHeight + child.Size.Y.Offset
- end
- end
- Gui.CanvasSize = UDim2.new(Gui.CanvasSize.X.Scale, Gui.CanvasSize.X.Offset, 0, windowHeight)
- Gui.Size =
- UDim2.new(
- Gui.Size.X.Scale,
- Gui.Size.X.Offset,
- 0,
- windowHeight > WINDOW_MAX_HEIGHT and WINDOW_MAX_HEIGHT or windowHeight
- )
- Gui.CanvasPosition = Vector2.new(0, math.clamp(windowHeight - 300, 0, math.huge))
- end
- --- Add a line to the command bar
- function Window:AddLine(text, color)
- if #text == 0 then
- Window:UpdateWindowHeight()
- return
- end
- local str = self.Cmdr.Util.EmulateTabstops(text or "nil", 8)
- local line = Line:Clone()
- line.Size =
- UDim2.new(
- line.Size.X.Scale,
- line.Size.X.Offset,
- 0,
- TextService:GetTextSize(
- str,
- line.TextSize,
- line.Font,
- Vector2.new(Gui.UIListLayout.AbsoluteContentSize.X, math.huge)
- ).Y + (LINE_HEIGHT - line.TextSize)
- )
- line.Text = str
- line.TextColor3 = color or line.TextColor3
- line.Parent = Gui
- end
- --- Returns if the command bar is visible
- function Window:IsVisible()
- return Gui.Visible
- end
- --- Sets the command bar visible or not
- function Window:SetVisible(visible)
- Gui.Visible = visible
- if visible then
- Entry.TextBox:CaptureFocus()
- self:SetEntryText("")
- if self.Cmdr.ActivationUnlocksMouse then
- self.PreviousMouseBehavior = UserInputService.MouseBehavior
- UserInputService.MouseBehavior = Enum.MouseBehavior.Default
- end
- else
- Entry.TextBox:ReleaseFocus()
- self.AutoComplete:Hide()
- if self.PreviousMouseBehavior then
- UserInputService.MouseBehavior = self.PreviousMouseBehavior
- self.PreviousMouseBehavior = nil
- end
- end
- end
- --- Hides the command bar
- function Window:Hide()
- return self:SetVisible(false)
- end
- --- Shows the command bar
- function Window:Show()
- return self:SetVisible(true)
- end
- --- Sets the text in the command bar text box, and captures focus
- function Window:SetEntryText(text)
- Entry.TextBox.Text = text
- if self:IsVisible() then
- Entry.TextBox:CaptureFocus()
- end
- end
- --- Gets the text in the command bar text box
- function Window:GetEntryText()
- return Entry.TextBox.Text:gsub("\t", "")
- end
- --- Sets whether the command is in a valid state or not.
- -- Cannot submit if in invalid state.
- function Window:SetIsValidInput(isValid, errorText)
- Entry.TextBox.TextColor3 = isValid and Color3.fromRGB(255, 255, 255) or Color3.fromRGB(255, 73, 73)
- self.Valid = isValid
- self._errorText = errorText
- end
- --- Event handler for text box focus lost
- function Window:LoseFocus(submit)
- local text = Entry.TextBox.Text
- self:ClearHistoryState()
- if Gui.Visible and not GuiService.MenuIsOpen then
- -- self:SetEntryText("")
- Entry.TextBox:CaptureFocus()
- elseif GuiService.MenuIsOpen and Gui.Visible then
- self:Hide()
- end
- if submit and self.Valid then
- wait()
- self:SetEntryText("")
- self.ProcessEntry(text)
- elseif submit then
- self:AddLine(self._errorText, Color3.fromRGB(255, 153, 153))
- end
- end
- function Window:TraverseHistory(delta)
- local history = self.Cmdr.Dispatcher:GetHistory()
- if self.HistoryState == nil then
- self.HistoryState = {
- Position = #history + 1;
- InitialText = self:GetEntryText();
- }
- end
- self.HistoryState.Position = math.clamp(self.HistoryState.Position + delta, 1, #history + 1)
- self:SetEntryText(
- self.HistoryState.Position == #history + 1
- and self.HistoryState.InitialText
- or history[self.HistoryState.Position]
- )
- end
- function Window:ClearHistoryState()
- self.HistoryState = nil
- end
- function Window:SelectVertical(delta)
- if self.AutoComplete:IsVisible() and not self.HistoryState then
- self.AutoComplete:Select(delta)
- else
- self:TraverseHistory(delta)
- end
- end
- local lastPressTime = 0
- local pressCount = 0
- --- Handles user input when the box is focused
- function Window:BeginInput(input, gameProcessed)
- if GuiService.MenuIsOpen then
- self:Hide()
- end
- if gameProcessed and self:IsVisible() == false then
- return
- end
- if self.Cmdr.ActivationKeys[input.KeyCode] then -- Activate the command bar
- if self.Cmdr.MashToEnable and not self.Cmdr.Enabled then
- if tick() - lastPressTime < 1 then
- if pressCount >= 5 then
- return self.Cmdr:SetEnabled(true)
- else
- pressCount = pressCount + 1
- end
- else
- pressCount = 1
- end
- lastPressTime = tick()
- elseif self.Cmdr.Enabled then
- self:SetVisible(not self:IsVisible())
- wait()
- self:SetEntryText("")
- if GuiService.MenuIsOpen then -- Special case for menu getting stuck open (roblox bug)
- self:Hide()
- end
- end
- return
- end
- if self.Cmdr.Enabled == false or not self:IsVisible() then
- if self:IsVisible() then
- self:Hide()
- end
- return
- end
- if input.KeyCode == Enum.KeyCode.Down then -- Auto Complete Down
- self:SelectVertical(1)
- elseif input.KeyCode == Enum.KeyCode.Up then -- Auto Complete Up
- self:SelectVertical(-1)
- elseif input.KeyCode == Enum.KeyCode.Return then -- Eat new lines
- wait()
- self:SetEntryText(self:GetEntryText():gsub("\n", ""):gsub("\r", ""))
- elseif input.KeyCode == Enum.KeyCode.Tab then -- Auto complete
- local item = self.AutoComplete:GetSelectedItem()
- local text = self:GetEntryText()
- if item and not (text:sub(#text, #text):match("%s") and self.AutoComplete.LastItem) then
- local replace = item[2]
- local newText
- local insertSpace = true
- local command = self.AutoComplete.Command
- if command then
- local lastArg = self.AutoComplete.Arg
- newText = command.Alias
- insertSpace = self.AutoComplete.NumArgs ~= #command.ArgumentDefinitions
- local args = command.Arguments
- for i = 1, #args do
- local arg = args[i]
- local segments = arg.RawSegments
- if arg == lastArg then
- segments[#segments] = replace
- end
- local argText = arg.Prefix .. table.concat(segments, ",")
- -- Put auto completion options in quotation marks if they have a space
- if argText:find(" ") then
- argText = ("%q"):format(argText)
- end
- newText = ("%s %s"):format(newText, argText)
- if arg == lastArg then
- break
- end
- end
- else
- newText = replace
- end
- -- need to wait a frame so we can eat the \t
- wait()
- -- Update the text box
- self:SetEntryText(newText .. (insertSpace and " " or ""))
- else
- -- Still need to eat the \t even if there is no auto-complete to show
- wait()
- self:SetEntryText(self:GetEntryText())
- end
- else
- self:ClearHistoryState()
- end
- end
- -- Hook events
- Entry.TextBox.FocusLost:Connect(
- function(submit)
- return Window:LoseFocus(submit)
- end
- )
- UserInputService.InputBegan:Connect(
- function(input, gameProcessed)
- return Window:BeginInput(input, gameProcessed)
- end
- )
- Entry.TextBox:GetPropertyChangedSignal("Text"):Connect(
- function()
- if Entry.TextBox.Text:match("\t") then -- Eat \t
- Entry.TextBox.Text = Entry.TextBox.Text:gsub("\t", "")
- return
- end
- if Window.OnTextChanged then
- return Window.OnTextChanged(Entry.TextBox.Text)
- end
- end
- )
- Gui.ChildAdded:Connect(Window.UpdateWindowHeight)
- return Window
- ]]></ProtectedString>
- <BinaryString name="Tags"></BinaryString>
- </Properties>
- </Item>
- </Item>
- <Item class="ModuleScript" referent="RBX3703B004929C4F1396DD20F7DAF0F8EF">
- <Properties>
- <Content name="LinkedSource"><null></null></Content>
- <string name="Name">CreateGui</string>
- <string name="ScriptGuid">{6AAC372A-F4F1-4F9E-B52F-81CAA1AC3127}</string>
- <ProtectedString name="Source"><![CDATA[-- A fully extensible and type-safe admin-commands console
- -- @documentation https://github.com/evaera/Cmdr/blob/master/README.md
- -- @source https://github.com/evaera/Cmdr
- -- @rostrap Cmdr
- -- @author evaera
- --[[
- DO NOT MODIFY. This file is auto-generated.
- Plugin: https://www.roblox.com/library/2307140444/Object-to-Lua
- ]]
- return function ()
- local Cmdr = Instance.new("ScreenGui")
- Cmdr.DisplayOrder = 1000
- Cmdr.Name = "Cmdr"
- Cmdr.ResetOnSpawn = false
- local Frame = Instance.new("ScrollingFrame")
- Frame.BackgroundColor3 = Color3.fromRGB(17, 17, 17)
- Frame.BackgroundTransparency = 0.4
- Frame.BorderSizePixel = 0
- Frame.CanvasSize = UDim2.new(0, 0, 0, 100)
- Frame.Name = "Frame"
- Frame.Position = UDim2.new(0.025, 0, 0, 25)
- Frame.ScrollBarThickness = 6
- Frame.ScrollingDirection = Enum.ScrollingDirection.Y
- Frame.Selectable = false
- Frame.Size = UDim2.new(0.95, 0, 0, 50)
- Frame.Visible = false
- Frame.Parent = Cmdr
- local Autocomplete = Instance.new("Frame")
- Autocomplete.BackgroundColor3 = Color3.fromRGB(59, 59, 59)
- Autocomplete.BackgroundTransparency = 0.5
- Autocomplete.BorderSizePixel = 0
- Autocomplete.Name = "Autocomplete"
- Autocomplete.Position = UDim2.new(0, 167, 0, 75)
- Autocomplete.Size = UDim2.new(0, 200, 0, 200)
- Autocomplete.Visible = false
- Autocomplete.Parent = Cmdr
- local UIListLayout = Instance.new("UIListLayout")
- UIListLayout.SortOrder = Enum.SortOrder.LayoutOrder
- UIListLayout.Parent = Frame
- local Line = Instance.new("TextLabel")
- Line.BackgroundColor3 = Color3.fromRGB(255, 255, 255)
- Line.BackgroundTransparency = 1
- Line.Font = Enum.Font.Code
- Line.Name = "Line"
- Line.Size = UDim2.new(1, 0, 0, 20)
- Line.TextColor3 = Color3.fromRGB(255, 255, 255)
- Line.TextSize = 14
- Line.TextXAlignment = Enum.TextXAlignment.Left
- Line.Parent = Frame
- local UIPadding = Instance.new("UIPadding")
- UIPadding.PaddingBottom = UDim.new(0, 10)
- UIPadding.PaddingLeft = UDim.new(0, 10)
- UIPadding.PaddingRight = UDim.new(0, 10)
- UIPadding.PaddingTop = UDim.new(0, 10)
- UIPadding.Parent = Frame
- local Entry = Instance.new("Frame")
- Entry.BackgroundTransparency = 1
- Entry.LayoutOrder = 999999999
- Entry.Name = "Entry"
- Entry.Size = UDim2.new(1, 0, 0, 20)
- Entry.Parent = Frame
- local UIListLayout2 = Instance.new("UIListLayout")
- UIListLayout2.SortOrder = Enum.SortOrder.LayoutOrder
- UIListLayout2.Parent = Autocomplete
- local Title = Instance.new("Frame")
- Title.BackgroundColor3 = Color3.fromRGB(59, 59, 59)
- Title.BackgroundTransparency = 0.2
- Title.BorderSizePixel = 0
- Title.LayoutOrder = -2
- Title.Name = "Title"
- Title.Size = UDim2.new(1, 0, 0, 40)
- Title.Parent = Autocomplete
- local Description = Instance.new("Frame")
- Description.BackgroundColor3 = Color3.fromRGB(59, 59, 59)
- Description.BackgroundTransparency = 0.2
- Description.BorderSizePixel = 0
- Description.LayoutOrder = -1
- Description.Name = "Description"
- Description.Size = UDim2.new(1, 0, 0, 20)
- Description.Parent = Autocomplete
- local TextButton = Instance.new("TextButton")
- TextButton.BackgroundColor3 = Color3.fromRGB(59, 59, 59)
- TextButton.BackgroundTransparency = 0.5
- TextButton.BorderSizePixel = 0
- TextButton.Font = Enum.Font.Code
- TextButton.Size = UDim2.new(1, 0, 0, 30)
- TextButton.Text = ""
- TextButton.TextColor3 = Color3.fromRGB(255, 255, 255)
- TextButton.TextSize = 14
- TextButton.TextXAlignment = Enum.TextXAlignment.Left
- TextButton.Parent = Autocomplete
- local TextBox = Instance.new("TextBox")
- TextBox.BackgroundColor3 = Color3.fromRGB(255, 255, 255)
- TextBox.BackgroundTransparency = 1
- TextBox.ClearTextOnFocus = false
- TextBox.Font = Enum.Font.Code
- TextBox.LayoutOrder = 999999999
- TextBox.Position = UDim2.new(0, 140, 0, 0)
- TextBox.Size = UDim2.new(1, 0, 0, 20)
- TextBox.Text = "x"
- TextBox.TextColor3 = Color3.fromRGB(255, 255, 255)
- TextBox.TextSize = 14
- TextBox.TextXAlignment = Enum.TextXAlignment.Left
- TextBox.Parent = Entry
- local TextLabel = Instance.new("TextLabel")
- TextLabel.BackgroundColor3 = Color3.fromRGB(255, 255, 255)
- TextLabel.BackgroundTransparency = 1
- TextLabel.Font = Enum.Font.Code
- TextLabel.Size = UDim2.new(0, 133, 0, 20)
- TextLabel.Text = ""
- TextLabel.TextColor3 = Color3.fromRGB(255, 223, 93)
- TextLabel.TextSize = 14
- TextLabel.TextXAlignment = Enum.TextXAlignment.Left
- TextLabel.Parent = Entry
- local Field = Instance.new("TextLabel")
- Field.BackgroundColor3 = Color3.fromRGB(255, 255, 255)
- Field.BackgroundTransparency = 1
- Field.Font = Enum.Font.SourceSansBold
- Field.Name = "Field"
- Field.Size = UDim2.new(0, 37, 1, 0)
- Field.Text = "from"
- Field.TextColor3 = Color3.fromRGB(255, 255, 255)
- Field.TextSize = 20
- Field.TextXAlignment = Enum.TextXAlignment.Left
- Field.Parent = Title
- local UIPadding2 = Instance.new("UIPadding")
- UIPadding2.PaddingLeft = UDim.new(0, 10)
- UIPadding2.Parent = Title
- local Label = Instance.new("TextLabel")
- Label.BackgroundColor3 = Color3.fromRGB(255, 255, 255)
- Label.BackgroundTransparency = 1
- Label.Font = Enum.Font.SourceSansLight
- Label.Name = "Label"
- Label.Size = UDim2.new(1, 0, 1, 0)
- Label.Text = "The players to teleport. The players to teleport. The players to teleport. The players to teleport. "
- Label.TextColor3 = Color3.fromRGB(255, 255, 255)
- Label.TextSize = 16
- Label.TextWrapped = true
- Label.TextXAlignment = Enum.TextXAlignment.Left
- Label.TextYAlignment = Enum.TextYAlignment.Top
- Label.Parent = Description
- local UIPadding3 = Instance.new("UIPadding")
- UIPadding3.PaddingBottom = UDim.new(0, 10)
- UIPadding3.PaddingLeft = UDim.new(0, 10)
- UIPadding3.PaddingRight = UDim.new(0, 10)
- UIPadding3.Parent = Description
- local Typed = Instance.new("TextLabel")
- Typed.BackgroundColor3 = Color3.fromRGB(255, 255, 255)
- Typed.BackgroundTransparency = 1
- Typed.Font = Enum.Font.Code
- Typed.Name = "Typed"
- Typed.Size = UDim2.new(1, 0, 1, 0)
- Typed.Text = "Lab"
- Typed.TextColor3 = Color3.fromRGB(131, 222, 255)
- Typed.TextSize = 14
- Typed.TextXAlignment = Enum.TextXAlignment.Left
- Typed.Parent = TextButton
- local UIPadding4 = Instance.new("UIPadding")
- UIPadding4.PaddingLeft = UDim.new(0, 10)
- UIPadding4.Parent = TextButton
- local Suggest = Instance.new("TextLabel")
- Suggest.BackgroundColor3 = Color3.fromRGB(255, 255, 255)
- Suggest.BackgroundTransparency = 1
- Suggest.Font = Enum.Font.Code
- Suggest.Name = "Suggest"
- Suggest.Size = UDim2.new(1, 0, 1, 0)
- Suggest.Text = " el"
- Suggest.TextColor3 = Color3.fromRGB(255, 255, 255)
- Suggest.TextSize = 14
- Suggest.TextXAlignment = Enum.TextXAlignment.Left
- Suggest.Parent = TextButton
- local Type = Instance.new("TextLabel")
- Type.BackgroundColor3 = Color3.fromRGB(255, 255, 255)
- Type.BackgroundTransparency = 1
- Type.BorderColor3 = Color3.fromRGB(255, 153, 153)
- Type.Font = Enum.Font.SourceSans
- Type.Name = "Type"
- Type.Position = UDim2.new(1, 0, 0, 0)
- Type.Size = UDim2.new(0, 0, 1, 0)
- Type.Text = ": Players"
- Type.TextColor3 = Color3.fromRGB(255, 255, 255)
- Type.TextSize = 15
- Type.TextXAlignment = Enum.TextXAlignment.Left
- Type.Parent = Field
- Cmdr.Parent = game:GetService('Players').LocalPlayer.PlayerGui
- return Cmdr
- end]]></ProtectedString>
- <BinaryString name="Tags"></BinaryString>
- </Properties>
- </Item>
- <Item class="Folder" referent="RBX47F98EC7EB6A4D6591877ED2C63BF730">
- <Properties>
- <string name="Name">Commands</string>
- <BinaryString name="Tags"></BinaryString>
- </Properties>
- <Item class="Folder" referent="RBX4731997E92A644028AD089E27BBDAA2C">
- <Properties>
- <string name="Name">Def</string>
- <BinaryString name="Tags"></BinaryString>
- </Properties>
- <Item class="ModuleScript" referent="RBXFD455615CEE34E55AB6C865CE2EA0E86">
- <Properties>
- <Content name="LinkedSource"><null></null></Content>
- <string name="Name">alias</string>
- <string name="ScriptGuid">{B21018D9-75E4-4E2E-847F-722175801FE8}</string>
- <ProtectedString name="Source"><![CDATA[-- A fully extensible and type-safe admin-commands console
- -- @documentation https://github.com/evaera/Cmdr/blob/master/README.md
- -- @source https://github.com/evaera/Cmdr
- -- @rostrap Cmdr
- -- @author evaera
- return {
- Name = "alias";
- Aliases = {};
- Description = "Creates a new, single command out of a command and given arguments.";
- Group = "DefaultUtil";
- Args = {
- {
- Type = "string";
- Name = "Alias name";
- Description = "The key or input type you'd like to bind the command to."
- },
- {
- Type = "string";
- Name = "Command string";
- Description = "The command text you want to run. Separate multiple commands with \"&&\". Accept arguments with $1, $2, $3, etc."
- },
- };
- Run = function(context, name, commandString)
- context.Cmdr.Registry:RegisterCommandObject(
- context.Cmdr.Util.MakeAliasCommand(name, commandString)
- )
- return ("Created alias %q"):format(name)
- end
- }]]></ProtectedString>
- <BinaryString name="Tags"></BinaryString>
- </Properties>
- </Item>
- <Item class="ModuleScript" referent="RBXFD7DD76329D8401487311567F1117501">
- <Properties>
- <Content name="LinkedSource"><null></null></Content>
- <string name="Name">bind</string>
- <string name="ScriptGuid">{ACEB33F1-B5FC-44FE-A1BD-767A8A49B89E}</string>
- <ProtectedString name="Source"><![CDATA[-- A fully extensible and type-safe admin-commands console
- -- @documentation https://github.com/evaera/Cmdr/blob/master/README.md
- -- @source https://github.com/evaera/Cmdr
- -- @rostrap Cmdr
- -- @author evaera
- local UserInputService = game:GetService("UserInputService")
- return {
- Name = "bind";
- Aliases = {};
- Description = "Binds a command string to a key or mouse input.";
- Group = "DefaultUtil";
- Args = {
- {
- Type = "userInput ! bindableResource @ player";
- Name = "Input";
- Description = "The key or input type you'd like to bind the command to."
- },
- {
- Type = "command";
- Name = "Command";
- Description = "The command you want to run on this input"
- },
- {
- Type = "string";
- Name = "Arguments";
- Description = "The arguments for the command";
- Default = "";
- }
- };
- Run = function(context, bind, command, arguments)
- local binds = context:GetStore("CMDR_Binds")
- command = command .. " " .. arguments
- if binds[bind] then
- binds[bind]:Disconnect()
- end
- local bindType = context:GetArgument(1).Type.Name
- if bindType == "userInput" then
- binds[bind] = UserInputService.InputBegan:Connect(function(input, gameProcessed)
- if gameProcessed then
- return
- end
- if input.UserInputType == bind or input.KeyCode == bind then
- context:Reply(context.Dispatcher:EvaluateAndRun(context.Cmdr.Util.RunEmbeddedCommands(context.Dispatcher, command)))
- end
- end)
- elseif bindType == "bindableResource" then
- return "Unimplemented..."
- elseif bindType == "player" then
- binds[bind] = bind.Chatted:Connect(function(message)
- local args = { message }
- local chatCommand = context.Cmdr.Util.RunEmbeddedCommands(context.Dispatcher, context.Cmdr.Util.SubstituteArgs(command, args))
- context:Reply(("%s $ %s : %s"):format(
- bind.Name,
- chatCommand,
- context.Dispatcher:EvaluateAndRun(chatCommand)
- ), Color3.fromRGB(244, 92, 66))
- end)
- end
- return "Bound command to input."
- end
- }]]></ProtectedString>
- <BinaryString name="Tags"></BinaryString>
- </Properties>
- </Item>
- <Item class="ModuleScript" referent="RBX7A73A217C3A240B28E24B4DC2606B120">
- <Properties>
- <Content name="LinkedSource"><null></null></Content>
- <string name="Name">clear</string>
- <string name="ScriptGuid">{B5F9EAF9-D44F-492D-9B6C-AAA30D096857}</string>
- <ProtectedString name="Source"><![CDATA[-- A fully extensible and type-safe admin-commands console
- -- @documentation https://github.com/evaera/Cmdr/blob/master/README.md
- -- @source https://github.com/evaera/Cmdr
- -- @rostrap Cmdr
- -- @author evaera
- local Players = game:GetService("Players")
- return {
- Name = "clear",
- Aliases = {},
- Description = "Clear all lines above the entry line of the Cmdr window.",
- Group = "DefaultUtil",
- Args = {},
- Run = function()
- local player = Players.LocalPlayer
- local gui = player:WaitForChild("PlayerGui"):WaitForChild("Cmdr")
- local frame = gui:WaitForChild("Frame")
- if gui and frame then
- for _, child in pairs(frame:GetChildren()) do
- if child.Name == "Line" and child:IsA("TextLabel") then
- child:Destroy()
- end
- end
- end
- return ""
- end
- }
- ]]></ProtectedString>
- <BinaryString name="Tags"></BinaryString>
- </Properties>
- </Item>
- <Item class="ModuleScript" referent="RBX34CA904CCCA54EFE8BE1C154B1A37233">
- <Properties>
- <Content name="LinkedSource"><null></null></Content>
- <string name="Name">echo</string>
- <string name="ScriptGuid">{206CE708-94B9-4EF9-99C1-ED0CB0B86997}</string>
- <ProtectedString name="Source"><![CDATA[-- A fully extensible and type-safe admin-commands console
- -- @documentation https://github.com/evaera/Cmdr/blob/master/README.md
- -- @source https://github.com/evaera/Cmdr
- -- @rostrap Cmdr
- -- @author evaera
- return {
- Name = "echo";
- Aliases = {};
- Description = "Echoes your text back to you.";
- Group = "DefaultUtil";
- Args = {
- {
- Type = "string";
- Name = "Text";
- Description = "The text."
- },
- };
- Run = function(_, text)
- return text
- end
- }]]></ProtectedString>
- <BinaryString name="Tags"></BinaryString>
- </Properties>
- </Item>
- <Item class="ModuleScript" referent="RBXB3DDC350AF6A40458400D914350D912D">
- <Properties>
- <Content name="LinkedSource"><null></null></Content>
- <string name="Name">history</string>
- <string name="ScriptGuid">{F1CDCC16-9A1F-48DD-8A9D-8B8DC961C0BC}</string>
- <ProtectedString name="Source"><![CDATA[-- A fully extensible and type-safe admin-commands console
- -- @documentation https://github.com/evaera/Cmdr/blob/master/README.md
- -- @source https://github.com/evaera/Cmdr
- -- @rostrap Cmdr
- -- @author evaera
- return {
- Name = "history";
- Aliases = {};
- AutoExec = {
- "alias ! run ${history $1}";
- "alias ^ run ${run replace ${history -1} $1 $2}";
- "alias !! ! -1";
- };
- Description = "Displays previous commands from history.";
- Group = "DefaultUtil";
- Args = {
- {
- Type = "integer";
- Name = "Line Number";
- Description = "Command line number (can be negative to go from end)"
- },
- };
- Run = function(context, line)
- local history = context.Dispatcher:GetHistory()
- if line <= 0 then
- line = #history + line
- end
- return history[line] or ""
- end
- }]]></ProtectedString>
- <BinaryString name="Tags"></BinaryString>
- </Properties>
- </Item>
- <Item class="ModuleScript" referent="RBX676C36DC4E034FC9BF0B5483872436C9">
- <Properties>
- <Content name="LinkedSource"><null></null></Content>
- <string name="Name">replace</string>
- <string name="ScriptGuid">{7DC0CEEF-77FC-4E75-80C3-DF429AF7FE3D}</string>
- <ProtectedString name="Source"><![CDATA[-- A fully extensible and type-safe admin-commands console
- -- @documentation https://github.com/evaera/Cmdr/blob/master/README.md
- -- @source https://github.com/evaera/Cmdr
- -- @rostrap Cmdr
- -- @author evaera
- return {
- Name = "replace";
- Aliases = {};
- Description = "Replaces text A with text B";
- Group = "DefaultUtil";
- Args = {
- {
- Type = "string";
- Name = "Haystack";
- Description = "The source string upon which to perform replacement."
- },
- {
- Type = "string";
- Name = "Needle";
- Description = "The string pattern search for."
- },
- {
- Type = "string";
- Name = "Replacement";
- Description = "The string to replace matches (%1 to insert matches)."
- },
- };
- Run = function(_, haystack, needle, replacement)
- return haystack:gsub(needle, replacement)
- end
- }]]></ProtectedString>
- <BinaryString name="Tags"></BinaryString>
- </Properties>
- </Item>
- <Item class="ModuleScript" referent="RBXBE09863FAB334CFA9929D6C87FFA2A1C">
- <Properties>
- <Content name="LinkedSource"><null></null></Content>
- <string name="Name">run</string>
- <string name="ScriptGuid">{E604A54B-4745-4AF6-A742-9F37223C0E6D}</string>
- <ProtectedString name="Source"><![CDATA[-- A fully extensible and type-safe admin-commands console
- -- @documentation https://github.com/evaera/Cmdr/blob/master/README.md
- -- @source https://github.com/evaera/Cmdr
- -- @rostrap Cmdr
- -- @author evaera
- return {
- Name = "run";
- Aliases = {};
- AutoExec = {
- "alias discard replace ${run $1} .* \\\"\\\""
- };
- Description = "Runs a given command string (replacing embedded commands).";
- Group = "DefaultUtil";
- Args = {
- {
- Type = "string";
- Name = "Command";
- Description = "The command string to run"
- },
- };
- Run = function(context, command)
- return context.Dispatcher:EvaluateAndRun(context.Cmdr.Util.RunEmbeddedCommands(context.Dispatcher, command))
- end
- }]]></ProtectedString>
- <BinaryString name="Tags"></BinaryString>
- </Properties>
- </Item>
- <Item class="ModuleScript" referent="RBX692EA8693EDA40CDAE85A5547E3FE20F">
- <Properties>
- <Content name="LinkedSource"><null></null></Content>
- <string name="Name">runif</string>
- <string name="ScriptGuid">{C83830D5-BB89-4EC5-8FD7-252E1E0905A6}</string>
- <ProtectedString name="Source"><![CDATA[-- A fully extensible and type-safe admin-commands console
- -- @documentation https://github.com/evaera/Cmdr/blob/master/README.md
- -- @source https://github.com/evaera/Cmdr
- -- @rostrap Cmdr
- -- @author evaera
- local conditions = {
- startsWith = function (text, arg)
- if text:sub(1, #arg) == arg then
- return text:sub(#arg + 1)
- end
- end
- }
- return {
- Name = "runif";
- Aliases = {};
- Description = "Runs a given command string if a certain condition is met.";
- Group = "DefaultUtil";
- Args = {
- {
- Type = "conditionFunction";
- Name = "Condition";
- Description = "The condition function"
- },
- {
- Type = "string";
- Name = "Argument";
- Description = "The argument to the condition function"
- },
- {
- Type = "string";
- Name = "Test against";
- Description = "The text to test against."
- },
- {
- Type = "string";
- Name = "Command";
- Description = "The command string to run if requirements are met. If omitted, return value from condition function is used.";
- Optional = true;
- },
- };
- Run = function(context, condition, arg, testAgainst, command)
- local conditionFunc = conditions[condition]
- if not conditionFunc then
- return ("Condition %q is not valid."):format(condition)
- end
- local text = conditionFunc(testAgainst, arg)
- if text then
- return context.Dispatcher:EvaluateAndRun(context.Cmdr.Util.RunEmbeddedCommands(context.Dispatcher, command or text))
- end
- return ""
- end
- }]]></ProtectedString>
- <BinaryString name="Tags"></BinaryString>
- </Properties>
- </Item>
- <Item class="ModuleScript" referent="RBX301C5052F0B7472097D6286887CACD42">
- <Properties>
- <Content name="LinkedSource"><null></null></Content>
- <string name="Name">unbind</string>
- <string name="ScriptGuid">{ABEB5DC5-66D5-4685-A15C-AA011202392A}</string>
- <ProtectedString name="Source"><![CDATA[-- A fully extensible and type-safe admin-commands console
- -- @documentation https://github.com/evaera/Cmdr/blob/master/README.md
- -- @source https://github.com/evaera/Cmdr
- -- @rostrap Cmdr
- -- @author evaera
- return {
- Name = "unbind";
- Aliases = {};
- Description = "Unbinds an input previously bound with Bind";
- Group = "DefaultUtil";
- Args = {
- {
- Type = "userInput ! bindableResource @ player";
- Name = "Input/Key";
- Description = "The key or input type you'd like to unbind."
- }
- };
- Run = function(context, inputEnum)
- local binds = context:GetStore("CMDR_Binds")
- if binds[inputEnum] then
- binds[inputEnum]:Disconnect()
- binds[inputEnum] = nil
- return "Unbound command from input."
- else
- return "That input wasn't bound."
- end
- end
- }]]></ProtectedString>
- <BinaryString name="Tags"></BinaryString>
- </Properties>
- </Item>
- <Item class="ModuleScript" referent="RBX9601429EDA45475CA90D07B97A5CC78B">
- <Properties>
- <Content name="LinkedSource"><null></null></Content>
- <string name="Name">eval</string>
- <string name="ScriptGuid">{9AC42E12-BCE6-4328-AB30-043D1EEFDAB9}</string>
- <ProtectedString name="Source"><![CDATA[-- A fully extensible and type-safe admin-commands console
- -- @documentation https://github.com/evaera/Cmdr/blob/master/README.md
- -- @source https://github.com/evaera/Cmdr
- -- @rostrap Cmdr
- -- @author evaera
- return {
- Name = "eval";
- Aliases = {"exec"};
- Description = "Executes code";
- Group = "DefaultUtil";
- Args = {
- {
- Type = "string";
- Name = "Source";
- Description = "Code you wish to run"
- }
- };
- Run = function(context, src)
- local f = context.Cmdr.Loadstring(src)
- if f then
- local e = getfenv(f)
- e.Cmdr = context.Cmdr
- e.Context = context
- return f()
- end
- return "Failed to run"
- end
- }]]></ProtectedString>
- <BinaryString name="Tags"></BinaryString>
- </Properties>
- </Item>
- <Item class="ModuleScript" referent="RBX825A72F9D24341DA8AF01E1B9F021D27">
- <Properties>
- <Content name="LinkedSource"><null></null></Content>
- <string name="Name">help</string>
- <string name="ScriptGuid">{D0768F0F-B8EC-4F54-8FAF-BDAD2E095F8A}</string>
- <ProtectedString name="Source"><![CDATA[-- A fully extensible and type-safe admin-commands console
- -- @documentation https://github.com/evaera/Cmdr/blob/master/README.md
- -- @source https://github.com/evaera/Cmdr
- -- @rostrap Cmdr
- -- @author evaera
- return {
- Name = "help";
- Description = "Displays a list of all commands, or inspects one command.";
- Group = "Help";
- Args = {
- {
- Type = "command";
- Name = "Command";
- Description = "The command to view information on";
- Optional = true;
- },
- };
- Run = function (context, commandName)
- if commandName then
- local command = context.Cmdr.Registry:GetCommand(commandName)
- context:Reply(("Command: %s"):format(command.Name), Color3.fromRGB(230, 126, 34))
- if command.Aliases and #command.Aliases > 0 then
- context:Reply(("Aliases: %s"):format(table.concat(command.Aliases, ", ")), Color3.fromRGB(230, 230, 230))
- end
- context:Reply(command.Description, Color3.fromRGB(230, 230, 230))
- for i, arg in ipairs(command.Args) do
- context:Reply(("#%d %s%s: %s - %s"):format(
- i,
- arg.Name,
- arg.Optional == true and "?" or "",
- arg.Type, arg.Description
- ))
- end
- else
- local commands = context.Cmdr.Registry:GetCommands()
- for _, cmd in pairs(commands) do
- context:Reply(("%s - %s"):format(cmd.Name, cmd.Description))
- end
- end
- return ""
- end;
- }]]></ProtectedString>
- <BinaryString name="Tags"></BinaryString>
- </Properties>
- </Item>
- </Item>
- <Item class="ModuleScript" referent="RBX1E0E6A007EB24761B56F8943089A7AAE">
- <Properties>
- <Content name="LinkedSource"><null></null></Content>
- <string name="Name">LightsaberBattlegrounds</string>
- <string name="ScriptGuid">{792920A8-AE46-412D-8D0B-B4B1581A6BDF}</string>
- <ProtectedString name="Source"><![CDATA[return function()
- return game.GameId == 1351881085
- end]]></ProtectedString>
- <BinaryString name="Tags"></BinaryString>
- </Properties>
- <Item class="ModuleScript" referent="RBX99AD8FE961E145D48066AF9CE6A9CDF6">
- <Properties>
- <Content name="LinkedSource"><null></null></Content>
- <string name="Name">Platform</string>
- <string name="ScriptGuid">{B2DF7DF4-1B01-4F1E-9CEF-5C9D8D78F6AE}</string>
- <ProtectedString name="Source"><![CDATA[return {
- Name = "platform";
- Aliases = {};
- Description = "Controls platform";
- Group = "DefaultUtil";
- Args = {
- {
- Type = "string";
- Name = "Action";
- Description = "Action you wish to execute with the platform"
- },
- };
- Run = function(_, text)
- local console = workspace:FindFirstChild('Console', true)
- text = text:lower()
- if text == 'up' then
- click_detector(console.Panel.Up.ClickDetector, 1)
- elseif text == 'down' then
- click_detector(console.Panel.Down.ClickDetector, 1)
- elseif text == 'toggle' then
- click_detector(console.Panel.Close.ClickDetector, 1)
- end
- return "Success"
- end
- }]]></ProtectedString>
- <BinaryString name="Tags"></BinaryString>
- </Properties>
- </Item>
- <Item class="ModuleScript" referent="RBX5CAFD43B6C4E442594425E2D9A02C968">
- <Properties>
- <Content name="LinkedSource"><null></null></Content>
- <string name="Name">SetWeaponSpeed</string>
- <string name="ScriptGuid">{DFAEA287-2663-4BBE-986D-DEA8E021FAA7}</string>
- <ProtectedString name="Source"><![CDATA[return {
- Name = "setweaponspeed";
- Aliases = {};
- Description = "Sets weapon speed";
- Group = "DefaultUtil";
- Args = {
- {
- Type = "number";
- Name = "Speed";
- Description = "New weapon speed"
- },
- };
- Run = function(_, number)
- local weapon = game:GetService('Players').LocalPlayer.Character:FindFirstChildWhichIsA('Tool')
- if weapon then
- weapon.Settings.Speed.Value = number
- end
- end
- }]]></ProtectedString>
- <BinaryString name="Tags"></BinaryString>
- </Properties>
- </Item>
- </Item>
- </Item>
- <Item class="Folder" referent="RBX931A1E5FD65F4602B4D075C659ED04A5">
- <Properties>
- <string name="Name">Types</string>
- <BinaryString name="Tags"></BinaryString>
- </Properties>
- <Item class="ModuleScript" referent="RBXCEE88289152742518A219084636CF9E1">
- <Properties>
- <Content name="LinkedSource"><null></null></Content>
- <string name="Name">BindableResource</string>
- <string name="ScriptGuid">{003CDED7-3D2B-4AE8-8FFE-E754EE143765}</string>
- <ProtectedString name="Source"><![CDATA[-- A fully extensible and type-safe admin-commands console
- -- @documentation https://github.com/evaera/Cmdr/blob/master/README.md
- -- @source https://github.com/evaera/Cmdr
- -- @rostrap Cmdr
- -- @author evaera
- return function (registry)
- registry:RegisterType("bindableResource", registry.Cmdr.Util.MakeEnumType("BindableResource", {"Chat"}))
- end
- ]]></ProtectedString>
- <BinaryString name="Tags"></BinaryString>
- </Properties>
- </Item>
- <Item class="ModuleScript" referent="RBX638C439C98D54A30B32D74C6A8956490">
- <Properties>
- <Content name="LinkedSource"><null></null></Content>
- <string name="Name">BrickColor</string>
- <string name="ScriptGuid">{3C24E196-5B6C-4F7F-8015-C41B2B9B37C5}</string>
- <ProtectedString name="Source"><![CDATA[-- A fully extensible and type-safe admin-commands console
- -- @documentation https://github.com/evaera/Cmdr/blob/master/README.md
- -- @source https://github.com/evaera/Cmdr
- -- @rostrap Cmdr
- -- @author evaera
- local Util = require(script.Parent.Parent.Shared.Util)
- local brickColorNames = {
- "White", "Grey", "Light yellow", "Brick yellow", "Light green (Mint)", "Light reddish violet", "Pastel Blue",
- "Light orange brown", "Nougat", "Bright red", "Med. reddish violet", "Bright blue", "Bright yellow", "Earth orange",
- "Black", "Dark grey", "Dark green", "Medium green", "Lig. Yellowich orange", "Bright green", "Dark orange",
- "Light bluish violet", "Transparent", "Tr. Red", "Tr. Lg blue", "Tr. Blue", "Tr. Yellow", "Light blue",
- "Tr. Flu. Reddish orange", "Tr. Green", "Tr. Flu. Green", "Phosph. White", "Light red", "Medium red", "Medium blue",
- "Light grey", "Bright violet", "Br. yellowish orange", "Bright orange", "Bright bluish green", "Earth yellow",
- "Bright bluish violet", "Tr. Brown", "Medium bluish violet", "Tr. Medi. reddish violet", "Med. yellowish green",
- "Med. bluish green", "Light bluish green", "Br. yellowish green", "Lig. yellowish green", "Med. yellowish orange",
- "Br. reddish orange", "Bright reddish violet", "Light orange", "Tr. Bright bluish violet", "Gold", "Dark nougat",
- "Silver", "Neon orange", "Neon green", "Sand blue", "Sand violet", "Medium orange", "Sand yellow", "Earth blue",
- "Earth green", "Tr. Flu. Blue", "Sand blue metallic", "Sand violet metallic", "Sand yellow metallic",
- "Dark grey metallic", "Black metallic", "Light grey metallic", "Sand green", "Sand red", "Dark red",
- "Tr. Flu. Yellow", "Tr. Flu. Red", "Gun metallic", "Red flip/flop", "Yellow flip/flop", "Silver flip/flop", "Curry",
- "Fire Yellow", "Flame yellowish orange", "Reddish brown", "Flame reddish orange", "Medium stone grey", "Royal blue",
- "Dark Royal blue", "Bright reddish lilac", "Dark stone grey", "Lemon metalic", "Light stone grey", "Dark Curry",
- "Faded green", "Turquoise", "Light Royal blue", "Medium Royal blue", "Rust", "Brown", "Reddish lilac", "Lilac",
- "Light lilac", "Bright purple", "Light purple", "Light pink", "Light brick yellow", "Warm yellowish orange",
- "Cool yellow", "Dove blue", "Medium lilac", "Slime green", "Smoky grey", "Dark blue", "Parsley green", "Steel blue",
- "Storm blue", "Lapis", "Dark indigo", "Sea green", "Shamrock", "Fossil", "Mulberry", "Forest green", "Cadet blue",
- "Electric blue", "Eggplant", "Moss", "Artichoke", "Sage green", "Ghost grey", "Lilac", "Plum", "Olivine",
- "Laurel green", "Quill grey", "Crimson", "Mint", "Baby blue", "Carnation pink", "Persimmon", "Maroon", "Gold",
- "Daisy orange", "Pearl", "Fog", "Salmon", "Terra Cotta", "Cocoa", "Wheat", "Buttermilk", "Mauve", "Sunrise",
- "Tawny", "Rust", "Cashmere", "Khaki", "Lily white", "Seashell", "Burgundy", "Cork", "Burlap", "Beige", "Oyster",
- "Pine Cone", "Fawn brown", "Hurricane grey", "Cloudy grey", "Linen", "Copper", "Dirt brown", "Bronze", "Flint",
- "Dark taupe", "Burnt Sienna", "Institutional white", "Mid gray", "Really black", "Really red", "Deep orange",
- "Alder", "Dusty Rose", "Olive", "New Yeller", "Really blue", "Navy blue", "Deep blue", "Cyan", "CGA brown",
- "Magenta", "Pink", "Deep orange", "Teal", "Toothpaste", "Lime green", "Camo", "Grime", "Lavender",
- "Pastel light blue", "Pastel orange", "Pastel violet", "Pastel blue-green", "Pastel green", "Pastel yellow",
- "Pastel brown", "Royal purple", "Hot pink"
- }
- local brickColorFinder = Util.MakeFuzzyFinder(brickColorNames)
- local brickColorType = {
- Transform = function(text)
- local brickColors = {}
- for i, name in pairs(brickColorFinder(text)) do
- brickColors[i] = BrickColor.new(name)
- end
- return brickColors
- end;
- Validate = function(brickColors)
- return #brickColors > 0, "No valid brick colors with that name could be found."
- end;
- Autocomplete = function(brickColors)
- return Util.GetNames(brickColors)
- end;
- Parse = function(brickColors)
- return brickColors[1]
- end;
- }
- local brickColor3Type = {
- Transform = brickColorType.Transform;
- Validate = brickColorType.Validate;
- Autocomplete = brickColorType.Autocomplete;
- Parse = function(brickColors)
- return brickColors[1].Color
- end;
- }
- return function(registry)
- registry:RegisterType("brickColor", brickColorType)
- registry:RegisterType("brickColors", Util.MakeListableType(brickColorType))
- registry:RegisterType("brickColor3", brickColor3Type)
- registry:RegisterType("brickColor3s", Util.MakeListableType(brickColor3Type))
- end]]></ProtectedString>
- <BinaryString name="Tags"></BinaryString>
- </Properties>
- </Item>
- <Item class="ModuleScript" referent="RBXEC1ED03605AE47CD8EE8B48015D5FA3D">
- <Properties>
- <Content name="LinkedSource"><null></null></Content>
- <string name="Name">Color3</string>
- <string name="ScriptGuid">{B560A0F5-2B93-4FD6-8EF7-3FE8336874EB}</string>
- <ProtectedString name="Source"><![CDATA[-- A fully extensible and type-safe admin-commands console
- -- @documentation https://github.com/evaera/Cmdr/blob/master/README.md
- -- @source https://github.com/evaera/Cmdr
- -- @rostrap Cmdr
- -- @author evaera
- local Util = require(script.Parent.Parent.Shared.Util)
- local color3Type = Util.MakeSequenceType({
- ValidateEach = function(value, i)
- if value == nil then
- return false, ("Invalid or missing number at position %d in Color3 type."):format(i)
- elseif value < 0 or value > 255 then
- return false, ("Number out of acceptable range 0-255 at position %d in Color3 type."):format(i)
- elseif value % 1 ~= 0 then
- return false, ("Number is not an integer at position %d in Color3 type."):format(i)
- end
- return true
- end;
- TransformEach = tonumber;
- Constructor = Color3.fromRGB;
- Length = 3;
- })
- local function parseHexDigit(x)
- if #x == 1 then
- x = x .. x
- end
- return tonumber(x, 16)
- end
- local hexColor3Type = {
- Transform = function(text)
- local r, g, b = text:match("^#?(%x%x?)(%x%x?)(%x%x?)$")
- return Util.Each(parseHexDigit, r, g, b)
- end;
- Validate = function(r, g, b)
- return r ~= nil and g ~= nil and b ~= nil, "Invalid hex color"
- end;
- Parse = function(...)
- return Color3.fromRGB(...)
- end;
- }
- return function (cmdr)
- cmdr:RegisterType("color3", color3Type)
- cmdr:RegisterType("color3s", Util.MakeListableType(color3Type))
- cmdr:RegisterType("hexColor3", hexColor3Type)
- cmdr:RegisterType("hexColor3s", Util.MakeListableType(hexColor3Type))
- end]]></ProtectedString>
- <BinaryString name="Tags"></BinaryString>
- </Properties>
- </Item>
- <Item class="ModuleScript" referent="RBXED5B094136AB418088F79E4D37ADAF02">
- <Properties>
- <Content name="LinkedSource"><null></null></Content>
- <string name="Name">Command</string>
- <string name="ScriptGuid">{A38076EA-89FD-4B05-A661-384DC2CD4618}</string>
- <ProtectedString name="Source"><![CDATA[-- A fully extensible and type-safe admin-commands console
- -- @documentation https://github.com/evaera/Cmdr/blob/master/README.md
- -- @source https://github.com/evaera/Cmdr
- -- @rostrap Cmdr
- -- @author evaera
- local Util = require(script.Parent.Parent.Shared.Util)
- return function (cmdr)
- local commandType = {
- Transform = function (text)
- local findCommand = Util.MakeFuzzyFinder(cmdr:GetCommandsAsStrings())
- return findCommand(text)
- end;
- Validate = function (commands)
- return #commands > 0, "No command with that name could be found."
- end;
- Autocomplete = function (commands)
- return commands
- end;
- Parse = function (commands)
- return commands[1]
- end;
- }
- cmdr:RegisterType("command", commandType)
- cmdr:RegisterType("commands", Util.MakeListableType(commandType))
- end]]></ProtectedString>
- <BinaryString name="Tags"></BinaryString>
- </Properties>
- </Item>
- <Item class="ModuleScript" referent="RBXD77FD4D79FE34C98B3486A695AD6880E">
- <Properties>
- <Content name="LinkedSource"><null></null></Content>
- <string name="Name">ConditionFunction</string>
- <string name="ScriptGuid">{CBE4A528-0A6A-4006-B652-3A2CBD6CA7FE}</string>
- <ProtectedString name="Source"><![CDATA[-- A fully extensible and type-safe admin-commands console
- -- @documentation https://github.com/evaera/Cmdr/blob/master/README.md
- -- @source https://github.com/evaera/Cmdr
- -- @rostrap Cmdr
- -- @author evaera
- return function (registry)
- registry:RegisterType("conditionFunction", registry.Cmdr.Util.MakeEnumType("ConditionFunction", {"startsWith"}))
- end
- ]]></ProtectedString>
- <BinaryString name="Tags"></BinaryString>
- </Properties>
- </Item>
- <Item class="ModuleScript" referent="RBXB384619ED8DC4797ADD5027BCB9C2BB9">
- <Properties>
- <Content name="LinkedSource"><null></null></Content>
- <string name="Name">Duration</string>
- <string name="ScriptGuid">{4FEC71D5-1572-4BCD-AD82-065C6B1CF2B7}</string>
- <ProtectedString name="Source"><![CDATA[-- A fully extensible and type-safe admin-commands console
- -- @documentation https://github.com/evaera/Cmdr/blob/master/README.md
- -- @source https://github.com/evaera/Cmdr
- -- @rostrap Cmdr
- -- @author evaera
- local Util = require(script.Parent.Parent.Shared.Util)
- local unitTable = {
- Years = 31556926,
- Months = 2629744,
- Weeks = 604800,
- Days = 86400,
- Hours = 3600,
- Minutes = 60,
- Seconds = 1
- }
- local searchKeyTable = {}
- for key, _ in pairs(unitTable) do
- table.insert(searchKeyTable, key)
- end
- local unitFinder = Util.MakeFuzzyFinder(searchKeyTable)
- local function stringToSecondDuration(stringDuration)
- -- The duration cannot be null or an empty string.
- if stringDuration == nil or stringDuration == "" then
- return nil
- end
- -- Allow 0 by itself (without a unit) to indicate 0 seconds
- local durationNum = tonumber(stringDuration)
- if durationNum and durationNum == 0 then
- return 0, 0, true
- end
- -- The duration must end with a unit,
- -- if it doesn't then return true as the fourth value to indicate the need to offer autocomplete for units.
- local endOnlyString = stringDuration:gsub("%d+%a+", "")
- local endNumber = endOnlyString:match("%d+")
- if endNumber then
- return nil, tonumber(endNumber), true
- end
- local seconds = nil
- local rawNum, rawUnit
- for rawComponent in stringDuration:gmatch("%d+%a+") do
- rawNum, rawUnit = rawComponent:match("(%d+)(%a+)")
- local unitNames = unitFinder(rawUnit)
- -- There were no matching units, it's invalid. Return the parsed number to be used for autocomplete
- if #unitNames == 0 then
- return nil, tonumber(rawNum)
- end
- if seconds == nil then seconds = 0 end
- -- While it was already defaulting to use minutes when using just "m", this does it without worrying
- -- about any consistency between list ordering.
- seconds = seconds + (rawUnit:lower() == "m" and 60 or unitTable[unitNames[1]]) * tonumber(rawNum)
- end
- -- If no durations were provided, return nil.
- if seconds == nil then
- return nil
- else
- return seconds, tonumber(rawNum)
- end
- end
- local function mapUnits(units, rawText, lastNumber, subStart)
- subStart = subStart or 1
- local returnTable = {}
- for i, unit in pairs(units) do
- if lastNumber == 1 then
- returnTable[i] = rawText .. unit:sub(subStart, #unit - 1)
- else
- returnTable[i] = rawText .. unit:sub(subStart)
- end
- end
- return returnTable
- end
- local durationType = {
- Transform = function(text)
- return text, stringToSecondDuration(text)
- end;
- Validate = function(_, duration)
- return duration ~= nil and duration >= 0
- end;
- Autocomplete = function(rawText, duration, lastNumber, isUnitMissing, matchedUnits)
- local returnTable = {}
- if isUnitMissing or matchedUnits then
- local unitsTable = isUnitMissing == true and unitFinder("") or matchedUnits
- if isUnitMissing == true then
- -- Concat the entire unit name to existing text.
- returnTable = mapUnits(unitsTable, rawText, lastNumber)
- else
- -- Concat the rest of the unit based on what already exists of the unit name.
- local existingUnitLength = rawText:match("^.*(%a+)$"):len()
- returnTable = mapUnits(unitsTable, rawText, existingUnitLength + 1)
- end
- elseif duration ~= nil then
- local endingUnit = rawText:match("^.*%d+(%a+)$")
- -- Assume there is a singular match at this point
- local fuzzyUnits = unitFinder(endingUnit)
- -- List all possible fuzzy matches. This is for the Minutes/Months ambiguity case.
- returnTable = mapUnits(fuzzyUnits, rawText, lastNumber, #endingUnit + 1)
- -- Sort alphabetically in the Minutes/Months case, so Minutes are displayed on top.
- table.sort(returnTable)
- end
- return returnTable
- end;
- Parse = function(_, duration)
- return duration
- end;
- }
- return function(registry)
- registry:RegisterType("duration", durationType)
- registry:RegisterType("durations", Util.MakeListableType(durationType))
- end]]></ProtectedString>
- <BinaryString name="Tags"></BinaryString>
- </Properties>
- </Item>
- <Item class="ModuleScript" referent="RBX599092F54B544F07B2AF0198C9DED4A4">
- <Properties>
- <Content name="LinkedSource"><null></null></Content>
- <string name="Name">Player</string>
- <string name="ScriptGuid">{43961925-6FC1-4F63-A525-6091F7E2A05D}</string>
- <ProtectedString name="Source"><![CDATA[-- A fully extensible and type-safe admin-commands console
- -- @documentation https://github.com/evaera/Cmdr/blob/master/README.md
- -- @source https://github.com/evaera/Cmdr
- -- @rostrap Cmdr
- -- @author evaera
- local Util = require(script.Parent.Parent.Shared.Util)
- local Players = game:GetService("Players")
- local function ShorthandSingle (text, executor)
- if text == "." or text == "me" then
- return {executor}
- end
- end
- local function ShorthandMultiple (text, executor)
- if text == "*" or text == "all" then
- return Players:GetPlayers()
- elseif text == "others" then
- local Others = Players:GetPlayers()
- for i = 1, #Others do
- if Others[i] == executor then
- table.remove(Others, i)
- break
- end
- end
- return Others
- end
- end
- local function CheckShorthands (text, executor, ...)
- for _, func in pairs({...}) do
- local values = func(text, executor)
- if values then return values end
- end
- end
- local playerType = {
- Transform = function (text, executor)
- local shorthand = CheckShorthands(text, executor, ShorthandSingle)
- if shorthand then
- return shorthand
- end
- local findPlayer = Util.MakeFuzzyFinder(Players:GetPlayers())
- return findPlayer(text)
- end;
- Validate = function (players)
- return #players > 0, "No player with that name could be found."
- end;
- Autocomplete = function (players)
- return Util.GetNames(players)
- end;
- Parse = function (players)
- return players[1]
- end;
- }
- local playersType = {
- Listable = true;
- Transform = function (text, executor)
- local shorthand = CheckShorthands(text, executor, ShorthandSingle, ShorthandMultiple)
- if shorthand then
- return shorthand, true
- end
- local findPlayers = Util.MakeFuzzyFinder(Players:GetPlayers())
- return findPlayers(text)
- end;
- Validate = function (players)
- return #players > 0, "No players were found matching that query."
- end;
- Autocomplete = function (players)
- return Util.GetNames(players)
- end;
- Parse = function (players, returnAll)
- return returnAll and players or { players[1] }
- end;
- }
- return function (cmdr)
- cmdr:RegisterType("player", playerType)
- cmdr:RegisterType("players", playersType)
- end
- ]]></ProtectedString>
- <BinaryString name="Tags"></BinaryString>
- </Properties>
- </Item>
- <Item class="ModuleScript" referent="RBXBDDBF036BDA048DF819154E24AFF6EEE">
- <Properties>
- <Content name="LinkedSource"><null></null></Content>
- <string name="Name">PlayerId</string>
- <string name="ScriptGuid">{733AD56E-6BD0-41EE-9E46-62442F9E8755}</string>
- <ProtectedString name="Source"><![CDATA[-- A fully extensible and type-safe admin-commands console
- -- @documentation https://github.com/evaera/Cmdr/blob/master/README.md
- -- @source https://github.com/evaera/Cmdr
- -- @rostrap Cmdr
- -- @author evaera
- local Util = require(script.Parent.Parent.Shared.Util)
- local Players = game:GetService("Players")
- local nameCache = {}
- local function getUserId(name)
- if nameCache[name] then
- return nameCache[name]
- elseif Players:FindFirstChild(name) then
- nameCache[name] = Players[name].UserId
- return Players[name].UserId
- else
- local ok, userid = pcall(Players.GetUserIdFromNameAsync, Players, name)
- if not ok then
- return nil
- end
- nameCache[name] = userid
- return userid
- end
- end
- local playerIdType = {
- DisplayName = "Full Player Name";
- Transform = function (text)
- local findPlayer = Util.MakeFuzzyFinder(Players:GetPlayers())
- return text, findPlayer(text)
- end;
- ValidateOnce = function (text)
- return getUserId(text) ~= nil, "No player with that name could be found."
- end;
- Autocomplete = function (_, players)
- return Util.GetNames(players)
- end;
- Parse = function (text)
- return getUserId(text)
- end;
- }
- return function (cmdr)
- cmdr:RegisterType("playerId", playerIdType)
- cmdr:RegisterType("playerIds", Util.MakeListableType(playerIdType))
- end
- ]]></ProtectedString>
- <BinaryString name="Tags"></BinaryString>
- </Properties>
- </Item>
- <Item class="ModuleScript" referent="RBX13B272783ADA4E12A46B93254E029747">
- <Properties>
- <Content name="LinkedSource"><null></null></Content>
- <string name="Name">Primitives</string>
- <string name="ScriptGuid">{CD355608-E8F5-40F8-A24F-642072874529}</string>
- <ProtectedString name="Source"><![CDATA[-- A fully extensible and type-safe admin-commands console
- -- @documentation https://github.com/evaera/Cmdr/blob/master/README.md
- -- @source https://github.com/evaera/Cmdr
- -- @rostrap Cmdr
- -- @author evaera
- local Util = require(script.Parent.Parent.Shared.Util)
- local stringType = {
- Validate = function (value)
- return value ~= nil
- end;
- Parse = function (value)
- return tostring(value)
- end;
- }
- local numberType = {
- Transform = function (text)
- return tonumber(text)
- end;
- Validate = function (value)
- return value ~= nil
- end;
- Parse = function (value)
- return value
- end;
- }
- local intType = {
- Transform = function (text)
- return tonumber(text)
- end;
- Validate = function (value)
- return value ~= nil and value == math.floor(value), "Only whole numbers are valid."
- end;
- Parse = function (value)
- return value
- end
- }
- local boolType do
- local truthy = Util.MakeDictionary({"true", "t", "yes", "y", "on", "enable", "enabled", "1", "+"});
- local falsy = Util.MakeDictionary({"false"; "f"; "no"; "n"; "off"; "disable"; "disabled"; "0"; "-"});
- boolType = {
- Transform = function (text)
- return text:lower()
- end;
- Validate = function (value)
- return truthy[value] ~= nil or falsy[value] ~= nil, "Please use true/yes/on or false/no/off."
- end;
- Parse = function (value)
- if truthy[value] then
- return true
- elseif falsy[value] then
- return false
- else
- error("Unknown boolean value.")
- end
- end;
- }
- end
- return function (cmdr)
- cmdr:RegisterType("string", stringType)
- cmdr:RegisterType("number", numberType)
- cmdr:RegisterType("integer", intType)
- cmdr:RegisterType("boolean", boolType)
- cmdr:RegisterType("strings", Util.MakeListableType(stringType))
- cmdr:RegisterType("numbers", Util.MakeListableType(numberType))
- cmdr:RegisterType("integers", Util.MakeListableType(intType))
- cmdr:RegisterType("booleans", Util.MakeListableType(boolType))
- end]]></ProtectedString>
- <BinaryString name="Tags"></BinaryString>
- </Properties>
- </Item>
- <Item class="ModuleScript" referent="RBXF471B0EAB54240449D47116CA22DF7D8">
- <Properties>
- <Content name="LinkedSource"><null></null></Content>
- <string name="Name">Team</string>
- <string name="ScriptGuid">{98D7A448-55AD-449B-A978-20528E3CF220}</string>
- <ProtectedString name="Source"><![CDATA[-- A fully extensible and type-safe admin-commands console
- -- @documentation https://github.com/evaera/Cmdr/blob/master/README.md
- -- @source https://github.com/evaera/Cmdr
- -- @rostrap Cmdr
- -- @author evaera
- local Teams = game:GetService("Teams")
- local Util = require(script.Parent.Parent.Shared.Util)
- local teamType = {
- Transform = function (text)
- local findTeam = Util.MakeFuzzyFinder(Teams:GetTeams())
- return findTeam(text)
- end;
- Validate = function (teams)
- return #teams > 0, "No team with that name could be found."
- end;
- Autocomplete = function (teams)
- return Util.GetNames(teams)
- end;
- Parse = function (teams)
- return teams[1];
- end;
- }
- local teamPlayersType = {
- Listable = true;
- Transform = teamType.Transform;
- Validate = teamType.Validate;
- Autocomplete = teamType.Autocomplete;
- Parse = function (teams)
- return teams[1]:GetPlayers()
- end;
- }
- local teamColorType = {
- Transform = teamType.Transform;
- Validate = teamType.Validate;
- Autocomplete = teamType.Autocomplete;
- Parse = function (teams)
- return teams[1].TeamColor
- end;
- }
- return function (cmdr)
- cmdr:RegisterType("team", teamType)
- cmdr:RegisterType("teams", Util.MakeListableType(teamType))
- cmdr:RegisterType("teamPlayers", teamPlayersType)
- cmdr:RegisterType("teamColor", teamColorType)
- cmdr:RegisterType("teamColors", Util.MakeListableType(teamColorType))
- end]]></ProtectedString>
- <BinaryString name="Tags"></BinaryString>
- </Properties>
- </Item>
- <Item class="ModuleScript" referent="RBX5B6895F0800A43ED98E1C88E3E931E76">
- <Properties>
- <Content name="LinkedSource"><null></null></Content>
- <string name="Name">UserInput</string>
- <string name="ScriptGuid">{1E4626B3-7A93-4BC8-A167-72EA55831D09}</string>
- <ProtectedString name="Source"><![CDATA[-- A fully extensible and type-safe admin-commands console
- -- @documentation https://github.com/evaera/Cmdr/blob/master/README.md
- -- @source https://github.com/evaera/Cmdr
- -- @rostrap Cmdr
- -- @author evaera
- local Util = require(script.Parent.Parent.Shared.Util)
- local combinedInputEnums = Enum.UserInputType:GetEnumItems()
- for _, e in pairs(Enum.KeyCode:GetEnumItems()) do
- combinedInputEnums[#combinedInputEnums + 1] = e
- end
- local userInputType = {
- Transform = function (text)
- local findEnum = Util.MakeFuzzyFinder(combinedInputEnums)
- return findEnum(text)
- end;
- Validate = function (enums)
- return #enums > 0
- end;
- Autocomplete = function (enums)
- return Util.GetNames(enums)
- end;
- Parse = function (enums)
- return enums[1];
- end;
- }
- return function (cmdr)
- cmdr:RegisterType("userInput", userInputType)
- cmdr:RegisterType("userInputs", Util.MakeListableType(userInputType))
- end]]></ProtectedString>
- <BinaryString name="Tags"></BinaryString>
- </Properties>
- </Item>
- <Item class="ModuleScript" referent="RBX2EA11A3AD0BD440096D0A01F152534F2">
- <Properties>
- <Content name="LinkedSource"><null></null></Content>
- <string name="Name">Vector</string>
- <string name="ScriptGuid">{BF85AE9A-0FBE-4EAA-98CF-F55AC02BA9FA}</string>
- <ProtectedString name="Source"><![CDATA[-- A fully extensible and type-safe admin-commands console
- -- @documentation https://github.com/evaera/Cmdr/blob/master/README.md
- -- @source https://github.com/evaera/Cmdr
- -- @rostrap Cmdr
- -- @author evaera
- local Util = require(script.Parent.Parent.Shared.Util)
- local function validateVector(value, i)
- if value == nil then
- return false, ("Invalid or missing number at position %d in Vector type."):format(i)
- end
- return true
- end
- local vector3Type = Util.MakeSequenceType({
- ValidateEach = validateVector;
- TransformEach = tonumber;
- Constructor = Vector3.new;
- Length = 3;
- })
- local vector2Type = Util.MakeSequenceType({
- ValidateEach = validateVector;
- TransformEach = tonumber;
- Constructor = Vector2.new;
- Length = 2;
- })
- return function (cmdr)
- cmdr:RegisterType("vector3", vector3Type)
- cmdr:RegisterType("vector3s", Util.MakeListableType(vector3Type))
- cmdr:RegisterType("vector2", vector2Type)
- cmdr:RegisterType("vector2s", Util.MakeListableType(vector2Type))
- end]]></ProtectedString>
- <BinaryString name="Tags"></BinaryString>
- </Properties>
- </Item>
- </Item>
- <Item class="Folder" referent="RBX66A52AFF88594D1AB194C4A00314A929">
- <Properties>
- <string name="Name">Shared</string>
- <BinaryString name="Tags"></BinaryString>
- </Properties>
- <Item class="ModuleScript" referent="RBX716C459E6F0242A195AA74E318076C2B">
- <Properties>
- <Content name="LinkedSource"><null></null></Content>
- <string name="Name">Argument</string>
- <string name="ScriptGuid">{27B50999-F950-4357-B8AA-3E602A18E479}</string>
- <ProtectedString name="Source"><![CDATA[-- A fully extensible and type-safe admin-commands console
- -- @documentation https://github.com/evaera/Cmdr/blob/master/README.md
- -- @source https://github.com/evaera/Cmdr
- -- @rostrap Cmdr
- -- @author evaera
- local TYPE_DEFAULTS = {
- -- Make all `players` types also be able to match by team
- players = "players % teamPlayers";
- playerId = "playerId # integer";
- playerIds = "playerIds # integers";
- brickColor = "brickColor % teamColor";
- brickColors = "brickColors % teamColors";
- color3 = "color3 # hexColor3 ! brickColor3";
- color3s = "color3s # hexColor3s ! brickColor3s";
- }
- local Util = require(script.Parent.Util)
- local Argument = {}
- Argument.__index = Argument
- --- Returns a new ArgumentContext, an object that handles parsing and validating arguments
- function Argument.new (command, argumentObject, value)
- local self = {
- Command = command; -- The command that owns this argument
- Type = nil; -- The type definition
- Name = argumentObject.Name; -- The name for this specific argument
- Object = argumentObject; -- The raw ArgumentObject (definition)
- Required = argumentObject.Default == nil and argumentObject.Optional ~= true; -- If the argument is required or not.
- Executor = command.Executor; -- The player who is running the command
- RawValue = nil; -- The raw, unparsed value
- RawSegments = {}; -- The raw, unparsed segments (if the raw value was comma-sep)
- TransformedValues = {}; -- The transformed value (generated later)
- Prefix = nil; -- The prefix for this command (%Team)
- }
- local parsedType, parsedRawValue, prefix = Util.ParsePrefixedUnionType(
- TYPE_DEFAULTS[argumentObject.Type] or argumentObject.Type, value
- )
- self.Type = command.Dispatcher.Registry:GetType(parsedType)
- self.RawValue = parsedRawValue
- self.Prefix = prefix
- if self.Type == nil then
- error(string.format("%s has an unregistered type %q", self.Name or "<none>", parsedType or "<none>"))
- end
- setmetatable(self, Argument)
- self:Transform()
- return self
- end
- --- Calls the transform function on this argument.
- -- The return value(s) from this function are passed to all of the other argument methods.
- -- Called automatically at instantiation
- function Argument:Transform()
- if #self.TransformedValues ~= 0 then
- return
- end
- if self.Type.Listable and #self.RawValue > 0 then
- local rawSegments = Util.SplitStringSimple(self.RawValue, ",")
- if self.RawValue:sub(#self.RawValue, #self.RawValue) == "," then
- rawSegments[#rawSegments + 1] = "" -- makes auto complete tick over right after pressing ,
- end
- for i, rawSegment in ipairs(rawSegments) do
- self.RawSegments[i] = rawSegment
- self.TransformedValues[i] = { self:TransformSegment(rawSegment) }
- end
- else
- self.RawSegments[1] = self.RawValue
- self.TransformedValues[1] = { self:TransformSegment(self.RawValue) }
- end
- end
- function Argument:TransformSegment(rawSegment)
- if self.Type.Transform then
- return self.Type.Transform(rawSegment, self.Executor)
- else
- return rawSegment
- end
- end
- --- Returns whatever the Transform method gave us.
- function Argument:GetTransformedValue(segment)
- return unpack(self.TransformedValues[segment])
- end
- --- Validates that the argument will work without any type errors.
- function Argument:Validate(isFinal)
- if self.RawValue == nil or #self.RawValue == 0 and self.Required == false then
- return true
- end
- if self.Type.Validate or self.Type.ValidateOnce then
- for i = 1, #self.TransformedValues do
- if self.Type.Validate then
- local valid, errorText = self.Type.Validate(self:GetTransformedValue(i))
- if not valid then
- return valid, errorText or "Invalid value"
- end
- end
- if isFinal and self.Type.ValidateOnce then
- local validOnce, errorTextOnce = self.Type.ValidateOnce(self:GetTransformedValue(i))
- if not validOnce then
- return validOnce, errorTextOnce
- end
- end
- end
- return true
- else
- return true
- end
- end
- --- Gets a list of all possible values that could match based on the current value.
- function Argument:GetAutocomplete()
- if self.Type.Autocomplete then
- return self.Type.Autocomplete(self:GetTransformedValue(#self.TransformedValues))
- else
- return {}
- end
- end
- function Argument:ParseValue(i)
- if self.Type.Parse then
- return self.Type.Parse(self:GetTransformedValue(i))
- else
- return self:GetTransformedValue(i)
- end
- end
- --- Returns the final value of the argument.
- function Argument:GetValue()
- if #self.RawValue == 0 and not self.Required and self.Object.Default ~= nil then
- return self.Object.Default
- end
- if not self.Type.Listable then
- return self:ParseValue(1)
- end
- local values = {}
- for i = 1, #self.TransformedValues do
- local parsedValue = self:ParseValue(i)
- if type(parsedValue) ~= "table" then
- error(("Listable types must return a table from Parse (%s)"):format(self.Type.Name))
- end
- for _, value in pairs(parsedValue) do
- values[value] = true -- Put them into a dictionary to ensure uniqueness
- end
- end
- local valueArray = {}
- for value in pairs(values) do
- valueArray[#valueArray + 1] = value
- end
- return valueArray
- end
- return Argument]]></ProtectedString>
- <BinaryString name="Tags"></BinaryString>
- </Properties>
- </Item>
- <Item class="ModuleScript" referent="RBX11E9EB676C274EDEA8E7B702715FF9C7">
- <Properties>
- <Content name="LinkedSource"><null></null></Content>
- <string name="Name">Command</string>
- <string name="ScriptGuid">{87DF3A9B-052B-4922-A962-9C737324A21F}</string>
- <ProtectedString name="Source"><![CDATA[-- A fully extensible and type-safe admin-commands console
- -- @documentation https://github.com/evaera/Cmdr/blob/master/README.md
- -- @source https://github.com/evaera/Cmdr
- -- @rostrap Cmdr
- -- @author evaera
- local RunService = game:GetService("RunService")
- local Players = game:GetService("Players")
- local Argument = require(script.Parent.Argument)
- local IsServer = RunService:IsServer()
- local Command = {}
- Command.__index = Command
- --- Returns a new CommandContext, an object which is created for every command validation.
- -- This is also what's passed as the context to the "Run" functions in commands
- function Command.new (options)
- local self = {
- Dispatcher = options.Dispatcher; -- The dispatcher that created this command context
- Cmdr = options.Dispatcher.Cmdr; -- A quick reference to Cmdr for command context
- Name = options.CommandObject.Name; -- The command name (not alias)
- RawText = options.Text; -- The raw text used to trigger this command
- Object = options.CommandObject; -- The command object (definition)
- Group = options.CommandObject.Group; -- The group this command is in
- State = {}; -- A table which will hold any custom command state information
- Aliases = options.CommandObject.Aliases;
- Alias = options.Alias; -- The command name that was used
- Description = options.CommandObject.Description;
- Executor = options.Executor; -- The player who ran the command
- ArgumentDefinitions = options.CommandObject.Args; -- The argument definitions from the command definition
- RawArguments = options.Arguments; -- Array of strings which are the unparsed values for the arguments
- Arguments = {}; -- A table which will hold ArgumentContexts for each argument
- Data = options.Data; -- A special container for any additional data the command needs to collect from the client
- Response = nil; -- Will be set at the very end when the command is run and a string is returned from the Run function.
- }
- setmetatable(self, Command)
- return self
- end
- --- Parses all of the command arguments into ArgumentContexts
- -- Called by the command dispatcher automatically
- -- allowIncompleteArguments: if true, will not throw an error for missing arguments
- function Command:Parse (allowIncompleteArguments)
- local hadOptional = false
- for i, definition in ipairs(self.ArgumentDefinitions) do
- local required = (definition.Default == nil and definition.Optional ~= true)
- if required and hadOptional then
- error(("Command %q: Required arguments cannot occur after optional arguments."):format(self.Name))
- elseif not required then
- hadOptional = true
- end
- if self.RawArguments[i] == nil and required and allowIncompleteArguments ~= true then
- return false, ("Required argument #%d %s is missing."):format(i, definition.Name)
- elseif self.RawArguments[i] or allowIncompleteArguments then
- self.Arguments[i] = Argument.new(self, definition, self.RawArguments[i] or "")
- end
- end
- return true
- end
- --- Validates that all of the arguments are in a valid state.
- -- This must be called before :Run() is called.
- -- Returns boolean (true if ok), errorText
- function Command:Validate (isFinal)
- self._Validated = true
- local errorText = ""
- local success = true
- for i, arg in pairs(self.Arguments) do
- local argSuccess, argErrorText = arg:Validate(isFinal)
- if not argSuccess then
- success = false
- errorText = ("%s; #%d %s: %s"):format(errorText, i, arg.Name, argErrorText or "error")
- end
- end
- return success, errorText:sub(3)
- end
- --- Returns the last argument that has a value.
- -- Useful for getting the autocomplete for the argument the user is working on.
- function Command:GetLastArgument()
- for i = #self.Arguments, 1, -1 do
- if self.Arguments[i].RawValue then
- return self.Arguments[i]
- end
- end
- end
- --- Returns a table containing the parsed values for all of the arguments.
- function Command:GatherArgumentValues ()
- local values = {}
- for i = 1, #self.ArgumentDefinitions do
- local arg = self.Arguments[i]
- if arg then
- values[i] = arg:GetValue()
- else
- values[i] = self.ArgumentDefinitions[i].Default
- end
- end
- return values
- end
- --- Runs the command. Handles dispatching to the server if necessary.
- -- Command:Validate() must be called before this is called or it will throw.
- function Command:Run ()
- if self._Validated == nil then
- error("Must validate a command before running.")
- end
- local beforeRunHook = self.Dispatcher:RunHooks("BeforeRun", self)
- if beforeRunHook then
- return beforeRunHook
- end
- if self.Object.Run then -- We can just Run it here on this machine
- self.Response = self.Object.Run(self, unpack(self:GatherArgumentValues()))
- elseif IsServer then -- Uh oh, we're already on the server and there's no Run function.
- warn(self.Name, "command has no implementation!")
- self.Response = "No implementation."
- else -- We're on the client, so we send this off to the server to let the server see what it can do with it.
- self.Response = self.Dispatcher:Send(self.RawText, self.Object.Data and self.Object.Data(self))
- end
- local afterRunHook = self.Dispatcher:RunHooks("AfterRun", self)
- if afterRunHook then
- return afterRunHook
- else
- return self.Response
- end
- end
- --- Returns an ArgumentContext for the specific index
- function Command:GetArgument (index)
- return self.Arguments[index]
- end
- -- Below are functions that are only meant to be used in command implementations --
- --- Returns the extra data associated with this command.
- -- This needs to be used instead of just context.Data for reliability when not using a remote command.
- function Command:GetData ()
- if self.Data then
- return self.Data
- end
- if self.Object.Data and not IsServer then
- self.Data = self.Object.Data(self)
- end
- return self.Data
- end
- --- Sends an event message to a player
- function Command:SendEvent(player, event, ...)
- assert(typeof(player) == "Instance", "Argument #1 must be a Player")
- assert(player:IsA("Player"), "Argument #1 must be a Player")
- assert(type(event) == "string", "Argument #2 must be a string")
- if IsServer then
- self.Dispatcher.Cmdr.RemoteEvent:FireClient(player, event, ...)
- elseif self.Dispatcher.Cmdr.Events[event] then
- assert(player == Players.LocalPlayer, "Event messages can only be sent to the local player on the client.")
- self.Dispatcher.Cmdr.Events[event](...)
- end
- end
- --- Sends an event message to all players
- function Command:BroadcastEvent(...)
- if not IsServer then
- error("Can't broadcast event messages from the client.", 2)
- end
- self.Dispatcher.Cmdr.RemoteEvent:FireAllClients(...)
- end
- --- Alias of self:SendEvent(self.Executor, "AddLine", text)
- function Command:Reply(...)
- return self:SendEvent(self.Executor, "AddLine", ...)
- end
- --- Alias of Registry:GetStore(...)
- function Command:GetStore(...)
- return self.Dispatcher.Cmdr.Registry:GetStore(...)
- end
- return Command
- ]]></ProtectedString>
- <BinaryString name="Tags"></BinaryString>
- </Properties>
- </Item>
- <Item class="ModuleScript" referent="RBX605C1FBC658B4EA8B3572B11FD05647A">
- <Properties>
- <Content name="LinkedSource"><null></null></Content>
- <string name="Name">Dispatcher</string>
- <string name="ScriptGuid">{7F66E598-3579-48FA-A96F-7321D43FDDEE}</string>
- <ProtectedString name="Source"><![CDATA[-- A fully extensible and type-safe admin-commands console
- -- @documentation https://github.com/evaera/Cmdr/blob/master/README.md
- -- @source https://github.com/evaera/Cmdr
- -- @rostrap Cmdr
- -- @author evaera
- local RunService = game:GetService("RunService")
- local TeleportService = game:GetService("TeleportService")
- local Players = game:GetService("Players")
- local Util = require(script.Parent.Util)
- local Command = require(script.Parent.Command)
- local HISTORY_SETTING_NAME = "CmdrCommandHistory"
- --- The dispatcher handles creating and running commands during the game.
- local Dispatcher = {
- Cmdr = nil;
- Registry = nil;
- }
- --- Takes in raw command information and generates a command out of it.
- -- text and executor are required arguments.
- -- allowIncompleteData, when true, will ignore errors about arguments missing so we can parse live as the user types.
- -- data is for special networked Data about the command gathered on the client. Purely Optional.
- -- returns the command if successful, or (false, errorText) if not
- function Dispatcher:Evaluate (text, executor, allowIncompleteArguments, data)
- if RunService:IsClient() == true and executor ~= Players.LocalPlayer then
- error("Can't evaluate a command that isn't sent by the local player.")
- end
- local arguments = Util.SplitString(text)
- local commandName = table.remove(arguments, 1)
- local commandObject = self.Registry:GetCommand(commandName)
- if commandObject then
- -- No need to continue splitting when there are no more arguments. We'll just mash any additional arguments into the last one.
- arguments = Util.MashExcessArguments(arguments, #commandObject.Args)
- -- Create the CommandContext and parse it.
- local command = Command.new({
- Dispatcher = self,
- Text = text,
- CommandObject = commandObject,
- Alias = commandName,
- Executor = executor,
- Arguments = arguments,
- Data = data
- })
- local success, errorText = command:Parse(allowIncompleteArguments)
- if success then
- return command
- else
- return false, errorText
- end
- else
- return false, "Invalid command. Use the help command to see all available commands."
- end
- end
- --- A helper that evaluates and runs the command in one go.
- -- Either returns any validation errors as a string, or the output of the command as a string. Definitely a string, though.
- function Dispatcher:EvaluateAndRun (text, executor, options)
- executor = executor or Players.LocalPlayer
- options = options or {}
- if RunService:IsClient() and options.IsHuman then
- self:PushHistory(text)
- end
- local command, errorText = self:Evaluate(text, executor, nil, options.Data)
- if not command then
- return errorText
- end
- local ok, out = pcall(function()
- local valid, errorText = command:Validate(true) -- luacheck: ignore
- if not valid then
- return errorText
- end
- return command:Run() or "Command executed."
- end)
- if not ok then
- warn(("Error occurred while evaluating command string %q\n%s"):format(text, out))
- end
- return ok and out or "An error occurred while running this command."
- end
- --- Send text as the local user to remote server to be evaluated there.
- function Dispatcher:Send (text, data)
- if RunService:IsClient() == false then
- error("Dispatcher:Send can only be called from the client.")
- end
- return self.Cmdr.RemoteFunction:InvokeServer(text, {
- Data = data
- })
- end
- --- Invoke a command programmatically as the local user e.g. from a settings menu
- -- Command should be the first argument, all arguments afterwards should be the arguments to the command.
- function Dispatcher:Run (...)
- if not Players.LocalPlayer then
- error("Dispatcher:Run can only be called from the client.")
- end
- local args = {...}
- local text = args[1]
- for i = 2, #args do
- text = text .. " " .. tostring(args[i])
- end
- local command, errorText = self:Evaluate(text, Players.LocalPlayer)
- if not command then
- error(errorText) -- We do a full-on error here since this is code-invoked and they should know better.
- end
- local success, errorText = command:Validate(true) -- luacheck: ignore
- if not success then
- error(errorText)
- end
- return command:Run()
- end
- --- Runs hooks matching name and returns nil for ok or a string for cancellation
- function Dispatcher:RunHooks(hookName, ...)
- if not self.Registry.Hooks[hookName] then
- error(("Invalid hook name: %q"):format(hookName), 2)
- end
- for _, hook in ipairs(self.Registry.Hooks[hookName]) do
- local value = hook.callback(...)
- if value ~= nil then
- return tostring(value)
- end
- end
- end
- function Dispatcher:PushHistory(text)
- assert(RunService:IsClient(), "PushHistory may only be used from the client.")
- local history = self:GetHistory()
- -- Remove duplicates
- if Util.TrimString(text) == "" or text == history[#history] then
- return
- end
- history[#history + 1] = text
- TeleportService:SetTeleportSetting(HISTORY_SETTING_NAME, history)
- end
- function Dispatcher:GetHistory()
- assert(RunService:IsClient(), "GetHistory may only be used from the client.")
- return TeleportService:GetTeleportSetting(HISTORY_SETTING_NAME) or {}
- end
- return function (cmdr)
- Dispatcher.Cmdr = cmdr
- Dispatcher.Registry = cmdr.Registry
- return Dispatcher
- end]]></ProtectedString>
- <BinaryString name="Tags"></BinaryString>
- </Properties>
- </Item>
- <Item class="ModuleScript" referent="RBXA013658904974BF49DB641C0A93175F4">
- <Properties>
- <Content name="LinkedSource"><null></null></Content>
- <string name="Name">Registry</string>
- <string name="ScriptGuid">{33D92E98-6382-4A3F-B9F7-7CDEC2031ED5}</string>
- <ProtectedString name="Source"><![CDATA[-- A fully extensible and type-safe admin-commands console
- -- @documentation https://github.com/evaera/Cmdr/blob/master/README.md
- -- @source https://github.com/evaera/Cmdr
- -- @rostrap Cmdr
- -- @author evaera
- local RunService = game:GetService("RunService")
- local Util = require(script.Parent.Util)
- --- The registry keeps track of all the commands and types that Cmdr knows about.
- local Registry = {
- TypeMethods = Util.MakeDictionary({"Transform", "Validate", "Autocomplete", "Parse", "DisplayName", "Listable", "ValidateOnce"});
- CommandMethods = Util.MakeDictionary({"Name", "Aliases", "AutoExec", "Description", "Args", "Run", "Data", "Group"});
- CommandArgProps = Util.MakeDictionary({"Name", "Type", "Description", "Optional", "Default"});
- Types = {};
- Commands = {};
- CommandsArray = {};
- Cmdr = nil;
- Hooks = {
- BeforeRun = {};
- AfterRun = {}
- };
- Stores = setmetatable({}, {
- __index = function (self, k)
- self[k] = {}
- return self[k]
- end
- });
- AutoExecBuffer = {};
- }
- --- Registers a type in the system.
- -- name: The type Name. This must be unique.
- function Registry:RegisterType (name, typeObject)
- if not name or not typeof(name) == "string" then
- error("Invalid type name provided: nil")
- end
- if not name:find("^[%d%l]%w*$") then
- error(('Invalid type name provided: "%s", type names must be alphanumeric and start with a lower-case letter or a digit.'):format(name))
- end
- for key in pairs(typeObject) do
- if self.TypeMethods[key] == nil then
- error("Unknown key/method in type \"" .. name .. "\": " .. key)
- end
- end
- if self.Types[name] ~= nil then
- error(('Type "%s" has already been registered.'):format(name))
- end
- typeObject.Name = name
- typeObject.DisplayName = typeObject.DisplayName or name
- self.Types[name] = typeObject
- end
- --- Helper method that registers types from all module scripts in a specific container.
- function Registry:RegisterTypesIn (container)
- for _, object in pairs(container:GetChildren()) do
- if object:IsA("ModuleScript") then
- object.Parent = self.Cmdr.ReplicatedRoot.Types
- require(object)(self)
- else
- self:RegisterTypesIn(object)
- end
- end
- end
- -- These are exactly the same thing. No one will notice. Except for you, dear reader.
- Registry.RegisterHooksIn = Registry.RegisterTypesIn
- --- Registers a command based purely on its definition.
- -- Prefer using Registry:RegisterCommand for proper handling of server/client model.
- function Registry:RegisterCommandObject (commandObject)
- for key in pairs(commandObject) do
- if self.CommandMethods[key] == nil then
- error("Unknown key/method in command " .. (commandObject.Name or "unknown command") .. ": " .. key)
- end
- end
- if commandObject.Args then
- for i, arg in pairs(commandObject.Args) do
- for key in pairs(arg) do
- if self.CommandArgProps[key] == nil then
- error(('Unknown propery in command "%s" argument #%d: %s'):format(commandObject.Name or "unknown", i, key))
- end
- end
- end
- end
- if RunService:IsClient() and commandObject.Data and commandObject.Run then
- error(('Invalid command implementation provided for "%s": "Data" and "Run" sections are mutually exclusive'):format(commandObject.Name or "unknown"))
- end
- if commandObject.AutoExec and RunService:IsClient() then
- table.insert(self.AutoExecBuffer, commandObject.AutoExec)
- self:FlushAutoExecBufferDeferred()
- end
- -- Unregister the old command if it exists...
- local oldCommand = self.Commands[commandObject.Name:lower()]
- if oldCommand and oldCommand.Aliases then
- for _, alias in pairs(oldCommand.Aliases) do
- self.Commands[alias:lower()] = nil
- end
- elseif not oldCommand then
- self.CommandsArray[#self.CommandsArray + 1] = commandObject
- end
- self.Commands[commandObject.Name:lower()] = commandObject
- if commandObject.Aliases then
- for _, alias in pairs(commandObject.Aliases) do
- self.Commands[alias:lower()] = commandObject
- end
- end
- end
- --- Registers a command definition and its server equivalent.
- -- Handles replicating the definition to the client.
- function Registry:RegisterCommand (commandScript, commandServerScript, filter)
- local commandObject = require(commandScript)
- if commandServerScript then
- commandObject.Run = require(commandServerScript)
- end
- if filter and not filter(commandObject) then
- return
- end
- self:RegisterCommandObject(commandObject)
- commandScript.Parent = self.Cmdr.ReplicatedRoot.Commands
- end
- --- A helper method that registers all commands inside a specific container.
- function Registry:RegisterCommandsIn (container, filter)
- local skippedServerScripts = {}
- local usedServerScripts = {}
- for _, commandScript in pairs(container:GetChildren()) do
- if commandScript:IsA("ModuleScript") then
- if not commandScript.Name:find("Server") then
- local serverCommandScript = container:FindFirstChild(commandScript.Name .. "Server")
- if serverCommandScript then
- usedServerScripts[serverCommandScript] = true
- end
- self:RegisterCommand(commandScript, serverCommandScript, filter)
- else
- skippedServerScripts[commandScript] = true
- end
- else
- self:RegisterCommandsIn(commandScript, filter)
- end
- end
- for skippedScript in pairs(skippedServerScripts) do
- if not usedServerScripts[skippedScript] then
- warn("Command script " .. skippedScript.Name .. " was skipped because it has 'Server' in its name, and has no equivalent shared script.")
- end
- end
- end
- --- Registers the default commands, with an optional filter function or array of groups.
- function Registry:RegisterDefaultCommands (arrayOrFunc)
- local isArray = type(arrayOrFunc) == "table"
- if isArray then
- arrayOrFunc = Util.MakeDictionary(arrayOrFunc)
- end
- self:RegisterCommandsIn(self.Cmdr.DefaultCommandsFolder, isArray and function (command)
- return arrayOrFunc[command.Group] or false
- end or arrayOrFunc)
- end
- --- Gets a command definition by name. (Can be an alias)
- function Registry:GetCommand (name)
- name = name or ""
- return self.Commands[name:lower()]
- end
- --- Returns a unique array of all registered commands (not including aliases)
- function Registry:GetCommands ()
- return self.CommandsArray
- end
- --- Returns an array of the names of all registered commands (not including aliases)
- function Registry:GetCommandsAsStrings ()
- local commands = {}
- for _, command in pairs(self.CommandsArray) do
- commands[#commands + 1] = command.Name
- end
- return commands
- end
- --- Gets a type definition by name.
- function Registry:GetType (name)
- return self.Types[name]
- end
- --- Adds a hook to be called when any command is run
- function Registry:RegisterHook(hookName, callback, priority)
- if not self.Hooks[hookName] then
- error(("Invalid hook name: %q"):format(hookName), 2)
- end
- table.insert(self.Hooks[hookName], { callback = callback; priority = priority or 0; } )
- table.sort(self.Hooks[hookName], function(a, b) return a.priority < b.priority end)
- end
- -- Backwards compatability (deprecated)
- Registry.AddHook = Registry.RegisterHook
- --- Returns the store with the given name
- -- Used for commands that require persistent state, like bind or ban
- function Registry:GetStore(name)
- return self.Stores[name]
- end
- --- Calls self:FlushAutoExecBuffer at the end of the frame
- function Registry:FlushAutoExecBufferDeferred()
- if self.AutoExecFlushConnection then
- return
- end
- self.AutoExecFlushConnection = RunService.Heartbeat:Connect(function()
- self.AutoExecFlushConnection:Disconnect()
- self.AutoExecFlushConnection = nil
- self:FlushAutoExecBuffer()
- end)
- end
- --- Runs all pending auto exec commands in Registry.AutoExecBuffer
- function Registry:FlushAutoExecBuffer()
- for _, commandGroup in ipairs(self.AutoExecBuffer) do
- for _, command in ipairs(commandGroup) do
- self.Cmdr.Dispatcher:EvaluateAndRun(command)
- end
- end
- end
- return function (cmdr)
- Registry.Cmdr = cmdr
- return Registry
- end
- ]]></ProtectedString>
- <BinaryString name="Tags"></BinaryString>
- </Properties>
- </Item>
- <Item class="ModuleScript" referent="RBXC7702180AA01435AA52F6BAFCD075B38">
- <Properties>
- <Content name="LinkedSource"><null></null></Content>
- <string name="Name">Util</string>
- <string name="ScriptGuid">{4CD0DF40-7153-4B7F-92BD-1697277A9BF8}</string>
- <ProtectedString name="Source"><![CDATA[-- A fully extensible and type-safe admin-commands console
- -- @documentation https://github.com/evaera/Cmdr/blob/master/README.md
- -- @source https://github.com/evaera/Cmdr
- -- @rostrap Cmdr
- -- @author evaera
- local TextService = game:GetService("TextService")
- local Util = {}
- --- Takes an array and flips its values into dictionary keys with value of true.
- function Util.MakeDictionary(array)
- local dictionary = {}
- for i = 1, #array do
- dictionary[array[i]] = true
- end
- return dictionary
- end
- -- Takes an array of instances and returns (array<names>, array<instances>)
- local function transformInstanceSet(instances)
- local names = {}
- for i = 1, #instances do
- names[i] = instances[i].Name
- end
- return names, instances
- end
- --- Returns a function that is a fuzzy finder for the specified set or container.
- -- Can pass an array of strings, array of instances, array of EnumItems,
- -- array of dictionaries with a Name key or an instance (in which case its children will be used)
- -- Exact matches will be inserted in the front of the resulting array
- function Util.MakeFuzzyFinder(setOrContainer)
- local names
- local instances = {}
- if typeof(setOrContainer) == "Enum" then
- setOrContainer = setOrContainer:GetEnumItems()
- end
- if typeof(setOrContainer) == "Instance" then
- names, instances = transformInstanceSet(setOrContainer:GetChildren())
- elseif typeof(setOrContainer) == "table" then
- if
- typeof(setOrContainer[1]) == "Instance" or typeof(setOrContainer[1]) == "EnumItem" or
- (typeof(setOrContainer[1]) == "table" and typeof(setOrContainer[1].Name) == "string")
- then
- names, instances = transformInstanceSet(setOrContainer)
- elseif type(setOrContainer[1]) == "string" then
- names = setOrContainer
- elseif setOrContainer[1] ~= nil then
- error("MakeFuzzyFinder only accepts tables of instances or strings.")
- else
- names = {}
- end
- else
- error("MakeFuzzyFinder only accepts a table, Enum, or Instance.")
- end
- -- Searches the set (checking exact matches first)
- return function(text, returnFirst)
- local results = {}
- for i, name in pairs(names) do
- local value = instances and instances[i] or name
- -- Continue on checking for non-exact matches...
- -- Still need to loop through everything, even on returnFirst, because possibility of an exact match.
- if name:lower() == text:lower() then
- if returnFirst then
- return value
- else
- table.insert(results, 1, value)
- end
- elseif name:lower():sub(1, #text) == text:lower() then
- results[#results + 1] = value
- end
- end
- if returnFirst then
- return results[1]
- end
- return results
- end
- end
- --- Takes an array of instances and returns an array of those instances' names.
- function Util.GetNames(instances)
- local names = {}
- for i = 1, #instances do
- names[i] = instances[i].Name or tostring(instances[i])
- end
- return names
- end
- --- Splits a string using a simple separator (no quote parsing)
- function Util.SplitStringSimple(inputstr, sep)
- if sep == nil then
- sep = "%s"
- end
- local t = {}
- local i = 1
- for str in string.gmatch(inputstr, "([^" .. sep .. "]+)") do
- t[i] = str
- i = i + 1
- end
- return t
- end
- local function charCode(n)
- return utf8.char(tonumber(n, 16))
- end
- -- Discard any excess return values
- local function first(x)
- return x
- end
- --- Parses escape sequences into their fully qualified characters
- function Util.ParseEscapeSequences(text)
- return text:gsub("\\(.)", {
- t = "\t";
- n = "\n";
- })
- :gsub("\\u(%x%x%x%x)", charCode)
- :gsub("\\x(%x%x)", charCode)
- end
- local function encodeControlChars(text)
- return first(
- text
- :gsub("\\\\", string.char(17))
- :gsub("\\\"", string.char(18))
- :gsub("\\'", string.char(19))
- )
- end
- local function decodeControlChars(text)
- return first(
- text
- :gsub(string.char(17), "\\")
- :gsub(string.char(18), "\"")
- :gsub(string.char(19), "'")
- )
- end
- --- Splits a string by space but taking into account quoted sequences which will be treated as a single argument.
- function Util.SplitString(text, max)
- text = encodeControlChars(text)
- max = max or math.huge
- local t = {}
- local spat, epat, buf, quoted = [=[^(['"])]=], [=[(['"])$]=]
- for str in text:gmatch("%S+") do
- str = Util.ParseEscapeSequences(str)
- local squoted = str:match(spat)
- local equoted = str:match(epat)
- local escaped = str:match([=[(\*)['"]$]=])
- if squoted and not quoted and not equoted then
- buf, quoted = str, squoted
- elseif buf and equoted == quoted and #escaped % 2 == 0 then
- str, buf, quoted = buf .. " " .. str, nil, nil
- elseif buf then
- buf = buf .. " " .. str
- end
- if not buf then
- t[#t + (#t > max and 0 or 1)] = decodeControlChars(str:gsub(spat, ""):gsub(epat, ""))
- end
- end
- if buf then
- t[#t + (#t > max and 0 or 1)] = decodeControlChars(buf)
- end
- return t
- end
- --- Takes an array of arguments and a max value.
- -- Any indicies past the max value will be appended to the last valid argument.
- function Util.MashExcessArguments(arguments, max)
- local t = {}
- for i = 1, #arguments do
- if i > max then
- t[max] = ("%s %s"):format(t[max] or "", arguments[i])
- else
- t[i] = arguments[i]
- end
- end
- return t
- end
- --- Trims whitespace from both sides of a string.
- function Util.TrimString(s)
- return s:match "^%s*(.-)%s*$"
- end
- --- Returns the text bounds size based on given text, label (from which properties will be pulled), and optional Vector2 container size.
- function Util.GetTextSize(text, label, size)
- return TextService:GetTextSize(text, label.TextSize, label.Font, size or Vector2.new(label.AbsoluteSize.X, 0))
- end
- --- Makes an Enum type.
- function Util.MakeEnumType(name, values)
- local findValue = Util.MakeFuzzyFinder(values)
- return {
- Validate = function(text)
- return findValue(text, true) ~= nil, ("Value %q is not a valid %s."):format(text, name)
- end,
- Autocomplete = function(text)
- local list = findValue(text)
- return type(list[1]) ~= "string" and Util.GetNames(list) or list
- end,
- Parse = function(text)
- return findValue(text, true)
- end
- }
- end
- --- Parses a prefixed union type argument (such as %Team)
- function Util.ParsePrefixedUnionType(typeValue, rawValue)
- local split = Util.SplitStringSimple(typeValue)
- -- Check prefixes in order from longest to shortest
- local types = {}
- for i = 1, #split, 2 do
- types[#types + 1] = {
- prefix = split[i - 1] or "",
- type = split[i]
- }
- end
- table.sort(
- types,
- function(a, b)
- return #a.prefix > #b.prefix
- end
- )
- for i = 1, #types do
- local t = types[i]
- if rawValue:sub(1, #t.prefix) == t.prefix then
- return t.type, rawValue:sub(#t.prefix + 1), t.prefix
- end
- end
- end
- --- Creates a listable type from a singlular type
- function Util.MakeListableType(type)
- local listableType = {
- Listable = true,
- Transform = type.Transform,
- Validate = type.Validate,
- Autocomplete = type.Autocomplete,
- Parse = function(...)
- return {type.Parse(...)}
- end
- }
- return listableType
- end
- local function encodeCommandEscape(text)
- return first(text:gsub("\\%$", string.char(20)))
- end
- local function decodeCommandEscape(text)
- return first(text:gsub(string.char(20), "$"))
- end
- --- Runs embedded commands and replaces them
- function Util.RunEmbeddedCommands(dispatcher, str)
- str = encodeCommandEscape(str)
- local results = {}
- -- We need to do this because you can't yield in the gsub function
- for text in str:gmatch("$(%b{})") do
- local doQuotes = true
- local commandString = text:sub(2, #text-1)
- if commandString:match("^{.+}$") then -- Allow double curly for literal replacement
- doQuotes = false
- commandString = commandString:sub(2, #commandString-1)
- end
- results[text] = dispatcher:EvaluateAndRun(commandString)
- if doQuotes and results[text]:find("%s") then
- results[text] = string.format("%q", results[text])
- end
- end
- return decodeCommandEscape(str:gsub("$(%b{})", results))
- end
- --- Replaces arguments in the format $1, $2, $something with whatever the
- -- given function returns for it.
- function Util.SubstituteArgs(str, replace)
- str = encodeCommandEscape(str)
- -- Convert numerical keys to strings
- if type(replace) == "table" then
- for i = 1, #replace do
- local k = tostring(i)
- replace[k] = replace[i]
- if replace[k]:find("%s") then
- replace[k] = string.format("%q", replace[k])
- end
- end
- end
- return decodeCommandEscape(str:gsub("$(%w+)", replace))
- end
- --- Creates an alias command
- function Util.MakeAliasCommand(name, commandString)
- return {
- Name = name,
- Aliases = {},
- Description = commandString,
- Group = "UserAlias",
- Args = {
- {
- Type = "string",
- Name = "Argument 1",
- Description = "",
- Default = ""
- },
- {
- Type = "string",
- Name = "Argument 2",
- Description = "",
- Default = ""
- },
- {
- Type = "string",
- Name = "Argument 3",
- Description = "",
- Default = ""
- },
- {
- Type = "string",
- Name = "Argument 4",
- Description = "",
- Default = ""
- },
- {
- Type = "string",
- Name = "Argument 5",
- Description = "",
- Default = ""
- }
- },
- Run = function(context, ...)
- local args = {...}
- local commands = Util.SplitStringSimple(commandString, "&&")
- for i, command in ipairs(commands) do
- local output = context.Dispatcher:EvaluateAndRun(
- Util.RunEmbeddedCommands(
- context.Dispatcher,
- Util.SubstituteArgs(command, args)
- )
- )
- if i == #commands then
- return output -- return last command's output
- else
- context:Reply(output)
- end
- end
- return ""
- end
- }
- end
- --- Makes a type that contains a sequence, e.g. Vector3 or Color3
- function Util.MakeSequenceType(options)
- options = options or {}
- assert(options.Parse ~= nil or options.Constructor ~= nil, "MakeSequenceType: Must provide one of: Constructor, Parse")
- options.TransformEach = options.TransformEach or function(...)
- return ...
- end
- options.ValidateEach = options.ValidateEach or function()
- return true
- end
- return {
- Transform = function (text)
- return Util.Map(Util.SplitPrioritizedDelimeter(text, {",", "%s"}), function(value)
- return options.TransformEach(value)
- end)
- end;
- Validate = function (components)
- if options.Length and #components > options.Length then
- return false, ("Maximum of %d values allowed in sequence"):format(options.Length)
- end
- for i = 1, options.Length or #components do
- local valid, reason = options.ValidateEach(components[i], i)
- if not valid then
- return false, reason
- end
- end
- return true
- end;
- Parse = options.Parse or function(components)
- return options.Constructor(unpack(components))
- end
- }
- end
- --- Splits a string by a single delimeter chosen from the given set.
- -- The first matching delimeter from the set becomes the split character.
- function Util.SplitPrioritizedDelimeter(text, delimeters)
- for i, delimeter in ipairs(delimeters) do
- if text:find(delimeter) or i == #delimeters then
- return Util.SplitStringSimple(text, delimeter)
- end
- end
- end
- --- Maps values of an array through a callback and returns an array of mapped values
- function Util.Map(array, callback)
- local results = {}
- for i, v in ipairs(array) do
- results[i] = callback(v, i)
- end
- return results
- end
- --- Maps arguments #2-n through callback and returns values as tuple
- function Util.Each(callback, ...)
- local results = {}
- for i, value in ipairs({...}) do
- results[i] = callback(value)
- end
- return unpack(results)
- end
- --- Emulates tabstops with spaces
- function Util.EmulateTabstops(text, tabWidth)
- local result = ""
- for i = 1, #text do
- local char = text:sub(i, i)
- result = result .. (char == "\t" and string.rep(" ", tabWidth - #result % tabWidth) or char)
- end
- return result
- end
- return Util
- ]]></ProtectedString>
- <BinaryString name="Tags"></BinaryString>
- </Properties>
- </Item>
- </Item>
- <Item class="ModuleScript" referent="RBX896F91C3C40E46B4A1781B9D98F5FB1E">
- <Properties>
- <Content name="LinkedSource"><null></null></Content>
- <string name="Name">Loadstring</string>
- <string name="ScriptGuid">{162F5D71-14B8-4FFC-AEDD-E8D0D282AD31}</string>
- <ProtectedString name="Source"><![CDATA[--[[
- 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]]></ProtectedString>
- <BinaryString name="Tags"></BinaryString>
- </Properties>
- <Item class="ModuleScript" referent="RBX139BCE3209D84EE097021EF03E3CD256">
- <Properties>
- <Content name="LinkedSource"><null></null></Content>
- <string name="Name">LuaZ</string>
- <string name="ScriptGuid">{6A425F9D-9F7A-417C-A445-E264422EEBDE}</string>
- <ProtectedString name="Source"><![CDATA[--[[--------------------------------------------------------------------
- lzio.lua
- Lua buffered streams in Lua
- This file is part of Yueliang.
- Copyright (c) 2005-2006 Kein-Hong Man <[email protected]>
- 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]]></ProtectedString>
- <BinaryString name="Tags"></BinaryString>
- </Properties>
- </Item>
- <Item class="ModuleScript" referent="RBX74ED26F683644E6283E6F69B9920D64A">
- <Properties>
- <Content name="LinkedSource"><null></null></Content>
- <string name="Name">LuaX</string>
- <string name="ScriptGuid">{9514EDB3-FB50-4B26-B929-88952220C9D9}</string>
- <ProtectedString name="Source"><![CDATA[--[[--------------------------------------------------------------------
- llex.lua
- Lua lexical analyzer in Lua
- This file is part of Yueliang.
- Copyright (c) 2005-2006 Kein-Hong Man <[email protected]>
- 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]]></ProtectedString>
- <BinaryString name="Tags"></BinaryString>
- </Properties>
- </Item>
- <Item class="ModuleScript" referent="RBX0C77EABDF4E04495A61E17E0F3651AB2">
- <Properties>
- <Content name="LinkedSource"><null></null></Content>
- <string name="Name">LuaY</string>
- <string name="ScriptGuid">{29BEDDC6-A318-4D8E-9DAB-C65576A9430E}</string>
- <ProtectedString name="Source"><![CDATA[--[[--------------------------------------------------------------------
- lparser.lua
- Lua 5 parser in Lua
- This file is part of Yueliang.
- Copyright (c) 2005-2007 Kein-Hong Man <[email protected]>
- 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]]></ProtectedString>
- <BinaryString name="Tags"></BinaryString>
- </Properties>
- </Item>
- <Item class="ModuleScript" referent="RBX6D281683B751445CAD6CA6A9CAA1B31F">
- <Properties>
- <Content name="LinkedSource"><null></null></Content>
- <string name="Name">LuaK</string>
- <string name="ScriptGuid">{E74CCBE8-6F78-46F4-B517-8E4B60989D32}</string>
- <ProtectedString name="Source"><![CDATA[--[[--------------------------------------------------------------------
- lcode.lua
- Lua 5 code generator in Lua
- This file is part of Yueliang.
- Copyright (c) 2005-2007 Kein-Hong Man <[email protected]>
- 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]]></ProtectedString>
- <BinaryString name="Tags"></BinaryString>
- </Properties>
- </Item>
- <Item class="ModuleScript" referent="RBX5721C2C4E9AA4045A3AE5FC31529EF48">
- <Properties>
- <Content name="LinkedSource"><null></null></Content>
- <string name="Name">LuaU</string>
- <string name="ScriptGuid">{9C277632-5516-4FB9-AC62-AC3F3BE90D9E}</string>
- <ProtectedString name="Source"><![CDATA[--[[--------------------------------------------------------------------
- ldump.lua
- Save precompiled Lua chunks
- This file is part of Yueliang.
- Copyright (c) 2006 Kein-Hong Man <[email protected]>
- 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]]></ProtectedString>
- <BinaryString name="Tags"></BinaryString>
- </Properties>
- </Item>
- <Item class="ModuleScript" referent="RBX860D6B646D15448DA00363761F66761F">
- <Properties>
- <Content name="LinkedSource"><null></null></Content>
- <string name="Name">LuaP</string>
- <string name="ScriptGuid">{F6F5E123-B39B-4C8A-B113-E644BAE8AA75}</string>
- <ProtectedString name="Source"><![CDATA[--[[--------------------------------------------------------------------
- lopcodes.lua
- Lua 5 virtual machine opcodes in Lua
- This file is part of Yueliang.
- Copyright (c) 2006 Kein-Hong Man <[email protected]>
- 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]]></ProtectedString>
- <BinaryString name="Tags"></BinaryString>
- </Properties>
- </Item>
- <Item class="ModuleScript" referent="RBX8B078DFEAA7F44208ECF5696D66AEBB3">
- <Properties>
- <Content name="LinkedSource"><null></null></Content>
- <string name="Name">Rerubi</string>
- <string name="ScriptGuid">{8CDD4A3E-999B-4E2B-ACC4-C871AF954901}</string>
- <ProtectedString name="Source"><![CDATA[local Select = select;
- local Byte = string.byte;
- local Sub = string.sub;
- local Opmode = {
- {b = 'OpArgR', c='OpArgN'}, {b = 'OpArgK', c='OpArgN'}, {b = 'OpArgU', c='OpArgU'},
- {b = 'OpArgR', c='OpArgN'}, {b = 'OpArgU', c='OpArgN'}, {b = 'OpArgK', c='OpArgN'},
- {b = 'OpArgR', c='OpArgK'}, {b = 'OpArgK', c='OpArgN'}, {b = 'OpArgU', c='OpArgN'},
- {b = 'OpArgK', c='OpArgK'}, {b = 'OpArgU', c='OpArgU'}, {b = 'OpArgR', c='OpArgK'},
- {b = 'OpArgK', c='OpArgK'}, {b = 'OpArgK', c='OpArgK'}, {b = 'OpArgK', c='OpArgK'},
- {b = 'OpArgK', c='OpArgK'}, {b = 'OpArgK', c='OpArgK'}, {b = 'OpArgK', c='OpArgK'},
- {b = 'OpArgR', c='OpArgN'}, {b = 'OpArgR', c='OpArgN'}, {b = 'OpArgR', c='OpArgN'},
- {b = 'OpArgR', c='OpArgR'}, {b = 'OpArgR', c='OpArgN'}, {b = 'OpArgK', c='OpArgK'},
- {b = 'OpArgK', c='OpArgK'}, {b = 'OpArgK', c='OpArgK'}, {b = 'OpArgR', c='OpArgU'},
- {b = 'OpArgR', c='OpArgU'}, {b = 'OpArgU', c='OpArgU'}, {b = 'OpArgU', c='OpArgU'},
- {b = 'OpArgU', c='OpArgN'}, {b = 'OpArgR', c='OpArgN'}, {b = 'OpArgR', c='OpArgN'},
- {b = 'OpArgN', c='OpArgU'}, {b = 'OpArgU', c='OpArgU'}, {b = 'OpArgN', c='OpArgN'},
- {b = 'OpArgU', c='OpArgN'}, {b = 'OpArgU', c='OpArgN'}
- };
- 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
- -- special thanks;
- -- @cntkillme for providing faster bit extraction
- -- @Eternal for being #1 bug finder and providing better float decoder
- -- @stravant for contributing to the original project this is derived from
- -- rerubi is an upgrade to the original Lua VM in Lua
- -- the prime goal of rerubi is to be the fastest:tm: alternative
- -- to a Lua in Lua bytecode execution
- 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()
- -- thanks @Eternal for giving me this so I could mangle it in here and have it work
- local Left = gBits32();
- local Right = gBits32();
- local IsNormal = 1
- local Mantissa = (gBit(Right, 1, 20) * (2 ^ 32))
- + Left;
- local Exponent = gBit(Right, 21, 31);
- local Sign = ((-1) ^ gBit(Right, 32));
- if (Exponent == 0) then
- if (Mantissa == 0) then
- return Sign * 0 -- +-0
- else
- Exponent = 1
- IsNormal = 0
- end
- elseif (Exponent == 2047) then
- if (Mantissa == 0) then
- return Sign * (1 / 0) -- +-Inf
- else
- return Sign * (0 / 0) -- +-Q/Nan
- end
- end
- -- sign * 2**e-1023 * isNormal.mantissa
- return math.ldexp(Sign, Exponent - 1023) * (IsNormal + (Mantissa / (2 ^ 52)))
- 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.
- };
- local ConstantReferences = {}; -- for an optimization
- 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 Mode = Opmode[Opco + 1];
- local Inst = {
- Enum = Opco;
- Value = Data;
- 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;
- -- Precompute data for some instructions
- do
- -- TEST and TESTSET
- if Opco == 26 or Opco == 27 then
- Inst[3] = Inst[3] == 0;
- end
- -- EQ, LT, LE
- if Opco >= 23 and Opco <= 25 then
- Inst[1] = Inst[1] ~= 0;
- end
- -- Anything that looks at a constant using B
- if Mode.b == 'OpArgK' then
- Inst[3] = Inst[3] or false; -- Simply to guarantee that Inst[4] is inserted in the array part
- if Inst[2] >= 256 then
- local Cons = Inst[2] - 256;
- Inst[4] = Cons;
- local ReferenceData = ConstantReferences[Cons];
- if not ReferenceData then
- ReferenceData = {};
- ConstantReferences[Cons] = ReferenceData;
- end
- ReferenceData[#ReferenceData + 1] = {Inst = Inst, Register = 4}
- end
- end
- -- Anything that looks at a constant using C
- if Mode.c == 'OpArgK' then
- Inst[4] = Inst[4] or false -- Simply to guarantee that Inst[5] is inserted in the array part
- if Inst[3] >= 256 then
- local Cons = Inst[3] - 256;
- Inst[5] = Cons;
- local ReferenceData = ConstantReferences[Cons];
- if not ReferenceData then
- ReferenceData = {};
- ConstantReferences[Cons] = ReferenceData;
- end
- ReferenceData[#ReferenceData + 1] = {Inst = Inst, Register = 5}
- end
- end
- 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;
- -- Finish precomputing constants
- local Refs = ConstantReferences[Idx - 1];
- if Refs then
- for i = 1, #Refs do
- Refs[i].Inst[Refs[i].Register] = Cons
- end
- end
- -- Write Constant to pool
- 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 _ = 1, gInt() do -- Locals in stack.
- gString(); -- Name of local.
- gBits32(); -- Starting point.
- gBits32(); -- End point.
- end;
- for _ = 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 '?';
- error(string.format('%s:%s: %s', Name, Line, tostring(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 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) then
- Top = Key;
- end;
- GStack[Key] = Value;
- end;
- });
- local function Loop()
- local Inst, Enum;
- 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 Stk = Stack;
- Stk[Inst[1]] = Stk[Inst[2]][Inst[5] or Stk[Inst[3]]];
- 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 Stk = Stack
- Stk[Inst[1]][Inst[4] or Stk[Inst[2]]] = Inst[5] or Stk[Inst[3]]
- elseif (Enum == 10) then -- NEWTABLE
- Stack[Inst[1]] = {};
- elseif (Enum == 11) then -- SELF
- local Stk = Stack;
- local A = Inst[1];
- local B = Stk[Inst[2]];
- local C = Inst[5] or Stk[Inst[3]];
- Stk[A + 1] = B;
- Stk[A] = B[C];
- elseif (Enum == 12) then -- ADD
- local Stk = Stack;
- Stk[Inst[1]] = (Inst[4] or Stk[Inst[2]]) + (Inst[5] or Stk[Inst[3]]);
- elseif (Enum == 13) then -- SUB
- local Stk = Stack;
- Stk[Inst[1]] = (Inst[4] or Stk[Inst[2]]) - (Inst[5] or Stk[Inst[3]]);
- elseif (Enum == 14) then -- MUL
- local Stk = Stack;
- Stk[Inst[1]] = (Inst[4] or Stk[Inst[2]]) * (Inst[5] or Stk[Inst[3]]);
- elseif (Enum == 15) then -- DIV
- local Stk = Stack;
- Stk[Inst[1]] = (Inst[4] or Stk[Inst[2]]) / (Inst[5] or Stk[Inst[3]]);
- elseif (Enum == 16) then -- MOD
- local Stk = Stack;
- Stk[Inst[1]] = (Inst[4] or Stk[Inst[2]]) % (Inst[5] or Stk[Inst[3]]);
- elseif (Enum == 17) then -- POW
- local Stk = Stack;
- Stk[Inst[1]] = (Inst[4] or Stk[Inst[2]]) ^ (Inst[5] or Stk[Inst[3]]);
- 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 = Stk[B];
- for Idx = B + 1, Inst[3] do
- K = K .. Stk[Idx];
- end;
- Stack[Inst[1]] = K;
- elseif (Enum == 22) then -- JMP
- InstrPoint = InstrPoint + Inst[2];
- elseif (Enum == 23) then -- EQ
- local Stk = Stack;
- local B = Inst[4] or Stk[Inst[2]];
- local C = Inst[5] or Stk[Inst[3]];
- if (B == C) ~= Inst[1] then
- InstrPoint = InstrPoint + 1;
- end;
- elseif (Enum == 24) then -- LT
- local Stk = Stack;
- local B = Inst[4] or Stk[Inst[2]];
- local C = Inst[5] or Stk[Inst[3]];
- if (B < C) ~= Inst[1] then
- InstrPoint = InstrPoint + 1;
- end;
- elseif (Enum == 25) then -- LE
- local Stk = Stack;
- local B = Inst[4] or Stk[Inst[2]];
- local C = Inst[5] or Stk[Inst[3]];
- if (B <= C) ~= Inst[1] then
- InstrPoint = InstrPoint + 1;
- end;
- elseif (Enum == 26) then -- TEST
- if Inst[3] then
- if Stack[Inst[1]] then
- InstrPoint = InstrPoint + 1;
- end
- elseif Stack[Inst[1]] then
- else
- InstrPoint = InstrPoint + 1;
- end
- elseif (Enum == 27) then -- TESTSET
- local B = Stack[Inst[2]];
- if Inst[3] then
- if B then
- InstrPoint = InstrPoint + 1;
- else
- Stack[Inst[1]] = B
- end
- elseif B then
- Stack[Inst[1]] = B
- else
- InstrPoint = InstrPoint + 1;
- 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, Edx;
- Args = {};
- if (B ~= 1) then
- if (B ~= 0) then
- Limit = A + B - 1;
- else
- Limit = Top;
- end;
- Edx = 0;
- for Idx = A + 1, Limit do
- Edx = Edx + 1;
- Args[Edx] = 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 - 1;
- end;
- Edx = 0;
- for Idx = A, Limit do
- Edx = Edx + 1;
- Stk[Idx] = Results[Edx];
- end;
- end;
- elseif (Enum == 29) then -- TAILCALL
- local A = Inst[1];
- local B = Inst[2];
- local Stk = Stack;
- local Args, Results;
- local Limit;
- local Rets = 0;
- Args = {};
- if (B ~= 1) then
- if (B ~= 0) then
- Limit = A + B - 1;
- else
- Limit = Top;
- end
- for Idx = A + 1, Limit do
- Args[#Args + 1] = Stk[Idx];
- end
- Results = {Stk[A](unpack(Args, 1, Limit - A))};
- else
- Results = {Stk[A]()};
- end;
- for Index in pairs(Results) do -- get return count
- if (Index > Rets) then
- Rets = Index;
- end;
- end;
- return Results, Rets;
- elseif (Enum == 30) then -- RETURN
- local A = Inst[1];
- local B = Inst[2];
- local Stk = Stack;
- local Edx, Output;
- local Limit;
- if (B == 1) then
- return;
- elseif (B == 0) then
- Limit = Top;
- else
- Limit = A + B - 2;
- end;
- Output = {};
- Edx = 0;
- for Idx = A, Limit do
- Edx = Edx + 1;
- Output[Edx] = Stk[Idx];
- end;
- return Output, Edx;
- 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;
- -- As per mirroring the real vm
- Stk[A] = assert(tonumber(Stk[A]), '`for` initial value must be a number');
- Stk[A + 1] = assert(tonumber(Stk[A + 1]), '`for` limit must be a number');
- Stk[A + 2] = assert(tonumber(Stk[A + 2]), '`for` step must be a number');
- Stk[A] = Stk[A] - Stk[A + 2];
- InstrPoint = InstrPoint + Inst[2];
- elseif (Enum == 33) then -- TFORLOOP
- local A = Inst[1];
- 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].Value;
- end;
- local Offset = (C - 1) * 50;
- local T = Stk[A]; -- Assuming T is the newly created table.
- if (B == 0) then
- B = Top - A;
- 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 NewProto = Proto[Inst[2]];
- local Stk = Stack;
- local Indexes;
- local NewUvals;
- if (NewProto.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, NewProto.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(NewProto, Env, NewUvals);
- elseif (Enum == 37) then -- VARARG
- local A = Inst[1];
- local B = Inst[2];
- local Stk, Vars = Stack, Vararg;
- Top = A - 1;
- for Idx = A, A + (B > 0 and B - 1 or Varargsz) do
- Stk[Idx] = Vars[Idx - A];
- end;
- end;
- end;
- end;
- local Args = {...};
- for Idx = 0, Varargsz do
- if (Idx >= Chunk.Args) then
- Vararg[Idx - Chunk.Args] = Args[Idx + 1];
- else
- Stack[Idx] = Args[Idx + 1];
- end;
- end;
- local A, B, C = pcall(Loop); -- Pcalling to allow yielding
- if A then -- We're always expecting this to come out true (because errorless code)
- if B and (C > 0) then -- So I flipped the conditions.
- return unpack(B, 1, C);
- 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;
- ]]></ProtectedString>
- <BinaryString name="Tags"></BinaryString>
- </Properties>
- </Item>
- <Item class="Script" referent="RBX9BAAC995A4854EFCBB2D1DFE3A152236">
- <Properties>
- <bool name="Disabled">true</bool>
- <Content name="LinkedSource"><null></null></Content>
- <string name="Name">Credits</string>
- <string name="ScriptGuid">{4B480C1C-A37C-41F3-BE92-D3386E3ACBD8}</string>
- <ProtectedString name="Source"><![CDATA[--[[
- LuLu 5.1 VM (original implementation) - http://lulu.luaforge.net/
- * Rerubu (rewrite based off LuLu) - https://github.com/Rerumu/Rerubi
- Yueliang 5 (Lua compiler in Lua) - http://yueliang.luaforge.net/
- * einsteinK: various bug fixes
- ]]]]></ProtectedString>
- <BinaryString name="Tags"></BinaryString>
- </Properties>
- </Item>
- </Item>
- </Item>
- </roblox>
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement