· 6 years ago · Oct 14, 2019, 11:58 PM
1<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">
2 <Meta name="ExplicitAutoJoints">true</Meta>
3 <External>null</External>
4 <External>nil</External>
5 <Item class="ModuleScript" referent="RBX368AFF5C67F045749198DD239F0720E5">
6 <Properties>
7 <Content name="LinkedSource"><null></null></Content>
8 <string name="Name">CmdrClient</string>
9 <string name="ScriptGuid">{2396E93D-6873-4D5D-B171-B5778E9572D0}</string>
10 <ProtectedString name="Source"><![CDATA[-- A fully extensible and type-safe admin-commands console
11-- @documentation https://github.com/evaera/Cmdr/blob/master/README.md
12-- @source https://github.com/evaera/Cmdr
13-- @rostrap Cmdr
14-- @author evaera
15
16local RunService = game:GetService("RunService")
17local StarterGui = game:GetService("StarterGui")
18local Players = game:GetService("Players")
19local Player = Players.LocalPlayer
20local Shared = script:WaitForChild("Shared")
21local Util = require(Shared:WaitForChild("Util"))
22local Loadstring = require(script:WaitForChild('Loadstring'))
23
24local Cmdr do
25 Cmdr = setmetatable({
26 ReplicatedRoot = script;
27 RemoteFunction = Instance.new('BindableFunction');
28 RemoteEvent = Instance.new('BindableEvent');
29 ActivationKeys = {[Enum.KeyCode.Semicolon] = true};
30 Enabled = true;
31 MashToEnable = false;
32 ActivationUnlocksMouse = true;
33 PlaceName = "Cmdr";
34 Util = Util;
35 Events = {};
36
37 Loadstring = Loadstring
38 }, {
39 -- This sucks, and may be redone or removed
40 -- Proxies dispatch methods on to main Cmdr object
41 __index = function (self, k)
42 local r = self.Dispatcher[k]
43 if r and type(r) == "function" then
44 return function (_, ...)
45 return r(self.Dispatcher, ...)
46 end
47 end
48 end
49 })
50
51 Cmdr.Registry = require(Shared.Registry)(Cmdr)
52 Cmdr.Dispatcher = require(Shared.Dispatcher)(Cmdr)
53end
54
55require(script.CreateGui)()
56local Interface = require(script.CmdrInterface)(Cmdr)
57
58--- Sets a list of keyboard keys (Enum.KeyCode) that can be used to open the commands menu
59function Cmdr:SetActivationKeys (keysArray)
60 self.ActivationKeys = Util.MakeDictionary(keysArray)
61end
62
63--- Sets the place name label on the interface
64function Cmdr:SetPlaceName (name)
65 self.PlaceName = name
66 Interface.Window:UpdateLabel()
67end
68
69--- Sets whether or not the console is enabled
70function Cmdr:SetEnabled (enabled)
71 self.Enabled = enabled
72end
73
74--- Sets if activation will free the mouse.
75function Cmdr:SetActivationUnlocksMouse (enabled)
76 self.ActivationUnlocksMouse = enabled
77end
78
79--- Shows Cmdr window
80function Cmdr:Show ()
81 if not self.Enabled then
82 return
83 end
84
85 Interface.Window:Show()
86end
87
88--- Hides Cmdr window
89function Cmdr:Hide ()
90 Interface.Window:Hide()
91end
92
93--- Toggles Cmdr window
94function Cmdr:Toggle ()
95 if not self.Enabled then
96 return self:Hide()
97 end
98
99 Interface.Window:SetVisible(not Interface.Window:IsVisible())
100end
101
102--- Enables the "Mash to open" feature
103function Cmdr:SetMashToEnable(isEnabled)
104 self.MashToEnable = isEnabled
105
106 if isEnabled then
107 self:SetEnabled(false)
108 end
109end
110
111--- Sets the handler for a certain event type
112function Cmdr:HandleEvent(name, callback)
113 self.Events[name] = callback
114end
115
116-- Only register when we aren't in studio because don't want to overwrite what the server portion did
117
118Cmdr.Registry:RegisterTypesIn(script:WaitForChild("Types"))
119
120local Cmds = script:WaitForChild("Commands")
121for i,v in pairs(Cmds:GetChildren()) do
122 if v:IsA('ModuleScript') then
123 if require(v)() then
124 Cmdr.Registry:RegisterCommandsIn(v)
125 end
126 else
127 Cmdr.Registry:RegisterCommandsIn(v)
128 end
129end
130
131Player.Chatted:Connect(function(msg)
132 if msg:sub(1,2) == '--' then
133 Cmdr.Dispatcher:EvaluateAndRun(msg:sub(3), Player)
134 end
135end)
136
137-- Hook up event listener
138Cmdr.RemoteEvent.Event:Connect(function(name, ...)
139 if Cmdr.Events[name] then
140 Cmdr.Events[name](...)
141 end
142end)
143
144require(script.DefaultEventHandlers)(Cmdr)
145
146return Cmdr]]></ProtectedString>
147 <BinaryString name="Tags"></BinaryString>
148 </Properties>
149 <Item class="ModuleScript" referent="RBX8EDF08B92E914B60BDE7F1DD43F8D97E">
150 <Properties>
151 <Content name="LinkedSource"><null></null></Content>
152 <string name="Name">DefaultEventHandlers</string>
153 <string name="ScriptGuid">{235F4510-3809-4E94-99F5-E7D5EF9E85AF}</string>
154 <ProtectedString name="Source"><![CDATA[-- A fully extensible and type-safe admin-commands console
155-- @documentation https://github.com/evaera/Cmdr/blob/master/README.md
156-- @source https://github.com/evaera/Cmdr
157-- @rostrap Cmdr
158-- @author evaera
159
160local StarterGui = game:GetService("StarterGui")
161local Window = require(script.Parent.CmdrInterface.Window)
162
163return function (Cmdr)
164 Cmdr:HandleEvent("Message", function (text)
165 StarterGui:SetCore("ChatMakeSystemMessage", {
166 Text = ("[Announcement] %s"):format(text);
167 Color = Color3.fromRGB(249, 217, 56);
168 })
169 end)
170
171 Cmdr:HandleEvent("AddLine", function (...)
172 Window:AddLine(...)
173 end)
174end]]></ProtectedString>
175 <BinaryString name="Tags"></BinaryString>
176 </Properties>
177 </Item>
178 <Item class="ModuleScript" referent="RBX326A7781248546519AAD866FC9257B72">
179 <Properties>
180 <Content name="LinkedSource"><null></null></Content>
181 <string name="Name">CmdrInterface</string>
182 <string name="ScriptGuid">{7E21D3D2-A7E4-4D31-9C6D-63FCF89BBB81}</string>
183 <ProtectedString name="Source"><![CDATA[-- A fully extensible and type-safe admin-commands console
184-- @documentation https://github.com/evaera/Cmdr/blob/master/README.md
185-- @source https://github.com/evaera/Cmdr
186-- @rostrap Cmdr
187-- @author evaera
188
189-- Here be dragons
190
191local Players = game:GetService("Players")
192local Player = Players.LocalPlayer
193
194return function (Cmdr)
195 local Util = Cmdr.Util
196
197 local Window = require(script:WaitForChild("Window"))
198 Window.Cmdr = Cmdr
199
200 local AutoComplete = require(script:WaitForChild("AutoComplete"))(Cmdr)
201 Window.AutoComplete = AutoComplete
202
203
204 -- Sets the Window.ProcessEntry callback so that we can dispatch our commands out
205 function Window.ProcessEntry(text)
206 text = Util.TrimString(text)
207
208 if #text == 0 then return end
209
210 Window:AddLine(Window:GetLabel() .. " " .. text, Color3.fromRGB(255, 223, 93))
211
212 Window:AddLine(Cmdr.Dispatcher:EvaluateAndRun(text, Player, {
213 IsHuman = true
214 }))
215 end
216
217 -- Sets the Window.OnTextChanged callback so we can update the auto complete
218 function Window.OnTextChanged (text)
219 local command = Cmdr.Dispatcher:Evaluate(text, Player, true)
220 local arguments = Util.SplitString(text)
221 local commandText = table.remove(arguments, 1)
222 local atEnd = false
223 if command then
224 arguments = Util.MashExcessArguments(arguments, #command.Object.Args)
225
226 atEnd = #arguments == #command.Object.Args
227 end
228
229 local entryComplete = commandText and #arguments > 0
230
231 if text:sub(#text, #text):match("%s") and not atEnd then
232 entryComplete = true
233 arguments[#arguments + 1] = ""
234 end
235
236 if command and entryComplete then
237 local commandValid, errorText = command:Validate()
238
239 Window:SetIsValidInput(commandValid, ("Validation errors: %s"):format(errorText or ""))
240
241 local acItems = {}
242
243 local lastArgument = command:GetArgument(#arguments)
244 if lastArgument then
245 local typedText = lastArgument.RawSegments[#lastArgument.RawSegments]
246 local items = lastArgument:GetAutocomplete()
247 for i, item in pairs(items) do
248 acItems[i] = {typedText, item}
249 end
250
251 local valid = true
252
253 if #typedText > 0 then
254 valid, errorText = lastArgument:Validate()
255 end
256
257 return AutoComplete:Show(acItems, {
258 at = atEnd and #text - #typedText + (text:sub(#text, #text):match("%s") and -1 or 0);
259 prefix = #lastArgument.RawSegments == 1 and lastArgument.Prefix or "";
260 isLast = #command.Arguments == #command.ArgumentDefinitions and #typedText > 0;
261 numArgs = #arguments;
262 command = command;
263 arg = lastArgument;
264 name = lastArgument.Name .. (lastArgument.Required and "" or "?");
265 type = lastArgument.Type.DisplayName;
266 description = (valid == false and errorText) or lastArgument.Object.Description;
267 invalid = not valid;
268 })
269 end
270 elseif commandText and #arguments == 0 then
271 Window:SetIsValidInput(true)
272 local exactCommand = Cmdr.Registry:GetCommand(commandText)
273 local exactMatch
274 if exactCommand then
275 exactMatch = {exactCommand.Name, exactCommand.Name, options = {
276 name = exactCommand.Name;
277 description = exactCommand.Description;
278 }}
279 end
280
281 local acItems = {exactMatch}
282 for _, cmd in pairs(Cmdr.Registry:GetCommandsAsStrings()) do
283 if commandText:lower() == cmd:lower():sub(1, #commandText) and (exactMatch == nil or exactMatch[1] ~= commandText) then
284 local commandObject = Cmdr.Registry:GetCommand(cmd)
285 acItems[#acItems + 1] = {commandText, cmd, options = {
286 name = commandObject.Name;
287 description = commandObject.Description;
288 }}
289 end
290 end
291
292 return AutoComplete:Show(acItems)
293 end
294
295 Window:SetIsValidInput(false, "Invalid command. Use the help command to see all available commands.")
296 AutoComplete:Hide()
297 end
298
299 Window:UpdateLabel()
300 Window:UpdateWindowHeight()
301
302 return {
303 Window = Window;
304 AutoComplete = AutoComplete;
305 }
306end
307]]></ProtectedString>
308 <BinaryString name="Tags"></BinaryString>
309 </Properties>
310 <Item class="ModuleScript" referent="RBX240D210F33274C2EB2EA753E4EDE69C9">
311 <Properties>
312 <Content name="LinkedSource"><null></null></Content>
313 <string name="Name">AutoComplete</string>
314 <string name="ScriptGuid">{E9F50325-6C3B-45B6-BDF0-44C5C331798A}</string>
315 <ProtectedString name="Source"><![CDATA[-- A fully extensible and type-safe admin-commands console
316-- @documentation https://github.com/evaera/Cmdr/blob/master/README.md
317-- @source https://github.com/evaera/Cmdr
318-- @rostrap Cmdr
319-- @author evaera
320
321-- luacheck: ignore 212
322local Players = game:GetService("Players")
323local Player = Players.LocalPlayer
324
325return function (Cmdr)
326 local AutoComplete = {
327 Items = {};
328 ItemOptions = {};
329 SelectedItem = 0;
330 }
331
332 local Util = Cmdr.Util
333 local Shorthands = Util.MakeDictionary({"me", "all", ".", "*", "others"})
334
335 local Gui = Player:WaitForChild("PlayerGui"):WaitForChild("Cmdr"):WaitForChild("Autocomplete")
336 local AutoItem = Gui:WaitForChild("TextButton")
337 local Title = Gui:WaitForChild("Title")
338 local Description = Gui:WaitForChild("Description")
339 local Entry = Gui.Parent:WaitForChild("Frame"):WaitForChild("Entry")
340 AutoItem.Parent = nil
341
342 -- Helper function that sets text and resizes labels
343 local function SetText(obj, textObj, text, sizeFromContents)
344 obj.Visible = text ~= nil
345 textObj.Text = text or ""
346
347 if sizeFromContents then
348 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)
349 end
350 end
351
352 -- Update the info display (Name, type, and description) based on given options.
353 local function UpdateInfoDisplay (options)
354 -- Update the objects' text and sizes
355 SetText(Title, Title.Field, options.name, true)
356 SetText(Title.Field.Type, Title.Field.Type, options.type and ": " .. options.type:sub(1, 1):upper() .. options.type:sub(2))
357 SetText(Description, Description.Label, options.description)
358
359 Description.Label.TextColor3 = options.invalid and Color3.fromRGB(255, 73, 73) or Color3.fromRGB(255, 255, 255)
360
361 -- Calculate needed width and height
362 local infoWidth = Title.Field.TextBounds.X + Title.Field.Type.TextBounds.X
363
364 local guiWidth = math.max(infoWidth, Gui.Size.X.Offset)
365 Description.Size = UDim2.new(1, 0, 0, 40)
366
367 -- Flow description text
368 while not Description.Label.TextFits do
369 Description.Size = Description.Size + UDim2.new(0, 0, 0, 2)
370
371 if Description.Size.Y.Offset > 500 then
372 break
373 end
374 end
375
376 -- Update container
377 wait()
378 Gui.UIListLayout:ApplyLayout()
379 Gui.Size = UDim2.new(0, guiWidth, 0, Gui.UIListLayout.AbsoluteContentSize.Y)
380 end
381
382 --- Shows the auto complete menu with the given list and possible options
383 -- item = {typedText, suggestedText, options?=options}
384 -- The options table is optional. `at` should only be passed into AutoComplete::Show
385 -- name, type, and description may be passed in an options dictionary inside the items as well
386 -- options.at?: the character index at which to show the menu
387 -- options.name?: The name to display in the info box
388 -- options.type?: The type to display in the info box
389 -- options.prefix?: The current type prefix (%Team)
390 -- options.description?: The description for the currently active info box
391 -- options.invalid?: If true, description is shown in red.
392 -- options.isLast?: If true, auto complete won't keep going after this argument.
393 function AutoComplete:Show (items, options)
394 options = options or {}
395
396 -- Remove old options.
397 for _, item in pairs(self.Items) do
398 if item.gui then
399 item.gui:Destroy()
400 end
401 end
402
403 -- Reset state
404 self.SelectedItem = 1
405 self.Items = items
406 self.Prefix = options.prefix or ""
407 self.LastItem = options.isLast or false
408 self.Command = options.command
409 self.Arg = options.arg
410 self.NumArgs = options.numArgs
411
412 -- Generate the new option labels
413 local autocompleteWidth = 200
414
415 for i, item in pairs(self.Items) do
416 local leftText = item[1]
417 local rightText = item[2]
418
419 if Shorthands[leftText] then
420 leftText = rightText
421 end
422
423 local btn = AutoItem:Clone()
424 btn.Name = leftText .. rightText
425 btn.BackgroundTransparency = i == self.SelectedItem and 0.5 or 1
426 btn.Typed.Text = leftText
427 btn.Suggest.Text = string.rep(" ", #leftText) .. rightText:sub(#leftText + 1)
428 btn.Parent = Gui
429 btn.LayoutOrder = i
430
431 local maxBounds = math.max(btn.Typed.TextBounds.X, btn.Suggest.TextBounds.X) + 20
432 if maxBounds > autocompleteWidth then
433 autocompleteWidth = maxBounds
434 end
435
436 item.gui = btn
437 end
438
439 Gui.UIListLayout:ApplyLayout()
440
441 -- Todo: Use TextService to find accurate position for auto complete box
442 local text = Entry.TextBox.Text
443 local words = Util.SplitString(text)
444 if text:sub(#text, #text) == " " and not options.at then
445 words[#words+1] = "e"
446 end
447 table.remove(words, #words)
448 local extra = (options.at and options.at or (#table.concat(words, " ") + 1)) * 7
449
450 -- Update the auto complete container
451 Gui.Position = UDim2.new(0, Entry.TextBox.AbsolutePosition.X - 10 + extra, 0, Entry.TextBox.AbsolutePosition.Y + 30)
452 Gui.Size = UDim2.new(0, autocompleteWidth, 0, Gui.UIListLayout.AbsoluteContentSize.Y)
453 Gui.Visible = true
454
455 -- Finally, update thge info display
456 UpdateInfoDisplay(self.Items[1] and self.Items[1].options or options)
457 end
458
459 --- Returns the selected item in the auto complete
460 function AutoComplete:GetSelectedItem ()
461 if Gui.Visible == false then
462 return nil
463 end
464
465 return AutoComplete.Items[AutoComplete.SelectedItem]
466 end
467
468 --- Hides the auto complete
469 function AutoComplete:Hide ()
470 Gui.Visible = false
471 end
472
473 --- Returns if the menu is visible
474 function AutoComplete:IsVisible ()
475 return Gui.Visible
476 end
477
478 --- Changes the user's item selection by the given delta
479 function AutoComplete:Select (delta)
480 if not Gui.Visible then return end
481
482 self.SelectedItem = self.SelectedItem + delta
483
484 if self.SelectedItem > #self.Items then
485 self.SelectedItem = 1
486 elseif self.SelectedItem < 1 then
487 self.SelectedItem = #self.Items
488 end
489
490 for i, item in pairs(self.Items) do
491 item.gui.BackgroundTransparency = i == self.SelectedItem and 0.5 or 1
492 end
493
494 if self.Items[self.SelectedItem] and self.Items[self.SelectedItem].options then
495 UpdateInfoDisplay(self.Items[self.SelectedItem].options or {})
496 end
497 end
498
499 return AutoComplete
500end
501]]></ProtectedString>
502 <BinaryString name="Tags"></BinaryString>
503 </Properties>
504 </Item>
505 <Item class="ModuleScript" referent="RBX608CDE2293BD49E3B78C34371D6C8E65">
506 <Properties>
507 <Content name="LinkedSource"><null></null></Content>
508 <string name="Name">Window</string>
509 <string name="ScriptGuid">{6D536A57-C13E-4C1A-9E42-10CA9207B1CA}</string>
510 <ProtectedString name="Source"><![CDATA[-- A fully extensible and type-safe admin-commands console
511-- @documentation https://github.com/evaera/Cmdr/blob/master/README.md
512-- @source https://github.com/evaera/Cmdr
513-- @rostrap Cmdr
514-- @author evaera
515
516-- Here be dragons
517-- luacheck: ignore 212
518local GuiService = game:GetService("GuiService")
519local UserInputService = game:GetService("UserInputService")
520local TextService = game:GetService("TextService")
521local Players = game:GetService("Players")
522local Player = Players.LocalPlayer
523
524local LINE_HEIGHT = 20
525local WINDOW_MAX_HEIGHT = 300
526
527--- Window handles the command bar GUI
528local Window = {
529 Valid = true,
530 AutoComplete = nil,
531 ProcessEntry = nil,
532 OnTextChanged = nil,
533 Cmdr = nil,
534 HistoryState = nil,
535}
536
537local Gui = Player:WaitForChild("PlayerGui"):WaitForChild("Cmdr"):WaitForChild("Frame")
538local Line = Gui:WaitForChild("Line")
539local Entry = Gui:WaitForChild("Entry")
540
541Line.Parent = nil
542
543--- Update the text entry label
544function Window:UpdateLabel()
545 Entry.TextLabel.Text = Player.Name .. "@" .. self.Cmdr.PlaceName .. "$"
546 Entry.TextLabel.Size = UDim2.new(0, Entry.TextLabel.TextBounds.X, 0, 20)
547 Entry.TextBox.Position = UDim2.new(0, Entry.TextLabel.Size.X.Offset + 7, 0, 0)
548end
549
550--- Get the text entry label
551function Window:GetLabel()
552 return Entry.TextLabel.Text
553end
554
555--- Recalculate the window height
556function Window:UpdateWindowHeight()
557 local windowHeight = LINE_HEIGHT
558
559 for _, child in pairs(Gui:GetChildren()) do
560 if child:IsA("GuiObject") then
561 windowHeight = windowHeight + child.Size.Y.Offset
562 end
563 end
564
565 Gui.CanvasSize = UDim2.new(Gui.CanvasSize.X.Scale, Gui.CanvasSize.X.Offset, 0, windowHeight)
566 Gui.Size =
567 UDim2.new(
568 Gui.Size.X.Scale,
569 Gui.Size.X.Offset,
570 0,
571 windowHeight > WINDOW_MAX_HEIGHT and WINDOW_MAX_HEIGHT or windowHeight
572 )
573
574 Gui.CanvasPosition = Vector2.new(0, math.clamp(windowHeight - 300, 0, math.huge))
575end
576
577--- Add a line to the command bar
578function Window:AddLine(text, color)
579 if #text == 0 then
580 Window:UpdateWindowHeight()
581 return
582 end
583
584 local str = self.Cmdr.Util.EmulateTabstops(text or "nil", 8)
585 local line = Line:Clone()
586 line.Size =
587 UDim2.new(
588 line.Size.X.Scale,
589 line.Size.X.Offset,
590 0,
591 TextService:GetTextSize(
592 str,
593 line.TextSize,
594 line.Font,
595 Vector2.new(Gui.UIListLayout.AbsoluteContentSize.X, math.huge)
596 ).Y + (LINE_HEIGHT - line.TextSize)
597 )
598 line.Text = str
599 line.TextColor3 = color or line.TextColor3
600 line.Parent = Gui
601end
602
603--- Returns if the command bar is visible
604function Window:IsVisible()
605 return Gui.Visible
606end
607
608--- Sets the command bar visible or not
609function Window:SetVisible(visible)
610 Gui.Visible = visible
611
612 if visible then
613 Entry.TextBox:CaptureFocus()
614 self:SetEntryText("")
615
616 if self.Cmdr.ActivationUnlocksMouse then
617 self.PreviousMouseBehavior = UserInputService.MouseBehavior
618 UserInputService.MouseBehavior = Enum.MouseBehavior.Default
619 end
620 else
621 Entry.TextBox:ReleaseFocus()
622 self.AutoComplete:Hide()
623
624 if self.PreviousMouseBehavior then
625 UserInputService.MouseBehavior = self.PreviousMouseBehavior
626 self.PreviousMouseBehavior = nil
627 end
628 end
629end
630
631--- Hides the command bar
632function Window:Hide()
633 return self:SetVisible(false)
634end
635
636--- Shows the command bar
637function Window:Show()
638 return self:SetVisible(true)
639end
640
641--- Sets the text in the command bar text box, and captures focus
642function Window:SetEntryText(text)
643 Entry.TextBox.Text = text
644
645 if self:IsVisible() then
646 Entry.TextBox:CaptureFocus()
647 end
648end
649
650--- Gets the text in the command bar text box
651function Window:GetEntryText()
652 return Entry.TextBox.Text:gsub("\t", "")
653end
654
655--- Sets whether the command is in a valid state or not.
656-- Cannot submit if in invalid state.
657function Window:SetIsValidInput(isValid, errorText)
658 Entry.TextBox.TextColor3 = isValid and Color3.fromRGB(255, 255, 255) or Color3.fromRGB(255, 73, 73)
659 self.Valid = isValid
660 self._errorText = errorText
661end
662
663--- Event handler for text box focus lost
664function Window:LoseFocus(submit)
665 local text = Entry.TextBox.Text
666
667 self:ClearHistoryState()
668
669 if Gui.Visible and not GuiService.MenuIsOpen then
670 -- self:SetEntryText("")
671 Entry.TextBox:CaptureFocus()
672 elseif GuiService.MenuIsOpen and Gui.Visible then
673 self:Hide()
674 end
675
676 if submit and self.Valid then
677 wait()
678 self:SetEntryText("")
679 self.ProcessEntry(text)
680 elseif submit then
681 self:AddLine(self._errorText, Color3.fromRGB(255, 153, 153))
682 end
683end
684
685function Window:TraverseHistory(delta)
686 local history = self.Cmdr.Dispatcher:GetHistory()
687
688 if self.HistoryState == nil then
689 self.HistoryState = {
690 Position = #history + 1;
691 InitialText = self:GetEntryText();
692 }
693 end
694
695 self.HistoryState.Position = math.clamp(self.HistoryState.Position + delta, 1, #history + 1)
696
697 self:SetEntryText(
698 self.HistoryState.Position == #history + 1
699 and self.HistoryState.InitialText
700 or history[self.HistoryState.Position]
701 )
702end
703
704function Window:ClearHistoryState()
705 self.HistoryState = nil
706end
707
708function Window:SelectVertical(delta)
709 if self.AutoComplete:IsVisible() and not self.HistoryState then
710 self.AutoComplete:Select(delta)
711 else
712 self:TraverseHistory(delta)
713 end
714end
715
716local lastPressTime = 0
717local pressCount = 0
718--- Handles user input when the box is focused
719function Window:BeginInput(input, gameProcessed)
720 if GuiService.MenuIsOpen then
721 self:Hide()
722 end
723
724 if gameProcessed and self:IsVisible() == false then
725 return
726 end
727
728 if self.Cmdr.ActivationKeys[input.KeyCode] then -- Activate the command bar
729 if self.Cmdr.MashToEnable and not self.Cmdr.Enabled then
730 if tick() - lastPressTime < 1 then
731 if pressCount >= 5 then
732 return self.Cmdr:SetEnabled(true)
733 else
734 pressCount = pressCount + 1
735 end
736 else
737 pressCount = 1
738 end
739 lastPressTime = tick()
740 elseif self.Cmdr.Enabled then
741 self:SetVisible(not self:IsVisible())
742 wait()
743 self:SetEntryText("")
744
745 if GuiService.MenuIsOpen then -- Special case for menu getting stuck open (roblox bug)
746 self:Hide()
747 end
748 end
749
750 return
751 end
752
753 if self.Cmdr.Enabled == false or not self:IsVisible() then
754 if self:IsVisible() then
755 self:Hide()
756 end
757
758 return
759 end
760
761 if input.KeyCode == Enum.KeyCode.Down then -- Auto Complete Down
762 self:SelectVertical(1)
763 elseif input.KeyCode == Enum.KeyCode.Up then -- Auto Complete Up
764 self:SelectVertical(-1)
765 elseif input.KeyCode == Enum.KeyCode.Return then -- Eat new lines
766 wait()
767 self:SetEntryText(self:GetEntryText():gsub("\n", ""):gsub("\r", ""))
768 elseif input.KeyCode == Enum.KeyCode.Tab then -- Auto complete
769 local item = self.AutoComplete:GetSelectedItem()
770 local text = self:GetEntryText()
771 if item and not (text:sub(#text, #text):match("%s") and self.AutoComplete.LastItem) then
772 local replace = item[2]
773 local newText
774 local insertSpace = true
775 local command = self.AutoComplete.Command
776
777 if command then
778 local lastArg = self.AutoComplete.Arg
779
780 newText = command.Alias
781 insertSpace = self.AutoComplete.NumArgs ~= #command.ArgumentDefinitions
782
783 local args = command.Arguments
784 for i = 1, #args do
785 local arg = args[i]
786 local segments = arg.RawSegments
787 if arg == lastArg then
788 segments[#segments] = replace
789 end
790
791 local argText = arg.Prefix .. table.concat(segments, ",")
792
793 -- Put auto completion options in quotation marks if they have a space
794 if argText:find(" ") then
795 argText = ("%q"):format(argText)
796 end
797
798 newText = ("%s %s"):format(newText, argText)
799
800 if arg == lastArg then
801 break
802 end
803 end
804 else
805 newText = replace
806 end
807 -- need to wait a frame so we can eat the \t
808 wait()
809 -- Update the text box
810 self:SetEntryText(newText .. (insertSpace and " " or ""))
811 else
812 -- Still need to eat the \t even if there is no auto-complete to show
813 wait()
814 self:SetEntryText(self:GetEntryText())
815 end
816 else
817 self:ClearHistoryState()
818 end
819end
820
821-- Hook events
822Entry.TextBox.FocusLost:Connect(
823 function(submit)
824 return Window:LoseFocus(submit)
825 end
826)
827
828UserInputService.InputBegan:Connect(
829 function(input, gameProcessed)
830 return Window:BeginInput(input, gameProcessed)
831 end
832)
833
834Entry.TextBox:GetPropertyChangedSignal("Text"):Connect(
835 function()
836 if Entry.TextBox.Text:match("\t") then -- Eat \t
837 Entry.TextBox.Text = Entry.TextBox.Text:gsub("\t", "")
838 return
839 end
840 if Window.OnTextChanged then
841 return Window.OnTextChanged(Entry.TextBox.Text)
842 end
843 end
844)
845
846Gui.ChildAdded:Connect(Window.UpdateWindowHeight)
847
848return Window
849]]></ProtectedString>
850 <BinaryString name="Tags"></BinaryString>
851 </Properties>
852 </Item>
853 </Item>
854 <Item class="ModuleScript" referent="RBX3703B004929C4F1396DD20F7DAF0F8EF">
855 <Properties>
856 <Content name="LinkedSource"><null></null></Content>
857 <string name="Name">CreateGui</string>
858 <string name="ScriptGuid">{6AAC372A-F4F1-4F9E-B52F-81CAA1AC3127}</string>
859 <ProtectedString name="Source"><![CDATA[-- A fully extensible and type-safe admin-commands console
860-- @documentation https://github.com/evaera/Cmdr/blob/master/README.md
861-- @source https://github.com/evaera/Cmdr
862-- @rostrap Cmdr
863-- @author evaera
864
865--[[
866 DO NOT MODIFY. This file is auto-generated.
867 Plugin: https://www.roblox.com/library/2307140444/Object-to-Lua
868]]
869
870return function ()
871 local Cmdr = Instance.new("ScreenGui")
872 Cmdr.DisplayOrder = 1000
873 Cmdr.Name = "Cmdr"
874 Cmdr.ResetOnSpawn = false
875
876 local Frame = Instance.new("ScrollingFrame")
877 Frame.BackgroundColor3 = Color3.fromRGB(17, 17, 17)
878 Frame.BackgroundTransparency = 0.4
879 Frame.BorderSizePixel = 0
880 Frame.CanvasSize = UDim2.new(0, 0, 0, 100)
881 Frame.Name = "Frame"
882 Frame.Position = UDim2.new(0.025, 0, 0, 25)
883 Frame.ScrollBarThickness = 6
884 Frame.ScrollingDirection = Enum.ScrollingDirection.Y
885 Frame.Selectable = false
886 Frame.Size = UDim2.new(0.95, 0, 0, 50)
887 Frame.Visible = false
888 Frame.Parent = Cmdr
889
890 local Autocomplete = Instance.new("Frame")
891 Autocomplete.BackgroundColor3 = Color3.fromRGB(59, 59, 59)
892 Autocomplete.BackgroundTransparency = 0.5
893 Autocomplete.BorderSizePixel = 0
894 Autocomplete.Name = "Autocomplete"
895 Autocomplete.Position = UDim2.new(0, 167, 0, 75)
896 Autocomplete.Size = UDim2.new(0, 200, 0, 200)
897 Autocomplete.Visible = false
898 Autocomplete.Parent = Cmdr
899
900 local UIListLayout = Instance.new("UIListLayout")
901 UIListLayout.SortOrder = Enum.SortOrder.LayoutOrder
902 UIListLayout.Parent = Frame
903
904 local Line = Instance.new("TextLabel")
905 Line.BackgroundColor3 = Color3.fromRGB(255, 255, 255)
906 Line.BackgroundTransparency = 1
907 Line.Font = Enum.Font.Code
908 Line.Name = "Line"
909 Line.Size = UDim2.new(1, 0, 0, 20)
910 Line.TextColor3 = Color3.fromRGB(255, 255, 255)
911 Line.TextSize = 14
912 Line.TextXAlignment = Enum.TextXAlignment.Left
913 Line.Parent = Frame
914
915 local UIPadding = Instance.new("UIPadding")
916 UIPadding.PaddingBottom = UDim.new(0, 10)
917 UIPadding.PaddingLeft = UDim.new(0, 10)
918 UIPadding.PaddingRight = UDim.new(0, 10)
919 UIPadding.PaddingTop = UDim.new(0, 10)
920 UIPadding.Parent = Frame
921
922 local Entry = Instance.new("Frame")
923 Entry.BackgroundTransparency = 1
924 Entry.LayoutOrder = 999999999
925 Entry.Name = "Entry"
926 Entry.Size = UDim2.new(1, 0, 0, 20)
927 Entry.Parent = Frame
928
929 local UIListLayout2 = Instance.new("UIListLayout")
930 UIListLayout2.SortOrder = Enum.SortOrder.LayoutOrder
931 UIListLayout2.Parent = Autocomplete
932
933 local Title = Instance.new("Frame")
934 Title.BackgroundColor3 = Color3.fromRGB(59, 59, 59)
935 Title.BackgroundTransparency = 0.2
936 Title.BorderSizePixel = 0
937 Title.LayoutOrder = -2
938 Title.Name = "Title"
939 Title.Size = UDim2.new(1, 0, 0, 40)
940 Title.Parent = Autocomplete
941
942 local Description = Instance.new("Frame")
943 Description.BackgroundColor3 = Color3.fromRGB(59, 59, 59)
944 Description.BackgroundTransparency = 0.2
945 Description.BorderSizePixel = 0
946 Description.LayoutOrder = -1
947 Description.Name = "Description"
948 Description.Size = UDim2.new(1, 0, 0, 20)
949 Description.Parent = Autocomplete
950
951 local TextButton = Instance.new("TextButton")
952 TextButton.BackgroundColor3 = Color3.fromRGB(59, 59, 59)
953 TextButton.BackgroundTransparency = 0.5
954 TextButton.BorderSizePixel = 0
955 TextButton.Font = Enum.Font.Code
956 TextButton.Size = UDim2.new(1, 0, 0, 30)
957 TextButton.Text = ""
958 TextButton.TextColor3 = Color3.fromRGB(255, 255, 255)
959 TextButton.TextSize = 14
960 TextButton.TextXAlignment = Enum.TextXAlignment.Left
961 TextButton.Parent = Autocomplete
962
963 local TextBox = Instance.new("TextBox")
964 TextBox.BackgroundColor3 = Color3.fromRGB(255, 255, 255)
965 TextBox.BackgroundTransparency = 1
966 TextBox.ClearTextOnFocus = false
967 TextBox.Font = Enum.Font.Code
968 TextBox.LayoutOrder = 999999999
969 TextBox.Position = UDim2.new(0, 140, 0, 0)
970 TextBox.Size = UDim2.new(1, 0, 0, 20)
971 TextBox.Text = "x"
972 TextBox.TextColor3 = Color3.fromRGB(255, 255, 255)
973 TextBox.TextSize = 14
974 TextBox.TextXAlignment = Enum.TextXAlignment.Left
975 TextBox.Parent = Entry
976
977 local TextLabel = Instance.new("TextLabel")
978 TextLabel.BackgroundColor3 = Color3.fromRGB(255, 255, 255)
979 TextLabel.BackgroundTransparency = 1
980 TextLabel.Font = Enum.Font.Code
981 TextLabel.Size = UDim2.new(0, 133, 0, 20)
982 TextLabel.Text = ""
983 TextLabel.TextColor3 = Color3.fromRGB(255, 223, 93)
984 TextLabel.TextSize = 14
985 TextLabel.TextXAlignment = Enum.TextXAlignment.Left
986 TextLabel.Parent = Entry
987
988 local Field = Instance.new("TextLabel")
989 Field.BackgroundColor3 = Color3.fromRGB(255, 255, 255)
990 Field.BackgroundTransparency = 1
991 Field.Font = Enum.Font.SourceSansBold
992 Field.Name = "Field"
993 Field.Size = UDim2.new(0, 37, 1, 0)
994 Field.Text = "from"
995 Field.TextColor3 = Color3.fromRGB(255, 255, 255)
996 Field.TextSize = 20
997 Field.TextXAlignment = Enum.TextXAlignment.Left
998 Field.Parent = Title
999
1000 local UIPadding2 = Instance.new("UIPadding")
1001 UIPadding2.PaddingLeft = UDim.new(0, 10)
1002 UIPadding2.Parent = Title
1003
1004 local Label = Instance.new("TextLabel")
1005 Label.BackgroundColor3 = Color3.fromRGB(255, 255, 255)
1006 Label.BackgroundTransparency = 1
1007 Label.Font = Enum.Font.SourceSansLight
1008 Label.Name = "Label"
1009 Label.Size = UDim2.new(1, 0, 1, 0)
1010 Label.Text = "The players to teleport. The players to teleport. The players to teleport. The players to teleport. "
1011 Label.TextColor3 = Color3.fromRGB(255, 255, 255)
1012 Label.TextSize = 16
1013 Label.TextWrapped = true
1014 Label.TextXAlignment = Enum.TextXAlignment.Left
1015 Label.TextYAlignment = Enum.TextYAlignment.Top
1016 Label.Parent = Description
1017
1018 local UIPadding3 = Instance.new("UIPadding")
1019 UIPadding3.PaddingBottom = UDim.new(0, 10)
1020 UIPadding3.PaddingLeft = UDim.new(0, 10)
1021 UIPadding3.PaddingRight = UDim.new(0, 10)
1022 UIPadding3.Parent = Description
1023
1024 local Typed = Instance.new("TextLabel")
1025 Typed.BackgroundColor3 = Color3.fromRGB(255, 255, 255)
1026 Typed.BackgroundTransparency = 1
1027 Typed.Font = Enum.Font.Code
1028 Typed.Name = "Typed"
1029 Typed.Size = UDim2.new(1, 0, 1, 0)
1030 Typed.Text = "Lab"
1031 Typed.TextColor3 = Color3.fromRGB(131, 222, 255)
1032 Typed.TextSize = 14
1033 Typed.TextXAlignment = Enum.TextXAlignment.Left
1034 Typed.Parent = TextButton
1035
1036 local UIPadding4 = Instance.new("UIPadding")
1037 UIPadding4.PaddingLeft = UDim.new(0, 10)
1038 UIPadding4.Parent = TextButton
1039
1040 local Suggest = Instance.new("TextLabel")
1041 Suggest.BackgroundColor3 = Color3.fromRGB(255, 255, 255)
1042 Suggest.BackgroundTransparency = 1
1043 Suggest.Font = Enum.Font.Code
1044 Suggest.Name = "Suggest"
1045 Suggest.Size = UDim2.new(1, 0, 1, 0)
1046 Suggest.Text = " el"
1047 Suggest.TextColor3 = Color3.fromRGB(255, 255, 255)
1048 Suggest.TextSize = 14
1049 Suggest.TextXAlignment = Enum.TextXAlignment.Left
1050 Suggest.Parent = TextButton
1051
1052 local Type = Instance.new("TextLabel")
1053 Type.BackgroundColor3 = Color3.fromRGB(255, 255, 255)
1054 Type.BackgroundTransparency = 1
1055 Type.BorderColor3 = Color3.fromRGB(255, 153, 153)
1056 Type.Font = Enum.Font.SourceSans
1057 Type.Name = "Type"
1058 Type.Position = UDim2.new(1, 0, 0, 0)
1059 Type.Size = UDim2.new(0, 0, 1, 0)
1060 Type.Text = ": Players"
1061 Type.TextColor3 = Color3.fromRGB(255, 255, 255)
1062 Type.TextSize = 15
1063 Type.TextXAlignment = Enum.TextXAlignment.Left
1064 Type.Parent = Field
1065
1066 Cmdr.Parent = game:GetService('Players').LocalPlayer.PlayerGui
1067 return Cmdr
1068end]]></ProtectedString>
1069 <BinaryString name="Tags"></BinaryString>
1070 </Properties>
1071 </Item>
1072 <Item class="Folder" referent="RBX47F98EC7EB6A4D6591877ED2C63BF730">
1073 <Properties>
1074 <string name="Name">Commands</string>
1075 <BinaryString name="Tags"></BinaryString>
1076 </Properties>
1077 <Item class="Folder" referent="RBX4731997E92A644028AD089E27BBDAA2C">
1078 <Properties>
1079 <string name="Name">Def</string>
1080 <BinaryString name="Tags"></BinaryString>
1081 </Properties>
1082 <Item class="ModuleScript" referent="RBXFD455615CEE34E55AB6C865CE2EA0E86">
1083 <Properties>
1084 <Content name="LinkedSource"><null></null></Content>
1085 <string name="Name">alias</string>
1086 <string name="ScriptGuid">{B21018D9-75E4-4E2E-847F-722175801FE8}</string>
1087 <ProtectedString name="Source"><![CDATA[-- A fully extensible and type-safe admin-commands console
1088-- @documentation https://github.com/evaera/Cmdr/blob/master/README.md
1089-- @source https://github.com/evaera/Cmdr
1090-- @rostrap Cmdr
1091-- @author evaera
1092
1093return {
1094 Name = "alias";
1095 Aliases = {};
1096 Description = "Creates a new, single command out of a command and given arguments.";
1097 Group = "DefaultUtil";
1098 Args = {
1099 {
1100 Type = "string";
1101 Name = "Alias name";
1102 Description = "The key or input type you'd like to bind the command to."
1103 },
1104 {
1105 Type = "string";
1106 Name = "Command string";
1107 Description = "The command text you want to run. Separate multiple commands with \"&&\". Accept arguments with $1, $2, $3, etc."
1108 },
1109 };
1110
1111 Run = function(context, name, commandString)
1112 context.Cmdr.Registry:RegisterCommandObject(
1113 context.Cmdr.Util.MakeAliasCommand(name, commandString)
1114 )
1115
1116 return ("Created alias %q"):format(name)
1117 end
1118}]]></ProtectedString>
1119 <BinaryString name="Tags"></BinaryString>
1120 </Properties>
1121 </Item>
1122 <Item class="ModuleScript" referent="RBXFD7DD76329D8401487311567F1117501">
1123 <Properties>
1124 <Content name="LinkedSource"><null></null></Content>
1125 <string name="Name">bind</string>
1126 <string name="ScriptGuid">{ACEB33F1-B5FC-44FE-A1BD-767A8A49B89E}</string>
1127 <ProtectedString name="Source"><![CDATA[-- A fully extensible and type-safe admin-commands console
1128-- @documentation https://github.com/evaera/Cmdr/blob/master/README.md
1129-- @source https://github.com/evaera/Cmdr
1130-- @rostrap Cmdr
1131-- @author evaera
1132
1133local UserInputService = game:GetService("UserInputService")
1134
1135return {
1136 Name = "bind";
1137 Aliases = {};
1138 Description = "Binds a command string to a key or mouse input.";
1139 Group = "DefaultUtil";
1140 Args = {
1141 {
1142 Type = "userInput ! bindableResource @ player";
1143 Name = "Input";
1144 Description = "The key or input type you'd like to bind the command to."
1145 },
1146 {
1147 Type = "command";
1148 Name = "Command";
1149 Description = "The command you want to run on this input"
1150 },
1151 {
1152 Type = "string";
1153 Name = "Arguments";
1154 Description = "The arguments for the command";
1155 Default = "";
1156 }
1157 };
1158
1159 Run = function(context, bind, command, arguments)
1160 local binds = context:GetStore("CMDR_Binds")
1161
1162 command = command .. " " .. arguments
1163
1164 if binds[bind] then
1165 binds[bind]:Disconnect()
1166 end
1167
1168 local bindType = context:GetArgument(1).Type.Name
1169
1170 if bindType == "userInput" then
1171 binds[bind] = UserInputService.InputBegan:Connect(function(input, gameProcessed)
1172 if gameProcessed then
1173 return
1174 end
1175
1176 if input.UserInputType == bind or input.KeyCode == bind then
1177 context:Reply(context.Dispatcher:EvaluateAndRun(context.Cmdr.Util.RunEmbeddedCommands(context.Dispatcher, command)))
1178 end
1179 end)
1180 elseif bindType == "bindableResource" then
1181 return "Unimplemented..."
1182 elseif bindType == "player" then
1183 binds[bind] = bind.Chatted:Connect(function(message)
1184 local args = { message }
1185 local chatCommand = context.Cmdr.Util.RunEmbeddedCommands(context.Dispatcher, context.Cmdr.Util.SubstituteArgs(command, args))
1186 context:Reply(("%s $ %s : %s"):format(
1187 bind.Name,
1188 chatCommand,
1189 context.Dispatcher:EvaluateAndRun(chatCommand)
1190 ), Color3.fromRGB(244, 92, 66))
1191 end)
1192 end
1193
1194
1195 return "Bound command to input."
1196 end
1197}]]></ProtectedString>
1198 <BinaryString name="Tags"></BinaryString>
1199 </Properties>
1200 </Item>
1201 <Item class="ModuleScript" referent="RBX7A73A217C3A240B28E24B4DC2606B120">
1202 <Properties>
1203 <Content name="LinkedSource"><null></null></Content>
1204 <string name="Name">clear</string>
1205 <string name="ScriptGuid">{B5F9EAF9-D44F-492D-9B6C-AAA30D096857}</string>
1206 <ProtectedString name="Source"><![CDATA[-- A fully extensible and type-safe admin-commands console
1207-- @documentation https://github.com/evaera/Cmdr/blob/master/README.md
1208-- @source https://github.com/evaera/Cmdr
1209-- @rostrap Cmdr
1210-- @author evaera
1211
1212local Players = game:GetService("Players")
1213
1214return {
1215 Name = "clear",
1216 Aliases = {},
1217 Description = "Clear all lines above the entry line of the Cmdr window.",
1218 Group = "DefaultUtil",
1219 Args = {},
1220 Run = function()
1221 local player = Players.LocalPlayer
1222 local gui = player:WaitForChild("PlayerGui"):WaitForChild("Cmdr")
1223 local frame = gui:WaitForChild("Frame")
1224
1225 if gui and frame then
1226 for _, child in pairs(frame:GetChildren()) do
1227 if child.Name == "Line" and child:IsA("TextLabel") then
1228 child:Destroy()
1229 end
1230 end
1231 end
1232 return ""
1233 end
1234}
1235]]></ProtectedString>
1236 <BinaryString name="Tags"></BinaryString>
1237 </Properties>
1238 </Item>
1239 <Item class="ModuleScript" referent="RBX34CA904CCCA54EFE8BE1C154B1A37233">
1240 <Properties>
1241 <Content name="LinkedSource"><null></null></Content>
1242 <string name="Name">echo</string>
1243 <string name="ScriptGuid">{206CE708-94B9-4EF9-99C1-ED0CB0B86997}</string>
1244 <ProtectedString name="Source"><![CDATA[-- A fully extensible and type-safe admin-commands console
1245-- @documentation https://github.com/evaera/Cmdr/blob/master/README.md
1246-- @source https://github.com/evaera/Cmdr
1247-- @rostrap Cmdr
1248-- @author evaera
1249
1250return {
1251 Name = "echo";
1252 Aliases = {};
1253 Description = "Echoes your text back to you.";
1254 Group = "DefaultUtil";
1255 Args = {
1256 {
1257 Type = "string";
1258 Name = "Text";
1259 Description = "The text."
1260 },
1261 };
1262
1263 Run = function(_, text)
1264 return text
1265 end
1266}]]></ProtectedString>
1267 <BinaryString name="Tags"></BinaryString>
1268 </Properties>
1269 </Item>
1270 <Item class="ModuleScript" referent="RBXB3DDC350AF6A40458400D914350D912D">
1271 <Properties>
1272 <Content name="LinkedSource"><null></null></Content>
1273 <string name="Name">history</string>
1274 <string name="ScriptGuid">{F1CDCC16-9A1F-48DD-8A9D-8B8DC961C0BC}</string>
1275 <ProtectedString name="Source"><![CDATA[-- A fully extensible and type-safe admin-commands console
1276-- @documentation https://github.com/evaera/Cmdr/blob/master/README.md
1277-- @source https://github.com/evaera/Cmdr
1278-- @rostrap Cmdr
1279-- @author evaera
1280
1281return {
1282 Name = "history";
1283 Aliases = {};
1284 AutoExec = {
1285 "alias ! run ${history $1}";
1286 "alias ^ run ${run replace ${history -1} $1 $2}";
1287 "alias !! ! -1";
1288 };
1289 Description = "Displays previous commands from history.";
1290 Group = "DefaultUtil";
1291 Args = {
1292 {
1293 Type = "integer";
1294 Name = "Line Number";
1295 Description = "Command line number (can be negative to go from end)"
1296 },
1297 };
1298
1299 Run = function(context, line)
1300 local history = context.Dispatcher:GetHistory()
1301
1302 if line <= 0 then
1303 line = #history + line
1304 end
1305
1306 return history[line] or ""
1307 end
1308}]]></ProtectedString>
1309 <BinaryString name="Tags"></BinaryString>
1310 </Properties>
1311 </Item>
1312 <Item class="ModuleScript" referent="RBX676C36DC4E034FC9BF0B5483872436C9">
1313 <Properties>
1314 <Content name="LinkedSource"><null></null></Content>
1315 <string name="Name">replace</string>
1316 <string name="ScriptGuid">{7DC0CEEF-77FC-4E75-80C3-DF429AF7FE3D}</string>
1317 <ProtectedString name="Source"><![CDATA[-- A fully extensible and type-safe admin-commands console
1318-- @documentation https://github.com/evaera/Cmdr/blob/master/README.md
1319-- @source https://github.com/evaera/Cmdr
1320-- @rostrap Cmdr
1321-- @author evaera
1322
1323return {
1324 Name = "replace";
1325 Aliases = {};
1326 Description = "Replaces text A with text B";
1327 Group = "DefaultUtil";
1328 Args = {
1329 {
1330 Type = "string";
1331 Name = "Haystack";
1332 Description = "The source string upon which to perform replacement."
1333 },
1334 {
1335 Type = "string";
1336 Name = "Needle";
1337 Description = "The string pattern search for."
1338 },
1339 {
1340 Type = "string";
1341 Name = "Replacement";
1342 Description = "The string to replace matches (%1 to insert matches)."
1343 },
1344 };
1345
1346 Run = function(_, haystack, needle, replacement)
1347 return haystack:gsub(needle, replacement)
1348 end
1349}]]></ProtectedString>
1350 <BinaryString name="Tags"></BinaryString>
1351 </Properties>
1352 </Item>
1353 <Item class="ModuleScript" referent="RBXBE09863FAB334CFA9929D6C87FFA2A1C">
1354 <Properties>
1355 <Content name="LinkedSource"><null></null></Content>
1356 <string name="Name">run</string>
1357 <string name="ScriptGuid">{E604A54B-4745-4AF6-A742-9F37223C0E6D}</string>
1358 <ProtectedString name="Source"><![CDATA[-- A fully extensible and type-safe admin-commands console
1359-- @documentation https://github.com/evaera/Cmdr/blob/master/README.md
1360-- @source https://github.com/evaera/Cmdr
1361-- @rostrap Cmdr
1362-- @author evaera
1363
1364return {
1365 Name = "run";
1366 Aliases = {};
1367 AutoExec = {
1368 "alias discard replace ${run $1} .* \\\"\\\""
1369 };
1370 Description = "Runs a given command string (replacing embedded commands).";
1371 Group = "DefaultUtil";
1372 Args = {
1373 {
1374 Type = "string";
1375 Name = "Command";
1376 Description = "The command string to run"
1377 },
1378 };
1379
1380 Run = function(context, command)
1381 return context.Dispatcher:EvaluateAndRun(context.Cmdr.Util.RunEmbeddedCommands(context.Dispatcher, command))
1382 end
1383}]]></ProtectedString>
1384 <BinaryString name="Tags"></BinaryString>
1385 </Properties>
1386 </Item>
1387 <Item class="ModuleScript" referent="RBX692EA8693EDA40CDAE85A5547E3FE20F">
1388 <Properties>
1389 <Content name="LinkedSource"><null></null></Content>
1390 <string name="Name">runif</string>
1391 <string name="ScriptGuid">{C83830D5-BB89-4EC5-8FD7-252E1E0905A6}</string>
1392 <ProtectedString name="Source"><![CDATA[-- A fully extensible and type-safe admin-commands console
1393-- @documentation https://github.com/evaera/Cmdr/blob/master/README.md
1394-- @source https://github.com/evaera/Cmdr
1395-- @rostrap Cmdr
1396-- @author evaera
1397
1398local conditions = {
1399 startsWith = function (text, arg)
1400 if text:sub(1, #arg) == arg then
1401 return text:sub(#arg + 1)
1402 end
1403 end
1404}
1405
1406return {
1407 Name = "runif";
1408 Aliases = {};
1409 Description = "Runs a given command string if a certain condition is met.";
1410 Group = "DefaultUtil";
1411 Args = {
1412 {
1413 Type = "conditionFunction";
1414 Name = "Condition";
1415 Description = "The condition function"
1416 },
1417 {
1418 Type = "string";
1419 Name = "Argument";
1420 Description = "The argument to the condition function"
1421 },
1422 {
1423 Type = "string";
1424 Name = "Test against";
1425 Description = "The text to test against."
1426 },
1427 {
1428 Type = "string";
1429 Name = "Command";
1430 Description = "The command string to run if requirements are met. If omitted, return value from condition function is used.";
1431 Optional = true;
1432 },
1433 };
1434
1435 Run = function(context, condition, arg, testAgainst, command)
1436 local conditionFunc = conditions[condition]
1437
1438 if not conditionFunc then
1439 return ("Condition %q is not valid."):format(condition)
1440 end
1441
1442 local text = conditionFunc(testAgainst, arg)
1443
1444 if text then
1445 return context.Dispatcher:EvaluateAndRun(context.Cmdr.Util.RunEmbeddedCommands(context.Dispatcher, command or text))
1446 end
1447
1448 return ""
1449 end
1450}]]></ProtectedString>
1451 <BinaryString name="Tags"></BinaryString>
1452 </Properties>
1453 </Item>
1454 <Item class="ModuleScript" referent="RBX301C5052F0B7472097D6286887CACD42">
1455 <Properties>
1456 <Content name="LinkedSource"><null></null></Content>
1457 <string name="Name">unbind</string>
1458 <string name="ScriptGuid">{ABEB5DC5-66D5-4685-A15C-AA011202392A}</string>
1459 <ProtectedString name="Source"><![CDATA[-- A fully extensible and type-safe admin-commands console
1460-- @documentation https://github.com/evaera/Cmdr/blob/master/README.md
1461-- @source https://github.com/evaera/Cmdr
1462-- @rostrap Cmdr
1463-- @author evaera
1464
1465return {
1466 Name = "unbind";
1467 Aliases = {};
1468 Description = "Unbinds an input previously bound with Bind";
1469 Group = "DefaultUtil";
1470 Args = {
1471 {
1472 Type = "userInput ! bindableResource @ player";
1473 Name = "Input/Key";
1474 Description = "The key or input type you'd like to unbind."
1475 }
1476 };
1477
1478 Run = function(context, inputEnum)
1479 local binds = context:GetStore("CMDR_Binds")
1480
1481 if binds[inputEnum] then
1482 binds[inputEnum]:Disconnect()
1483 binds[inputEnum] = nil
1484 return "Unbound command from input."
1485 else
1486 return "That input wasn't bound."
1487 end
1488 end
1489}]]></ProtectedString>
1490 <BinaryString name="Tags"></BinaryString>
1491 </Properties>
1492 </Item>
1493 <Item class="ModuleScript" referent="RBX9601429EDA45475CA90D07B97A5CC78B">
1494 <Properties>
1495 <Content name="LinkedSource"><null></null></Content>
1496 <string name="Name">eval</string>
1497 <string name="ScriptGuid">{9AC42E12-BCE6-4328-AB30-043D1EEFDAB9}</string>
1498 <ProtectedString name="Source"><![CDATA[-- A fully extensible and type-safe admin-commands console
1499-- @documentation https://github.com/evaera/Cmdr/blob/master/README.md
1500-- @source https://github.com/evaera/Cmdr
1501-- @rostrap Cmdr
1502-- @author evaera
1503
1504return {
1505 Name = "eval";
1506 Aliases = {"exec"};
1507 Description = "Executes code";
1508 Group = "DefaultUtil";
1509 Args = {
1510 {
1511 Type = "string";
1512 Name = "Source";
1513 Description = "Code you wish to run"
1514 }
1515 };
1516
1517 Run = function(context, src)
1518 local f = context.Cmdr.Loadstring(src)
1519 if f then
1520 local e = getfenv(f)
1521 e.Cmdr = context.Cmdr
1522 e.Context = context
1523
1524 return f()
1525 end
1526
1527 return "Failed to run"
1528 end
1529}]]></ProtectedString>
1530 <BinaryString name="Tags"></BinaryString>
1531 </Properties>
1532 </Item>
1533 <Item class="ModuleScript" referent="RBX825A72F9D24341DA8AF01E1B9F021D27">
1534 <Properties>
1535 <Content name="LinkedSource"><null></null></Content>
1536 <string name="Name">help</string>
1537 <string name="ScriptGuid">{D0768F0F-B8EC-4F54-8FAF-BDAD2E095F8A}</string>
1538 <ProtectedString name="Source"><![CDATA[-- A fully extensible and type-safe admin-commands console
1539-- @documentation https://github.com/evaera/Cmdr/blob/master/README.md
1540-- @source https://github.com/evaera/Cmdr
1541-- @rostrap Cmdr
1542-- @author evaera
1543
1544return {
1545 Name = "help";
1546 Description = "Displays a list of all commands, or inspects one command.";
1547 Group = "Help";
1548 Args = {
1549 {
1550 Type = "command";
1551 Name = "Command";
1552 Description = "The command to view information on";
1553 Optional = true;
1554 },
1555 };
1556
1557 Run = function (context, commandName)
1558 if commandName then
1559 local command = context.Cmdr.Registry:GetCommand(commandName)
1560 context:Reply(("Command: %s"):format(command.Name), Color3.fromRGB(230, 126, 34))
1561 if command.Aliases and #command.Aliases > 0 then
1562 context:Reply(("Aliases: %s"):format(table.concat(command.Aliases, ", ")), Color3.fromRGB(230, 230, 230))
1563 end
1564 context:Reply(command.Description, Color3.fromRGB(230, 230, 230))
1565 for i, arg in ipairs(command.Args) do
1566 context:Reply(("#%d %s%s: %s - %s"):format(
1567 i,
1568 arg.Name,
1569 arg.Optional == true and "?" or "",
1570 arg.Type, arg.Description
1571 ))
1572 end
1573 else
1574 local commands = context.Cmdr.Registry:GetCommands()
1575
1576 for _, cmd in pairs(commands) do
1577 context:Reply(("%s - %s"):format(cmd.Name, cmd.Description))
1578 end
1579 end
1580 return ""
1581 end;
1582}]]></ProtectedString>
1583 <BinaryString name="Tags"></BinaryString>
1584 </Properties>
1585 </Item>
1586 </Item>
1587 <Item class="ModuleScript" referent="RBX1E0E6A007EB24761B56F8943089A7AAE">
1588 <Properties>
1589 <Content name="LinkedSource"><null></null></Content>
1590 <string name="Name">LightsaberBattlegrounds</string>
1591 <string name="ScriptGuid">{792920A8-AE46-412D-8D0B-B4B1581A6BDF}</string>
1592 <ProtectedString name="Source"><![CDATA[return function()
1593 return game.GameId == 1351881085
1594end]]></ProtectedString>
1595 <BinaryString name="Tags"></BinaryString>
1596 </Properties>
1597 <Item class="ModuleScript" referent="RBX99AD8FE961E145D48066AF9CE6A9CDF6">
1598 <Properties>
1599 <Content name="LinkedSource"><null></null></Content>
1600 <string name="Name">Platform</string>
1601 <string name="ScriptGuid">{B2DF7DF4-1B01-4F1E-9CEF-5C9D8D78F6AE}</string>
1602 <ProtectedString name="Source"><![CDATA[return {
1603 Name = "platform";
1604 Aliases = {};
1605 Description = "Controls platform";
1606 Group = "DefaultUtil";
1607 Args = {
1608 {
1609 Type = "string";
1610 Name = "Action";
1611 Description = "Action you wish to execute with the platform"
1612 },
1613 };
1614
1615 Run = function(_, text)
1616
1617 local console = workspace:FindFirstChild('Console', true)
1618
1619 text = text:lower()
1620 if text == 'up' then
1621 click_detector(console.Panel.Up.ClickDetector, 1)
1622 elseif text == 'down' then
1623 click_detector(console.Panel.Down.ClickDetector, 1)
1624 elseif text == 'toggle' then
1625 click_detector(console.Panel.Close.ClickDetector, 1)
1626 end
1627
1628 return "Success"
1629 end
1630}]]></ProtectedString>
1631 <BinaryString name="Tags"></BinaryString>
1632 </Properties>
1633 </Item>
1634 <Item class="ModuleScript" referent="RBX5CAFD43B6C4E442594425E2D9A02C968">
1635 <Properties>
1636 <Content name="LinkedSource"><null></null></Content>
1637 <string name="Name">SetWeaponSpeed</string>
1638 <string name="ScriptGuid">{DFAEA287-2663-4BBE-986D-DEA8E021FAA7}</string>
1639 <ProtectedString name="Source"><![CDATA[return {
1640 Name = "setweaponspeed";
1641 Aliases = {};
1642 Description = "Sets weapon speed";
1643 Group = "DefaultUtil";
1644 Args = {
1645 {
1646 Type = "number";
1647 Name = "Speed";
1648 Description = "New weapon speed"
1649 },
1650 };
1651
1652 Run = function(_, number)
1653 local weapon = game:GetService('Players').LocalPlayer.Character:FindFirstChildWhichIsA('Tool')
1654 if weapon then
1655 weapon.Settings.Speed.Value = number
1656 end
1657 end
1658}
1659
1660
1661]]></ProtectedString>
1662 <BinaryString name="Tags"></BinaryString>
1663 </Properties>
1664 </Item>
1665 <Item class="ModuleScript" referent="RBX1FBA337156B3452BA700423D6B6B5BCE">
1666 <Properties>
1667 <Content name="LinkedSource"><null></null></Content>
1668 <string name="Name">CopyWeaponScript</string>
1669 <string name="ScriptGuid">{CA3B9808-A9B8-4AE1-B4A0-514F62D47602}</string>
1670 <ProtectedString name="Source"><![CDATA[return {
1671 Name = "copyweaponscript";
1672 Aliases = {};
1673 Description = "Copies weapons script n times";
1674 Group = "DefaultUtil";
1675 Args = {
1676 {
1677 Type = "number";
1678 Name = "Amount";
1679 Description = "How many times to copy it"
1680 },
1681 };
1682
1683 Run = function(_, number)
1684 local weapon = game:GetService('Players').LocalPlayer.Character:FindFirstChildWhichIsA('Tool')
1685 if weapon then
1686 for i=1, number do
1687 weapon.SaberLocal:Clone().Parent = weapon
1688 end
1689 end
1690 end
1691}
1692
1693]]></ProtectedString>
1694 <BinaryString name="Tags"></BinaryString>
1695 </Properties>
1696 </Item>
1697 </Item>
1698 </Item>
1699 <Item class="Folder" referent="RBX931A1E5FD65F4602B4D075C659ED04A5">
1700 <Properties>
1701 <string name="Name">Types</string>
1702 <BinaryString name="Tags"></BinaryString>
1703 </Properties>
1704 <Item class="ModuleScript" referent="RBXCEE88289152742518A219084636CF9E1">
1705 <Properties>
1706 <Content name="LinkedSource"><null></null></Content>
1707 <string name="Name">BindableResource</string>
1708 <string name="ScriptGuid">{003CDED7-3D2B-4AE8-8FFE-E754EE143765}</string>
1709 <ProtectedString name="Source"><![CDATA[-- A fully extensible and type-safe admin-commands console
1710-- @documentation https://github.com/evaera/Cmdr/blob/master/README.md
1711-- @source https://github.com/evaera/Cmdr
1712-- @rostrap Cmdr
1713-- @author evaera
1714
1715return function (registry)
1716 registry:RegisterType("bindableResource", registry.Cmdr.Util.MakeEnumType("BindableResource", {"Chat"}))
1717end
1718]]></ProtectedString>
1719 <BinaryString name="Tags"></BinaryString>
1720 </Properties>
1721 </Item>
1722 <Item class="ModuleScript" referent="RBX638C439C98D54A30B32D74C6A8956490">
1723 <Properties>
1724 <Content name="LinkedSource"><null></null></Content>
1725 <string name="Name">BrickColor</string>
1726 <string name="ScriptGuid">{3C24E196-5B6C-4F7F-8015-C41B2B9B37C5}</string>
1727 <ProtectedString name="Source"><![CDATA[-- A fully extensible and type-safe admin-commands console
1728-- @documentation https://github.com/evaera/Cmdr/blob/master/README.md
1729-- @source https://github.com/evaera/Cmdr
1730-- @rostrap Cmdr
1731-- @author evaera
1732
1733local Util = require(script.Parent.Parent.Shared.Util)
1734
1735local brickColorNames = {
1736 "White", "Grey", "Light yellow", "Brick yellow", "Light green (Mint)", "Light reddish violet", "Pastel Blue",
1737 "Light orange brown", "Nougat", "Bright red", "Med. reddish violet", "Bright blue", "Bright yellow", "Earth orange",
1738 "Black", "Dark grey", "Dark green", "Medium green", "Lig. Yellowich orange", "Bright green", "Dark orange",
1739 "Light bluish violet", "Transparent", "Tr. Red", "Tr. Lg blue", "Tr. Blue", "Tr. Yellow", "Light blue",
1740 "Tr. Flu. Reddish orange", "Tr. Green", "Tr. Flu. Green", "Phosph. White", "Light red", "Medium red", "Medium blue",
1741 "Light grey", "Bright violet", "Br. yellowish orange", "Bright orange", "Bright bluish green", "Earth yellow",
1742 "Bright bluish violet", "Tr. Brown", "Medium bluish violet", "Tr. Medi. reddish violet", "Med. yellowish green",
1743 "Med. bluish green", "Light bluish green", "Br. yellowish green", "Lig. yellowish green", "Med. yellowish orange",
1744 "Br. reddish orange", "Bright reddish violet", "Light orange", "Tr. Bright bluish violet", "Gold", "Dark nougat",
1745 "Silver", "Neon orange", "Neon green", "Sand blue", "Sand violet", "Medium orange", "Sand yellow", "Earth blue",
1746 "Earth green", "Tr. Flu. Blue", "Sand blue metallic", "Sand violet metallic", "Sand yellow metallic",
1747 "Dark grey metallic", "Black metallic", "Light grey metallic", "Sand green", "Sand red", "Dark red",
1748 "Tr. Flu. Yellow", "Tr. Flu. Red", "Gun metallic", "Red flip/flop", "Yellow flip/flop", "Silver flip/flop", "Curry",
1749 "Fire Yellow", "Flame yellowish orange", "Reddish brown", "Flame reddish orange", "Medium stone grey", "Royal blue",
1750 "Dark Royal blue", "Bright reddish lilac", "Dark stone grey", "Lemon metalic", "Light stone grey", "Dark Curry",
1751 "Faded green", "Turquoise", "Light Royal blue", "Medium Royal blue", "Rust", "Brown", "Reddish lilac", "Lilac",
1752 "Light lilac", "Bright purple", "Light purple", "Light pink", "Light brick yellow", "Warm yellowish orange",
1753 "Cool yellow", "Dove blue", "Medium lilac", "Slime green", "Smoky grey", "Dark blue", "Parsley green", "Steel blue",
1754 "Storm blue", "Lapis", "Dark indigo", "Sea green", "Shamrock", "Fossil", "Mulberry", "Forest green", "Cadet blue",
1755 "Electric blue", "Eggplant", "Moss", "Artichoke", "Sage green", "Ghost grey", "Lilac", "Plum", "Olivine",
1756 "Laurel green", "Quill grey", "Crimson", "Mint", "Baby blue", "Carnation pink", "Persimmon", "Maroon", "Gold",
1757 "Daisy orange", "Pearl", "Fog", "Salmon", "Terra Cotta", "Cocoa", "Wheat", "Buttermilk", "Mauve", "Sunrise",
1758 "Tawny", "Rust", "Cashmere", "Khaki", "Lily white", "Seashell", "Burgundy", "Cork", "Burlap", "Beige", "Oyster",
1759 "Pine Cone", "Fawn brown", "Hurricane grey", "Cloudy grey", "Linen", "Copper", "Dirt brown", "Bronze", "Flint",
1760 "Dark taupe", "Burnt Sienna", "Institutional white", "Mid gray", "Really black", "Really red", "Deep orange",
1761 "Alder", "Dusty Rose", "Olive", "New Yeller", "Really blue", "Navy blue", "Deep blue", "Cyan", "CGA brown",
1762 "Magenta", "Pink", "Deep orange", "Teal", "Toothpaste", "Lime green", "Camo", "Grime", "Lavender",
1763 "Pastel light blue", "Pastel orange", "Pastel violet", "Pastel blue-green", "Pastel green", "Pastel yellow",
1764 "Pastel brown", "Royal purple", "Hot pink"
1765}
1766
1767local brickColorFinder = Util.MakeFuzzyFinder(brickColorNames)
1768
1769local brickColorType = {
1770 Transform = function(text)
1771 local brickColors = {}
1772 for i, name in pairs(brickColorFinder(text)) do
1773 brickColors[i] = BrickColor.new(name)
1774 end
1775 return brickColors
1776 end;
1777
1778 Validate = function(brickColors)
1779 return #brickColors > 0, "No valid brick colors with that name could be found."
1780 end;
1781
1782 Autocomplete = function(brickColors)
1783 return Util.GetNames(brickColors)
1784 end;
1785
1786 Parse = function(brickColors)
1787 return brickColors[1]
1788 end;
1789}
1790
1791local brickColor3Type = {
1792 Transform = brickColorType.Transform;
1793 Validate = brickColorType.Validate;
1794 Autocomplete = brickColorType.Autocomplete;
1795
1796 Parse = function(brickColors)
1797 return brickColors[1].Color
1798 end;
1799}
1800
1801return function(registry)
1802 registry:RegisterType("brickColor", brickColorType)
1803 registry:RegisterType("brickColors", Util.MakeListableType(brickColorType))
1804
1805 registry:RegisterType("brickColor3", brickColor3Type)
1806 registry:RegisterType("brickColor3s", Util.MakeListableType(brickColor3Type))
1807end]]></ProtectedString>
1808 <BinaryString name="Tags"></BinaryString>
1809 </Properties>
1810 </Item>
1811 <Item class="ModuleScript" referent="RBXEC1ED03605AE47CD8EE8B48015D5FA3D">
1812 <Properties>
1813 <Content name="LinkedSource"><null></null></Content>
1814 <string name="Name">Color3</string>
1815 <string name="ScriptGuid">{B560A0F5-2B93-4FD6-8EF7-3FE8336874EB}</string>
1816 <ProtectedString name="Source"><![CDATA[-- A fully extensible and type-safe admin-commands console
1817-- @documentation https://github.com/evaera/Cmdr/blob/master/README.md
1818-- @source https://github.com/evaera/Cmdr
1819-- @rostrap Cmdr
1820-- @author evaera
1821
1822local Util = require(script.Parent.Parent.Shared.Util)
1823
1824local color3Type = Util.MakeSequenceType({
1825 ValidateEach = function(value, i)
1826 if value == nil then
1827 return false, ("Invalid or missing number at position %d in Color3 type."):format(i)
1828 elseif value < 0 or value > 255 then
1829 return false, ("Number out of acceptable range 0-255 at position %d in Color3 type."):format(i)
1830 elseif value % 1 ~= 0 then
1831 return false, ("Number is not an integer at position %d in Color3 type."):format(i)
1832 end
1833
1834 return true
1835 end;
1836 TransformEach = tonumber;
1837 Constructor = Color3.fromRGB;
1838 Length = 3;
1839})
1840
1841local function parseHexDigit(x)
1842 if #x == 1 then
1843 x = x .. x
1844 end
1845
1846 return tonumber(x, 16)
1847end
1848
1849local hexColor3Type = {
1850 Transform = function(text)
1851 local r, g, b = text:match("^#?(%x%x?)(%x%x?)(%x%x?)$")
1852 return Util.Each(parseHexDigit, r, g, b)
1853 end;
1854
1855 Validate = function(r, g, b)
1856 return r ~= nil and g ~= nil and b ~= nil, "Invalid hex color"
1857 end;
1858
1859 Parse = function(...)
1860 return Color3.fromRGB(...)
1861 end;
1862}
1863
1864return function (cmdr)
1865 cmdr:RegisterType("color3", color3Type)
1866 cmdr:RegisterType("color3s", Util.MakeListableType(color3Type))
1867
1868 cmdr:RegisterType("hexColor3", hexColor3Type)
1869 cmdr:RegisterType("hexColor3s", Util.MakeListableType(hexColor3Type))
1870end]]></ProtectedString>
1871 <BinaryString name="Tags"></BinaryString>
1872 </Properties>
1873 </Item>
1874 <Item class="ModuleScript" referent="RBXED5B094136AB418088F79E4D37ADAF02">
1875 <Properties>
1876 <Content name="LinkedSource"><null></null></Content>
1877 <string name="Name">Command</string>
1878 <string name="ScriptGuid">{A38076EA-89FD-4B05-A661-384DC2CD4618}</string>
1879 <ProtectedString name="Source"><![CDATA[-- A fully extensible and type-safe admin-commands console
1880-- @documentation https://github.com/evaera/Cmdr/blob/master/README.md
1881-- @source https://github.com/evaera/Cmdr
1882-- @rostrap Cmdr
1883-- @author evaera
1884
1885local Util = require(script.Parent.Parent.Shared.Util)
1886
1887return function (cmdr)
1888 local commandType = {
1889 Transform = function (text)
1890 local findCommand = Util.MakeFuzzyFinder(cmdr:GetCommandsAsStrings())
1891
1892 return findCommand(text)
1893 end;
1894
1895 Validate = function (commands)
1896 return #commands > 0, "No command with that name could be found."
1897 end;
1898
1899 Autocomplete = function (commands)
1900 return commands
1901 end;
1902
1903 Parse = function (commands)
1904 return commands[1]
1905 end;
1906 }
1907
1908 cmdr:RegisterType("command", commandType)
1909 cmdr:RegisterType("commands", Util.MakeListableType(commandType))
1910end]]></ProtectedString>
1911 <BinaryString name="Tags"></BinaryString>
1912 </Properties>
1913 </Item>
1914 <Item class="ModuleScript" referent="RBXD77FD4D79FE34C98B3486A695AD6880E">
1915 <Properties>
1916 <Content name="LinkedSource"><null></null></Content>
1917 <string name="Name">ConditionFunction</string>
1918 <string name="ScriptGuid">{CBE4A528-0A6A-4006-B652-3A2CBD6CA7FE}</string>
1919 <ProtectedString name="Source"><![CDATA[-- A fully extensible and type-safe admin-commands console
1920-- @documentation https://github.com/evaera/Cmdr/blob/master/README.md
1921-- @source https://github.com/evaera/Cmdr
1922-- @rostrap Cmdr
1923-- @author evaera
1924
1925return function (registry)
1926 registry:RegisterType("conditionFunction", registry.Cmdr.Util.MakeEnumType("ConditionFunction", {"startsWith"}))
1927end
1928]]></ProtectedString>
1929 <BinaryString name="Tags"></BinaryString>
1930 </Properties>
1931 </Item>
1932 <Item class="ModuleScript" referent="RBXB384619ED8DC4797ADD5027BCB9C2BB9">
1933 <Properties>
1934 <Content name="LinkedSource"><null></null></Content>
1935 <string name="Name">Duration</string>
1936 <string name="ScriptGuid">{4FEC71D5-1572-4BCD-AD82-065C6B1CF2B7}</string>
1937 <ProtectedString name="Source"><![CDATA[-- A fully extensible and type-safe admin-commands console
1938-- @documentation https://github.com/evaera/Cmdr/blob/master/README.md
1939-- @source https://github.com/evaera/Cmdr
1940-- @rostrap Cmdr
1941-- @author evaera
1942
1943local Util = require(script.Parent.Parent.Shared.Util)
1944
1945local unitTable = {
1946 Years = 31556926,
1947 Months = 2629744,
1948 Weeks = 604800,
1949 Days = 86400,
1950 Hours = 3600,
1951 Minutes = 60,
1952 Seconds = 1
1953}
1954
1955local searchKeyTable = {}
1956for key, _ in pairs(unitTable) do
1957 table.insert(searchKeyTable, key)
1958end
1959local unitFinder = Util.MakeFuzzyFinder(searchKeyTable)
1960
1961local function stringToSecondDuration(stringDuration)
1962 -- The duration cannot be null or an empty string.
1963 if stringDuration == nil or stringDuration == "" then
1964 return nil
1965 end
1966 -- Allow 0 by itself (without a unit) to indicate 0 seconds
1967 local durationNum = tonumber(stringDuration)
1968 if durationNum and durationNum == 0 then
1969 return 0, 0, true
1970 end
1971 -- The duration must end with a unit,
1972 -- if it doesn't then return true as the fourth value to indicate the need to offer autocomplete for units.
1973 local endOnlyString = stringDuration:gsub("%d+%a+", "")
1974 local endNumber = endOnlyString:match("%d+")
1975 if endNumber then
1976 return nil, tonumber(endNumber), true
1977 end
1978 local seconds = nil
1979 local rawNum, rawUnit
1980 for rawComponent in stringDuration:gmatch("%d+%a+") do
1981 rawNum, rawUnit = rawComponent:match("(%d+)(%a+)")
1982 local unitNames = unitFinder(rawUnit)
1983 -- There were no matching units, it's invalid. Return the parsed number to be used for autocomplete
1984 if #unitNames == 0 then
1985 return nil, tonumber(rawNum)
1986 end
1987 if seconds == nil then seconds = 0 end
1988 -- While it was already defaulting to use minutes when using just "m", this does it without worrying
1989 -- about any consistency between list ordering.
1990 seconds = seconds + (rawUnit:lower() == "m" and 60 or unitTable[unitNames[1]]) * tonumber(rawNum)
1991 end
1992 -- If no durations were provided, return nil.
1993 if seconds == nil then
1994 return nil
1995 else
1996 return seconds, tonumber(rawNum)
1997 end
1998end
1999
2000local function mapUnits(units, rawText, lastNumber, subStart)
2001 subStart = subStart or 1
2002 local returnTable = {}
2003 for i, unit in pairs(units) do
2004 if lastNumber == 1 then
2005 returnTable[i] = rawText .. unit:sub(subStart, #unit - 1)
2006 else
2007 returnTable[i] = rawText .. unit:sub(subStart)
2008 end
2009 end
2010 return returnTable
2011end
2012
2013local durationType = {
2014 Transform = function(text)
2015 return text, stringToSecondDuration(text)
2016 end;
2017
2018 Validate = function(_, duration)
2019 return duration ~= nil and duration >= 0
2020 end;
2021
2022 Autocomplete = function(rawText, duration, lastNumber, isUnitMissing, matchedUnits)
2023 local returnTable = {}
2024 if isUnitMissing or matchedUnits then
2025 local unitsTable = isUnitMissing == true and unitFinder("") or matchedUnits
2026 if isUnitMissing == true then
2027 -- Concat the entire unit name to existing text.
2028 returnTable = mapUnits(unitsTable, rawText, lastNumber)
2029 else
2030 -- Concat the rest of the unit based on what already exists of the unit name.
2031 local existingUnitLength = rawText:match("^.*(%a+)$"):len()
2032 returnTable = mapUnits(unitsTable, rawText, existingUnitLength + 1)
2033 end
2034 elseif duration ~= nil then
2035 local endingUnit = rawText:match("^.*%d+(%a+)$")
2036 -- Assume there is a singular match at this point
2037 local fuzzyUnits = unitFinder(endingUnit)
2038 -- List all possible fuzzy matches. This is for the Minutes/Months ambiguity case.
2039 returnTable = mapUnits(fuzzyUnits, rawText, lastNumber, #endingUnit + 1)
2040 -- Sort alphabetically in the Minutes/Months case, so Minutes are displayed on top.
2041 table.sort(returnTable)
2042 end
2043 return returnTable
2044 end;
2045
2046 Parse = function(_, duration)
2047 return duration
2048 end;
2049}
2050
2051return function(registry)
2052 registry:RegisterType("duration", durationType)
2053 registry:RegisterType("durations", Util.MakeListableType(durationType))
2054end]]></ProtectedString>
2055 <BinaryString name="Tags"></BinaryString>
2056 </Properties>
2057 </Item>
2058 <Item class="ModuleScript" referent="RBX599092F54B544F07B2AF0198C9DED4A4">
2059 <Properties>
2060 <Content name="LinkedSource"><null></null></Content>
2061 <string name="Name">Player</string>
2062 <string name="ScriptGuid">{43961925-6FC1-4F63-A525-6091F7E2A05D}</string>
2063 <ProtectedString name="Source"><![CDATA[-- A fully extensible and type-safe admin-commands console
2064-- @documentation https://github.com/evaera/Cmdr/blob/master/README.md
2065-- @source https://github.com/evaera/Cmdr
2066-- @rostrap Cmdr
2067-- @author evaera
2068
2069local Util = require(script.Parent.Parent.Shared.Util)
2070local Players = game:GetService("Players")
2071
2072local function ShorthandSingle (text, executor)
2073 if text == "." or text == "me" then
2074 return {executor}
2075 end
2076end
2077
2078local function ShorthandMultiple (text, executor)
2079 if text == "*" or text == "all" then
2080 return Players:GetPlayers()
2081 elseif text == "others" then
2082 local Others = Players:GetPlayers()
2083 for i = 1, #Others do
2084 if Others[i] == executor then
2085 table.remove(Others, i)
2086 break
2087 end
2088 end
2089 return Others
2090 end
2091end
2092
2093local function CheckShorthands (text, executor, ...)
2094 for _, func in pairs({...}) do
2095 local values = func(text, executor)
2096
2097 if values then return values end
2098 end
2099end
2100
2101local playerType = {
2102 Transform = function (text, executor)
2103 local shorthand = CheckShorthands(text, executor, ShorthandSingle)
2104 if shorthand then
2105 return shorthand
2106 end
2107
2108 local findPlayer = Util.MakeFuzzyFinder(Players:GetPlayers())
2109
2110 return findPlayer(text)
2111 end;
2112
2113 Validate = function (players)
2114 return #players > 0, "No player with that name could be found."
2115 end;
2116
2117 Autocomplete = function (players)
2118 return Util.GetNames(players)
2119 end;
2120
2121 Parse = function (players)
2122 return players[1]
2123 end;
2124}
2125
2126local playersType = {
2127 Listable = true;
2128
2129 Transform = function (text, executor)
2130 local shorthand = CheckShorthands(text, executor, ShorthandSingle, ShorthandMultiple)
2131
2132 if shorthand then
2133 return shorthand, true
2134 end
2135
2136 local findPlayers = Util.MakeFuzzyFinder(Players:GetPlayers())
2137
2138 return findPlayers(text)
2139 end;
2140
2141 Validate = function (players)
2142 return #players > 0, "No players were found matching that query."
2143 end;
2144
2145 Autocomplete = function (players)
2146 return Util.GetNames(players)
2147 end;
2148
2149 Parse = function (players, returnAll)
2150 return returnAll and players or { players[1] }
2151 end;
2152}
2153
2154return function (cmdr)
2155 cmdr:RegisterType("player", playerType)
2156 cmdr:RegisterType("players", playersType)
2157end
2158]]></ProtectedString>
2159 <BinaryString name="Tags"></BinaryString>
2160 </Properties>
2161 </Item>
2162 <Item class="ModuleScript" referent="RBXBDDBF036BDA048DF819154E24AFF6EEE">
2163 <Properties>
2164 <Content name="LinkedSource"><null></null></Content>
2165 <string name="Name">PlayerId</string>
2166 <string name="ScriptGuid">{733AD56E-6BD0-41EE-9E46-62442F9E8755}</string>
2167 <ProtectedString name="Source"><![CDATA[-- A fully extensible and type-safe admin-commands console
2168-- @documentation https://github.com/evaera/Cmdr/blob/master/README.md
2169-- @source https://github.com/evaera/Cmdr
2170-- @rostrap Cmdr
2171-- @author evaera
2172
2173local Util = require(script.Parent.Parent.Shared.Util)
2174local Players = game:GetService("Players")
2175
2176local nameCache = {}
2177local function getUserId(name)
2178 if nameCache[name] then
2179 return nameCache[name]
2180 elseif Players:FindFirstChild(name) then
2181 nameCache[name] = Players[name].UserId
2182 return Players[name].UserId
2183 else
2184 local ok, userid = pcall(Players.GetUserIdFromNameAsync, Players, name)
2185
2186 if not ok then
2187 return nil
2188 end
2189
2190 nameCache[name] = userid
2191 return userid
2192 end
2193end
2194
2195local playerIdType = {
2196 DisplayName = "Full Player Name";
2197
2198 Transform = function (text)
2199 local findPlayer = Util.MakeFuzzyFinder(Players:GetPlayers())
2200
2201 return text, findPlayer(text)
2202 end;
2203
2204 ValidateOnce = function (text)
2205 return getUserId(text) ~= nil, "No player with that name could be found."
2206 end;
2207
2208 Autocomplete = function (_, players)
2209 return Util.GetNames(players)
2210 end;
2211
2212 Parse = function (text)
2213 return getUserId(text)
2214 end;
2215}
2216
2217return function (cmdr)
2218 cmdr:RegisterType("playerId", playerIdType)
2219 cmdr:RegisterType("playerIds", Util.MakeListableType(playerIdType))
2220end
2221]]></ProtectedString>
2222 <BinaryString name="Tags"></BinaryString>
2223 </Properties>
2224 </Item>
2225 <Item class="ModuleScript" referent="RBX13B272783ADA4E12A46B93254E029747">
2226 <Properties>
2227 <Content name="LinkedSource"><null></null></Content>
2228 <string name="Name">Primitives</string>
2229 <string name="ScriptGuid">{CD355608-E8F5-40F8-A24F-642072874529}</string>
2230 <ProtectedString name="Source"><![CDATA[-- A fully extensible and type-safe admin-commands console
2231-- @documentation https://github.com/evaera/Cmdr/blob/master/README.md
2232-- @source https://github.com/evaera/Cmdr
2233-- @rostrap Cmdr
2234-- @author evaera
2235
2236local Util = require(script.Parent.Parent.Shared.Util)
2237
2238local stringType = {
2239 Validate = function (value)
2240 return value ~= nil
2241 end;
2242
2243 Parse = function (value)
2244 return tostring(value)
2245 end;
2246}
2247
2248local numberType = {
2249 Transform = function (text)
2250 return tonumber(text)
2251 end;
2252
2253 Validate = function (value)
2254 return value ~= nil
2255 end;
2256
2257 Parse = function (value)
2258 return value
2259 end;
2260}
2261
2262local intType = {
2263 Transform = function (text)
2264 return tonumber(text)
2265 end;
2266
2267 Validate = function (value)
2268 return value ~= nil and value == math.floor(value), "Only whole numbers are valid."
2269 end;
2270
2271 Parse = function (value)
2272 return value
2273 end
2274}
2275
2276local boolType do
2277 local truthy = Util.MakeDictionary({"true", "t", "yes", "y", "on", "enable", "enabled", "1", "+"});
2278 local falsy = Util.MakeDictionary({"false"; "f"; "no"; "n"; "off"; "disable"; "disabled"; "0"; "-"});
2279
2280 boolType = {
2281 Transform = function (text)
2282 return text:lower()
2283 end;
2284
2285 Validate = function (value)
2286 return truthy[value] ~= nil or falsy[value] ~= nil, "Please use true/yes/on or false/no/off."
2287 end;
2288
2289 Parse = function (value)
2290 if truthy[value] then
2291 return true
2292 elseif falsy[value] then
2293 return false
2294 else
2295 error("Unknown boolean value.")
2296 end
2297 end;
2298 }
2299end
2300
2301return function (cmdr)
2302 cmdr:RegisterType("string", stringType)
2303 cmdr:RegisterType("number", numberType)
2304 cmdr:RegisterType("integer", intType)
2305 cmdr:RegisterType("boolean", boolType)
2306
2307 cmdr:RegisterType("strings", Util.MakeListableType(stringType))
2308 cmdr:RegisterType("numbers", Util.MakeListableType(numberType))
2309 cmdr:RegisterType("integers", Util.MakeListableType(intType))
2310 cmdr:RegisterType("booleans", Util.MakeListableType(boolType))
2311end]]></ProtectedString>
2312 <BinaryString name="Tags"></BinaryString>
2313 </Properties>
2314 </Item>
2315 <Item class="ModuleScript" referent="RBXF471B0EAB54240449D47116CA22DF7D8">
2316 <Properties>
2317 <Content name="LinkedSource"><null></null></Content>
2318 <string name="Name">Team</string>
2319 <string name="ScriptGuid">{98D7A448-55AD-449B-A978-20528E3CF220}</string>
2320 <ProtectedString name="Source"><![CDATA[-- A fully extensible and type-safe admin-commands console
2321-- @documentation https://github.com/evaera/Cmdr/blob/master/README.md
2322-- @source https://github.com/evaera/Cmdr
2323-- @rostrap Cmdr
2324-- @author evaera
2325
2326local Teams = game:GetService("Teams")
2327local Util = require(script.Parent.Parent.Shared.Util)
2328
2329local teamType = {
2330 Transform = function (text)
2331 local findTeam = Util.MakeFuzzyFinder(Teams:GetTeams())
2332
2333 return findTeam(text)
2334 end;
2335
2336 Validate = function (teams)
2337 return #teams > 0, "No team with that name could be found."
2338 end;
2339
2340 Autocomplete = function (teams)
2341 return Util.GetNames(teams)
2342 end;
2343
2344 Parse = function (teams)
2345 return teams[1];
2346 end;
2347}
2348
2349local teamPlayersType = {
2350 Listable = true;
2351 Transform = teamType.Transform;
2352 Validate = teamType.Validate;
2353 Autocomplete = teamType.Autocomplete;
2354
2355 Parse = function (teams)
2356 return teams[1]:GetPlayers()
2357 end;
2358}
2359
2360local teamColorType = {
2361 Transform = teamType.Transform;
2362 Validate = teamType.Validate;
2363 Autocomplete = teamType.Autocomplete;
2364
2365 Parse = function (teams)
2366 return teams[1].TeamColor
2367 end;
2368}
2369
2370return function (cmdr)
2371 cmdr:RegisterType("team", teamType)
2372 cmdr:RegisterType("teams", Util.MakeListableType(teamType))
2373
2374 cmdr:RegisterType("teamPlayers", teamPlayersType)
2375
2376 cmdr:RegisterType("teamColor", teamColorType)
2377 cmdr:RegisterType("teamColors", Util.MakeListableType(teamColorType))
2378end]]></ProtectedString>
2379 <BinaryString name="Tags"></BinaryString>
2380 </Properties>
2381 </Item>
2382 <Item class="ModuleScript" referent="RBX5B6895F0800A43ED98E1C88E3E931E76">
2383 <Properties>
2384 <Content name="LinkedSource"><null></null></Content>
2385 <string name="Name">UserInput</string>
2386 <string name="ScriptGuid">{1E4626B3-7A93-4BC8-A167-72EA55831D09}</string>
2387 <ProtectedString name="Source"><![CDATA[-- A fully extensible and type-safe admin-commands console
2388-- @documentation https://github.com/evaera/Cmdr/blob/master/README.md
2389-- @source https://github.com/evaera/Cmdr
2390-- @rostrap Cmdr
2391-- @author evaera
2392
2393local Util = require(script.Parent.Parent.Shared.Util)
2394
2395local combinedInputEnums = Enum.UserInputType:GetEnumItems()
2396
2397for _, e in pairs(Enum.KeyCode:GetEnumItems()) do
2398 combinedInputEnums[#combinedInputEnums + 1] = e
2399end
2400
2401local userInputType = {
2402 Transform = function (text)
2403 local findEnum = Util.MakeFuzzyFinder(combinedInputEnums)
2404
2405 return findEnum(text)
2406 end;
2407
2408 Validate = function (enums)
2409 return #enums > 0
2410 end;
2411
2412 Autocomplete = function (enums)
2413 return Util.GetNames(enums)
2414 end;
2415
2416 Parse = function (enums)
2417 return enums[1];
2418 end;
2419}
2420
2421return function (cmdr)
2422 cmdr:RegisterType("userInput", userInputType)
2423 cmdr:RegisterType("userInputs", Util.MakeListableType(userInputType))
2424end]]></ProtectedString>
2425 <BinaryString name="Tags"></BinaryString>
2426 </Properties>
2427 </Item>
2428 <Item class="ModuleScript" referent="RBX2EA11A3AD0BD440096D0A01F152534F2">
2429 <Properties>
2430 <Content name="LinkedSource"><null></null></Content>
2431 <string name="Name">Vector</string>
2432 <string name="ScriptGuid">{BF85AE9A-0FBE-4EAA-98CF-F55AC02BA9FA}</string>
2433 <ProtectedString name="Source"><![CDATA[-- A fully extensible and type-safe admin-commands console
2434-- @documentation https://github.com/evaera/Cmdr/blob/master/README.md
2435-- @source https://github.com/evaera/Cmdr
2436-- @rostrap Cmdr
2437-- @author evaera
2438
2439local Util = require(script.Parent.Parent.Shared.Util)
2440
2441local function validateVector(value, i)
2442 if value == nil then
2443 return false, ("Invalid or missing number at position %d in Vector type."):format(i)
2444 end
2445
2446 return true
2447end
2448
2449local vector3Type = Util.MakeSequenceType({
2450 ValidateEach = validateVector;
2451 TransformEach = tonumber;
2452 Constructor = Vector3.new;
2453 Length = 3;
2454})
2455
2456local vector2Type = Util.MakeSequenceType({
2457 ValidateEach = validateVector;
2458 TransformEach = tonumber;
2459 Constructor = Vector2.new;
2460 Length = 2;
2461})
2462
2463return function (cmdr)
2464 cmdr:RegisterType("vector3", vector3Type)
2465 cmdr:RegisterType("vector3s", Util.MakeListableType(vector3Type))
2466
2467 cmdr:RegisterType("vector2", vector2Type)
2468 cmdr:RegisterType("vector2s", Util.MakeListableType(vector2Type))
2469end]]></ProtectedString>
2470 <BinaryString name="Tags"></BinaryString>
2471 </Properties>
2472 </Item>
2473 </Item>
2474 <Item class="Folder" referent="RBX66A52AFF88594D1AB194C4A00314A929">
2475 <Properties>
2476 <string name="Name">Shared</string>
2477 <BinaryString name="Tags"></BinaryString>
2478 </Properties>
2479 <Item class="ModuleScript" referent="RBX716C459E6F0242A195AA74E318076C2B">
2480 <Properties>
2481 <Content name="LinkedSource"><null></null></Content>
2482 <string name="Name">Argument</string>
2483 <string name="ScriptGuid">{27B50999-F950-4357-B8AA-3E602A18E479}</string>
2484 <ProtectedString name="Source"><![CDATA[-- A fully extensible and type-safe admin-commands console
2485-- @documentation https://github.com/evaera/Cmdr/blob/master/README.md
2486-- @source https://github.com/evaera/Cmdr
2487-- @rostrap Cmdr
2488-- @author evaera
2489
2490local TYPE_DEFAULTS = {
2491 -- Make all `players` types also be able to match by team
2492 players = "players % teamPlayers";
2493
2494 playerId = "playerId # integer";
2495 playerIds = "playerIds # integers";
2496
2497 brickColor = "brickColor % teamColor";
2498 brickColors = "brickColors % teamColors";
2499
2500 color3 = "color3 # hexColor3 ! brickColor3";
2501 color3s = "color3s # hexColor3s ! brickColor3s";
2502}
2503
2504local Util = require(script.Parent.Util)
2505
2506local Argument = {}
2507Argument.__index = Argument
2508
2509--- Returns a new ArgumentContext, an object that handles parsing and validating arguments
2510function Argument.new (command, argumentObject, value)
2511 local self = {
2512 Command = command; -- The command that owns this argument
2513 Type = nil; -- The type definition
2514 Name = argumentObject.Name; -- The name for this specific argument
2515 Object = argumentObject; -- The raw ArgumentObject (definition)
2516 Required = argumentObject.Default == nil and argumentObject.Optional ~= true; -- If the argument is required or not.
2517 Executor = command.Executor; -- The player who is running the command
2518 RawValue = nil; -- The raw, unparsed value
2519 RawSegments = {}; -- The raw, unparsed segments (if the raw value was comma-sep)
2520 TransformedValues = {}; -- The transformed value (generated later)
2521 Prefix = nil; -- The prefix for this command (%Team)
2522 }
2523
2524 local parsedType, parsedRawValue, prefix = Util.ParsePrefixedUnionType(
2525 TYPE_DEFAULTS[argumentObject.Type] or argumentObject.Type, value
2526 )
2527
2528 self.Type = command.Dispatcher.Registry:GetType(parsedType)
2529 self.RawValue = parsedRawValue
2530 self.Prefix = prefix
2531
2532 if self.Type == nil then
2533 error(string.format("%s has an unregistered type %q", self.Name or "<none>", parsedType or "<none>"))
2534 end
2535
2536 setmetatable(self, Argument)
2537
2538 self:Transform()
2539
2540 return self
2541end
2542
2543--- Calls the transform function on this argument.
2544-- The return value(s) from this function are passed to all of the other argument methods.
2545-- Called automatically at instantiation
2546function Argument:Transform()
2547 if #self.TransformedValues ~= 0 then
2548 return
2549 end
2550
2551 if self.Type.Listable and #self.RawValue > 0 then
2552 local rawSegments = Util.SplitStringSimple(self.RawValue, ",")
2553
2554 if self.RawValue:sub(#self.RawValue, #self.RawValue) == "," then
2555 rawSegments[#rawSegments + 1] = "" -- makes auto complete tick over right after pressing ,
2556 end
2557
2558 for i, rawSegment in ipairs(rawSegments) do
2559 self.RawSegments[i] = rawSegment
2560 self.TransformedValues[i] = { self:TransformSegment(rawSegment) }
2561 end
2562 else
2563 self.RawSegments[1] = self.RawValue
2564 self.TransformedValues[1] = { self:TransformSegment(self.RawValue) }
2565 end
2566end
2567
2568function Argument:TransformSegment(rawSegment)
2569 if self.Type.Transform then
2570 return self.Type.Transform(rawSegment, self.Executor)
2571 else
2572 return rawSegment
2573 end
2574end
2575
2576--- Returns whatever the Transform method gave us.
2577function Argument:GetTransformedValue(segment)
2578 return unpack(self.TransformedValues[segment])
2579end
2580
2581--- Validates that the argument will work without any type errors.
2582function Argument:Validate(isFinal)
2583 if self.RawValue == nil or #self.RawValue == 0 and self.Required == false then
2584 return true
2585 end
2586
2587 if self.Type.Validate or self.Type.ValidateOnce then
2588 for i = 1, #self.TransformedValues do
2589 if self.Type.Validate then
2590 local valid, errorText = self.Type.Validate(self:GetTransformedValue(i))
2591
2592 if not valid then
2593 return valid, errorText or "Invalid value"
2594 end
2595 end
2596
2597 if isFinal and self.Type.ValidateOnce then
2598 local validOnce, errorTextOnce = self.Type.ValidateOnce(self:GetTransformedValue(i))
2599
2600 if not validOnce then
2601 return validOnce, errorTextOnce
2602 end
2603 end
2604 end
2605
2606 return true
2607 else
2608 return true
2609 end
2610end
2611
2612--- Gets a list of all possible values that could match based on the current value.
2613function Argument:GetAutocomplete()
2614 if self.Type.Autocomplete then
2615 return self.Type.Autocomplete(self:GetTransformedValue(#self.TransformedValues))
2616 else
2617 return {}
2618 end
2619end
2620
2621function Argument:ParseValue(i)
2622 if self.Type.Parse then
2623 return self.Type.Parse(self:GetTransformedValue(i))
2624 else
2625 return self:GetTransformedValue(i)
2626 end
2627end
2628
2629--- Returns the final value of the argument.
2630function Argument:GetValue()
2631 if #self.RawValue == 0 and not self.Required and self.Object.Default ~= nil then
2632 return self.Object.Default
2633 end
2634
2635 if not self.Type.Listable then
2636 return self:ParseValue(1)
2637 end
2638
2639 local values = {}
2640
2641 for i = 1, #self.TransformedValues do
2642 local parsedValue = self:ParseValue(i)
2643
2644 if type(parsedValue) ~= "table" then
2645 error(("Listable types must return a table from Parse (%s)"):format(self.Type.Name))
2646 end
2647
2648 for _, value in pairs(parsedValue) do
2649 values[value] = true -- Put them into a dictionary to ensure uniqueness
2650 end
2651 end
2652
2653 local valueArray = {}
2654
2655 for value in pairs(values) do
2656 valueArray[#valueArray + 1] = value
2657 end
2658
2659 return valueArray
2660end
2661
2662return Argument]]></ProtectedString>
2663 <BinaryString name="Tags"></BinaryString>
2664 </Properties>
2665 </Item>
2666 <Item class="ModuleScript" referent="RBX11E9EB676C274EDEA8E7B702715FF9C7">
2667 <Properties>
2668 <Content name="LinkedSource"><null></null></Content>
2669 <string name="Name">Command</string>
2670 <string name="ScriptGuid">{87DF3A9B-052B-4922-A962-9C737324A21F}</string>
2671 <ProtectedString name="Source"><![CDATA[-- A fully extensible and type-safe admin-commands console
2672-- @documentation https://github.com/evaera/Cmdr/blob/master/README.md
2673-- @source https://github.com/evaera/Cmdr
2674-- @rostrap Cmdr
2675-- @author evaera
2676
2677local RunService = game:GetService("RunService")
2678local Players = game:GetService("Players")
2679local Argument = require(script.Parent.Argument)
2680
2681local IsServer = RunService:IsServer()
2682
2683local Command = {}
2684Command.__index = Command
2685
2686--- Returns a new CommandContext, an object which is created for every command validation.
2687-- This is also what's passed as the context to the "Run" functions in commands
2688function Command.new (options)
2689 local self = {
2690 Dispatcher = options.Dispatcher; -- The dispatcher that created this command context
2691 Cmdr = options.Dispatcher.Cmdr; -- A quick reference to Cmdr for command context
2692 Name = options.CommandObject.Name; -- The command name (not alias)
2693 RawText = options.Text; -- The raw text used to trigger this command
2694 Object = options.CommandObject; -- The command object (definition)
2695 Group = options.CommandObject.Group; -- The group this command is in
2696 State = {}; -- A table which will hold any custom command state information
2697 Aliases = options.CommandObject.Aliases;
2698 Alias = options.Alias; -- The command name that was used
2699 Description = options.CommandObject.Description;
2700 Executor = options.Executor; -- The player who ran the command
2701 ArgumentDefinitions = options.CommandObject.Args; -- The argument definitions from the command definition
2702 RawArguments = options.Arguments; -- Array of strings which are the unparsed values for the arguments
2703 Arguments = {}; -- A table which will hold ArgumentContexts for each argument
2704 Data = options.Data; -- A special container for any additional data the command needs to collect from the client
2705 Response = nil; -- Will be set at the very end when the command is run and a string is returned from the Run function.
2706 }
2707
2708 setmetatable(self, Command)
2709
2710 return self
2711end
2712
2713--- Parses all of the command arguments into ArgumentContexts
2714-- Called by the command dispatcher automatically
2715-- allowIncompleteArguments: if true, will not throw an error for missing arguments
2716function Command:Parse (allowIncompleteArguments)
2717 local hadOptional = false
2718 for i, definition in ipairs(self.ArgumentDefinitions) do
2719 local required = (definition.Default == nil and definition.Optional ~= true)
2720
2721 if required and hadOptional then
2722 error(("Command %q: Required arguments cannot occur after optional arguments."):format(self.Name))
2723 elseif not required then
2724 hadOptional = true
2725 end
2726
2727 if self.RawArguments[i] == nil and required and allowIncompleteArguments ~= true then
2728 return false, ("Required argument #%d %s is missing."):format(i, definition.Name)
2729 elseif self.RawArguments[i] or allowIncompleteArguments then
2730 self.Arguments[i] = Argument.new(self, definition, self.RawArguments[i] or "")
2731 end
2732 end
2733
2734 return true
2735end
2736
2737--- Validates that all of the arguments are in a valid state.
2738-- This must be called before :Run() is called.
2739-- Returns boolean (true if ok), errorText
2740function Command:Validate (isFinal)
2741 self._Validated = true
2742 local errorText = ""
2743 local success = true
2744
2745 for i, arg in pairs(self.Arguments) do
2746 local argSuccess, argErrorText = arg:Validate(isFinal)
2747
2748 if not argSuccess then
2749 success = false
2750 errorText = ("%s; #%d %s: %s"):format(errorText, i, arg.Name, argErrorText or "error")
2751 end
2752 end
2753
2754 return success, errorText:sub(3)
2755end
2756
2757--- Returns the last argument that has a value.
2758-- Useful for getting the autocomplete for the argument the user is working on.
2759function Command:GetLastArgument()
2760 for i = #self.Arguments, 1, -1 do
2761 if self.Arguments[i].RawValue then
2762 return self.Arguments[i]
2763 end
2764 end
2765end
2766
2767--- Returns a table containing the parsed values for all of the arguments.
2768function Command:GatherArgumentValues ()
2769 local values = {}
2770
2771 for i = 1, #self.ArgumentDefinitions do
2772 local arg = self.Arguments[i]
2773 if arg then
2774 values[i] = arg:GetValue()
2775 else
2776 values[i] = self.ArgumentDefinitions[i].Default
2777 end
2778 end
2779
2780 return values
2781end
2782
2783--- Runs the command. Handles dispatching to the server if necessary.
2784-- Command:Validate() must be called before this is called or it will throw.
2785function Command:Run ()
2786 if self._Validated == nil then
2787 error("Must validate a command before running.")
2788 end
2789
2790 local beforeRunHook = self.Dispatcher:RunHooks("BeforeRun", self)
2791 if beforeRunHook then
2792 return beforeRunHook
2793 end
2794
2795 if self.Object.Run then -- We can just Run it here on this machine
2796 self.Response = self.Object.Run(self, unpack(self:GatherArgumentValues()))
2797 elseif IsServer then -- Uh oh, we're already on the server and there's no Run function.
2798 warn(self.Name, "command has no implementation!")
2799 self.Response = "No implementation."
2800 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.
2801 self.Response = self.Dispatcher:Send(self.RawText, self.Object.Data and self.Object.Data(self))
2802 end
2803
2804 local afterRunHook = self.Dispatcher:RunHooks("AfterRun", self)
2805 if afterRunHook then
2806 return afterRunHook
2807 else
2808 return self.Response
2809 end
2810end
2811
2812--- Returns an ArgumentContext for the specific index
2813function Command:GetArgument (index)
2814 return self.Arguments[index]
2815end
2816
2817-- Below are functions that are only meant to be used in command implementations --
2818
2819--- Returns the extra data associated with this command.
2820-- This needs to be used instead of just context.Data for reliability when not using a remote command.
2821function Command:GetData ()
2822 if self.Data then
2823 return self.Data
2824 end
2825
2826 if self.Object.Data and not IsServer then
2827 self.Data = self.Object.Data(self)
2828 end
2829
2830 return self.Data
2831end
2832
2833--- Sends an event message to a player
2834function Command:SendEvent(player, event, ...)
2835 assert(typeof(player) == "Instance", "Argument #1 must be a Player")
2836 assert(player:IsA("Player"), "Argument #1 must be a Player")
2837 assert(type(event) == "string", "Argument #2 must be a string")
2838
2839 if IsServer then
2840 self.Dispatcher.Cmdr.RemoteEvent:FireClient(player, event, ...)
2841 elseif self.Dispatcher.Cmdr.Events[event] then
2842 assert(player == Players.LocalPlayer, "Event messages can only be sent to the local player on the client.")
2843 self.Dispatcher.Cmdr.Events[event](...)
2844 end
2845end
2846
2847--- Sends an event message to all players
2848function Command:BroadcastEvent(...)
2849 if not IsServer then
2850 error("Can't broadcast event messages from the client.", 2)
2851 end
2852
2853 self.Dispatcher.Cmdr.RemoteEvent:FireAllClients(...)
2854end
2855
2856--- Alias of self:SendEvent(self.Executor, "AddLine", text)
2857function Command:Reply(...)
2858 return self:SendEvent(self.Executor, "AddLine", ...)
2859end
2860
2861--- Alias of Registry:GetStore(...)
2862function Command:GetStore(...)
2863 return self.Dispatcher.Cmdr.Registry:GetStore(...)
2864end
2865
2866return Command
2867]]></ProtectedString>
2868 <BinaryString name="Tags"></BinaryString>
2869 </Properties>
2870 </Item>
2871 <Item class="ModuleScript" referent="RBX605C1FBC658B4EA8B3572B11FD05647A">
2872 <Properties>
2873 <Content name="LinkedSource"><null></null></Content>
2874 <string name="Name">Dispatcher</string>
2875 <string name="ScriptGuid">{7F66E598-3579-48FA-A96F-7321D43FDDEE}</string>
2876 <ProtectedString name="Source"><![CDATA[-- A fully extensible and type-safe admin-commands console
2877-- @documentation https://github.com/evaera/Cmdr/blob/master/README.md
2878-- @source https://github.com/evaera/Cmdr
2879-- @rostrap Cmdr
2880-- @author evaera
2881
2882local RunService = game:GetService("RunService")
2883local TeleportService = game:GetService("TeleportService")
2884local Players = game:GetService("Players")
2885local Util = require(script.Parent.Util)
2886local Command = require(script.Parent.Command)
2887
2888local HISTORY_SETTING_NAME = "CmdrCommandHistory"
2889
2890--- The dispatcher handles creating and running commands during the game.
2891local Dispatcher = {
2892 Cmdr = nil;
2893 Registry = nil;
2894
2895}
2896
2897--- Takes in raw command information and generates a command out of it.
2898-- text and executor are required arguments.
2899-- allowIncompleteData, when true, will ignore errors about arguments missing so we can parse live as the user types.
2900-- data is for special networked Data about the command gathered on the client. Purely Optional.
2901-- returns the command if successful, or (false, errorText) if not
2902function Dispatcher:Evaluate (text, executor, allowIncompleteArguments, data)
2903 if RunService:IsClient() == true and executor ~= Players.LocalPlayer then
2904 error("Can't evaluate a command that isn't sent by the local player.")
2905 end
2906
2907 local arguments = Util.SplitString(text)
2908 local commandName = table.remove(arguments, 1)
2909 local commandObject = self.Registry:GetCommand(commandName)
2910
2911 if commandObject then
2912 -- No need to continue splitting when there are no more arguments. We'll just mash any additional arguments into the last one.
2913 arguments = Util.MashExcessArguments(arguments, #commandObject.Args)
2914
2915 -- Create the CommandContext and parse it.
2916 local command = Command.new({
2917 Dispatcher = self,
2918 Text = text,
2919 CommandObject = commandObject,
2920 Alias = commandName,
2921 Executor = executor,
2922 Arguments = arguments,
2923 Data = data
2924 })
2925 local success, errorText = command:Parse(allowIncompleteArguments)
2926
2927 if success then
2928 return command
2929 else
2930 return false, errorText
2931 end
2932 else
2933 return false, "Invalid command. Use the help command to see all available commands."
2934 end
2935end
2936
2937--- A helper that evaluates and runs the command in one go.
2938-- Either returns any validation errors as a string, or the output of the command as a string. Definitely a string, though.
2939function Dispatcher:EvaluateAndRun (text, executor, options)
2940 executor = executor or Players.LocalPlayer
2941 options = options or {}
2942
2943 if RunService:IsClient() and options.IsHuman then
2944 self:PushHistory(text)
2945 end
2946
2947 local command, errorText = self:Evaluate(text, executor, nil, options.Data)
2948
2949 if not command then
2950 return errorText
2951 end
2952
2953 local ok, out = pcall(function()
2954 local valid, errorText = command:Validate(true) -- luacheck: ignore
2955
2956 if not valid then
2957 return errorText
2958 end
2959
2960 return command:Run() or "Command executed."
2961 end)
2962
2963 if not ok then
2964 warn(("Error occurred while evaluating command string %q\n%s"):format(text, out))
2965 end
2966
2967 return ok and out or "An error occurred while running this command."
2968end
2969
2970--- Send text as the local user to remote server to be evaluated there.
2971function Dispatcher:Send (text, data)
2972 if RunService:IsClient() == false then
2973 error("Dispatcher:Send can only be called from the client.")
2974 end
2975
2976 return self.Cmdr.RemoteFunction:InvokeServer(text, {
2977 Data = data
2978 })
2979end
2980
2981--- Invoke a command programmatically as the local user e.g. from a settings menu
2982-- Command should be the first argument, all arguments afterwards should be the arguments to the command.
2983function Dispatcher:Run (...)
2984 if not Players.LocalPlayer then
2985 error("Dispatcher:Run can only be called from the client.")
2986 end
2987
2988 local args = {...}
2989 local text = args[1]
2990
2991 for i = 2, #args do
2992 text = text .. " " .. tostring(args[i])
2993 end
2994
2995 local command, errorText = self:Evaluate(text, Players.LocalPlayer)
2996
2997 if not command then
2998 error(errorText) -- We do a full-on error here since this is code-invoked and they should know better.
2999 end
3000
3001 local success, errorText = command:Validate(true) -- luacheck: ignore
3002
3003 if not success then
3004 error(errorText)
3005 end
3006
3007 return command:Run()
3008end
3009
3010--- Runs hooks matching name and returns nil for ok or a string for cancellation
3011function Dispatcher:RunHooks(hookName, ...)
3012 if not self.Registry.Hooks[hookName] then
3013 error(("Invalid hook name: %q"):format(hookName), 2)
3014 end
3015
3016 for _, hook in ipairs(self.Registry.Hooks[hookName]) do
3017 local value = hook.callback(...)
3018
3019 if value ~= nil then
3020 return tostring(value)
3021 end
3022 end
3023end
3024
3025function Dispatcher:PushHistory(text)
3026 assert(RunService:IsClient(), "PushHistory may only be used from the client.")
3027
3028 local history = self:GetHistory()
3029
3030 -- Remove duplicates
3031 if Util.TrimString(text) == "" or text == history[#history] then
3032 return
3033 end
3034
3035 history[#history + 1] = text
3036
3037 TeleportService:SetTeleportSetting(HISTORY_SETTING_NAME, history)
3038end
3039
3040function Dispatcher:GetHistory()
3041 assert(RunService:IsClient(), "GetHistory may only be used from the client.")
3042
3043 return TeleportService:GetTeleportSetting(HISTORY_SETTING_NAME) or {}
3044end
3045
3046return function (cmdr)
3047 Dispatcher.Cmdr = cmdr
3048 Dispatcher.Registry = cmdr.Registry
3049
3050 return Dispatcher
3051end]]></ProtectedString>
3052 <BinaryString name="Tags"></BinaryString>
3053 </Properties>
3054 </Item>
3055 <Item class="ModuleScript" referent="RBXA013658904974BF49DB641C0A93175F4">
3056 <Properties>
3057 <Content name="LinkedSource"><null></null></Content>
3058 <string name="Name">Registry</string>
3059 <string name="ScriptGuid">{33D92E98-6382-4A3F-B9F7-7CDEC2031ED5}</string>
3060 <ProtectedString name="Source"><![CDATA[-- A fully extensible and type-safe admin-commands console
3061-- @documentation https://github.com/evaera/Cmdr/blob/master/README.md
3062-- @source https://github.com/evaera/Cmdr
3063-- @rostrap Cmdr
3064-- @author evaera
3065
3066local RunService = game:GetService("RunService")
3067
3068local Util = require(script.Parent.Util)
3069
3070--- The registry keeps track of all the commands and types that Cmdr knows about.
3071local Registry = {
3072 TypeMethods = Util.MakeDictionary({"Transform", "Validate", "Autocomplete", "Parse", "DisplayName", "Listable", "ValidateOnce"});
3073 CommandMethods = Util.MakeDictionary({"Name", "Aliases", "AutoExec", "Description", "Args", "Run", "Data", "Group"});
3074 CommandArgProps = Util.MakeDictionary({"Name", "Type", "Description", "Optional", "Default"});
3075 Types = {};
3076 Commands = {};
3077 CommandsArray = {};
3078 Cmdr = nil;
3079 Hooks = {
3080 BeforeRun = {};
3081 AfterRun = {}
3082 };
3083 Stores = setmetatable({}, {
3084 __index = function (self, k)
3085 self[k] = {}
3086 return self[k]
3087 end
3088 });
3089 AutoExecBuffer = {};
3090}
3091
3092--- Registers a type in the system.
3093-- name: The type Name. This must be unique.
3094function Registry:RegisterType (name, typeObject)
3095 if not name or not typeof(name) == "string" then
3096 error("Invalid type name provided: nil")
3097 end
3098
3099 if not name:find("^[%d%l]%w*$") then
3100 error(('Invalid type name provided: "%s", type names must be alphanumeric and start with a lower-case letter or a digit.'):format(name))
3101 end
3102
3103 for key in pairs(typeObject) do
3104 if self.TypeMethods[key] == nil then
3105 error("Unknown key/method in type \"" .. name .. "\": " .. key)
3106 end
3107 end
3108
3109 if self.Types[name] ~= nil then
3110 error(('Type "%s" has already been registered.'):format(name))
3111 end
3112
3113 typeObject.Name = name
3114 typeObject.DisplayName = typeObject.DisplayName or name
3115
3116 self.Types[name] = typeObject
3117end
3118
3119--- Helper method that registers types from all module scripts in a specific container.
3120function Registry:RegisterTypesIn (container)
3121 for _, object in pairs(container:GetChildren()) do
3122 if object:IsA("ModuleScript") then
3123 object.Parent = self.Cmdr.ReplicatedRoot.Types
3124
3125 require(object)(self)
3126 else
3127 self:RegisterTypesIn(object)
3128 end
3129 end
3130end
3131
3132-- These are exactly the same thing. No one will notice. Except for you, dear reader.
3133Registry.RegisterHooksIn = Registry.RegisterTypesIn
3134
3135--- Registers a command based purely on its definition.
3136-- Prefer using Registry:RegisterCommand for proper handling of server/client model.
3137function Registry:RegisterCommandObject (commandObject)
3138 for key in pairs(commandObject) do
3139 if self.CommandMethods[key] == nil then
3140 error("Unknown key/method in command " .. (commandObject.Name or "unknown command") .. ": " .. key)
3141 end
3142 end
3143
3144 if commandObject.Args then
3145 for i, arg in pairs(commandObject.Args) do
3146 for key in pairs(arg) do
3147 if self.CommandArgProps[key] == nil then
3148 error(('Unknown propery in command "%s" argument #%d: %s'):format(commandObject.Name or "unknown", i, key))
3149 end
3150 end
3151 end
3152 end
3153
3154 if RunService:IsClient() and commandObject.Data and commandObject.Run then
3155 error(('Invalid command implementation provided for "%s": "Data" and "Run" sections are mutually exclusive'):format(commandObject.Name or "unknown"))
3156 end
3157
3158 if commandObject.AutoExec and RunService:IsClient() then
3159 table.insert(self.AutoExecBuffer, commandObject.AutoExec)
3160 self:FlushAutoExecBufferDeferred()
3161 end
3162
3163 -- Unregister the old command if it exists...
3164 local oldCommand = self.Commands[commandObject.Name:lower()]
3165 if oldCommand and oldCommand.Aliases then
3166 for _, alias in pairs(oldCommand.Aliases) do
3167 self.Commands[alias:lower()] = nil
3168 end
3169 elseif not oldCommand then
3170 self.CommandsArray[#self.CommandsArray + 1] = commandObject
3171 end
3172
3173 self.Commands[commandObject.Name:lower()] = commandObject
3174
3175 if commandObject.Aliases then
3176 for _, alias in pairs(commandObject.Aliases) do
3177 self.Commands[alias:lower()] = commandObject
3178 end
3179 end
3180end
3181
3182--- Registers a command definition and its server equivalent.
3183-- Handles replicating the definition to the client.
3184function Registry:RegisterCommand (commandScript, commandServerScript, filter)
3185 local commandObject = require(commandScript)
3186
3187 if commandServerScript then
3188 commandObject.Run = require(commandServerScript)
3189 end
3190
3191 if filter and not filter(commandObject) then
3192 return
3193 end
3194
3195 self:RegisterCommandObject(commandObject)
3196
3197 commandScript.Parent = self.Cmdr.ReplicatedRoot.Commands
3198end
3199
3200--- A helper method that registers all commands inside a specific container.
3201function Registry:RegisterCommandsIn (container, filter)
3202 local skippedServerScripts = {}
3203 local usedServerScripts = {}
3204
3205 for _, commandScript in pairs(container:GetChildren()) do
3206 if commandScript:IsA("ModuleScript") then
3207 if not commandScript.Name:find("Server") then
3208 local serverCommandScript = container:FindFirstChild(commandScript.Name .. "Server")
3209
3210 if serverCommandScript then
3211 usedServerScripts[serverCommandScript] = true
3212 end
3213
3214 self:RegisterCommand(commandScript, serverCommandScript, filter)
3215 else
3216 skippedServerScripts[commandScript] = true
3217 end
3218 else
3219 self:RegisterCommandsIn(commandScript, filter)
3220 end
3221 end
3222
3223 for skippedScript in pairs(skippedServerScripts) do
3224 if not usedServerScripts[skippedScript] then
3225 warn("Command script " .. skippedScript.Name .. " was skipped because it has 'Server' in its name, and has no equivalent shared script.")
3226 end
3227 end
3228end
3229
3230--- Registers the default commands, with an optional filter function or array of groups.
3231function Registry:RegisterDefaultCommands (arrayOrFunc)
3232 local isArray = type(arrayOrFunc) == "table"
3233
3234 if isArray then
3235 arrayOrFunc = Util.MakeDictionary(arrayOrFunc)
3236 end
3237
3238 self:RegisterCommandsIn(self.Cmdr.DefaultCommandsFolder, isArray and function (command)
3239 return arrayOrFunc[command.Group] or false
3240 end or arrayOrFunc)
3241end
3242
3243--- Gets a command definition by name. (Can be an alias)
3244function Registry:GetCommand (name)
3245 name = name or ""
3246 return self.Commands[name:lower()]
3247end
3248
3249--- Returns a unique array of all registered commands (not including aliases)
3250function Registry:GetCommands ()
3251 return self.CommandsArray
3252end
3253
3254--- Returns an array of the names of all registered commands (not including aliases)
3255function Registry:GetCommandsAsStrings ()
3256 local commands = {}
3257
3258 for _, command in pairs(self.CommandsArray) do
3259 commands[#commands + 1] = command.Name
3260 end
3261
3262 return commands
3263end
3264
3265--- Gets a type definition by name.
3266function Registry:GetType (name)
3267 return self.Types[name]
3268end
3269
3270--- Adds a hook to be called when any command is run
3271function Registry:RegisterHook(hookName, callback, priority)
3272 if not self.Hooks[hookName] then
3273 error(("Invalid hook name: %q"):format(hookName), 2)
3274 end
3275
3276 table.insert(self.Hooks[hookName], { callback = callback; priority = priority or 0; } )
3277 table.sort(self.Hooks[hookName], function(a, b) return a.priority < b.priority end)
3278end
3279
3280-- Backwards compatability (deprecated)
3281Registry.AddHook = Registry.RegisterHook
3282
3283--- Returns the store with the given name
3284-- Used for commands that require persistent state, like bind or ban
3285function Registry:GetStore(name)
3286 return self.Stores[name]
3287end
3288
3289--- Calls self:FlushAutoExecBuffer at the end of the frame
3290function Registry:FlushAutoExecBufferDeferred()
3291 if self.AutoExecFlushConnection then
3292 return
3293 end
3294
3295 self.AutoExecFlushConnection = RunService.Heartbeat:Connect(function()
3296 self.AutoExecFlushConnection:Disconnect()
3297 self.AutoExecFlushConnection = nil
3298 self:FlushAutoExecBuffer()
3299 end)
3300end
3301
3302--- Runs all pending auto exec commands in Registry.AutoExecBuffer
3303function Registry:FlushAutoExecBuffer()
3304 for _, commandGroup in ipairs(self.AutoExecBuffer) do
3305 for _, command in ipairs(commandGroup) do
3306 self.Cmdr.Dispatcher:EvaluateAndRun(command)
3307 end
3308 end
3309end
3310
3311return function (cmdr)
3312 Registry.Cmdr = cmdr
3313
3314 return Registry
3315end
3316]]></ProtectedString>
3317 <BinaryString name="Tags"></BinaryString>
3318 </Properties>
3319 </Item>
3320 <Item class="ModuleScript" referent="RBXC7702180AA01435AA52F6BAFCD075B38">
3321 <Properties>
3322 <Content name="LinkedSource"><null></null></Content>
3323 <string name="Name">Util</string>
3324 <string name="ScriptGuid">{4CD0DF40-7153-4B7F-92BD-1697277A9BF8}</string>
3325 <ProtectedString name="Source"><![CDATA[-- A fully extensible and type-safe admin-commands console
3326-- @documentation https://github.com/evaera/Cmdr/blob/master/README.md
3327-- @source https://github.com/evaera/Cmdr
3328-- @rostrap Cmdr
3329-- @author evaera
3330
3331local TextService = game:GetService("TextService")
3332
3333local Util = {}
3334
3335--- Takes an array and flips its values into dictionary keys with value of true.
3336function Util.MakeDictionary(array)
3337 local dictionary = {}
3338
3339 for i = 1, #array do
3340 dictionary[array[i]] = true
3341 end
3342
3343 return dictionary
3344end
3345
3346-- Takes an array of instances and returns (array<names>, array<instances>)
3347local function transformInstanceSet(instances)
3348 local names = {}
3349
3350 for i = 1, #instances do
3351 names[i] = instances[i].Name
3352 end
3353
3354 return names, instances
3355end
3356
3357--- Returns a function that is a fuzzy finder for the specified set or container.
3358-- Can pass an array of strings, array of instances, array of EnumItems,
3359-- array of dictionaries with a Name key or an instance (in which case its children will be used)
3360-- Exact matches will be inserted in the front of the resulting array
3361function Util.MakeFuzzyFinder(setOrContainer)
3362 local names
3363 local instances = {}
3364
3365 if typeof(setOrContainer) == "Enum" then
3366 setOrContainer = setOrContainer:GetEnumItems()
3367 end
3368
3369 if typeof(setOrContainer) == "Instance" then
3370 names, instances = transformInstanceSet(setOrContainer:GetChildren())
3371 elseif typeof(setOrContainer) == "table" then
3372 if
3373 typeof(setOrContainer[1]) == "Instance" or typeof(setOrContainer[1]) == "EnumItem" or
3374 (typeof(setOrContainer[1]) == "table" and typeof(setOrContainer[1].Name) == "string")
3375 then
3376 names, instances = transformInstanceSet(setOrContainer)
3377 elseif type(setOrContainer[1]) == "string" then
3378 names = setOrContainer
3379 elseif setOrContainer[1] ~= nil then
3380 error("MakeFuzzyFinder only accepts tables of instances or strings.")
3381 else
3382 names = {}
3383 end
3384 else
3385 error("MakeFuzzyFinder only accepts a table, Enum, or Instance.")
3386 end
3387
3388 -- Searches the set (checking exact matches first)
3389 return function(text, returnFirst)
3390 local results = {}
3391
3392 for i, name in pairs(names) do
3393 local value = instances and instances[i] or name
3394
3395 -- Continue on checking for non-exact matches...
3396 -- Still need to loop through everything, even on returnFirst, because possibility of an exact match.
3397 if name:lower() == text:lower() then
3398 if returnFirst then
3399 return value
3400 else
3401 table.insert(results, 1, value)
3402 end
3403 elseif name:lower():sub(1, #text) == text:lower() then
3404 results[#results + 1] = value
3405 end
3406 end
3407
3408 if returnFirst then
3409 return results[1]
3410 end
3411
3412 return results
3413 end
3414end
3415
3416--- Takes an array of instances and returns an array of those instances' names.
3417function Util.GetNames(instances)
3418 local names = {}
3419
3420 for i = 1, #instances do
3421 names[i] = instances[i].Name or tostring(instances[i])
3422 end
3423
3424 return names
3425end
3426
3427--- Splits a string using a simple separator (no quote parsing)
3428function Util.SplitStringSimple(inputstr, sep)
3429 if sep == nil then
3430 sep = "%s"
3431 end
3432 local t = {}
3433 local i = 1
3434 for str in string.gmatch(inputstr, "([^" .. sep .. "]+)") do
3435 t[i] = str
3436 i = i + 1
3437 end
3438 return t
3439end
3440
3441local function charCode(n)
3442 return utf8.char(tonumber(n, 16))
3443end
3444
3445-- Discard any excess return values
3446local function first(x)
3447 return x
3448end
3449
3450--- Parses escape sequences into their fully qualified characters
3451function Util.ParseEscapeSequences(text)
3452 return text:gsub("\\(.)", {
3453 t = "\t";
3454 n = "\n";
3455 })
3456 :gsub("\\u(%x%x%x%x)", charCode)
3457 :gsub("\\x(%x%x)", charCode)
3458end
3459
3460local function encodeControlChars(text)
3461 return first(
3462 text
3463 :gsub("\\\\", string.char(17))
3464 :gsub("\\\"", string.char(18))
3465 :gsub("\\'", string.char(19))
3466 )
3467end
3468
3469local function decodeControlChars(text)
3470 return first(
3471 text
3472 :gsub(string.char(17), "\\")
3473 :gsub(string.char(18), "\"")
3474 :gsub(string.char(19), "'")
3475 )
3476end
3477
3478--- Splits a string by space but taking into account quoted sequences which will be treated as a single argument.
3479function Util.SplitString(text, max)
3480 text = encodeControlChars(text)
3481 max = max or math.huge
3482 local t = {}
3483 local spat, epat, buf, quoted = [=[^(['"])]=], [=[(['"])$]=]
3484 for str in text:gmatch("%S+") do
3485 str = Util.ParseEscapeSequences(str)
3486 local squoted = str:match(spat)
3487 local equoted = str:match(epat)
3488 local escaped = str:match([=[(\*)['"]$]=])
3489 if squoted and not quoted and not equoted then
3490 buf, quoted = str, squoted
3491 elseif buf and equoted == quoted and #escaped % 2 == 0 then
3492 str, buf, quoted = buf .. " " .. str, nil, nil
3493 elseif buf then
3494 buf = buf .. " " .. str
3495 end
3496 if not buf then
3497 t[#t + (#t > max and 0 or 1)] = decodeControlChars(str:gsub(spat, ""):gsub(epat, ""))
3498 end
3499 end
3500
3501 if buf then
3502 t[#t + (#t > max and 0 or 1)] = decodeControlChars(buf)
3503 end
3504
3505 return t
3506end
3507
3508--- Takes an array of arguments and a max value.
3509-- Any indicies past the max value will be appended to the last valid argument.
3510function Util.MashExcessArguments(arguments, max)
3511 local t = {}
3512 for i = 1, #arguments do
3513 if i > max then
3514 t[max] = ("%s %s"):format(t[max] or "", arguments[i])
3515 else
3516 t[i] = arguments[i]
3517 end
3518 end
3519 return t
3520end
3521
3522--- Trims whitespace from both sides of a string.
3523function Util.TrimString(s)
3524 return s:match "^%s*(.-)%s*$"
3525end
3526
3527--- Returns the text bounds size based on given text, label (from which properties will be pulled), and optional Vector2 container size.
3528function Util.GetTextSize(text, label, size)
3529 return TextService:GetTextSize(text, label.TextSize, label.Font, size or Vector2.new(label.AbsoluteSize.X, 0))
3530end
3531
3532--- Makes an Enum type.
3533function Util.MakeEnumType(name, values)
3534 local findValue = Util.MakeFuzzyFinder(values)
3535 return {
3536 Validate = function(text)
3537 return findValue(text, true) ~= nil, ("Value %q is not a valid %s."):format(text, name)
3538 end,
3539 Autocomplete = function(text)
3540 local list = findValue(text)
3541 return type(list[1]) ~= "string" and Util.GetNames(list) or list
3542 end,
3543 Parse = function(text)
3544 return findValue(text, true)
3545 end
3546 }
3547end
3548
3549--- Parses a prefixed union type argument (such as %Team)
3550function Util.ParsePrefixedUnionType(typeValue, rawValue)
3551 local split = Util.SplitStringSimple(typeValue)
3552
3553 -- Check prefixes in order from longest to shortest
3554 local types = {}
3555 for i = 1, #split, 2 do
3556 types[#types + 1] = {
3557 prefix = split[i - 1] or "",
3558 type = split[i]
3559 }
3560 end
3561
3562 table.sort(
3563 types,
3564 function(a, b)
3565 return #a.prefix > #b.prefix
3566 end
3567 )
3568
3569 for i = 1, #types do
3570 local t = types[i]
3571
3572 if rawValue:sub(1, #t.prefix) == t.prefix then
3573 return t.type, rawValue:sub(#t.prefix + 1), t.prefix
3574 end
3575 end
3576end
3577
3578--- Creates a listable type from a singlular type
3579function Util.MakeListableType(type)
3580 local listableType = {
3581 Listable = true,
3582 Transform = type.Transform,
3583 Validate = type.Validate,
3584 Autocomplete = type.Autocomplete,
3585 Parse = function(...)
3586 return {type.Parse(...)}
3587 end
3588 }
3589
3590 return listableType
3591end
3592
3593local function encodeCommandEscape(text)
3594 return first(text:gsub("\\%$", string.char(20)))
3595end
3596
3597local function decodeCommandEscape(text)
3598 return first(text:gsub(string.char(20), "$"))
3599end
3600
3601--- Runs embedded commands and replaces them
3602function Util.RunEmbeddedCommands(dispatcher, str)
3603 str = encodeCommandEscape(str)
3604
3605 local results = {}
3606 -- We need to do this because you can't yield in the gsub function
3607 for text in str:gmatch("$(%b{})") do
3608 local doQuotes = true
3609 local commandString = text:sub(2, #text-1)
3610
3611 if commandString:match("^{.+}$") then -- Allow double curly for literal replacement
3612 doQuotes = false
3613 commandString = commandString:sub(2, #commandString-1)
3614 end
3615
3616 results[text] = dispatcher:EvaluateAndRun(commandString)
3617
3618 if doQuotes and results[text]:find("%s") then
3619 results[text] = string.format("%q", results[text])
3620 end
3621 end
3622
3623 return decodeCommandEscape(str:gsub("$(%b{})", results))
3624end
3625
3626--- Replaces arguments in the format $1, $2, $something with whatever the
3627-- given function returns for it.
3628function Util.SubstituteArgs(str, replace)
3629 str = encodeCommandEscape(str)
3630 -- Convert numerical keys to strings
3631 if type(replace) == "table" then
3632 for i = 1, #replace do
3633 local k = tostring(i)
3634 replace[k] = replace[i]
3635
3636 if replace[k]:find("%s") then
3637 replace[k] = string.format("%q", replace[k])
3638 end
3639 end
3640 end
3641 return decodeCommandEscape(str:gsub("$(%w+)", replace))
3642end
3643
3644--- Creates an alias command
3645function Util.MakeAliasCommand(name, commandString)
3646 return {
3647 Name = name,
3648 Aliases = {},
3649 Description = commandString,
3650 Group = "UserAlias",
3651 Args = {
3652 {
3653 Type = "string",
3654 Name = "Argument 1",
3655 Description = "",
3656 Default = ""
3657 },
3658 {
3659 Type = "string",
3660 Name = "Argument 2",
3661 Description = "",
3662 Default = ""
3663 },
3664 {
3665 Type = "string",
3666 Name = "Argument 3",
3667 Description = "",
3668 Default = ""
3669 },
3670 {
3671 Type = "string",
3672 Name = "Argument 4",
3673 Description = "",
3674 Default = ""
3675 },
3676 {
3677 Type = "string",
3678 Name = "Argument 5",
3679 Description = "",
3680 Default = ""
3681 }
3682 },
3683 Run = function(context, ...)
3684 local args = {...}
3685 local commands = Util.SplitStringSimple(commandString, "&&")
3686
3687 for i, command in ipairs(commands) do
3688 local output = context.Dispatcher:EvaluateAndRun(
3689 Util.RunEmbeddedCommands(
3690 context.Dispatcher,
3691 Util.SubstituteArgs(command, args)
3692 )
3693 )
3694 if i == #commands then
3695 return output -- return last command's output
3696 else
3697 context:Reply(output)
3698 end
3699 end
3700
3701 return ""
3702 end
3703 }
3704end
3705
3706--- Makes a type that contains a sequence, e.g. Vector3 or Color3
3707function Util.MakeSequenceType(options)
3708 options = options or {}
3709
3710 assert(options.Parse ~= nil or options.Constructor ~= nil, "MakeSequenceType: Must provide one of: Constructor, Parse")
3711
3712 options.TransformEach = options.TransformEach or function(...)
3713 return ...
3714 end
3715
3716 options.ValidateEach = options.ValidateEach or function()
3717 return true
3718 end
3719
3720 return {
3721 Transform = function (text)
3722 return Util.Map(Util.SplitPrioritizedDelimeter(text, {",", "%s"}), function(value)
3723 return options.TransformEach(value)
3724 end)
3725 end;
3726
3727 Validate = function (components)
3728 if options.Length and #components > options.Length then
3729 return false, ("Maximum of %d values allowed in sequence"):format(options.Length)
3730 end
3731
3732 for i = 1, options.Length or #components do
3733 local valid, reason = options.ValidateEach(components[i], i)
3734
3735 if not valid then
3736 return false, reason
3737 end
3738 end
3739
3740 return true
3741 end;
3742
3743 Parse = options.Parse or function(components)
3744 return options.Constructor(unpack(components))
3745 end
3746 }
3747end
3748
3749--- Splits a string by a single delimeter chosen from the given set.
3750-- The first matching delimeter from the set becomes the split character.
3751function Util.SplitPrioritizedDelimeter(text, delimeters)
3752 for i, delimeter in ipairs(delimeters) do
3753 if text:find(delimeter) or i == #delimeters then
3754 return Util.SplitStringSimple(text, delimeter)
3755 end
3756 end
3757end
3758
3759--- Maps values of an array through a callback and returns an array of mapped values
3760function Util.Map(array, callback)
3761 local results = {}
3762
3763 for i, v in ipairs(array) do
3764 results[i] = callback(v, i)
3765 end
3766
3767 return results
3768end
3769
3770--- Maps arguments #2-n through callback and returns values as tuple
3771function Util.Each(callback, ...)
3772 local results = {}
3773 for i, value in ipairs({...}) do
3774 results[i] = callback(value)
3775 end
3776 return unpack(results)
3777end
3778
3779--- Emulates tabstops with spaces
3780function Util.EmulateTabstops(text, tabWidth)
3781 local result = ""
3782 for i = 1, #text do
3783 local char = text:sub(i, i)
3784
3785 result = result .. (char == "\t" and string.rep(" ", tabWidth - #result % tabWidth) or char)
3786 end
3787 return result
3788end
3789
3790return Util
3791]]></ProtectedString>
3792 <BinaryString name="Tags"></BinaryString>
3793 </Properties>
3794 </Item>
3795 </Item>
3796 <Item class="ModuleScript" referent="RBX896F91C3C40E46B4A1781B9D98F5FB1E">
3797 <Properties>
3798 <Content name="LinkedSource"><null></null></Content>
3799 <string name="Name">Loadstring</string>
3800 <string name="ScriptGuid">{162F5D71-14B8-4FFC-AEDD-E8D0D282AD31}</string>
3801 <ProtectedString name="Source"><![CDATA[--[[
3802 Credit to einsteinK.
3803 Credit to Stravant for LBI.
3804
3805 Credit to the creators of all the other modules used in this.
3806
3807 Sceleratis was here and decided modify some things.
3808
3809 einsteinK was here again to fix a bug in LBI for if-statements
3810--]]
3811
3812local waitDeps = {
3813 'Rerubi';
3814 'LuaK';
3815 'LuaP';
3816 'LuaU';
3817 'LuaX';
3818 'LuaY';
3819 'LuaZ';
3820}
3821
3822for i,v in pairs(waitDeps) do script:WaitForChild(v) end
3823
3824local luaX = require(script.LuaX)
3825local luaY = require(script.LuaY)
3826local luaZ = require(script.LuaZ)
3827local luaU = require(script.LuaU)
3828local rerubi = require(script.Rerubi)
3829
3830luaX:init()
3831local LuaState = {}
3832
3833getfenv().script = nil
3834
3835return function(str,env)
3836 local f,writer,buff,name
3837 local env = env or getfenv(2)
3838 local name = (env.script and env.script:GetFullName())
3839 local ran,error = pcall(function()
3840 local zio = luaZ:init(luaZ:make_getS(str), nil)
3841 if not zio then return error() end
3842 local func = luaY:parser(LuaState, zio, nil, name or "nil")
3843 writer, buff = luaU:make_setS()
3844 luaU:dump(LuaState, func, writer, buff)
3845 f = rerubi(buff.data, env)
3846 end)
3847
3848 if ran then
3849 return f,buff.data
3850 else
3851 return nil,error
3852 end
3853end]]></ProtectedString>
3854 <BinaryString name="Tags"></BinaryString>
3855 </Properties>
3856 <Item class="ModuleScript" referent="RBX139BCE3209D84EE097021EF03E3CD256">
3857 <Properties>
3858 <Content name="LinkedSource"><null></null></Content>
3859 <string name="Name">LuaZ</string>
3860 <string name="ScriptGuid">{6A425F9D-9F7A-417C-A445-E264422EEBDE}</string>
3861 <ProtectedString name="Source"><![CDATA[--[[--------------------------------------------------------------------
3862
3863 lzio.lua
3864 Lua buffered streams in Lua
3865 This file is part of Yueliang.
3866
3867 Copyright (c) 2005-2006 Kein-Hong Man <khman@users.sf.net>
3868 The COPYRIGHT file describes the conditions
3869 under which this software may be distributed.
3870
3871 See the ChangeLog for more information.
3872
3873----------------------------------------------------------------------]]
3874
3875--[[--------------------------------------------------------------------
3876-- Notes:
3877-- * EOZ is implemented as a string, "EOZ"
3878-- * Format of z structure (ZIO)
3879-- z.n -- bytes still unread
3880-- z.p -- last read position position in buffer
3881-- z.reader -- chunk reader function
3882-- z.data -- additional data
3883-- * Current position, p, is now last read index instead of a pointer
3884--
3885-- Not implemented:
3886-- * luaZ_lookahead: used only in lapi.c:lua_load to detect binary chunk
3887-- * luaZ_read: used only in lundump.c:ezread to read +1 bytes
3888-- * luaZ_openspace: dropped; let Lua handle buffers as strings (used in
3889-- lundump.c:LoadString & lvm.c:luaV_concat)
3890-- * luaZ buffer macros: dropped; buffers are handled as strings
3891-- * lauxlib.c:getF reader implementation has an extraline flag to
3892-- skip over a shbang (#!) line, this is not implemented here
3893--
3894-- Added:
3895-- (both of the following are vaguely adapted from lauxlib.c)
3896-- * luaZ:make_getS: create Reader from a string
3897-- * luaZ:make_getF: create Reader that reads from a file
3898--
3899-- Changed in 5.1.x:
3900-- * Chunkreader renamed to Reader (ditto with Chunkwriter)
3901-- * Zio struct: no more name string, added Lua state for reader
3902-- (however, Yueliang readers do not require a Lua state)
3903----------------------------------------------------------------------]]
3904
3905local luaZ = {}
3906
3907------------------------------------------------------------------------
3908-- * reader() should return a string, or nil if nothing else to parse.
3909-- Additional data can be set only during stream initialization
3910-- * Readers are handled in lauxlib.c, see luaL_load(file|buffer|string)
3911-- * LUAL_BUFFERSIZE=BUFSIZ=512 in make_getF() (located in luaconf.h)
3912-- * Original Reader typedef:
3913-- const char * (*lua_Reader) (lua_State *L, void *ud, size_t *sz);
3914-- * This Lua chunk reader implementation:
3915-- returns string or nil, no arguments to function
3916------------------------------------------------------------------------
3917
3918------------------------------------------------------------------------
3919-- create a chunk reader from a source string
3920------------------------------------------------------------------------
3921function luaZ:make_getS(buff)
3922 local b = buff
3923 return function() -- chunk reader anonymous function here
3924 if not b then return nil end
3925 local data = b
3926 b = nil
3927 return data
3928 end
3929end
3930
3931------------------------------------------------------------------------
3932-- create a chunk reader from a source file
3933------------------------------------------------------------------------
3934--[[
3935function luaZ:make_getF(filename)
3936 local LUAL_BUFFERSIZE = 512
3937 local h = io.open(filename, "r")
3938 if not h then return nil end
3939 return function() -- chunk reader anonymous function here
3940 if not h or io.type(h) == "closed file" then return nil end
3941 local buff = h:read(LUAL_BUFFERSIZE)
3942 if not buff then h:close(); h = nil end
3943 return buff
3944 end
3945end
3946--]]
3947------------------------------------------------------------------------
3948-- creates a zio input stream
3949-- returns the ZIO structure, z
3950------------------------------------------------------------------------
3951function luaZ:init(reader, data, name)
3952 if not reader then return end
3953 local z = {}
3954 z.reader = reader
3955 z.data = data or ""
3956 z.name = name
3957 -- set up additional data for reading
3958 if not data or data == "" then z.n = 0 else z.n = #data end
3959 z.p = 0
3960 return z
3961end
3962
3963------------------------------------------------------------------------
3964-- fill up input buffer
3965------------------------------------------------------------------------
3966function luaZ:fill(z)
3967 local buff = z.reader()
3968 z.data = buff
3969 if not buff or buff == "" then return "EOZ" end
3970 z.n, z.p = #buff - 1, 1
3971 return string.sub(buff, 1, 1)
3972end
3973
3974------------------------------------------------------------------------
3975-- get next character from the input stream
3976-- * local n, p are used to optimize code generation
3977------------------------------------------------------------------------
3978function luaZ:zgetc(z)
3979 local n, p = z.n, z.p + 1
3980 if n > 0 then
3981 z.n, z.p = n - 1, p
3982 return string.sub(z.data, p, p)
3983 else
3984 return self:fill(z)
3985 end
3986end
3987
3988return luaZ]]></ProtectedString>
3989 <BinaryString name="Tags"></BinaryString>
3990 </Properties>
3991 </Item>
3992 <Item class="ModuleScript" referent="RBX74ED26F683644E6283E6F69B9920D64A">
3993 <Properties>
3994 <Content name="LinkedSource"><null></null></Content>
3995 <string name="Name">LuaX</string>
3996 <string name="ScriptGuid">{9514EDB3-FB50-4B26-B929-88952220C9D9}</string>
3997 <ProtectedString name="Source"><![CDATA[--[[--------------------------------------------------------------------
3998
3999 llex.lua
4000 Lua lexical analyzer in Lua
4001 This file is part of Yueliang.
4002
4003 Copyright (c) 2005-2006 Kein-Hong Man <khman@users.sf.net>
4004 The COPYRIGHT file describes the conditions
4005 under which this software may be distributed.
4006
4007 See the ChangeLog for more information.
4008
4009----------------------------------------------------------------------]]
4010
4011--[[--------------------------------------------------------------------
4012-- Notes:
4013-- * intended to 'imitate' llex.c code; performance is not a concern
4014-- * tokens are strings; code structure largely retained
4015-- * deleted stuff (compared to llex.c) are noted, comments retained
4016-- * nextc() returns the currently read character to simplify coding
4017-- here; next() in llex.c does not return anything
4018-- * compatibility code is marked with "--#" comments
4019--
4020-- Added:
4021-- * luaX:chunkid (function luaO_chunkid from lobject.c)
4022-- * luaX:str2d (function luaO_str2d from lobject.c)
4023-- * luaX.LUA_QS used in luaX:lexerror (from luaconf.h)
4024-- * luaX.LUA_COMPAT_LSTR in luaX:read_long_string (from luaconf.h)
4025-- * luaX.MAX_INT used in luaX:inclinenumber (from llimits.h)
4026--
4027-- To use the lexer:
4028-- (1) luaX:init() to initialize the lexer
4029-- (2) luaX:setinput() to set the input stream to lex
4030-- (3) call luaX:next() or luaX:luaX:lookahead() to get tokens,
4031-- until "TK_EOS": luaX:next()
4032-- * since EOZ is returned as a string, be careful when regexp testing
4033--
4034-- Not implemented:
4035-- * luaX_newstring: not required by this Lua implementation
4036-- * buffer MAX_SIZET size limit (from llimits.h) test not implemented
4037-- in the interest of performance
4038-- * locale-aware number handling is largely redundant as Lua's
4039-- tonumber() function is already capable of this
4040--
4041-- Changed in 5.1.x:
4042-- * TK_NAME token order moved down
4043-- * string representation for TK_NAME, TK_NUMBER, TK_STRING changed
4044-- * token struct renamed to lower case (LS -> ls)
4045-- * LexState struct: removed nestlevel, added decpoint
4046-- * error message functions have been greatly simplified
4047-- * token2string renamed to luaX_tokens, exposed in llex.h
4048-- * lexer now handles all kinds of newlines, including CRLF
4049-- * shbang first line handling removed from luaX:setinput;
4050-- it is now done in lauxlib.c (luaL_loadfile)
4051-- * next(ls) macro renamed to nextc(ls) due to new luaX_next function
4052-- * EXTRABUFF and MAXNOCHECK removed due to lexer changes
4053-- * checkbuffer(ls, len) macro deleted
4054-- * luaX:read_numeral now has 3 support functions: luaX:trydecpoint,
4055-- luaX:buffreplace and (luaO_str2d from lobject.c) luaX:str2d
4056-- * luaX:read_numeral is now more promiscuous in slurping characters;
4057-- hexadecimal numbers was added, locale-aware decimal points too
4058-- * luaX:skip_sep is new; used by luaX:read_long_string
4059-- * luaX:read_long_string handles new-style long blocks, with some
4060-- optional compatibility code
4061-- * luaX:llex: parts changed to support new-style long blocks
4062-- * luaX:llex: readname functionality has been folded in
4063-- * luaX:llex: removed test for control characters
4064--
4065--------------------------------------------------------------------]]
4066
4067local luaZ = require(script.Parent.LuaZ)
4068
4069local luaX = {}
4070
4071-- FIRST_RESERVED is not required as tokens are manipulated as strings
4072-- TOKEN_LEN deleted; maximum length of a reserved word not needed
4073
4074------------------------------------------------------------------------
4075-- "ORDER RESERVED" deleted; enumeration in one place: luaX.RESERVED
4076------------------------------------------------------------------------
4077
4078-- terminal symbols denoted by reserved words: TK_AND to TK_WHILE
4079-- other terminal symbols: TK_NAME to TK_EOS
4080luaX.RESERVED = [[
4081TK_AND and
4082TK_BREAK break
4083TK_DO do
4084TK_ELSE else
4085TK_ELSEIF elseif
4086TK_END end
4087TK_FALSE false
4088TK_FOR for
4089TK_FUNCTION function
4090TK_IF if
4091TK_IN in
4092TK_LOCAL local
4093TK_NIL nil
4094TK_NOT not
4095TK_OR or
4096TK_REPEAT repeat
4097TK_RETURN return
4098TK_THEN then
4099TK_TRUE true
4100TK_UNTIL until
4101TK_WHILE while
4102TK_CONCAT ..
4103TK_DOTS ...
4104TK_EQ ==
4105TK_GE >=
4106TK_LE <=
4107TK_NE ~=
4108TK_NAME <name>
4109TK_NUMBER <number>
4110TK_STRING <string>
4111TK_EOS <eof>]]
4112
4113-- NUM_RESERVED is not required; number of reserved words
4114
4115--[[--------------------------------------------------------------------
4116-- Instead of passing seminfo, the Token struct (e.g. ls.t) is passed
4117-- so that lexer functions can use its table element, ls.t.seminfo
4118--
4119-- SemInfo (struct no longer needed, a mixed-type value is used)
4120--
4121-- Token (struct of ls.t and ls.lookahead):
4122-- token -- token symbol
4123-- seminfo -- semantics information
4124--
4125-- LexState (struct of ls; ls is initialized by luaX:setinput):
4126-- current -- current character (charint)
4127-- linenumber -- input line counter
4128-- lastline -- line of last token 'consumed'
4129-- t -- current token (table: struct Token)
4130-- lookahead -- look ahead token (table: struct Token)
4131-- fs -- 'FuncState' is private to the parser
4132-- L -- LuaState
4133-- z -- input stream
4134-- buff -- buffer for tokens
4135-- source -- current source name
4136-- decpoint -- locale decimal point
4137-- nestlevel -- level of nested non-terminals
4138----------------------------------------------------------------------]]
4139
4140-- luaX.tokens (was luaX_tokens) is now a hash; see luaX:init
4141
4142luaX.MAXSRC = 80
4143luaX.MAX_INT = 2147483645 -- constants from elsewhere (see above)
4144luaX.LUA_QS = "'%s'"
4145luaX.LUA_COMPAT_LSTR = 1
4146--luaX.MAX_SIZET = 4294967293
4147
4148------------------------------------------------------------------------
4149-- initialize lexer
4150-- * original luaX_init has code to create and register token strings
4151-- * luaX.tokens: TK_* -> token
4152-- * luaX.enums: token -> TK_* (used in luaX:llex)
4153------------------------------------------------------------------------
4154function luaX:init()
4155 local tokens, enums = {}, {}
4156 for v in string.gmatch(self.RESERVED, "[^\n]+") do
4157 local _, _, tok, str = string.find(v, "(%S+)%s+(%S+)")
4158 tokens[tok] = str
4159 enums[str] = tok
4160 end
4161 self.tokens = tokens
4162 self.enums = enums
4163end
4164
4165------------------------------------------------------------------------
4166-- returns a suitably-formatted chunk name or id
4167-- * from lobject.c, used in llex.c and ldebug.c
4168-- * the result, out, is returned (was first argument)
4169------------------------------------------------------------------------
4170function luaX:chunkid(source, bufflen)
4171 local out
4172 local first = string.sub(source, 1, 1)
4173 if first == "=" then
4174 out = string.sub(source, 2, bufflen) -- remove first char
4175 else -- out = "source", or "...source"
4176 if first == "@" then
4177 source = string.sub(source, 2) -- skip the '@'
4178 bufflen = bufflen - #" '...' "
4179 local l = #source
4180 out = ""
4181 if l > bufflen then
4182 source = string.sub(source, 1 + l - bufflen) -- get last part of file name
4183 out = out.."..."
4184 end
4185 out = out..source
4186 else -- out = [string "string"]
4187 local len = string.find(source, "[\n\r]") -- stop at first newline
4188 len = len and (len - 1) or #source
4189 bufflen = bufflen - #(" [string \"...\"] ")
4190 if len > bufflen then len = bufflen end
4191 out = "[string \""
4192 if len < #source then -- must truncate?
4193 out = out..string.sub(source, 1, len).."..."
4194 else
4195 out = out..source
4196 end
4197 out = out.."\"]"
4198 end
4199 end
4200 return out
4201end
4202
4203--[[--------------------------------------------------------------------
4204-- Support functions for lexer
4205-- * all lexer errors eventually reaches lexerror:
4206 syntaxerror -> lexerror
4207----------------------------------------------------------------------]]
4208
4209------------------------------------------------------------------------
4210-- look up token and return keyword if found (also called by parser)
4211------------------------------------------------------------------------
4212function luaX:token2str(ls, token)
4213 if string.sub(token, 1, 3) ~= "TK_" then
4214 if string.find(token, "%c") then
4215 return string.format("char(%d)", string.byte(token))
4216 end
4217 return token
4218 else
4219 end
4220 return self.tokens[token]
4221end
4222
4223------------------------------------------------------------------------
4224-- throws a lexer error
4225-- * txtToken has been made local to luaX:lexerror
4226-- * can't communicate LUA_ERRSYNTAX, so it is unimplemented
4227------------------------------------------------------------------------
4228function luaX:lexerror(ls, msg, token)
4229 local function txtToken(ls, token)
4230 if token == "TK_NAME" or
4231 token == "TK_STRING" or
4232 token == "TK_NUMBER" then
4233 return ls.buff
4234 else
4235 return self:token2str(ls, token)
4236 end
4237 end
4238 local buff = self:chunkid(ls.source, self.MAXSRC)
4239 local msg = string.format("%s:%d: %s", buff, ls.linenumber, msg)
4240 if token then
4241 msg = string.format("%s near "..self.LUA_QS, msg, txtToken(ls, token))
4242 end
4243 -- luaD_throw(ls->L, LUA_ERRSYNTAX)
4244 error(msg)
4245end
4246
4247------------------------------------------------------------------------
4248-- throws a syntax error (mainly called by parser)
4249-- * ls.t.token has to be set by the function calling luaX:llex
4250-- (see luaX:next and luaX:lookahead elsewhere in this file)
4251------------------------------------------------------------------------
4252function luaX:syntaxerror(ls, msg)
4253 self:lexerror(ls, msg, ls.t.token)
4254end
4255
4256------------------------------------------------------------------------
4257-- move on to next line
4258------------------------------------------------------------------------
4259function luaX:currIsNewline(ls)
4260 return ls.current == "\n" or ls.current == "\r"
4261end
4262
4263function luaX:inclinenumber(ls)
4264 local old = ls.current
4265 -- lua_assert(currIsNewline(ls))
4266 self:nextc(ls) -- skip '\n' or '\r'
4267 if self:currIsNewline(ls) and ls.current ~= old then
4268 self:nextc(ls) -- skip '\n\r' or '\r\n'
4269 end
4270 ls.linenumber = ls.linenumber + 1
4271 if ls.linenumber >= self.MAX_INT then
4272 self:syntaxerror(ls, "chunk has too many lines")
4273 end
4274end
4275
4276------------------------------------------------------------------------
4277-- initializes an input stream for lexing
4278-- * if ls (the lexer state) is passed as a table, then it is filled in,
4279-- otherwise it has to be retrieved as a return value
4280-- * LUA_MINBUFFER not used; buffer handling not required any more
4281------------------------------------------------------------------------
4282function luaX:setinput(L, ls, z, source)
4283 if not ls then ls = {} end -- create struct
4284 if not ls.lookahead then ls.lookahead = {} end
4285 if not ls.t then ls.t = {} end
4286 ls.decpoint = "."
4287 ls.L = L
4288 ls.lookahead.token = "TK_EOS" -- no look-ahead token
4289 ls.z = z
4290 ls.fs = nil
4291 ls.linenumber = 1
4292 ls.lastline = 1
4293 ls.source = source
4294 self:nextc(ls) -- read first char
4295end
4296
4297--[[--------------------------------------------------------------------
4298-- LEXICAL ANALYZER
4299----------------------------------------------------------------------]]
4300
4301------------------------------------------------------------------------
4302-- checks if current character read is found in the set 'set'
4303------------------------------------------------------------------------
4304function luaX:check_next(ls, set)
4305 if not string.find(set, ls.current, 1, 1) then
4306 return false
4307 end
4308 self:save_and_next(ls)
4309 return true
4310end
4311
4312------------------------------------------------------------------------
4313-- retrieve next token, checking the lookahead buffer if necessary
4314-- * note that the macro next(ls) in llex.c is now luaX:nextc
4315-- * utilized used in lparser.c (various places)
4316------------------------------------------------------------------------
4317function luaX:next(ls)
4318 ls.lastline = ls.linenumber
4319 if ls.lookahead.token ~= "TK_EOS" then -- is there a look-ahead token?
4320 -- this must be copy-by-value
4321 ls.t.seminfo = ls.lookahead.seminfo -- use this one
4322 ls.t.token = ls.lookahead.token
4323 ls.lookahead.token = "TK_EOS" -- and discharge it
4324 else
4325 ls.t.token = self:llex(ls, ls.t) -- read next token
4326 end
4327end
4328
4329------------------------------------------------------------------------
4330-- fill in the lookahead buffer
4331-- * utilized used in lparser.c:constructor
4332------------------------------------------------------------------------
4333function luaX:lookahead(ls)
4334 -- lua_assert(ls.lookahead.token == "TK_EOS")
4335 ls.lookahead.token = self:llex(ls, ls.lookahead)
4336end
4337
4338------------------------------------------------------------------------
4339-- gets the next character and returns it
4340-- * this is the next() macro in llex.c; see notes at the beginning
4341------------------------------------------------------------------------
4342function luaX:nextc(ls)
4343 local c = luaZ:zgetc(ls.z)
4344 ls.current = c
4345 return c
4346end
4347
4348------------------------------------------------------------------------
4349-- saves the given character into the token buffer
4350-- * buffer handling code removed, not used in this implementation
4351-- * test for maximum token buffer length not used, makes things faster
4352------------------------------------------------------------------------
4353
4354function luaX:save(ls, c)
4355 local buff = ls.buff
4356 -- if you want to use this, please uncomment luaX.MAX_SIZET further up
4357 --if #buff > self.MAX_SIZET then
4358 -- self:lexerror(ls, "lexical element too long")
4359 --end
4360 ls.buff = buff..c
4361end
4362
4363------------------------------------------------------------------------
4364-- save current character into token buffer, grabs next character
4365-- * like luaX:nextc, returns the character read for convenience
4366------------------------------------------------------------------------
4367function luaX:save_and_next(ls)
4368 self:save(ls, ls.current)
4369 return self:nextc(ls)
4370end
4371
4372------------------------------------------------------------------------
4373-- LUA_NUMBER
4374-- * luaX:read_numeral is the main lexer function to read a number
4375-- * luaX:str2d, luaX:buffreplace, luaX:trydecpoint are support functions
4376------------------------------------------------------------------------
4377
4378------------------------------------------------------------------------
4379-- string to number converter (was luaO_str2d from lobject.c)
4380-- * returns the number, nil if fails (originally returns a boolean)
4381-- * conversion function originally lua_str2number(s,p), a macro which
4382-- maps to the strtod() function by default (from luaconf.h)
4383------------------------------------------------------------------------
4384function luaX:str2d(s)
4385 local result = tonumber(s)
4386 if result then return result end
4387 -- conversion failed
4388 if string.lower(string.sub(s, 1, 2)) == "0x" then -- maybe an hexadecimal constant?
4389 result = tonumber(s, 16)
4390 if result then return result end -- most common case
4391 -- Was: invalid trailing characters?
4392 -- In C, this function then skips over trailing spaces.
4393 -- true is returned if nothing else is found except for spaces.
4394 -- If there is still something else, then it returns a false.
4395 -- All this is not necessary using Lua's tonumber.
4396 end
4397 return nil
4398end
4399
4400------------------------------------------------------------------------
4401-- single-character replacement, for locale-aware decimal points
4402------------------------------------------------------------------------
4403function luaX:buffreplace(ls, from, to)
4404 local result, buff = "", ls.buff
4405 for p = 1, #buff do
4406 local c = string.sub(buff, p, p)
4407 if c == from then c = to end
4408 result = result..c
4409 end
4410 ls.buff = result
4411end
4412
4413------------------------------------------------------------------------
4414-- Attempt to convert a number by translating '.' decimal points to
4415-- the decimal point character used by the current locale. This is not
4416-- needed in Yueliang as Lua's tonumber() is already locale-aware.
4417-- Instead, the code is here in case the user implements localeconv().
4418------------------------------------------------------------------------
4419function luaX:trydecpoint(ls, Token)
4420 -- format error: try to update decimal point separator
4421 local old = ls.decpoint
4422 -- translate the following to Lua if you implement localeconv():
4423 -- struct lconv *cv = localeconv();
4424 -- ls->decpoint = (cv ? cv->decimal_point[0] : '.');
4425 self:buffreplace(ls, old, ls.decpoint) -- try updated decimal separator
4426 local seminfo = self:str2d(ls.buff)
4427 Token.seminfo = seminfo
4428 if not seminfo then
4429 -- format error with correct decimal point: no more options
4430 self:buffreplace(ls, ls.decpoint, ".") -- undo change (for error message)
4431 self:lexerror(ls, "malformed number", "TK_NUMBER")
4432 end
4433end
4434
4435------------------------------------------------------------------------
4436-- main number conversion function
4437-- * "^%w$" needed in the scan in order to detect "EOZ"
4438------------------------------------------------------------------------
4439function luaX:read_numeral(ls, Token)
4440 -- lua_assert(string.find(ls.current, "%d"))
4441 repeat
4442 self:save_and_next(ls)
4443 until string.find(ls.current, "%D") and ls.current ~= "."
4444 if self:check_next(ls, "Ee") then -- 'E'?
4445 self:check_next(ls, "+-") -- optional exponent sign
4446 end
4447 while string.find(ls.current, "^%w$") or ls.current == "_" do
4448 self:save_and_next(ls)
4449 end
4450 self:buffreplace(ls, ".", ls.decpoint) -- follow locale for decimal point
4451 local seminfo = self:str2d(ls.buff)
4452 Token.seminfo = seminfo
4453 if not seminfo then -- format error?
4454 self:trydecpoint(ls, Token) -- try to update decimal point separator
4455 end
4456end
4457
4458------------------------------------------------------------------------
4459-- count separators ("=") in a long string delimiter
4460-- * used by luaX:read_long_string
4461------------------------------------------------------------------------
4462function luaX:skip_sep(ls)
4463 local count = 0
4464 local s = ls.current
4465 -- lua_assert(s == "[" or s == "]")
4466 self:save_and_next(ls)
4467 while ls.current == "=" do
4468 self:save_and_next(ls)
4469 count = count + 1
4470 end
4471 return (ls.current == s) and count or (-count) - 1
4472end
4473
4474------------------------------------------------------------------------
4475-- reads a long string or long comment
4476------------------------------------------------------------------------
4477function luaX:read_long_string(ls, Token, sep)
4478 local cont = 0
4479 self:save_and_next(ls) -- skip 2nd '['
4480 if self:currIsNewline(ls) then -- string starts with a newline?
4481 self:inclinenumber(ls) -- skip it
4482 end
4483 while true do
4484 local c = ls.current
4485 if c == "EOZ" then
4486 self:lexerror(ls, Token and "unfinished long string" or
4487 "unfinished long comment", "TK_EOS")
4488 elseif c == "[" then
4489 --# compatibility code start
4490 if self.LUA_COMPAT_LSTR then
4491 if self:skip_sep(ls) == sep then
4492 self:save_and_next(ls) -- skip 2nd '['
4493 cont = cont + 1
4494 --# compatibility code start
4495 if self.LUA_COMPAT_LSTR == 1 then
4496 if sep == 0 then
4497 self:lexerror(ls, "nesting of [[...]] is deprecated", "[")
4498 end
4499 end
4500 --# compatibility code end
4501 end
4502 end
4503 --# compatibility code end
4504 elseif c == "]" then
4505 if self:skip_sep(ls) == sep then
4506 self:save_and_next(ls) -- skip 2nd ']'
4507 --# compatibility code start
4508 if self.LUA_COMPAT_LSTR and self.LUA_COMPAT_LSTR == 2 then
4509 cont = cont - 1
4510 if sep == 0 and cont >= 0 then break end
4511 end
4512 --# compatibility code end
4513 break
4514 end
4515 elseif self:currIsNewline(ls) then
4516 self:save(ls, "\n")
4517 self:inclinenumber(ls)
4518 if not Token then ls.buff = "" end -- avoid wasting space
4519 else -- default
4520 if Token then
4521 self:save_and_next(ls)
4522 else
4523 self:nextc(ls)
4524 end
4525 end--if c
4526 end--while
4527 if Token then
4528 local p = 3 + sep
4529 Token.seminfo = string.sub(ls.buff, p, -p)
4530 end
4531end
4532
4533------------------------------------------------------------------------
4534-- reads a string
4535-- * has been restructured significantly compared to the original C code
4536------------------------------------------------------------------------
4537
4538function luaX:read_string(ls, del, Token)
4539 self:save_and_next(ls)
4540 while ls.current ~= del do
4541 local c = ls.current
4542 if c == "EOZ" then
4543 self:lexerror(ls, "unfinished string", "TK_EOS")
4544 elseif self:currIsNewline(ls) then
4545 self:lexerror(ls, "unfinished string", "TK_STRING")
4546 elseif c == "\\" then
4547 c = self:nextc(ls) -- do not save the '\'
4548 if self:currIsNewline(ls) then -- go through
4549 self:save(ls, "\n")
4550 self:inclinenumber(ls)
4551 elseif c ~= "EOZ" then -- will raise an error next loop
4552 -- escapes handling greatly simplified here:
4553 local i = string.find("abfnrtv", c, 1, 1)
4554 if i then
4555 self:save(ls, string.sub("\a\b\f\n\r\t\v", i, i))
4556 self:nextc(ls)
4557 elseif not string.find(c, "%d") then
4558 self:save_and_next(ls) -- handles \\, \", \', and \?
4559 else -- \xxx
4560 c, i = 0, 0
4561 repeat
4562 c = 10 * c + ls.current
4563 self:nextc(ls)
4564 i = i + 1
4565 until i >= 3 or not string.find(ls.current, "%d")
4566 if c > 255 then -- UCHAR_MAX
4567 self:lexerror(ls, "escape sequence too large", "TK_STRING")
4568 end
4569 self:save(ls, string.char(c))
4570 end
4571 end
4572 else
4573 self:save_and_next(ls)
4574 end--if c
4575 end--while
4576 self:save_and_next(ls) -- skip delimiter
4577 Token.seminfo = string.sub(ls.buff, 2, -2)
4578end
4579
4580------------------------------------------------------------------------
4581-- main lexer function
4582------------------------------------------------------------------------
4583function luaX:llex(ls, Token)
4584 ls.buff = ""
4585 while true do
4586 local c = ls.current
4587 ----------------------------------------------------------------
4588 if self:currIsNewline(ls) then
4589 self:inclinenumber(ls)
4590 ----------------------------------------------------------------
4591 elseif c == "-" then
4592 c = self:nextc(ls)
4593 if c ~= "-" then return "-" end
4594 -- else is a comment
4595 local sep = -1
4596 if self:nextc(ls) == '[' then
4597 sep = self:skip_sep(ls)
4598 ls.buff = "" -- 'skip_sep' may dirty the buffer
4599 end
4600 if sep >= 0 then
4601 self:read_long_string(ls, nil, sep) -- long comment
4602 ls.buff = ""
4603 else -- else short comment
4604 while not self:currIsNewline(ls) and ls.current ~= "EOZ" do
4605 self:nextc(ls)
4606 end
4607 end
4608 ----------------------------------------------------------------
4609 elseif c == "[" then
4610 local sep = self:skip_sep(ls)
4611 if sep >= 0 then
4612 self:read_long_string(ls, Token, sep)
4613 return "TK_STRING"
4614 elseif sep == -1 then
4615 return "["
4616 else
4617 self:lexerror(ls, "invalid long string delimiter", "TK_STRING")
4618 end
4619 ----------------------------------------------------------------
4620 elseif c == "=" then
4621 c = self:nextc(ls)
4622 if c ~= "=" then return "="
4623 else self:nextc(ls); return "TK_EQ" end
4624 ----------------------------------------------------------------
4625 elseif c == "<" then
4626 c = self:nextc(ls)
4627 if c ~= "=" then return "<"
4628 else self:nextc(ls); return "TK_LE" end
4629 ----------------------------------------------------------------
4630 elseif c == ">" then
4631 c = self:nextc(ls)
4632 if c ~= "=" then return ">"
4633 else self:nextc(ls); return "TK_GE" end
4634 ----------------------------------------------------------------
4635 elseif c == "~" then
4636 c = self:nextc(ls)
4637 if c ~= "=" then return "~"
4638 else self:nextc(ls); return "TK_NE" end
4639 ----------------------------------------------------------------
4640 elseif c == "\"" or c == "'" then
4641 self:read_string(ls, c, Token)
4642 return "TK_STRING"
4643 ----------------------------------------------------------------
4644 elseif c == "." then
4645 c = self:save_and_next(ls)
4646 if self:check_next(ls, ".") then
4647 if self:check_next(ls, ".") then
4648 return "TK_DOTS" -- ...
4649 else return "TK_CONCAT" -- ..
4650 end
4651 elseif not string.find(c, "%d") then
4652 return "."
4653 else
4654 self:read_numeral(ls, Token)
4655 return "TK_NUMBER"
4656 end
4657 ----------------------------------------------------------------
4658 elseif c == "EOZ" then
4659 return "TK_EOS"
4660 ----------------------------------------------------------------
4661 else -- default
4662 if string.find(c, "%s") then
4663 -- lua_assert(self:currIsNewline(ls))
4664 self:nextc(ls)
4665 elseif string.find(c, "%d") then
4666 self:read_numeral(ls, Token)
4667 return "TK_NUMBER"
4668 elseif string.find(c, "[_%a]") then
4669 -- identifier or reserved word
4670 repeat
4671 c = self:save_and_next(ls)
4672 until c == "EOZ" or not string.find(c, "[_%w]")
4673 local ts = ls.buff
4674 local tok = self.enums[ts]
4675 if tok then return tok end -- reserved word?
4676 Token.seminfo = ts
4677 return "TK_NAME"
4678 else
4679 self:nextc(ls)
4680 return c -- single-char tokens (+ - / ...)
4681 end
4682 ----------------------------------------------------------------
4683 end--if c
4684 end--while
4685end
4686
4687return luaX]]></ProtectedString>
4688 <BinaryString name="Tags"></BinaryString>
4689 </Properties>
4690 </Item>
4691 <Item class="ModuleScript" referent="RBX0C77EABDF4E04495A61E17E0F3651AB2">
4692 <Properties>
4693 <Content name="LinkedSource"><null></null></Content>
4694 <string name="Name">LuaY</string>
4695 <string name="ScriptGuid">{29BEDDC6-A318-4D8E-9DAB-C65576A9430E}</string>
4696 <ProtectedString name="Source"><![CDATA[--[[--------------------------------------------------------------------
4697
4698 lparser.lua
4699 Lua 5 parser in Lua
4700 This file is part of Yueliang.
4701
4702 Copyright (c) 2005-2007 Kein-Hong Man <khman@users.sf.net>
4703 The COPYRIGHT file describes the conditions
4704 under which this software may be distributed.
4705
4706 See the ChangeLog for more information.
4707
4708----------------------------------------------------------------------]]
4709
4710--[[--------------------------------------------------------------------
4711-- Notes:
4712-- * some unused C code that were not converted are kept as comments
4713-- * LUA_COMPAT_VARARG option changed into a comment block
4714-- * for value/size specific code added, look for 'NOTE: '
4715--
4716-- Not implemented:
4717-- * luaX_newstring not needed by this Lua implementation
4718-- * luaG_checkcode() in assert is not currently implemented
4719--
4720-- Added:
4721-- * some constants added from various header files
4722-- * luaY.LUA_QS used in error_expected, check_match (from luaconf.h)
4723-- * luaY:LUA_QL needed for error messages (from luaconf.h)
4724-- * luaY:growvector (from lmem.h) -- skeleton only, limit checking
4725-- * luaY.SHRT_MAX (from <limits.h>) for registerlocalvar
4726-- * luaY:newproto (from lfunc.c)
4727-- * luaY:int2fb (from lobject.c)
4728-- * NOTE: HASARG_MASK, for implementing a VARARG_HASARG bit operation
4729-- * NOTE: value-specific code for VARARG_NEEDSARG to replace a bitop
4730--
4731-- Changed in 5.1.x:
4732-- * various code changes are not detailed...
4733-- * names of constants may have changed, e.g. added a LUAI_ prefix
4734-- * struct expkind: added VKNUM, VVARARG; VCALL's info changed?
4735-- * struct expdesc: added nval
4736-- * struct FuncState: upvalues data type changed to upvaldesc
4737-- * macro hasmultret is new
4738-- * function checklimit moved to parser from lexer
4739-- * functions anchor_token, errorlimit, checknext are new
4740-- * checknext is new, equivalent to 5.0.x's check, see check too
4741-- * luaY:next and luaY:lookahead moved to lexer
4742-- * break keyword no longer skipped in luaY:breakstat
4743-- * function new_localvarstr replaced by new_localvarliteral
4744-- * registerlocalvar limits local variables to SHRT_MAX
4745-- * create_local deleted, new_localvarliteral used instead
4746-- * constant LUAI_MAXUPVALUES increased to 60
4747-- * constants MAXPARAMS, LUA_MAXPARSERLEVEL, MAXSTACK removed
4748-- * function interface changed: singlevaraux, singlevar
4749-- * enterlevel and leavelevel uses nCcalls to track call depth
4750-- * added a name argument to main entry function, luaY:parser
4751-- * function luaY_index changed to yindex
4752-- * luaY:int2fb()'s table size encoding format has been changed
4753-- * luaY:log2() no longer needed for table constructors
4754-- * function code_params deleted, functionality folded in parlist
4755-- * vararg flags handling (is_vararg) changes; also see VARARG_*
4756-- * LUA_COMPATUPSYNTAX section for old-style upvalues removed
4757-- * repeatstat() calls chunk() instead of block()
4758-- * function interface changed: cond, test_then_block
4759-- * while statement implementation considerably simplified; MAXEXPWHILE
4760-- and EXTRAEXP no longer required, no limits to the complexity of a
4761-- while condition
4762-- * repeat, forbody statement implementation has major changes,
4763-- mostly due to new scoping behaviour of local variables
4764-- * OPR_MULT renamed to OPR_MUL
4765----------------------------------------------------------------------]]
4766
4767--requires luaP, luaX, luaK
4768local luaY = {}
4769local luaX = require(script.Parent.LuaX)
4770local luaK = require(script.Parent.LuaK)(luaY)
4771local luaP = require(script.Parent.LuaP)
4772
4773--[[--------------------------------------------------------------------
4774-- Expression descriptor
4775-- * expkind changed to string constants; luaY:assignment was the only
4776-- function to use a relational operator with this enumeration
4777-- VVOID -- no value
4778-- VNIL -- no value
4779-- VTRUE -- no value
4780-- VFALSE -- no value
4781-- VK -- info = index of constant in 'k'
4782-- VKNUM -- nval = numerical value
4783-- VLOCAL -- info = local register
4784-- VUPVAL, -- info = index of upvalue in 'upvalues'
4785-- VGLOBAL -- info = index of table; aux = index of global name in 'k'
4786-- VINDEXED -- info = table register; aux = index register (or 'k')
4787-- VJMP -- info = instruction pc
4788-- VRELOCABLE -- info = instruction pc
4789-- VNONRELOC -- info = result register
4790-- VCALL -- info = instruction pc
4791-- VVARARG -- info = instruction pc
4792} ----------------------------------------------------------------------]]
4793
4794--[[--------------------------------------------------------------------
4795-- * expdesc in Lua 5.1.x has a union u and another struct s; this Lua
4796-- implementation ignores all instances of u and s usage
4797-- struct expdesc:
4798-- k -- (enum: expkind)
4799-- info, aux -- (int, int)
4800-- nval -- (lua_Number)
4801-- t -- patch list of 'exit when true'
4802-- f -- patch list of 'exit when false'
4803----------------------------------------------------------------------]]
4804
4805--[[--------------------------------------------------------------------
4806-- struct upvaldesc:
4807-- k -- (lu_byte)
4808-- info -- (lu_byte)
4809----------------------------------------------------------------------]]
4810
4811--[[--------------------------------------------------------------------
4812-- state needed to generate code for a given function
4813-- struct FuncState:
4814-- f -- current function header (table: Proto)
4815-- h -- table to find (and reuse) elements in 'k' (table: Table)
4816-- prev -- enclosing function (table: FuncState)
4817-- ls -- lexical state (table: LexState)
4818-- L -- copy of the Lua state (table: lua_State)
4819-- bl -- chain of current blocks (table: BlockCnt)
4820-- pc -- next position to code (equivalent to 'ncode')
4821-- lasttarget -- 'pc' of last 'jump target'
4822-- jpc -- list of pending jumps to 'pc'
4823-- freereg -- first free register
4824-- nk -- number of elements in 'k'
4825-- np -- number of elements in 'p'
4826-- nlocvars -- number of elements in 'locvars'
4827-- nactvar -- number of active local variables
4828-- upvalues[LUAI_MAXUPVALUES] -- upvalues (table: upvaldesc)
4829-- actvar[LUAI_MAXVARS] -- declared-variable stack
4830----------------------------------------------------------------------]]
4831
4832------------------------------------------------------------------------
4833-- constants used by parser
4834-- * picks up duplicate values from luaX if required
4835------------------------------------------------------------------------
4836
4837luaY.LUA_QS = luaX.LUA_QS or "'%s'" -- (from luaconf.h)
4838
4839luaY.SHRT_MAX = 32767 -- (from <limits.h>)
4840luaY.LUAI_MAXVARS = 200 -- (luaconf.h)
4841luaY.LUAI_MAXUPVALUES = 60 -- (luaconf.h)
4842luaY.MAX_INT = luaX.MAX_INT or 2147483645 -- (from llimits.h)
4843 -- * INT_MAX-2 for 32-bit systems
4844luaY.LUAI_MAXCCALLS = 200 -- (from luaconf.h)
4845
4846luaY.VARARG_HASARG = 1 -- (from lobject.h)
4847-- NOTE: HASARG_MASK is value-specific
4848luaY.HASARG_MASK = 2 -- this was added for a bitop in parlist()
4849luaY.VARARG_ISVARARG = 2
4850-- NOTE: there is some value-specific code that involves VARARG_NEEDSARG
4851luaY.VARARG_NEEDSARG = 4
4852
4853luaY.LUA_MULTRET = -1 -- (lua.h)
4854
4855--[[--------------------------------------------------------------------
4856-- other functions
4857----------------------------------------------------------------------]]
4858
4859------------------------------------------------------------------------
4860-- LUA_QL describes how error messages quote program elements.
4861-- CHANGE it if you want a different appearance. (from luaconf.h)
4862------------------------------------------------------------------------
4863function luaY:LUA_QL(x)
4864 return "'"..x.."'"
4865end
4866
4867------------------------------------------------------------------------
4868-- this is a stripped-down luaM_growvector (from lmem.h) which is a
4869-- macro based on luaM_growaux (in lmem.c); all the following does is
4870-- reproduce the size limit checking logic of the original function
4871-- so that error behaviour is identical; all arguments preserved for
4872-- convenience, even those which are unused
4873-- * set the t field to nil, since this originally does a sizeof(t)
4874-- * size (originally a pointer) is never updated, their final values
4875-- are set by luaY:close_func(), so overall things should still work
4876------------------------------------------------------------------------
4877function luaY:growvector(L, v, nelems, size, t, limit, e)
4878 if nelems >= limit then
4879 error(e) -- was luaG_runerror
4880 end
4881end
4882
4883------------------------------------------------------------------------
4884-- initialize a new function prototype structure (from lfunc.c)
4885-- * used only in open_func()
4886------------------------------------------------------------------------
4887function luaY:newproto(L)
4888 local f = {} -- Proto
4889 -- luaC_link(L, obj2gco(f), LUA_TPROTO); /* GC */
4890 f.k = {}
4891 f.sizek = 0
4892 f.p = {}
4893 f.sizep = 0
4894 f.code = {}
4895 f.sizecode = 0
4896 f.sizelineinfo = 0
4897 f.sizeupvalues = 0
4898 f.nups = 0
4899 f.upvalues = {}
4900 f.numparams = 0
4901 f.is_vararg = 0
4902 f.maxstacksize = 0
4903 f.lineinfo = {}
4904 f.sizelocvars = 0
4905 f.locvars = {}
4906 f.lineDefined = 0
4907 f.lastlinedefined = 0
4908 f.source = nil
4909 return f
4910end
4911
4912------------------------------------------------------------------------
4913-- converts an integer to a "floating point byte", represented as
4914-- (eeeeexxx), where the real value is (1xxx) * 2^(eeeee - 1) if
4915-- eeeee != 0 and (xxx) otherwise.
4916------------------------------------------------------------------------
4917function luaY:int2fb(x)
4918 local e = 0 -- exponent
4919 while x >= 16 do
4920 x = math.floor((x + 1) / 2)
4921 e = e + 1
4922 end
4923 if x < 8 then
4924 return x
4925 else
4926 return ((e + 1) * 8) + (x - 8)
4927 end
4928end
4929
4930--[[--------------------------------------------------------------------
4931-- parser functions
4932----------------------------------------------------------------------]]
4933
4934------------------------------------------------------------------------
4935-- true of the kind of expression produces multiple return values
4936------------------------------------------------------------------------
4937function luaY:hasmultret(k)
4938 return k == "VCALL" or k == "VVARARG"
4939end
4940
4941------------------------------------------------------------------------
4942-- convenience function to access active local i, returns entry
4943------------------------------------------------------------------------
4944function luaY:getlocvar(fs, i)
4945 return fs.f.locvars[ fs.actvar[i] ]
4946end
4947
4948------------------------------------------------------------------------
4949-- check a limit, string m provided as an error message
4950------------------------------------------------------------------------
4951function luaY:checklimit(fs, v, l, m)
4952 if v > l then self:errorlimit(fs, l, m) end
4953end
4954
4955--[[--------------------------------------------------------------------
4956-- nodes for block list (list of active blocks)
4957-- struct BlockCnt:
4958-- previous -- chain (table: BlockCnt)
4959-- breaklist -- list of jumps out of this loop
4960-- nactvar -- # active local variables outside the breakable structure
4961-- upval -- true if some variable in the block is an upvalue (boolean)
4962-- isbreakable -- true if 'block' is a loop (boolean)
4963----------------------------------------------------------------------]]
4964
4965------------------------------------------------------------------------
4966-- prototypes for recursive non-terminal functions
4967------------------------------------------------------------------------
4968-- prototypes deleted; not required in Lua
4969
4970------------------------------------------------------------------------
4971-- reanchor if last token is has a constant string, see close_func()
4972-- * used only in close_func()
4973------------------------------------------------------------------------
4974function luaY:anchor_token(ls)
4975 if ls.t.token == "TK_NAME" or ls.t.token == "TK_STRING" then
4976 -- not relevant to Lua implementation of parser
4977 -- local ts = ls.t.seminfo
4978 -- luaX_newstring(ls, getstr(ts), ts->tsv.len); /* C */
4979 end
4980end
4981
4982------------------------------------------------------------------------
4983-- throws a syntax error if token expected is not there
4984------------------------------------------------------------------------
4985function luaY:error_expected(ls, token)
4986 luaX:syntaxerror(ls,
4987 string.format(self.LUA_QS.." expected", luaX:token2str(ls, token)))
4988end
4989
4990------------------------------------------------------------------------
4991-- prepares error message for display, for limits exceeded
4992-- * used only in checklimit()
4993------------------------------------------------------------------------
4994function luaY:errorlimit(fs, limit, what)
4995 local msg = (fs.f.linedefined == 0) and
4996 string.format("main function has more than %d %s", limit, what) or
4997 string.format("function at line %d has more than %d %s",
4998 fs.f.linedefined, limit, what)
4999 luaX:lexerror(fs.ls, msg, 0)
5000end
5001
5002------------------------------------------------------------------------
5003-- tests for a token, returns outcome
5004-- * return value changed to boolean
5005------------------------------------------------------------------------
5006function luaY:testnext(ls, c)
5007 if ls.t.token == c then
5008 luaX:next(ls)
5009 return true
5010 else
5011 return false
5012 end
5013end
5014
5015------------------------------------------------------------------------
5016-- check for existence of a token, throws error if not found
5017------------------------------------------------------------------------
5018function luaY:check(ls, c)
5019 if ls.t.token ~= c then
5020 self:error_expected(ls, c)
5021 end
5022end
5023
5024------------------------------------------------------------------------
5025-- verify existence of a token, then skip it
5026------------------------------------------------------------------------
5027function luaY:checknext(ls, c)
5028 self:check(ls, c)
5029 luaX:next(ls)
5030end
5031
5032------------------------------------------------------------------------
5033-- throws error if condition not matched
5034------------------------------------------------------------------------
5035function luaY:check_condition(ls, c, msg)
5036 if not c then luaX:syntaxerror(ls, msg) end
5037end
5038
5039------------------------------------------------------------------------
5040-- verifies token conditions are met or else throw error
5041------------------------------------------------------------------------
5042function luaY:check_match(ls, what, who, where)
5043 if not self:testnext(ls, what) then
5044 if where == ls.linenumber then
5045 self:error_expected(ls, what)
5046 else
5047 luaX:syntaxerror(ls, string.format(
5048 self.LUA_QS.." expected (to close "..self.LUA_QS.." at line %d)",
5049 luaX:token2str(ls, what), luaX:token2str(ls, who), where))
5050 end
5051 end
5052end
5053
5054------------------------------------------------------------------------
5055-- expect that token is a name, return the name
5056------------------------------------------------------------------------
5057function luaY:str_checkname(ls)
5058 self:check(ls, "TK_NAME")
5059 local ts = ls.t.seminfo
5060 luaX:next(ls)
5061 return ts
5062end
5063
5064------------------------------------------------------------------------
5065-- initialize a struct expdesc, expression description data structure
5066------------------------------------------------------------------------
5067function luaY:init_exp(e, k, i)
5068 e.f, e.t = luaK.NO_JUMP, luaK.NO_JUMP
5069 e.k = k
5070 e.info = i
5071end
5072
5073------------------------------------------------------------------------
5074-- adds given string s in string pool, sets e as VK
5075------------------------------------------------------------------------
5076function luaY:codestring(ls, e, s)
5077 self:init_exp(e, "VK", luaK:stringK(ls.fs, s))
5078end
5079
5080------------------------------------------------------------------------
5081-- consume a name token, adds it to string pool, sets e as VK
5082------------------------------------------------------------------------
5083function luaY:checkname(ls, e)
5084 self:codestring(ls, e, self:str_checkname(ls))
5085end
5086
5087------------------------------------------------------------------------
5088-- creates struct entry for a local variable
5089-- * used only in new_localvar()
5090------------------------------------------------------------------------
5091function luaY:registerlocalvar(ls, varname)
5092 local fs = ls.fs
5093 local f = fs.f
5094 self:growvector(ls.L, f.locvars, fs.nlocvars, f.sizelocvars,
5095 nil, self.SHRT_MAX, "too many local variables")
5096 -- loop to initialize empty f.locvar positions not required
5097 f.locvars[fs.nlocvars] = {} -- LocVar
5098 f.locvars[fs.nlocvars].varname = varname
5099 -- luaC_objbarrier(ls.L, f, varname) /* GC */
5100 local nlocvars = fs.nlocvars
5101 fs.nlocvars = fs.nlocvars + 1
5102 return nlocvars
5103end
5104
5105------------------------------------------------------------------------
5106-- creates a new local variable given a name and an offset from nactvar
5107-- * used in fornum(), forlist(), parlist(), body()
5108------------------------------------------------------------------------
5109function luaY:new_localvarliteral(ls, v, n)
5110 self:new_localvar(ls, v, n)
5111end
5112
5113------------------------------------------------------------------------
5114-- register a local variable, set in active variable list
5115------------------------------------------------------------------------
5116function luaY:new_localvar(ls, name, n)
5117 local fs = ls.fs
5118 self:checklimit(fs, fs.nactvar + n + 1, self.LUAI_MAXVARS, "local variables")
5119 fs.actvar[fs.nactvar + n] = self:registerlocalvar(ls, name)
5120end
5121
5122------------------------------------------------------------------------
5123-- adds nvars number of new local variables, set debug information
5124------------------------------------------------------------------------
5125function luaY:adjustlocalvars(ls, nvars)
5126 local fs = ls.fs
5127 fs.nactvar = fs.nactvar + nvars
5128 for i = nvars, 1, -1 do
5129 self:getlocvar(fs, fs.nactvar - i).startpc = fs.pc
5130 end
5131end
5132
5133------------------------------------------------------------------------
5134-- removes a number of locals, set debug information
5135------------------------------------------------------------------------
5136function luaY:removevars(ls, tolevel)
5137 local fs = ls.fs
5138 while fs.nactvar > tolevel do
5139 fs.nactvar = fs.nactvar - 1
5140 self:getlocvar(fs, fs.nactvar).endpc = fs.pc
5141 end
5142end
5143
5144------------------------------------------------------------------------
5145-- returns an existing upvalue index based on the given name, or
5146-- creates a new upvalue struct entry and returns the new index
5147-- * used only in singlevaraux()
5148------------------------------------------------------------------------
5149function luaY:indexupvalue(fs, name, v)
5150 local f = fs.f
5151 for i = 0, f.nups - 1 do
5152 if fs.upvalues[i].k == v.k and fs.upvalues[i].info == v.info then
5153 assert(f.upvalues[i] == name)
5154 return i
5155 end
5156 end
5157 -- new one
5158 self:checklimit(fs, f.nups + 1, self.LUAI_MAXUPVALUES, "upvalues")
5159 self:growvector(fs.L, f.upvalues, f.nups, f.sizeupvalues,
5160 nil, self.MAX_INT, "")
5161 -- loop to initialize empty f.upvalues positions not required
5162 f.upvalues[f.nups] = name
5163 -- luaC_objbarrier(fs->L, f, name); /* GC */
5164 assert(v.k == "VLOCAL" or v.k == "VUPVAL")
5165 -- this is a partial copy; only k & info fields used
5166 fs.upvalues[f.nups] = { k = v.k, info = v.info }
5167 local nups = f.nups
5168 f.nups = f.nups + 1
5169 return nups
5170end
5171
5172------------------------------------------------------------------------
5173-- search the local variable namespace of the given fs for a match
5174-- * used only in singlevaraux()
5175------------------------------------------------------------------------
5176function luaY:searchvar(fs, n)
5177 for i = fs.nactvar - 1, 0, -1 do
5178 if n == self:getlocvar(fs, i).varname then
5179 return i
5180 end
5181 end
5182 return -1 -- not found
5183end
5184
5185------------------------------------------------------------------------
5186-- * mark upvalue flags in function states up to a given level
5187-- * used only in singlevaraux()
5188------------------------------------------------------------------------
5189function luaY:markupval(fs, level)
5190 local bl = fs.bl
5191 while bl and bl.nactvar > level do bl = bl.previous end
5192 if bl then bl.upval = true end
5193end
5194
5195------------------------------------------------------------------------
5196-- handle locals, globals and upvalues and related processing
5197-- * search mechanism is recursive, calls itself to search parents
5198-- * used only in singlevar()
5199------------------------------------------------------------------------
5200function luaY:singlevaraux(fs, n, var, base)
5201 if fs == nil then -- no more levels?
5202 self:init_exp(var, "VGLOBAL", luaP.NO_REG) -- default is global variable
5203 return "VGLOBAL"
5204 else
5205 local v = self:searchvar(fs, n) -- look up at current level
5206 if v >= 0 then
5207 self:init_exp(var, "VLOCAL", v)
5208 if base == 0 then
5209 self:markupval(fs, v) -- local will be used as an upval
5210 end
5211 return "VLOCAL"
5212 else -- not found at current level; try upper one
5213 if self:singlevaraux(fs.prev, n, var, 0) == "VGLOBAL" then
5214 return "VGLOBAL"
5215 end
5216 var.info = self:indexupvalue(fs, n, var) -- else was LOCAL or UPVAL
5217 var.k = "VUPVAL" -- upvalue in this level
5218 return "VUPVAL"
5219 end--if v
5220 end--if fs
5221end
5222
5223------------------------------------------------------------------------
5224-- consume a name token, creates a variable (global|local|upvalue)
5225-- * used in prefixexp(), funcname()
5226------------------------------------------------------------------------
5227function luaY:singlevar(ls, var)
5228 local varname = self:str_checkname(ls)
5229 local fs = ls.fs
5230 if self:singlevaraux(fs, varname, var, 1) == "VGLOBAL" then
5231 var.info = luaK:stringK(fs, varname) -- info points to global name
5232 end
5233end
5234
5235------------------------------------------------------------------------
5236-- adjust RHS to match LHS in an assignment
5237-- * used in assignment(), forlist(), localstat()
5238------------------------------------------------------------------------
5239function luaY:adjust_assign(ls, nvars, nexps, e)
5240 local fs = ls.fs
5241 local extra = nvars - nexps
5242 if self:hasmultret(e.k) then
5243 extra = extra + 1 -- includes call itself
5244 if extra <= 0 then extra = 0 end
5245 luaK:setreturns(fs, e, extra) -- last exp. provides the difference
5246 if extra > 1 then luaK:reserveregs(fs, extra - 1) end
5247 else
5248 if e.k ~= "VVOID" then luaK:exp2nextreg(fs, e) end -- close last expression
5249 if extra > 0 then
5250 local reg = fs.freereg
5251 luaK:reserveregs(fs, extra)
5252 luaK:_nil(fs, reg, extra)
5253 end
5254 end
5255end
5256
5257------------------------------------------------------------------------
5258-- tracks and limits parsing depth, assert check at end of parsing
5259------------------------------------------------------------------------
5260function luaY:enterlevel(ls)
5261 ls.L.nCcalls = ls.L.nCcalls + 1
5262 if ls.L.nCcalls > self.LUAI_MAXCCALLS then
5263 luaX:lexerror(ls, "chunk has too many syntax levels", 0)
5264 end
5265end
5266
5267------------------------------------------------------------------------
5268-- tracks parsing depth, a pair with luaY:enterlevel()
5269------------------------------------------------------------------------
5270function luaY:leavelevel(ls)
5271 ls.L.nCcalls = ls.L.nCcalls - 1
5272end
5273
5274------------------------------------------------------------------------
5275-- enters a code unit, initializes elements
5276------------------------------------------------------------------------
5277function luaY:enterblock(fs, bl, isbreakable)
5278 bl.breaklist = luaK.NO_JUMP
5279 bl.isbreakable = isbreakable
5280 bl.nactvar = fs.nactvar
5281 bl.upval = false
5282 bl.previous = fs.bl
5283 fs.bl = bl
5284 assert(fs.freereg == fs.nactvar)
5285end
5286
5287------------------------------------------------------------------------
5288-- leaves a code unit, close any upvalues
5289------------------------------------------------------------------------
5290function luaY:leaveblock(fs)
5291 local bl = fs.bl
5292 fs.bl = bl.previous
5293 self:removevars(fs.ls, bl.nactvar)
5294 if bl.upval then
5295 luaK:codeABC(fs, "OP_CLOSE", bl.nactvar, 0, 0)
5296 end
5297 -- a block either controls scope or breaks (never both)
5298 assert(not bl.isbreakable or not bl.upval)
5299 assert(bl.nactvar == fs.nactvar)
5300 fs.freereg = fs.nactvar -- free registers
5301 luaK:patchtohere(fs, bl.breaklist)
5302end
5303
5304------------------------------------------------------------------------
5305-- implement the instantiation of a function prototype, append list of
5306-- upvalues after the instantiation instruction
5307-- * used only in body()
5308------------------------------------------------------------------------
5309function luaY:pushclosure(ls, func, v)
5310 local fs = ls.fs
5311 local f = fs.f
5312 self:growvector(ls.L, f.p, fs.np, f.sizep, nil,
5313 luaP.MAXARG_Bx, "constant table overflow")
5314 -- loop to initialize empty f.p positions not required
5315 f.p[fs.np] = func.f
5316 fs.np = fs.np + 1
5317 -- luaC_objbarrier(ls->L, f, func->f); /* C */
5318 self:init_exp(v, "VRELOCABLE", luaK:codeABx(fs, "OP_CLOSURE", 0, fs.np - 1))
5319 for i = 0, func.f.nups - 1 do
5320 local o = (func.upvalues[i].k == "VLOCAL") and "OP_MOVE" or "OP_GETUPVAL"
5321 luaK:codeABC(fs, o, 0, func.upvalues[i].info, 0)
5322 end
5323end
5324
5325------------------------------------------------------------------------
5326-- opening of a function
5327------------------------------------------------------------------------
5328function luaY:open_func(ls, fs)
5329 local L = ls.L
5330 local f = self:newproto(ls.L)
5331 fs.f = f
5332 fs.prev = ls.fs -- linked list of funcstates
5333 fs.ls = ls
5334 fs.L = L
5335 ls.fs = fs
5336 fs.pc = 0
5337 fs.lasttarget = -1
5338 fs.jpc = luaK.NO_JUMP
5339 fs.freereg = 0
5340 fs.nk = 0
5341 fs.np = 0
5342 fs.nlocvars = 0
5343 fs.nactvar = 0
5344 fs.bl = nil
5345 f.source = ls.source
5346 f.maxstacksize = 2 -- registers 0/1 are always valid
5347 fs.h = {} -- constant table; was luaH_new call
5348 -- anchor table of constants and prototype (to avoid being collected)
5349 -- sethvalue2s(L, L->top, fs->h); incr_top(L); /* C */
5350 -- setptvalue2s(L, L->top, f); incr_top(L);
5351end
5352
5353------------------------------------------------------------------------
5354-- closing of a function
5355------------------------------------------------------------------------
5356function luaY:close_func(ls)
5357 local L = ls.L
5358 local fs = ls.fs
5359 local f = fs.f
5360 self:removevars(ls, 0)
5361 luaK:ret(fs, 0, 0) -- final return
5362 -- luaM_reallocvector deleted for f->code, f->lineinfo, f->k, f->p,
5363 -- f->locvars, f->upvalues; not required for Lua table arrays
5364 f.sizecode = fs.pc
5365 f.sizelineinfo = fs.pc
5366 f.sizek = fs.nk
5367 f.sizep = fs.np
5368 f.sizelocvars = fs.nlocvars
5369 f.sizeupvalues = f.nups
5370 --assert(luaG_checkcode(f)) -- currently not implemented
5371 assert(fs.bl == nil)
5372 ls.fs = fs.prev
5373 -- the following is not required for this implementation; kept here
5374 -- for completeness
5375 -- L->top -= 2; /* remove table and prototype from the stack */
5376 -- last token read was anchored in defunct function; must reanchor it
5377 if fs then self:anchor_token(ls) end
5378end
5379
5380------------------------------------------------------------------------
5381-- parser initialization function
5382-- * note additional sub-tables needed for LexState, FuncState
5383------------------------------------------------------------------------
5384function luaY:parser(L, z, buff, name)
5385 local lexstate = {} -- LexState
5386 lexstate.t = {}
5387 lexstate.lookahead = {}
5388 local funcstate = {} -- FuncState
5389 funcstate.upvalues = {}
5390 funcstate.actvar = {}
5391 -- the following nCcalls initialization added for convenience
5392 L.nCcalls = 0
5393 lexstate.buff = buff
5394 luaX:setinput(L, lexstate, z, name)
5395 self:open_func(lexstate, funcstate)
5396 funcstate.f.is_vararg = self.VARARG_ISVARARG -- main func. is always vararg
5397 luaX:next(lexstate) -- read first token
5398 self:chunk(lexstate)
5399 self:check(lexstate, "TK_EOS")
5400 self:close_func(lexstate)
5401 assert(funcstate.prev == nil)
5402 assert(funcstate.f.nups == 0)
5403 assert(lexstate.fs == nil)
5404 return funcstate.f
5405end
5406
5407--[[--------------------------------------------------------------------
5408-- GRAMMAR RULES
5409----------------------------------------------------------------------]]
5410
5411------------------------------------------------------------------------
5412-- parse a function name suffix, for function call specifications
5413-- * used in primaryexp(), funcname()
5414------------------------------------------------------------------------
5415function luaY:field(ls, v)
5416 -- field -> ['.' | ':'] NAME
5417 local fs = ls.fs
5418 local key = {} -- expdesc
5419 luaK:exp2anyreg(fs, v)
5420 luaX:next(ls) -- skip the dot or colon
5421 self:checkname(ls, key)
5422 luaK:indexed(fs, v, key)
5423end
5424
5425------------------------------------------------------------------------
5426-- parse a table indexing suffix, for constructors, expressions
5427-- * used in recfield(), primaryexp()
5428------------------------------------------------------------------------
5429function luaY:yindex(ls, v)
5430 -- index -> '[' expr ']'
5431 luaX:next(ls) -- skip the '['
5432 self:expr(ls, v)
5433 luaK:exp2val(ls.fs, v)
5434 self:checknext(ls, "]")
5435end
5436
5437--[[--------------------------------------------------------------------
5438-- Rules for Constructors
5439----------------------------------------------------------------------]]
5440
5441--[[--------------------------------------------------------------------
5442-- struct ConsControl:
5443-- v -- last list item read (table: struct expdesc)
5444-- t -- table descriptor (table: struct expdesc)
5445-- nh -- total number of 'record' elements
5446-- na -- total number of array elements
5447-- tostore -- number of array elements pending to be stored
5448----------------------------------------------------------------------]]
5449
5450------------------------------------------------------------------------
5451-- parse a table record (hash) field
5452-- * used in constructor()
5453------------------------------------------------------------------------
5454function luaY:recfield(ls, cc)
5455 -- recfield -> (NAME | '['exp1']') = exp1
5456 local fs = ls.fs
5457 local reg = ls.fs.freereg
5458 local key, val = {}, {} -- expdesc
5459 if ls.t.token == "TK_NAME" then
5460 self:checklimit(fs, cc.nh, self.MAX_INT, "items in a constructor")
5461 self:checkname(ls, key)
5462 else -- ls->t.token == '['
5463 self:yindex(ls, key)
5464 end
5465 cc.nh = cc.nh + 1
5466 self:checknext(ls, "=")
5467 local rkkey = luaK:exp2RK(fs, key)
5468 self:expr(ls, val)
5469 luaK:codeABC(fs, "OP_SETTABLE", cc.t.info, rkkey, luaK:exp2RK(fs, val))
5470 fs.freereg = reg -- free registers
5471end
5472
5473------------------------------------------------------------------------
5474-- emit a set list instruction if enough elements (LFIELDS_PER_FLUSH)
5475-- * used in constructor()
5476------------------------------------------------------------------------
5477function luaY:closelistfield(fs, cc)
5478 if cc.v.k == "VVOID" then return end -- there is no list item
5479 luaK:exp2nextreg(fs, cc.v)
5480 cc.v.k = "VVOID"
5481 if cc.tostore == luaP.LFIELDS_PER_FLUSH then
5482 luaK:setlist(fs, cc.t.info, cc.na, cc.tostore) -- flush
5483 cc.tostore = 0 -- no more items pending
5484 end
5485end
5486
5487------------------------------------------------------------------------
5488-- emit a set list instruction at the end of parsing list constructor
5489-- * used in constructor()
5490------------------------------------------------------------------------
5491function luaY:lastlistfield(fs, cc)
5492 if cc.tostore == 0 then return end
5493 if self:hasmultret(cc.v.k) then
5494 luaK:setmultret(fs, cc.v)
5495 luaK:setlist(fs, cc.t.info, cc.na, self.LUA_MULTRET)
5496 cc.na = cc.na - 1 -- do not count last expression (unknown number of elements)
5497 else
5498 if cc.v.k ~= "VVOID" then
5499 luaK:exp2nextreg(fs, cc.v)
5500 end
5501 luaK:setlist(fs, cc.t.info, cc.na, cc.tostore)
5502 end
5503end
5504
5505------------------------------------------------------------------------
5506-- parse a table list (array) field
5507-- * used in constructor()
5508------------------------------------------------------------------------
5509function luaY:listfield(ls, cc)
5510 self:expr(ls, cc.v)
5511 self:checklimit(ls.fs, cc.na, self.MAX_INT, "items in a constructor")
5512 cc.na = cc.na + 1
5513 cc.tostore = cc.tostore + 1
5514end
5515
5516------------------------------------------------------------------------
5517-- parse a table constructor
5518-- * used in funcargs(), simpleexp()
5519------------------------------------------------------------------------
5520function luaY:constructor(ls, t)
5521 -- constructor -> '{' [ field { fieldsep field } [ fieldsep ] ] '}'
5522 -- field -> recfield | listfield
5523 -- fieldsep -> ',' | ';'
5524 local fs = ls.fs
5525 local line = ls.linenumber
5526 local pc = luaK:codeABC(fs, "OP_NEWTABLE", 0, 0, 0)
5527 local cc = {} -- ConsControl
5528 cc.v = {}
5529 cc.na, cc.nh, cc.tostore = 0, 0, 0
5530 cc.t = t
5531 self:init_exp(t, "VRELOCABLE", pc)
5532 self:init_exp(cc.v, "VVOID", 0) -- no value (yet)
5533 luaK:exp2nextreg(ls.fs, t) -- fix it at stack top (for gc)
5534 self:checknext(ls, "{")
5535 repeat
5536 assert(cc.v.k == "VVOID" or cc.tostore > 0)
5537 if ls.t.token == "}" then break end
5538 self:closelistfield(fs, cc)
5539 local c = ls.t.token
5540
5541 if c == "TK_NAME" then -- may be listfields or recfields
5542 luaX:lookahead(ls)
5543 if ls.lookahead.token ~= "=" then -- expression?
5544 self:listfield(ls, cc)
5545 else
5546 self:recfield(ls, cc)
5547 end
5548 elseif c == "[" then -- constructor_item -> recfield
5549 self:recfield(ls, cc)
5550 else -- constructor_part -> listfield
5551 self:listfield(ls, cc)
5552 end
5553 until not self:testnext(ls, ",") and not self:testnext(ls, ";")
5554 self:check_match(ls, "}", "{", line)
5555 self:lastlistfield(fs, cc)
5556 luaP:SETARG_B(fs.f.code[pc], self:int2fb(cc.na)) -- set initial array size
5557 luaP:SETARG_C(fs.f.code[pc], self:int2fb(cc.nh)) -- set initial table size
5558end
5559
5560-- }======================================================================
5561
5562------------------------------------------------------------------------
5563-- parse the arguments (parameters) of a function declaration
5564-- * used in body()
5565------------------------------------------------------------------------
5566function luaY:parlist(ls)
5567 -- parlist -> [ param { ',' param } ]
5568 local fs = ls.fs
5569 local f = fs.f
5570 local nparams = 0
5571 f.is_vararg = 0
5572 if ls.t.token ~= ")" then -- is 'parlist' not empty?
5573 repeat
5574 local c = ls.t.token
5575 if c == "TK_NAME" then -- param -> NAME
5576 self:new_localvar(ls, self:str_checkname(ls), nparams)
5577 nparams = nparams + 1
5578 elseif c == "TK_DOTS" then -- param -> `...'
5579 luaX:next(ls)
5580-- [[
5581-- #if defined(LUA_COMPAT_VARARG)
5582 -- use `arg' as default name
5583 self:new_localvarliteral(ls, "arg", nparams)
5584 nparams = nparams + 1
5585 f.is_vararg = self.VARARG_HASARG + self.VARARG_NEEDSARG
5586-- #endif
5587--]]
5588 f.is_vararg = f.is_vararg + self.VARARG_ISVARARG
5589 else
5590 luaX:syntaxerror(ls, "<name> or "..self:LUA_QL("...").." expected")
5591 end
5592 until f.is_vararg ~= 0 or not self:testnext(ls, ",")
5593 end--if
5594 self:adjustlocalvars(ls, nparams)
5595 -- NOTE: the following works only when HASARG_MASK is 2!
5596 f.numparams = fs.nactvar - (f.is_vararg % self.HASARG_MASK)
5597 luaK:reserveregs(fs, fs.nactvar) -- reserve register for parameters
5598end
5599
5600------------------------------------------------------------------------
5601-- parse function declaration body
5602-- * used in simpleexp(), localfunc(), funcstat()
5603------------------------------------------------------------------------
5604function luaY:body(ls, e, needself, line)
5605 -- body -> '(' parlist ')' chunk END
5606 local new_fs = {} -- FuncState
5607 new_fs.upvalues = {}
5608 new_fs.actvar = {}
5609 self:open_func(ls, new_fs)
5610 new_fs.f.lineDefined = line
5611 self:checknext(ls, "(")
5612 if needself then
5613 self:new_localvarliteral(ls, "self", 0)
5614 self:adjustlocalvars(ls, 1)
5615 end
5616 self:parlist(ls)
5617 self:checknext(ls, ")")
5618 self:chunk(ls)
5619 new_fs.f.lastlinedefined = ls.linenumber
5620 self:check_match(ls, "TK_END", "TK_FUNCTION", line)
5621 self:close_func(ls)
5622 self:pushclosure(ls, new_fs, e)
5623end
5624
5625------------------------------------------------------------------------
5626-- parse a list of comma-separated expressions
5627-- * used is multiple locations
5628------------------------------------------------------------------------
5629function luaY:explist1(ls, v)
5630 -- explist1 -> expr { ',' expr }
5631 local n = 1 -- at least one expression
5632 self:expr(ls, v)
5633 while self:testnext(ls, ",") do
5634 luaK:exp2nextreg(ls.fs, v)
5635 self:expr(ls, v)
5636 n = n + 1
5637 end
5638 return n
5639end
5640
5641------------------------------------------------------------------------
5642-- parse the parameters of a function call
5643-- * contrast with parlist(), used in function declarations
5644-- * used in primaryexp()
5645------------------------------------------------------------------------
5646function luaY:funcargs(ls, f)
5647 local fs = ls.fs
5648 local args = {} -- expdesc
5649 local nparams
5650 local line = ls.linenumber
5651 local c = ls.t.token
5652 if c == "(" then -- funcargs -> '(' [ explist1 ] ')'
5653 if line ~= ls.lastline then
5654 luaX:syntaxerror(ls, "ambiguous syntax (function call x new statement)")
5655 end
5656 luaX:next(ls)
5657 if ls.t.token == ")" then -- arg list is empty?
5658 args.k = "VVOID"
5659 else
5660 self:explist1(ls, args)
5661 luaK:setmultret(fs, args)
5662 end
5663 self:check_match(ls, ")", "(", line)
5664 elseif c == "{" then -- funcargs -> constructor
5665 self:constructor(ls, args)
5666 elseif c == "TK_STRING" then -- funcargs -> STRING
5667 self:codestring(ls, args, ls.t.seminfo)
5668 luaX:next(ls) -- must use 'seminfo' before 'next'
5669 else
5670 luaX:syntaxerror(ls, "function arguments expected")
5671 return
5672 end
5673 assert(f.k == "VNONRELOC")
5674 local base = f.info -- base register for call
5675 if self:hasmultret(args.k) then
5676 nparams = self.LUA_MULTRET -- open call
5677 else
5678 if args.k ~= "VVOID" then
5679 luaK:exp2nextreg(fs, args) -- close last argument
5680 end
5681 nparams = fs.freereg - (base + 1)
5682 end
5683 self:init_exp(f, "VCALL", luaK:codeABC(fs, "OP_CALL", base, nparams + 1, 2))
5684 luaK:fixline(fs, line)
5685 fs.freereg = base + 1 -- call remove function and arguments and leaves
5686 -- (unless changed) one result
5687end
5688
5689--[[--------------------------------------------------------------------
5690-- Expression parsing
5691----------------------------------------------------------------------]]
5692
5693------------------------------------------------------------------------
5694-- parses an expression in parentheses or a single variable
5695-- * used in primaryexp()
5696------------------------------------------------------------------------
5697function luaY:prefixexp(ls, v)
5698 -- prefixexp -> NAME | '(' expr ')'
5699 local c = ls.t.token
5700 if c == "(" then
5701 local line = ls.linenumber
5702 luaX:next(ls)
5703 self:expr(ls, v)
5704 self:check_match(ls, ")", "(", line)
5705 luaK:dischargevars(ls.fs, v)
5706 elseif c == "TK_NAME" then
5707 self:singlevar(ls, v)
5708 else
5709 luaX:syntaxerror(ls, "unexpected symbol")
5710 end--if c
5711 return
5712end
5713
5714------------------------------------------------------------------------
5715-- parses a prefixexp (an expression in parentheses or a single variable)
5716-- or a function call specification
5717-- * used in simpleexp(), assignment(), exprstat()
5718------------------------------------------------------------------------
5719function luaY:primaryexp(ls, v)
5720 -- primaryexp ->
5721 -- prefixexp { '.' NAME | '[' exp ']' | ':' NAME funcargs | funcargs }
5722 local fs = ls.fs
5723 self:prefixexp(ls, v)
5724 while true do
5725 local c = ls.t.token
5726 if c == "." then -- field
5727 self:field(ls, v)
5728 elseif c == "[" then -- '[' exp1 ']'
5729 local key = {} -- expdesc
5730 luaK:exp2anyreg(fs, v)
5731 self:yindex(ls, key)
5732 luaK:indexed(fs, v, key)
5733 elseif c == ":" then -- ':' NAME funcargs
5734 local key = {} -- expdesc
5735 luaX:next(ls)
5736 self:checkname(ls, key)
5737 luaK:_self(fs, v, key)
5738 self:funcargs(ls, v)
5739 elseif c == "(" or c == "TK_STRING" or c == "{" then -- funcargs
5740 luaK:exp2nextreg(fs, v)
5741 self:funcargs(ls, v)
5742 else
5743 return
5744 end--if c
5745 end--while
5746end
5747
5748------------------------------------------------------------------------
5749-- parses general expression types, constants handled here
5750-- * used in subexpr()
5751------------------------------------------------------------------------
5752function luaY:simpleexp(ls, v)
5753 -- simpleexp -> NUMBER | STRING | NIL | TRUE | FALSE | ... |
5754 -- constructor | FUNCTION body | primaryexp
5755 local c = ls.t.token
5756 if c == "TK_NUMBER" then
5757 self:init_exp(v, "VKNUM", 0)
5758 v.nval = ls.t.seminfo
5759 elseif c == "TK_STRING" then
5760 self:codestring(ls, v, ls.t.seminfo)
5761 elseif c == "TK_NIL" then
5762 self:init_exp(v, "VNIL", 0)
5763 elseif c == "TK_TRUE" then
5764 self:init_exp(v, "VTRUE", 0)
5765 elseif c == "TK_FALSE" then
5766 self:init_exp(v, "VFALSE", 0)
5767 elseif c == "TK_DOTS" then -- vararg
5768 local fs = ls.fs
5769 self:check_condition(ls, fs.f.is_vararg ~= 0,
5770 "cannot use "..self:LUA_QL("...").." outside a vararg function");
5771 -- NOTE: the following substitutes for a bitop, but is value-specific
5772 local is_vararg = fs.f.is_vararg
5773 if is_vararg >= self.VARARG_NEEDSARG then
5774 fs.f.is_vararg = is_vararg - self.VARARG_NEEDSARG -- don't need 'arg'
5775 end
5776 self:init_exp(v, "VVARARG", luaK:codeABC(fs, "OP_VARARG", 0, 1, 0))
5777 elseif c == "{" then -- constructor
5778 self:constructor(ls, v)
5779 return
5780 elseif c == "TK_FUNCTION" then
5781 luaX:next(ls)
5782 self:body(ls, v, false, ls.linenumber)
5783 return
5784 else
5785 self:primaryexp(ls, v)
5786 return
5787 end--if c
5788 luaX:next(ls)
5789end
5790
5791------------------------------------------------------------------------
5792-- Translates unary operators tokens if found, otherwise returns
5793-- OPR_NOUNOPR. getunopr() and getbinopr() are used in subexpr().
5794-- * used in subexpr()
5795------------------------------------------------------------------------
5796function luaY:getunopr(op)
5797 if op == "TK_NOT" then
5798 return "OPR_NOT"
5799 elseif op == "-" then
5800 return "OPR_MINUS"
5801 elseif op == "#" then
5802 return "OPR_LEN"
5803 else
5804 return "OPR_NOUNOPR"
5805 end
5806end
5807
5808------------------------------------------------------------------------
5809-- Translates binary operator tokens if found, otherwise returns
5810-- OPR_NOBINOPR. Code generation uses OPR_* style tokens.
5811-- * used in subexpr()
5812------------------------------------------------------------------------
5813luaY.getbinopr_table = {
5814 ["+"] = "OPR_ADD",
5815 ["-"] = "OPR_SUB",
5816 ["*"] = "OPR_MUL",
5817 ["/"] = "OPR_DIV",
5818 ["%"] = "OPR_MOD",
5819 ["^"] = "OPR_POW",
5820 ["TK_CONCAT"] = "OPR_CONCAT",
5821 ["TK_NE"] = "OPR_NE",
5822 ["TK_EQ"] = "OPR_EQ",
5823 ["<"] = "OPR_LT",
5824 ["TK_LE"] = "OPR_LE",
5825 [">"] = "OPR_GT",
5826 ["TK_GE"] = "OPR_GE",
5827 ["TK_AND"] = "OPR_AND",
5828 ["TK_OR"] = "OPR_OR",
5829}
5830function luaY:getbinopr(op)
5831 local opr = self.getbinopr_table[op]
5832 if opr then return opr else return "OPR_NOBINOPR" end
5833end
5834
5835------------------------------------------------------------------------
5836-- the following priority table consists of pairs of left/right values
5837-- for binary operators (was a static const struct); grep for ORDER OPR
5838-- * the following struct is replaced:
5839-- static const struct {
5840-- lu_byte left; /* left priority for each binary operator */
5841-- lu_byte right; /* right priority */
5842-- } priority[] = { /* ORDER OPR */
5843------------------------------------------------------------------------
5844luaY.priority = {
5845 {6, 6}, {6, 6}, {7, 7}, {7, 7}, {7, 7}, -- `+' `-' `/' `%'
5846 {10, 9}, {5, 4}, -- power and concat (right associative)
5847 {3, 3}, {3, 3}, -- equality
5848 {3, 3}, {3, 3}, {3, 3}, {3, 3}, -- order
5849 {2, 2}, {1, 1} -- logical (and/or)
5850}
5851
5852luaY.UNARY_PRIORITY = 8 -- priority for unary operators
5853
5854------------------------------------------------------------------------
5855-- Parse subexpressions. Includes handling of unary operators and binary
5856-- operators. A subexpr is given the rhs priority level of the operator
5857-- immediately left of it, if any (limit is -1 if none,) and if a binop
5858-- is found, limit is compared with the lhs priority level of the binop
5859-- in order to determine which executes first.
5860------------------------------------------------------------------------
5861
5862------------------------------------------------------------------------
5863-- subexpr -> (simpleexp | unop subexpr) { binop subexpr }
5864-- where 'binop' is any binary operator with a priority higher than 'limit'
5865-- * for priority lookups with self.priority[], 1=left and 2=right
5866-- * recursively called
5867-- * used in expr()
5868------------------------------------------------------------------------
5869function luaY:subexpr(ls, v, limit)
5870 self:enterlevel(ls)
5871 local uop = self:getunopr(ls.t.token)
5872 if uop ~= "OPR_NOUNOPR" then
5873 luaX:next(ls)
5874 self:subexpr(ls, v, self.UNARY_PRIORITY)
5875 luaK:prefix(ls.fs, uop, v)
5876 else
5877 self:simpleexp(ls, v)
5878 end
5879 -- expand while operators have priorities higher than 'limit'
5880 local op = self:getbinopr(ls.t.token)
5881 while op ~= "OPR_NOBINOPR" and self.priority[luaK.BinOpr[op] + 1][1] > limit do
5882 local v2 = {} -- expdesc
5883 luaX:next(ls)
5884 luaK:infix(ls.fs, op, v)
5885 -- read sub-expression with higher priority
5886 local nextop = self:subexpr(ls, v2, self.priority[luaK.BinOpr[op] + 1][2])
5887 luaK:posfix(ls.fs, op, v, v2)
5888 op = nextop
5889 end
5890 self:leavelevel(ls)
5891 return op -- return first untreated operator
5892end
5893
5894------------------------------------------------------------------------
5895-- Expression parsing starts here. Function subexpr is entered with the
5896-- left operator (which is non-existent) priority of -1, which is lower
5897-- than all actual operators. Expr information is returned in parm v.
5898-- * used in multiple locations
5899------------------------------------------------------------------------
5900function luaY:expr(ls, v)
5901 self:subexpr(ls, v, 0)
5902end
5903
5904-- }====================================================================
5905
5906--[[--------------------------------------------------------------------
5907-- Rules for Statements
5908----------------------------------------------------------------------]]
5909
5910------------------------------------------------------------------------
5911-- checks next token, used as a look-ahead
5912-- * returns boolean instead of 0|1
5913-- * used in retstat(), chunk()
5914------------------------------------------------------------------------
5915function luaY:block_follow(token)
5916 if token == "TK_ELSE" or token == "TK_ELSEIF" or token == "TK_END"
5917 or token == "TK_UNTIL" or token == "TK_EOS" then
5918 return true
5919 else
5920 return false
5921 end
5922end
5923
5924------------------------------------------------------------------------
5925-- parse a code block or unit
5926-- * used in multiple functions
5927------------------------------------------------------------------------
5928function luaY:block(ls)
5929 -- block -> chunk
5930 local fs = ls.fs
5931 local bl = {} -- BlockCnt
5932 self:enterblock(fs, bl, false)
5933 self:chunk(ls)
5934 assert(bl.breaklist == luaK.NO_JUMP)
5935 self:leaveblock(fs)
5936end
5937
5938------------------------------------------------------------------------
5939-- structure to chain all variables in the left-hand side of an
5940-- assignment
5941-- struct LHS_assign:
5942-- prev -- (table: struct LHS_assign)
5943-- v -- variable (global, local, upvalue, or indexed) (table: expdesc)
5944------------------------------------------------------------------------
5945
5946------------------------------------------------------------------------
5947-- check whether, in an assignment to a local variable, the local variable
5948-- is needed in a previous assignment (to a table). If so, save original
5949-- local value in a safe place and use this safe copy in the previous
5950-- assignment.
5951-- * used in assignment()
5952------------------------------------------------------------------------
5953function luaY:check_conflict(ls, lh, v)
5954 local fs = ls.fs
5955 local extra = fs.freereg -- eventual position to save local variable
5956 local conflict = false
5957 while lh do
5958 if lh.v.k == "VINDEXED" then
5959 if lh.v.info == v.info then -- conflict?
5960 conflict = true
5961 lh.v.info = extra -- previous assignment will use safe copy
5962 end
5963 if lh.v.aux == v.info then -- conflict?
5964 conflict = true
5965 lh.v.aux = extra -- previous assignment will use safe copy
5966 end
5967 end
5968 lh = lh.prev
5969 end
5970 if conflict then
5971 luaK:codeABC(fs, "OP_MOVE", fs.freereg, v.info, 0) -- make copy
5972 luaK:reserveregs(fs, 1)
5973 end
5974end
5975
5976------------------------------------------------------------------------
5977-- parse a variable assignment sequence
5978-- * recursively called
5979-- * used in exprstat()
5980------------------------------------------------------------------------
5981function luaY:assignment(ls, lh, nvars)
5982 local e = {} -- expdesc
5983 -- test was: VLOCAL <= lh->v.k && lh->v.k <= VINDEXED
5984 local c = lh.v.k
5985 self:check_condition(ls, c == "VLOCAL" or c == "VUPVAL" or c == "VGLOBAL"
5986 or c == "VINDEXED", "syntax error")
5987 if self:testnext(ls, ",") then -- assignment -> ',' primaryexp assignment
5988 local nv = {} -- LHS_assign
5989 nv.v = {}
5990 nv.prev = lh
5991 self:primaryexp(ls, nv.v)
5992 if nv.v.k == "VLOCAL" then
5993 self:check_conflict(ls, lh, nv.v)
5994 end
5995 self:checklimit(ls.fs, nvars, self.LUAI_MAXCCALLS - ls.L.nCcalls,
5996 "variables in assignment")
5997 self:assignment(ls, nv, nvars + 1)
5998 else -- assignment -> '=' explist1
5999 self:checknext(ls, "=")
6000 local nexps = self:explist1(ls, e)
6001 if nexps ~= nvars then
6002 self:adjust_assign(ls, nvars, nexps, e)
6003 if nexps > nvars then
6004 ls.fs.freereg = ls.fs.freereg - (nexps - nvars) -- remove extra values
6005 end
6006 else
6007 luaK:setoneret(ls.fs, e) -- close last expression
6008 luaK:storevar(ls.fs, lh.v, e)
6009 return -- avoid default
6010 end
6011 end
6012 self:init_exp(e, "VNONRELOC", ls.fs.freereg - 1) -- default assignment
6013 luaK:storevar(ls.fs, lh.v, e)
6014end
6015
6016------------------------------------------------------------------------
6017-- parse condition in a repeat statement or an if control structure
6018-- * used in repeatstat(), test_then_block()
6019------------------------------------------------------------------------
6020function luaY:cond(ls)
6021 -- cond -> exp
6022 local v = {} -- expdesc
6023 self:expr(ls, v) -- read condition
6024 if v.k == "VNIL" then v.k = "VFALSE" end -- 'falses' are all equal here
6025 luaK:goiftrue(ls.fs, v)
6026 return v.f
6027end
6028
6029------------------------------------------------------------------------
6030-- parse a break statement
6031-- * used in statements()
6032------------------------------------------------------------------------
6033function luaY:breakstat(ls)
6034 -- stat -> BREAK
6035 local fs = ls.fs
6036 local bl = fs.bl
6037 local upval = false
6038 while bl and not bl.isbreakable do
6039 if bl.upval then upval = true end
6040 bl = bl.previous
6041 end
6042 if not bl then
6043 luaX:syntaxerror(ls, "no loop to break")
6044 end
6045 if upval then
6046 luaK:codeABC(fs, "OP_CLOSE", bl.nactvar, 0, 0)
6047 end
6048 bl.breaklist = luaK:concat(fs, bl.breaklist, luaK:jump(fs))
6049end
6050
6051------------------------------------------------------------------------
6052-- parse a while-do control structure, body processed by block()
6053-- * with dynamic array sizes, MAXEXPWHILE + EXTRAEXP limits imposed by
6054-- the function's implementation can be removed
6055-- * used in statements()
6056------------------------------------------------------------------------
6057function luaY:whilestat(ls, line)
6058 -- whilestat -> WHILE cond DO block END
6059 local fs = ls.fs
6060 local bl = {} -- BlockCnt
6061 luaX:next(ls) -- skip WHILE
6062 local whileinit = luaK:getlabel(fs)
6063 local condexit = self:cond(ls)
6064 self:enterblock(fs, bl, true)
6065 self:checknext(ls, "TK_DO")
6066 self:block(ls)
6067 luaK:patchlist(fs, luaK:jump(fs), whileinit)
6068 self:check_match(ls, "TK_END", "TK_WHILE", line)
6069 self:leaveblock(fs)
6070 luaK:patchtohere(fs, condexit) -- false conditions finish the loop
6071end
6072
6073------------------------------------------------------------------------
6074-- parse a repeat-until control structure, body parsed by chunk()
6075-- * used in statements()
6076------------------------------------------------------------------------
6077function luaY:repeatstat(ls, line)
6078 -- repeatstat -> REPEAT block UNTIL cond
6079 local fs = ls.fs
6080 local repeat_init = luaK:getlabel(fs)
6081 local bl1, bl2 = {}, {} -- BlockCnt
6082 self:enterblock(fs, bl1, true) -- loop block
6083 self:enterblock(fs, bl2, false) -- scope block
6084 luaX:next(ls) -- skip REPEAT
6085 self:chunk(ls)
6086 self:check_match(ls, "TK_UNTIL", "TK_REPEAT", line)
6087 local condexit = self:cond(ls) -- read condition (inside scope block)
6088 if not bl2.upval then -- no upvalues?
6089 self:leaveblock(fs) -- finish scope
6090 luaK:patchlist(ls.fs, condexit, repeat_init) -- close the loop
6091 else -- complete semantics when there are upvalues
6092 self:breakstat(ls) -- if condition then break
6093 luaK:patchtohere(ls.fs, condexit) -- else...
6094 self:leaveblock(fs) -- finish scope...
6095 luaK:patchlist(ls.fs, luaK:jump(fs), repeat_init) -- and repeat
6096 end
6097 self:leaveblock(fs) -- finish loop
6098end
6099
6100------------------------------------------------------------------------
6101-- parse the single expressions needed in numerical for loops
6102-- * used in fornum()
6103------------------------------------------------------------------------
6104function luaY:exp1(ls)
6105 local e = {} -- expdesc
6106 self:expr(ls, e)
6107 local k = e.k
6108 luaK:exp2nextreg(ls.fs, e)
6109 return k
6110end
6111
6112------------------------------------------------------------------------
6113-- parse a for loop body for both versions of the for loop
6114-- * used in fornum(), forlist()
6115------------------------------------------------------------------------
6116function luaY:forbody(ls, base, line, nvars, isnum)
6117 -- forbody -> DO block
6118 local bl = {} -- BlockCnt
6119 local fs = ls.fs
6120 self:adjustlocalvars(ls, 3) -- control variables
6121 self:checknext(ls, "TK_DO")
6122 local prep = isnum and luaK:codeAsBx(fs, "OP_FORPREP", base, luaK.NO_JUMP)
6123 or luaK:jump(fs)
6124 self:enterblock(fs, bl, false) -- scope for declared variables
6125 self:adjustlocalvars(ls, nvars)
6126 luaK:reserveregs(fs, nvars)
6127 self:block(ls)
6128 self:leaveblock(fs) -- end of scope for declared variables
6129 luaK:patchtohere(fs, prep)
6130 local endfor = isnum and luaK:codeAsBx(fs, "OP_FORLOOP", base, luaK.NO_JUMP)
6131 or luaK:codeABC(fs, "OP_TFORLOOP", base, 0, nvars)
6132 luaK:fixline(fs, line) -- pretend that `OP_FOR' starts the loop
6133 luaK:patchlist(fs, isnum and endfor or luaK:jump(fs), prep + 1)
6134end
6135
6136------------------------------------------------------------------------
6137-- parse a numerical for loop, calls forbody()
6138-- * used in forstat()
6139------------------------------------------------------------------------
6140function luaY:fornum(ls, varname, line)
6141 -- fornum -> NAME = exp1,exp1[,exp1] forbody
6142 local fs = ls.fs
6143 local base = fs.freereg
6144 self:new_localvarliteral(ls, "(for index)", 0)
6145 self:new_localvarliteral(ls, "(for limit)", 1)
6146 self:new_localvarliteral(ls, "(for step)", 2)
6147 self:new_localvar(ls, varname, 3)
6148 self:checknext(ls, '=')
6149 self:exp1(ls) -- initial value
6150 self:checknext(ls, ",")
6151 self:exp1(ls) -- limit
6152 if self:testnext(ls, ",") then
6153 self:exp1(ls) -- optional step
6154 else -- default step = 1
6155 luaK:codeABx(fs, "OP_LOADK", fs.freereg, luaK:numberK(fs, 1))
6156 luaK:reserveregs(fs, 1)
6157 end
6158 self:forbody(ls, base, line, 1, true)
6159end
6160
6161------------------------------------------------------------------------
6162-- parse a generic for loop, calls forbody()
6163-- * used in forstat()
6164------------------------------------------------------------------------
6165function luaY:forlist(ls, indexname)
6166 -- forlist -> NAME {,NAME} IN explist1 forbody
6167 local fs = ls.fs
6168 local e = {} -- expdesc
6169 local nvars = 0
6170 local base = fs.freereg
6171 -- create control variables
6172 self:new_localvarliteral(ls, "(for generator)", nvars)
6173 nvars = nvars + 1
6174 self:new_localvarliteral(ls, "(for state)", nvars)
6175 nvars = nvars + 1
6176 self:new_localvarliteral(ls, "(for control)", nvars)
6177 nvars = nvars + 1
6178 -- create declared variables
6179 self:new_localvar(ls, indexname, nvars)
6180 nvars = nvars + 1
6181 while self:testnext(ls, ",") do
6182 self:new_localvar(ls, self:str_checkname(ls), nvars)
6183 nvars = nvars + 1
6184 end
6185 self:checknext(ls, "TK_IN")
6186 local line = ls.linenumber
6187 self:adjust_assign(ls, 3, self:explist1(ls, e), e)
6188 luaK:checkstack(fs, 3) -- extra space to call generator
6189 self:forbody(ls, base, line, nvars - 3, false)
6190end
6191
6192------------------------------------------------------------------------
6193-- initial parsing for a for loop, calls fornum() or forlist()
6194-- * used in statements()
6195------------------------------------------------------------------------
6196function luaY:forstat(ls, line)
6197 -- forstat -> FOR (fornum | forlist) END
6198 local fs = ls.fs
6199 local bl = {} -- BlockCnt
6200 self:enterblock(fs, bl, true) -- scope for loop and control variables
6201 luaX:next(ls) -- skip `for'
6202 local varname = self:str_checkname(ls) -- first variable name
6203 local c = ls.t.token
6204 if c == "=" then
6205 self:fornum(ls, varname, line)
6206 elseif c == "," or c == "TK_IN" then
6207 self:forlist(ls, varname)
6208 else
6209 luaX:syntaxerror(ls, self:LUA_QL("=").." or "..self:LUA_QL("in").." expected")
6210 end
6211 self:check_match(ls, "TK_END", "TK_FOR", line)
6212 self:leaveblock(fs) -- loop scope (`break' jumps to this point)
6213end
6214
6215------------------------------------------------------------------------
6216-- parse part of an if control structure, including the condition
6217-- * used in ifstat()
6218------------------------------------------------------------------------
6219function luaY:test_then_block(ls)
6220 -- test_then_block -> [IF | ELSEIF] cond THEN block
6221 luaX:next(ls) -- skip IF or ELSEIF
6222 local condexit = self:cond(ls)
6223 self:checknext(ls, "TK_THEN")
6224 self:block(ls) -- `then' part
6225 return condexit
6226end
6227
6228------------------------------------------------------------------------
6229-- parse an if control structure
6230-- * used in statements()
6231------------------------------------------------------------------------
6232function luaY:ifstat(ls, line)
6233 -- ifstat -> IF cond THEN block {ELSEIF cond THEN block} [ELSE block] END
6234 local fs = ls.fs
6235 local escapelist = luaK.NO_JUMP
6236 local flist = self:test_then_block(ls) -- IF cond THEN block
6237 while ls.t.token == "TK_ELSEIF" do
6238 escapelist = luaK:concat(fs, escapelist, luaK:jump(fs))
6239 luaK:patchtohere(fs, flist)
6240 flist = self:test_then_block(ls) -- ELSEIF cond THEN block
6241 end
6242 if ls.t.token == "TK_ELSE" then
6243 escapelist = luaK:concat(fs, escapelist, luaK:jump(fs))
6244 luaK:patchtohere(fs, flist)
6245 luaX:next(ls) -- skip ELSE (after patch, for correct line info)
6246 self:block(ls) -- 'else' part
6247 else
6248 escapelist = luaK:concat(fs, escapelist, flist)
6249 end
6250 luaK:patchtohere(fs, escapelist)
6251 self:check_match(ls, "TK_END", "TK_IF", line)
6252end
6253
6254------------------------------------------------------------------------
6255-- parse a local function statement
6256-- * used in statements()
6257------------------------------------------------------------------------
6258function luaY:localfunc(ls)
6259 local v, b = {}, {} -- expdesc
6260 local fs = ls.fs
6261 self:new_localvar(ls, self:str_checkname(ls), 0)
6262 self:init_exp(v, "VLOCAL", fs.freereg)
6263 luaK:reserveregs(fs, 1)
6264 self:adjustlocalvars(ls, 1)
6265 self:body(ls, b, false, ls.linenumber)
6266 luaK:storevar(fs, v, b)
6267 -- debug information will only see the variable after this point!
6268 self:getlocvar(fs, fs.nactvar - 1).startpc = fs.pc
6269end
6270
6271------------------------------------------------------------------------
6272-- parse a local variable declaration statement
6273-- * used in statements()
6274------------------------------------------------------------------------
6275function luaY:localstat(ls)
6276 -- stat -> LOCAL NAME {',' NAME} ['=' explist1]
6277 local nvars = 0
6278 local nexps
6279 local e = {} -- expdesc
6280 repeat
6281 self:new_localvar(ls, self:str_checkname(ls), nvars)
6282 nvars = nvars + 1
6283 until not self:testnext(ls, ",")
6284 if self:testnext(ls, "=") then
6285 nexps = self:explist1(ls, e)
6286 else
6287 e.k = "VVOID"
6288 nexps = 0
6289 end
6290 self:adjust_assign(ls, nvars, nexps, e)
6291 self:adjustlocalvars(ls, nvars)
6292end
6293
6294------------------------------------------------------------------------
6295-- parse a function name specification
6296-- * used in funcstat()
6297------------------------------------------------------------------------
6298function luaY:funcname(ls, v)
6299 -- funcname -> NAME {field} [':' NAME]
6300 local needself = false
6301 self:singlevar(ls, v)
6302 while ls.t.token == "." do
6303 self:field(ls, v)
6304 end
6305 if ls.t.token == ":" then
6306 needself = true
6307 self:field(ls, v)
6308 end
6309 return needself
6310end
6311
6312------------------------------------------------------------------------
6313-- parse a function statement
6314-- * used in statements()
6315------------------------------------------------------------------------
6316function luaY:funcstat(ls, line)
6317 -- funcstat -> FUNCTION funcname body
6318 local v, b = {}, {} -- expdesc
6319 luaX:next(ls) -- skip FUNCTION
6320 local needself = self:funcname(ls, v)
6321 self:body(ls, b, needself, line)
6322 luaK:storevar(ls.fs, v, b)
6323 luaK:fixline(ls.fs, line) -- definition 'happens' in the first line
6324end
6325
6326------------------------------------------------------------------------
6327-- parse a function call with no returns or an assignment statement
6328-- * used in statements()
6329------------------------------------------------------------------------
6330function luaY:exprstat(ls)
6331 -- stat -> func | assignment
6332 local fs = ls.fs
6333 local v = {} -- LHS_assign
6334 v.v = {}
6335 self:primaryexp(ls, v.v)
6336 if v.v.k == "VCALL" then -- stat -> func
6337 luaP:SETARG_C(luaK:getcode(fs, v.v), 1) -- call statement uses no results
6338 else -- stat -> assignment
6339 v.prev = nil
6340 self:assignment(ls, v, 1)
6341 end
6342end
6343
6344------------------------------------------------------------------------
6345-- parse a return statement
6346-- * used in statements()
6347------------------------------------------------------------------------
6348function luaY:retstat(ls)
6349 -- stat -> RETURN explist
6350 local fs = ls.fs
6351 local e = {} -- expdesc
6352 local first, nret -- registers with returned values
6353 luaX:next(ls) -- skip RETURN
6354 if self:block_follow(ls.t.token) or ls.t.token == ";" then
6355 first, nret = 0, 0 -- return no values
6356 else
6357 nret = self:explist1(ls, e) -- optional return values
6358 if self:hasmultret(e.k) then
6359 luaK:setmultret(fs, e)
6360 if e.k == "VCALL" and nret == 1 then -- tail call?
6361 luaP:SET_OPCODE(luaK:getcode(fs, e), "OP_TAILCALL")
6362 assert(luaP:GETARG_A(luaK:getcode(fs, e)) == fs.nactvar)
6363 end
6364 first = fs.nactvar
6365 nret = self.LUA_MULTRET -- return all values
6366 else
6367 if nret == 1 then -- only one single value?
6368 first = luaK:exp2anyreg(fs, e)
6369 else
6370 luaK:exp2nextreg(fs, e) -- values must go to the 'stack'
6371 first = fs.nactvar -- return all 'active' values
6372 assert(nret == fs.freereg - first)
6373 end
6374 end--if
6375 end--if
6376 luaK:ret(fs, first, nret)
6377end
6378
6379------------------------------------------------------------------------
6380-- initial parsing for statements, calls a lot of functions
6381-- * returns boolean instead of 0|1
6382-- * used in chunk()
6383------------------------------------------------------------------------
6384function luaY:statement(ls)
6385 local line = ls.linenumber -- may be needed for error messages
6386 local c = ls.t.token
6387 if c == "TK_IF" then -- stat -> ifstat
6388 self:ifstat(ls, line)
6389 return false
6390 elseif c == "TK_WHILE" then -- stat -> whilestat
6391 self:whilestat(ls, line)
6392 return false
6393 elseif c == "TK_DO" then -- stat -> DO block END
6394 luaX:next(ls) -- skip DO
6395 self:block(ls)
6396 self:check_match(ls, "TK_END", "TK_DO", line)
6397 return false
6398 elseif c == "TK_FOR" then -- stat -> forstat
6399 self:forstat(ls, line)
6400 return false
6401 elseif c == "TK_REPEAT" then -- stat -> repeatstat
6402 self:repeatstat(ls, line)
6403 return false
6404 elseif c == "TK_FUNCTION" then -- stat -> funcstat
6405 self:funcstat(ls, line)
6406 return false
6407 elseif c == "TK_LOCAL" then -- stat -> localstat
6408 luaX:next(ls) -- skip LOCAL
6409 if self:testnext(ls, "TK_FUNCTION") then -- local function?
6410 self:localfunc(ls)
6411 else
6412 self:localstat(ls)
6413 end
6414 return false
6415 elseif c == "TK_RETURN" then -- stat -> retstat
6416 self:retstat(ls)
6417 return true -- must be last statement
6418 elseif c == "TK_BREAK" then -- stat -> breakstat
6419 luaX:next(ls) -- skip BREAK
6420 self:breakstat(ls)
6421 return true -- must be last statement
6422 else
6423 self:exprstat(ls)
6424 return false -- to avoid warnings
6425 end--if c
6426end
6427
6428------------------------------------------------------------------------
6429-- parse a chunk, which consists of a bunch of statements
6430-- * used in parser(), body(), block(), repeatstat()
6431------------------------------------------------------------------------
6432function luaY:chunk(ls)
6433 -- chunk -> { stat [';'] }
6434 local islast = false
6435 self:enterlevel(ls)
6436 while not islast and not self:block_follow(ls.t.token) do
6437 islast = self:statement(ls)
6438 self:testnext(ls, ";")
6439 assert(ls.fs.f.maxstacksize >= ls.fs.freereg and
6440 ls.fs.freereg >= ls.fs.nactvar)
6441 ls.fs.freereg = ls.fs.nactvar -- free registers
6442 end
6443 self:leavelevel(ls)
6444end
6445
6446-- }======================================================================
6447return luaY]]></ProtectedString>
6448 <BinaryString name="Tags"></BinaryString>
6449 </Properties>
6450 </Item>
6451 <Item class="ModuleScript" referent="RBX6D281683B751445CAD6CA6A9CAA1B31F">
6452 <Properties>
6453 <Content name="LinkedSource"><null></null></Content>
6454 <string name="Name">LuaK</string>
6455 <string name="ScriptGuid">{E74CCBE8-6F78-46F4-B517-8E4B60989D32}</string>
6456 <ProtectedString name="Source"><![CDATA[--[[--------------------------------------------------------------------
6457
6458 lcode.lua
6459 Lua 5 code generator in Lua
6460 This file is part of Yueliang.
6461
6462 Copyright (c) 2005-2007 Kein-Hong Man <khman@users.sf.net>
6463 The COPYRIGHT file describes the conditions
6464 under which this software may be distributed.
6465
6466 See the ChangeLog for more information.
6467
6468----------------------------------------------------------------------]]
6469
6470--[[--------------------------------------------------------------------
6471-- Notes:
6472-- * one function manipulate a pointer argument with a simple data type
6473-- (can't be emulated by a table, ambiguous), now returns that value:
6474-- luaK:concat(fs, l1, l2)
6475-- * luaM_growvector uses the faux luaY:growvector, for limit checking
6476-- * some function parameters changed to boolean, additional code
6477-- translates boolean back to 1/0 for instruction fields
6478--
6479-- Not implemented:
6480-- * NOTE there is a failed assert in luaK:addk, a porting problem
6481--
6482-- Added:
6483-- * constant MAXSTACK from llimits.h
6484-- * luaK:ttisnumber(o) (from lobject.h)
6485-- * luaK:nvalue(o) (from lobject.h)
6486-- * luaK:setnilvalue(o) (from lobject.h)
6487-- * luaK:setnvalue(o, x) (from lobject.h)
6488-- * luaK:setbvalue(o, x) (from lobject.h)
6489-- * luaK:sethvalue(o, x) (from lobject.h), parameter L deleted
6490-- * luaK:setsvalue(o, x) (from lobject.h), parameter L deleted
6491-- * luaK:numadd, luaK:numsub, luaK:nummul, luaK:numdiv, luaK:nummod,
6492-- luaK:numpow, luaK:numunm, luaK:numisnan (from luaconf.h)
6493-- * copyexp(e1, e2) added in luaK:posfix to copy expdesc struct
6494--
6495-- Changed in 5.1.x:
6496-- * enum BinOpr has a new entry, OPR_MOD
6497-- * enum UnOpr has a new entry, OPR_LEN
6498-- * binopistest, unused in 5.0.x, has been deleted
6499-- * macro setmultret is new
6500-- * functions isnumeral, luaK_ret, boolK are new
6501-- * funcion nilK was named nil_constant in 5.0.x
6502-- * function interface changed: need_value, patchtestreg, concat
6503-- * TObject now a TValue
6504-- * functions luaK_setreturns, luaK_setoneret are new
6505-- * function luaK:setcallreturns deleted, to be replaced by:
6506-- luaK:setmultret, luaK:ret, luaK:setreturns, luaK:setoneret
6507-- * functions constfolding, codearith, codecomp are new
6508-- * luaK:codebinop has been deleted
6509-- * function luaK_setlist is new
6510-- * OPR_MULT renamed to OPR_MUL
6511----------------------------------------------------------------------]]
6512
6513-- requires luaP, luaX, luaY
6514local luaK = {}
6515local luaP = require(script.Parent.LuaP)
6516local luaX = require(script.Parent.LuaX)
6517
6518------------------------------------------------------------------------
6519-- constants used by code generator
6520------------------------------------------------------------------------
6521-- maximum stack for a Lua function
6522luaK.MAXSTACK = 250 -- (from llimits.h)
6523
6524--[[--------------------------------------------------------------------
6525-- other functions
6526----------------------------------------------------------------------]]
6527
6528------------------------------------------------------------------------
6529-- emulation of TValue macros (these are from lobject.h)
6530-- * TValue is a table since lcode passes references around
6531-- * tt member field removed, using Lua's type() instead
6532-- * for setsvalue, sethvalue, parameter L (deleted here) in lobject.h
6533-- is used in an assert for testing, see checkliveness(g,obj)
6534------------------------------------------------------------------------
6535function luaK:ttisnumber(o)
6536 if o then return type(o.value) == "number" else return false end
6537end
6538function luaK:nvalue(o) return o.value end
6539function luaK:setnilvalue(o) o.value = nil end
6540function luaK:setsvalue(o, x) o.value = x end
6541luaK.setnvalue = luaK.setsvalue
6542luaK.sethvalue = luaK.setsvalue
6543luaK.setbvalue = luaK.setsvalue
6544
6545------------------------------------------------------------------------
6546-- The luai_num* macros define the primitive operations over numbers.
6547-- * this is not the entire set of primitive operations from luaconf.h
6548-- * used in luaK:constfolding()
6549------------------------------------------------------------------------
6550function luaK:numadd(a, b) return a + b end
6551function luaK:numsub(a, b) return a - b end
6552function luaK:nummul(a, b) return a * b end
6553function luaK:numdiv(a, b) return a / b end
6554function luaK:nummod(a, b) return a % b end
6555 -- ((a) - floor((a)/(b))*(b)) /* actual, for reference */
6556function luaK:numpow(a, b) return a ^ b end
6557function luaK:numunm(a) return -a end
6558function luaK:numisnan(a) return not a == a end
6559 -- a NaN cannot equal another NaN
6560
6561--[[--------------------------------------------------------------------
6562-- code generator functions
6563----------------------------------------------------------------------]]
6564
6565------------------------------------------------------------------------
6566-- Marks the end of a patch list. It is an invalid value both as an absolute
6567-- address, and as a list link (would link an element to itself).
6568------------------------------------------------------------------------
6569luaK.NO_JUMP = -1
6570
6571------------------------------------------------------------------------
6572-- grep "ORDER OPR" if you change these enums
6573------------------------------------------------------------------------
6574luaK.BinOpr = {
6575 OPR_ADD = 0, OPR_SUB = 1, OPR_MUL = 2, OPR_DIV = 3, OPR_MOD = 4, OPR_POW = 5,
6576 OPR_CONCAT = 6,
6577 OPR_NE = 7, OPR_EQ = 8,
6578 OPR_LT = 9, OPR_LE = 10, OPR_GT = 11, OPR_GE = 12,
6579 OPR_AND = 13, OPR_OR = 14,
6580 OPR_NOBINOPR = 15,
6581}
6582
6583-- * UnOpr is used by luaK:prefix's op argument, but not directly used
6584-- because the function receives the symbols as strings, e.g. "OPR_NOT"
6585luaK.UnOpr = {
6586 OPR_MINUS = 0, OPR_NOT = 1, OPR_LEN = 2, OPR_NOUNOPR = 3
6587}
6588
6589------------------------------------------------------------------------
6590-- returns the instruction object for given e (expdesc), was a macro
6591------------------------------------------------------------------------
6592function luaK:getcode(fs, e)
6593 return fs.f.code[e.info]
6594end
6595
6596------------------------------------------------------------------------
6597-- codes an instruction with a signed Bx (sBx) field, was a macro
6598-- * used in luaK:jump(), (lparser) luaY:forbody()
6599------------------------------------------------------------------------
6600function luaK:codeAsBx(fs, o, A, sBx)
6601 return self:codeABx(fs, o, A, sBx + luaP.MAXARG_sBx)
6602end
6603
6604------------------------------------------------------------------------
6605-- set the expdesc e instruction for multiple returns, was a macro
6606------------------------------------------------------------------------
6607function luaK:setmultret(fs, e)
6608 self:setreturns(fs, e, luaY.LUA_MULTRET)
6609end
6610
6611------------------------------------------------------------------------
6612-- there is a jump if patch lists are not identical, was a macro
6613-- * used in luaK:exp2reg(), luaK:exp2anyreg(), luaK:exp2val()
6614------------------------------------------------------------------------
6615function luaK:hasjumps(e)
6616 return e.t ~= e.f
6617end
6618
6619------------------------------------------------------------------------
6620-- true if the expression is a constant number (for constant folding)
6621-- * used in constfolding(), infix()
6622------------------------------------------------------------------------
6623function luaK:isnumeral(e)
6624 return e.k == "VKNUM" and e.t == self.NO_JUMP and e.f == self.NO_JUMP
6625end
6626
6627------------------------------------------------------------------------
6628-- codes loading of nil, optimization done if consecutive locations
6629-- * used in luaK:discharge2reg(), (lparser) luaY:adjust_assign()
6630------------------------------------------------------------------------
6631function luaK:_nil(fs, from, n)
6632 if fs.pc > fs.lasttarget then -- no jumps to current position?
6633 if fs.pc == 0 then -- function start?
6634 if from >= fs.nactvar then
6635 return -- positions are already clean
6636 end
6637 else
6638 local previous = fs.f.code[fs.pc - 1]
6639 if luaP:GET_OPCODE(previous) == "OP_LOADNIL" then
6640 local pfrom = luaP:GETARG_A(previous)
6641 local pto = luaP:GETARG_B(previous)
6642 if pfrom <= from and from <= pto + 1 then -- can connect both?
6643 if from + n - 1 > pto then
6644 luaP:SETARG_B(previous, from + n - 1)
6645 end
6646 return
6647 end
6648 end
6649 end
6650 end
6651 self:codeABC(fs, "OP_LOADNIL", from, from + n - 1, 0) -- else no optimization
6652end
6653
6654------------------------------------------------------------------------
6655--
6656-- * used in multiple locations
6657------------------------------------------------------------------------
6658function luaK:jump(fs)
6659 local jpc = fs.jpc -- save list of jumps to here
6660 fs.jpc = self.NO_JUMP
6661 local j = self:codeAsBx(fs, "OP_JMP", 0, self.NO_JUMP)
6662 j = self:concat(fs, j, jpc) -- keep them on hold
6663 return j
6664end
6665
6666------------------------------------------------------------------------
6667-- codes a RETURN instruction
6668-- * used in luaY:close_func(), luaY:retstat()
6669------------------------------------------------------------------------
6670function luaK:ret(fs, first, nret)
6671 self:codeABC(fs, "OP_RETURN", first, nret + 1, 0)
6672end
6673
6674------------------------------------------------------------------------
6675--
6676-- * used in luaK:jumponcond(), luaK:codecomp()
6677------------------------------------------------------------------------
6678function luaK:condjump(fs, op, A, B, C)
6679 self:codeABC(fs, op, A, B, C)
6680 return self:jump(fs)
6681end
6682
6683------------------------------------------------------------------------
6684--
6685-- * used in luaK:patchlistaux(), luaK:concat()
6686------------------------------------------------------------------------
6687function luaK:fixjump(fs, pc, dest)
6688 local jmp = fs.f.code[pc]
6689 local offset = dest - (pc + 1)
6690 assert(dest ~= self.NO_JUMP)
6691 if math.abs(offset) > luaP.MAXARG_sBx then
6692 luaX:syntaxerror(fs.ls, "control structure too long")
6693 end
6694 luaP:SETARG_sBx(jmp, offset)
6695end
6696
6697------------------------------------------------------------------------
6698-- returns current 'pc' and marks it as a jump target (to avoid wrong
6699-- optimizations with consecutive instructions not in the same basic block).
6700-- * used in multiple locations
6701-- * fs.lasttarget tested only by luaK:_nil() when optimizing OP_LOADNIL
6702------------------------------------------------------------------------
6703function luaK:getlabel(fs)
6704 fs.lasttarget = fs.pc
6705 return fs.pc
6706end
6707
6708------------------------------------------------------------------------
6709--
6710-- * used in luaK:need_value(), luaK:removevalues(), luaK:patchlistaux(),
6711-- luaK:concat()
6712------------------------------------------------------------------------
6713function luaK:getjump(fs, pc)
6714 local offset = luaP:GETARG_sBx(fs.f.code[pc])
6715 if offset == self.NO_JUMP then -- point to itself represents end of list
6716 return self.NO_JUMP -- end of list
6717 else
6718 return (pc + 1) + offset -- turn offset into absolute position
6719 end
6720end
6721
6722------------------------------------------------------------------------
6723--
6724-- * used in luaK:need_value(), luaK:patchtestreg(), luaK:invertjump()
6725------------------------------------------------------------------------
6726function luaK:getjumpcontrol(fs, pc)
6727 local pi = fs.f.code[pc]
6728 local ppi = fs.f.code[pc - 1]
6729 if pc >= 1 and luaP:testTMode(luaP:GET_OPCODE(ppi)) ~= 0 then
6730 return ppi
6731 else
6732 return pi
6733 end
6734end
6735
6736------------------------------------------------------------------------
6737-- check whether list has any jump that do not produce a value
6738-- (or produce an inverted value)
6739-- * return value changed to boolean
6740-- * used only in luaK:exp2reg()
6741------------------------------------------------------------------------
6742function luaK:need_value(fs, list)
6743 while list ~= self.NO_JUMP do
6744 local i = self:getjumpcontrol(fs, list)
6745 if luaP:GET_OPCODE(i) ~= "OP_TESTSET" then return true end
6746 list = self:getjump(fs, list)
6747 end
6748 return false -- not found
6749end
6750
6751------------------------------------------------------------------------
6752--
6753-- * used in luaK:removevalues(), luaK:patchlistaux()
6754------------------------------------------------------------------------
6755function luaK:patchtestreg(fs, node, reg)
6756 local i = self:getjumpcontrol(fs, node)
6757 if luaP:GET_OPCODE(i) ~= "OP_TESTSET" then
6758 return false -- cannot patch other instructions
6759 end
6760 if reg ~= luaP.NO_REG and reg ~= luaP:GETARG_B(i) then
6761 luaP:SETARG_A(i, reg)
6762 else -- no register to put value or register already has the value
6763 -- due to use of a table as i, i cannot be replaced by another table
6764 -- so the following is required; there is no change to ARG_C
6765 luaP:SET_OPCODE(i, "OP_TEST")
6766 local b = luaP:GETARG_B(i)
6767 luaP:SETARG_A(i, b)
6768 luaP:SETARG_B(i, 0)
6769 -- *i = CREATE_ABC(OP_TEST, GETARG_B(*i), 0, GETARG_C(*i)); /* C */
6770 end
6771 return true
6772end
6773
6774------------------------------------------------------------------------
6775--
6776-- * used only in luaK:codenot()
6777------------------------------------------------------------------------
6778function luaK:removevalues(fs, list)
6779 while list ~= self.NO_JUMP do
6780 self:patchtestreg(fs, list, luaP.NO_REG)
6781 list = self:getjump(fs, list)
6782 end
6783end
6784
6785------------------------------------------------------------------------
6786--
6787-- * used in luaK:dischargejpc(), luaK:patchlist(), luaK:exp2reg()
6788------------------------------------------------------------------------
6789function luaK:patchlistaux(fs, list, vtarget, reg, dtarget)
6790 while list ~= self.NO_JUMP do
6791 local _next = self:getjump(fs, list)
6792 if self:patchtestreg(fs, list, reg) then
6793 self:fixjump(fs, list, vtarget)
6794 else
6795 self:fixjump(fs, list, dtarget) -- jump to default target
6796 end
6797 list = _next
6798 end
6799end
6800
6801------------------------------------------------------------------------
6802--
6803-- * used only in luaK:code()
6804------------------------------------------------------------------------
6805function luaK:dischargejpc(fs)
6806 self:patchlistaux(fs, fs.jpc, fs.pc, luaP.NO_REG, fs.pc)
6807 fs.jpc = self.NO_JUMP
6808end
6809
6810------------------------------------------------------------------------
6811--
6812-- * used in (lparser) luaY:whilestat(), luaY:repeatstat(), luaY:forbody()
6813------------------------------------------------------------------------
6814function luaK:patchlist(fs, list, target)
6815 if target == fs.pc then
6816 self:patchtohere(fs, list)
6817 else
6818 assert(target < fs.pc)
6819 self:patchlistaux(fs, list, target, luaP.NO_REG, target)
6820 end
6821end
6822
6823------------------------------------------------------------------------
6824--
6825-- * used in multiple locations
6826------------------------------------------------------------------------
6827function luaK:patchtohere(fs, list)
6828 self:getlabel(fs)
6829 fs.jpc = self:concat(fs, fs.jpc, list)
6830end
6831
6832------------------------------------------------------------------------
6833-- * l1 was a pointer, now l1 is returned and callee assigns the value
6834-- * used in multiple locations
6835------------------------------------------------------------------------
6836function luaK:concat(fs, l1, l2)
6837 if l2 == self.NO_JUMP then return l1
6838 elseif l1 == self.NO_JUMP then
6839 return l2
6840 else
6841 local list = l1
6842 local _next = self:getjump(fs, list)
6843 while _next ~= self.NO_JUMP do -- find last element
6844 list = _next
6845 _next = self:getjump(fs, list)
6846 end
6847 self:fixjump(fs, list, l2)
6848 end
6849 return l1
6850end
6851
6852------------------------------------------------------------------------
6853--
6854-- * used in luaK:reserveregs(), (lparser) luaY:forlist()
6855------------------------------------------------------------------------
6856function luaK:checkstack(fs, n)
6857 local newstack = fs.freereg + n
6858 if newstack > fs.f.maxstacksize then
6859 if newstack >= self.MAXSTACK then
6860 luaX:syntaxerror(fs.ls, "function or expression too complex")
6861 end
6862 fs.f.maxstacksize = newstack
6863 end
6864end
6865
6866------------------------------------------------------------------------
6867--
6868-- * used in multiple locations
6869------------------------------------------------------------------------
6870function luaK:reserveregs(fs, n)
6871 self:checkstack(fs, n)
6872 fs.freereg = fs.freereg + n
6873end
6874
6875------------------------------------------------------------------------
6876--
6877-- * used in luaK:freeexp(), luaK:dischargevars()
6878------------------------------------------------------------------------
6879function luaK:freereg(fs, reg)
6880 if not luaP:ISK(reg) and reg >= fs.nactvar then
6881 fs.freereg = fs.freereg - 1
6882 assert(reg == fs.freereg)
6883 end
6884end
6885
6886------------------------------------------------------------------------
6887--
6888-- * used in multiple locations
6889------------------------------------------------------------------------
6890function luaK:freeexp(fs, e)
6891 if e.k == "VNONRELOC" then
6892 self:freereg(fs, e.info)
6893 end
6894end
6895
6896------------------------------------------------------------------------
6897-- * TODO NOTE implementation is not 100% correct, since the assert fails
6898-- * luaH_set, setobj deleted; direct table access used instead
6899-- * used in luaK:stringK(), luaK:numberK(), luaK:boolK(), luaK:nilK()
6900------------------------------------------------------------------------
6901function luaK:addk(fs, k, v)
6902 local L = fs.L
6903 local idx = fs.h[k.value]
6904 --TValue *idx = luaH_set(L, fs->h, k); /* C */
6905 local f = fs.f
6906 if self:ttisnumber(idx) then
6907 --TODO this assert currently FAILS (last tested for 5.0.2)
6908 --assert(fs.f.k[self:nvalue(idx)] == v)
6909 --assert(luaO_rawequalObj(&fs->f->k[cast_int(nvalue(idx))], v)); /* C */
6910 return self:nvalue(idx)
6911 else -- constant not found; create a new entry
6912 idx = {}
6913 self:setnvalue(idx, fs.nk)
6914 fs.h[k.value] = idx
6915 -- setnvalue(idx, cast_num(fs->nk)); /* C */
6916 luaY:growvector(L, f.k, fs.nk, f.sizek, nil,
6917 luaP.MAXARG_Bx, "constant table overflow")
6918 -- loop to initialize empty f.k positions not required
6919 f.k[fs.nk] = v
6920 -- setobj(L, &f->k[fs->nk], v); /* C */
6921 -- luaC_barrier(L, f, v); /* GC */
6922 local nk = fs.nk
6923 fs.nk = fs.nk + 1
6924 return nk
6925 end
6926
6927end
6928
6929------------------------------------------------------------------------
6930-- creates and sets a string object
6931-- * used in (lparser) luaY:codestring(), luaY:singlevar()
6932------------------------------------------------------------------------
6933function luaK:stringK(fs, s)
6934 local o = {} -- TValue
6935 self:setsvalue(o, s)
6936 return self:addk(fs, o, o)
6937end
6938
6939------------------------------------------------------------------------
6940-- creates and sets a number object
6941-- * used in luaK:prefix() for negative (or negation of) numbers
6942-- * used in (lparser) luaY:simpleexp(), luaY:fornum()
6943------------------------------------------------------------------------
6944function luaK:numberK(fs, r)
6945 local o = {} -- TValue
6946 self:setnvalue(o, r)
6947 return self:addk(fs, o, o)
6948end
6949
6950------------------------------------------------------------------------
6951-- creates and sets a boolean object
6952-- * used only in luaK:exp2RK()
6953------------------------------------------------------------------------
6954function luaK:boolK(fs, b)
6955 local o = {} -- TValue
6956 self:setbvalue(o, b)
6957 return self:addk(fs, o, o)
6958end
6959
6960------------------------------------------------------------------------
6961-- creates and sets a nil object
6962-- * used only in luaK:exp2RK()
6963------------------------------------------------------------------------
6964function luaK:nilK(fs)
6965 local k, v = {}, {} -- TValue
6966 self:setnilvalue(v)
6967 -- cannot use nil as key; instead use table itself to represent nil
6968 self:sethvalue(k, fs.h)
6969 return self:addk(fs, k, v)
6970end
6971
6972------------------------------------------------------------------------
6973--
6974-- * used in luaK:setmultret(), (lparser) luaY:adjust_assign()
6975------------------------------------------------------------------------
6976function luaK:setreturns(fs, e, nresults)
6977 if e.k == "VCALL" then -- expression is an open function call?
6978 luaP:SETARG_C(self:getcode(fs, e), nresults + 1)
6979 elseif e.k == "VVARARG" then
6980 luaP:SETARG_B(self:getcode(fs, e), nresults + 1);
6981 luaP:SETARG_A(self:getcode(fs, e), fs.freereg);
6982 luaK:reserveregs(fs, 1)
6983 end
6984end
6985
6986------------------------------------------------------------------------
6987--
6988-- * used in luaK:dischargevars(), (lparser) luaY:assignment()
6989------------------------------------------------------------------------
6990function luaK:setoneret(fs, e)
6991 if e.k == "VCALL" then -- expression is an open function call?
6992 e.k = "VNONRELOC"
6993 e.info = luaP:GETARG_A(self:getcode(fs, e))
6994 elseif e.k == "VVARARG" then
6995 luaP:SETARG_B(self:getcode(fs, e), 2)
6996 e.k = "VRELOCABLE" -- can relocate its simple result
6997 end
6998end
6999
7000------------------------------------------------------------------------
7001--
7002-- * used in multiple locations
7003------------------------------------------------------------------------
7004function luaK:dischargevars(fs, e)
7005 local k = e.k
7006 if k == "VLOCAL" then
7007 e.k = "VNONRELOC"
7008 elseif k == "VUPVAL" then
7009 e.info = self:codeABC(fs, "OP_GETUPVAL", 0, e.info, 0)
7010 e.k = "VRELOCABLE"
7011 elseif k == "VGLOBAL" then
7012 e.info = self:codeABx(fs, "OP_GETGLOBAL", 0, e.info)
7013 e.k = "VRELOCABLE"
7014 elseif k == "VINDEXED" then
7015 self:freereg(fs, e.aux)
7016 self:freereg(fs, e.info)
7017 e.info = self:codeABC(fs, "OP_GETTABLE", 0, e.info, e.aux)
7018 e.k = "VRELOCABLE"
7019 elseif k == "VVARARG" or k == "VCALL" then
7020 self:setoneret(fs, e)
7021 else
7022 -- there is one value available (somewhere)
7023 end
7024end
7025
7026------------------------------------------------------------------------
7027--
7028-- * used only in luaK:exp2reg()
7029------------------------------------------------------------------------
7030function luaK:code_label(fs, A, b, jump)
7031 self:getlabel(fs) -- those instructions may be jump targets
7032 return self:codeABC(fs, "OP_LOADBOOL", A, b, jump)
7033end
7034
7035------------------------------------------------------------------------
7036--
7037-- * used in luaK:discharge2anyreg(), luaK:exp2reg()
7038------------------------------------------------------------------------
7039function luaK:discharge2reg(fs, e, reg)
7040 self:dischargevars(fs, e)
7041 local k = e.k
7042 if k == "VNIL" then
7043 self:_nil(fs, reg, 1)
7044 elseif k == "VFALSE" or k == "VTRUE" then
7045 self:codeABC(fs, "OP_LOADBOOL", reg, (e.k == "VTRUE") and 1 or 0, 0)
7046 elseif k == "VK" then
7047 self:codeABx(fs, "OP_LOADK", reg, e.info)
7048 elseif k == "VKNUM" then
7049 self:codeABx(fs, "OP_LOADK", reg, self:numberK(fs, e.nval))
7050 elseif k == "VRELOCABLE" then
7051 local pc = self:getcode(fs, e)
7052 luaP:SETARG_A(pc, reg)
7053 elseif k == "VNONRELOC" then
7054 if reg ~= e.info then
7055 self:codeABC(fs, "OP_MOVE", reg, e.info, 0)
7056 end
7057 else
7058 assert(e.k == "VVOID" or e.k == "VJMP")
7059 return -- nothing to do...
7060 end
7061 e.info = reg
7062 e.k = "VNONRELOC"
7063end
7064
7065------------------------------------------------------------------------
7066--
7067-- * used in luaK:jumponcond(), luaK:codenot()
7068------------------------------------------------------------------------
7069function luaK:discharge2anyreg(fs, e)
7070 if e.k ~= "VNONRELOC" then
7071 self:reserveregs(fs, 1)
7072 self:discharge2reg(fs, e, fs.freereg - 1)
7073 end
7074end
7075
7076------------------------------------------------------------------------
7077--
7078-- * used in luaK:exp2nextreg(), luaK:exp2anyreg(), luaK:storevar()
7079------------------------------------------------------------------------
7080function luaK:exp2reg(fs, e, reg)
7081 self:discharge2reg(fs, e, reg)
7082 if e.k == "VJMP" then
7083 e.t = self:concat(fs, e.t, e.info) -- put this jump in 't' list
7084 end
7085 if self:hasjumps(e) then
7086 local final -- position after whole expression
7087 local p_f = self.NO_JUMP -- position of an eventual LOAD false
7088 local p_t = self.NO_JUMP -- position of an eventual LOAD true
7089 if self:need_value(fs, e.t) or self:need_value(fs, e.f) then
7090 local fj = (e.k == "VJMP") and self.NO_JUMP or self:jump(fs)
7091 p_f = self:code_label(fs, reg, 0, 1)
7092 p_t = self:code_label(fs, reg, 1, 0)
7093 self:patchtohere(fs, fj)
7094 end
7095 final = self:getlabel(fs)
7096 self:patchlistaux(fs, e.f, final, reg, p_f)
7097 self:patchlistaux(fs, e.t, final, reg, p_t)
7098 end
7099 e.f, e.t = self.NO_JUMP, self.NO_JUMP
7100 e.info = reg
7101 e.k = "VNONRELOC"
7102end
7103
7104------------------------------------------------------------------------
7105--
7106-- * used in multiple locations
7107------------------------------------------------------------------------
7108function luaK:exp2nextreg(fs, e)
7109 self:dischargevars(fs, e)
7110 self:freeexp(fs, e)
7111 self:reserveregs(fs, 1)
7112 self:exp2reg(fs, e, fs.freereg - 1)
7113end
7114
7115------------------------------------------------------------------------
7116--
7117-- * used in multiple locations
7118------------------------------------------------------------------------
7119function luaK:exp2anyreg(fs, e)
7120 self:dischargevars(fs, e)
7121 if e.k == "VNONRELOC" then
7122 if not self:hasjumps(e) then -- exp is already in a register
7123 return e.info
7124 end
7125 if e.info >= fs.nactvar then -- reg. is not a local?
7126 self:exp2reg(fs, e, e.info) -- put value on it
7127 return e.info
7128 end
7129 end
7130 self:exp2nextreg(fs, e) -- default
7131 return e.info
7132end
7133
7134------------------------------------------------------------------------
7135--
7136-- * used in luaK:exp2RK(), luaK:prefix(), luaK:posfix()
7137-- * used in (lparser) luaY:yindex()
7138------------------------------------------------------------------------
7139function luaK:exp2val(fs, e)
7140 if self:hasjumps(e) then
7141 self:exp2anyreg(fs, e)
7142 else
7143 self:dischargevars(fs, e)
7144 end
7145end
7146
7147------------------------------------------------------------------------
7148--
7149-- * used in multiple locations
7150------------------------------------------------------------------------
7151function luaK:exp2RK(fs, e)
7152 self:exp2val(fs, e)
7153 local k = e.k
7154 if k == "VKNUM" or k == "VTRUE" or k == "VFALSE" or k == "VNIL" then
7155 if fs.nk <= luaP.MAXINDEXRK then -- constant fit in RK operand?
7156 -- converted from a 2-deep ternary operator expression
7157 if e.k == "VNIL" then
7158 e.info = self:nilK(fs)
7159 else
7160 e.info = (e.k == "VKNUM") and self:numberK(fs, e.nval)
7161 or self:boolK(fs, e.k == "VTRUE")
7162 end
7163 e.k = "VK"
7164 return luaP:RKASK(e.info)
7165 end
7166 elseif k == "VK" then
7167 if e.info <= luaP.MAXINDEXRK then -- constant fit in argC?
7168 return luaP:RKASK(e.info)
7169 end
7170 else
7171 -- default
7172 end
7173 -- not a constant in the right range: put it in a register
7174 return self:exp2anyreg(fs, e)
7175end
7176
7177------------------------------------------------------------------------
7178--
7179-- * used in (lparser) luaY:assignment(), luaY:localfunc(), luaY:funcstat()
7180------------------------------------------------------------------------
7181function luaK:storevar(fs, var, ex)
7182 local k = var.k
7183 if k == "VLOCAL" then
7184 self:freeexp(fs, ex)
7185 self:exp2reg(fs, ex, var.info)
7186 return
7187 elseif k == "VUPVAL" then
7188 local e = self:exp2anyreg(fs, ex)
7189 self:codeABC(fs, "OP_SETUPVAL", e, var.info, 0)
7190 elseif k == "VGLOBAL" then
7191 local e = self:exp2anyreg(fs, ex)
7192 self:codeABx(fs, "OP_SETGLOBAL", e, var.info)
7193 elseif k == "VINDEXED" then
7194 local e = self:exp2RK(fs, ex)
7195 self:codeABC(fs, "OP_SETTABLE", var.info, var.aux, e)
7196 else
7197 assert(0) -- invalid var kind to store
7198 end
7199 self:freeexp(fs, ex)
7200end
7201
7202------------------------------------------------------------------------
7203--
7204-- * used only in (lparser) luaY:primaryexp()
7205------------------------------------------------------------------------
7206function luaK:_self(fs, e, key)
7207 self:exp2anyreg(fs, e)
7208 self:freeexp(fs, e)
7209 local func = fs.freereg
7210 self:reserveregs(fs, 2)
7211 self:codeABC(fs, "OP_SELF", func, e.info, self:exp2RK(fs, key))
7212 self:freeexp(fs, key)
7213 e.info = func
7214 e.k = "VNONRELOC"
7215end
7216
7217------------------------------------------------------------------------
7218--
7219-- * used in luaK:goiftrue(), luaK:codenot()
7220------------------------------------------------------------------------
7221function luaK:invertjump(fs, e)
7222 local pc = self:getjumpcontrol(fs, e.info)
7223 assert(luaP:testTMode(luaP:GET_OPCODE(pc)) ~= 0 and
7224 luaP:GET_OPCODE(pc) ~= "OP_TESTSET" and
7225 luaP:GET_OPCODE(pc) ~= "OP_TEST")
7226 luaP:SETARG_A(pc, (luaP:GETARG_A(pc) == 0) and 1 or 0)
7227end
7228
7229------------------------------------------------------------------------
7230--
7231-- * used in luaK:goiftrue(), luaK:goiffalse()
7232------------------------------------------------------------------------
7233function luaK:jumponcond(fs, e, cond)
7234 if e.k == "VRELOCABLE" then
7235 local ie = self:getcode(fs, e)
7236 if luaP:GET_OPCODE(ie) == "OP_NOT" then
7237 fs.pc = fs.pc - 1 -- remove previous OP_NOT
7238 return self:condjump(fs, "OP_TEST", luaP:GETARG_B(ie), 0, cond and 0 or 1)
7239 end
7240 -- else go through
7241 end
7242 self:discharge2anyreg(fs, e)
7243 self:freeexp(fs, e)
7244 return self:condjump(fs, "OP_TESTSET", luaP.NO_REG, e.info, cond and 1 or 0)
7245end
7246
7247------------------------------------------------------------------------
7248--
7249-- * used in luaK:infix(), (lparser) luaY:cond()
7250------------------------------------------------------------------------
7251function luaK:goiftrue(fs, e)
7252 local pc -- pc of last jump
7253 self:dischargevars(fs, e)
7254 local k = e.k
7255 if k == "VK" or k == "VKNUM" or k == "VTRUE" then
7256 pc = self.NO_JUMP -- always true; do nothing
7257 elseif k == "VFALSE" then
7258 pc = self:jump(fs) -- always jump
7259 elseif k == "VJMP" then
7260 self:invertjump(fs, e)
7261 pc = e.info
7262 else
7263 pc = self:jumponcond(fs, e, false)
7264 end
7265 e.f = self:concat(fs, e.f, pc) -- insert last jump in `f' list
7266 self:patchtohere(fs, e.t)
7267 e.t = self.NO_JUMP
7268end
7269
7270------------------------------------------------------------------------
7271--
7272-- * used in luaK:infix()
7273------------------------------------------------------------------------
7274function luaK:goiffalse(fs, e)
7275 local pc -- pc of last jump
7276 self:dischargevars(fs, e)
7277 local k = e.k
7278 if k == "VNIL" or k == "VFALSE"then
7279 pc = self.NO_JUMP -- always false; do nothing
7280 elseif k == "VTRUE" then
7281 pc = self:jump(fs) -- always jump
7282 elseif k == "VJMP" then
7283 pc = e.info
7284 else
7285 pc = self:jumponcond(fs, e, true)
7286 end
7287 e.t = self:concat(fs, e.t, pc) -- insert last jump in `t' list
7288 self:patchtohere(fs, e.f)
7289 e.f = self.NO_JUMP
7290end
7291
7292------------------------------------------------------------------------
7293--
7294-- * used only in luaK:prefix()
7295------------------------------------------------------------------------
7296function luaK:codenot(fs, e)
7297 self:dischargevars(fs, e)
7298 local k = e.k
7299 if k == "VNIL" or k == "VFALSE" then
7300 e.k = "VTRUE"
7301 elseif k == "VK" or k == "VKNUM" or k == "VTRUE" then
7302 e.k = "VFALSE"
7303 elseif k == "VJMP" then
7304 self:invertjump(fs, e)
7305 elseif k == "VRELOCABLE" or k == "VNONRELOC" then
7306 self:discharge2anyreg(fs, e)
7307 self:freeexp(fs, e)
7308 e.info = self:codeABC(fs, "OP_NOT", 0, e.info, 0)
7309 e.k = "VRELOCABLE"
7310 else
7311 assert(0) -- cannot happen
7312 end
7313 -- interchange true and false lists
7314 e.f, e.t = e.t, e.f
7315 self:removevalues(fs, e.f)
7316 self:removevalues(fs, e.t)
7317end
7318
7319------------------------------------------------------------------------
7320--
7321-- * used in (lparser) luaY:field(), luaY:primaryexp()
7322------------------------------------------------------------------------
7323function luaK:indexed(fs, t, k)
7324 t.aux = self:exp2RK(fs, k)
7325 t.k = "VINDEXED"
7326end
7327
7328------------------------------------------------------------------------
7329--
7330-- * used only in luaK:codearith()
7331------------------------------------------------------------------------
7332function luaK:constfolding(op, e1, e2)
7333 local r
7334 if not self:isnumeral(e1) or not self:isnumeral(e2) then return false end
7335 local v1 = e1.nval
7336 local v2 = e2.nval
7337 if op == "OP_ADD" then
7338 r = self:numadd(v1, v2)
7339 elseif op == "OP_SUB" then
7340 r = self:numsub(v1, v2)
7341 elseif op == "OP_MUL" then
7342 r = self:nummul(v1, v2)
7343 elseif op == "OP_DIV" then
7344 if v2 == 0 then return false end -- do not attempt to divide by 0
7345 r = self:numdiv(v1, v2)
7346 elseif op == "OP_MOD" then
7347 if v2 == 0 then return false end -- do not attempt to divide by 0
7348 r = self:nummod(v1, v2)
7349 elseif op == "OP_POW" then
7350 r = self:numpow(v1, v2)
7351 elseif op == "OP_UNM" then
7352 r = self:numunm(v1)
7353 elseif op == "OP_LEN" then
7354 return false -- no constant folding for 'len'
7355 else
7356 assert(0)
7357 r = 0
7358 end
7359 if self:numisnan(r) then return false end -- do not attempt to produce NaN
7360 e1.nval = r
7361 return true
7362end
7363
7364------------------------------------------------------------------------
7365--
7366-- * used in luaK:prefix(), luaK:posfix()
7367------------------------------------------------------------------------
7368function luaK:codearith(fs, op, e1, e2)
7369 if self:constfolding(op, e1, e2) then
7370 return
7371 else
7372 local o2 = (op ~= "OP_UNM" and op ~= "OP_LEN") and self:exp2RK(fs, e2) or 0
7373 local o1 = self:exp2RK(fs, e1)
7374 if o1 > o2 then
7375 self:freeexp(fs, e1)
7376 self:freeexp(fs, e2)
7377 else
7378 self:freeexp(fs, e2)
7379 self:freeexp(fs, e1)
7380 end
7381 e1.info = self:codeABC(fs, op, 0, o1, o2)
7382 e1.k = "VRELOCABLE"
7383 end
7384end
7385
7386------------------------------------------------------------------------
7387--
7388-- * used only in luaK:posfix()
7389------------------------------------------------------------------------
7390function luaK:codecomp(fs, op, cond, e1, e2)
7391 local o1 = self:exp2RK(fs, e1)
7392 local o2 = self:exp2RK(fs, e2)
7393 self:freeexp(fs, e2)
7394 self:freeexp(fs, e1)
7395 if cond == 0 and op ~= "OP_EQ" then
7396 -- exchange args to replace by `<' or `<='
7397 o1, o2 = o2, o1 -- o1 <==> o2
7398 cond = 1
7399 end
7400 e1.info = self:condjump(fs, op, cond, o1, o2)
7401 e1.k = "VJMP"
7402end
7403
7404------------------------------------------------------------------------
7405--
7406-- * used only in (lparser) luaY:subexpr()
7407------------------------------------------------------------------------
7408function luaK:prefix(fs, op, e)
7409 local e2 = {} -- expdesc
7410 e2.t, e2.f = self.NO_JUMP, self.NO_JUMP
7411 e2.k = "VKNUM"
7412 e2.nval = 0
7413 if op == "OPR_MINUS" then
7414 if not self:isnumeral(e) then
7415 self:exp2anyreg(fs, e) -- cannot operate on non-numeric constants
7416 end
7417 self:codearith(fs, "OP_UNM", e, e2)
7418 elseif op == "OPR_NOT" then
7419 self:codenot(fs, e)
7420 elseif op == "OPR_LEN" then
7421 self:exp2anyreg(fs, e) -- cannot operate on constants
7422 self:codearith(fs, "OP_LEN", e, e2)
7423 else
7424 assert(0)
7425 end
7426end
7427
7428------------------------------------------------------------------------
7429--
7430-- * used only in (lparser) luaY:subexpr()
7431------------------------------------------------------------------------
7432function luaK:infix(fs, op, v)
7433 if op == "OPR_AND" then
7434 self:goiftrue(fs, v)
7435 elseif op == "OPR_OR" then
7436 self:goiffalse(fs, v)
7437 elseif op == "OPR_CONCAT" then
7438 self:exp2nextreg(fs, v) -- operand must be on the 'stack'
7439 elseif op == "OPR_ADD" or op == "OPR_SUB" or
7440 op == "OPR_MUL" or op == "OPR_DIV" or
7441 op == "OPR_MOD" or op == "OPR_POW" then
7442 if not self:isnumeral(v) then self:exp2RK(fs, v) end
7443 else
7444 self:exp2RK(fs, v)
7445 end
7446end
7447
7448------------------------------------------------------------------------
7449--
7450-- * used only in (lparser) luaY:subexpr()
7451------------------------------------------------------------------------
7452-- table lookups to simplify testing
7453luaK.arith_op = {
7454 OPR_ADD = "OP_ADD", OPR_SUB = "OP_SUB", OPR_MUL = "OP_MUL",
7455 OPR_DIV = "OP_DIV", OPR_MOD = "OP_MOD", OPR_POW = "OP_POW",
7456}
7457luaK.comp_op = {
7458 OPR_EQ = "OP_EQ", OPR_NE = "OP_EQ", OPR_LT = "OP_LT",
7459 OPR_LE = "OP_LE", OPR_GT = "OP_LT", OPR_GE = "OP_LE",
7460}
7461luaK.comp_cond = {
7462 OPR_EQ = 1, OPR_NE = 0, OPR_LT = 1,
7463 OPR_LE = 1, OPR_GT = 0, OPR_GE = 0,
7464}
7465function luaK:posfix(fs, op, e1, e2)
7466 -- needed because e1 = e2 doesn't copy values...
7467 -- * in 5.0.x, only k/info/aux/t/f copied, t for AND, f for OR
7468 -- but here, all elements are copied for completeness' sake
7469 local function copyexp(e1, e2)
7470 e1.k = e2.k
7471 e1.info = e2.info; e1.aux = e2.aux
7472 e1.nval = e2.nval
7473 e1.t = e2.t; e1.f = e2.f
7474 end
7475 if op == "OPR_AND" then
7476 assert(e1.t == self.NO_JUMP) -- list must be closed
7477 self:dischargevars(fs, e2)
7478 e2.f = self:concat(fs, e2.f, e1.f)
7479 copyexp(e1, e2)
7480 elseif op == "OPR_OR" then
7481 assert(e1.f == self.NO_JUMP) -- list must be closed
7482 self:dischargevars(fs, e2)
7483 e2.t = self:concat(fs, e2.t, e1.t)
7484 copyexp(e1, e2)
7485 elseif op == "OPR_CONCAT" then
7486 self:exp2val(fs, e2)
7487 if e2.k == "VRELOCABLE" and luaP:GET_OPCODE(self:getcode(fs, e2)) == "OP_CONCAT" then
7488 assert(e1.info == luaP:GETARG_B(self:getcode(fs, e2)) - 1)
7489 self:freeexp(fs, e1)
7490 luaP:SETARG_B(self:getcode(fs, e2), e1.info)
7491 e1.k = "VRELOCABLE"
7492 e1.info = e2.info
7493 else
7494 self:exp2nextreg(fs, e2) -- operand must be on the 'stack'
7495 self:codearith(fs, "OP_CONCAT", e1, e2)
7496 end
7497 else
7498 -- the following uses a table lookup in place of conditionals
7499 local arith = self.arith_op[op]
7500 if arith then
7501 self:codearith(fs, arith, e1, e2)
7502 else
7503 local comp = self.comp_op[op]
7504 if comp then
7505 self:codecomp(fs, comp, self.comp_cond[op], e1, e2)
7506 else
7507 assert(0)
7508 end
7509 end--if arith
7510 end--if op
7511end
7512
7513------------------------------------------------------------------------
7514-- adjusts debug information for last instruction written, in order to
7515-- change the line where item comes into existence
7516-- * used in (lparser) luaY:funcargs(), luaY:forbody(), luaY:funcstat()
7517------------------------------------------------------------------------
7518function luaK:fixline(fs, line)
7519 fs.f.lineinfo[fs.pc - 1] = line
7520end
7521
7522------------------------------------------------------------------------
7523-- general function to write an instruction into the instruction buffer,
7524-- sets debug information too
7525-- * used in luaK:codeABC(), luaK:codeABx()
7526-- * called directly by (lparser) luaY:whilestat()
7527------------------------------------------------------------------------
7528function luaK:code(fs, i, line)
7529 local f = fs.f
7530 self:dischargejpc(fs) -- 'pc' will change
7531 -- put new instruction in code array
7532 luaY:growvector(fs.L, f.code, fs.pc, f.sizecode, nil,
7533 luaY.MAX_INT, "code size overflow")
7534 f.code[fs.pc] = i
7535 -- save corresponding line information
7536 luaY:growvector(fs.L, f.lineinfo, fs.pc, f.sizelineinfo, nil,
7537 luaY.MAX_INT, "code size overflow")
7538 f.lineinfo[fs.pc] = line
7539 local pc = fs.pc
7540 fs.pc = fs.pc + 1
7541 return pc
7542end
7543
7544------------------------------------------------------------------------
7545-- writes an instruction of type ABC
7546-- * calls luaK:code()
7547------------------------------------------------------------------------
7548function luaK:codeABC(fs, o, a, b, c)
7549 assert(luaP:getOpMode(o) == luaP.OpMode.iABC)
7550 assert(luaP:getBMode(o) ~= luaP.OpArgMask.OpArgN or b == 0)
7551 assert(luaP:getCMode(o) ~= luaP.OpArgMask.OpArgN or c == 0)
7552 return self:code(fs, luaP:CREATE_ABC(o, a, b, c), fs.ls.lastline)
7553end
7554
7555------------------------------------------------------------------------
7556-- writes an instruction of type ABx
7557-- * calls luaK:code(), called by luaK:codeAsBx()
7558------------------------------------------------------------------------
7559function luaK:codeABx(fs, o, a, bc)
7560 assert(luaP:getOpMode(o) == luaP.OpMode.iABx or
7561 luaP:getOpMode(o) == luaP.OpMode.iAsBx)
7562 assert(luaP:getCMode(o) == luaP.OpArgMask.OpArgN)
7563 return self:code(fs, luaP:CREATE_ABx(o, a, bc), fs.ls.lastline)
7564end
7565
7566------------------------------------------------------------------------
7567--
7568-- * used in (lparser) luaY:closelistfield(), luaY:lastlistfield()
7569------------------------------------------------------------------------
7570function luaK:setlist(fs, base, nelems, tostore)
7571 local c = math.floor((nelems - 1)/luaP.LFIELDS_PER_FLUSH) + 1
7572 local b = (tostore == luaY.LUA_MULTRET) and 0 or tostore
7573 assert(tostore ~= 0)
7574 if c <= luaP.MAXARG_C then
7575 self:codeABC(fs, "OP_SETLIST", base, b, c)
7576 else
7577 self:codeABC(fs, "OP_SETLIST", base, b, 0)
7578 self:code(fs, luaP:CREATE_Inst(c), fs.ls.lastline)
7579 end
7580 fs.freereg = base + 1 -- free registers with list values
7581end
7582
7583return function(a) luaY = a return luaK end]]></ProtectedString>
7584 <BinaryString name="Tags"></BinaryString>
7585 </Properties>
7586 </Item>
7587 <Item class="ModuleScript" referent="RBX5721C2C4E9AA4045A3AE5FC31529EF48">
7588 <Properties>
7589 <Content name="LinkedSource"><null></null></Content>
7590 <string name="Name">LuaU</string>
7591 <string name="ScriptGuid">{9C277632-5516-4FB9-AC62-AC3F3BE90D9E}</string>
7592 <ProtectedString name="Source"><![CDATA[--[[--------------------------------------------------------------------
7593
7594 ldump.lua
7595 Save precompiled Lua chunks
7596 This file is part of Yueliang.
7597
7598 Copyright (c) 2006 Kein-Hong Man <khman@users.sf.net>
7599 The COPYRIGHT file describes the conditions
7600 under which this software may be distributed.
7601
7602 See the ChangeLog for more information.
7603
7604----------------------------------------------------------------------]]
7605
7606--[[--------------------------------------------------------------------
7607-- Notes:
7608-- * WARNING! byte order (little endian) and data type sizes for header
7609-- signature values hard-coded; see luaU:header
7610-- * chunk writer generators are included, see below
7611-- * one significant difference is that instructions are still in table
7612-- form (with OP/A/B/C/Bx fields) and luaP:Instruction() is needed to
7613-- convert them into 4-char strings
7614--
7615-- Not implemented:
7616-- * DumpVar, DumpMem has been removed
7617-- * DumpVector folded into folded into DumpDebug, DumpCode
7618--
7619-- Added:
7620-- * for convenience, the following two functions have been added:
7621-- luaU:make_setS: create a chunk writer that writes to a string
7622-- luaU:make_setF: create a chunk writer that writes to a file
7623-- (lua.h contains a typedef for lua_Writer/lua_Chunkwriter, and
7624-- a Lua-based implementation exists, writer() in lstrlib.c)
7625-- * luaU:ttype(o) (from lobject.h)
7626-- * for converting number types to its binary equivalent:
7627-- luaU:from_double(x): encode double value for writing
7628-- luaU:from_int(x): encode integer value for writing
7629-- (error checking is limited for these conversion functions)
7630-- (double conversion does not support denormals or NaNs)
7631--
7632-- Changed in 5.1.x:
7633-- * the dumper was mostly rewritten in Lua 5.1.x, so notes on the
7634-- differences between 5.0.x and 5.1.x is limited
7635-- * LUAC_VERSION bumped to 0x51, LUAC_FORMAT added
7636-- * developer is expected to adjust LUAC_FORMAT in order to identify
7637-- non-standard binary chunk formats
7638-- * header signature code is smaller, has been simplified, and is
7639-- tested as a single unit; its logic is shared with the undumper
7640-- * no more endian conversion, invalid endianness mean rejection
7641-- * opcode field sizes are no longer exposed in the header
7642-- * code moved to front of a prototype, followed by constants
7643-- * debug information moved to the end of the binary chunk, and the
7644-- relevant functions folded into a single function
7645-- * luaU:dump returns a writer status code
7646-- * chunk writer now implements status code because dumper uses it
7647-- * luaU:endianness removed
7648----------------------------------------------------------------------]]
7649
7650--requires luaP
7651local luaU = {}
7652local luaP = require(script.Parent.LuaP)
7653
7654-- mark for precompiled code ('<esc>Lua') (from lua.h)
7655luaU.LUA_SIGNATURE = "\27Lua"
7656
7657-- constants used by dumper (from lua.h)
7658luaU.LUA_TNUMBER = 3
7659luaU.LUA_TSTRING = 4
7660luaU.LUA_TNIL = 0
7661luaU.LUA_TBOOLEAN = 1
7662luaU.LUA_TNONE = -1
7663
7664-- constants for header of binary files (from lundump.h)
7665luaU.LUAC_VERSION = 0x51 -- this is Lua 5.1
7666luaU.LUAC_FORMAT = 0 -- this is the official format
7667luaU.LUAC_HEADERSIZE = 12 -- size of header of binary files
7668
7669--[[--------------------------------------------------------------------
7670-- Additional functions to handle chunk writing
7671-- * to use make_setS and make_setF, see test_ldump.lua elsewhere
7672----------------------------------------------------------------------]]
7673
7674------------------------------------------------------------------------
7675-- create a chunk writer that writes to a string
7676-- * returns the writer function and a table containing the string
7677-- * to get the final result, look in buff.data
7678------------------------------------------------------------------------
7679function luaU:make_setS()
7680 local buff = {}
7681 buff.data = ""
7682 local writer =
7683 function(s, buff) -- chunk writer
7684 if not s then return 0 end
7685 buff.data = buff.data..s
7686 return 0
7687 end
7688 return writer, buff
7689end
7690
7691------------------------------------------------------------------------
7692-- create a chunk writer that writes to a file
7693-- * returns the writer function and a table containing the file handle
7694-- * if a nil is passed, then writer should close the open file
7695------------------------------------------------------------------------
7696
7697--[[
7698function luaU:make_setF(filename)
7699 local buff = {}
7700 buff.h = io.open(filename, "wb")
7701 if not buff.h then return nil end
7702 local writer =
7703 function(s, buff) -- chunk writer
7704 if not buff.h then return 0 end
7705 if not s then
7706 if buff.h:close() then return 0 end
7707 else
7708 if buff.h:write(s) then return 0 end
7709 end
7710 return 1
7711 end
7712 return writer, buff
7713end--]]
7714
7715------------------------------------------------------------------------
7716-- works like the lobject.h version except that TObject used in these
7717-- scripts only has a 'value' field, no 'tt' field (native types used)
7718------------------------------------------------------------------------
7719function luaU:ttype(o)
7720 local tt = type(o.value)
7721 if tt == "number" then return self.LUA_TNUMBER
7722 elseif tt == "string" then return self.LUA_TSTRING
7723 elseif tt == "nil" then return self.LUA_TNIL
7724 elseif tt == "boolean" then return self.LUA_TBOOLEAN
7725 else
7726 return self.LUA_TNONE -- the rest should not appear
7727 end
7728end
7729
7730-----------------------------------------------------------------------
7731-- converts a IEEE754 double number to an 8-byte little-endian string
7732-- * luaU:from_double() and luaU:from_int() are adapted from ChunkBake
7733-- * supports +/- Infinity, but not denormals or NaNs
7734-----------------------------------------------------------------------
7735function luaU:from_double(x)
7736 local function grab_byte(v)
7737 local c = v % 256
7738 return (v - c) / 256, string.char(c)
7739 end
7740 local sign = 0
7741 if x < 0 then sign = 1; x = -x end
7742 local mantissa, exponent = math.frexp(x)
7743 if x == 0 then -- zero
7744 mantissa, exponent = 0, 0
7745 elseif x == 1/0 then
7746 mantissa, exponent = 0, 2047
7747 else
7748 mantissa = (mantissa * 2 - 1) * math.ldexp(0.5, 53)
7749 exponent = exponent + 1022
7750 end
7751 local v, byte = "" -- convert to bytes
7752 x = math.floor(mantissa)
7753 for i = 1,6 do
7754 x, byte = grab_byte(x); v = v..byte -- 47:0
7755 end
7756 x, byte = grab_byte(exponent * 16 + x); v = v..byte -- 55:48
7757 x, byte = grab_byte(sign * 128 + x); v = v..byte -- 63:56
7758 return v
7759end
7760
7761-----------------------------------------------------------------------
7762-- converts a number to a little-endian 32-bit integer string
7763-- * input value assumed to not overflow, can be signed/unsigned
7764-----------------------------------------------------------------------
7765function luaU:from_int(x)
7766 local v = ""
7767 x = math.floor(x)
7768 if x < 0 then x = 4294967296 + x end -- ULONG_MAX+1
7769 for i = 1, 4 do
7770 local c = x % 256
7771 v = v..string.char(c); x = math.floor(x / 256)
7772 end
7773 return v
7774end
7775
7776--[[--------------------------------------------------------------------
7777-- Functions to make a binary chunk
7778-- * many functions have the size parameter removed, since output is
7779-- in the form of a string and some sizes are implicit or hard-coded
7780----------------------------------------------------------------------]]
7781
7782--[[--------------------------------------------------------------------
7783-- struct DumpState:
7784-- L -- lua_State (not used in this script)
7785-- writer -- lua_Writer (chunk writer function)
7786-- data -- void* (chunk writer context or data already written)
7787-- strip -- if true, don't write any debug information
7788-- status -- if non-zero, an error has occured
7789----------------------------------------------------------------------]]
7790
7791------------------------------------------------------------------------
7792-- dumps a block of bytes
7793-- * lua_unlock(D.L), lua_lock(D.L) unused
7794------------------------------------------------------------------------
7795function luaU:DumpBlock(b, D)
7796 if D.status == 0 then
7797 -- lua_unlock(D->L);
7798 D.status = D.write(b, D.data)
7799 -- lua_lock(D->L);
7800 end
7801end
7802
7803------------------------------------------------------------------------
7804-- dumps a char
7805------------------------------------------------------------------------
7806function luaU:DumpChar(y, D)
7807 self:DumpBlock(string.char(y), D)
7808end
7809
7810------------------------------------------------------------------------
7811-- dumps a 32-bit signed or unsigned integer (for int) (hard-coded)
7812------------------------------------------------------------------------
7813function luaU:DumpInt(x, D)
7814 self:DumpBlock(self:from_int(x), D)
7815end
7816
7817------------------------------------------------------------------------
7818-- dumps a lua_Number (hard-coded as a double)
7819------------------------------------------------------------------------
7820function luaU:DumpNumber(x, D)
7821 self:DumpBlock(self:from_double(x), D)
7822end
7823
7824------------------------------------------------------------------------
7825-- dumps a Lua string (size type is hard-coded)
7826------------------------------------------------------------------------
7827function luaU:DumpString(s, D)
7828 if s == nil then
7829 self:DumpInt(0, D)
7830 else
7831 s = s.."\0" -- include trailing '\0'
7832 self:DumpInt(#s, D)
7833 self:DumpBlock(s, D)
7834 end
7835end
7836
7837------------------------------------------------------------------------
7838-- dumps instruction block from function prototype
7839------------------------------------------------------------------------
7840function luaU:DumpCode(f, D)
7841 local n = f.sizecode
7842 --was DumpVector
7843 self:DumpInt(n, D)
7844 for i = 0, n - 1 do
7845 self:DumpBlock(luaP:Instruction(f.code[i]), D)
7846 end
7847end
7848
7849------------------------------------------------------------------------
7850-- dump constant pool from function prototype
7851-- * bvalue(o), nvalue(o) and rawtsvalue(o) macros removed
7852------------------------------------------------------------------------
7853function luaU:DumpConstants(f, D)
7854 local n = f.sizek
7855 self:DumpInt(n, D)
7856 for i = 0, n - 1 do
7857 local o = f.k[i] -- TValue
7858 local tt = self:ttype(o)
7859 self:DumpChar(tt, D)
7860 if tt == self.LUA_TNIL then
7861 elseif tt == self.LUA_TBOOLEAN then
7862 self:DumpChar(o.value and 1 or 0, D)
7863 elseif tt == self.LUA_TNUMBER then
7864 self:DumpNumber(o.value, D)
7865 elseif tt == self.LUA_TSTRING then
7866 self:DumpString(o.value, D)
7867 else
7868 --lua_assert(0) -- cannot happen
7869 end
7870 end
7871 n = f.sizep
7872 self:DumpInt(n, D)
7873 for i = 0, n - 1 do
7874 self:DumpFunction(f.p[i], f.source, D)
7875 end
7876end
7877
7878------------------------------------------------------------------------
7879-- dump debug information
7880------------------------------------------------------------------------
7881function luaU:DumpDebug(f, D)
7882 local n
7883 n = D.strip and 0 or f.sizelineinfo -- dump line information
7884 --was DumpVector
7885 self:DumpInt(n, D)
7886 for i = 0, n - 1 do
7887 self:DumpInt(f.lineinfo[i], D)
7888 end
7889 n = D.strip and 0 or f.sizelocvars -- dump local information
7890 self:DumpInt(n, D)
7891 for i = 0, n - 1 do
7892 self:DumpString(f.locvars[i].varname, D)
7893 self:DumpInt(f.locvars[i].startpc, D)
7894 self:DumpInt(f.locvars[i].endpc, D)
7895 end
7896 n = D.strip and 0 or f.sizeupvalues -- dump upvalue information
7897 self:DumpInt(n, D)
7898 for i = 0, n - 1 do
7899 self:DumpString(f.upvalues[i], D)
7900 end
7901end
7902
7903------------------------------------------------------------------------
7904-- dump child function prototypes from function prototype
7905------------------------------------------------------------------------
7906function luaU:DumpFunction(f, p, D)
7907 local source = f.source
7908 if source == p or D.strip then source = nil end
7909 self:DumpString(source, D)
7910 self:DumpInt(f.lineDefined, D)
7911 self:DumpInt(f.lastlinedefined, D)
7912 self:DumpChar(f.nups, D)
7913 self:DumpChar(f.numparams, D)
7914 self:DumpChar(f.is_vararg, D)
7915 self:DumpChar(f.maxstacksize, D)
7916 self:DumpCode(f, D)
7917 self:DumpConstants(f, D)
7918 self:DumpDebug(f, D)
7919end
7920
7921------------------------------------------------------------------------
7922-- dump Lua header section (some sizes hard-coded)
7923------------------------------------------------------------------------
7924function luaU:DumpHeader(D)
7925 local h = self:header()
7926 assert(#h == self.LUAC_HEADERSIZE) -- fixed buffer now an assert
7927 self:DumpBlock(h, D)
7928end
7929
7930------------------------------------------------------------------------
7931-- make header (from lundump.c)
7932-- returns the header string
7933------------------------------------------------------------------------
7934function luaU:header()
7935 local x = 1
7936 return self.LUA_SIGNATURE..
7937 string.char(
7938 self.LUAC_VERSION,
7939 self.LUAC_FORMAT,
7940 x, -- endianness (1=little)
7941 4, -- sizeof(int)
7942 4, -- sizeof(size_t)
7943 4, -- sizeof(Instruction)
7944 8, -- sizeof(lua_Number)
7945 0) -- is lua_Number integral?
7946end
7947
7948------------------------------------------------------------------------
7949-- dump Lua function as precompiled chunk
7950-- (lua_State* L, const Proto* f, lua_Writer w, void* data, int strip)
7951-- * w, data are created from make_setS, make_setF
7952------------------------------------------------------------------------
7953function luaU:dump(L, f, w, data, strip)
7954 local D = {} -- DumpState
7955 D.L = L
7956 D.write = w
7957 D.data = data
7958 D.strip = strip
7959 D.status = 0
7960 self:DumpHeader(D)
7961 self:DumpFunction(f, nil, D)
7962 -- added: for a chunk writer writing to a file, this final call with
7963 -- nil data is to indicate to the writer to close the file
7964 D.write(nil, D.data)
7965 return D.status
7966end
7967
7968return luaU]]></ProtectedString>
7969 <BinaryString name="Tags"></BinaryString>
7970 </Properties>
7971 </Item>
7972 <Item class="ModuleScript" referent="RBX860D6B646D15448DA00363761F66761F">
7973 <Properties>
7974 <Content name="LinkedSource"><null></null></Content>
7975 <string name="Name">LuaP</string>
7976 <string name="ScriptGuid">{F6F5E123-B39B-4C8A-B113-E644BAE8AA75}</string>
7977 <ProtectedString name="Source"><![CDATA[--[[--------------------------------------------------------------------
7978
7979 lopcodes.lua
7980 Lua 5 virtual machine opcodes in Lua
7981 This file is part of Yueliang.
7982
7983 Copyright (c) 2006 Kein-Hong Man <khman@users.sf.net>
7984 The COPYRIGHT file describes the conditions
7985 under which this software may be distributed.
7986
7987 See the ChangeLog for more information.
7988
7989----------------------------------------------------------------------]]
7990
7991--[[--------------------------------------------------------------------
7992-- Notes:
7993-- * an Instruction is a table with OP, A, B, C, Bx elements; this
7994-- makes the code easy to follow and should allow instruction handling
7995-- to work with doubles and ints
7996-- * WARNING luaP:Instruction outputs instructions encoded in little-
7997-- endian form and field size and positions are hard-coded
7998--
7999-- Not implemented:
8000-- *
8001--
8002-- Added:
8003-- * luaP:CREATE_Inst(c): create an inst from a number (for OP_SETLIST)
8004-- * luaP:Instruction(i): convert field elements to a 4-char string
8005-- * luaP:DecodeInst(x): convert 4-char string into field elements
8006--
8007-- Changed in 5.1.x:
8008-- * POS_OP added, instruction field positions changed
8009-- * some symbol names may have changed, e.g. LUAI_BITSINT
8010-- * new operators for RK indices: BITRK, ISK(x), INDEXK(r), RKASK(x)
8011-- * OP_MOD, OP_LEN is new
8012-- * OP_TEST is now OP_TESTSET, OP_TEST is new
8013-- * OP_FORLOOP, OP_TFORLOOP adjusted, OP_FORPREP is new
8014-- * OP_TFORPREP deleted
8015-- * OP_SETLIST and OP_SETLISTO merged and extended
8016-- * OP_VARARG is new
8017-- * many changes to implementation of OpMode data
8018----------------------------------------------------------------------]]
8019
8020local luaP = {}
8021
8022--[[
8023===========================================================================
8024 We assume that instructions are unsigned numbers.
8025 All instructions have an opcode in the first 6 bits.
8026 Instructions can have the following fields:
8027 'A' : 8 bits
8028 'B' : 9 bits
8029 'C' : 9 bits
8030 'Bx' : 18 bits ('B' and 'C' together)
8031 'sBx' : signed Bx
8032
8033 A signed argument is represented in excess K; that is, the number
8034 value is the unsigned value minus K. K is exactly the maximum value
8035 for that argument (so that -max is represented by 0, and +max is
8036 represented by 2*max), which is half the maximum for the corresponding
8037 unsigned argument.
8038===========================================================================
8039--]]
8040
8041luaP.OpMode = { iABC = 0, iABx = 1, iAsBx = 2 } -- basic instruction format
8042
8043------------------------------------------------------------------------
8044-- size and position of opcode arguments.
8045-- * WARNING size and position is hard-coded elsewhere in this script
8046------------------------------------------------------------------------
8047luaP.SIZE_C = 9
8048luaP.SIZE_B = 9
8049luaP.SIZE_Bx = luaP.SIZE_C + luaP.SIZE_B
8050luaP.SIZE_A = 8
8051
8052luaP.SIZE_OP = 6
8053
8054luaP.POS_OP = 0
8055luaP.POS_A = luaP.POS_OP + luaP.SIZE_OP
8056luaP.POS_C = luaP.POS_A + luaP.SIZE_A
8057luaP.POS_B = luaP.POS_C + luaP.SIZE_C
8058luaP.POS_Bx = luaP.POS_C
8059
8060------------------------------------------------------------------------
8061-- limits for opcode arguments.
8062-- we use (signed) int to manipulate most arguments,
8063-- so they must fit in LUAI_BITSINT-1 bits (-1 for sign)
8064------------------------------------------------------------------------
8065-- removed "#if SIZE_Bx < BITS_INT-1" test, assume this script is
8066-- running on a Lua VM with double or int as LUA_NUMBER
8067
8068luaP.MAXARG_Bx = math.ldexp(1, luaP.SIZE_Bx) - 1
8069luaP.MAXARG_sBx = math.floor(luaP.MAXARG_Bx / 2) -- 'sBx' is signed
8070
8071luaP.MAXARG_A = math.ldexp(1, luaP.SIZE_A) - 1
8072luaP.MAXARG_B = math.ldexp(1, luaP.SIZE_B) - 1
8073luaP.MAXARG_C = math.ldexp(1, luaP.SIZE_C) - 1
8074
8075-- creates a mask with 'n' 1 bits at position 'p'
8076-- MASK1(n,p) deleted, not required
8077-- creates a mask with 'n' 0 bits at position 'p'
8078-- MASK0(n,p) deleted, not required
8079
8080--[[--------------------------------------------------------------------
8081 Visual representation for reference:
8082
8083 31 | | | 0 bit position
8084 +-----+-----+-----+----------+
8085 | B | C | A | Opcode | iABC format
8086 +-----+-----+-----+----------+
8087 - 9 - 9 - 8 - 6 - field sizes
8088 +-----+-----+-----+----------+
8089 | [s]Bx | A | Opcode | iABx | iAsBx format
8090 +-----+-----+-----+----------+
8091
8092----------------------------------------------------------------------]]
8093
8094------------------------------------------------------------------------
8095-- the following macros help to manipulate instructions
8096-- * changed to a table object representation, very clean compared to
8097-- the [nightmare] alternatives of using a number or a string
8098-- * Bx is a separate element from B and C, since there is never a need
8099-- to split Bx in the parser or code generator
8100------------------------------------------------------------------------
8101
8102-- these accept or return opcodes in the form of string names
8103function luaP:GET_OPCODE(i) return self.ROpCode[i.OP] end
8104function luaP:SET_OPCODE(i, o) i.OP = self.OpCode[o] end
8105
8106function luaP:GETARG_A(i) return i.A end
8107function luaP:SETARG_A(i, u) i.A = u end
8108
8109function luaP:GETARG_B(i) return i.B end
8110function luaP:SETARG_B(i, b) i.B = b end
8111
8112function luaP:GETARG_C(i) return i.C end
8113function luaP:SETARG_C(i, b) i.C = b end
8114
8115function luaP:GETARG_Bx(i) return i.Bx end
8116function luaP:SETARG_Bx(i, b) i.Bx = b end
8117
8118function luaP:GETARG_sBx(i) return i.Bx - self.MAXARG_sBx end
8119function luaP:SETARG_sBx(i, b) i.Bx = b + self.MAXARG_sBx end
8120
8121function luaP:CREATE_ABC(o,a,b,c)
8122 return {OP = self.OpCode[o], A = a, B = b, C = c}
8123end
8124
8125function luaP:CREATE_ABx(o,a,bc)
8126 return {OP = self.OpCode[o], A = a, Bx = bc}
8127end
8128
8129------------------------------------------------------------------------
8130-- create an instruction from a number (for OP_SETLIST)
8131------------------------------------------------------------------------
8132function luaP:CREATE_Inst(c)
8133 local o = c % 64
8134 c = (c - o) / 64
8135 local a = c % 256
8136 c = (c - a) / 256
8137 return self:CREATE_ABx(o, a, c)
8138end
8139
8140------------------------------------------------------------------------
8141-- returns a 4-char string little-endian encoded form of an instruction
8142------------------------------------------------------------------------
8143function luaP:Instruction(i)
8144 if i.Bx then
8145 -- change to OP/A/B/C format
8146 i.C = i.Bx % 512
8147 i.B = (i.Bx - i.C) / 512
8148 end
8149 local I = i.A * 64 + i.OP
8150 local c0 = I % 256
8151 I = i.C * 64 + (I - c0) / 256 -- 6 bits of A left
8152 local c1 = I % 256
8153 I = i.B * 128 + (I - c1) / 256 -- 7 bits of C left
8154 local c2 = I % 256
8155 local c3 = (I - c2) / 256
8156 return string.char(c0, c1, c2, c3)
8157end
8158
8159------------------------------------------------------------------------
8160-- decodes a 4-char little-endian string into an instruction struct
8161------------------------------------------------------------------------
8162function luaP:DecodeInst(x)
8163 local byte = string.byte
8164 local i = {}
8165 local I = byte(x, 1)
8166 local op = I % 64
8167 i.OP = op
8168 I = byte(x, 2) * 4 + (I - op) / 64 -- 2 bits of c0 left
8169 local a = I % 256
8170 i.A = a
8171 I = byte(x, 3) * 4 + (I - a) / 256 -- 2 bits of c1 left
8172 local c = I % 512
8173 i.C = c
8174 i.B = byte(x, 4) * 2 + (I - c) / 512 -- 1 bits of c2 left
8175 local opmode = self.OpMode[tonumber(string.sub(self.opmodes[op + 1], 7, 7))]
8176 if opmode ~= "iABC" then
8177 i.Bx = i.B * 512 + i.C
8178 end
8179 return i
8180end
8181
8182------------------------------------------------------------------------
8183-- Macros to operate RK indices
8184-- * these use arithmetic instead of bit ops
8185------------------------------------------------------------------------
8186
8187-- this bit 1 means constant (0 means register)
8188luaP.BITRK = math.ldexp(1, luaP.SIZE_B - 1)
8189
8190-- test whether value is a constant
8191function luaP:ISK(x) return x >= self.BITRK end
8192
8193-- gets the index of the constant
8194function luaP:INDEXK(x) return x - self.BITRK end
8195
8196luaP.MAXINDEXRK = luaP.BITRK - 1
8197
8198-- code a constant index as a RK value
8199function luaP:RKASK(x) return x + self.BITRK end
8200
8201------------------------------------------------------------------------
8202-- invalid register that fits in 8 bits
8203------------------------------------------------------------------------
8204luaP.NO_REG = luaP.MAXARG_A
8205
8206------------------------------------------------------------------------
8207-- R(x) - register
8208-- Kst(x) - constant (in constant table)
8209-- RK(x) == if ISK(x) then Kst(INDEXK(x)) else R(x)
8210------------------------------------------------------------------------
8211
8212------------------------------------------------------------------------
8213-- grep "ORDER OP" if you change these enums
8214------------------------------------------------------------------------
8215
8216--[[--------------------------------------------------------------------
8217Lua virtual machine opcodes (enum OpCode):
8218------------------------------------------------------------------------
8219name args description
8220------------------------------------------------------------------------
8221OP_MOVE A B R(A) := R(B)
8222OP_LOADK A Bx R(A) := Kst(Bx)
8223OP_LOADBOOL A B C R(A) := (Bool)B; if (C) pc++
8224OP_LOADNIL A B R(A) := ... := R(B) := nil
8225OP_GETUPVAL A B R(A) := UpValue[B]
8226OP_GETGLOBAL A Bx R(A) := Gbl[Kst(Bx)]
8227OP_GETTABLE A B C R(A) := R(B)[RK(C)]
8228OP_SETGLOBAL A Bx Gbl[Kst(Bx)] := R(A)
8229OP_SETUPVAL A B UpValue[B] := R(A)
8230OP_SETTABLE A B C R(A)[RK(B)] := RK(C)
8231OP_NEWTABLE A B C R(A) := {} (size = B,C)
8232OP_SELF A B C R(A+1) := R(B); R(A) := R(B)[RK(C)]
8233OP_ADD A B C R(A) := RK(B) + RK(C)
8234OP_SUB A B C R(A) := RK(B) - RK(C)
8235OP_MUL A B C R(A) := RK(B) * RK(C)
8236OP_DIV A B C R(A) := RK(B) / RK(C)
8237OP_MOD A B C R(A) := RK(B) % RK(C)
8238OP_POW A B C R(A) := RK(B) ^ RK(C)
8239OP_UNM A B R(A) := -R(B)
8240OP_NOT A B R(A) := not R(B)
8241OP_LEN A B R(A) := length of R(B)
8242OP_CONCAT A B C R(A) := R(B).. ... ..R(C)
8243OP_JMP sBx pc+=sBx
8244OP_EQ A B C if ((RK(B) == RK(C)) ~= A) then pc++
8245OP_LT A B C if ((RK(B) < RK(C)) ~= A) then pc++
8246OP_LE A B C if ((RK(B) <= RK(C)) ~= A) then pc++
8247OP_TEST A C if not (R(A) <=> C) then pc++
8248OP_TESTSET A B C if (R(B) <=> C) then R(A) := R(B) else pc++
8249OP_CALL A B C R(A), ... ,R(A+C-2) := R(A)(R(A+1), ... ,R(A+B-1))
8250OP_TAILCALL A B C return R(A)(R(A+1), ... ,R(A+B-1))
8251OP_RETURN A B return R(A), ... ,R(A+B-2) (see note)
8252OP_FORLOOP A sBx R(A)+=R(A+2);
8253 if R(A) <?= R(A+1) then { pc+=sBx; R(A+3)=R(A) }
8254OP_FORPREP A sBx R(A)-=R(A+2); pc+=sBx
8255OP_TFORLOOP A C R(A+3), ... ,R(A+2+C) := R(A)(R(A+1), R(A+2));
8256 if R(A+3) ~= nil then R(A+2)=R(A+3) else pc++
8257OP_SETLIST A B C R(A)[(C-1)*FPF+i] := R(A+i), 1 <= i <= B
8258OP_CLOSE A close all variables in the stack up to (>=) R(A)
8259OP_CLOSURE A Bx R(A) := closure(KPROTO[Bx], R(A), ... ,R(A+n))
8260OP_VARARG A B R(A), R(A+1), ..., R(A+B-1) = vararg
8261----------------------------------------------------------------------]]
8262
8263luaP.opnames = {} -- opcode names
8264luaP.OpCode = {} -- lookup name -> number
8265luaP.ROpCode = {} -- lookup number -> name
8266
8267------------------------------------------------------------------------
8268-- ORDER OP
8269------------------------------------------------------------------------
8270local i = 0
8271for v in string.gmatch([[
8272MOVE LOADK LOADBOOL LOADNIL GETUPVAL
8273GETGLOBAL GETTABLE SETGLOBAL SETUPVAL SETTABLE
8274NEWTABLE SELF ADD SUB MUL
8275DIV MOD POW UNM NOT
8276LEN CONCAT JMP EQ LT
8277LE TEST TESTSET CALL TAILCALL
8278RETURN FORLOOP FORPREP TFORLOOP SETLIST
8279CLOSE CLOSURE VARARG
8280]], "%S+") do
8281 local n = "OP_"..v
8282 luaP.opnames[i] = v
8283 luaP.OpCode[n] = i
8284 luaP.ROpCode[i] = n
8285 i = i + 1
8286end
8287luaP.NUM_OPCODES = i
8288
8289--[[
8290===========================================================================
8291 Notes:
8292 (*) In OP_CALL, if (B == 0) then B = top. C is the number of returns - 1,
8293 and can be 0: OP_CALL then sets 'top' to last_result+1, so
8294 next open instruction (OP_CALL, OP_RETURN, OP_SETLIST) may use 'top'.
8295 (*) In OP_VARARG, if (B == 0) then use actual number of varargs and
8296 set top (like in OP_CALL with C == 0).
8297 (*) In OP_RETURN, if (B == 0) then return up to 'top'
8298 (*) In OP_SETLIST, if (B == 0) then B = 'top';
8299 if (C == 0) then next 'instruction' is real C
8300 (*) For comparisons, A specifies what condition the test should accept
8301 (true or false).
8302 (*) All 'skips' (pc++) assume that next instruction is a jump
8303===========================================================================
8304--]]
8305
8306--[[--------------------------------------------------------------------
8307 masks for instruction properties. The format is:
8308 bits 0-1: op mode
8309 bits 2-3: C arg mode
8310 bits 4-5: B arg mode
8311 bit 6: instruction set register A
8312 bit 7: operator is a test
8313
8314 for OpArgMask:
8315 OpArgN - argument is not used
8316 OpArgU - argument is used
8317 OpArgR - argument is a register or a jump offset
8318 OpArgK - argument is a constant or register/constant
8319----------------------------------------------------------------------]]
8320
8321-- was enum OpArgMask
8322luaP.OpArgMask = { OpArgN = 0, OpArgU = 1, OpArgR = 2, OpArgK = 3 }
8323
8324------------------------------------------------------------------------
8325-- e.g. to compare with symbols, luaP:getOpMode(...) == luaP.OpCode.iABC
8326-- * accepts opcode parameter as strings, e.g. "OP_MOVE"
8327------------------------------------------------------------------------
8328
8329function luaP:getOpMode(m)
8330 return self.opmodes[self.OpCode[m]] % 4
8331end
8332
8333function luaP:getBMode(m)
8334 return math.floor(self.opmodes[self.OpCode[m]] / 16) % 4
8335end
8336
8337function luaP:getCMode(m)
8338 return math.floor(self.opmodes[self.OpCode[m]] / 4) % 4
8339end
8340
8341function luaP:testAMode(m)
8342 return math.floor(self.opmodes[self.OpCode[m]] / 64) % 2
8343end
8344
8345function luaP:testTMode(m)
8346 return math.floor(self.opmodes[self.OpCode[m]] / 128)
8347end
8348
8349-- luaP_opnames[] is set above, as the luaP.opnames table
8350
8351-- number of list items to accumulate before a SETLIST instruction
8352luaP.LFIELDS_PER_FLUSH = 50
8353
8354------------------------------------------------------------------------
8355-- build instruction properties array
8356-- * deliberately coded to look like the C equivalent
8357------------------------------------------------------------------------
8358local function opmode(t, a, b, c, m)
8359 local luaP = luaP
8360 return t * 128 + a * 64 +
8361 luaP.OpArgMask[b] * 16 + luaP.OpArgMask[c] * 4 + luaP.OpMode[m]
8362end
8363
8364-- ORDER OP
8365luaP.opmodes = {
8366-- T A B C mode opcode
8367 opmode(0, 1, "OpArgK", "OpArgN", "iABx"), -- OP_LOADK
8368 opmode(0, 1, "OpArgU", "OpArgU", "iABC"), -- OP_LOADBOOL
8369 opmode(0, 1, "OpArgR", "OpArgN", "iABC"), -- OP_LOADNIL
8370 opmode(0, 1, "OpArgU", "OpArgN", "iABC"), -- OP_GETUPVAL
8371 opmode(0, 1, "OpArgK", "OpArgN", "iABx"), -- OP_GETGLOBAL
8372 opmode(0, 1, "OpArgR", "OpArgK", "iABC"), -- OP_GETTABLE
8373 opmode(0, 0, "OpArgK", "OpArgN", "iABx"), -- OP_SETGLOBAL
8374 opmode(0, 0, "OpArgU", "OpArgN", "iABC"), -- OP_SETUPVAL
8375 opmode(0, 0, "OpArgK", "OpArgK", "iABC"), -- OP_SETTABLE
8376 opmode(0, 1, "OpArgU", "OpArgU", "iABC"), -- OP_NEWTABLE
8377 opmode(0, 1, "OpArgR", "OpArgK", "iABC"), -- OP_SELF
8378 opmode(0, 1, "OpArgK", "OpArgK", "iABC"), -- OP_ADD
8379 opmode(0, 1, "OpArgK", "OpArgK", "iABC"), -- OP_SUB
8380 opmode(0, 1, "OpArgK", "OpArgK", "iABC"), -- OP_MUL
8381 opmode(0, 1, "OpArgK", "OpArgK", "iABC"), -- OP_DIV
8382 opmode(0, 1, "OpArgK", "OpArgK", "iABC"), -- OP_MOD
8383 opmode(0, 1, "OpArgK", "OpArgK", "iABC"), -- OP_POW
8384 opmode(0, 1, "OpArgR", "OpArgN", "iABC"), -- OP_UNM
8385 opmode(0, 1, "OpArgR", "OpArgN", "iABC"), -- OP_NOT
8386 opmode(0, 1, "OpArgR", "OpArgN", "iABC"), -- OP_LEN
8387 opmode(0, 1, "OpArgR", "OpArgR", "iABC"), -- OP_CONCAT
8388 opmode(0, 0, "OpArgR", "OpArgN", "iAsBx"), -- OP_JMP
8389 opmode(1, 0, "OpArgK", "OpArgK", "iABC"), -- OP_EQ
8390 opmode(1, 0, "OpArgK", "OpArgK", "iABC"), -- OP_LT
8391 opmode(1, 0, "OpArgK", "OpArgK", "iABC"), -- OP_LE
8392 opmode(1, 1, "OpArgR", "OpArgU", "iABC"), -- OP_TEST
8393 opmode(1, 1, "OpArgR", "OpArgU", "iABC"), -- OP_TESTSET
8394 opmode(0, 1, "OpArgU", "OpArgU", "iABC"), -- OP_CALL
8395 opmode(0, 1, "OpArgU", "OpArgU", "iABC"), -- OP_TAILCALL
8396 opmode(0, 0, "OpArgU", "OpArgN", "iABC"), -- OP_RETURN
8397 opmode(0, 1, "OpArgR", "OpArgN", "iAsBx"), -- OP_FORLOOP
8398 opmode(0, 1, "OpArgR", "OpArgN", "iAsBx"), -- OP_FORPREP
8399 opmode(1, 0, "OpArgN", "OpArgU", "iABC"), -- OP_TFORLOOP
8400 opmode(0, 0, "OpArgU", "OpArgU", "iABC"), -- OP_SETLIST
8401 opmode(0, 0, "OpArgN", "OpArgN", "iABC"), -- OP_CLOSE
8402 opmode(0, 1, "OpArgU", "OpArgN", "iABx"), -- OP_CLOSURE
8403 opmode(0, 1, "OpArgU", "OpArgN", "iABC"), -- OP_VARARG
8404}
8405-- an awkward way to set a zero-indexed table...
8406luaP.opmodes[0] =
8407 opmode(0, 1, "OpArgR", "OpArgN", "iABC") -- OP_MOVE
8408
8409return luaP]]></ProtectedString>
8410 <BinaryString name="Tags"></BinaryString>
8411 </Properties>
8412 </Item>
8413 <Item class="ModuleScript" referent="RBX8B078DFEAA7F44208ECF5696D66AEBB3">
8414 <Properties>
8415 <Content name="LinkedSource"><null></null></Content>
8416 <string name="Name">Rerubi</string>
8417 <string name="ScriptGuid">{8CDD4A3E-999B-4E2B-ACC4-C871AF954901}</string>
8418 <ProtectedString name="Source"><![CDATA[local Select = select;
8419local Byte = string.byte;
8420local Sub = string.sub;
8421
8422local Opmode = {
8423 {b = 'OpArgR', c='OpArgN'}, {b = 'OpArgK', c='OpArgN'}, {b = 'OpArgU', c='OpArgU'},
8424 {b = 'OpArgR', c='OpArgN'}, {b = 'OpArgU', c='OpArgN'}, {b = 'OpArgK', c='OpArgN'},
8425 {b = 'OpArgR', c='OpArgK'}, {b = 'OpArgK', c='OpArgN'}, {b = 'OpArgU', c='OpArgN'},
8426 {b = 'OpArgK', c='OpArgK'}, {b = 'OpArgU', c='OpArgU'}, {b = 'OpArgR', c='OpArgK'},
8427 {b = 'OpArgK', c='OpArgK'}, {b = 'OpArgK', c='OpArgK'}, {b = 'OpArgK', c='OpArgK'},
8428 {b = 'OpArgK', c='OpArgK'}, {b = 'OpArgK', c='OpArgK'}, {b = 'OpArgK', c='OpArgK'},
8429 {b = 'OpArgR', c='OpArgN'}, {b = 'OpArgR', c='OpArgN'}, {b = 'OpArgR', c='OpArgN'},
8430 {b = 'OpArgR', c='OpArgR'}, {b = 'OpArgR', c='OpArgN'}, {b = 'OpArgK', c='OpArgK'},
8431 {b = 'OpArgK', c='OpArgK'}, {b = 'OpArgK', c='OpArgK'}, {b = 'OpArgR', c='OpArgU'},
8432 {b = 'OpArgR', c='OpArgU'}, {b = 'OpArgU', c='OpArgU'}, {b = 'OpArgU', c='OpArgU'},
8433 {b = 'OpArgU', c='OpArgN'}, {b = 'OpArgR', c='OpArgN'}, {b = 'OpArgR', c='OpArgN'},
8434 {b = 'OpArgN', c='OpArgU'}, {b = 'OpArgU', c='OpArgU'}, {b = 'OpArgN', c='OpArgN'},
8435 {b = 'OpArgU', c='OpArgN'}, {b = 'OpArgU', c='OpArgN'}
8436};
8437
8438local Opcode = { -- Opcode types.
8439 'ABC', 'ABx', 'ABC', 'ABC';
8440 'ABC', 'ABx', 'ABC', 'ABx';
8441 'ABC', 'ABC', 'ABC', 'ABC';
8442 'ABC', 'ABC', 'ABC', 'ABC';
8443 'ABC', 'ABC', 'ABC', 'ABC';
8444 'ABC', 'ABC', 'AsBx', 'ABC';
8445 'ABC', 'ABC', 'ABC', 'ABC';
8446 'ABC', 'ABC', 'ABC', 'AsBx';
8447 'AsBx', 'ABC', 'ABC', 'ABC';
8448 'ABx', 'ABC';
8449};
8450
8451-- rlbi author -> Rerumu
8452-- special thanks;
8453-- @cntkillme for providing faster bit extraction
8454-- @Eternal for being #1 bug finder and providing better float decoder
8455-- @stravant for contributing to the original project this is derived from
8456
8457-- rerubi is an upgrade to the original Lua VM in Lua
8458-- the prime goal of rerubi is to be the fastest:tm: alternative
8459-- to a Lua in Lua bytecode execution
8460
8461local function gBit(Bit, Start, End) -- No tail-calls, yay.
8462 if End then -- Thanks to cntkillme for giving input on this shorter, better approach.
8463 local Res = (Bit / 2 ^ (Start - 1)) % 2 ^ ((End - 1) - (Start - 1) + 1);
8464
8465 return Res - Res % 1;
8466 else
8467 local Plc = 2 ^ (Start - 1);
8468
8469 if (Bit % (Plc + Plc) >= Plc) then
8470 return 1;
8471 else
8472 return 0;
8473 end;
8474 end;
8475end;
8476
8477local function GetMeaning(ByteString)
8478 local Pos = 1;
8479 local gSizet;
8480 local gInt;
8481
8482 local function gBits8() -- Get the next byte in the stream.
8483 local F = Byte(ByteString, Pos, Pos);
8484
8485 Pos = Pos + 1;
8486
8487 return F;
8488 end;
8489
8490 local function gBits32()
8491 local W, X, Y, Z = Byte(ByteString, Pos, Pos + 3);
8492
8493 Pos = Pos + 4;
8494
8495 return (Z * 16777216) + (Y * 65536) + (X * 256) + W;
8496 end;
8497
8498 local function gBits64()
8499 return gBits32() * 4294967296 + gBits32();
8500 end;
8501
8502 local function gFloat()
8503 -- thanks @Eternal for giving me this so I could mangle it in here and have it work
8504 local Left = gBits32();
8505 local Right = gBits32();
8506 local IsNormal = 1
8507 local Mantissa = (gBit(Right, 1, 20) * (2 ^ 32))
8508 + Left;
8509
8510 local Exponent = gBit(Right, 21, 31);
8511 local Sign = ((-1) ^ gBit(Right, 32));
8512
8513 if (Exponent == 0) then
8514 if (Mantissa == 0) then
8515 return Sign * 0 -- +-0
8516 else
8517 Exponent = 1
8518 IsNormal = 0
8519 end
8520 elseif (Exponent == 2047) then
8521 if (Mantissa == 0) then
8522 return Sign * (1 / 0) -- +-Inf
8523 else
8524 return Sign * (0 / 0) -- +-Q/Nan
8525 end
8526 end
8527
8528 -- sign * 2**e-1023 * isNormal.mantissa
8529 return math.ldexp(Sign, Exponent - 1023) * (IsNormal + (Mantissa / (2 ^ 52)))
8530 end;
8531
8532 local function gString(Len)
8533 local Str;
8534
8535 if Len then
8536 Str = Sub(ByteString, Pos, Pos + Len - 1);
8537
8538 Pos = Pos + Len;
8539 else
8540 Len = gSizet();
8541
8542 if (Len == 0) then return; end;
8543
8544 Str = Sub(ByteString, Pos, Pos + Len - 1);
8545
8546 Pos = Pos + Len;
8547 end;
8548
8549 return Str;
8550 end;
8551
8552 local function ChunkDecode()
8553 local Instr = {};
8554 local Const = {};
8555 local Proto = {};
8556 local Chunk = {
8557 Instr = Instr; -- Instructions
8558 Const = Const; -- Constants
8559 Proto = Proto; -- Prototypes
8560 Lines = {}; -- Lines
8561 Name = gString(); -- Grab name string.
8562 FirstL = gInt(); -- First line.
8563 LastL = gInt(); -- Last line.
8564 Upvals = gBits8(); -- Upvalue count.
8565 Args = gBits8(); -- Arg count.
8566 Vargs = gBits8(); -- Vararg type.
8567 Stack = gBits8(); -- Stack.
8568 };
8569 local ConstantReferences = {}; -- for an optimization
8570
8571 if Chunk.Name then
8572 Chunk.Name = Sub(Chunk.Name, 1, -2);
8573 end;
8574
8575
8576 for Idx = 1, gInt() do -- Loading instructions to the chunk.
8577 local Data = gBits32();
8578 local Opco = gBit(Data, 1, 6);
8579 local Type = Opcode[Opco + 1];
8580 local Mode = Opmode[Opco + 1];
8581
8582 local Inst = {
8583 Enum = Opco;
8584 Value = Data;
8585 gBit(Data, 7, 14); -- Register A.
8586 };
8587
8588 if (Type == 'ABC') then -- Most common, basic instruction type.
8589 Inst[2] = gBit(Data, 24, 32);
8590 Inst[3] = gBit(Data, 15, 23);
8591 elseif (Type == 'ABx') then
8592 Inst[2] = gBit(Data, 15, 32);
8593 elseif (Type == 'AsBx') then
8594 Inst[2] = gBit(Data, 15, 32) - 131071;
8595 end;
8596
8597 -- Precompute data for some instructions
8598 do
8599 -- TEST and TESTSET
8600 if Opco == 26 or Opco == 27 then
8601 Inst[3] = Inst[3] == 0;
8602 end
8603
8604 -- EQ, LT, LE
8605 if Opco >= 23 and Opco <= 25 then
8606 Inst[1] = Inst[1] ~= 0;
8607 end
8608
8609 -- Anything that looks at a constant using B
8610 if Mode.b == 'OpArgK' then
8611 Inst[3] = Inst[3] or false; -- Simply to guarantee that Inst[4] is inserted in the array part
8612 if Inst[2] >= 256 then
8613 local Cons = Inst[2] - 256;
8614 Inst[4] = Cons;
8615
8616 local ReferenceData = ConstantReferences[Cons];
8617 if not ReferenceData then
8618 ReferenceData = {};
8619 ConstantReferences[Cons] = ReferenceData;
8620 end
8621
8622 ReferenceData[#ReferenceData + 1] = {Inst = Inst, Register = 4}
8623 end
8624 end
8625
8626 -- Anything that looks at a constant using C
8627 if Mode.c == 'OpArgK' then
8628 Inst[4] = Inst[4] or false -- Simply to guarantee that Inst[5] is inserted in the array part
8629 if Inst[3] >= 256 then
8630 local Cons = Inst[3] - 256;
8631 Inst[5] = Cons;
8632
8633 local ReferenceData = ConstantReferences[Cons];
8634 if not ReferenceData then
8635 ReferenceData = {};
8636 ConstantReferences[Cons] = ReferenceData;
8637 end
8638
8639 ReferenceData[#ReferenceData + 1] = {Inst = Inst, Register = 5}
8640 end
8641 end
8642 end
8643
8644 Instr[Idx] = Inst;
8645 end;
8646
8647 for Idx = 1, gInt() do -- Load constants.
8648 local Type = gBits8();
8649 local Cons;
8650
8651 if (Type == 1) then -- Boolean
8652 Cons = (gBits8() ~= 0);
8653 elseif (Type == 3) then -- Float/Double
8654 Cons = gFloat();
8655 elseif (Type == 4) then
8656 Cons = Sub(gString(), 1, -2);
8657 end;
8658
8659 -- Finish precomputing constants
8660 local Refs = ConstantReferences[Idx - 1];
8661 if Refs then
8662 for i = 1, #Refs do
8663 Refs[i].Inst[Refs[i].Register] = Cons
8664 end
8665 end
8666
8667 -- Write Constant to pool
8668 Const[Idx - 1] = Cons;
8669 end;
8670
8671 for Idx = 1, gInt() do -- Nested function prototypes.
8672 Proto[Idx - 1] = ChunkDecode();
8673 end;
8674
8675 do -- Debugging
8676 local Lines = Chunk.Lines;
8677
8678 for Idx = 1, gInt() do
8679 Lines[Idx] = gBits32();
8680 end;
8681
8682 for _ = 1, gInt() do -- Locals in stack.
8683 gString(); -- Name of local.
8684 gBits32(); -- Starting point.
8685 gBits32(); -- End point.
8686 end;
8687
8688 for _ = 1, gInt() do -- Upvalues.
8689 gString(); -- Name of upvalue.
8690 end;
8691 end;
8692
8693 return Chunk; -- Finished chunk.
8694 end;
8695
8696 do -- Most of this chunk I was too lazy to reformat or change
8697 assert(gString(4) == "\27Lua", "Lua bytecode expected.");
8698 assert(gBits8() == 0x51, "Only Lua 5.1 is supported.");
8699
8700 gBits8(); -- Probably version control.
8701 gBits8(); -- Is small endians.
8702
8703 local IntSize = gBits8(); -- Int size
8704 local Sizet = gBits8(); -- size_t
8705
8706 if (IntSize == 4) then
8707 gInt = gBits32;
8708 elseif (IntSize == 8) then
8709 gInt = gBits64;
8710 else
8711 error('Integer size not supported', 2);
8712 end;
8713
8714 if (Sizet == 4) then
8715 gSizet = gBits32;
8716 elseif (Sizet == 8) then
8717 gSizet = gBits64;
8718 else
8719 error('Sizet size not supported', 2);
8720 end;
8721
8722 assert(gString(3) == "\4\8\0", "Unsupported bytecode target platform");
8723 end;
8724
8725 return ChunkDecode();
8726end;
8727
8728local function _Returns(...)
8729 return Select('#', ...), {...};
8730end;
8731
8732local function Wrap(Chunk, Env, Upvalues)
8733 local Instr = Chunk.Instr;
8734 local Const = Chunk.Const;
8735 local Proto = Chunk.Proto;
8736
8737 local function OnError(Err, Position) -- Handle your errors in whatever way.
8738 local Name = Chunk.Name or 'Code';
8739 local Line = Chunk.Lines[Position] or '?';
8740
8741 error(string.format('%s:%s: %s', Name, Line, tostring(Err)), 0);
8742 end;
8743
8744 return function(...)
8745 -- Returned function to run bytecode chunk (Don't be stupid, you can't setfenv this to work your way).
8746 local InstrPoint, Top = 1, -1;
8747 local Vararg, Varargsz = {}, Select('#', ...) - 1;
8748
8749 local GStack = {};
8750 local Lupvals = {};
8751 local Stack = setmetatable({}, {
8752 __index = GStack;
8753 __newindex = function(_, Key, Value)
8754 if (Key > Top) then
8755 Top = Key;
8756 end;
8757
8758 GStack[Key] = Value;
8759 end;
8760 });
8761
8762 local function Loop()
8763 local Inst, Enum;
8764
8765 while true do
8766 Inst = Instr[InstrPoint];
8767 Enum = Inst.Enum;
8768 InstrPoint = InstrPoint + 1;
8769
8770 if (Enum == 0) then -- MOVE
8771 Stack[Inst[1]] = Stack[Inst[2]];
8772 elseif (Enum == 1) then -- LOADK
8773 Stack[Inst[1]] = Const[Inst[2]];
8774 elseif (Enum == 2) then -- LOADBOOL
8775 Stack[Inst[1]] = (Inst[2] ~= 0);
8776
8777 if (Inst[3] ~= 0) then
8778 InstrPoint = InstrPoint + 1;
8779 end;
8780 elseif (Enum == 3) then -- LOADNIL
8781 local Stk = Stack;
8782
8783 for Idx = Inst[1], Inst[2] do
8784 Stk[Idx] = nil;
8785 end;
8786 elseif (Enum == 4) then -- GETUPVAL
8787 Stack[Inst[1]] = Upvalues[Inst[2]];
8788 elseif (Enum == 5) then -- GETGLOBAL
8789 Stack[Inst[1]] = Env[Const[Inst[2]]];
8790 elseif (Enum == 6) then -- GETTABLE
8791 local Stk = Stack;
8792 Stk[Inst[1]] = Stk[Inst[2]][Inst[5] or Stk[Inst[3]]];
8793 elseif (Enum == 7) then -- SETGLOBAL
8794 Env[Const[Inst[2]]] = Stack[Inst[1]];
8795 elseif (Enum == 8) then -- SETUPVAL
8796 Upvalues[Inst[2]] = Stack[Inst[1]];
8797 elseif (Enum == 9) then -- SETTABLE
8798 local Stk = Stack
8799 Stk[Inst[1]][Inst[4] or Stk[Inst[2]]] = Inst[5] or Stk[Inst[3]]
8800 elseif (Enum == 10) then -- NEWTABLE
8801 Stack[Inst[1]] = {};
8802 elseif (Enum == 11) then -- SELF
8803 local Stk = Stack;
8804 local A = Inst[1];
8805 local B = Stk[Inst[2]];
8806 local C = Inst[5] or Stk[Inst[3]];
8807 Stk[A + 1] = B;
8808 Stk[A] = B[C];
8809 elseif (Enum == 12) then -- ADD
8810 local Stk = Stack;
8811 Stk[Inst[1]] = (Inst[4] or Stk[Inst[2]]) + (Inst[5] or Stk[Inst[3]]);
8812 elseif (Enum == 13) then -- SUB
8813 local Stk = Stack;
8814 Stk[Inst[1]] = (Inst[4] or Stk[Inst[2]]) - (Inst[5] or Stk[Inst[3]]);
8815 elseif (Enum == 14) then -- MUL
8816 local Stk = Stack;
8817 Stk[Inst[1]] = (Inst[4] or Stk[Inst[2]]) * (Inst[5] or Stk[Inst[3]]);
8818 elseif (Enum == 15) then -- DIV
8819 local Stk = Stack;
8820 Stk[Inst[1]] = (Inst[4] or Stk[Inst[2]]) / (Inst[5] or Stk[Inst[3]]);
8821 elseif (Enum == 16) then -- MOD
8822 local Stk = Stack;
8823 Stk[Inst[1]] = (Inst[4] or Stk[Inst[2]]) % (Inst[5] or Stk[Inst[3]]);
8824 elseif (Enum == 17) then -- POW
8825 local Stk = Stack;
8826 Stk[Inst[1]] = (Inst[4] or Stk[Inst[2]]) ^ (Inst[5] or Stk[Inst[3]]);
8827 elseif (Enum == 18) then -- UNM
8828 Stack[Inst[1]] = -Stack[Inst[2]];
8829 elseif (Enum == 19) then -- NOT
8830 Stack[Inst[1]] = (not Stack[Inst[2]]);
8831 elseif (Enum == 20) then -- LEN
8832 Stack[Inst[1]] = #Stack[Inst[2]];
8833 elseif (Enum == 21) then -- CONCAT
8834 local Stk = Stack;
8835 local B = Inst[2];
8836 local K = Stk[B];
8837
8838 for Idx = B + 1, Inst[3] do
8839 K = K .. Stk[Idx];
8840 end;
8841
8842 Stack[Inst[1]] = K;
8843 elseif (Enum == 22) then -- JMP
8844 InstrPoint = InstrPoint + Inst[2];
8845 elseif (Enum == 23) then -- EQ
8846 local Stk = Stack;
8847 local B = Inst[4] or Stk[Inst[2]];
8848 local C = Inst[5] or Stk[Inst[3]];
8849
8850 if (B == C) ~= Inst[1] then
8851 InstrPoint = InstrPoint + 1;
8852 end;
8853 elseif (Enum == 24) then -- LT
8854 local Stk = Stack;
8855 local B = Inst[4] or Stk[Inst[2]];
8856 local C = Inst[5] or Stk[Inst[3]];
8857
8858 if (B < C) ~= Inst[1] then
8859 InstrPoint = InstrPoint + 1;
8860 end;
8861 elseif (Enum == 25) then -- LE
8862 local Stk = Stack;
8863 local B = Inst[4] or Stk[Inst[2]];
8864 local C = Inst[5] or Stk[Inst[3]];
8865
8866 if (B <= C) ~= Inst[1] then
8867 InstrPoint = InstrPoint + 1;
8868 end;
8869 elseif (Enum == 26) then -- TEST
8870 if Inst[3] then
8871 if Stack[Inst[1]] then
8872 InstrPoint = InstrPoint + 1;
8873 end
8874 elseif Stack[Inst[1]] then
8875 else
8876 InstrPoint = InstrPoint + 1;
8877 end
8878 elseif (Enum == 27) then -- TESTSET
8879 local B = Stack[Inst[2]];
8880
8881 if Inst[3] then
8882 if B then
8883 InstrPoint = InstrPoint + 1;
8884 else
8885 Stack[Inst[1]] = B
8886 end
8887 elseif B then
8888 Stack[Inst[1]] = B
8889 else
8890 InstrPoint = InstrPoint + 1;
8891 end
8892 elseif (Enum == 28) then -- CALL
8893 local A = Inst[1];
8894 local B = Inst[2];
8895 local C = Inst[3];
8896 local Stk = Stack;
8897 local Args, Results;
8898 local Limit, Edx;
8899
8900 Args = {};
8901
8902 if (B ~= 1) then
8903 if (B ~= 0) then
8904 Limit = A + B - 1;
8905 else
8906 Limit = Top;
8907 end;
8908
8909 Edx = 0;
8910
8911 for Idx = A + 1, Limit do
8912 Edx = Edx + 1;
8913
8914 Args[Edx] = Stk[Idx];
8915 end;
8916
8917 Limit, Results = _Returns(Stk[A](unpack(Args, 1, Limit - A)));
8918 else
8919 Limit, Results = _Returns(Stk[A]());
8920 end;
8921
8922 Top = A - 1;
8923
8924 if (C ~= 1) then
8925 if (C ~= 0) then
8926 Limit = A + C - 2;
8927 else
8928 Limit = Limit + A - 1;
8929 end;
8930
8931 Edx = 0;
8932
8933 for Idx = A, Limit do
8934 Edx = Edx + 1;
8935
8936 Stk[Idx] = Results[Edx];
8937 end;
8938 end;
8939 elseif (Enum == 29) then -- TAILCALL
8940 local A = Inst[1];
8941 local B = Inst[2];
8942 local Stk = Stack;
8943 local Args, Results;
8944 local Limit;
8945 local Rets = 0;
8946
8947 Args = {};
8948
8949 if (B ~= 1) then
8950 if (B ~= 0) then
8951 Limit = A + B - 1;
8952 else
8953 Limit = Top;
8954 end
8955
8956 for Idx = A + 1, Limit do
8957 Args[#Args + 1] = Stk[Idx];
8958 end
8959
8960 Results = {Stk[A](unpack(Args, 1, Limit - A))};
8961 else
8962 Results = {Stk[A]()};
8963 end;
8964
8965 for Index in pairs(Results) do -- get return count
8966 if (Index > Rets) then
8967 Rets = Index;
8968 end;
8969 end;
8970
8971 return Results, Rets;
8972 elseif (Enum == 30) then -- RETURN
8973 local A = Inst[1];
8974 local B = Inst[2];
8975 local Stk = Stack;
8976 local Edx, Output;
8977 local Limit;
8978
8979 if (B == 1) then
8980 return;
8981 elseif (B == 0) then
8982 Limit = Top;
8983 else
8984 Limit = A + B - 2;
8985 end;
8986
8987 Output = {};
8988 Edx = 0;
8989
8990 for Idx = A, Limit do
8991 Edx = Edx + 1;
8992
8993 Output[Edx] = Stk[Idx];
8994 end;
8995
8996 return Output, Edx;
8997 elseif (Enum == 31) then -- FORLOOP
8998 local A = Inst[1];
8999 local Stk = Stack;
9000
9001 local Step = Stk[A + 2];
9002 local Index = Stk[A] + Step;
9003
9004 Stk[A] = Index;
9005
9006 if (Step > 0) then
9007 if Index <= Stk[A + 1] then
9008 InstrPoint = InstrPoint + Inst[2];
9009
9010 Stk[A + 3] = Index;
9011 end;
9012 else
9013 if Index >= Stk[A + 1] then
9014 InstrPoint = InstrPoint + Inst[2];
9015
9016 Stk[A + 3] = Index;
9017 end
9018 end
9019 elseif (Enum == 32) then -- FORPREP
9020 local A = Inst[1];
9021 local Stk = Stack;
9022
9023 -- As per mirroring the real vm
9024 Stk[A] = assert(tonumber(Stk[A]), '`for` initial value must be a number');
9025 Stk[A + 1] = assert(tonumber(Stk[A + 1]), '`for` limit must be a number');
9026 Stk[A + 2] = assert(tonumber(Stk[A + 2]), '`for` step must be a number');
9027
9028 Stk[A] = Stk[A] - Stk[A + 2];
9029
9030 InstrPoint = InstrPoint + Inst[2];
9031 elseif (Enum == 33) then -- TFORLOOP
9032 local A = Inst[1];
9033 local C = Inst[3];
9034 local Stk = Stack;
9035
9036 local Offset = A + 2;
9037 local Result = {Stk[A](Stk[A + 1], Stk[A + 2])};
9038
9039 for Idx = 1, C do
9040 Stack[Offset + Idx] = Result[Idx];
9041 end;
9042
9043 if (Stk[A + 3] ~= nil) then
9044 Stk[A + 2] = Stk[A + 3];
9045 else
9046 InstrPoint = InstrPoint + 1;
9047 end;
9048 elseif (Enum == 34) then -- SETLIST
9049 local A = Inst[1];
9050 local B = Inst[2];
9051 local C = Inst[3];
9052 local Stk = Stack;
9053
9054 if (C == 0) then
9055 InstrPoint = InstrPoint + 1;
9056 C = Instr[InstrPoint].Value;
9057 end;
9058
9059 local Offset = (C - 1) * 50;
9060 local T = Stk[A]; -- Assuming T is the newly created table.
9061
9062 if (B == 0) then
9063 B = Top - A;
9064 end;
9065
9066 for Idx = 1, B do
9067 T[Offset + Idx] = Stk[A + Idx];
9068 end;
9069 elseif (Enum == 35) then -- CLOSE
9070 local A = Inst[1];
9071 local Cls = {}; -- Slight doubts on any issues this may cause
9072
9073 for Idx = 1, #Lupvals do
9074 local List = Lupvals[Idx];
9075
9076 for Idz = 0, #List do
9077 local Upv = List[Idz];
9078 local Stk = Upv[1];
9079 local Pos = Upv[2];
9080
9081 if (Stk == Stack) and (Pos >= A) then
9082 Cls[Pos] = Stk[Pos];
9083 Upv[1] = Cls; -- @memcorrupt credit me for the spoonfeed
9084 end;
9085 end;
9086 end;
9087 elseif (Enum == 36) then -- CLOSURE
9088 local NewProto = Proto[Inst[2]];
9089 local Stk = Stack;
9090
9091 local Indexes;
9092 local NewUvals;
9093
9094 if (NewProto.Upvals ~= 0) then
9095 Indexes = {};
9096 NewUvals = setmetatable({}, {
9097 __index = function(_, Key)
9098 local Val = Indexes[Key];
9099
9100 return Val[1][Val[2]];
9101 end,
9102 __newindex = function(_, Key, Value)
9103 local Val = Indexes[Key];
9104
9105 Val[1][Val[2]] = Value;
9106 end;
9107 }
9108 );
9109
9110 for Idx = 1, NewProto.Upvals do
9111 local Mvm = Instr[InstrPoint];
9112
9113 if (Mvm.Enum == 0) then -- MOVE
9114 Indexes[Idx - 1] = {Stk, Mvm[2]};
9115 elseif (Mvm.Enum == 4) then -- GETUPVAL
9116 Indexes[Idx - 1] = {Upvalues, Mvm[2]};
9117 end;
9118
9119 InstrPoint = InstrPoint + 1;
9120 end;
9121
9122 Lupvals[#Lupvals + 1] = Indexes;
9123 end;
9124
9125 Stk[Inst[1]] = Wrap(NewProto, Env, NewUvals);
9126 elseif (Enum == 37) then -- VARARG
9127 local A = Inst[1];
9128 local B = Inst[2];
9129 local Stk, Vars = Stack, Vararg;
9130
9131 Top = A - 1;
9132
9133 for Idx = A, A + (B > 0 and B - 1 or Varargsz) do
9134 Stk[Idx] = Vars[Idx - A];
9135 end;
9136 end;
9137 end;
9138 end;
9139
9140 local Args = {...};
9141
9142 for Idx = 0, Varargsz do
9143 if (Idx >= Chunk.Args) then
9144 Vararg[Idx - Chunk.Args] = Args[Idx + 1];
9145 else
9146 Stack[Idx] = Args[Idx + 1];
9147 end;
9148 end;
9149
9150 local A, B, C = pcall(Loop); -- Pcalling to allow yielding
9151
9152 if A then -- We're always expecting this to come out true (because errorless code)
9153 if B and (C > 0) then -- So I flipped the conditions.
9154 return unpack(B, 1, C);
9155 end;
9156
9157 return;
9158 else
9159 OnError(B, InstrPoint - 1); -- Didn't get time to test the `-1` honestly, but I assume it works properly
9160 end;
9161 end;
9162end;
9163
9164return function(BCode, Env) -- lua_function LoadBytecode (string BCode, table Env)
9165 local Buffer = GetMeaning(BCode);
9166
9167 return Wrap(Buffer, Env or getfenv(0)), Buffer;
9168end;
9169]]></ProtectedString>
9170 <BinaryString name="Tags"></BinaryString>
9171 </Properties>
9172 </Item>
9173 <Item class="Script" referent="RBX9BAAC995A4854EFCBB2D1DFE3A152236">
9174 <Properties>
9175 <bool name="Disabled">true</bool>
9176 <Content name="LinkedSource"><null></null></Content>
9177 <string name="Name">Credits</string>
9178 <string name="ScriptGuid">{4B480C1C-A37C-41F3-BE92-D3386E3ACBD8}</string>
9179 <ProtectedString name="Source"><![CDATA[--[[
9180LuLu 5.1 VM (original implementation) - http://lulu.luaforge.net/
9181* Rerubu (rewrite based off LuLu) - https://github.com/Rerumu/Rerubi
9182Yueliang 5 (Lua compiler in Lua) - http://yueliang.luaforge.net/
9183* einsteinK: various bug fixes
9184]]]]></ProtectedString>
9185 <BinaryString name="Tags"></BinaryString>
9186 </Properties>
9187 </Item>
9188 </Item>
9189 </Item>
9190</roblox>