· 6 years ago · Oct 14, 2019, 10:40 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}]]></ProtectedString>
1659 <BinaryString name="Tags"></BinaryString>
1660 </Properties>
1661 </Item>
1662 </Item>
1663 </Item>
1664 <Item class="Folder" referent="RBX931A1E5FD65F4602B4D075C659ED04A5">
1665 <Properties>
1666 <string name="Name">Types</string>
1667 <BinaryString name="Tags"></BinaryString>
1668 </Properties>
1669 <Item class="ModuleScript" referent="RBXCEE88289152742518A219084636CF9E1">
1670 <Properties>
1671 <Content name="LinkedSource"><null></null></Content>
1672 <string name="Name">BindableResource</string>
1673 <string name="ScriptGuid">{003CDED7-3D2B-4AE8-8FFE-E754EE143765}</string>
1674 <ProtectedString name="Source"><![CDATA[-- A fully extensible and type-safe admin-commands console
1675-- @documentation https://github.com/evaera/Cmdr/blob/master/README.md
1676-- @source https://github.com/evaera/Cmdr
1677-- @rostrap Cmdr
1678-- @author evaera
1679
1680return function (registry)
1681 registry:RegisterType("bindableResource", registry.Cmdr.Util.MakeEnumType("BindableResource", {"Chat"}))
1682end
1683]]></ProtectedString>
1684 <BinaryString name="Tags"></BinaryString>
1685 </Properties>
1686 </Item>
1687 <Item class="ModuleScript" referent="RBX638C439C98D54A30B32D74C6A8956490">
1688 <Properties>
1689 <Content name="LinkedSource"><null></null></Content>
1690 <string name="Name">BrickColor</string>
1691 <string name="ScriptGuid">{3C24E196-5B6C-4F7F-8015-C41B2B9B37C5}</string>
1692 <ProtectedString name="Source"><![CDATA[-- A fully extensible and type-safe admin-commands console
1693-- @documentation https://github.com/evaera/Cmdr/blob/master/README.md
1694-- @source https://github.com/evaera/Cmdr
1695-- @rostrap Cmdr
1696-- @author evaera
1697
1698local Util = require(script.Parent.Parent.Shared.Util)
1699
1700local brickColorNames = {
1701 "White", "Grey", "Light yellow", "Brick yellow", "Light green (Mint)", "Light reddish violet", "Pastel Blue",
1702 "Light orange brown", "Nougat", "Bright red", "Med. reddish violet", "Bright blue", "Bright yellow", "Earth orange",
1703 "Black", "Dark grey", "Dark green", "Medium green", "Lig. Yellowich orange", "Bright green", "Dark orange",
1704 "Light bluish violet", "Transparent", "Tr. Red", "Tr. Lg blue", "Tr. Blue", "Tr. Yellow", "Light blue",
1705 "Tr. Flu. Reddish orange", "Tr. Green", "Tr. Flu. Green", "Phosph. White", "Light red", "Medium red", "Medium blue",
1706 "Light grey", "Bright violet", "Br. yellowish orange", "Bright orange", "Bright bluish green", "Earth yellow",
1707 "Bright bluish violet", "Tr. Brown", "Medium bluish violet", "Tr. Medi. reddish violet", "Med. yellowish green",
1708 "Med. bluish green", "Light bluish green", "Br. yellowish green", "Lig. yellowish green", "Med. yellowish orange",
1709 "Br. reddish orange", "Bright reddish violet", "Light orange", "Tr. Bright bluish violet", "Gold", "Dark nougat",
1710 "Silver", "Neon orange", "Neon green", "Sand blue", "Sand violet", "Medium orange", "Sand yellow", "Earth blue",
1711 "Earth green", "Tr. Flu. Blue", "Sand blue metallic", "Sand violet metallic", "Sand yellow metallic",
1712 "Dark grey metallic", "Black metallic", "Light grey metallic", "Sand green", "Sand red", "Dark red",
1713 "Tr. Flu. Yellow", "Tr. Flu. Red", "Gun metallic", "Red flip/flop", "Yellow flip/flop", "Silver flip/flop", "Curry",
1714 "Fire Yellow", "Flame yellowish orange", "Reddish brown", "Flame reddish orange", "Medium stone grey", "Royal blue",
1715 "Dark Royal blue", "Bright reddish lilac", "Dark stone grey", "Lemon metalic", "Light stone grey", "Dark Curry",
1716 "Faded green", "Turquoise", "Light Royal blue", "Medium Royal blue", "Rust", "Brown", "Reddish lilac", "Lilac",
1717 "Light lilac", "Bright purple", "Light purple", "Light pink", "Light brick yellow", "Warm yellowish orange",
1718 "Cool yellow", "Dove blue", "Medium lilac", "Slime green", "Smoky grey", "Dark blue", "Parsley green", "Steel blue",
1719 "Storm blue", "Lapis", "Dark indigo", "Sea green", "Shamrock", "Fossil", "Mulberry", "Forest green", "Cadet blue",
1720 "Electric blue", "Eggplant", "Moss", "Artichoke", "Sage green", "Ghost grey", "Lilac", "Plum", "Olivine",
1721 "Laurel green", "Quill grey", "Crimson", "Mint", "Baby blue", "Carnation pink", "Persimmon", "Maroon", "Gold",
1722 "Daisy orange", "Pearl", "Fog", "Salmon", "Terra Cotta", "Cocoa", "Wheat", "Buttermilk", "Mauve", "Sunrise",
1723 "Tawny", "Rust", "Cashmere", "Khaki", "Lily white", "Seashell", "Burgundy", "Cork", "Burlap", "Beige", "Oyster",
1724 "Pine Cone", "Fawn brown", "Hurricane grey", "Cloudy grey", "Linen", "Copper", "Dirt brown", "Bronze", "Flint",
1725 "Dark taupe", "Burnt Sienna", "Institutional white", "Mid gray", "Really black", "Really red", "Deep orange",
1726 "Alder", "Dusty Rose", "Olive", "New Yeller", "Really blue", "Navy blue", "Deep blue", "Cyan", "CGA brown",
1727 "Magenta", "Pink", "Deep orange", "Teal", "Toothpaste", "Lime green", "Camo", "Grime", "Lavender",
1728 "Pastel light blue", "Pastel orange", "Pastel violet", "Pastel blue-green", "Pastel green", "Pastel yellow",
1729 "Pastel brown", "Royal purple", "Hot pink"
1730}
1731
1732local brickColorFinder = Util.MakeFuzzyFinder(brickColorNames)
1733
1734local brickColorType = {
1735 Transform = function(text)
1736 local brickColors = {}
1737 for i, name in pairs(brickColorFinder(text)) do
1738 brickColors[i] = BrickColor.new(name)
1739 end
1740 return brickColors
1741 end;
1742
1743 Validate = function(brickColors)
1744 return #brickColors > 0, "No valid brick colors with that name could be found."
1745 end;
1746
1747 Autocomplete = function(brickColors)
1748 return Util.GetNames(brickColors)
1749 end;
1750
1751 Parse = function(brickColors)
1752 return brickColors[1]
1753 end;
1754}
1755
1756local brickColor3Type = {
1757 Transform = brickColorType.Transform;
1758 Validate = brickColorType.Validate;
1759 Autocomplete = brickColorType.Autocomplete;
1760
1761 Parse = function(brickColors)
1762 return brickColors[1].Color
1763 end;
1764}
1765
1766return function(registry)
1767 registry:RegisterType("brickColor", brickColorType)
1768 registry:RegisterType("brickColors", Util.MakeListableType(brickColorType))
1769
1770 registry:RegisterType("brickColor3", brickColor3Type)
1771 registry:RegisterType("brickColor3s", Util.MakeListableType(brickColor3Type))
1772end]]></ProtectedString>
1773 <BinaryString name="Tags"></BinaryString>
1774 </Properties>
1775 </Item>
1776 <Item class="ModuleScript" referent="RBXEC1ED03605AE47CD8EE8B48015D5FA3D">
1777 <Properties>
1778 <Content name="LinkedSource"><null></null></Content>
1779 <string name="Name">Color3</string>
1780 <string name="ScriptGuid">{B560A0F5-2B93-4FD6-8EF7-3FE8336874EB}</string>
1781 <ProtectedString name="Source"><![CDATA[-- A fully extensible and type-safe admin-commands console
1782-- @documentation https://github.com/evaera/Cmdr/blob/master/README.md
1783-- @source https://github.com/evaera/Cmdr
1784-- @rostrap Cmdr
1785-- @author evaera
1786
1787local Util = require(script.Parent.Parent.Shared.Util)
1788
1789local color3Type = Util.MakeSequenceType({
1790 ValidateEach = function(value, i)
1791 if value == nil then
1792 return false, ("Invalid or missing number at position %d in Color3 type."):format(i)
1793 elseif value < 0 or value > 255 then
1794 return false, ("Number out of acceptable range 0-255 at position %d in Color3 type."):format(i)
1795 elseif value % 1 ~= 0 then
1796 return false, ("Number is not an integer at position %d in Color3 type."):format(i)
1797 end
1798
1799 return true
1800 end;
1801 TransformEach = tonumber;
1802 Constructor = Color3.fromRGB;
1803 Length = 3;
1804})
1805
1806local function parseHexDigit(x)
1807 if #x == 1 then
1808 x = x .. x
1809 end
1810
1811 return tonumber(x, 16)
1812end
1813
1814local hexColor3Type = {
1815 Transform = function(text)
1816 local r, g, b = text:match("^#?(%x%x?)(%x%x?)(%x%x?)$")
1817 return Util.Each(parseHexDigit, r, g, b)
1818 end;
1819
1820 Validate = function(r, g, b)
1821 return r ~= nil and g ~= nil and b ~= nil, "Invalid hex color"
1822 end;
1823
1824 Parse = function(...)
1825 return Color3.fromRGB(...)
1826 end;
1827}
1828
1829return function (cmdr)
1830 cmdr:RegisterType("color3", color3Type)
1831 cmdr:RegisterType("color3s", Util.MakeListableType(color3Type))
1832
1833 cmdr:RegisterType("hexColor3", hexColor3Type)
1834 cmdr:RegisterType("hexColor3s", Util.MakeListableType(hexColor3Type))
1835end]]></ProtectedString>
1836 <BinaryString name="Tags"></BinaryString>
1837 </Properties>
1838 </Item>
1839 <Item class="ModuleScript" referent="RBXED5B094136AB418088F79E4D37ADAF02">
1840 <Properties>
1841 <Content name="LinkedSource"><null></null></Content>
1842 <string name="Name">Command</string>
1843 <string name="ScriptGuid">{A38076EA-89FD-4B05-A661-384DC2CD4618}</string>
1844 <ProtectedString name="Source"><![CDATA[-- A fully extensible and type-safe admin-commands console
1845-- @documentation https://github.com/evaera/Cmdr/blob/master/README.md
1846-- @source https://github.com/evaera/Cmdr
1847-- @rostrap Cmdr
1848-- @author evaera
1849
1850local Util = require(script.Parent.Parent.Shared.Util)
1851
1852return function (cmdr)
1853 local commandType = {
1854 Transform = function (text)
1855 local findCommand = Util.MakeFuzzyFinder(cmdr:GetCommandsAsStrings())
1856
1857 return findCommand(text)
1858 end;
1859
1860 Validate = function (commands)
1861 return #commands > 0, "No command with that name could be found."
1862 end;
1863
1864 Autocomplete = function (commands)
1865 return commands
1866 end;
1867
1868 Parse = function (commands)
1869 return commands[1]
1870 end;
1871 }
1872
1873 cmdr:RegisterType("command", commandType)
1874 cmdr:RegisterType("commands", Util.MakeListableType(commandType))
1875end]]></ProtectedString>
1876 <BinaryString name="Tags"></BinaryString>
1877 </Properties>
1878 </Item>
1879 <Item class="ModuleScript" referent="RBXD77FD4D79FE34C98B3486A695AD6880E">
1880 <Properties>
1881 <Content name="LinkedSource"><null></null></Content>
1882 <string name="Name">ConditionFunction</string>
1883 <string name="ScriptGuid">{CBE4A528-0A6A-4006-B652-3A2CBD6CA7FE}</string>
1884 <ProtectedString name="Source"><![CDATA[-- A fully extensible and type-safe admin-commands console
1885-- @documentation https://github.com/evaera/Cmdr/blob/master/README.md
1886-- @source https://github.com/evaera/Cmdr
1887-- @rostrap Cmdr
1888-- @author evaera
1889
1890return function (registry)
1891 registry:RegisterType("conditionFunction", registry.Cmdr.Util.MakeEnumType("ConditionFunction", {"startsWith"}))
1892end
1893]]></ProtectedString>
1894 <BinaryString name="Tags"></BinaryString>
1895 </Properties>
1896 </Item>
1897 <Item class="ModuleScript" referent="RBXB384619ED8DC4797ADD5027BCB9C2BB9">
1898 <Properties>
1899 <Content name="LinkedSource"><null></null></Content>
1900 <string name="Name">Duration</string>
1901 <string name="ScriptGuid">{4FEC71D5-1572-4BCD-AD82-065C6B1CF2B7}</string>
1902 <ProtectedString name="Source"><![CDATA[-- A fully extensible and type-safe admin-commands console
1903-- @documentation https://github.com/evaera/Cmdr/blob/master/README.md
1904-- @source https://github.com/evaera/Cmdr
1905-- @rostrap Cmdr
1906-- @author evaera
1907
1908local Util = require(script.Parent.Parent.Shared.Util)
1909
1910local unitTable = {
1911 Years = 31556926,
1912 Months = 2629744,
1913 Weeks = 604800,
1914 Days = 86400,
1915 Hours = 3600,
1916 Minutes = 60,
1917 Seconds = 1
1918}
1919
1920local searchKeyTable = {}
1921for key, _ in pairs(unitTable) do
1922 table.insert(searchKeyTable, key)
1923end
1924local unitFinder = Util.MakeFuzzyFinder(searchKeyTable)
1925
1926local function stringToSecondDuration(stringDuration)
1927 -- The duration cannot be null or an empty string.
1928 if stringDuration == nil or stringDuration == "" then
1929 return nil
1930 end
1931 -- Allow 0 by itself (without a unit) to indicate 0 seconds
1932 local durationNum = tonumber(stringDuration)
1933 if durationNum and durationNum == 0 then
1934 return 0, 0, true
1935 end
1936 -- The duration must end with a unit,
1937 -- if it doesn't then return true as the fourth value to indicate the need to offer autocomplete for units.
1938 local endOnlyString = stringDuration:gsub("%d+%a+", "")
1939 local endNumber = endOnlyString:match("%d+")
1940 if endNumber then
1941 return nil, tonumber(endNumber), true
1942 end
1943 local seconds = nil
1944 local rawNum, rawUnit
1945 for rawComponent in stringDuration:gmatch("%d+%a+") do
1946 rawNum, rawUnit = rawComponent:match("(%d+)(%a+)")
1947 local unitNames = unitFinder(rawUnit)
1948 -- There were no matching units, it's invalid. Return the parsed number to be used for autocomplete
1949 if #unitNames == 0 then
1950 return nil, tonumber(rawNum)
1951 end
1952 if seconds == nil then seconds = 0 end
1953 -- While it was already defaulting to use minutes when using just "m", this does it without worrying
1954 -- about any consistency between list ordering.
1955 seconds = seconds + (rawUnit:lower() == "m" and 60 or unitTable[unitNames[1]]) * tonumber(rawNum)
1956 end
1957 -- If no durations were provided, return nil.
1958 if seconds == nil then
1959 return nil
1960 else
1961 return seconds, tonumber(rawNum)
1962 end
1963end
1964
1965local function mapUnits(units, rawText, lastNumber, subStart)
1966 subStart = subStart or 1
1967 local returnTable = {}
1968 for i, unit in pairs(units) do
1969 if lastNumber == 1 then
1970 returnTable[i] = rawText .. unit:sub(subStart, #unit - 1)
1971 else
1972 returnTable[i] = rawText .. unit:sub(subStart)
1973 end
1974 end
1975 return returnTable
1976end
1977
1978local durationType = {
1979 Transform = function(text)
1980 return text, stringToSecondDuration(text)
1981 end;
1982
1983 Validate = function(_, duration)
1984 return duration ~= nil and duration >= 0
1985 end;
1986
1987 Autocomplete = function(rawText, duration, lastNumber, isUnitMissing, matchedUnits)
1988 local returnTable = {}
1989 if isUnitMissing or matchedUnits then
1990 local unitsTable = isUnitMissing == true and unitFinder("") or matchedUnits
1991 if isUnitMissing == true then
1992 -- Concat the entire unit name to existing text.
1993 returnTable = mapUnits(unitsTable, rawText, lastNumber)
1994 else
1995 -- Concat the rest of the unit based on what already exists of the unit name.
1996 local existingUnitLength = rawText:match("^.*(%a+)$"):len()
1997 returnTable = mapUnits(unitsTable, rawText, existingUnitLength + 1)
1998 end
1999 elseif duration ~= nil then
2000 local endingUnit = rawText:match("^.*%d+(%a+)$")
2001 -- Assume there is a singular match at this point
2002 local fuzzyUnits = unitFinder(endingUnit)
2003 -- List all possible fuzzy matches. This is for the Minutes/Months ambiguity case.
2004 returnTable = mapUnits(fuzzyUnits, rawText, lastNumber, #endingUnit + 1)
2005 -- Sort alphabetically in the Minutes/Months case, so Minutes are displayed on top.
2006 table.sort(returnTable)
2007 end
2008 return returnTable
2009 end;
2010
2011 Parse = function(_, duration)
2012 return duration
2013 end;
2014}
2015
2016return function(registry)
2017 registry:RegisterType("duration", durationType)
2018 registry:RegisterType("durations", Util.MakeListableType(durationType))
2019end]]></ProtectedString>
2020 <BinaryString name="Tags"></BinaryString>
2021 </Properties>
2022 </Item>
2023 <Item class="ModuleScript" referent="RBX599092F54B544F07B2AF0198C9DED4A4">
2024 <Properties>
2025 <Content name="LinkedSource"><null></null></Content>
2026 <string name="Name">Player</string>
2027 <string name="ScriptGuid">{43961925-6FC1-4F63-A525-6091F7E2A05D}</string>
2028 <ProtectedString name="Source"><![CDATA[-- A fully extensible and type-safe admin-commands console
2029-- @documentation https://github.com/evaera/Cmdr/blob/master/README.md
2030-- @source https://github.com/evaera/Cmdr
2031-- @rostrap Cmdr
2032-- @author evaera
2033
2034local Util = require(script.Parent.Parent.Shared.Util)
2035local Players = game:GetService("Players")
2036
2037local function ShorthandSingle (text, executor)
2038 if text == "." or text == "me" then
2039 return {executor}
2040 end
2041end
2042
2043local function ShorthandMultiple (text, executor)
2044 if text == "*" or text == "all" then
2045 return Players:GetPlayers()
2046 elseif text == "others" then
2047 local Others = Players:GetPlayers()
2048 for i = 1, #Others do
2049 if Others[i] == executor then
2050 table.remove(Others, i)
2051 break
2052 end
2053 end
2054 return Others
2055 end
2056end
2057
2058local function CheckShorthands (text, executor, ...)
2059 for _, func in pairs({...}) do
2060 local values = func(text, executor)
2061
2062 if values then return values end
2063 end
2064end
2065
2066local playerType = {
2067 Transform = function (text, executor)
2068 local shorthand = CheckShorthands(text, executor, ShorthandSingle)
2069 if shorthand then
2070 return shorthand
2071 end
2072
2073 local findPlayer = Util.MakeFuzzyFinder(Players:GetPlayers())
2074
2075 return findPlayer(text)
2076 end;
2077
2078 Validate = function (players)
2079 return #players > 0, "No player with that name could be found."
2080 end;
2081
2082 Autocomplete = function (players)
2083 return Util.GetNames(players)
2084 end;
2085
2086 Parse = function (players)
2087 return players[1]
2088 end;
2089}
2090
2091local playersType = {
2092 Listable = true;
2093
2094 Transform = function (text, executor)
2095 local shorthand = CheckShorthands(text, executor, ShorthandSingle, ShorthandMultiple)
2096
2097 if shorthand then
2098 return shorthand, true
2099 end
2100
2101 local findPlayers = Util.MakeFuzzyFinder(Players:GetPlayers())
2102
2103 return findPlayers(text)
2104 end;
2105
2106 Validate = function (players)
2107 return #players > 0, "No players were found matching that query."
2108 end;
2109
2110 Autocomplete = function (players)
2111 return Util.GetNames(players)
2112 end;
2113
2114 Parse = function (players, returnAll)
2115 return returnAll and players or { players[1] }
2116 end;
2117}
2118
2119return function (cmdr)
2120 cmdr:RegisterType("player", playerType)
2121 cmdr:RegisterType("players", playersType)
2122end
2123]]></ProtectedString>
2124 <BinaryString name="Tags"></BinaryString>
2125 </Properties>
2126 </Item>
2127 <Item class="ModuleScript" referent="RBXBDDBF036BDA048DF819154E24AFF6EEE">
2128 <Properties>
2129 <Content name="LinkedSource"><null></null></Content>
2130 <string name="Name">PlayerId</string>
2131 <string name="ScriptGuid">{733AD56E-6BD0-41EE-9E46-62442F9E8755}</string>
2132 <ProtectedString name="Source"><![CDATA[-- A fully extensible and type-safe admin-commands console
2133-- @documentation https://github.com/evaera/Cmdr/blob/master/README.md
2134-- @source https://github.com/evaera/Cmdr
2135-- @rostrap Cmdr
2136-- @author evaera
2137
2138local Util = require(script.Parent.Parent.Shared.Util)
2139local Players = game:GetService("Players")
2140
2141local nameCache = {}
2142local function getUserId(name)
2143 if nameCache[name] then
2144 return nameCache[name]
2145 elseif Players:FindFirstChild(name) then
2146 nameCache[name] = Players[name].UserId
2147 return Players[name].UserId
2148 else
2149 local ok, userid = pcall(Players.GetUserIdFromNameAsync, Players, name)
2150
2151 if not ok then
2152 return nil
2153 end
2154
2155 nameCache[name] = userid
2156 return userid
2157 end
2158end
2159
2160local playerIdType = {
2161 DisplayName = "Full Player Name";
2162
2163 Transform = function (text)
2164 local findPlayer = Util.MakeFuzzyFinder(Players:GetPlayers())
2165
2166 return text, findPlayer(text)
2167 end;
2168
2169 ValidateOnce = function (text)
2170 return getUserId(text) ~= nil, "No player with that name could be found."
2171 end;
2172
2173 Autocomplete = function (_, players)
2174 return Util.GetNames(players)
2175 end;
2176
2177 Parse = function (text)
2178 return getUserId(text)
2179 end;
2180}
2181
2182return function (cmdr)
2183 cmdr:RegisterType("playerId", playerIdType)
2184 cmdr:RegisterType("playerIds", Util.MakeListableType(playerIdType))
2185end
2186]]></ProtectedString>
2187 <BinaryString name="Tags"></BinaryString>
2188 </Properties>
2189 </Item>
2190 <Item class="ModuleScript" referent="RBX13B272783ADA4E12A46B93254E029747">
2191 <Properties>
2192 <Content name="LinkedSource"><null></null></Content>
2193 <string name="Name">Primitives</string>
2194 <string name="ScriptGuid">{CD355608-E8F5-40F8-A24F-642072874529}</string>
2195 <ProtectedString name="Source"><![CDATA[-- A fully extensible and type-safe admin-commands console
2196-- @documentation https://github.com/evaera/Cmdr/blob/master/README.md
2197-- @source https://github.com/evaera/Cmdr
2198-- @rostrap Cmdr
2199-- @author evaera
2200
2201local Util = require(script.Parent.Parent.Shared.Util)
2202
2203local stringType = {
2204 Validate = function (value)
2205 return value ~= nil
2206 end;
2207
2208 Parse = function (value)
2209 return tostring(value)
2210 end;
2211}
2212
2213local numberType = {
2214 Transform = function (text)
2215 return tonumber(text)
2216 end;
2217
2218 Validate = function (value)
2219 return value ~= nil
2220 end;
2221
2222 Parse = function (value)
2223 return value
2224 end;
2225}
2226
2227local intType = {
2228 Transform = function (text)
2229 return tonumber(text)
2230 end;
2231
2232 Validate = function (value)
2233 return value ~= nil and value == math.floor(value), "Only whole numbers are valid."
2234 end;
2235
2236 Parse = function (value)
2237 return value
2238 end
2239}
2240
2241local boolType do
2242 local truthy = Util.MakeDictionary({"true", "t", "yes", "y", "on", "enable", "enabled", "1", "+"});
2243 local falsy = Util.MakeDictionary({"false"; "f"; "no"; "n"; "off"; "disable"; "disabled"; "0"; "-"});
2244
2245 boolType = {
2246 Transform = function (text)
2247 return text:lower()
2248 end;
2249
2250 Validate = function (value)
2251 return truthy[value] ~= nil or falsy[value] ~= nil, "Please use true/yes/on or false/no/off."
2252 end;
2253
2254 Parse = function (value)
2255 if truthy[value] then
2256 return true
2257 elseif falsy[value] then
2258 return false
2259 else
2260 error("Unknown boolean value.")
2261 end
2262 end;
2263 }
2264end
2265
2266return function (cmdr)
2267 cmdr:RegisterType("string", stringType)
2268 cmdr:RegisterType("number", numberType)
2269 cmdr:RegisterType("integer", intType)
2270 cmdr:RegisterType("boolean", boolType)
2271
2272 cmdr:RegisterType("strings", Util.MakeListableType(stringType))
2273 cmdr:RegisterType("numbers", Util.MakeListableType(numberType))
2274 cmdr:RegisterType("integers", Util.MakeListableType(intType))
2275 cmdr:RegisterType("booleans", Util.MakeListableType(boolType))
2276end]]></ProtectedString>
2277 <BinaryString name="Tags"></BinaryString>
2278 </Properties>
2279 </Item>
2280 <Item class="ModuleScript" referent="RBXF471B0EAB54240449D47116CA22DF7D8">
2281 <Properties>
2282 <Content name="LinkedSource"><null></null></Content>
2283 <string name="Name">Team</string>
2284 <string name="ScriptGuid">{98D7A448-55AD-449B-A978-20528E3CF220}</string>
2285 <ProtectedString name="Source"><![CDATA[-- A fully extensible and type-safe admin-commands console
2286-- @documentation https://github.com/evaera/Cmdr/blob/master/README.md
2287-- @source https://github.com/evaera/Cmdr
2288-- @rostrap Cmdr
2289-- @author evaera
2290
2291local Teams = game:GetService("Teams")
2292local Util = require(script.Parent.Parent.Shared.Util)
2293
2294local teamType = {
2295 Transform = function (text)
2296 local findTeam = Util.MakeFuzzyFinder(Teams:GetTeams())
2297
2298 return findTeam(text)
2299 end;
2300
2301 Validate = function (teams)
2302 return #teams > 0, "No team with that name could be found."
2303 end;
2304
2305 Autocomplete = function (teams)
2306 return Util.GetNames(teams)
2307 end;
2308
2309 Parse = function (teams)
2310 return teams[1];
2311 end;
2312}
2313
2314local teamPlayersType = {
2315 Listable = true;
2316 Transform = teamType.Transform;
2317 Validate = teamType.Validate;
2318 Autocomplete = teamType.Autocomplete;
2319
2320 Parse = function (teams)
2321 return teams[1]:GetPlayers()
2322 end;
2323}
2324
2325local teamColorType = {
2326 Transform = teamType.Transform;
2327 Validate = teamType.Validate;
2328 Autocomplete = teamType.Autocomplete;
2329
2330 Parse = function (teams)
2331 return teams[1].TeamColor
2332 end;
2333}
2334
2335return function (cmdr)
2336 cmdr:RegisterType("team", teamType)
2337 cmdr:RegisterType("teams", Util.MakeListableType(teamType))
2338
2339 cmdr:RegisterType("teamPlayers", teamPlayersType)
2340
2341 cmdr:RegisterType("teamColor", teamColorType)
2342 cmdr:RegisterType("teamColors", Util.MakeListableType(teamColorType))
2343end]]></ProtectedString>
2344 <BinaryString name="Tags"></BinaryString>
2345 </Properties>
2346 </Item>
2347 <Item class="ModuleScript" referent="RBX5B6895F0800A43ED98E1C88E3E931E76">
2348 <Properties>
2349 <Content name="LinkedSource"><null></null></Content>
2350 <string name="Name">UserInput</string>
2351 <string name="ScriptGuid">{1E4626B3-7A93-4BC8-A167-72EA55831D09}</string>
2352 <ProtectedString name="Source"><![CDATA[-- A fully extensible and type-safe admin-commands console
2353-- @documentation https://github.com/evaera/Cmdr/blob/master/README.md
2354-- @source https://github.com/evaera/Cmdr
2355-- @rostrap Cmdr
2356-- @author evaera
2357
2358local Util = require(script.Parent.Parent.Shared.Util)
2359
2360local combinedInputEnums = Enum.UserInputType:GetEnumItems()
2361
2362for _, e in pairs(Enum.KeyCode:GetEnumItems()) do
2363 combinedInputEnums[#combinedInputEnums + 1] = e
2364end
2365
2366local userInputType = {
2367 Transform = function (text)
2368 local findEnum = Util.MakeFuzzyFinder(combinedInputEnums)
2369
2370 return findEnum(text)
2371 end;
2372
2373 Validate = function (enums)
2374 return #enums > 0
2375 end;
2376
2377 Autocomplete = function (enums)
2378 return Util.GetNames(enums)
2379 end;
2380
2381 Parse = function (enums)
2382 return enums[1];
2383 end;
2384}
2385
2386return function (cmdr)
2387 cmdr:RegisterType("userInput", userInputType)
2388 cmdr:RegisterType("userInputs", Util.MakeListableType(userInputType))
2389end]]></ProtectedString>
2390 <BinaryString name="Tags"></BinaryString>
2391 </Properties>
2392 </Item>
2393 <Item class="ModuleScript" referent="RBX2EA11A3AD0BD440096D0A01F152534F2">
2394 <Properties>
2395 <Content name="LinkedSource"><null></null></Content>
2396 <string name="Name">Vector</string>
2397 <string name="ScriptGuid">{BF85AE9A-0FBE-4EAA-98CF-F55AC02BA9FA}</string>
2398 <ProtectedString name="Source"><![CDATA[-- A fully extensible and type-safe admin-commands console
2399-- @documentation https://github.com/evaera/Cmdr/blob/master/README.md
2400-- @source https://github.com/evaera/Cmdr
2401-- @rostrap Cmdr
2402-- @author evaera
2403
2404local Util = require(script.Parent.Parent.Shared.Util)
2405
2406local function validateVector(value, i)
2407 if value == nil then
2408 return false, ("Invalid or missing number at position %d in Vector type."):format(i)
2409 end
2410
2411 return true
2412end
2413
2414local vector3Type = Util.MakeSequenceType({
2415 ValidateEach = validateVector;
2416 TransformEach = tonumber;
2417 Constructor = Vector3.new;
2418 Length = 3;
2419})
2420
2421local vector2Type = Util.MakeSequenceType({
2422 ValidateEach = validateVector;
2423 TransformEach = tonumber;
2424 Constructor = Vector2.new;
2425 Length = 2;
2426})
2427
2428return function (cmdr)
2429 cmdr:RegisterType("vector3", vector3Type)
2430 cmdr:RegisterType("vector3s", Util.MakeListableType(vector3Type))
2431
2432 cmdr:RegisterType("vector2", vector2Type)
2433 cmdr:RegisterType("vector2s", Util.MakeListableType(vector2Type))
2434end]]></ProtectedString>
2435 <BinaryString name="Tags"></BinaryString>
2436 </Properties>
2437 </Item>
2438 </Item>
2439 <Item class="Folder" referent="RBX66A52AFF88594D1AB194C4A00314A929">
2440 <Properties>
2441 <string name="Name">Shared</string>
2442 <BinaryString name="Tags"></BinaryString>
2443 </Properties>
2444 <Item class="ModuleScript" referent="RBX716C459E6F0242A195AA74E318076C2B">
2445 <Properties>
2446 <Content name="LinkedSource"><null></null></Content>
2447 <string name="Name">Argument</string>
2448 <string name="ScriptGuid">{27B50999-F950-4357-B8AA-3E602A18E479}</string>
2449 <ProtectedString name="Source"><![CDATA[-- A fully extensible and type-safe admin-commands console
2450-- @documentation https://github.com/evaera/Cmdr/blob/master/README.md
2451-- @source https://github.com/evaera/Cmdr
2452-- @rostrap Cmdr
2453-- @author evaera
2454
2455local TYPE_DEFAULTS = {
2456 -- Make all `players` types also be able to match by team
2457 players = "players % teamPlayers";
2458
2459 playerId = "playerId # integer";
2460 playerIds = "playerIds # integers";
2461
2462 brickColor = "brickColor % teamColor";
2463 brickColors = "brickColors % teamColors";
2464
2465 color3 = "color3 # hexColor3 ! brickColor3";
2466 color3s = "color3s # hexColor3s ! brickColor3s";
2467}
2468
2469local Util = require(script.Parent.Util)
2470
2471local Argument = {}
2472Argument.__index = Argument
2473
2474--- Returns a new ArgumentContext, an object that handles parsing and validating arguments
2475function Argument.new (command, argumentObject, value)
2476 local self = {
2477 Command = command; -- The command that owns this argument
2478 Type = nil; -- The type definition
2479 Name = argumentObject.Name; -- The name for this specific argument
2480 Object = argumentObject; -- The raw ArgumentObject (definition)
2481 Required = argumentObject.Default == nil and argumentObject.Optional ~= true; -- If the argument is required or not.
2482 Executor = command.Executor; -- The player who is running the command
2483 RawValue = nil; -- The raw, unparsed value
2484 RawSegments = {}; -- The raw, unparsed segments (if the raw value was comma-sep)
2485 TransformedValues = {}; -- The transformed value (generated later)
2486 Prefix = nil; -- The prefix for this command (%Team)
2487 }
2488
2489 local parsedType, parsedRawValue, prefix = Util.ParsePrefixedUnionType(
2490 TYPE_DEFAULTS[argumentObject.Type] or argumentObject.Type, value
2491 )
2492
2493 self.Type = command.Dispatcher.Registry:GetType(parsedType)
2494 self.RawValue = parsedRawValue
2495 self.Prefix = prefix
2496
2497 if self.Type == nil then
2498 error(string.format("%s has an unregistered type %q", self.Name or "<none>", parsedType or "<none>"))
2499 end
2500
2501 setmetatable(self, Argument)
2502
2503 self:Transform()
2504
2505 return self
2506end
2507
2508--- Calls the transform function on this argument.
2509-- The return value(s) from this function are passed to all of the other argument methods.
2510-- Called automatically at instantiation
2511function Argument:Transform()
2512 if #self.TransformedValues ~= 0 then
2513 return
2514 end
2515
2516 if self.Type.Listable and #self.RawValue > 0 then
2517 local rawSegments = Util.SplitStringSimple(self.RawValue, ",")
2518
2519 if self.RawValue:sub(#self.RawValue, #self.RawValue) == "," then
2520 rawSegments[#rawSegments + 1] = "" -- makes auto complete tick over right after pressing ,
2521 end
2522
2523 for i, rawSegment in ipairs(rawSegments) do
2524 self.RawSegments[i] = rawSegment
2525 self.TransformedValues[i] = { self:TransformSegment(rawSegment) }
2526 end
2527 else
2528 self.RawSegments[1] = self.RawValue
2529 self.TransformedValues[1] = { self:TransformSegment(self.RawValue) }
2530 end
2531end
2532
2533function Argument:TransformSegment(rawSegment)
2534 if self.Type.Transform then
2535 return self.Type.Transform(rawSegment, self.Executor)
2536 else
2537 return rawSegment
2538 end
2539end
2540
2541--- Returns whatever the Transform method gave us.
2542function Argument:GetTransformedValue(segment)
2543 return unpack(self.TransformedValues[segment])
2544end
2545
2546--- Validates that the argument will work without any type errors.
2547function Argument:Validate(isFinal)
2548 if self.RawValue == nil or #self.RawValue == 0 and self.Required == false then
2549 return true
2550 end
2551
2552 if self.Type.Validate or self.Type.ValidateOnce then
2553 for i = 1, #self.TransformedValues do
2554 if self.Type.Validate then
2555 local valid, errorText = self.Type.Validate(self:GetTransformedValue(i))
2556
2557 if not valid then
2558 return valid, errorText or "Invalid value"
2559 end
2560 end
2561
2562 if isFinal and self.Type.ValidateOnce then
2563 local validOnce, errorTextOnce = self.Type.ValidateOnce(self:GetTransformedValue(i))
2564
2565 if not validOnce then
2566 return validOnce, errorTextOnce
2567 end
2568 end
2569 end
2570
2571 return true
2572 else
2573 return true
2574 end
2575end
2576
2577--- Gets a list of all possible values that could match based on the current value.
2578function Argument:GetAutocomplete()
2579 if self.Type.Autocomplete then
2580 return self.Type.Autocomplete(self:GetTransformedValue(#self.TransformedValues))
2581 else
2582 return {}
2583 end
2584end
2585
2586function Argument:ParseValue(i)
2587 if self.Type.Parse then
2588 return self.Type.Parse(self:GetTransformedValue(i))
2589 else
2590 return self:GetTransformedValue(i)
2591 end
2592end
2593
2594--- Returns the final value of the argument.
2595function Argument:GetValue()
2596 if #self.RawValue == 0 and not self.Required and self.Object.Default ~= nil then
2597 return self.Object.Default
2598 end
2599
2600 if not self.Type.Listable then
2601 return self:ParseValue(1)
2602 end
2603
2604 local values = {}
2605
2606 for i = 1, #self.TransformedValues do
2607 local parsedValue = self:ParseValue(i)
2608
2609 if type(parsedValue) ~= "table" then
2610 error(("Listable types must return a table from Parse (%s)"):format(self.Type.Name))
2611 end
2612
2613 for _, value in pairs(parsedValue) do
2614 values[value] = true -- Put them into a dictionary to ensure uniqueness
2615 end
2616 end
2617
2618 local valueArray = {}
2619
2620 for value in pairs(values) do
2621 valueArray[#valueArray + 1] = value
2622 end
2623
2624 return valueArray
2625end
2626
2627return Argument]]></ProtectedString>
2628 <BinaryString name="Tags"></BinaryString>
2629 </Properties>
2630 </Item>
2631 <Item class="ModuleScript" referent="RBX11E9EB676C274EDEA8E7B702715FF9C7">
2632 <Properties>
2633 <Content name="LinkedSource"><null></null></Content>
2634 <string name="Name">Command</string>
2635 <string name="ScriptGuid">{87DF3A9B-052B-4922-A962-9C737324A21F}</string>
2636 <ProtectedString name="Source"><![CDATA[-- A fully extensible and type-safe admin-commands console
2637-- @documentation https://github.com/evaera/Cmdr/blob/master/README.md
2638-- @source https://github.com/evaera/Cmdr
2639-- @rostrap Cmdr
2640-- @author evaera
2641
2642local RunService = game:GetService("RunService")
2643local Players = game:GetService("Players")
2644local Argument = require(script.Parent.Argument)
2645
2646local IsServer = RunService:IsServer()
2647
2648local Command = {}
2649Command.__index = Command
2650
2651--- Returns a new CommandContext, an object which is created for every command validation.
2652-- This is also what's passed as the context to the "Run" functions in commands
2653function Command.new (options)
2654 local self = {
2655 Dispatcher = options.Dispatcher; -- The dispatcher that created this command context
2656 Cmdr = options.Dispatcher.Cmdr; -- A quick reference to Cmdr for command context
2657 Name = options.CommandObject.Name; -- The command name (not alias)
2658 RawText = options.Text; -- The raw text used to trigger this command
2659 Object = options.CommandObject; -- The command object (definition)
2660 Group = options.CommandObject.Group; -- The group this command is in
2661 State = {}; -- A table which will hold any custom command state information
2662 Aliases = options.CommandObject.Aliases;
2663 Alias = options.Alias; -- The command name that was used
2664 Description = options.CommandObject.Description;
2665 Executor = options.Executor; -- The player who ran the command
2666 ArgumentDefinitions = options.CommandObject.Args; -- The argument definitions from the command definition
2667 RawArguments = options.Arguments; -- Array of strings which are the unparsed values for the arguments
2668 Arguments = {}; -- A table which will hold ArgumentContexts for each argument
2669 Data = options.Data; -- A special container for any additional data the command needs to collect from the client
2670 Response = nil; -- Will be set at the very end when the command is run and a string is returned from the Run function.
2671 }
2672
2673 setmetatable(self, Command)
2674
2675 return self
2676end
2677
2678--- Parses all of the command arguments into ArgumentContexts
2679-- Called by the command dispatcher automatically
2680-- allowIncompleteArguments: if true, will not throw an error for missing arguments
2681function Command:Parse (allowIncompleteArguments)
2682 local hadOptional = false
2683 for i, definition in ipairs(self.ArgumentDefinitions) do
2684 local required = (definition.Default == nil and definition.Optional ~= true)
2685
2686 if required and hadOptional then
2687 error(("Command %q: Required arguments cannot occur after optional arguments."):format(self.Name))
2688 elseif not required then
2689 hadOptional = true
2690 end
2691
2692 if self.RawArguments[i] == nil and required and allowIncompleteArguments ~= true then
2693 return false, ("Required argument #%d %s is missing."):format(i, definition.Name)
2694 elseif self.RawArguments[i] or allowIncompleteArguments then
2695 self.Arguments[i] = Argument.new(self, definition, self.RawArguments[i] or "")
2696 end
2697 end
2698
2699 return true
2700end
2701
2702--- Validates that all of the arguments are in a valid state.
2703-- This must be called before :Run() is called.
2704-- Returns boolean (true if ok), errorText
2705function Command:Validate (isFinal)
2706 self._Validated = true
2707 local errorText = ""
2708 local success = true
2709
2710 for i, arg in pairs(self.Arguments) do
2711 local argSuccess, argErrorText = arg:Validate(isFinal)
2712
2713 if not argSuccess then
2714 success = false
2715 errorText = ("%s; #%d %s: %s"):format(errorText, i, arg.Name, argErrorText or "error")
2716 end
2717 end
2718
2719 return success, errorText:sub(3)
2720end
2721
2722--- Returns the last argument that has a value.
2723-- Useful for getting the autocomplete for the argument the user is working on.
2724function Command:GetLastArgument()
2725 for i = #self.Arguments, 1, -1 do
2726 if self.Arguments[i].RawValue then
2727 return self.Arguments[i]
2728 end
2729 end
2730end
2731
2732--- Returns a table containing the parsed values for all of the arguments.
2733function Command:GatherArgumentValues ()
2734 local values = {}
2735
2736 for i = 1, #self.ArgumentDefinitions do
2737 local arg = self.Arguments[i]
2738 if arg then
2739 values[i] = arg:GetValue()
2740 else
2741 values[i] = self.ArgumentDefinitions[i].Default
2742 end
2743 end
2744
2745 return values
2746end
2747
2748--- Runs the command. Handles dispatching to the server if necessary.
2749-- Command:Validate() must be called before this is called or it will throw.
2750function Command:Run ()
2751 if self._Validated == nil then
2752 error("Must validate a command before running.")
2753 end
2754
2755 local beforeRunHook = self.Dispatcher:RunHooks("BeforeRun", self)
2756 if beforeRunHook then
2757 return beforeRunHook
2758 end
2759
2760 if self.Object.Run then -- We can just Run it here on this machine
2761 self.Response = self.Object.Run(self, unpack(self:GatherArgumentValues()))
2762 elseif IsServer then -- Uh oh, we're already on the server and there's no Run function.
2763 warn(self.Name, "command has no implementation!")
2764 self.Response = "No implementation."
2765 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.
2766 self.Response = self.Dispatcher:Send(self.RawText, self.Object.Data and self.Object.Data(self))
2767 end
2768
2769 local afterRunHook = self.Dispatcher:RunHooks("AfterRun", self)
2770 if afterRunHook then
2771 return afterRunHook
2772 else
2773 return self.Response
2774 end
2775end
2776
2777--- Returns an ArgumentContext for the specific index
2778function Command:GetArgument (index)
2779 return self.Arguments[index]
2780end
2781
2782-- Below are functions that are only meant to be used in command implementations --
2783
2784--- Returns the extra data associated with this command.
2785-- This needs to be used instead of just context.Data for reliability when not using a remote command.
2786function Command:GetData ()
2787 if self.Data then
2788 return self.Data
2789 end
2790
2791 if self.Object.Data and not IsServer then
2792 self.Data = self.Object.Data(self)
2793 end
2794
2795 return self.Data
2796end
2797
2798--- Sends an event message to a player
2799function Command:SendEvent(player, event, ...)
2800 assert(typeof(player) == "Instance", "Argument #1 must be a Player")
2801 assert(player:IsA("Player"), "Argument #1 must be a Player")
2802 assert(type(event) == "string", "Argument #2 must be a string")
2803
2804 if IsServer then
2805 self.Dispatcher.Cmdr.RemoteEvent:FireClient(player, event, ...)
2806 elseif self.Dispatcher.Cmdr.Events[event] then
2807 assert(player == Players.LocalPlayer, "Event messages can only be sent to the local player on the client.")
2808 self.Dispatcher.Cmdr.Events[event](...)
2809 end
2810end
2811
2812--- Sends an event message to all players
2813function Command:BroadcastEvent(...)
2814 if not IsServer then
2815 error("Can't broadcast event messages from the client.", 2)
2816 end
2817
2818 self.Dispatcher.Cmdr.RemoteEvent:FireAllClients(...)
2819end
2820
2821--- Alias of self:SendEvent(self.Executor, "AddLine", text)
2822function Command:Reply(...)
2823 return self:SendEvent(self.Executor, "AddLine", ...)
2824end
2825
2826--- Alias of Registry:GetStore(...)
2827function Command:GetStore(...)
2828 return self.Dispatcher.Cmdr.Registry:GetStore(...)
2829end
2830
2831return Command
2832]]></ProtectedString>
2833 <BinaryString name="Tags"></BinaryString>
2834 </Properties>
2835 </Item>
2836 <Item class="ModuleScript" referent="RBX605C1FBC658B4EA8B3572B11FD05647A">
2837 <Properties>
2838 <Content name="LinkedSource"><null></null></Content>
2839 <string name="Name">Dispatcher</string>
2840 <string name="ScriptGuid">{7F66E598-3579-48FA-A96F-7321D43FDDEE}</string>
2841 <ProtectedString name="Source"><![CDATA[-- A fully extensible and type-safe admin-commands console
2842-- @documentation https://github.com/evaera/Cmdr/blob/master/README.md
2843-- @source https://github.com/evaera/Cmdr
2844-- @rostrap Cmdr
2845-- @author evaera
2846
2847local RunService = game:GetService("RunService")
2848local TeleportService = game:GetService("TeleportService")
2849local Players = game:GetService("Players")
2850local Util = require(script.Parent.Util)
2851local Command = require(script.Parent.Command)
2852
2853local HISTORY_SETTING_NAME = "CmdrCommandHistory"
2854
2855--- The dispatcher handles creating and running commands during the game.
2856local Dispatcher = {
2857 Cmdr = nil;
2858 Registry = nil;
2859
2860}
2861
2862--- Takes in raw command information and generates a command out of it.
2863-- text and executor are required arguments.
2864-- allowIncompleteData, when true, will ignore errors about arguments missing so we can parse live as the user types.
2865-- data is for special networked Data about the command gathered on the client. Purely Optional.
2866-- returns the command if successful, or (false, errorText) if not
2867function Dispatcher:Evaluate (text, executor, allowIncompleteArguments, data)
2868 if RunService:IsClient() == true and executor ~= Players.LocalPlayer then
2869 error("Can't evaluate a command that isn't sent by the local player.")
2870 end
2871
2872 local arguments = Util.SplitString(text)
2873 local commandName = table.remove(arguments, 1)
2874 local commandObject = self.Registry:GetCommand(commandName)
2875
2876 if commandObject then
2877 -- No need to continue splitting when there are no more arguments. We'll just mash any additional arguments into the last one.
2878 arguments = Util.MashExcessArguments(arguments, #commandObject.Args)
2879
2880 -- Create the CommandContext and parse it.
2881 local command = Command.new({
2882 Dispatcher = self,
2883 Text = text,
2884 CommandObject = commandObject,
2885 Alias = commandName,
2886 Executor = executor,
2887 Arguments = arguments,
2888 Data = data
2889 })
2890 local success, errorText = command:Parse(allowIncompleteArguments)
2891
2892 if success then
2893 return command
2894 else
2895 return false, errorText
2896 end
2897 else
2898 return false, "Invalid command. Use the help command to see all available commands."
2899 end
2900end
2901
2902--- A helper that evaluates and runs the command in one go.
2903-- Either returns any validation errors as a string, or the output of the command as a string. Definitely a string, though.
2904function Dispatcher:EvaluateAndRun (text, executor, options)
2905 executor = executor or Players.LocalPlayer
2906 options = options or {}
2907
2908 if RunService:IsClient() and options.IsHuman then
2909 self:PushHistory(text)
2910 end
2911
2912 local command, errorText = self:Evaluate(text, executor, nil, options.Data)
2913
2914 if not command then
2915 return errorText
2916 end
2917
2918 local ok, out = pcall(function()
2919 local valid, errorText = command:Validate(true) -- luacheck: ignore
2920
2921 if not valid then
2922 return errorText
2923 end
2924
2925 return command:Run() or "Command executed."
2926 end)
2927
2928 if not ok then
2929 warn(("Error occurred while evaluating command string %q\n%s"):format(text, out))
2930 end
2931
2932 return ok and out or "An error occurred while running this command."
2933end
2934
2935--- Send text as the local user to remote server to be evaluated there.
2936function Dispatcher:Send (text, data)
2937 if RunService:IsClient() == false then
2938 error("Dispatcher:Send can only be called from the client.")
2939 end
2940
2941 return self.Cmdr.RemoteFunction:InvokeServer(text, {
2942 Data = data
2943 })
2944end
2945
2946--- Invoke a command programmatically as the local user e.g. from a settings menu
2947-- Command should be the first argument, all arguments afterwards should be the arguments to the command.
2948function Dispatcher:Run (...)
2949 if not Players.LocalPlayer then
2950 error("Dispatcher:Run can only be called from the client.")
2951 end
2952
2953 local args = {...}
2954 local text = args[1]
2955
2956 for i = 2, #args do
2957 text = text .. " " .. tostring(args[i])
2958 end
2959
2960 local command, errorText = self:Evaluate(text, Players.LocalPlayer)
2961
2962 if not command then
2963 error(errorText) -- We do a full-on error here since this is code-invoked and they should know better.
2964 end
2965
2966 local success, errorText = command:Validate(true) -- luacheck: ignore
2967
2968 if not success then
2969 error(errorText)
2970 end
2971
2972 return command:Run()
2973end
2974
2975--- Runs hooks matching name and returns nil for ok or a string for cancellation
2976function Dispatcher:RunHooks(hookName, ...)
2977 if not self.Registry.Hooks[hookName] then
2978 error(("Invalid hook name: %q"):format(hookName), 2)
2979 end
2980
2981 for _, hook in ipairs(self.Registry.Hooks[hookName]) do
2982 local value = hook.callback(...)
2983
2984 if value ~= nil then
2985 return tostring(value)
2986 end
2987 end
2988end
2989
2990function Dispatcher:PushHistory(text)
2991 assert(RunService:IsClient(), "PushHistory may only be used from the client.")
2992
2993 local history = self:GetHistory()
2994
2995 -- Remove duplicates
2996 if Util.TrimString(text) == "" or text == history[#history] then
2997 return
2998 end
2999
3000 history[#history + 1] = text
3001
3002 TeleportService:SetTeleportSetting(HISTORY_SETTING_NAME, history)
3003end
3004
3005function Dispatcher:GetHistory()
3006 assert(RunService:IsClient(), "GetHistory may only be used from the client.")
3007
3008 return TeleportService:GetTeleportSetting(HISTORY_SETTING_NAME) or {}
3009end
3010
3011return function (cmdr)
3012 Dispatcher.Cmdr = cmdr
3013 Dispatcher.Registry = cmdr.Registry
3014
3015 return Dispatcher
3016end]]></ProtectedString>
3017 <BinaryString name="Tags"></BinaryString>
3018 </Properties>
3019 </Item>
3020 <Item class="ModuleScript" referent="RBXA013658904974BF49DB641C0A93175F4">
3021 <Properties>
3022 <Content name="LinkedSource"><null></null></Content>
3023 <string name="Name">Registry</string>
3024 <string name="ScriptGuid">{33D92E98-6382-4A3F-B9F7-7CDEC2031ED5}</string>
3025 <ProtectedString name="Source"><![CDATA[-- A fully extensible and type-safe admin-commands console
3026-- @documentation https://github.com/evaera/Cmdr/blob/master/README.md
3027-- @source https://github.com/evaera/Cmdr
3028-- @rostrap Cmdr
3029-- @author evaera
3030
3031local RunService = game:GetService("RunService")
3032
3033local Util = require(script.Parent.Util)
3034
3035--- The registry keeps track of all the commands and types that Cmdr knows about.
3036local Registry = {
3037 TypeMethods = Util.MakeDictionary({"Transform", "Validate", "Autocomplete", "Parse", "DisplayName", "Listable", "ValidateOnce"});
3038 CommandMethods = Util.MakeDictionary({"Name", "Aliases", "AutoExec", "Description", "Args", "Run", "Data", "Group"});
3039 CommandArgProps = Util.MakeDictionary({"Name", "Type", "Description", "Optional", "Default"});
3040 Types = {};
3041 Commands = {};
3042 CommandsArray = {};
3043 Cmdr = nil;
3044 Hooks = {
3045 BeforeRun = {};
3046 AfterRun = {}
3047 };
3048 Stores = setmetatable({}, {
3049 __index = function (self, k)
3050 self[k] = {}
3051 return self[k]
3052 end
3053 });
3054 AutoExecBuffer = {};
3055}
3056
3057--- Registers a type in the system.
3058-- name: The type Name. This must be unique.
3059function Registry:RegisterType (name, typeObject)
3060 if not name or not typeof(name) == "string" then
3061 error("Invalid type name provided: nil")
3062 end
3063
3064 if not name:find("^[%d%l]%w*$") then
3065 error(('Invalid type name provided: "%s", type names must be alphanumeric and start with a lower-case letter or a digit.'):format(name))
3066 end
3067
3068 for key in pairs(typeObject) do
3069 if self.TypeMethods[key] == nil then
3070 error("Unknown key/method in type \"" .. name .. "\": " .. key)
3071 end
3072 end
3073
3074 if self.Types[name] ~= nil then
3075 error(('Type "%s" has already been registered.'):format(name))
3076 end
3077
3078 typeObject.Name = name
3079 typeObject.DisplayName = typeObject.DisplayName or name
3080
3081 self.Types[name] = typeObject
3082end
3083
3084--- Helper method that registers types from all module scripts in a specific container.
3085function Registry:RegisterTypesIn (container)
3086 for _, object in pairs(container:GetChildren()) do
3087 if object:IsA("ModuleScript") then
3088 object.Parent = self.Cmdr.ReplicatedRoot.Types
3089
3090 require(object)(self)
3091 else
3092 self:RegisterTypesIn(object)
3093 end
3094 end
3095end
3096
3097-- These are exactly the same thing. No one will notice. Except for you, dear reader.
3098Registry.RegisterHooksIn = Registry.RegisterTypesIn
3099
3100--- Registers a command based purely on its definition.
3101-- Prefer using Registry:RegisterCommand for proper handling of server/client model.
3102function Registry:RegisterCommandObject (commandObject)
3103 for key in pairs(commandObject) do
3104 if self.CommandMethods[key] == nil then
3105 error("Unknown key/method in command " .. (commandObject.Name or "unknown command") .. ": " .. key)
3106 end
3107 end
3108
3109 if commandObject.Args then
3110 for i, arg in pairs(commandObject.Args) do
3111 for key in pairs(arg) do
3112 if self.CommandArgProps[key] == nil then
3113 error(('Unknown propery in command "%s" argument #%d: %s'):format(commandObject.Name or "unknown", i, key))
3114 end
3115 end
3116 end
3117 end
3118
3119 if RunService:IsClient() and commandObject.Data and commandObject.Run then
3120 error(('Invalid command implementation provided for "%s": "Data" and "Run" sections are mutually exclusive'):format(commandObject.Name or "unknown"))
3121 end
3122
3123 if commandObject.AutoExec and RunService:IsClient() then
3124 table.insert(self.AutoExecBuffer, commandObject.AutoExec)
3125 self:FlushAutoExecBufferDeferred()
3126 end
3127
3128 -- Unregister the old command if it exists...
3129 local oldCommand = self.Commands[commandObject.Name:lower()]
3130 if oldCommand and oldCommand.Aliases then
3131 for _, alias in pairs(oldCommand.Aliases) do
3132 self.Commands[alias:lower()] = nil
3133 end
3134 elseif not oldCommand then
3135 self.CommandsArray[#self.CommandsArray + 1] = commandObject
3136 end
3137
3138 self.Commands[commandObject.Name:lower()] = commandObject
3139
3140 if commandObject.Aliases then
3141 for _, alias in pairs(commandObject.Aliases) do
3142 self.Commands[alias:lower()] = commandObject
3143 end
3144 end
3145end
3146
3147--- Registers a command definition and its server equivalent.
3148-- Handles replicating the definition to the client.
3149function Registry:RegisterCommand (commandScript, commandServerScript, filter)
3150 local commandObject = require(commandScript)
3151
3152 if commandServerScript then
3153 commandObject.Run = require(commandServerScript)
3154 end
3155
3156 if filter and not filter(commandObject) then
3157 return
3158 end
3159
3160 self:RegisterCommandObject(commandObject)
3161
3162 commandScript.Parent = self.Cmdr.ReplicatedRoot.Commands
3163end
3164
3165--- A helper method that registers all commands inside a specific container.
3166function Registry:RegisterCommandsIn (container, filter)
3167 local skippedServerScripts = {}
3168 local usedServerScripts = {}
3169
3170 for _, commandScript in pairs(container:GetChildren()) do
3171 if commandScript:IsA("ModuleScript") then
3172 if not commandScript.Name:find("Server") then
3173 local serverCommandScript = container:FindFirstChild(commandScript.Name .. "Server")
3174
3175 if serverCommandScript then
3176 usedServerScripts[serverCommandScript] = true
3177 end
3178
3179 self:RegisterCommand(commandScript, serverCommandScript, filter)
3180 else
3181 skippedServerScripts[commandScript] = true
3182 end
3183 else
3184 self:RegisterCommandsIn(commandScript, filter)
3185 end
3186 end
3187
3188 for skippedScript in pairs(skippedServerScripts) do
3189 if not usedServerScripts[skippedScript] then
3190 warn("Command script " .. skippedScript.Name .. " was skipped because it has 'Server' in its name, and has no equivalent shared script.")
3191 end
3192 end
3193end
3194
3195--- Registers the default commands, with an optional filter function or array of groups.
3196function Registry:RegisterDefaultCommands (arrayOrFunc)
3197 local isArray = type(arrayOrFunc) == "table"
3198
3199 if isArray then
3200 arrayOrFunc = Util.MakeDictionary(arrayOrFunc)
3201 end
3202
3203 self:RegisterCommandsIn(self.Cmdr.DefaultCommandsFolder, isArray and function (command)
3204 return arrayOrFunc[command.Group] or false
3205 end or arrayOrFunc)
3206end
3207
3208--- Gets a command definition by name. (Can be an alias)
3209function Registry:GetCommand (name)
3210 name = name or ""
3211 return self.Commands[name:lower()]
3212end
3213
3214--- Returns a unique array of all registered commands (not including aliases)
3215function Registry:GetCommands ()
3216 return self.CommandsArray
3217end
3218
3219--- Returns an array of the names of all registered commands (not including aliases)
3220function Registry:GetCommandsAsStrings ()
3221 local commands = {}
3222
3223 for _, command in pairs(self.CommandsArray) do
3224 commands[#commands + 1] = command.Name
3225 end
3226
3227 return commands
3228end
3229
3230--- Gets a type definition by name.
3231function Registry:GetType (name)
3232 return self.Types[name]
3233end
3234
3235--- Adds a hook to be called when any command is run
3236function Registry:RegisterHook(hookName, callback, priority)
3237 if not self.Hooks[hookName] then
3238 error(("Invalid hook name: %q"):format(hookName), 2)
3239 end
3240
3241 table.insert(self.Hooks[hookName], { callback = callback; priority = priority or 0; } )
3242 table.sort(self.Hooks[hookName], function(a, b) return a.priority < b.priority end)
3243end
3244
3245-- Backwards compatability (deprecated)
3246Registry.AddHook = Registry.RegisterHook
3247
3248--- Returns the store with the given name
3249-- Used for commands that require persistent state, like bind or ban
3250function Registry:GetStore(name)
3251 return self.Stores[name]
3252end
3253
3254--- Calls self:FlushAutoExecBuffer at the end of the frame
3255function Registry:FlushAutoExecBufferDeferred()
3256 if self.AutoExecFlushConnection then
3257 return
3258 end
3259
3260 self.AutoExecFlushConnection = RunService.Heartbeat:Connect(function()
3261 self.AutoExecFlushConnection:Disconnect()
3262 self.AutoExecFlushConnection = nil
3263 self:FlushAutoExecBuffer()
3264 end)
3265end
3266
3267--- Runs all pending auto exec commands in Registry.AutoExecBuffer
3268function Registry:FlushAutoExecBuffer()
3269 for _, commandGroup in ipairs(self.AutoExecBuffer) do
3270 for _, command in ipairs(commandGroup) do
3271 self.Cmdr.Dispatcher:EvaluateAndRun(command)
3272 end
3273 end
3274end
3275
3276return function (cmdr)
3277 Registry.Cmdr = cmdr
3278
3279 return Registry
3280end
3281]]></ProtectedString>
3282 <BinaryString name="Tags"></BinaryString>
3283 </Properties>
3284 </Item>
3285 <Item class="ModuleScript" referent="RBXC7702180AA01435AA52F6BAFCD075B38">
3286 <Properties>
3287 <Content name="LinkedSource"><null></null></Content>
3288 <string name="Name">Util</string>
3289 <string name="ScriptGuid">{4CD0DF40-7153-4B7F-92BD-1697277A9BF8}</string>
3290 <ProtectedString name="Source"><![CDATA[-- A fully extensible and type-safe admin-commands console
3291-- @documentation https://github.com/evaera/Cmdr/blob/master/README.md
3292-- @source https://github.com/evaera/Cmdr
3293-- @rostrap Cmdr
3294-- @author evaera
3295
3296local TextService = game:GetService("TextService")
3297
3298local Util = {}
3299
3300--- Takes an array and flips its values into dictionary keys with value of true.
3301function Util.MakeDictionary(array)
3302 local dictionary = {}
3303
3304 for i = 1, #array do
3305 dictionary[array[i]] = true
3306 end
3307
3308 return dictionary
3309end
3310
3311-- Takes an array of instances and returns (array<names>, array<instances>)
3312local function transformInstanceSet(instances)
3313 local names = {}
3314
3315 for i = 1, #instances do
3316 names[i] = instances[i].Name
3317 end
3318
3319 return names, instances
3320end
3321
3322--- Returns a function that is a fuzzy finder for the specified set or container.
3323-- Can pass an array of strings, array of instances, array of EnumItems,
3324-- array of dictionaries with a Name key or an instance (in which case its children will be used)
3325-- Exact matches will be inserted in the front of the resulting array
3326function Util.MakeFuzzyFinder(setOrContainer)
3327 local names
3328 local instances = {}
3329
3330 if typeof(setOrContainer) == "Enum" then
3331 setOrContainer = setOrContainer:GetEnumItems()
3332 end
3333
3334 if typeof(setOrContainer) == "Instance" then
3335 names, instances = transformInstanceSet(setOrContainer:GetChildren())
3336 elseif typeof(setOrContainer) == "table" then
3337 if
3338 typeof(setOrContainer[1]) == "Instance" or typeof(setOrContainer[1]) == "EnumItem" or
3339 (typeof(setOrContainer[1]) == "table" and typeof(setOrContainer[1].Name) == "string")
3340 then
3341 names, instances = transformInstanceSet(setOrContainer)
3342 elseif type(setOrContainer[1]) == "string" then
3343 names = setOrContainer
3344 elseif setOrContainer[1] ~= nil then
3345 error("MakeFuzzyFinder only accepts tables of instances or strings.")
3346 else
3347 names = {}
3348 end
3349 else
3350 error("MakeFuzzyFinder only accepts a table, Enum, or Instance.")
3351 end
3352
3353 -- Searches the set (checking exact matches first)
3354 return function(text, returnFirst)
3355 local results = {}
3356
3357 for i, name in pairs(names) do
3358 local value = instances and instances[i] or name
3359
3360 -- Continue on checking for non-exact matches...
3361 -- Still need to loop through everything, even on returnFirst, because possibility of an exact match.
3362 if name:lower() == text:lower() then
3363 if returnFirst then
3364 return value
3365 else
3366 table.insert(results, 1, value)
3367 end
3368 elseif name:lower():sub(1, #text) == text:lower() then
3369 results[#results + 1] = value
3370 end
3371 end
3372
3373 if returnFirst then
3374 return results[1]
3375 end
3376
3377 return results
3378 end
3379end
3380
3381--- Takes an array of instances and returns an array of those instances' names.
3382function Util.GetNames(instances)
3383 local names = {}
3384
3385 for i = 1, #instances do
3386 names[i] = instances[i].Name or tostring(instances[i])
3387 end
3388
3389 return names
3390end
3391
3392--- Splits a string using a simple separator (no quote parsing)
3393function Util.SplitStringSimple(inputstr, sep)
3394 if sep == nil then
3395 sep = "%s"
3396 end
3397 local t = {}
3398 local i = 1
3399 for str in string.gmatch(inputstr, "([^" .. sep .. "]+)") do
3400 t[i] = str
3401 i = i + 1
3402 end
3403 return t
3404end
3405
3406local function charCode(n)
3407 return utf8.char(tonumber(n, 16))
3408end
3409
3410-- Discard any excess return values
3411local function first(x)
3412 return x
3413end
3414
3415--- Parses escape sequences into their fully qualified characters
3416function Util.ParseEscapeSequences(text)
3417 return text:gsub("\\(.)", {
3418 t = "\t";
3419 n = "\n";
3420 })
3421 :gsub("\\u(%x%x%x%x)", charCode)
3422 :gsub("\\x(%x%x)", charCode)
3423end
3424
3425local function encodeControlChars(text)
3426 return first(
3427 text
3428 :gsub("\\\\", string.char(17))
3429 :gsub("\\\"", string.char(18))
3430 :gsub("\\'", string.char(19))
3431 )
3432end
3433
3434local function decodeControlChars(text)
3435 return first(
3436 text
3437 :gsub(string.char(17), "\\")
3438 :gsub(string.char(18), "\"")
3439 :gsub(string.char(19), "'")
3440 )
3441end
3442
3443--- Splits a string by space but taking into account quoted sequences which will be treated as a single argument.
3444function Util.SplitString(text, max)
3445 text = encodeControlChars(text)
3446 max = max or math.huge
3447 local t = {}
3448 local spat, epat, buf, quoted = [=[^(['"])]=], [=[(['"])$]=]
3449 for str in text:gmatch("%S+") do
3450 str = Util.ParseEscapeSequences(str)
3451 local squoted = str:match(spat)
3452 local equoted = str:match(epat)
3453 local escaped = str:match([=[(\*)['"]$]=])
3454 if squoted and not quoted and not equoted then
3455 buf, quoted = str, squoted
3456 elseif buf and equoted == quoted and #escaped % 2 == 0 then
3457 str, buf, quoted = buf .. " " .. str, nil, nil
3458 elseif buf then
3459 buf = buf .. " " .. str
3460 end
3461 if not buf then
3462 t[#t + (#t > max and 0 or 1)] = decodeControlChars(str:gsub(spat, ""):gsub(epat, ""))
3463 end
3464 end
3465
3466 if buf then
3467 t[#t + (#t > max and 0 or 1)] = decodeControlChars(buf)
3468 end
3469
3470 return t
3471end
3472
3473--- Takes an array of arguments and a max value.
3474-- Any indicies past the max value will be appended to the last valid argument.
3475function Util.MashExcessArguments(arguments, max)
3476 local t = {}
3477 for i = 1, #arguments do
3478 if i > max then
3479 t[max] = ("%s %s"):format(t[max] or "", arguments[i])
3480 else
3481 t[i] = arguments[i]
3482 end
3483 end
3484 return t
3485end
3486
3487--- Trims whitespace from both sides of a string.
3488function Util.TrimString(s)
3489 return s:match "^%s*(.-)%s*$"
3490end
3491
3492--- Returns the text bounds size based on given text, label (from which properties will be pulled), and optional Vector2 container size.
3493function Util.GetTextSize(text, label, size)
3494 return TextService:GetTextSize(text, label.TextSize, label.Font, size or Vector2.new(label.AbsoluteSize.X, 0))
3495end
3496
3497--- Makes an Enum type.
3498function Util.MakeEnumType(name, values)
3499 local findValue = Util.MakeFuzzyFinder(values)
3500 return {
3501 Validate = function(text)
3502 return findValue(text, true) ~= nil, ("Value %q is not a valid %s."):format(text, name)
3503 end,
3504 Autocomplete = function(text)
3505 local list = findValue(text)
3506 return type(list[1]) ~= "string" and Util.GetNames(list) or list
3507 end,
3508 Parse = function(text)
3509 return findValue(text, true)
3510 end
3511 }
3512end
3513
3514--- Parses a prefixed union type argument (such as %Team)
3515function Util.ParsePrefixedUnionType(typeValue, rawValue)
3516 local split = Util.SplitStringSimple(typeValue)
3517
3518 -- Check prefixes in order from longest to shortest
3519 local types = {}
3520 for i = 1, #split, 2 do
3521 types[#types + 1] = {
3522 prefix = split[i - 1] or "",
3523 type = split[i]
3524 }
3525 end
3526
3527 table.sort(
3528 types,
3529 function(a, b)
3530 return #a.prefix > #b.prefix
3531 end
3532 )
3533
3534 for i = 1, #types do
3535 local t = types[i]
3536
3537 if rawValue:sub(1, #t.prefix) == t.prefix then
3538 return t.type, rawValue:sub(#t.prefix + 1), t.prefix
3539 end
3540 end
3541end
3542
3543--- Creates a listable type from a singlular type
3544function Util.MakeListableType(type)
3545 local listableType = {
3546 Listable = true,
3547 Transform = type.Transform,
3548 Validate = type.Validate,
3549 Autocomplete = type.Autocomplete,
3550 Parse = function(...)
3551 return {type.Parse(...)}
3552 end
3553 }
3554
3555 return listableType
3556end
3557
3558local function encodeCommandEscape(text)
3559 return first(text:gsub("\\%$", string.char(20)))
3560end
3561
3562local function decodeCommandEscape(text)
3563 return first(text:gsub(string.char(20), "$"))
3564end
3565
3566--- Runs embedded commands and replaces them
3567function Util.RunEmbeddedCommands(dispatcher, str)
3568 str = encodeCommandEscape(str)
3569
3570 local results = {}
3571 -- We need to do this because you can't yield in the gsub function
3572 for text in str:gmatch("$(%b{})") do
3573 local doQuotes = true
3574 local commandString = text:sub(2, #text-1)
3575
3576 if commandString:match("^{.+}$") then -- Allow double curly for literal replacement
3577 doQuotes = false
3578 commandString = commandString:sub(2, #commandString-1)
3579 end
3580
3581 results[text] = dispatcher:EvaluateAndRun(commandString)
3582
3583 if doQuotes and results[text]:find("%s") then
3584 results[text] = string.format("%q", results[text])
3585 end
3586 end
3587
3588 return decodeCommandEscape(str:gsub("$(%b{})", results))
3589end
3590
3591--- Replaces arguments in the format $1, $2, $something with whatever the
3592-- given function returns for it.
3593function Util.SubstituteArgs(str, replace)
3594 str = encodeCommandEscape(str)
3595 -- Convert numerical keys to strings
3596 if type(replace) == "table" then
3597 for i = 1, #replace do
3598 local k = tostring(i)
3599 replace[k] = replace[i]
3600
3601 if replace[k]:find("%s") then
3602 replace[k] = string.format("%q", replace[k])
3603 end
3604 end
3605 end
3606 return decodeCommandEscape(str:gsub("$(%w+)", replace))
3607end
3608
3609--- Creates an alias command
3610function Util.MakeAliasCommand(name, commandString)
3611 return {
3612 Name = name,
3613 Aliases = {},
3614 Description = commandString,
3615 Group = "UserAlias",
3616 Args = {
3617 {
3618 Type = "string",
3619 Name = "Argument 1",
3620 Description = "",
3621 Default = ""
3622 },
3623 {
3624 Type = "string",
3625 Name = "Argument 2",
3626 Description = "",
3627 Default = ""
3628 },
3629 {
3630 Type = "string",
3631 Name = "Argument 3",
3632 Description = "",
3633 Default = ""
3634 },
3635 {
3636 Type = "string",
3637 Name = "Argument 4",
3638 Description = "",
3639 Default = ""
3640 },
3641 {
3642 Type = "string",
3643 Name = "Argument 5",
3644 Description = "",
3645 Default = ""
3646 }
3647 },
3648 Run = function(context, ...)
3649 local args = {...}
3650 local commands = Util.SplitStringSimple(commandString, "&&")
3651
3652 for i, command in ipairs(commands) do
3653 local output = context.Dispatcher:EvaluateAndRun(
3654 Util.RunEmbeddedCommands(
3655 context.Dispatcher,
3656 Util.SubstituteArgs(command, args)
3657 )
3658 )
3659 if i == #commands then
3660 return output -- return last command's output
3661 else
3662 context:Reply(output)
3663 end
3664 end
3665
3666 return ""
3667 end
3668 }
3669end
3670
3671--- Makes a type that contains a sequence, e.g. Vector3 or Color3
3672function Util.MakeSequenceType(options)
3673 options = options or {}
3674
3675 assert(options.Parse ~= nil or options.Constructor ~= nil, "MakeSequenceType: Must provide one of: Constructor, Parse")
3676
3677 options.TransformEach = options.TransformEach or function(...)
3678 return ...
3679 end
3680
3681 options.ValidateEach = options.ValidateEach or function()
3682 return true
3683 end
3684
3685 return {
3686 Transform = function (text)
3687 return Util.Map(Util.SplitPrioritizedDelimeter(text, {",", "%s"}), function(value)
3688 return options.TransformEach(value)
3689 end)
3690 end;
3691
3692 Validate = function (components)
3693 if options.Length and #components > options.Length then
3694 return false, ("Maximum of %d values allowed in sequence"):format(options.Length)
3695 end
3696
3697 for i = 1, options.Length or #components do
3698 local valid, reason = options.ValidateEach(components[i], i)
3699
3700 if not valid then
3701 return false, reason
3702 end
3703 end
3704
3705 return true
3706 end;
3707
3708 Parse = options.Parse or function(components)
3709 return options.Constructor(unpack(components))
3710 end
3711 }
3712end
3713
3714--- Splits a string by a single delimeter chosen from the given set.
3715-- The first matching delimeter from the set becomes the split character.
3716function Util.SplitPrioritizedDelimeter(text, delimeters)
3717 for i, delimeter in ipairs(delimeters) do
3718 if text:find(delimeter) or i == #delimeters then
3719 return Util.SplitStringSimple(text, delimeter)
3720 end
3721 end
3722end
3723
3724--- Maps values of an array through a callback and returns an array of mapped values
3725function Util.Map(array, callback)
3726 local results = {}
3727
3728 for i, v in ipairs(array) do
3729 results[i] = callback(v, i)
3730 end
3731
3732 return results
3733end
3734
3735--- Maps arguments #2-n through callback and returns values as tuple
3736function Util.Each(callback, ...)
3737 local results = {}
3738 for i, value in ipairs({...}) do
3739 results[i] = callback(value)
3740 end
3741 return unpack(results)
3742end
3743
3744--- Emulates tabstops with spaces
3745function Util.EmulateTabstops(text, tabWidth)
3746 local result = ""
3747 for i = 1, #text do
3748 local char = text:sub(i, i)
3749
3750 result = result .. (char == "\t" and string.rep(" ", tabWidth - #result % tabWidth) or char)
3751 end
3752 return result
3753end
3754
3755return Util
3756]]></ProtectedString>
3757 <BinaryString name="Tags"></BinaryString>
3758 </Properties>
3759 </Item>
3760 </Item>
3761 <Item class="ModuleScript" referent="RBX896F91C3C40E46B4A1781B9D98F5FB1E">
3762 <Properties>
3763 <Content name="LinkedSource"><null></null></Content>
3764 <string name="Name">Loadstring</string>
3765 <string name="ScriptGuid">{162F5D71-14B8-4FFC-AEDD-E8D0D282AD31}</string>
3766 <ProtectedString name="Source"><![CDATA[--[[
3767 Credit to einsteinK.
3768 Credit to Stravant for LBI.
3769
3770 Credit to the creators of all the other modules used in this.
3771
3772 Sceleratis was here and decided modify some things.
3773
3774 einsteinK was here again to fix a bug in LBI for if-statements
3775--]]
3776
3777local waitDeps = {
3778 'Rerubi';
3779 'LuaK';
3780 'LuaP';
3781 'LuaU';
3782 'LuaX';
3783 'LuaY';
3784 'LuaZ';
3785}
3786
3787for i,v in pairs(waitDeps) do script:WaitForChild(v) end
3788
3789local luaX = require(script.LuaX)
3790local luaY = require(script.LuaY)
3791local luaZ = require(script.LuaZ)
3792local luaU = require(script.LuaU)
3793local rerubi = require(script.Rerubi)
3794
3795luaX:init()
3796local LuaState = {}
3797
3798getfenv().script = nil
3799
3800return function(str,env)
3801 local f,writer,buff,name
3802 local env = env or getfenv(2)
3803 local name = (env.script and env.script:GetFullName())
3804 local ran,error = pcall(function()
3805 local zio = luaZ:init(luaZ:make_getS(str), nil)
3806 if not zio then return error() end
3807 local func = luaY:parser(LuaState, zio, nil, name or "nil")
3808 writer, buff = luaU:make_setS()
3809 luaU:dump(LuaState, func, writer, buff)
3810 f = rerubi(buff.data, env)
3811 end)
3812
3813 if ran then
3814 return f,buff.data
3815 else
3816 return nil,error
3817 end
3818end]]></ProtectedString>
3819 <BinaryString name="Tags"></BinaryString>
3820 </Properties>
3821 <Item class="ModuleScript" referent="RBX139BCE3209D84EE097021EF03E3CD256">
3822 <Properties>
3823 <Content name="LinkedSource"><null></null></Content>
3824 <string name="Name">LuaZ</string>
3825 <string name="ScriptGuid">{6A425F9D-9F7A-417C-A445-E264422EEBDE}</string>
3826 <ProtectedString name="Source"><![CDATA[--[[--------------------------------------------------------------------
3827
3828 lzio.lua
3829 Lua buffered streams in Lua
3830 This file is part of Yueliang.
3831
3832 Copyright (c) 2005-2006 Kein-Hong Man <khman@users.sf.net>
3833 The COPYRIGHT file describes the conditions
3834 under which this software may be distributed.
3835
3836 See the ChangeLog for more information.
3837
3838----------------------------------------------------------------------]]
3839
3840--[[--------------------------------------------------------------------
3841-- Notes:
3842-- * EOZ is implemented as a string, "EOZ"
3843-- * Format of z structure (ZIO)
3844-- z.n -- bytes still unread
3845-- z.p -- last read position position in buffer
3846-- z.reader -- chunk reader function
3847-- z.data -- additional data
3848-- * Current position, p, is now last read index instead of a pointer
3849--
3850-- Not implemented:
3851-- * luaZ_lookahead: used only in lapi.c:lua_load to detect binary chunk
3852-- * luaZ_read: used only in lundump.c:ezread to read +1 bytes
3853-- * luaZ_openspace: dropped; let Lua handle buffers as strings (used in
3854-- lundump.c:LoadString & lvm.c:luaV_concat)
3855-- * luaZ buffer macros: dropped; buffers are handled as strings
3856-- * lauxlib.c:getF reader implementation has an extraline flag to
3857-- skip over a shbang (#!) line, this is not implemented here
3858--
3859-- Added:
3860-- (both of the following are vaguely adapted from lauxlib.c)
3861-- * luaZ:make_getS: create Reader from a string
3862-- * luaZ:make_getF: create Reader that reads from a file
3863--
3864-- Changed in 5.1.x:
3865-- * Chunkreader renamed to Reader (ditto with Chunkwriter)
3866-- * Zio struct: no more name string, added Lua state for reader
3867-- (however, Yueliang readers do not require a Lua state)
3868----------------------------------------------------------------------]]
3869
3870local luaZ = {}
3871
3872------------------------------------------------------------------------
3873-- * reader() should return a string, or nil if nothing else to parse.
3874-- Additional data can be set only during stream initialization
3875-- * Readers are handled in lauxlib.c, see luaL_load(file|buffer|string)
3876-- * LUAL_BUFFERSIZE=BUFSIZ=512 in make_getF() (located in luaconf.h)
3877-- * Original Reader typedef:
3878-- const char * (*lua_Reader) (lua_State *L, void *ud, size_t *sz);
3879-- * This Lua chunk reader implementation:
3880-- returns string or nil, no arguments to function
3881------------------------------------------------------------------------
3882
3883------------------------------------------------------------------------
3884-- create a chunk reader from a source string
3885------------------------------------------------------------------------
3886function luaZ:make_getS(buff)
3887 local b = buff
3888 return function() -- chunk reader anonymous function here
3889 if not b then return nil end
3890 local data = b
3891 b = nil
3892 return data
3893 end
3894end
3895
3896------------------------------------------------------------------------
3897-- create a chunk reader from a source file
3898------------------------------------------------------------------------
3899--[[
3900function luaZ:make_getF(filename)
3901 local LUAL_BUFFERSIZE = 512
3902 local h = io.open(filename, "r")
3903 if not h then return nil end
3904 return function() -- chunk reader anonymous function here
3905 if not h or io.type(h) == "closed file" then return nil end
3906 local buff = h:read(LUAL_BUFFERSIZE)
3907 if not buff then h:close(); h = nil end
3908 return buff
3909 end
3910end
3911--]]
3912------------------------------------------------------------------------
3913-- creates a zio input stream
3914-- returns the ZIO structure, z
3915------------------------------------------------------------------------
3916function luaZ:init(reader, data, name)
3917 if not reader then return end
3918 local z = {}
3919 z.reader = reader
3920 z.data = data or ""
3921 z.name = name
3922 -- set up additional data for reading
3923 if not data or data == "" then z.n = 0 else z.n = #data end
3924 z.p = 0
3925 return z
3926end
3927
3928------------------------------------------------------------------------
3929-- fill up input buffer
3930------------------------------------------------------------------------
3931function luaZ:fill(z)
3932 local buff = z.reader()
3933 z.data = buff
3934 if not buff or buff == "" then return "EOZ" end
3935 z.n, z.p = #buff - 1, 1
3936 return string.sub(buff, 1, 1)
3937end
3938
3939------------------------------------------------------------------------
3940-- get next character from the input stream
3941-- * local n, p are used to optimize code generation
3942------------------------------------------------------------------------
3943function luaZ:zgetc(z)
3944 local n, p = z.n, z.p + 1
3945 if n > 0 then
3946 z.n, z.p = n - 1, p
3947 return string.sub(z.data, p, p)
3948 else
3949 return self:fill(z)
3950 end
3951end
3952
3953return luaZ]]></ProtectedString>
3954 <BinaryString name="Tags"></BinaryString>
3955 </Properties>
3956 </Item>
3957 <Item class="ModuleScript" referent="RBX74ED26F683644E6283E6F69B9920D64A">
3958 <Properties>
3959 <Content name="LinkedSource"><null></null></Content>
3960 <string name="Name">LuaX</string>
3961 <string name="ScriptGuid">{9514EDB3-FB50-4B26-B929-88952220C9D9}</string>
3962 <ProtectedString name="Source"><![CDATA[--[[--------------------------------------------------------------------
3963
3964 llex.lua
3965 Lua lexical analyzer in Lua
3966 This file is part of Yueliang.
3967
3968 Copyright (c) 2005-2006 Kein-Hong Man <khman@users.sf.net>
3969 The COPYRIGHT file describes the conditions
3970 under which this software may be distributed.
3971
3972 See the ChangeLog for more information.
3973
3974----------------------------------------------------------------------]]
3975
3976--[[--------------------------------------------------------------------
3977-- Notes:
3978-- * intended to 'imitate' llex.c code; performance is not a concern
3979-- * tokens are strings; code structure largely retained
3980-- * deleted stuff (compared to llex.c) are noted, comments retained
3981-- * nextc() returns the currently read character to simplify coding
3982-- here; next() in llex.c does not return anything
3983-- * compatibility code is marked with "--#" comments
3984--
3985-- Added:
3986-- * luaX:chunkid (function luaO_chunkid from lobject.c)
3987-- * luaX:str2d (function luaO_str2d from lobject.c)
3988-- * luaX.LUA_QS used in luaX:lexerror (from luaconf.h)
3989-- * luaX.LUA_COMPAT_LSTR in luaX:read_long_string (from luaconf.h)
3990-- * luaX.MAX_INT used in luaX:inclinenumber (from llimits.h)
3991--
3992-- To use the lexer:
3993-- (1) luaX:init() to initialize the lexer
3994-- (2) luaX:setinput() to set the input stream to lex
3995-- (3) call luaX:next() or luaX:luaX:lookahead() to get tokens,
3996-- until "TK_EOS": luaX:next()
3997-- * since EOZ is returned as a string, be careful when regexp testing
3998--
3999-- Not implemented:
4000-- * luaX_newstring: not required by this Lua implementation
4001-- * buffer MAX_SIZET size limit (from llimits.h) test not implemented
4002-- in the interest of performance
4003-- * locale-aware number handling is largely redundant as Lua's
4004-- tonumber() function is already capable of this
4005--
4006-- Changed in 5.1.x:
4007-- * TK_NAME token order moved down
4008-- * string representation for TK_NAME, TK_NUMBER, TK_STRING changed
4009-- * token struct renamed to lower case (LS -> ls)
4010-- * LexState struct: removed nestlevel, added decpoint
4011-- * error message functions have been greatly simplified
4012-- * token2string renamed to luaX_tokens, exposed in llex.h
4013-- * lexer now handles all kinds of newlines, including CRLF
4014-- * shbang first line handling removed from luaX:setinput;
4015-- it is now done in lauxlib.c (luaL_loadfile)
4016-- * next(ls) macro renamed to nextc(ls) due to new luaX_next function
4017-- * EXTRABUFF and MAXNOCHECK removed due to lexer changes
4018-- * checkbuffer(ls, len) macro deleted
4019-- * luaX:read_numeral now has 3 support functions: luaX:trydecpoint,
4020-- luaX:buffreplace and (luaO_str2d from lobject.c) luaX:str2d
4021-- * luaX:read_numeral is now more promiscuous in slurping characters;
4022-- hexadecimal numbers was added, locale-aware decimal points too
4023-- * luaX:skip_sep is new; used by luaX:read_long_string
4024-- * luaX:read_long_string handles new-style long blocks, with some
4025-- optional compatibility code
4026-- * luaX:llex: parts changed to support new-style long blocks
4027-- * luaX:llex: readname functionality has been folded in
4028-- * luaX:llex: removed test for control characters
4029--
4030--------------------------------------------------------------------]]
4031
4032local luaZ = require(script.Parent.LuaZ)
4033
4034local luaX = {}
4035
4036-- FIRST_RESERVED is not required as tokens are manipulated as strings
4037-- TOKEN_LEN deleted; maximum length of a reserved word not needed
4038
4039------------------------------------------------------------------------
4040-- "ORDER RESERVED" deleted; enumeration in one place: luaX.RESERVED
4041------------------------------------------------------------------------
4042
4043-- terminal symbols denoted by reserved words: TK_AND to TK_WHILE
4044-- other terminal symbols: TK_NAME to TK_EOS
4045luaX.RESERVED = [[
4046TK_AND and
4047TK_BREAK break
4048TK_DO do
4049TK_ELSE else
4050TK_ELSEIF elseif
4051TK_END end
4052TK_FALSE false
4053TK_FOR for
4054TK_FUNCTION function
4055TK_IF if
4056TK_IN in
4057TK_LOCAL local
4058TK_NIL nil
4059TK_NOT not
4060TK_OR or
4061TK_REPEAT repeat
4062TK_RETURN return
4063TK_THEN then
4064TK_TRUE true
4065TK_UNTIL until
4066TK_WHILE while
4067TK_CONCAT ..
4068TK_DOTS ...
4069TK_EQ ==
4070TK_GE >=
4071TK_LE <=
4072TK_NE ~=
4073TK_NAME <name>
4074TK_NUMBER <number>
4075TK_STRING <string>
4076TK_EOS <eof>]]
4077
4078-- NUM_RESERVED is not required; number of reserved words
4079
4080--[[--------------------------------------------------------------------
4081-- Instead of passing seminfo, the Token struct (e.g. ls.t) is passed
4082-- so that lexer functions can use its table element, ls.t.seminfo
4083--
4084-- SemInfo (struct no longer needed, a mixed-type value is used)
4085--
4086-- Token (struct of ls.t and ls.lookahead):
4087-- token -- token symbol
4088-- seminfo -- semantics information
4089--
4090-- LexState (struct of ls; ls is initialized by luaX:setinput):
4091-- current -- current character (charint)
4092-- linenumber -- input line counter
4093-- lastline -- line of last token 'consumed'
4094-- t -- current token (table: struct Token)
4095-- lookahead -- look ahead token (table: struct Token)
4096-- fs -- 'FuncState' is private to the parser
4097-- L -- LuaState
4098-- z -- input stream
4099-- buff -- buffer for tokens
4100-- source -- current source name
4101-- decpoint -- locale decimal point
4102-- nestlevel -- level of nested non-terminals
4103----------------------------------------------------------------------]]
4104
4105-- luaX.tokens (was luaX_tokens) is now a hash; see luaX:init
4106
4107luaX.MAXSRC = 80
4108luaX.MAX_INT = 2147483645 -- constants from elsewhere (see above)
4109luaX.LUA_QS = "'%s'"
4110luaX.LUA_COMPAT_LSTR = 1
4111--luaX.MAX_SIZET = 4294967293
4112
4113------------------------------------------------------------------------
4114-- initialize lexer
4115-- * original luaX_init has code to create and register token strings
4116-- * luaX.tokens: TK_* -> token
4117-- * luaX.enums: token -> TK_* (used in luaX:llex)
4118------------------------------------------------------------------------
4119function luaX:init()
4120 local tokens, enums = {}, {}
4121 for v in string.gmatch(self.RESERVED, "[^\n]+") do
4122 local _, _, tok, str = string.find(v, "(%S+)%s+(%S+)")
4123 tokens[tok] = str
4124 enums[str] = tok
4125 end
4126 self.tokens = tokens
4127 self.enums = enums
4128end
4129
4130------------------------------------------------------------------------
4131-- returns a suitably-formatted chunk name or id
4132-- * from lobject.c, used in llex.c and ldebug.c
4133-- * the result, out, is returned (was first argument)
4134------------------------------------------------------------------------
4135function luaX:chunkid(source, bufflen)
4136 local out
4137 local first = string.sub(source, 1, 1)
4138 if first == "=" then
4139 out = string.sub(source, 2, bufflen) -- remove first char
4140 else -- out = "source", or "...source"
4141 if first == "@" then
4142 source = string.sub(source, 2) -- skip the '@'
4143 bufflen = bufflen - #" '...' "
4144 local l = #source
4145 out = ""
4146 if l > bufflen then
4147 source = string.sub(source, 1 + l - bufflen) -- get last part of file name
4148 out = out.."..."
4149 end
4150 out = out..source
4151 else -- out = [string "string"]
4152 local len = string.find(source, "[\n\r]") -- stop at first newline
4153 len = len and (len - 1) or #source
4154 bufflen = bufflen - #(" [string \"...\"] ")
4155 if len > bufflen then len = bufflen end
4156 out = "[string \""
4157 if len < #source then -- must truncate?
4158 out = out..string.sub(source, 1, len).."..."
4159 else
4160 out = out..source
4161 end
4162 out = out.."\"]"
4163 end
4164 end
4165 return out
4166end
4167
4168--[[--------------------------------------------------------------------
4169-- Support functions for lexer
4170-- * all lexer errors eventually reaches lexerror:
4171 syntaxerror -> lexerror
4172----------------------------------------------------------------------]]
4173
4174------------------------------------------------------------------------
4175-- look up token and return keyword if found (also called by parser)
4176------------------------------------------------------------------------
4177function luaX:token2str(ls, token)
4178 if string.sub(token, 1, 3) ~= "TK_" then
4179 if string.find(token, "%c") then
4180 return string.format("char(%d)", string.byte(token))
4181 end
4182 return token
4183 else
4184 end
4185 return self.tokens[token]
4186end
4187
4188------------------------------------------------------------------------
4189-- throws a lexer error
4190-- * txtToken has been made local to luaX:lexerror
4191-- * can't communicate LUA_ERRSYNTAX, so it is unimplemented
4192------------------------------------------------------------------------
4193function luaX:lexerror(ls, msg, token)
4194 local function txtToken(ls, token)
4195 if token == "TK_NAME" or
4196 token == "TK_STRING" or
4197 token == "TK_NUMBER" then
4198 return ls.buff
4199 else
4200 return self:token2str(ls, token)
4201 end
4202 end
4203 local buff = self:chunkid(ls.source, self.MAXSRC)
4204 local msg = string.format("%s:%d: %s", buff, ls.linenumber, msg)
4205 if token then
4206 msg = string.format("%s near "..self.LUA_QS, msg, txtToken(ls, token))
4207 end
4208 -- luaD_throw(ls->L, LUA_ERRSYNTAX)
4209 error(msg)
4210end
4211
4212------------------------------------------------------------------------
4213-- throws a syntax error (mainly called by parser)
4214-- * ls.t.token has to be set by the function calling luaX:llex
4215-- (see luaX:next and luaX:lookahead elsewhere in this file)
4216------------------------------------------------------------------------
4217function luaX:syntaxerror(ls, msg)
4218 self:lexerror(ls, msg, ls.t.token)
4219end
4220
4221------------------------------------------------------------------------
4222-- move on to next line
4223------------------------------------------------------------------------
4224function luaX:currIsNewline(ls)
4225 return ls.current == "\n" or ls.current == "\r"
4226end
4227
4228function luaX:inclinenumber(ls)
4229 local old = ls.current
4230 -- lua_assert(currIsNewline(ls))
4231 self:nextc(ls) -- skip '\n' or '\r'
4232 if self:currIsNewline(ls) and ls.current ~= old then
4233 self:nextc(ls) -- skip '\n\r' or '\r\n'
4234 end
4235 ls.linenumber = ls.linenumber + 1
4236 if ls.linenumber >= self.MAX_INT then
4237 self:syntaxerror(ls, "chunk has too many lines")
4238 end
4239end
4240
4241------------------------------------------------------------------------
4242-- initializes an input stream for lexing
4243-- * if ls (the lexer state) is passed as a table, then it is filled in,
4244-- otherwise it has to be retrieved as a return value
4245-- * LUA_MINBUFFER not used; buffer handling not required any more
4246------------------------------------------------------------------------
4247function luaX:setinput(L, ls, z, source)
4248 if not ls then ls = {} end -- create struct
4249 if not ls.lookahead then ls.lookahead = {} end
4250 if not ls.t then ls.t = {} end
4251 ls.decpoint = "."
4252 ls.L = L
4253 ls.lookahead.token = "TK_EOS" -- no look-ahead token
4254 ls.z = z
4255 ls.fs = nil
4256 ls.linenumber = 1
4257 ls.lastline = 1
4258 ls.source = source
4259 self:nextc(ls) -- read first char
4260end
4261
4262--[[--------------------------------------------------------------------
4263-- LEXICAL ANALYZER
4264----------------------------------------------------------------------]]
4265
4266------------------------------------------------------------------------
4267-- checks if current character read is found in the set 'set'
4268------------------------------------------------------------------------
4269function luaX:check_next(ls, set)
4270 if not string.find(set, ls.current, 1, 1) then
4271 return false
4272 end
4273 self:save_and_next(ls)
4274 return true
4275end
4276
4277------------------------------------------------------------------------
4278-- retrieve next token, checking the lookahead buffer if necessary
4279-- * note that the macro next(ls) in llex.c is now luaX:nextc
4280-- * utilized used in lparser.c (various places)
4281------------------------------------------------------------------------
4282function luaX:next(ls)
4283 ls.lastline = ls.linenumber
4284 if ls.lookahead.token ~= "TK_EOS" then -- is there a look-ahead token?
4285 -- this must be copy-by-value
4286 ls.t.seminfo = ls.lookahead.seminfo -- use this one
4287 ls.t.token = ls.lookahead.token
4288 ls.lookahead.token = "TK_EOS" -- and discharge it
4289 else
4290 ls.t.token = self:llex(ls, ls.t) -- read next token
4291 end
4292end
4293
4294------------------------------------------------------------------------
4295-- fill in the lookahead buffer
4296-- * utilized used in lparser.c:constructor
4297------------------------------------------------------------------------
4298function luaX:lookahead(ls)
4299 -- lua_assert(ls.lookahead.token == "TK_EOS")
4300 ls.lookahead.token = self:llex(ls, ls.lookahead)
4301end
4302
4303------------------------------------------------------------------------
4304-- gets the next character and returns it
4305-- * this is the next() macro in llex.c; see notes at the beginning
4306------------------------------------------------------------------------
4307function luaX:nextc(ls)
4308 local c = luaZ:zgetc(ls.z)
4309 ls.current = c
4310 return c
4311end
4312
4313------------------------------------------------------------------------
4314-- saves the given character into the token buffer
4315-- * buffer handling code removed, not used in this implementation
4316-- * test for maximum token buffer length not used, makes things faster
4317------------------------------------------------------------------------
4318
4319function luaX:save(ls, c)
4320 local buff = ls.buff
4321 -- if you want to use this, please uncomment luaX.MAX_SIZET further up
4322 --if #buff > self.MAX_SIZET then
4323 -- self:lexerror(ls, "lexical element too long")
4324 --end
4325 ls.buff = buff..c
4326end
4327
4328------------------------------------------------------------------------
4329-- save current character into token buffer, grabs next character
4330-- * like luaX:nextc, returns the character read for convenience
4331------------------------------------------------------------------------
4332function luaX:save_and_next(ls)
4333 self:save(ls, ls.current)
4334 return self:nextc(ls)
4335end
4336
4337------------------------------------------------------------------------
4338-- LUA_NUMBER
4339-- * luaX:read_numeral is the main lexer function to read a number
4340-- * luaX:str2d, luaX:buffreplace, luaX:trydecpoint are support functions
4341------------------------------------------------------------------------
4342
4343------------------------------------------------------------------------
4344-- string to number converter (was luaO_str2d from lobject.c)
4345-- * returns the number, nil if fails (originally returns a boolean)
4346-- * conversion function originally lua_str2number(s,p), a macro which
4347-- maps to the strtod() function by default (from luaconf.h)
4348------------------------------------------------------------------------
4349function luaX:str2d(s)
4350 local result = tonumber(s)
4351 if result then return result end
4352 -- conversion failed
4353 if string.lower(string.sub(s, 1, 2)) == "0x" then -- maybe an hexadecimal constant?
4354 result = tonumber(s, 16)
4355 if result then return result end -- most common case
4356 -- Was: invalid trailing characters?
4357 -- In C, this function then skips over trailing spaces.
4358 -- true is returned if nothing else is found except for spaces.
4359 -- If there is still something else, then it returns a false.
4360 -- All this is not necessary using Lua's tonumber.
4361 end
4362 return nil
4363end
4364
4365------------------------------------------------------------------------
4366-- single-character replacement, for locale-aware decimal points
4367------------------------------------------------------------------------
4368function luaX:buffreplace(ls, from, to)
4369 local result, buff = "", ls.buff
4370 for p = 1, #buff do
4371 local c = string.sub(buff, p, p)
4372 if c == from then c = to end
4373 result = result..c
4374 end
4375 ls.buff = result
4376end
4377
4378------------------------------------------------------------------------
4379-- Attempt to convert a number by translating '.' decimal points to
4380-- the decimal point character used by the current locale. This is not
4381-- needed in Yueliang as Lua's tonumber() is already locale-aware.
4382-- Instead, the code is here in case the user implements localeconv().
4383------------------------------------------------------------------------
4384function luaX:trydecpoint(ls, Token)
4385 -- format error: try to update decimal point separator
4386 local old = ls.decpoint
4387 -- translate the following to Lua if you implement localeconv():
4388 -- struct lconv *cv = localeconv();
4389 -- ls->decpoint = (cv ? cv->decimal_point[0] : '.');
4390 self:buffreplace(ls, old, ls.decpoint) -- try updated decimal separator
4391 local seminfo = self:str2d(ls.buff)
4392 Token.seminfo = seminfo
4393 if not seminfo then
4394 -- format error with correct decimal point: no more options
4395 self:buffreplace(ls, ls.decpoint, ".") -- undo change (for error message)
4396 self:lexerror(ls, "malformed number", "TK_NUMBER")
4397 end
4398end
4399
4400------------------------------------------------------------------------
4401-- main number conversion function
4402-- * "^%w$" needed in the scan in order to detect "EOZ"
4403------------------------------------------------------------------------
4404function luaX:read_numeral(ls, Token)
4405 -- lua_assert(string.find(ls.current, "%d"))
4406 repeat
4407 self:save_and_next(ls)
4408 until string.find(ls.current, "%D") and ls.current ~= "."
4409 if self:check_next(ls, "Ee") then -- 'E'?
4410 self:check_next(ls, "+-") -- optional exponent sign
4411 end
4412 while string.find(ls.current, "^%w$") or ls.current == "_" do
4413 self:save_and_next(ls)
4414 end
4415 self:buffreplace(ls, ".", ls.decpoint) -- follow locale for decimal point
4416 local seminfo = self:str2d(ls.buff)
4417 Token.seminfo = seminfo
4418 if not seminfo then -- format error?
4419 self:trydecpoint(ls, Token) -- try to update decimal point separator
4420 end
4421end
4422
4423------------------------------------------------------------------------
4424-- count separators ("=") in a long string delimiter
4425-- * used by luaX:read_long_string
4426------------------------------------------------------------------------
4427function luaX:skip_sep(ls)
4428 local count = 0
4429 local s = ls.current
4430 -- lua_assert(s == "[" or s == "]")
4431 self:save_and_next(ls)
4432 while ls.current == "=" do
4433 self:save_and_next(ls)
4434 count = count + 1
4435 end
4436 return (ls.current == s) and count or (-count) - 1
4437end
4438
4439------------------------------------------------------------------------
4440-- reads a long string or long comment
4441------------------------------------------------------------------------
4442function luaX:read_long_string(ls, Token, sep)
4443 local cont = 0
4444 self:save_and_next(ls) -- skip 2nd '['
4445 if self:currIsNewline(ls) then -- string starts with a newline?
4446 self:inclinenumber(ls) -- skip it
4447 end
4448 while true do
4449 local c = ls.current
4450 if c == "EOZ" then
4451 self:lexerror(ls, Token and "unfinished long string" or
4452 "unfinished long comment", "TK_EOS")
4453 elseif c == "[" then
4454 --# compatibility code start
4455 if self.LUA_COMPAT_LSTR then
4456 if self:skip_sep(ls) == sep then
4457 self:save_and_next(ls) -- skip 2nd '['
4458 cont = cont + 1
4459 --# compatibility code start
4460 if self.LUA_COMPAT_LSTR == 1 then
4461 if sep == 0 then
4462 self:lexerror(ls, "nesting of [[...]] is deprecated", "[")
4463 end
4464 end
4465 --# compatibility code end
4466 end
4467 end
4468 --# compatibility code end
4469 elseif c == "]" then
4470 if self:skip_sep(ls) == sep then
4471 self:save_and_next(ls) -- skip 2nd ']'
4472 --# compatibility code start
4473 if self.LUA_COMPAT_LSTR and self.LUA_COMPAT_LSTR == 2 then
4474 cont = cont - 1
4475 if sep == 0 and cont >= 0 then break end
4476 end
4477 --# compatibility code end
4478 break
4479 end
4480 elseif self:currIsNewline(ls) then
4481 self:save(ls, "\n")
4482 self:inclinenumber(ls)
4483 if not Token then ls.buff = "" end -- avoid wasting space
4484 else -- default
4485 if Token then
4486 self:save_and_next(ls)
4487 else
4488 self:nextc(ls)
4489 end
4490 end--if c
4491 end--while
4492 if Token then
4493 local p = 3 + sep
4494 Token.seminfo = string.sub(ls.buff, p, -p)
4495 end
4496end
4497
4498------------------------------------------------------------------------
4499-- reads a string
4500-- * has been restructured significantly compared to the original C code
4501------------------------------------------------------------------------
4502
4503function luaX:read_string(ls, del, Token)
4504 self:save_and_next(ls)
4505 while ls.current ~= del do
4506 local c = ls.current
4507 if c == "EOZ" then
4508 self:lexerror(ls, "unfinished string", "TK_EOS")
4509 elseif self:currIsNewline(ls) then
4510 self:lexerror(ls, "unfinished string", "TK_STRING")
4511 elseif c == "\\" then
4512 c = self:nextc(ls) -- do not save the '\'
4513 if self:currIsNewline(ls) then -- go through
4514 self:save(ls, "\n")
4515 self:inclinenumber(ls)
4516 elseif c ~= "EOZ" then -- will raise an error next loop
4517 -- escapes handling greatly simplified here:
4518 local i = string.find("abfnrtv", c, 1, 1)
4519 if i then
4520 self:save(ls, string.sub("\a\b\f\n\r\t\v", i, i))
4521 self:nextc(ls)
4522 elseif not string.find(c, "%d") then
4523 self:save_and_next(ls) -- handles \\, \", \', and \?
4524 else -- \xxx
4525 c, i = 0, 0
4526 repeat
4527 c = 10 * c + ls.current
4528 self:nextc(ls)
4529 i = i + 1
4530 until i >= 3 or not string.find(ls.current, "%d")
4531 if c > 255 then -- UCHAR_MAX
4532 self:lexerror(ls, "escape sequence too large", "TK_STRING")
4533 end
4534 self:save(ls, string.char(c))
4535 end
4536 end
4537 else
4538 self:save_and_next(ls)
4539 end--if c
4540 end--while
4541 self:save_and_next(ls) -- skip delimiter
4542 Token.seminfo = string.sub(ls.buff, 2, -2)
4543end
4544
4545------------------------------------------------------------------------
4546-- main lexer function
4547------------------------------------------------------------------------
4548function luaX:llex(ls, Token)
4549 ls.buff = ""
4550 while true do
4551 local c = ls.current
4552 ----------------------------------------------------------------
4553 if self:currIsNewline(ls) then
4554 self:inclinenumber(ls)
4555 ----------------------------------------------------------------
4556 elseif c == "-" then
4557 c = self:nextc(ls)
4558 if c ~= "-" then return "-" end
4559 -- else is a comment
4560 local sep = -1
4561 if self:nextc(ls) == '[' then
4562 sep = self:skip_sep(ls)
4563 ls.buff = "" -- 'skip_sep' may dirty the buffer
4564 end
4565 if sep >= 0 then
4566 self:read_long_string(ls, nil, sep) -- long comment
4567 ls.buff = ""
4568 else -- else short comment
4569 while not self:currIsNewline(ls) and ls.current ~= "EOZ" do
4570 self:nextc(ls)
4571 end
4572 end
4573 ----------------------------------------------------------------
4574 elseif c == "[" then
4575 local sep = self:skip_sep(ls)
4576 if sep >= 0 then
4577 self:read_long_string(ls, Token, sep)
4578 return "TK_STRING"
4579 elseif sep == -1 then
4580 return "["
4581 else
4582 self:lexerror(ls, "invalid long string delimiter", "TK_STRING")
4583 end
4584 ----------------------------------------------------------------
4585 elseif c == "=" then
4586 c = self:nextc(ls)
4587 if c ~= "=" then return "="
4588 else self:nextc(ls); return "TK_EQ" end
4589 ----------------------------------------------------------------
4590 elseif c == "<" then
4591 c = self:nextc(ls)
4592 if c ~= "=" then return "<"
4593 else self:nextc(ls); return "TK_LE" end
4594 ----------------------------------------------------------------
4595 elseif c == ">" then
4596 c = self:nextc(ls)
4597 if c ~= "=" then return ">"
4598 else self:nextc(ls); return "TK_GE" end
4599 ----------------------------------------------------------------
4600 elseif c == "~" then
4601 c = self:nextc(ls)
4602 if c ~= "=" then return "~"
4603 else self:nextc(ls); return "TK_NE" end
4604 ----------------------------------------------------------------
4605 elseif c == "\"" or c == "'" then
4606 self:read_string(ls, c, Token)
4607 return "TK_STRING"
4608 ----------------------------------------------------------------
4609 elseif c == "." then
4610 c = self:save_and_next(ls)
4611 if self:check_next(ls, ".") then
4612 if self:check_next(ls, ".") then
4613 return "TK_DOTS" -- ...
4614 else return "TK_CONCAT" -- ..
4615 end
4616 elseif not string.find(c, "%d") then
4617 return "."
4618 else
4619 self:read_numeral(ls, Token)
4620 return "TK_NUMBER"
4621 end
4622 ----------------------------------------------------------------
4623 elseif c == "EOZ" then
4624 return "TK_EOS"
4625 ----------------------------------------------------------------
4626 else -- default
4627 if string.find(c, "%s") then
4628 -- lua_assert(self:currIsNewline(ls))
4629 self:nextc(ls)
4630 elseif string.find(c, "%d") then
4631 self:read_numeral(ls, Token)
4632 return "TK_NUMBER"
4633 elseif string.find(c, "[_%a]") then
4634 -- identifier or reserved word
4635 repeat
4636 c = self:save_and_next(ls)
4637 until c == "EOZ" or not string.find(c, "[_%w]")
4638 local ts = ls.buff
4639 local tok = self.enums[ts]
4640 if tok then return tok end -- reserved word?
4641 Token.seminfo = ts
4642 return "TK_NAME"
4643 else
4644 self:nextc(ls)
4645 return c -- single-char tokens (+ - / ...)
4646 end
4647 ----------------------------------------------------------------
4648 end--if c
4649 end--while
4650end
4651
4652return luaX]]></ProtectedString>
4653 <BinaryString name="Tags"></BinaryString>
4654 </Properties>
4655 </Item>
4656 <Item class="ModuleScript" referent="RBX0C77EABDF4E04495A61E17E0F3651AB2">
4657 <Properties>
4658 <Content name="LinkedSource"><null></null></Content>
4659 <string name="Name">LuaY</string>
4660 <string name="ScriptGuid">{29BEDDC6-A318-4D8E-9DAB-C65576A9430E}</string>
4661 <ProtectedString name="Source"><![CDATA[--[[--------------------------------------------------------------------
4662
4663 lparser.lua
4664 Lua 5 parser in Lua
4665 This file is part of Yueliang.
4666
4667 Copyright (c) 2005-2007 Kein-Hong Man <khman@users.sf.net>
4668 The COPYRIGHT file describes the conditions
4669 under which this software may be distributed.
4670
4671 See the ChangeLog for more information.
4672
4673----------------------------------------------------------------------]]
4674
4675--[[--------------------------------------------------------------------
4676-- Notes:
4677-- * some unused C code that were not converted are kept as comments
4678-- * LUA_COMPAT_VARARG option changed into a comment block
4679-- * for value/size specific code added, look for 'NOTE: '
4680--
4681-- Not implemented:
4682-- * luaX_newstring not needed by this Lua implementation
4683-- * luaG_checkcode() in assert is not currently implemented
4684--
4685-- Added:
4686-- * some constants added from various header files
4687-- * luaY.LUA_QS used in error_expected, check_match (from luaconf.h)
4688-- * luaY:LUA_QL needed for error messages (from luaconf.h)
4689-- * luaY:growvector (from lmem.h) -- skeleton only, limit checking
4690-- * luaY.SHRT_MAX (from <limits.h>) for registerlocalvar
4691-- * luaY:newproto (from lfunc.c)
4692-- * luaY:int2fb (from lobject.c)
4693-- * NOTE: HASARG_MASK, for implementing a VARARG_HASARG bit operation
4694-- * NOTE: value-specific code for VARARG_NEEDSARG to replace a bitop
4695--
4696-- Changed in 5.1.x:
4697-- * various code changes are not detailed...
4698-- * names of constants may have changed, e.g. added a LUAI_ prefix
4699-- * struct expkind: added VKNUM, VVARARG; VCALL's info changed?
4700-- * struct expdesc: added nval
4701-- * struct FuncState: upvalues data type changed to upvaldesc
4702-- * macro hasmultret is new
4703-- * function checklimit moved to parser from lexer
4704-- * functions anchor_token, errorlimit, checknext are new
4705-- * checknext is new, equivalent to 5.0.x's check, see check too
4706-- * luaY:next and luaY:lookahead moved to lexer
4707-- * break keyword no longer skipped in luaY:breakstat
4708-- * function new_localvarstr replaced by new_localvarliteral
4709-- * registerlocalvar limits local variables to SHRT_MAX
4710-- * create_local deleted, new_localvarliteral used instead
4711-- * constant LUAI_MAXUPVALUES increased to 60
4712-- * constants MAXPARAMS, LUA_MAXPARSERLEVEL, MAXSTACK removed
4713-- * function interface changed: singlevaraux, singlevar
4714-- * enterlevel and leavelevel uses nCcalls to track call depth
4715-- * added a name argument to main entry function, luaY:parser
4716-- * function luaY_index changed to yindex
4717-- * luaY:int2fb()'s table size encoding format has been changed
4718-- * luaY:log2() no longer needed for table constructors
4719-- * function code_params deleted, functionality folded in parlist
4720-- * vararg flags handling (is_vararg) changes; also see VARARG_*
4721-- * LUA_COMPATUPSYNTAX section for old-style upvalues removed
4722-- * repeatstat() calls chunk() instead of block()
4723-- * function interface changed: cond, test_then_block
4724-- * while statement implementation considerably simplified; MAXEXPWHILE
4725-- and EXTRAEXP no longer required, no limits to the complexity of a
4726-- while condition
4727-- * repeat, forbody statement implementation has major changes,
4728-- mostly due to new scoping behaviour of local variables
4729-- * OPR_MULT renamed to OPR_MUL
4730----------------------------------------------------------------------]]
4731
4732--requires luaP, luaX, luaK
4733local luaY = {}
4734local luaX = require(script.Parent.LuaX)
4735local luaK = require(script.Parent.LuaK)(luaY)
4736local luaP = require(script.Parent.LuaP)
4737
4738--[[--------------------------------------------------------------------
4739-- Expression descriptor
4740-- * expkind changed to string constants; luaY:assignment was the only
4741-- function to use a relational operator with this enumeration
4742-- VVOID -- no value
4743-- VNIL -- no value
4744-- VTRUE -- no value
4745-- VFALSE -- no value
4746-- VK -- info = index of constant in 'k'
4747-- VKNUM -- nval = numerical value
4748-- VLOCAL -- info = local register
4749-- VUPVAL, -- info = index of upvalue in 'upvalues'
4750-- VGLOBAL -- info = index of table; aux = index of global name in 'k'
4751-- VINDEXED -- info = table register; aux = index register (or 'k')
4752-- VJMP -- info = instruction pc
4753-- VRELOCABLE -- info = instruction pc
4754-- VNONRELOC -- info = result register
4755-- VCALL -- info = instruction pc
4756-- VVARARG -- info = instruction pc
4757} ----------------------------------------------------------------------]]
4758
4759--[[--------------------------------------------------------------------
4760-- * expdesc in Lua 5.1.x has a union u and another struct s; this Lua
4761-- implementation ignores all instances of u and s usage
4762-- struct expdesc:
4763-- k -- (enum: expkind)
4764-- info, aux -- (int, int)
4765-- nval -- (lua_Number)
4766-- t -- patch list of 'exit when true'
4767-- f -- patch list of 'exit when false'
4768----------------------------------------------------------------------]]
4769
4770--[[--------------------------------------------------------------------
4771-- struct upvaldesc:
4772-- k -- (lu_byte)
4773-- info -- (lu_byte)
4774----------------------------------------------------------------------]]
4775
4776--[[--------------------------------------------------------------------
4777-- state needed to generate code for a given function
4778-- struct FuncState:
4779-- f -- current function header (table: Proto)
4780-- h -- table to find (and reuse) elements in 'k' (table: Table)
4781-- prev -- enclosing function (table: FuncState)
4782-- ls -- lexical state (table: LexState)
4783-- L -- copy of the Lua state (table: lua_State)
4784-- bl -- chain of current blocks (table: BlockCnt)
4785-- pc -- next position to code (equivalent to 'ncode')
4786-- lasttarget -- 'pc' of last 'jump target'
4787-- jpc -- list of pending jumps to 'pc'
4788-- freereg -- first free register
4789-- nk -- number of elements in 'k'
4790-- np -- number of elements in 'p'
4791-- nlocvars -- number of elements in 'locvars'
4792-- nactvar -- number of active local variables
4793-- upvalues[LUAI_MAXUPVALUES] -- upvalues (table: upvaldesc)
4794-- actvar[LUAI_MAXVARS] -- declared-variable stack
4795----------------------------------------------------------------------]]
4796
4797------------------------------------------------------------------------
4798-- constants used by parser
4799-- * picks up duplicate values from luaX if required
4800------------------------------------------------------------------------
4801
4802luaY.LUA_QS = luaX.LUA_QS or "'%s'" -- (from luaconf.h)
4803
4804luaY.SHRT_MAX = 32767 -- (from <limits.h>)
4805luaY.LUAI_MAXVARS = 200 -- (luaconf.h)
4806luaY.LUAI_MAXUPVALUES = 60 -- (luaconf.h)
4807luaY.MAX_INT = luaX.MAX_INT or 2147483645 -- (from llimits.h)
4808 -- * INT_MAX-2 for 32-bit systems
4809luaY.LUAI_MAXCCALLS = 200 -- (from luaconf.h)
4810
4811luaY.VARARG_HASARG = 1 -- (from lobject.h)
4812-- NOTE: HASARG_MASK is value-specific
4813luaY.HASARG_MASK = 2 -- this was added for a bitop in parlist()
4814luaY.VARARG_ISVARARG = 2
4815-- NOTE: there is some value-specific code that involves VARARG_NEEDSARG
4816luaY.VARARG_NEEDSARG = 4
4817
4818luaY.LUA_MULTRET = -1 -- (lua.h)
4819
4820--[[--------------------------------------------------------------------
4821-- other functions
4822----------------------------------------------------------------------]]
4823
4824------------------------------------------------------------------------
4825-- LUA_QL describes how error messages quote program elements.
4826-- CHANGE it if you want a different appearance. (from luaconf.h)
4827------------------------------------------------------------------------
4828function luaY:LUA_QL(x)
4829 return "'"..x.."'"
4830end
4831
4832------------------------------------------------------------------------
4833-- this is a stripped-down luaM_growvector (from lmem.h) which is a
4834-- macro based on luaM_growaux (in lmem.c); all the following does is
4835-- reproduce the size limit checking logic of the original function
4836-- so that error behaviour is identical; all arguments preserved for
4837-- convenience, even those which are unused
4838-- * set the t field to nil, since this originally does a sizeof(t)
4839-- * size (originally a pointer) is never updated, their final values
4840-- are set by luaY:close_func(), so overall things should still work
4841------------------------------------------------------------------------
4842function luaY:growvector(L, v, nelems, size, t, limit, e)
4843 if nelems >= limit then
4844 error(e) -- was luaG_runerror
4845 end
4846end
4847
4848------------------------------------------------------------------------
4849-- initialize a new function prototype structure (from lfunc.c)
4850-- * used only in open_func()
4851------------------------------------------------------------------------
4852function luaY:newproto(L)
4853 local f = {} -- Proto
4854 -- luaC_link(L, obj2gco(f), LUA_TPROTO); /* GC */
4855 f.k = {}
4856 f.sizek = 0
4857 f.p = {}
4858 f.sizep = 0
4859 f.code = {}
4860 f.sizecode = 0
4861 f.sizelineinfo = 0
4862 f.sizeupvalues = 0
4863 f.nups = 0
4864 f.upvalues = {}
4865 f.numparams = 0
4866 f.is_vararg = 0
4867 f.maxstacksize = 0
4868 f.lineinfo = {}
4869 f.sizelocvars = 0
4870 f.locvars = {}
4871 f.lineDefined = 0
4872 f.lastlinedefined = 0
4873 f.source = nil
4874 return f
4875end
4876
4877------------------------------------------------------------------------
4878-- converts an integer to a "floating point byte", represented as
4879-- (eeeeexxx), where the real value is (1xxx) * 2^(eeeee - 1) if
4880-- eeeee != 0 and (xxx) otherwise.
4881------------------------------------------------------------------------
4882function luaY:int2fb(x)
4883 local e = 0 -- exponent
4884 while x >= 16 do
4885 x = math.floor((x + 1) / 2)
4886 e = e + 1
4887 end
4888 if x < 8 then
4889 return x
4890 else
4891 return ((e + 1) * 8) + (x - 8)
4892 end
4893end
4894
4895--[[--------------------------------------------------------------------
4896-- parser functions
4897----------------------------------------------------------------------]]
4898
4899------------------------------------------------------------------------
4900-- true of the kind of expression produces multiple return values
4901------------------------------------------------------------------------
4902function luaY:hasmultret(k)
4903 return k == "VCALL" or k == "VVARARG"
4904end
4905
4906------------------------------------------------------------------------
4907-- convenience function to access active local i, returns entry
4908------------------------------------------------------------------------
4909function luaY:getlocvar(fs, i)
4910 return fs.f.locvars[ fs.actvar[i] ]
4911end
4912
4913------------------------------------------------------------------------
4914-- check a limit, string m provided as an error message
4915------------------------------------------------------------------------
4916function luaY:checklimit(fs, v, l, m)
4917 if v > l then self:errorlimit(fs, l, m) end
4918end
4919
4920--[[--------------------------------------------------------------------
4921-- nodes for block list (list of active blocks)
4922-- struct BlockCnt:
4923-- previous -- chain (table: BlockCnt)
4924-- breaklist -- list of jumps out of this loop
4925-- nactvar -- # active local variables outside the breakable structure
4926-- upval -- true if some variable in the block is an upvalue (boolean)
4927-- isbreakable -- true if 'block' is a loop (boolean)
4928----------------------------------------------------------------------]]
4929
4930------------------------------------------------------------------------
4931-- prototypes for recursive non-terminal functions
4932------------------------------------------------------------------------
4933-- prototypes deleted; not required in Lua
4934
4935------------------------------------------------------------------------
4936-- reanchor if last token is has a constant string, see close_func()
4937-- * used only in close_func()
4938------------------------------------------------------------------------
4939function luaY:anchor_token(ls)
4940 if ls.t.token == "TK_NAME" or ls.t.token == "TK_STRING" then
4941 -- not relevant to Lua implementation of parser
4942 -- local ts = ls.t.seminfo
4943 -- luaX_newstring(ls, getstr(ts), ts->tsv.len); /* C */
4944 end
4945end
4946
4947------------------------------------------------------------------------
4948-- throws a syntax error if token expected is not there
4949------------------------------------------------------------------------
4950function luaY:error_expected(ls, token)
4951 luaX:syntaxerror(ls,
4952 string.format(self.LUA_QS.." expected", luaX:token2str(ls, token)))
4953end
4954
4955------------------------------------------------------------------------
4956-- prepares error message for display, for limits exceeded
4957-- * used only in checklimit()
4958------------------------------------------------------------------------
4959function luaY:errorlimit(fs, limit, what)
4960 local msg = (fs.f.linedefined == 0) and
4961 string.format("main function has more than %d %s", limit, what) or
4962 string.format("function at line %d has more than %d %s",
4963 fs.f.linedefined, limit, what)
4964 luaX:lexerror(fs.ls, msg, 0)
4965end
4966
4967------------------------------------------------------------------------
4968-- tests for a token, returns outcome
4969-- * return value changed to boolean
4970------------------------------------------------------------------------
4971function luaY:testnext(ls, c)
4972 if ls.t.token == c then
4973 luaX:next(ls)
4974 return true
4975 else
4976 return false
4977 end
4978end
4979
4980------------------------------------------------------------------------
4981-- check for existence of a token, throws error if not found
4982------------------------------------------------------------------------
4983function luaY:check(ls, c)
4984 if ls.t.token ~= c then
4985 self:error_expected(ls, c)
4986 end
4987end
4988
4989------------------------------------------------------------------------
4990-- verify existence of a token, then skip it
4991------------------------------------------------------------------------
4992function luaY:checknext(ls, c)
4993 self:check(ls, c)
4994 luaX:next(ls)
4995end
4996
4997------------------------------------------------------------------------
4998-- throws error if condition not matched
4999------------------------------------------------------------------------
5000function luaY:check_condition(ls, c, msg)
5001 if not c then luaX:syntaxerror(ls, msg) end
5002end
5003
5004------------------------------------------------------------------------
5005-- verifies token conditions are met or else throw error
5006------------------------------------------------------------------------
5007function luaY:check_match(ls, what, who, where)
5008 if not self:testnext(ls, what) then
5009 if where == ls.linenumber then
5010 self:error_expected(ls, what)
5011 else
5012 luaX:syntaxerror(ls, string.format(
5013 self.LUA_QS.." expected (to close "..self.LUA_QS.." at line %d)",
5014 luaX:token2str(ls, what), luaX:token2str(ls, who), where))
5015 end
5016 end
5017end
5018
5019------------------------------------------------------------------------
5020-- expect that token is a name, return the name
5021------------------------------------------------------------------------
5022function luaY:str_checkname(ls)
5023 self:check(ls, "TK_NAME")
5024 local ts = ls.t.seminfo
5025 luaX:next(ls)
5026 return ts
5027end
5028
5029------------------------------------------------------------------------
5030-- initialize a struct expdesc, expression description data structure
5031------------------------------------------------------------------------
5032function luaY:init_exp(e, k, i)
5033 e.f, e.t = luaK.NO_JUMP, luaK.NO_JUMP
5034 e.k = k
5035 e.info = i
5036end
5037
5038------------------------------------------------------------------------
5039-- adds given string s in string pool, sets e as VK
5040------------------------------------------------------------------------
5041function luaY:codestring(ls, e, s)
5042 self:init_exp(e, "VK", luaK:stringK(ls.fs, s))
5043end
5044
5045------------------------------------------------------------------------
5046-- consume a name token, adds it to string pool, sets e as VK
5047------------------------------------------------------------------------
5048function luaY:checkname(ls, e)
5049 self:codestring(ls, e, self:str_checkname(ls))
5050end
5051
5052------------------------------------------------------------------------
5053-- creates struct entry for a local variable
5054-- * used only in new_localvar()
5055------------------------------------------------------------------------
5056function luaY:registerlocalvar(ls, varname)
5057 local fs = ls.fs
5058 local f = fs.f
5059 self:growvector(ls.L, f.locvars, fs.nlocvars, f.sizelocvars,
5060 nil, self.SHRT_MAX, "too many local variables")
5061 -- loop to initialize empty f.locvar positions not required
5062 f.locvars[fs.nlocvars] = {} -- LocVar
5063 f.locvars[fs.nlocvars].varname = varname
5064 -- luaC_objbarrier(ls.L, f, varname) /* GC */
5065 local nlocvars = fs.nlocvars
5066 fs.nlocvars = fs.nlocvars + 1
5067 return nlocvars
5068end
5069
5070------------------------------------------------------------------------
5071-- creates a new local variable given a name and an offset from nactvar
5072-- * used in fornum(), forlist(), parlist(), body()
5073------------------------------------------------------------------------
5074function luaY:new_localvarliteral(ls, v, n)
5075 self:new_localvar(ls, v, n)
5076end
5077
5078------------------------------------------------------------------------
5079-- register a local variable, set in active variable list
5080------------------------------------------------------------------------
5081function luaY:new_localvar(ls, name, n)
5082 local fs = ls.fs
5083 self:checklimit(fs, fs.nactvar + n + 1, self.LUAI_MAXVARS, "local variables")
5084 fs.actvar[fs.nactvar + n] = self:registerlocalvar(ls, name)
5085end
5086
5087------------------------------------------------------------------------
5088-- adds nvars number of new local variables, set debug information
5089------------------------------------------------------------------------
5090function luaY:adjustlocalvars(ls, nvars)
5091 local fs = ls.fs
5092 fs.nactvar = fs.nactvar + nvars
5093 for i = nvars, 1, -1 do
5094 self:getlocvar(fs, fs.nactvar - i).startpc = fs.pc
5095 end
5096end
5097
5098------------------------------------------------------------------------
5099-- removes a number of locals, set debug information
5100------------------------------------------------------------------------
5101function luaY:removevars(ls, tolevel)
5102 local fs = ls.fs
5103 while fs.nactvar > tolevel do
5104 fs.nactvar = fs.nactvar - 1
5105 self:getlocvar(fs, fs.nactvar).endpc = fs.pc
5106 end
5107end
5108
5109------------------------------------------------------------------------
5110-- returns an existing upvalue index based on the given name, or
5111-- creates a new upvalue struct entry and returns the new index
5112-- * used only in singlevaraux()
5113------------------------------------------------------------------------
5114function luaY:indexupvalue(fs, name, v)
5115 local f = fs.f
5116 for i = 0, f.nups - 1 do
5117 if fs.upvalues[i].k == v.k and fs.upvalues[i].info == v.info then
5118 assert(f.upvalues[i] == name)
5119 return i
5120 end
5121 end
5122 -- new one
5123 self:checklimit(fs, f.nups + 1, self.LUAI_MAXUPVALUES, "upvalues")
5124 self:growvector(fs.L, f.upvalues, f.nups, f.sizeupvalues,
5125 nil, self.MAX_INT, "")
5126 -- loop to initialize empty f.upvalues positions not required
5127 f.upvalues[f.nups] = name
5128 -- luaC_objbarrier(fs->L, f, name); /* GC */
5129 assert(v.k == "VLOCAL" or v.k == "VUPVAL")
5130 -- this is a partial copy; only k & info fields used
5131 fs.upvalues[f.nups] = { k = v.k, info = v.info }
5132 local nups = f.nups
5133 f.nups = f.nups + 1
5134 return nups
5135end
5136
5137------------------------------------------------------------------------
5138-- search the local variable namespace of the given fs for a match
5139-- * used only in singlevaraux()
5140------------------------------------------------------------------------
5141function luaY:searchvar(fs, n)
5142 for i = fs.nactvar - 1, 0, -1 do
5143 if n == self:getlocvar(fs, i).varname then
5144 return i
5145 end
5146 end
5147 return -1 -- not found
5148end
5149
5150------------------------------------------------------------------------
5151-- * mark upvalue flags in function states up to a given level
5152-- * used only in singlevaraux()
5153------------------------------------------------------------------------
5154function luaY:markupval(fs, level)
5155 local bl = fs.bl
5156 while bl and bl.nactvar > level do bl = bl.previous end
5157 if bl then bl.upval = true end
5158end
5159
5160------------------------------------------------------------------------
5161-- handle locals, globals and upvalues and related processing
5162-- * search mechanism is recursive, calls itself to search parents
5163-- * used only in singlevar()
5164------------------------------------------------------------------------
5165function luaY:singlevaraux(fs, n, var, base)
5166 if fs == nil then -- no more levels?
5167 self:init_exp(var, "VGLOBAL", luaP.NO_REG) -- default is global variable
5168 return "VGLOBAL"
5169 else
5170 local v = self:searchvar(fs, n) -- look up at current level
5171 if v >= 0 then
5172 self:init_exp(var, "VLOCAL", v)
5173 if base == 0 then
5174 self:markupval(fs, v) -- local will be used as an upval
5175 end
5176 return "VLOCAL"
5177 else -- not found at current level; try upper one
5178 if self:singlevaraux(fs.prev, n, var, 0) == "VGLOBAL" then
5179 return "VGLOBAL"
5180 end
5181 var.info = self:indexupvalue(fs, n, var) -- else was LOCAL or UPVAL
5182 var.k = "VUPVAL" -- upvalue in this level
5183 return "VUPVAL"
5184 end--if v
5185 end--if fs
5186end
5187
5188------------------------------------------------------------------------
5189-- consume a name token, creates a variable (global|local|upvalue)
5190-- * used in prefixexp(), funcname()
5191------------------------------------------------------------------------
5192function luaY:singlevar(ls, var)
5193 local varname = self:str_checkname(ls)
5194 local fs = ls.fs
5195 if self:singlevaraux(fs, varname, var, 1) == "VGLOBAL" then
5196 var.info = luaK:stringK(fs, varname) -- info points to global name
5197 end
5198end
5199
5200------------------------------------------------------------------------
5201-- adjust RHS to match LHS in an assignment
5202-- * used in assignment(), forlist(), localstat()
5203------------------------------------------------------------------------
5204function luaY:adjust_assign(ls, nvars, nexps, e)
5205 local fs = ls.fs
5206 local extra = nvars - nexps
5207 if self:hasmultret(e.k) then
5208 extra = extra + 1 -- includes call itself
5209 if extra <= 0 then extra = 0 end
5210 luaK:setreturns(fs, e, extra) -- last exp. provides the difference
5211 if extra > 1 then luaK:reserveregs(fs, extra - 1) end
5212 else
5213 if e.k ~= "VVOID" then luaK:exp2nextreg(fs, e) end -- close last expression
5214 if extra > 0 then
5215 local reg = fs.freereg
5216 luaK:reserveregs(fs, extra)
5217 luaK:_nil(fs, reg, extra)
5218 end
5219 end
5220end
5221
5222------------------------------------------------------------------------
5223-- tracks and limits parsing depth, assert check at end of parsing
5224------------------------------------------------------------------------
5225function luaY:enterlevel(ls)
5226 ls.L.nCcalls = ls.L.nCcalls + 1
5227 if ls.L.nCcalls > self.LUAI_MAXCCALLS then
5228 luaX:lexerror(ls, "chunk has too many syntax levels", 0)
5229 end
5230end
5231
5232------------------------------------------------------------------------
5233-- tracks parsing depth, a pair with luaY:enterlevel()
5234------------------------------------------------------------------------
5235function luaY:leavelevel(ls)
5236 ls.L.nCcalls = ls.L.nCcalls - 1
5237end
5238
5239------------------------------------------------------------------------
5240-- enters a code unit, initializes elements
5241------------------------------------------------------------------------
5242function luaY:enterblock(fs, bl, isbreakable)
5243 bl.breaklist = luaK.NO_JUMP
5244 bl.isbreakable = isbreakable
5245 bl.nactvar = fs.nactvar
5246 bl.upval = false
5247 bl.previous = fs.bl
5248 fs.bl = bl
5249 assert(fs.freereg == fs.nactvar)
5250end
5251
5252------------------------------------------------------------------------
5253-- leaves a code unit, close any upvalues
5254------------------------------------------------------------------------
5255function luaY:leaveblock(fs)
5256 local bl = fs.bl
5257 fs.bl = bl.previous
5258 self:removevars(fs.ls, bl.nactvar)
5259 if bl.upval then
5260 luaK:codeABC(fs, "OP_CLOSE", bl.nactvar, 0, 0)
5261 end
5262 -- a block either controls scope or breaks (never both)
5263 assert(not bl.isbreakable or not bl.upval)
5264 assert(bl.nactvar == fs.nactvar)
5265 fs.freereg = fs.nactvar -- free registers
5266 luaK:patchtohere(fs, bl.breaklist)
5267end
5268
5269------------------------------------------------------------------------
5270-- implement the instantiation of a function prototype, append list of
5271-- upvalues after the instantiation instruction
5272-- * used only in body()
5273------------------------------------------------------------------------
5274function luaY:pushclosure(ls, func, v)
5275 local fs = ls.fs
5276 local f = fs.f
5277 self:growvector(ls.L, f.p, fs.np, f.sizep, nil,
5278 luaP.MAXARG_Bx, "constant table overflow")
5279 -- loop to initialize empty f.p positions not required
5280 f.p[fs.np] = func.f
5281 fs.np = fs.np + 1
5282 -- luaC_objbarrier(ls->L, f, func->f); /* C */
5283 self:init_exp(v, "VRELOCABLE", luaK:codeABx(fs, "OP_CLOSURE", 0, fs.np - 1))
5284 for i = 0, func.f.nups - 1 do
5285 local o = (func.upvalues[i].k == "VLOCAL") and "OP_MOVE" or "OP_GETUPVAL"
5286 luaK:codeABC(fs, o, 0, func.upvalues[i].info, 0)
5287 end
5288end
5289
5290------------------------------------------------------------------------
5291-- opening of a function
5292------------------------------------------------------------------------
5293function luaY:open_func(ls, fs)
5294 local L = ls.L
5295 local f = self:newproto(ls.L)
5296 fs.f = f
5297 fs.prev = ls.fs -- linked list of funcstates
5298 fs.ls = ls
5299 fs.L = L
5300 ls.fs = fs
5301 fs.pc = 0
5302 fs.lasttarget = -1
5303 fs.jpc = luaK.NO_JUMP
5304 fs.freereg = 0
5305 fs.nk = 0
5306 fs.np = 0
5307 fs.nlocvars = 0
5308 fs.nactvar = 0
5309 fs.bl = nil
5310 f.source = ls.source
5311 f.maxstacksize = 2 -- registers 0/1 are always valid
5312 fs.h = {} -- constant table; was luaH_new call
5313 -- anchor table of constants and prototype (to avoid being collected)
5314 -- sethvalue2s(L, L->top, fs->h); incr_top(L); /* C */
5315 -- setptvalue2s(L, L->top, f); incr_top(L);
5316end
5317
5318------------------------------------------------------------------------
5319-- closing of a function
5320------------------------------------------------------------------------
5321function luaY:close_func(ls)
5322 local L = ls.L
5323 local fs = ls.fs
5324 local f = fs.f
5325 self:removevars(ls, 0)
5326 luaK:ret(fs, 0, 0) -- final return
5327 -- luaM_reallocvector deleted for f->code, f->lineinfo, f->k, f->p,
5328 -- f->locvars, f->upvalues; not required for Lua table arrays
5329 f.sizecode = fs.pc
5330 f.sizelineinfo = fs.pc
5331 f.sizek = fs.nk
5332 f.sizep = fs.np
5333 f.sizelocvars = fs.nlocvars
5334 f.sizeupvalues = f.nups
5335 --assert(luaG_checkcode(f)) -- currently not implemented
5336 assert(fs.bl == nil)
5337 ls.fs = fs.prev
5338 -- the following is not required for this implementation; kept here
5339 -- for completeness
5340 -- L->top -= 2; /* remove table and prototype from the stack */
5341 -- last token read was anchored in defunct function; must reanchor it
5342 if fs then self:anchor_token(ls) end
5343end
5344
5345------------------------------------------------------------------------
5346-- parser initialization function
5347-- * note additional sub-tables needed for LexState, FuncState
5348------------------------------------------------------------------------
5349function luaY:parser(L, z, buff, name)
5350 local lexstate = {} -- LexState
5351 lexstate.t = {}
5352 lexstate.lookahead = {}
5353 local funcstate = {} -- FuncState
5354 funcstate.upvalues = {}
5355 funcstate.actvar = {}
5356 -- the following nCcalls initialization added for convenience
5357 L.nCcalls = 0
5358 lexstate.buff = buff
5359 luaX:setinput(L, lexstate, z, name)
5360 self:open_func(lexstate, funcstate)
5361 funcstate.f.is_vararg = self.VARARG_ISVARARG -- main func. is always vararg
5362 luaX:next(lexstate) -- read first token
5363 self:chunk(lexstate)
5364 self:check(lexstate, "TK_EOS")
5365 self:close_func(lexstate)
5366 assert(funcstate.prev == nil)
5367 assert(funcstate.f.nups == 0)
5368 assert(lexstate.fs == nil)
5369 return funcstate.f
5370end
5371
5372--[[--------------------------------------------------------------------
5373-- GRAMMAR RULES
5374----------------------------------------------------------------------]]
5375
5376------------------------------------------------------------------------
5377-- parse a function name suffix, for function call specifications
5378-- * used in primaryexp(), funcname()
5379------------------------------------------------------------------------
5380function luaY:field(ls, v)
5381 -- field -> ['.' | ':'] NAME
5382 local fs = ls.fs
5383 local key = {} -- expdesc
5384 luaK:exp2anyreg(fs, v)
5385 luaX:next(ls) -- skip the dot or colon
5386 self:checkname(ls, key)
5387 luaK:indexed(fs, v, key)
5388end
5389
5390------------------------------------------------------------------------
5391-- parse a table indexing suffix, for constructors, expressions
5392-- * used in recfield(), primaryexp()
5393------------------------------------------------------------------------
5394function luaY:yindex(ls, v)
5395 -- index -> '[' expr ']'
5396 luaX:next(ls) -- skip the '['
5397 self:expr(ls, v)
5398 luaK:exp2val(ls.fs, v)
5399 self:checknext(ls, "]")
5400end
5401
5402--[[--------------------------------------------------------------------
5403-- Rules for Constructors
5404----------------------------------------------------------------------]]
5405
5406--[[--------------------------------------------------------------------
5407-- struct ConsControl:
5408-- v -- last list item read (table: struct expdesc)
5409-- t -- table descriptor (table: struct expdesc)
5410-- nh -- total number of 'record' elements
5411-- na -- total number of array elements
5412-- tostore -- number of array elements pending to be stored
5413----------------------------------------------------------------------]]
5414
5415------------------------------------------------------------------------
5416-- parse a table record (hash) field
5417-- * used in constructor()
5418------------------------------------------------------------------------
5419function luaY:recfield(ls, cc)
5420 -- recfield -> (NAME | '['exp1']') = exp1
5421 local fs = ls.fs
5422 local reg = ls.fs.freereg
5423 local key, val = {}, {} -- expdesc
5424 if ls.t.token == "TK_NAME" then
5425 self:checklimit(fs, cc.nh, self.MAX_INT, "items in a constructor")
5426 self:checkname(ls, key)
5427 else -- ls->t.token == '['
5428 self:yindex(ls, key)
5429 end
5430 cc.nh = cc.nh + 1
5431 self:checknext(ls, "=")
5432 local rkkey = luaK:exp2RK(fs, key)
5433 self:expr(ls, val)
5434 luaK:codeABC(fs, "OP_SETTABLE", cc.t.info, rkkey, luaK:exp2RK(fs, val))
5435 fs.freereg = reg -- free registers
5436end
5437
5438------------------------------------------------------------------------
5439-- emit a set list instruction if enough elements (LFIELDS_PER_FLUSH)
5440-- * used in constructor()
5441------------------------------------------------------------------------
5442function luaY:closelistfield(fs, cc)
5443 if cc.v.k == "VVOID" then return end -- there is no list item
5444 luaK:exp2nextreg(fs, cc.v)
5445 cc.v.k = "VVOID"
5446 if cc.tostore == luaP.LFIELDS_PER_FLUSH then
5447 luaK:setlist(fs, cc.t.info, cc.na, cc.tostore) -- flush
5448 cc.tostore = 0 -- no more items pending
5449 end
5450end
5451
5452------------------------------------------------------------------------
5453-- emit a set list instruction at the end of parsing list constructor
5454-- * used in constructor()
5455------------------------------------------------------------------------
5456function luaY:lastlistfield(fs, cc)
5457 if cc.tostore == 0 then return end
5458 if self:hasmultret(cc.v.k) then
5459 luaK:setmultret(fs, cc.v)
5460 luaK:setlist(fs, cc.t.info, cc.na, self.LUA_MULTRET)
5461 cc.na = cc.na - 1 -- do not count last expression (unknown number of elements)
5462 else
5463 if cc.v.k ~= "VVOID" then
5464 luaK:exp2nextreg(fs, cc.v)
5465 end
5466 luaK:setlist(fs, cc.t.info, cc.na, cc.tostore)
5467 end
5468end
5469
5470------------------------------------------------------------------------
5471-- parse a table list (array) field
5472-- * used in constructor()
5473------------------------------------------------------------------------
5474function luaY:listfield(ls, cc)
5475 self:expr(ls, cc.v)
5476 self:checklimit(ls.fs, cc.na, self.MAX_INT, "items in a constructor")
5477 cc.na = cc.na + 1
5478 cc.tostore = cc.tostore + 1
5479end
5480
5481------------------------------------------------------------------------
5482-- parse a table constructor
5483-- * used in funcargs(), simpleexp()
5484------------------------------------------------------------------------
5485function luaY:constructor(ls, t)
5486 -- constructor -> '{' [ field { fieldsep field } [ fieldsep ] ] '}'
5487 -- field -> recfield | listfield
5488 -- fieldsep -> ',' | ';'
5489 local fs = ls.fs
5490 local line = ls.linenumber
5491 local pc = luaK:codeABC(fs, "OP_NEWTABLE", 0, 0, 0)
5492 local cc = {} -- ConsControl
5493 cc.v = {}
5494 cc.na, cc.nh, cc.tostore = 0, 0, 0
5495 cc.t = t
5496 self:init_exp(t, "VRELOCABLE", pc)
5497 self:init_exp(cc.v, "VVOID", 0) -- no value (yet)
5498 luaK:exp2nextreg(ls.fs, t) -- fix it at stack top (for gc)
5499 self:checknext(ls, "{")
5500 repeat
5501 assert(cc.v.k == "VVOID" or cc.tostore > 0)
5502 if ls.t.token == "}" then break end
5503 self:closelistfield(fs, cc)
5504 local c = ls.t.token
5505
5506 if c == "TK_NAME" then -- may be listfields or recfields
5507 luaX:lookahead(ls)
5508 if ls.lookahead.token ~= "=" then -- expression?
5509 self:listfield(ls, cc)
5510 else
5511 self:recfield(ls, cc)
5512 end
5513 elseif c == "[" then -- constructor_item -> recfield
5514 self:recfield(ls, cc)
5515 else -- constructor_part -> listfield
5516 self:listfield(ls, cc)
5517 end
5518 until not self:testnext(ls, ",") and not self:testnext(ls, ";")
5519 self:check_match(ls, "}", "{", line)
5520 self:lastlistfield(fs, cc)
5521 luaP:SETARG_B(fs.f.code[pc], self:int2fb(cc.na)) -- set initial array size
5522 luaP:SETARG_C(fs.f.code[pc], self:int2fb(cc.nh)) -- set initial table size
5523end
5524
5525-- }======================================================================
5526
5527------------------------------------------------------------------------
5528-- parse the arguments (parameters) of a function declaration
5529-- * used in body()
5530------------------------------------------------------------------------
5531function luaY:parlist(ls)
5532 -- parlist -> [ param { ',' param } ]
5533 local fs = ls.fs
5534 local f = fs.f
5535 local nparams = 0
5536 f.is_vararg = 0
5537 if ls.t.token ~= ")" then -- is 'parlist' not empty?
5538 repeat
5539 local c = ls.t.token
5540 if c == "TK_NAME" then -- param -> NAME
5541 self:new_localvar(ls, self:str_checkname(ls), nparams)
5542 nparams = nparams + 1
5543 elseif c == "TK_DOTS" then -- param -> `...'
5544 luaX:next(ls)
5545-- [[
5546-- #if defined(LUA_COMPAT_VARARG)
5547 -- use `arg' as default name
5548 self:new_localvarliteral(ls, "arg", nparams)
5549 nparams = nparams + 1
5550 f.is_vararg = self.VARARG_HASARG + self.VARARG_NEEDSARG
5551-- #endif
5552--]]
5553 f.is_vararg = f.is_vararg + self.VARARG_ISVARARG
5554 else
5555 luaX:syntaxerror(ls, "<name> or "..self:LUA_QL("...").." expected")
5556 end
5557 until f.is_vararg ~= 0 or not self:testnext(ls, ",")
5558 end--if
5559 self:adjustlocalvars(ls, nparams)
5560 -- NOTE: the following works only when HASARG_MASK is 2!
5561 f.numparams = fs.nactvar - (f.is_vararg % self.HASARG_MASK)
5562 luaK:reserveregs(fs, fs.nactvar) -- reserve register for parameters
5563end
5564
5565------------------------------------------------------------------------
5566-- parse function declaration body
5567-- * used in simpleexp(), localfunc(), funcstat()
5568------------------------------------------------------------------------
5569function luaY:body(ls, e, needself, line)
5570 -- body -> '(' parlist ')' chunk END
5571 local new_fs = {} -- FuncState
5572 new_fs.upvalues = {}
5573 new_fs.actvar = {}
5574 self:open_func(ls, new_fs)
5575 new_fs.f.lineDefined = line
5576 self:checknext(ls, "(")
5577 if needself then
5578 self:new_localvarliteral(ls, "self", 0)
5579 self:adjustlocalvars(ls, 1)
5580 end
5581 self:parlist(ls)
5582 self:checknext(ls, ")")
5583 self:chunk(ls)
5584 new_fs.f.lastlinedefined = ls.linenumber
5585 self:check_match(ls, "TK_END", "TK_FUNCTION", line)
5586 self:close_func(ls)
5587 self:pushclosure(ls, new_fs, e)
5588end
5589
5590------------------------------------------------------------------------
5591-- parse a list of comma-separated expressions
5592-- * used is multiple locations
5593------------------------------------------------------------------------
5594function luaY:explist1(ls, v)
5595 -- explist1 -> expr { ',' expr }
5596 local n = 1 -- at least one expression
5597 self:expr(ls, v)
5598 while self:testnext(ls, ",") do
5599 luaK:exp2nextreg(ls.fs, v)
5600 self:expr(ls, v)
5601 n = n + 1
5602 end
5603 return n
5604end
5605
5606------------------------------------------------------------------------
5607-- parse the parameters of a function call
5608-- * contrast with parlist(), used in function declarations
5609-- * used in primaryexp()
5610------------------------------------------------------------------------
5611function luaY:funcargs(ls, f)
5612 local fs = ls.fs
5613 local args = {} -- expdesc
5614 local nparams
5615 local line = ls.linenumber
5616 local c = ls.t.token
5617 if c == "(" then -- funcargs -> '(' [ explist1 ] ')'
5618 if line ~= ls.lastline then
5619 luaX:syntaxerror(ls, "ambiguous syntax (function call x new statement)")
5620 end
5621 luaX:next(ls)
5622 if ls.t.token == ")" then -- arg list is empty?
5623 args.k = "VVOID"
5624 else
5625 self:explist1(ls, args)
5626 luaK:setmultret(fs, args)
5627 end
5628 self:check_match(ls, ")", "(", line)
5629 elseif c == "{" then -- funcargs -> constructor
5630 self:constructor(ls, args)
5631 elseif c == "TK_STRING" then -- funcargs -> STRING
5632 self:codestring(ls, args, ls.t.seminfo)
5633 luaX:next(ls) -- must use 'seminfo' before 'next'
5634 else
5635 luaX:syntaxerror(ls, "function arguments expected")
5636 return
5637 end
5638 assert(f.k == "VNONRELOC")
5639 local base = f.info -- base register for call
5640 if self:hasmultret(args.k) then
5641 nparams = self.LUA_MULTRET -- open call
5642 else
5643 if args.k ~= "VVOID" then
5644 luaK:exp2nextreg(fs, args) -- close last argument
5645 end
5646 nparams = fs.freereg - (base + 1)
5647 end
5648 self:init_exp(f, "VCALL", luaK:codeABC(fs, "OP_CALL", base, nparams + 1, 2))
5649 luaK:fixline(fs, line)
5650 fs.freereg = base + 1 -- call remove function and arguments and leaves
5651 -- (unless changed) one result
5652end
5653
5654--[[--------------------------------------------------------------------
5655-- Expression parsing
5656----------------------------------------------------------------------]]
5657
5658------------------------------------------------------------------------
5659-- parses an expression in parentheses or a single variable
5660-- * used in primaryexp()
5661------------------------------------------------------------------------
5662function luaY:prefixexp(ls, v)
5663 -- prefixexp -> NAME | '(' expr ')'
5664 local c = ls.t.token
5665 if c == "(" then
5666 local line = ls.linenumber
5667 luaX:next(ls)
5668 self:expr(ls, v)
5669 self:check_match(ls, ")", "(", line)
5670 luaK:dischargevars(ls.fs, v)
5671 elseif c == "TK_NAME" then
5672 self:singlevar(ls, v)
5673 else
5674 luaX:syntaxerror(ls, "unexpected symbol")
5675 end--if c
5676 return
5677end
5678
5679------------------------------------------------------------------------
5680-- parses a prefixexp (an expression in parentheses or a single variable)
5681-- or a function call specification
5682-- * used in simpleexp(), assignment(), exprstat()
5683------------------------------------------------------------------------
5684function luaY:primaryexp(ls, v)
5685 -- primaryexp ->
5686 -- prefixexp { '.' NAME | '[' exp ']' | ':' NAME funcargs | funcargs }
5687 local fs = ls.fs
5688 self:prefixexp(ls, v)
5689 while true do
5690 local c = ls.t.token
5691 if c == "." then -- field
5692 self:field(ls, v)
5693 elseif c == "[" then -- '[' exp1 ']'
5694 local key = {} -- expdesc
5695 luaK:exp2anyreg(fs, v)
5696 self:yindex(ls, key)
5697 luaK:indexed(fs, v, key)
5698 elseif c == ":" then -- ':' NAME funcargs
5699 local key = {} -- expdesc
5700 luaX:next(ls)
5701 self:checkname(ls, key)
5702 luaK:_self(fs, v, key)
5703 self:funcargs(ls, v)
5704 elseif c == "(" or c == "TK_STRING" or c == "{" then -- funcargs
5705 luaK:exp2nextreg(fs, v)
5706 self:funcargs(ls, v)
5707 else
5708 return
5709 end--if c
5710 end--while
5711end
5712
5713------------------------------------------------------------------------
5714-- parses general expression types, constants handled here
5715-- * used in subexpr()
5716------------------------------------------------------------------------
5717function luaY:simpleexp(ls, v)
5718 -- simpleexp -> NUMBER | STRING | NIL | TRUE | FALSE | ... |
5719 -- constructor | FUNCTION body | primaryexp
5720 local c = ls.t.token
5721 if c == "TK_NUMBER" then
5722 self:init_exp(v, "VKNUM", 0)
5723 v.nval = ls.t.seminfo
5724 elseif c == "TK_STRING" then
5725 self:codestring(ls, v, ls.t.seminfo)
5726 elseif c == "TK_NIL" then
5727 self:init_exp(v, "VNIL", 0)
5728 elseif c == "TK_TRUE" then
5729 self:init_exp(v, "VTRUE", 0)
5730 elseif c == "TK_FALSE" then
5731 self:init_exp(v, "VFALSE", 0)
5732 elseif c == "TK_DOTS" then -- vararg
5733 local fs = ls.fs
5734 self:check_condition(ls, fs.f.is_vararg ~= 0,
5735 "cannot use "..self:LUA_QL("...").." outside a vararg function");
5736 -- NOTE: the following substitutes for a bitop, but is value-specific
5737 local is_vararg = fs.f.is_vararg
5738 if is_vararg >= self.VARARG_NEEDSARG then
5739 fs.f.is_vararg = is_vararg - self.VARARG_NEEDSARG -- don't need 'arg'
5740 end
5741 self:init_exp(v, "VVARARG", luaK:codeABC(fs, "OP_VARARG", 0, 1, 0))
5742 elseif c == "{" then -- constructor
5743 self:constructor(ls, v)
5744 return
5745 elseif c == "TK_FUNCTION" then
5746 luaX:next(ls)
5747 self:body(ls, v, false, ls.linenumber)
5748 return
5749 else
5750 self:primaryexp(ls, v)
5751 return
5752 end--if c
5753 luaX:next(ls)
5754end
5755
5756------------------------------------------------------------------------
5757-- Translates unary operators tokens if found, otherwise returns
5758-- OPR_NOUNOPR. getunopr() and getbinopr() are used in subexpr().
5759-- * used in subexpr()
5760------------------------------------------------------------------------
5761function luaY:getunopr(op)
5762 if op == "TK_NOT" then
5763 return "OPR_NOT"
5764 elseif op == "-" then
5765 return "OPR_MINUS"
5766 elseif op == "#" then
5767 return "OPR_LEN"
5768 else
5769 return "OPR_NOUNOPR"
5770 end
5771end
5772
5773------------------------------------------------------------------------
5774-- Translates binary operator tokens if found, otherwise returns
5775-- OPR_NOBINOPR. Code generation uses OPR_* style tokens.
5776-- * used in subexpr()
5777------------------------------------------------------------------------
5778luaY.getbinopr_table = {
5779 ["+"] = "OPR_ADD",
5780 ["-"] = "OPR_SUB",
5781 ["*"] = "OPR_MUL",
5782 ["/"] = "OPR_DIV",
5783 ["%"] = "OPR_MOD",
5784 ["^"] = "OPR_POW",
5785 ["TK_CONCAT"] = "OPR_CONCAT",
5786 ["TK_NE"] = "OPR_NE",
5787 ["TK_EQ"] = "OPR_EQ",
5788 ["<"] = "OPR_LT",
5789 ["TK_LE"] = "OPR_LE",
5790 [">"] = "OPR_GT",
5791 ["TK_GE"] = "OPR_GE",
5792 ["TK_AND"] = "OPR_AND",
5793 ["TK_OR"] = "OPR_OR",
5794}
5795function luaY:getbinopr(op)
5796 local opr = self.getbinopr_table[op]
5797 if opr then return opr else return "OPR_NOBINOPR" end
5798end
5799
5800------------------------------------------------------------------------
5801-- the following priority table consists of pairs of left/right values
5802-- for binary operators (was a static const struct); grep for ORDER OPR
5803-- * the following struct is replaced:
5804-- static const struct {
5805-- lu_byte left; /* left priority for each binary operator */
5806-- lu_byte right; /* right priority */
5807-- } priority[] = { /* ORDER OPR */
5808------------------------------------------------------------------------
5809luaY.priority = {
5810 {6, 6}, {6, 6}, {7, 7}, {7, 7}, {7, 7}, -- `+' `-' `/' `%'
5811 {10, 9}, {5, 4}, -- power and concat (right associative)
5812 {3, 3}, {3, 3}, -- equality
5813 {3, 3}, {3, 3}, {3, 3}, {3, 3}, -- order
5814 {2, 2}, {1, 1} -- logical (and/or)
5815}
5816
5817luaY.UNARY_PRIORITY = 8 -- priority for unary operators
5818
5819------------------------------------------------------------------------
5820-- Parse subexpressions. Includes handling of unary operators and binary
5821-- operators. A subexpr is given the rhs priority level of the operator
5822-- immediately left of it, if any (limit is -1 if none,) and if a binop
5823-- is found, limit is compared with the lhs priority level of the binop
5824-- in order to determine which executes first.
5825------------------------------------------------------------------------
5826
5827------------------------------------------------------------------------
5828-- subexpr -> (simpleexp | unop subexpr) { binop subexpr }
5829-- where 'binop' is any binary operator with a priority higher than 'limit'
5830-- * for priority lookups with self.priority[], 1=left and 2=right
5831-- * recursively called
5832-- * used in expr()
5833------------------------------------------------------------------------
5834function luaY:subexpr(ls, v, limit)
5835 self:enterlevel(ls)
5836 local uop = self:getunopr(ls.t.token)
5837 if uop ~= "OPR_NOUNOPR" then
5838 luaX:next(ls)
5839 self:subexpr(ls, v, self.UNARY_PRIORITY)
5840 luaK:prefix(ls.fs, uop, v)
5841 else
5842 self:simpleexp(ls, v)
5843 end
5844 -- expand while operators have priorities higher than 'limit'
5845 local op = self:getbinopr(ls.t.token)
5846 while op ~= "OPR_NOBINOPR" and self.priority[luaK.BinOpr[op] + 1][1] > limit do
5847 local v2 = {} -- expdesc
5848 luaX:next(ls)
5849 luaK:infix(ls.fs, op, v)
5850 -- read sub-expression with higher priority
5851 local nextop = self:subexpr(ls, v2, self.priority[luaK.BinOpr[op] + 1][2])
5852 luaK:posfix(ls.fs, op, v, v2)
5853 op = nextop
5854 end
5855 self:leavelevel(ls)
5856 return op -- return first untreated operator
5857end
5858
5859------------------------------------------------------------------------
5860-- Expression parsing starts here. Function subexpr is entered with the
5861-- left operator (which is non-existent) priority of -1, which is lower
5862-- than all actual operators. Expr information is returned in parm v.
5863-- * used in multiple locations
5864------------------------------------------------------------------------
5865function luaY:expr(ls, v)
5866 self:subexpr(ls, v, 0)
5867end
5868
5869-- }====================================================================
5870
5871--[[--------------------------------------------------------------------
5872-- Rules for Statements
5873----------------------------------------------------------------------]]
5874
5875------------------------------------------------------------------------
5876-- checks next token, used as a look-ahead
5877-- * returns boolean instead of 0|1
5878-- * used in retstat(), chunk()
5879------------------------------------------------------------------------
5880function luaY:block_follow(token)
5881 if token == "TK_ELSE" or token == "TK_ELSEIF" or token == "TK_END"
5882 or token == "TK_UNTIL" or token == "TK_EOS" then
5883 return true
5884 else
5885 return false
5886 end
5887end
5888
5889------------------------------------------------------------------------
5890-- parse a code block or unit
5891-- * used in multiple functions
5892------------------------------------------------------------------------
5893function luaY:block(ls)
5894 -- block -> chunk
5895 local fs = ls.fs
5896 local bl = {} -- BlockCnt
5897 self:enterblock(fs, bl, false)
5898 self:chunk(ls)
5899 assert(bl.breaklist == luaK.NO_JUMP)
5900 self:leaveblock(fs)
5901end
5902
5903------------------------------------------------------------------------
5904-- structure to chain all variables in the left-hand side of an
5905-- assignment
5906-- struct LHS_assign:
5907-- prev -- (table: struct LHS_assign)
5908-- v -- variable (global, local, upvalue, or indexed) (table: expdesc)
5909------------------------------------------------------------------------
5910
5911------------------------------------------------------------------------
5912-- check whether, in an assignment to a local variable, the local variable
5913-- is needed in a previous assignment (to a table). If so, save original
5914-- local value in a safe place and use this safe copy in the previous
5915-- assignment.
5916-- * used in assignment()
5917------------------------------------------------------------------------
5918function luaY:check_conflict(ls, lh, v)
5919 local fs = ls.fs
5920 local extra = fs.freereg -- eventual position to save local variable
5921 local conflict = false
5922 while lh do
5923 if lh.v.k == "VINDEXED" then
5924 if lh.v.info == v.info then -- conflict?
5925 conflict = true
5926 lh.v.info = extra -- previous assignment will use safe copy
5927 end
5928 if lh.v.aux == v.info then -- conflict?
5929 conflict = true
5930 lh.v.aux = extra -- previous assignment will use safe copy
5931 end
5932 end
5933 lh = lh.prev
5934 end
5935 if conflict then
5936 luaK:codeABC(fs, "OP_MOVE", fs.freereg, v.info, 0) -- make copy
5937 luaK:reserveregs(fs, 1)
5938 end
5939end
5940
5941------------------------------------------------------------------------
5942-- parse a variable assignment sequence
5943-- * recursively called
5944-- * used in exprstat()
5945------------------------------------------------------------------------
5946function luaY:assignment(ls, lh, nvars)
5947 local e = {} -- expdesc
5948 -- test was: VLOCAL <= lh->v.k && lh->v.k <= VINDEXED
5949 local c = lh.v.k
5950 self:check_condition(ls, c == "VLOCAL" or c == "VUPVAL" or c == "VGLOBAL"
5951 or c == "VINDEXED", "syntax error")
5952 if self:testnext(ls, ",") then -- assignment -> ',' primaryexp assignment
5953 local nv = {} -- LHS_assign
5954 nv.v = {}
5955 nv.prev = lh
5956 self:primaryexp(ls, nv.v)
5957 if nv.v.k == "VLOCAL" then
5958 self:check_conflict(ls, lh, nv.v)
5959 end
5960 self:checklimit(ls.fs, nvars, self.LUAI_MAXCCALLS - ls.L.nCcalls,
5961 "variables in assignment")
5962 self:assignment(ls, nv, nvars + 1)
5963 else -- assignment -> '=' explist1
5964 self:checknext(ls, "=")
5965 local nexps = self:explist1(ls, e)
5966 if nexps ~= nvars then
5967 self:adjust_assign(ls, nvars, nexps, e)
5968 if nexps > nvars then
5969 ls.fs.freereg = ls.fs.freereg - (nexps - nvars) -- remove extra values
5970 end
5971 else
5972 luaK:setoneret(ls.fs, e) -- close last expression
5973 luaK:storevar(ls.fs, lh.v, e)
5974 return -- avoid default
5975 end
5976 end
5977 self:init_exp(e, "VNONRELOC", ls.fs.freereg - 1) -- default assignment
5978 luaK:storevar(ls.fs, lh.v, e)
5979end
5980
5981------------------------------------------------------------------------
5982-- parse condition in a repeat statement or an if control structure
5983-- * used in repeatstat(), test_then_block()
5984------------------------------------------------------------------------
5985function luaY:cond(ls)
5986 -- cond -> exp
5987 local v = {} -- expdesc
5988 self:expr(ls, v) -- read condition
5989 if v.k == "VNIL" then v.k = "VFALSE" end -- 'falses' are all equal here
5990 luaK:goiftrue(ls.fs, v)
5991 return v.f
5992end
5993
5994------------------------------------------------------------------------
5995-- parse a break statement
5996-- * used in statements()
5997------------------------------------------------------------------------
5998function luaY:breakstat(ls)
5999 -- stat -> BREAK
6000 local fs = ls.fs
6001 local bl = fs.bl
6002 local upval = false
6003 while bl and not bl.isbreakable do
6004 if bl.upval then upval = true end
6005 bl = bl.previous
6006 end
6007 if not bl then
6008 luaX:syntaxerror(ls, "no loop to break")
6009 end
6010 if upval then
6011 luaK:codeABC(fs, "OP_CLOSE", bl.nactvar, 0, 0)
6012 end
6013 bl.breaklist = luaK:concat(fs, bl.breaklist, luaK:jump(fs))
6014end
6015
6016------------------------------------------------------------------------
6017-- parse a while-do control structure, body processed by block()
6018-- * with dynamic array sizes, MAXEXPWHILE + EXTRAEXP limits imposed by
6019-- the function's implementation can be removed
6020-- * used in statements()
6021------------------------------------------------------------------------
6022function luaY:whilestat(ls, line)
6023 -- whilestat -> WHILE cond DO block END
6024 local fs = ls.fs
6025 local bl = {} -- BlockCnt
6026 luaX:next(ls) -- skip WHILE
6027 local whileinit = luaK:getlabel(fs)
6028 local condexit = self:cond(ls)
6029 self:enterblock(fs, bl, true)
6030 self:checknext(ls, "TK_DO")
6031 self:block(ls)
6032 luaK:patchlist(fs, luaK:jump(fs), whileinit)
6033 self:check_match(ls, "TK_END", "TK_WHILE", line)
6034 self:leaveblock(fs)
6035 luaK:patchtohere(fs, condexit) -- false conditions finish the loop
6036end
6037
6038------------------------------------------------------------------------
6039-- parse a repeat-until control structure, body parsed by chunk()
6040-- * used in statements()
6041------------------------------------------------------------------------
6042function luaY:repeatstat(ls, line)
6043 -- repeatstat -> REPEAT block UNTIL cond
6044 local fs = ls.fs
6045 local repeat_init = luaK:getlabel(fs)
6046 local bl1, bl2 = {}, {} -- BlockCnt
6047 self:enterblock(fs, bl1, true) -- loop block
6048 self:enterblock(fs, bl2, false) -- scope block
6049 luaX:next(ls) -- skip REPEAT
6050 self:chunk(ls)
6051 self:check_match(ls, "TK_UNTIL", "TK_REPEAT", line)
6052 local condexit = self:cond(ls) -- read condition (inside scope block)
6053 if not bl2.upval then -- no upvalues?
6054 self:leaveblock(fs) -- finish scope
6055 luaK:patchlist(ls.fs, condexit, repeat_init) -- close the loop
6056 else -- complete semantics when there are upvalues
6057 self:breakstat(ls) -- if condition then break
6058 luaK:patchtohere(ls.fs, condexit) -- else...
6059 self:leaveblock(fs) -- finish scope...
6060 luaK:patchlist(ls.fs, luaK:jump(fs), repeat_init) -- and repeat
6061 end
6062 self:leaveblock(fs) -- finish loop
6063end
6064
6065------------------------------------------------------------------------
6066-- parse the single expressions needed in numerical for loops
6067-- * used in fornum()
6068------------------------------------------------------------------------
6069function luaY:exp1(ls)
6070 local e = {} -- expdesc
6071 self:expr(ls, e)
6072 local k = e.k
6073 luaK:exp2nextreg(ls.fs, e)
6074 return k
6075end
6076
6077------------------------------------------------------------------------
6078-- parse a for loop body for both versions of the for loop
6079-- * used in fornum(), forlist()
6080------------------------------------------------------------------------
6081function luaY:forbody(ls, base, line, nvars, isnum)
6082 -- forbody -> DO block
6083 local bl = {} -- BlockCnt
6084 local fs = ls.fs
6085 self:adjustlocalvars(ls, 3) -- control variables
6086 self:checknext(ls, "TK_DO")
6087 local prep = isnum and luaK:codeAsBx(fs, "OP_FORPREP", base, luaK.NO_JUMP)
6088 or luaK:jump(fs)
6089 self:enterblock(fs, bl, false) -- scope for declared variables
6090 self:adjustlocalvars(ls, nvars)
6091 luaK:reserveregs(fs, nvars)
6092 self:block(ls)
6093 self:leaveblock(fs) -- end of scope for declared variables
6094 luaK:patchtohere(fs, prep)
6095 local endfor = isnum and luaK:codeAsBx(fs, "OP_FORLOOP", base, luaK.NO_JUMP)
6096 or luaK:codeABC(fs, "OP_TFORLOOP", base, 0, nvars)
6097 luaK:fixline(fs, line) -- pretend that `OP_FOR' starts the loop
6098 luaK:patchlist(fs, isnum and endfor or luaK:jump(fs), prep + 1)
6099end
6100
6101------------------------------------------------------------------------
6102-- parse a numerical for loop, calls forbody()
6103-- * used in forstat()
6104------------------------------------------------------------------------
6105function luaY:fornum(ls, varname, line)
6106 -- fornum -> NAME = exp1,exp1[,exp1] forbody
6107 local fs = ls.fs
6108 local base = fs.freereg
6109 self:new_localvarliteral(ls, "(for index)", 0)
6110 self:new_localvarliteral(ls, "(for limit)", 1)
6111 self:new_localvarliteral(ls, "(for step)", 2)
6112 self:new_localvar(ls, varname, 3)
6113 self:checknext(ls, '=')
6114 self:exp1(ls) -- initial value
6115 self:checknext(ls, ",")
6116 self:exp1(ls) -- limit
6117 if self:testnext(ls, ",") then
6118 self:exp1(ls) -- optional step
6119 else -- default step = 1
6120 luaK:codeABx(fs, "OP_LOADK", fs.freereg, luaK:numberK(fs, 1))
6121 luaK:reserveregs(fs, 1)
6122 end
6123 self:forbody(ls, base, line, 1, true)
6124end
6125
6126------------------------------------------------------------------------
6127-- parse a generic for loop, calls forbody()
6128-- * used in forstat()
6129------------------------------------------------------------------------
6130function luaY:forlist(ls, indexname)
6131 -- forlist -> NAME {,NAME} IN explist1 forbody
6132 local fs = ls.fs
6133 local e = {} -- expdesc
6134 local nvars = 0
6135 local base = fs.freereg
6136 -- create control variables
6137 self:new_localvarliteral(ls, "(for generator)", nvars)
6138 nvars = nvars + 1
6139 self:new_localvarliteral(ls, "(for state)", nvars)
6140 nvars = nvars + 1
6141 self:new_localvarliteral(ls, "(for control)", nvars)
6142 nvars = nvars + 1
6143 -- create declared variables
6144 self:new_localvar(ls, indexname, nvars)
6145 nvars = nvars + 1
6146 while self:testnext(ls, ",") do
6147 self:new_localvar(ls, self:str_checkname(ls), nvars)
6148 nvars = nvars + 1
6149 end
6150 self:checknext(ls, "TK_IN")
6151 local line = ls.linenumber
6152 self:adjust_assign(ls, 3, self:explist1(ls, e), e)
6153 luaK:checkstack(fs, 3) -- extra space to call generator
6154 self:forbody(ls, base, line, nvars - 3, false)
6155end
6156
6157------------------------------------------------------------------------
6158-- initial parsing for a for loop, calls fornum() or forlist()
6159-- * used in statements()
6160------------------------------------------------------------------------
6161function luaY:forstat(ls, line)
6162 -- forstat -> FOR (fornum | forlist) END
6163 local fs = ls.fs
6164 local bl = {} -- BlockCnt
6165 self:enterblock(fs, bl, true) -- scope for loop and control variables
6166 luaX:next(ls) -- skip `for'
6167 local varname = self:str_checkname(ls) -- first variable name
6168 local c = ls.t.token
6169 if c == "=" then
6170 self:fornum(ls, varname, line)
6171 elseif c == "," or c == "TK_IN" then
6172 self:forlist(ls, varname)
6173 else
6174 luaX:syntaxerror(ls, self:LUA_QL("=").." or "..self:LUA_QL("in").." expected")
6175 end
6176 self:check_match(ls, "TK_END", "TK_FOR", line)
6177 self:leaveblock(fs) -- loop scope (`break' jumps to this point)
6178end
6179
6180------------------------------------------------------------------------
6181-- parse part of an if control structure, including the condition
6182-- * used in ifstat()
6183------------------------------------------------------------------------
6184function luaY:test_then_block(ls)
6185 -- test_then_block -> [IF | ELSEIF] cond THEN block
6186 luaX:next(ls) -- skip IF or ELSEIF
6187 local condexit = self:cond(ls)
6188 self:checknext(ls, "TK_THEN")
6189 self:block(ls) -- `then' part
6190 return condexit
6191end
6192
6193------------------------------------------------------------------------
6194-- parse an if control structure
6195-- * used in statements()
6196------------------------------------------------------------------------
6197function luaY:ifstat(ls, line)
6198 -- ifstat -> IF cond THEN block {ELSEIF cond THEN block} [ELSE block] END
6199 local fs = ls.fs
6200 local escapelist = luaK.NO_JUMP
6201 local flist = self:test_then_block(ls) -- IF cond THEN block
6202 while ls.t.token == "TK_ELSEIF" do
6203 escapelist = luaK:concat(fs, escapelist, luaK:jump(fs))
6204 luaK:patchtohere(fs, flist)
6205 flist = self:test_then_block(ls) -- ELSEIF cond THEN block
6206 end
6207 if ls.t.token == "TK_ELSE" then
6208 escapelist = luaK:concat(fs, escapelist, luaK:jump(fs))
6209 luaK:patchtohere(fs, flist)
6210 luaX:next(ls) -- skip ELSE (after patch, for correct line info)
6211 self:block(ls) -- 'else' part
6212 else
6213 escapelist = luaK:concat(fs, escapelist, flist)
6214 end
6215 luaK:patchtohere(fs, escapelist)
6216 self:check_match(ls, "TK_END", "TK_IF", line)
6217end
6218
6219------------------------------------------------------------------------
6220-- parse a local function statement
6221-- * used in statements()
6222------------------------------------------------------------------------
6223function luaY:localfunc(ls)
6224 local v, b = {}, {} -- expdesc
6225 local fs = ls.fs
6226 self:new_localvar(ls, self:str_checkname(ls), 0)
6227 self:init_exp(v, "VLOCAL", fs.freereg)
6228 luaK:reserveregs(fs, 1)
6229 self:adjustlocalvars(ls, 1)
6230 self:body(ls, b, false, ls.linenumber)
6231 luaK:storevar(fs, v, b)
6232 -- debug information will only see the variable after this point!
6233 self:getlocvar(fs, fs.nactvar - 1).startpc = fs.pc
6234end
6235
6236------------------------------------------------------------------------
6237-- parse a local variable declaration statement
6238-- * used in statements()
6239------------------------------------------------------------------------
6240function luaY:localstat(ls)
6241 -- stat -> LOCAL NAME {',' NAME} ['=' explist1]
6242 local nvars = 0
6243 local nexps
6244 local e = {} -- expdesc
6245 repeat
6246 self:new_localvar(ls, self:str_checkname(ls), nvars)
6247 nvars = nvars + 1
6248 until not self:testnext(ls, ",")
6249 if self:testnext(ls, "=") then
6250 nexps = self:explist1(ls, e)
6251 else
6252 e.k = "VVOID"
6253 nexps = 0
6254 end
6255 self:adjust_assign(ls, nvars, nexps, e)
6256 self:adjustlocalvars(ls, nvars)
6257end
6258
6259------------------------------------------------------------------------
6260-- parse a function name specification
6261-- * used in funcstat()
6262------------------------------------------------------------------------
6263function luaY:funcname(ls, v)
6264 -- funcname -> NAME {field} [':' NAME]
6265 local needself = false
6266 self:singlevar(ls, v)
6267 while ls.t.token == "." do
6268 self:field(ls, v)
6269 end
6270 if ls.t.token == ":" then
6271 needself = true
6272 self:field(ls, v)
6273 end
6274 return needself
6275end
6276
6277------------------------------------------------------------------------
6278-- parse a function statement
6279-- * used in statements()
6280------------------------------------------------------------------------
6281function luaY:funcstat(ls, line)
6282 -- funcstat -> FUNCTION funcname body
6283 local v, b = {}, {} -- expdesc
6284 luaX:next(ls) -- skip FUNCTION
6285 local needself = self:funcname(ls, v)
6286 self:body(ls, b, needself, line)
6287 luaK:storevar(ls.fs, v, b)
6288 luaK:fixline(ls.fs, line) -- definition 'happens' in the first line
6289end
6290
6291------------------------------------------------------------------------
6292-- parse a function call with no returns or an assignment statement
6293-- * used in statements()
6294------------------------------------------------------------------------
6295function luaY:exprstat(ls)
6296 -- stat -> func | assignment
6297 local fs = ls.fs
6298 local v = {} -- LHS_assign
6299 v.v = {}
6300 self:primaryexp(ls, v.v)
6301 if v.v.k == "VCALL" then -- stat -> func
6302 luaP:SETARG_C(luaK:getcode(fs, v.v), 1) -- call statement uses no results
6303 else -- stat -> assignment
6304 v.prev = nil
6305 self:assignment(ls, v, 1)
6306 end
6307end
6308
6309------------------------------------------------------------------------
6310-- parse a return statement
6311-- * used in statements()
6312------------------------------------------------------------------------
6313function luaY:retstat(ls)
6314 -- stat -> RETURN explist
6315 local fs = ls.fs
6316 local e = {} -- expdesc
6317 local first, nret -- registers with returned values
6318 luaX:next(ls) -- skip RETURN
6319 if self:block_follow(ls.t.token) or ls.t.token == ";" then
6320 first, nret = 0, 0 -- return no values
6321 else
6322 nret = self:explist1(ls, e) -- optional return values
6323 if self:hasmultret(e.k) then
6324 luaK:setmultret(fs, e)
6325 if e.k == "VCALL" and nret == 1 then -- tail call?
6326 luaP:SET_OPCODE(luaK:getcode(fs, e), "OP_TAILCALL")
6327 assert(luaP:GETARG_A(luaK:getcode(fs, e)) == fs.nactvar)
6328 end
6329 first = fs.nactvar
6330 nret = self.LUA_MULTRET -- return all values
6331 else
6332 if nret == 1 then -- only one single value?
6333 first = luaK:exp2anyreg(fs, e)
6334 else
6335 luaK:exp2nextreg(fs, e) -- values must go to the 'stack'
6336 first = fs.nactvar -- return all 'active' values
6337 assert(nret == fs.freereg - first)
6338 end
6339 end--if
6340 end--if
6341 luaK:ret(fs, first, nret)
6342end
6343
6344------------------------------------------------------------------------
6345-- initial parsing for statements, calls a lot of functions
6346-- * returns boolean instead of 0|1
6347-- * used in chunk()
6348------------------------------------------------------------------------
6349function luaY:statement(ls)
6350 local line = ls.linenumber -- may be needed for error messages
6351 local c = ls.t.token
6352 if c == "TK_IF" then -- stat -> ifstat
6353 self:ifstat(ls, line)
6354 return false
6355 elseif c == "TK_WHILE" then -- stat -> whilestat
6356 self:whilestat(ls, line)
6357 return false
6358 elseif c == "TK_DO" then -- stat -> DO block END
6359 luaX:next(ls) -- skip DO
6360 self:block(ls)
6361 self:check_match(ls, "TK_END", "TK_DO", line)
6362 return false
6363 elseif c == "TK_FOR" then -- stat -> forstat
6364 self:forstat(ls, line)
6365 return false
6366 elseif c == "TK_REPEAT" then -- stat -> repeatstat
6367 self:repeatstat(ls, line)
6368 return false
6369 elseif c == "TK_FUNCTION" then -- stat -> funcstat
6370 self:funcstat(ls, line)
6371 return false
6372 elseif c == "TK_LOCAL" then -- stat -> localstat
6373 luaX:next(ls) -- skip LOCAL
6374 if self:testnext(ls, "TK_FUNCTION") then -- local function?
6375 self:localfunc(ls)
6376 else
6377 self:localstat(ls)
6378 end
6379 return false
6380 elseif c == "TK_RETURN" then -- stat -> retstat
6381 self:retstat(ls)
6382 return true -- must be last statement
6383 elseif c == "TK_BREAK" then -- stat -> breakstat
6384 luaX:next(ls) -- skip BREAK
6385 self:breakstat(ls)
6386 return true -- must be last statement
6387 else
6388 self:exprstat(ls)
6389 return false -- to avoid warnings
6390 end--if c
6391end
6392
6393------------------------------------------------------------------------
6394-- parse a chunk, which consists of a bunch of statements
6395-- * used in parser(), body(), block(), repeatstat()
6396------------------------------------------------------------------------
6397function luaY:chunk(ls)
6398 -- chunk -> { stat [';'] }
6399 local islast = false
6400 self:enterlevel(ls)
6401 while not islast and not self:block_follow(ls.t.token) do
6402 islast = self:statement(ls)
6403 self:testnext(ls, ";")
6404 assert(ls.fs.f.maxstacksize >= ls.fs.freereg and
6405 ls.fs.freereg >= ls.fs.nactvar)
6406 ls.fs.freereg = ls.fs.nactvar -- free registers
6407 end
6408 self:leavelevel(ls)
6409end
6410
6411-- }======================================================================
6412return luaY]]></ProtectedString>
6413 <BinaryString name="Tags"></BinaryString>
6414 </Properties>
6415 </Item>
6416 <Item class="ModuleScript" referent="RBX6D281683B751445CAD6CA6A9CAA1B31F">
6417 <Properties>
6418 <Content name="LinkedSource"><null></null></Content>
6419 <string name="Name">LuaK</string>
6420 <string name="ScriptGuid">{E74CCBE8-6F78-46F4-B517-8E4B60989D32}</string>
6421 <ProtectedString name="Source"><![CDATA[--[[--------------------------------------------------------------------
6422
6423 lcode.lua
6424 Lua 5 code generator in Lua
6425 This file is part of Yueliang.
6426
6427 Copyright (c) 2005-2007 Kein-Hong Man <khman@users.sf.net>
6428 The COPYRIGHT file describes the conditions
6429 under which this software may be distributed.
6430
6431 See the ChangeLog for more information.
6432
6433----------------------------------------------------------------------]]
6434
6435--[[--------------------------------------------------------------------
6436-- Notes:
6437-- * one function manipulate a pointer argument with a simple data type
6438-- (can't be emulated by a table, ambiguous), now returns that value:
6439-- luaK:concat(fs, l1, l2)
6440-- * luaM_growvector uses the faux luaY:growvector, for limit checking
6441-- * some function parameters changed to boolean, additional code
6442-- translates boolean back to 1/0 for instruction fields
6443--
6444-- Not implemented:
6445-- * NOTE there is a failed assert in luaK:addk, a porting problem
6446--
6447-- Added:
6448-- * constant MAXSTACK from llimits.h
6449-- * luaK:ttisnumber(o) (from lobject.h)
6450-- * luaK:nvalue(o) (from lobject.h)
6451-- * luaK:setnilvalue(o) (from lobject.h)
6452-- * luaK:setnvalue(o, x) (from lobject.h)
6453-- * luaK:setbvalue(o, x) (from lobject.h)
6454-- * luaK:sethvalue(o, x) (from lobject.h), parameter L deleted
6455-- * luaK:setsvalue(o, x) (from lobject.h), parameter L deleted
6456-- * luaK:numadd, luaK:numsub, luaK:nummul, luaK:numdiv, luaK:nummod,
6457-- luaK:numpow, luaK:numunm, luaK:numisnan (from luaconf.h)
6458-- * copyexp(e1, e2) added in luaK:posfix to copy expdesc struct
6459--
6460-- Changed in 5.1.x:
6461-- * enum BinOpr has a new entry, OPR_MOD
6462-- * enum UnOpr has a new entry, OPR_LEN
6463-- * binopistest, unused in 5.0.x, has been deleted
6464-- * macro setmultret is new
6465-- * functions isnumeral, luaK_ret, boolK are new
6466-- * funcion nilK was named nil_constant in 5.0.x
6467-- * function interface changed: need_value, patchtestreg, concat
6468-- * TObject now a TValue
6469-- * functions luaK_setreturns, luaK_setoneret are new
6470-- * function luaK:setcallreturns deleted, to be replaced by:
6471-- luaK:setmultret, luaK:ret, luaK:setreturns, luaK:setoneret
6472-- * functions constfolding, codearith, codecomp are new
6473-- * luaK:codebinop has been deleted
6474-- * function luaK_setlist is new
6475-- * OPR_MULT renamed to OPR_MUL
6476----------------------------------------------------------------------]]
6477
6478-- requires luaP, luaX, luaY
6479local luaK = {}
6480local luaP = require(script.Parent.LuaP)
6481local luaX = require(script.Parent.LuaX)
6482
6483------------------------------------------------------------------------
6484-- constants used by code generator
6485------------------------------------------------------------------------
6486-- maximum stack for a Lua function
6487luaK.MAXSTACK = 250 -- (from llimits.h)
6488
6489--[[--------------------------------------------------------------------
6490-- other functions
6491----------------------------------------------------------------------]]
6492
6493------------------------------------------------------------------------
6494-- emulation of TValue macros (these are from lobject.h)
6495-- * TValue is a table since lcode passes references around
6496-- * tt member field removed, using Lua's type() instead
6497-- * for setsvalue, sethvalue, parameter L (deleted here) in lobject.h
6498-- is used in an assert for testing, see checkliveness(g,obj)
6499------------------------------------------------------------------------
6500function luaK:ttisnumber(o)
6501 if o then return type(o.value) == "number" else return false end
6502end
6503function luaK:nvalue(o) return o.value end
6504function luaK:setnilvalue(o) o.value = nil end
6505function luaK:setsvalue(o, x) o.value = x end
6506luaK.setnvalue = luaK.setsvalue
6507luaK.sethvalue = luaK.setsvalue
6508luaK.setbvalue = luaK.setsvalue
6509
6510------------------------------------------------------------------------
6511-- The luai_num* macros define the primitive operations over numbers.
6512-- * this is not the entire set of primitive operations from luaconf.h
6513-- * used in luaK:constfolding()
6514------------------------------------------------------------------------
6515function luaK:numadd(a, b) return a + b end
6516function luaK:numsub(a, b) return a - b end
6517function luaK:nummul(a, b) return a * b end
6518function luaK:numdiv(a, b) return a / b end
6519function luaK:nummod(a, b) return a % b end
6520 -- ((a) - floor((a)/(b))*(b)) /* actual, for reference */
6521function luaK:numpow(a, b) return a ^ b end
6522function luaK:numunm(a) return -a end
6523function luaK:numisnan(a) return not a == a end
6524 -- a NaN cannot equal another NaN
6525
6526--[[--------------------------------------------------------------------
6527-- code generator functions
6528----------------------------------------------------------------------]]
6529
6530------------------------------------------------------------------------
6531-- Marks the end of a patch list. It is an invalid value both as an absolute
6532-- address, and as a list link (would link an element to itself).
6533------------------------------------------------------------------------
6534luaK.NO_JUMP = -1
6535
6536------------------------------------------------------------------------
6537-- grep "ORDER OPR" if you change these enums
6538------------------------------------------------------------------------
6539luaK.BinOpr = {
6540 OPR_ADD = 0, OPR_SUB = 1, OPR_MUL = 2, OPR_DIV = 3, OPR_MOD = 4, OPR_POW = 5,
6541 OPR_CONCAT = 6,
6542 OPR_NE = 7, OPR_EQ = 8,
6543 OPR_LT = 9, OPR_LE = 10, OPR_GT = 11, OPR_GE = 12,
6544 OPR_AND = 13, OPR_OR = 14,
6545 OPR_NOBINOPR = 15,
6546}
6547
6548-- * UnOpr is used by luaK:prefix's op argument, but not directly used
6549-- because the function receives the symbols as strings, e.g. "OPR_NOT"
6550luaK.UnOpr = {
6551 OPR_MINUS = 0, OPR_NOT = 1, OPR_LEN = 2, OPR_NOUNOPR = 3
6552}
6553
6554------------------------------------------------------------------------
6555-- returns the instruction object for given e (expdesc), was a macro
6556------------------------------------------------------------------------
6557function luaK:getcode(fs, e)
6558 return fs.f.code[e.info]
6559end
6560
6561------------------------------------------------------------------------
6562-- codes an instruction with a signed Bx (sBx) field, was a macro
6563-- * used in luaK:jump(), (lparser) luaY:forbody()
6564------------------------------------------------------------------------
6565function luaK:codeAsBx(fs, o, A, sBx)
6566 return self:codeABx(fs, o, A, sBx + luaP.MAXARG_sBx)
6567end
6568
6569------------------------------------------------------------------------
6570-- set the expdesc e instruction for multiple returns, was a macro
6571------------------------------------------------------------------------
6572function luaK:setmultret(fs, e)
6573 self:setreturns(fs, e, luaY.LUA_MULTRET)
6574end
6575
6576------------------------------------------------------------------------
6577-- there is a jump if patch lists are not identical, was a macro
6578-- * used in luaK:exp2reg(), luaK:exp2anyreg(), luaK:exp2val()
6579------------------------------------------------------------------------
6580function luaK:hasjumps(e)
6581 return e.t ~= e.f
6582end
6583
6584------------------------------------------------------------------------
6585-- true if the expression is a constant number (for constant folding)
6586-- * used in constfolding(), infix()
6587------------------------------------------------------------------------
6588function luaK:isnumeral(e)
6589 return e.k == "VKNUM" and e.t == self.NO_JUMP and e.f == self.NO_JUMP
6590end
6591
6592------------------------------------------------------------------------
6593-- codes loading of nil, optimization done if consecutive locations
6594-- * used in luaK:discharge2reg(), (lparser) luaY:adjust_assign()
6595------------------------------------------------------------------------
6596function luaK:_nil(fs, from, n)
6597 if fs.pc > fs.lasttarget then -- no jumps to current position?
6598 if fs.pc == 0 then -- function start?
6599 if from >= fs.nactvar then
6600 return -- positions are already clean
6601 end
6602 else
6603 local previous = fs.f.code[fs.pc - 1]
6604 if luaP:GET_OPCODE(previous) == "OP_LOADNIL" then
6605 local pfrom = luaP:GETARG_A(previous)
6606 local pto = luaP:GETARG_B(previous)
6607 if pfrom <= from and from <= pto + 1 then -- can connect both?
6608 if from + n - 1 > pto then
6609 luaP:SETARG_B(previous, from + n - 1)
6610 end
6611 return
6612 end
6613 end
6614 end
6615 end
6616 self:codeABC(fs, "OP_LOADNIL", from, from + n - 1, 0) -- else no optimization
6617end
6618
6619------------------------------------------------------------------------
6620--
6621-- * used in multiple locations
6622------------------------------------------------------------------------
6623function luaK:jump(fs)
6624 local jpc = fs.jpc -- save list of jumps to here
6625 fs.jpc = self.NO_JUMP
6626 local j = self:codeAsBx(fs, "OP_JMP", 0, self.NO_JUMP)
6627 j = self:concat(fs, j, jpc) -- keep them on hold
6628 return j
6629end
6630
6631------------------------------------------------------------------------
6632-- codes a RETURN instruction
6633-- * used in luaY:close_func(), luaY:retstat()
6634------------------------------------------------------------------------
6635function luaK:ret(fs, first, nret)
6636 self:codeABC(fs, "OP_RETURN", first, nret + 1, 0)
6637end
6638
6639------------------------------------------------------------------------
6640--
6641-- * used in luaK:jumponcond(), luaK:codecomp()
6642------------------------------------------------------------------------
6643function luaK:condjump(fs, op, A, B, C)
6644 self:codeABC(fs, op, A, B, C)
6645 return self:jump(fs)
6646end
6647
6648------------------------------------------------------------------------
6649--
6650-- * used in luaK:patchlistaux(), luaK:concat()
6651------------------------------------------------------------------------
6652function luaK:fixjump(fs, pc, dest)
6653 local jmp = fs.f.code[pc]
6654 local offset = dest - (pc + 1)
6655 assert(dest ~= self.NO_JUMP)
6656 if math.abs(offset) > luaP.MAXARG_sBx then
6657 luaX:syntaxerror(fs.ls, "control structure too long")
6658 end
6659 luaP:SETARG_sBx(jmp, offset)
6660end
6661
6662------------------------------------------------------------------------
6663-- returns current 'pc' and marks it as a jump target (to avoid wrong
6664-- optimizations with consecutive instructions not in the same basic block).
6665-- * used in multiple locations
6666-- * fs.lasttarget tested only by luaK:_nil() when optimizing OP_LOADNIL
6667------------------------------------------------------------------------
6668function luaK:getlabel(fs)
6669 fs.lasttarget = fs.pc
6670 return fs.pc
6671end
6672
6673------------------------------------------------------------------------
6674--
6675-- * used in luaK:need_value(), luaK:removevalues(), luaK:patchlistaux(),
6676-- luaK:concat()
6677------------------------------------------------------------------------
6678function luaK:getjump(fs, pc)
6679 local offset = luaP:GETARG_sBx(fs.f.code[pc])
6680 if offset == self.NO_JUMP then -- point to itself represents end of list
6681 return self.NO_JUMP -- end of list
6682 else
6683 return (pc + 1) + offset -- turn offset into absolute position
6684 end
6685end
6686
6687------------------------------------------------------------------------
6688--
6689-- * used in luaK:need_value(), luaK:patchtestreg(), luaK:invertjump()
6690------------------------------------------------------------------------
6691function luaK:getjumpcontrol(fs, pc)
6692 local pi = fs.f.code[pc]
6693 local ppi = fs.f.code[pc - 1]
6694 if pc >= 1 and luaP:testTMode(luaP:GET_OPCODE(ppi)) ~= 0 then
6695 return ppi
6696 else
6697 return pi
6698 end
6699end
6700
6701------------------------------------------------------------------------
6702-- check whether list has any jump that do not produce a value
6703-- (or produce an inverted value)
6704-- * return value changed to boolean
6705-- * used only in luaK:exp2reg()
6706------------------------------------------------------------------------
6707function luaK:need_value(fs, list)
6708 while list ~= self.NO_JUMP do
6709 local i = self:getjumpcontrol(fs, list)
6710 if luaP:GET_OPCODE(i) ~= "OP_TESTSET" then return true end
6711 list = self:getjump(fs, list)
6712 end
6713 return false -- not found
6714end
6715
6716------------------------------------------------------------------------
6717--
6718-- * used in luaK:removevalues(), luaK:patchlistaux()
6719------------------------------------------------------------------------
6720function luaK:patchtestreg(fs, node, reg)
6721 local i = self:getjumpcontrol(fs, node)
6722 if luaP:GET_OPCODE(i) ~= "OP_TESTSET" then
6723 return false -- cannot patch other instructions
6724 end
6725 if reg ~= luaP.NO_REG and reg ~= luaP:GETARG_B(i) then
6726 luaP:SETARG_A(i, reg)
6727 else -- no register to put value or register already has the value
6728 -- due to use of a table as i, i cannot be replaced by another table
6729 -- so the following is required; there is no change to ARG_C
6730 luaP:SET_OPCODE(i, "OP_TEST")
6731 local b = luaP:GETARG_B(i)
6732 luaP:SETARG_A(i, b)
6733 luaP:SETARG_B(i, 0)
6734 -- *i = CREATE_ABC(OP_TEST, GETARG_B(*i), 0, GETARG_C(*i)); /* C */
6735 end
6736 return true
6737end
6738
6739------------------------------------------------------------------------
6740--
6741-- * used only in luaK:codenot()
6742------------------------------------------------------------------------
6743function luaK:removevalues(fs, list)
6744 while list ~= self.NO_JUMP do
6745 self:patchtestreg(fs, list, luaP.NO_REG)
6746 list = self:getjump(fs, list)
6747 end
6748end
6749
6750------------------------------------------------------------------------
6751--
6752-- * used in luaK:dischargejpc(), luaK:patchlist(), luaK:exp2reg()
6753------------------------------------------------------------------------
6754function luaK:patchlistaux(fs, list, vtarget, reg, dtarget)
6755 while list ~= self.NO_JUMP do
6756 local _next = self:getjump(fs, list)
6757 if self:patchtestreg(fs, list, reg) then
6758 self:fixjump(fs, list, vtarget)
6759 else
6760 self:fixjump(fs, list, dtarget) -- jump to default target
6761 end
6762 list = _next
6763 end
6764end
6765
6766------------------------------------------------------------------------
6767--
6768-- * used only in luaK:code()
6769------------------------------------------------------------------------
6770function luaK:dischargejpc(fs)
6771 self:patchlistaux(fs, fs.jpc, fs.pc, luaP.NO_REG, fs.pc)
6772 fs.jpc = self.NO_JUMP
6773end
6774
6775------------------------------------------------------------------------
6776--
6777-- * used in (lparser) luaY:whilestat(), luaY:repeatstat(), luaY:forbody()
6778------------------------------------------------------------------------
6779function luaK:patchlist(fs, list, target)
6780 if target == fs.pc then
6781 self:patchtohere(fs, list)
6782 else
6783 assert(target < fs.pc)
6784 self:patchlistaux(fs, list, target, luaP.NO_REG, target)
6785 end
6786end
6787
6788------------------------------------------------------------------------
6789--
6790-- * used in multiple locations
6791------------------------------------------------------------------------
6792function luaK:patchtohere(fs, list)
6793 self:getlabel(fs)
6794 fs.jpc = self:concat(fs, fs.jpc, list)
6795end
6796
6797------------------------------------------------------------------------
6798-- * l1 was a pointer, now l1 is returned and callee assigns the value
6799-- * used in multiple locations
6800------------------------------------------------------------------------
6801function luaK:concat(fs, l1, l2)
6802 if l2 == self.NO_JUMP then return l1
6803 elseif l1 == self.NO_JUMP then
6804 return l2
6805 else
6806 local list = l1
6807 local _next = self:getjump(fs, list)
6808 while _next ~= self.NO_JUMP do -- find last element
6809 list = _next
6810 _next = self:getjump(fs, list)
6811 end
6812 self:fixjump(fs, list, l2)
6813 end
6814 return l1
6815end
6816
6817------------------------------------------------------------------------
6818--
6819-- * used in luaK:reserveregs(), (lparser) luaY:forlist()
6820------------------------------------------------------------------------
6821function luaK:checkstack(fs, n)
6822 local newstack = fs.freereg + n
6823 if newstack > fs.f.maxstacksize then
6824 if newstack >= self.MAXSTACK then
6825 luaX:syntaxerror(fs.ls, "function or expression too complex")
6826 end
6827 fs.f.maxstacksize = newstack
6828 end
6829end
6830
6831------------------------------------------------------------------------
6832--
6833-- * used in multiple locations
6834------------------------------------------------------------------------
6835function luaK:reserveregs(fs, n)
6836 self:checkstack(fs, n)
6837 fs.freereg = fs.freereg + n
6838end
6839
6840------------------------------------------------------------------------
6841--
6842-- * used in luaK:freeexp(), luaK:dischargevars()
6843------------------------------------------------------------------------
6844function luaK:freereg(fs, reg)
6845 if not luaP:ISK(reg) and reg >= fs.nactvar then
6846 fs.freereg = fs.freereg - 1
6847 assert(reg == fs.freereg)
6848 end
6849end
6850
6851------------------------------------------------------------------------
6852--
6853-- * used in multiple locations
6854------------------------------------------------------------------------
6855function luaK:freeexp(fs, e)
6856 if e.k == "VNONRELOC" then
6857 self:freereg(fs, e.info)
6858 end
6859end
6860
6861------------------------------------------------------------------------
6862-- * TODO NOTE implementation is not 100% correct, since the assert fails
6863-- * luaH_set, setobj deleted; direct table access used instead
6864-- * used in luaK:stringK(), luaK:numberK(), luaK:boolK(), luaK:nilK()
6865------------------------------------------------------------------------
6866function luaK:addk(fs, k, v)
6867 local L = fs.L
6868 local idx = fs.h[k.value]
6869 --TValue *idx = luaH_set(L, fs->h, k); /* C */
6870 local f = fs.f
6871 if self:ttisnumber(idx) then
6872 --TODO this assert currently FAILS (last tested for 5.0.2)
6873 --assert(fs.f.k[self:nvalue(idx)] == v)
6874 --assert(luaO_rawequalObj(&fs->f->k[cast_int(nvalue(idx))], v)); /* C */
6875 return self:nvalue(idx)
6876 else -- constant not found; create a new entry
6877 idx = {}
6878 self:setnvalue(idx, fs.nk)
6879 fs.h[k.value] = idx
6880 -- setnvalue(idx, cast_num(fs->nk)); /* C */
6881 luaY:growvector(L, f.k, fs.nk, f.sizek, nil,
6882 luaP.MAXARG_Bx, "constant table overflow")
6883 -- loop to initialize empty f.k positions not required
6884 f.k[fs.nk] = v
6885 -- setobj(L, &f->k[fs->nk], v); /* C */
6886 -- luaC_barrier(L, f, v); /* GC */
6887 local nk = fs.nk
6888 fs.nk = fs.nk + 1
6889 return nk
6890 end
6891
6892end
6893
6894------------------------------------------------------------------------
6895-- creates and sets a string object
6896-- * used in (lparser) luaY:codestring(), luaY:singlevar()
6897------------------------------------------------------------------------
6898function luaK:stringK(fs, s)
6899 local o = {} -- TValue
6900 self:setsvalue(o, s)
6901 return self:addk(fs, o, o)
6902end
6903
6904------------------------------------------------------------------------
6905-- creates and sets a number object
6906-- * used in luaK:prefix() for negative (or negation of) numbers
6907-- * used in (lparser) luaY:simpleexp(), luaY:fornum()
6908------------------------------------------------------------------------
6909function luaK:numberK(fs, r)
6910 local o = {} -- TValue
6911 self:setnvalue(o, r)
6912 return self:addk(fs, o, o)
6913end
6914
6915------------------------------------------------------------------------
6916-- creates and sets a boolean object
6917-- * used only in luaK:exp2RK()
6918------------------------------------------------------------------------
6919function luaK:boolK(fs, b)
6920 local o = {} -- TValue
6921 self:setbvalue(o, b)
6922 return self:addk(fs, o, o)
6923end
6924
6925------------------------------------------------------------------------
6926-- creates and sets a nil object
6927-- * used only in luaK:exp2RK()
6928------------------------------------------------------------------------
6929function luaK:nilK(fs)
6930 local k, v = {}, {} -- TValue
6931 self:setnilvalue(v)
6932 -- cannot use nil as key; instead use table itself to represent nil
6933 self:sethvalue(k, fs.h)
6934 return self:addk(fs, k, v)
6935end
6936
6937------------------------------------------------------------------------
6938--
6939-- * used in luaK:setmultret(), (lparser) luaY:adjust_assign()
6940------------------------------------------------------------------------
6941function luaK:setreturns(fs, e, nresults)
6942 if e.k == "VCALL" then -- expression is an open function call?
6943 luaP:SETARG_C(self:getcode(fs, e), nresults + 1)
6944 elseif e.k == "VVARARG" then
6945 luaP:SETARG_B(self:getcode(fs, e), nresults + 1);
6946 luaP:SETARG_A(self:getcode(fs, e), fs.freereg);
6947 luaK:reserveregs(fs, 1)
6948 end
6949end
6950
6951------------------------------------------------------------------------
6952--
6953-- * used in luaK:dischargevars(), (lparser) luaY:assignment()
6954------------------------------------------------------------------------
6955function luaK:setoneret(fs, e)
6956 if e.k == "VCALL" then -- expression is an open function call?
6957 e.k = "VNONRELOC"
6958 e.info = luaP:GETARG_A(self:getcode(fs, e))
6959 elseif e.k == "VVARARG" then
6960 luaP:SETARG_B(self:getcode(fs, e), 2)
6961 e.k = "VRELOCABLE" -- can relocate its simple result
6962 end
6963end
6964
6965------------------------------------------------------------------------
6966--
6967-- * used in multiple locations
6968------------------------------------------------------------------------
6969function luaK:dischargevars(fs, e)
6970 local k = e.k
6971 if k == "VLOCAL" then
6972 e.k = "VNONRELOC"
6973 elseif k == "VUPVAL" then
6974 e.info = self:codeABC(fs, "OP_GETUPVAL", 0, e.info, 0)
6975 e.k = "VRELOCABLE"
6976 elseif k == "VGLOBAL" then
6977 e.info = self:codeABx(fs, "OP_GETGLOBAL", 0, e.info)
6978 e.k = "VRELOCABLE"
6979 elseif k == "VINDEXED" then
6980 self:freereg(fs, e.aux)
6981 self:freereg(fs, e.info)
6982 e.info = self:codeABC(fs, "OP_GETTABLE", 0, e.info, e.aux)
6983 e.k = "VRELOCABLE"
6984 elseif k == "VVARARG" or k == "VCALL" then
6985 self:setoneret(fs, e)
6986 else
6987 -- there is one value available (somewhere)
6988 end
6989end
6990
6991------------------------------------------------------------------------
6992--
6993-- * used only in luaK:exp2reg()
6994------------------------------------------------------------------------
6995function luaK:code_label(fs, A, b, jump)
6996 self:getlabel(fs) -- those instructions may be jump targets
6997 return self:codeABC(fs, "OP_LOADBOOL", A, b, jump)
6998end
6999
7000------------------------------------------------------------------------
7001--
7002-- * used in luaK:discharge2anyreg(), luaK:exp2reg()
7003------------------------------------------------------------------------
7004function luaK:discharge2reg(fs, e, reg)
7005 self:dischargevars(fs, e)
7006 local k = e.k
7007 if k == "VNIL" then
7008 self:_nil(fs, reg, 1)
7009 elseif k == "VFALSE" or k == "VTRUE" then
7010 self:codeABC(fs, "OP_LOADBOOL", reg, (e.k == "VTRUE") and 1 or 0, 0)
7011 elseif k == "VK" then
7012 self:codeABx(fs, "OP_LOADK", reg, e.info)
7013 elseif k == "VKNUM" then
7014 self:codeABx(fs, "OP_LOADK", reg, self:numberK(fs, e.nval))
7015 elseif k == "VRELOCABLE" then
7016 local pc = self:getcode(fs, e)
7017 luaP:SETARG_A(pc, reg)
7018 elseif k == "VNONRELOC" then
7019 if reg ~= e.info then
7020 self:codeABC(fs, "OP_MOVE", reg, e.info, 0)
7021 end
7022 else
7023 assert(e.k == "VVOID" or e.k == "VJMP")
7024 return -- nothing to do...
7025 end
7026 e.info = reg
7027 e.k = "VNONRELOC"
7028end
7029
7030------------------------------------------------------------------------
7031--
7032-- * used in luaK:jumponcond(), luaK:codenot()
7033------------------------------------------------------------------------
7034function luaK:discharge2anyreg(fs, e)
7035 if e.k ~= "VNONRELOC" then
7036 self:reserveregs(fs, 1)
7037 self:discharge2reg(fs, e, fs.freereg - 1)
7038 end
7039end
7040
7041------------------------------------------------------------------------
7042--
7043-- * used in luaK:exp2nextreg(), luaK:exp2anyreg(), luaK:storevar()
7044------------------------------------------------------------------------
7045function luaK:exp2reg(fs, e, reg)
7046 self:discharge2reg(fs, e, reg)
7047 if e.k == "VJMP" then
7048 e.t = self:concat(fs, e.t, e.info) -- put this jump in 't' list
7049 end
7050 if self:hasjumps(e) then
7051 local final -- position after whole expression
7052 local p_f = self.NO_JUMP -- position of an eventual LOAD false
7053 local p_t = self.NO_JUMP -- position of an eventual LOAD true
7054 if self:need_value(fs, e.t) or self:need_value(fs, e.f) then
7055 local fj = (e.k == "VJMP") and self.NO_JUMP or self:jump(fs)
7056 p_f = self:code_label(fs, reg, 0, 1)
7057 p_t = self:code_label(fs, reg, 1, 0)
7058 self:patchtohere(fs, fj)
7059 end
7060 final = self:getlabel(fs)
7061 self:patchlistaux(fs, e.f, final, reg, p_f)
7062 self:patchlistaux(fs, e.t, final, reg, p_t)
7063 end
7064 e.f, e.t = self.NO_JUMP, self.NO_JUMP
7065 e.info = reg
7066 e.k = "VNONRELOC"
7067end
7068
7069------------------------------------------------------------------------
7070--
7071-- * used in multiple locations
7072------------------------------------------------------------------------
7073function luaK:exp2nextreg(fs, e)
7074 self:dischargevars(fs, e)
7075 self:freeexp(fs, e)
7076 self:reserveregs(fs, 1)
7077 self:exp2reg(fs, e, fs.freereg - 1)
7078end
7079
7080------------------------------------------------------------------------
7081--
7082-- * used in multiple locations
7083------------------------------------------------------------------------
7084function luaK:exp2anyreg(fs, e)
7085 self:dischargevars(fs, e)
7086 if e.k == "VNONRELOC" then
7087 if not self:hasjumps(e) then -- exp is already in a register
7088 return e.info
7089 end
7090 if e.info >= fs.nactvar then -- reg. is not a local?
7091 self:exp2reg(fs, e, e.info) -- put value on it
7092 return e.info
7093 end
7094 end
7095 self:exp2nextreg(fs, e) -- default
7096 return e.info
7097end
7098
7099------------------------------------------------------------------------
7100--
7101-- * used in luaK:exp2RK(), luaK:prefix(), luaK:posfix()
7102-- * used in (lparser) luaY:yindex()
7103------------------------------------------------------------------------
7104function luaK:exp2val(fs, e)
7105 if self:hasjumps(e) then
7106 self:exp2anyreg(fs, e)
7107 else
7108 self:dischargevars(fs, e)
7109 end
7110end
7111
7112------------------------------------------------------------------------
7113--
7114-- * used in multiple locations
7115------------------------------------------------------------------------
7116function luaK:exp2RK(fs, e)
7117 self:exp2val(fs, e)
7118 local k = e.k
7119 if k == "VKNUM" or k == "VTRUE" or k == "VFALSE" or k == "VNIL" then
7120 if fs.nk <= luaP.MAXINDEXRK then -- constant fit in RK operand?
7121 -- converted from a 2-deep ternary operator expression
7122 if e.k == "VNIL" then
7123 e.info = self:nilK(fs)
7124 else
7125 e.info = (e.k == "VKNUM") and self:numberK(fs, e.nval)
7126 or self:boolK(fs, e.k == "VTRUE")
7127 end
7128 e.k = "VK"
7129 return luaP:RKASK(e.info)
7130 end
7131 elseif k == "VK" then
7132 if e.info <= luaP.MAXINDEXRK then -- constant fit in argC?
7133 return luaP:RKASK(e.info)
7134 end
7135 else
7136 -- default
7137 end
7138 -- not a constant in the right range: put it in a register
7139 return self:exp2anyreg(fs, e)
7140end
7141
7142------------------------------------------------------------------------
7143--
7144-- * used in (lparser) luaY:assignment(), luaY:localfunc(), luaY:funcstat()
7145------------------------------------------------------------------------
7146function luaK:storevar(fs, var, ex)
7147 local k = var.k
7148 if k == "VLOCAL" then
7149 self:freeexp(fs, ex)
7150 self:exp2reg(fs, ex, var.info)
7151 return
7152 elseif k == "VUPVAL" then
7153 local e = self:exp2anyreg(fs, ex)
7154 self:codeABC(fs, "OP_SETUPVAL", e, var.info, 0)
7155 elseif k == "VGLOBAL" then
7156 local e = self:exp2anyreg(fs, ex)
7157 self:codeABx(fs, "OP_SETGLOBAL", e, var.info)
7158 elseif k == "VINDEXED" then
7159 local e = self:exp2RK(fs, ex)
7160 self:codeABC(fs, "OP_SETTABLE", var.info, var.aux, e)
7161 else
7162 assert(0) -- invalid var kind to store
7163 end
7164 self:freeexp(fs, ex)
7165end
7166
7167------------------------------------------------------------------------
7168--
7169-- * used only in (lparser) luaY:primaryexp()
7170------------------------------------------------------------------------
7171function luaK:_self(fs, e, key)
7172 self:exp2anyreg(fs, e)
7173 self:freeexp(fs, e)
7174 local func = fs.freereg
7175 self:reserveregs(fs, 2)
7176 self:codeABC(fs, "OP_SELF", func, e.info, self:exp2RK(fs, key))
7177 self:freeexp(fs, key)
7178 e.info = func
7179 e.k = "VNONRELOC"
7180end
7181
7182------------------------------------------------------------------------
7183--
7184-- * used in luaK:goiftrue(), luaK:codenot()
7185------------------------------------------------------------------------
7186function luaK:invertjump(fs, e)
7187 local pc = self:getjumpcontrol(fs, e.info)
7188 assert(luaP:testTMode(luaP:GET_OPCODE(pc)) ~= 0 and
7189 luaP:GET_OPCODE(pc) ~= "OP_TESTSET" and
7190 luaP:GET_OPCODE(pc) ~= "OP_TEST")
7191 luaP:SETARG_A(pc, (luaP:GETARG_A(pc) == 0) and 1 or 0)
7192end
7193
7194------------------------------------------------------------------------
7195--
7196-- * used in luaK:goiftrue(), luaK:goiffalse()
7197------------------------------------------------------------------------
7198function luaK:jumponcond(fs, e, cond)
7199 if e.k == "VRELOCABLE" then
7200 local ie = self:getcode(fs, e)
7201 if luaP:GET_OPCODE(ie) == "OP_NOT" then
7202 fs.pc = fs.pc - 1 -- remove previous OP_NOT
7203 return self:condjump(fs, "OP_TEST", luaP:GETARG_B(ie), 0, cond and 0 or 1)
7204 end
7205 -- else go through
7206 end
7207 self:discharge2anyreg(fs, e)
7208 self:freeexp(fs, e)
7209 return self:condjump(fs, "OP_TESTSET", luaP.NO_REG, e.info, cond and 1 or 0)
7210end
7211
7212------------------------------------------------------------------------
7213--
7214-- * used in luaK:infix(), (lparser) luaY:cond()
7215------------------------------------------------------------------------
7216function luaK:goiftrue(fs, e)
7217 local pc -- pc of last jump
7218 self:dischargevars(fs, e)
7219 local k = e.k
7220 if k == "VK" or k == "VKNUM" or k == "VTRUE" then
7221 pc = self.NO_JUMP -- always true; do nothing
7222 elseif k == "VFALSE" then
7223 pc = self:jump(fs) -- always jump
7224 elseif k == "VJMP" then
7225 self:invertjump(fs, e)
7226 pc = e.info
7227 else
7228 pc = self:jumponcond(fs, e, false)
7229 end
7230 e.f = self:concat(fs, e.f, pc) -- insert last jump in `f' list
7231 self:patchtohere(fs, e.t)
7232 e.t = self.NO_JUMP
7233end
7234
7235------------------------------------------------------------------------
7236--
7237-- * used in luaK:infix()
7238------------------------------------------------------------------------
7239function luaK:goiffalse(fs, e)
7240 local pc -- pc of last jump
7241 self:dischargevars(fs, e)
7242 local k = e.k
7243 if k == "VNIL" or k == "VFALSE"then
7244 pc = self.NO_JUMP -- always false; do nothing
7245 elseif k == "VTRUE" then
7246 pc = self:jump(fs) -- always jump
7247 elseif k == "VJMP" then
7248 pc = e.info
7249 else
7250 pc = self:jumponcond(fs, e, true)
7251 end
7252 e.t = self:concat(fs, e.t, pc) -- insert last jump in `t' list
7253 self:patchtohere(fs, e.f)
7254 e.f = self.NO_JUMP
7255end
7256
7257------------------------------------------------------------------------
7258--
7259-- * used only in luaK:prefix()
7260------------------------------------------------------------------------
7261function luaK:codenot(fs, e)
7262 self:dischargevars(fs, e)
7263 local k = e.k
7264 if k == "VNIL" or k == "VFALSE" then
7265 e.k = "VTRUE"
7266 elseif k == "VK" or k == "VKNUM" or k == "VTRUE" then
7267 e.k = "VFALSE"
7268 elseif k == "VJMP" then
7269 self:invertjump(fs, e)
7270 elseif k == "VRELOCABLE" or k == "VNONRELOC" then
7271 self:discharge2anyreg(fs, e)
7272 self:freeexp(fs, e)
7273 e.info = self:codeABC(fs, "OP_NOT", 0, e.info, 0)
7274 e.k = "VRELOCABLE"
7275 else
7276 assert(0) -- cannot happen
7277 end
7278 -- interchange true and false lists
7279 e.f, e.t = e.t, e.f
7280 self:removevalues(fs, e.f)
7281 self:removevalues(fs, e.t)
7282end
7283
7284------------------------------------------------------------------------
7285--
7286-- * used in (lparser) luaY:field(), luaY:primaryexp()
7287------------------------------------------------------------------------
7288function luaK:indexed(fs, t, k)
7289 t.aux = self:exp2RK(fs, k)
7290 t.k = "VINDEXED"
7291end
7292
7293------------------------------------------------------------------------
7294--
7295-- * used only in luaK:codearith()
7296------------------------------------------------------------------------
7297function luaK:constfolding(op, e1, e2)
7298 local r
7299 if not self:isnumeral(e1) or not self:isnumeral(e2) then return false end
7300 local v1 = e1.nval
7301 local v2 = e2.nval
7302 if op == "OP_ADD" then
7303 r = self:numadd(v1, v2)
7304 elseif op == "OP_SUB" then
7305 r = self:numsub(v1, v2)
7306 elseif op == "OP_MUL" then
7307 r = self:nummul(v1, v2)
7308 elseif op == "OP_DIV" then
7309 if v2 == 0 then return false end -- do not attempt to divide by 0
7310 r = self:numdiv(v1, v2)
7311 elseif op == "OP_MOD" then
7312 if v2 == 0 then return false end -- do not attempt to divide by 0
7313 r = self:nummod(v1, v2)
7314 elseif op == "OP_POW" then
7315 r = self:numpow(v1, v2)
7316 elseif op == "OP_UNM" then
7317 r = self:numunm(v1)
7318 elseif op == "OP_LEN" then
7319 return false -- no constant folding for 'len'
7320 else
7321 assert(0)
7322 r = 0
7323 end
7324 if self:numisnan(r) then return false end -- do not attempt to produce NaN
7325 e1.nval = r
7326 return true
7327end
7328
7329------------------------------------------------------------------------
7330--
7331-- * used in luaK:prefix(), luaK:posfix()
7332------------------------------------------------------------------------
7333function luaK:codearith(fs, op, e1, e2)
7334 if self:constfolding(op, e1, e2) then
7335 return
7336 else
7337 local o2 = (op ~= "OP_UNM" and op ~= "OP_LEN") and self:exp2RK(fs, e2) or 0
7338 local o1 = self:exp2RK(fs, e1)
7339 if o1 > o2 then
7340 self:freeexp(fs, e1)
7341 self:freeexp(fs, e2)
7342 else
7343 self:freeexp(fs, e2)
7344 self:freeexp(fs, e1)
7345 end
7346 e1.info = self:codeABC(fs, op, 0, o1, o2)
7347 e1.k = "VRELOCABLE"
7348 end
7349end
7350
7351------------------------------------------------------------------------
7352--
7353-- * used only in luaK:posfix()
7354------------------------------------------------------------------------
7355function luaK:codecomp(fs, op, cond, e1, e2)
7356 local o1 = self:exp2RK(fs, e1)
7357 local o2 = self:exp2RK(fs, e2)
7358 self:freeexp(fs, e2)
7359 self:freeexp(fs, e1)
7360 if cond == 0 and op ~= "OP_EQ" then
7361 -- exchange args to replace by `<' or `<='
7362 o1, o2 = o2, o1 -- o1 <==> o2
7363 cond = 1
7364 end
7365 e1.info = self:condjump(fs, op, cond, o1, o2)
7366 e1.k = "VJMP"
7367end
7368
7369------------------------------------------------------------------------
7370--
7371-- * used only in (lparser) luaY:subexpr()
7372------------------------------------------------------------------------
7373function luaK:prefix(fs, op, e)
7374 local e2 = {} -- expdesc
7375 e2.t, e2.f = self.NO_JUMP, self.NO_JUMP
7376 e2.k = "VKNUM"
7377 e2.nval = 0
7378 if op == "OPR_MINUS" then
7379 if not self:isnumeral(e) then
7380 self:exp2anyreg(fs, e) -- cannot operate on non-numeric constants
7381 end
7382 self:codearith(fs, "OP_UNM", e, e2)
7383 elseif op == "OPR_NOT" then
7384 self:codenot(fs, e)
7385 elseif op == "OPR_LEN" then
7386 self:exp2anyreg(fs, e) -- cannot operate on constants
7387 self:codearith(fs, "OP_LEN", e, e2)
7388 else
7389 assert(0)
7390 end
7391end
7392
7393------------------------------------------------------------------------
7394--
7395-- * used only in (lparser) luaY:subexpr()
7396------------------------------------------------------------------------
7397function luaK:infix(fs, op, v)
7398 if op == "OPR_AND" then
7399 self:goiftrue(fs, v)
7400 elseif op == "OPR_OR" then
7401 self:goiffalse(fs, v)
7402 elseif op == "OPR_CONCAT" then
7403 self:exp2nextreg(fs, v) -- operand must be on the 'stack'
7404 elseif op == "OPR_ADD" or op == "OPR_SUB" or
7405 op == "OPR_MUL" or op == "OPR_DIV" or
7406 op == "OPR_MOD" or op == "OPR_POW" then
7407 if not self:isnumeral(v) then self:exp2RK(fs, v) end
7408 else
7409 self:exp2RK(fs, v)
7410 end
7411end
7412
7413------------------------------------------------------------------------
7414--
7415-- * used only in (lparser) luaY:subexpr()
7416------------------------------------------------------------------------
7417-- table lookups to simplify testing
7418luaK.arith_op = {
7419 OPR_ADD = "OP_ADD", OPR_SUB = "OP_SUB", OPR_MUL = "OP_MUL",
7420 OPR_DIV = "OP_DIV", OPR_MOD = "OP_MOD", OPR_POW = "OP_POW",
7421}
7422luaK.comp_op = {
7423 OPR_EQ = "OP_EQ", OPR_NE = "OP_EQ", OPR_LT = "OP_LT",
7424 OPR_LE = "OP_LE", OPR_GT = "OP_LT", OPR_GE = "OP_LE",
7425}
7426luaK.comp_cond = {
7427 OPR_EQ = 1, OPR_NE = 0, OPR_LT = 1,
7428 OPR_LE = 1, OPR_GT = 0, OPR_GE = 0,
7429}
7430function luaK:posfix(fs, op, e1, e2)
7431 -- needed because e1 = e2 doesn't copy values...
7432 -- * in 5.0.x, only k/info/aux/t/f copied, t for AND, f for OR
7433 -- but here, all elements are copied for completeness' sake
7434 local function copyexp(e1, e2)
7435 e1.k = e2.k
7436 e1.info = e2.info; e1.aux = e2.aux
7437 e1.nval = e2.nval
7438 e1.t = e2.t; e1.f = e2.f
7439 end
7440 if op == "OPR_AND" then
7441 assert(e1.t == self.NO_JUMP) -- list must be closed
7442 self:dischargevars(fs, e2)
7443 e2.f = self:concat(fs, e2.f, e1.f)
7444 copyexp(e1, e2)
7445 elseif op == "OPR_OR" then
7446 assert(e1.f == self.NO_JUMP) -- list must be closed
7447 self:dischargevars(fs, e2)
7448 e2.t = self:concat(fs, e2.t, e1.t)
7449 copyexp(e1, e2)
7450 elseif op == "OPR_CONCAT" then
7451 self:exp2val(fs, e2)
7452 if e2.k == "VRELOCABLE" and luaP:GET_OPCODE(self:getcode(fs, e2)) == "OP_CONCAT" then
7453 assert(e1.info == luaP:GETARG_B(self:getcode(fs, e2)) - 1)
7454 self:freeexp(fs, e1)
7455 luaP:SETARG_B(self:getcode(fs, e2), e1.info)
7456 e1.k = "VRELOCABLE"
7457 e1.info = e2.info
7458 else
7459 self:exp2nextreg(fs, e2) -- operand must be on the 'stack'
7460 self:codearith(fs, "OP_CONCAT", e1, e2)
7461 end
7462 else
7463 -- the following uses a table lookup in place of conditionals
7464 local arith = self.arith_op[op]
7465 if arith then
7466 self:codearith(fs, arith, e1, e2)
7467 else
7468 local comp = self.comp_op[op]
7469 if comp then
7470 self:codecomp(fs, comp, self.comp_cond[op], e1, e2)
7471 else
7472 assert(0)
7473 end
7474 end--if arith
7475 end--if op
7476end
7477
7478------------------------------------------------------------------------
7479-- adjusts debug information for last instruction written, in order to
7480-- change the line where item comes into existence
7481-- * used in (lparser) luaY:funcargs(), luaY:forbody(), luaY:funcstat()
7482------------------------------------------------------------------------
7483function luaK:fixline(fs, line)
7484 fs.f.lineinfo[fs.pc - 1] = line
7485end
7486
7487------------------------------------------------------------------------
7488-- general function to write an instruction into the instruction buffer,
7489-- sets debug information too
7490-- * used in luaK:codeABC(), luaK:codeABx()
7491-- * called directly by (lparser) luaY:whilestat()
7492------------------------------------------------------------------------
7493function luaK:code(fs, i, line)
7494 local f = fs.f
7495 self:dischargejpc(fs) -- 'pc' will change
7496 -- put new instruction in code array
7497 luaY:growvector(fs.L, f.code, fs.pc, f.sizecode, nil,
7498 luaY.MAX_INT, "code size overflow")
7499 f.code[fs.pc] = i
7500 -- save corresponding line information
7501 luaY:growvector(fs.L, f.lineinfo, fs.pc, f.sizelineinfo, nil,
7502 luaY.MAX_INT, "code size overflow")
7503 f.lineinfo[fs.pc] = line
7504 local pc = fs.pc
7505 fs.pc = fs.pc + 1
7506 return pc
7507end
7508
7509------------------------------------------------------------------------
7510-- writes an instruction of type ABC
7511-- * calls luaK:code()
7512------------------------------------------------------------------------
7513function luaK:codeABC(fs, o, a, b, c)
7514 assert(luaP:getOpMode(o) == luaP.OpMode.iABC)
7515 assert(luaP:getBMode(o) ~= luaP.OpArgMask.OpArgN or b == 0)
7516 assert(luaP:getCMode(o) ~= luaP.OpArgMask.OpArgN or c == 0)
7517 return self:code(fs, luaP:CREATE_ABC(o, a, b, c), fs.ls.lastline)
7518end
7519
7520------------------------------------------------------------------------
7521-- writes an instruction of type ABx
7522-- * calls luaK:code(), called by luaK:codeAsBx()
7523------------------------------------------------------------------------
7524function luaK:codeABx(fs, o, a, bc)
7525 assert(luaP:getOpMode(o) == luaP.OpMode.iABx or
7526 luaP:getOpMode(o) == luaP.OpMode.iAsBx)
7527 assert(luaP:getCMode(o) == luaP.OpArgMask.OpArgN)
7528 return self:code(fs, luaP:CREATE_ABx(o, a, bc), fs.ls.lastline)
7529end
7530
7531------------------------------------------------------------------------
7532--
7533-- * used in (lparser) luaY:closelistfield(), luaY:lastlistfield()
7534------------------------------------------------------------------------
7535function luaK:setlist(fs, base, nelems, tostore)
7536 local c = math.floor((nelems - 1)/luaP.LFIELDS_PER_FLUSH) + 1
7537 local b = (tostore == luaY.LUA_MULTRET) and 0 or tostore
7538 assert(tostore ~= 0)
7539 if c <= luaP.MAXARG_C then
7540 self:codeABC(fs, "OP_SETLIST", base, b, c)
7541 else
7542 self:codeABC(fs, "OP_SETLIST", base, b, 0)
7543 self:code(fs, luaP:CREATE_Inst(c), fs.ls.lastline)
7544 end
7545 fs.freereg = base + 1 -- free registers with list values
7546end
7547
7548return function(a) luaY = a return luaK end]]></ProtectedString>
7549 <BinaryString name="Tags"></BinaryString>
7550 </Properties>
7551 </Item>
7552 <Item class="ModuleScript" referent="RBX5721C2C4E9AA4045A3AE5FC31529EF48">
7553 <Properties>
7554 <Content name="LinkedSource"><null></null></Content>
7555 <string name="Name">LuaU</string>
7556 <string name="ScriptGuid">{9C277632-5516-4FB9-AC62-AC3F3BE90D9E}</string>
7557 <ProtectedString name="Source"><![CDATA[--[[--------------------------------------------------------------------
7558
7559 ldump.lua
7560 Save precompiled Lua chunks
7561 This file is part of Yueliang.
7562
7563 Copyright (c) 2006 Kein-Hong Man <khman@users.sf.net>
7564 The COPYRIGHT file describes the conditions
7565 under which this software may be distributed.
7566
7567 See the ChangeLog for more information.
7568
7569----------------------------------------------------------------------]]
7570
7571--[[--------------------------------------------------------------------
7572-- Notes:
7573-- * WARNING! byte order (little endian) and data type sizes for header
7574-- signature values hard-coded; see luaU:header
7575-- * chunk writer generators are included, see below
7576-- * one significant difference is that instructions are still in table
7577-- form (with OP/A/B/C/Bx fields) and luaP:Instruction() is needed to
7578-- convert them into 4-char strings
7579--
7580-- Not implemented:
7581-- * DumpVar, DumpMem has been removed
7582-- * DumpVector folded into folded into DumpDebug, DumpCode
7583--
7584-- Added:
7585-- * for convenience, the following two functions have been added:
7586-- luaU:make_setS: create a chunk writer that writes to a string
7587-- luaU:make_setF: create a chunk writer that writes to a file
7588-- (lua.h contains a typedef for lua_Writer/lua_Chunkwriter, and
7589-- a Lua-based implementation exists, writer() in lstrlib.c)
7590-- * luaU:ttype(o) (from lobject.h)
7591-- * for converting number types to its binary equivalent:
7592-- luaU:from_double(x): encode double value for writing
7593-- luaU:from_int(x): encode integer value for writing
7594-- (error checking is limited for these conversion functions)
7595-- (double conversion does not support denormals or NaNs)
7596--
7597-- Changed in 5.1.x:
7598-- * the dumper was mostly rewritten in Lua 5.1.x, so notes on the
7599-- differences between 5.0.x and 5.1.x is limited
7600-- * LUAC_VERSION bumped to 0x51, LUAC_FORMAT added
7601-- * developer is expected to adjust LUAC_FORMAT in order to identify
7602-- non-standard binary chunk formats
7603-- * header signature code is smaller, has been simplified, and is
7604-- tested as a single unit; its logic is shared with the undumper
7605-- * no more endian conversion, invalid endianness mean rejection
7606-- * opcode field sizes are no longer exposed in the header
7607-- * code moved to front of a prototype, followed by constants
7608-- * debug information moved to the end of the binary chunk, and the
7609-- relevant functions folded into a single function
7610-- * luaU:dump returns a writer status code
7611-- * chunk writer now implements status code because dumper uses it
7612-- * luaU:endianness removed
7613----------------------------------------------------------------------]]
7614
7615--requires luaP
7616local luaU = {}
7617local luaP = require(script.Parent.LuaP)
7618
7619-- mark for precompiled code ('<esc>Lua') (from lua.h)
7620luaU.LUA_SIGNATURE = "\27Lua"
7621
7622-- constants used by dumper (from lua.h)
7623luaU.LUA_TNUMBER = 3
7624luaU.LUA_TSTRING = 4
7625luaU.LUA_TNIL = 0
7626luaU.LUA_TBOOLEAN = 1
7627luaU.LUA_TNONE = -1
7628
7629-- constants for header of binary files (from lundump.h)
7630luaU.LUAC_VERSION = 0x51 -- this is Lua 5.1
7631luaU.LUAC_FORMAT = 0 -- this is the official format
7632luaU.LUAC_HEADERSIZE = 12 -- size of header of binary files
7633
7634--[[--------------------------------------------------------------------
7635-- Additional functions to handle chunk writing
7636-- * to use make_setS and make_setF, see test_ldump.lua elsewhere
7637----------------------------------------------------------------------]]
7638
7639------------------------------------------------------------------------
7640-- create a chunk writer that writes to a string
7641-- * returns the writer function and a table containing the string
7642-- * to get the final result, look in buff.data
7643------------------------------------------------------------------------
7644function luaU:make_setS()
7645 local buff = {}
7646 buff.data = ""
7647 local writer =
7648 function(s, buff) -- chunk writer
7649 if not s then return 0 end
7650 buff.data = buff.data..s
7651 return 0
7652 end
7653 return writer, buff
7654end
7655
7656------------------------------------------------------------------------
7657-- create a chunk writer that writes to a file
7658-- * returns the writer function and a table containing the file handle
7659-- * if a nil is passed, then writer should close the open file
7660------------------------------------------------------------------------
7661
7662--[[
7663function luaU:make_setF(filename)
7664 local buff = {}
7665 buff.h = io.open(filename, "wb")
7666 if not buff.h then return nil end
7667 local writer =
7668 function(s, buff) -- chunk writer
7669 if not buff.h then return 0 end
7670 if not s then
7671 if buff.h:close() then return 0 end
7672 else
7673 if buff.h:write(s) then return 0 end
7674 end
7675 return 1
7676 end
7677 return writer, buff
7678end--]]
7679
7680------------------------------------------------------------------------
7681-- works like the lobject.h version except that TObject used in these
7682-- scripts only has a 'value' field, no 'tt' field (native types used)
7683------------------------------------------------------------------------
7684function luaU:ttype(o)
7685 local tt = type(o.value)
7686 if tt == "number" then return self.LUA_TNUMBER
7687 elseif tt == "string" then return self.LUA_TSTRING
7688 elseif tt == "nil" then return self.LUA_TNIL
7689 elseif tt == "boolean" then return self.LUA_TBOOLEAN
7690 else
7691 return self.LUA_TNONE -- the rest should not appear
7692 end
7693end
7694
7695-----------------------------------------------------------------------
7696-- converts a IEEE754 double number to an 8-byte little-endian string
7697-- * luaU:from_double() and luaU:from_int() are adapted from ChunkBake
7698-- * supports +/- Infinity, but not denormals or NaNs
7699-----------------------------------------------------------------------
7700function luaU:from_double(x)
7701 local function grab_byte(v)
7702 local c = v % 256
7703 return (v - c) / 256, string.char(c)
7704 end
7705 local sign = 0
7706 if x < 0 then sign = 1; x = -x end
7707 local mantissa, exponent = math.frexp(x)
7708 if x == 0 then -- zero
7709 mantissa, exponent = 0, 0
7710 elseif x == 1/0 then
7711 mantissa, exponent = 0, 2047
7712 else
7713 mantissa = (mantissa * 2 - 1) * math.ldexp(0.5, 53)
7714 exponent = exponent + 1022
7715 end
7716 local v, byte = "" -- convert to bytes
7717 x = math.floor(mantissa)
7718 for i = 1,6 do
7719 x, byte = grab_byte(x); v = v..byte -- 47:0
7720 end
7721 x, byte = grab_byte(exponent * 16 + x); v = v..byte -- 55:48
7722 x, byte = grab_byte(sign * 128 + x); v = v..byte -- 63:56
7723 return v
7724end
7725
7726-----------------------------------------------------------------------
7727-- converts a number to a little-endian 32-bit integer string
7728-- * input value assumed to not overflow, can be signed/unsigned
7729-----------------------------------------------------------------------
7730function luaU:from_int(x)
7731 local v = ""
7732 x = math.floor(x)
7733 if x < 0 then x = 4294967296 + x end -- ULONG_MAX+1
7734 for i = 1, 4 do
7735 local c = x % 256
7736 v = v..string.char(c); x = math.floor(x / 256)
7737 end
7738 return v
7739end
7740
7741--[[--------------------------------------------------------------------
7742-- Functions to make a binary chunk
7743-- * many functions have the size parameter removed, since output is
7744-- in the form of a string and some sizes are implicit or hard-coded
7745----------------------------------------------------------------------]]
7746
7747--[[--------------------------------------------------------------------
7748-- struct DumpState:
7749-- L -- lua_State (not used in this script)
7750-- writer -- lua_Writer (chunk writer function)
7751-- data -- void* (chunk writer context or data already written)
7752-- strip -- if true, don't write any debug information
7753-- status -- if non-zero, an error has occured
7754----------------------------------------------------------------------]]
7755
7756------------------------------------------------------------------------
7757-- dumps a block of bytes
7758-- * lua_unlock(D.L), lua_lock(D.L) unused
7759------------------------------------------------------------------------
7760function luaU:DumpBlock(b, D)
7761 if D.status == 0 then
7762 -- lua_unlock(D->L);
7763 D.status = D.write(b, D.data)
7764 -- lua_lock(D->L);
7765 end
7766end
7767
7768------------------------------------------------------------------------
7769-- dumps a char
7770------------------------------------------------------------------------
7771function luaU:DumpChar(y, D)
7772 self:DumpBlock(string.char(y), D)
7773end
7774
7775------------------------------------------------------------------------
7776-- dumps a 32-bit signed or unsigned integer (for int) (hard-coded)
7777------------------------------------------------------------------------
7778function luaU:DumpInt(x, D)
7779 self:DumpBlock(self:from_int(x), D)
7780end
7781
7782------------------------------------------------------------------------
7783-- dumps a lua_Number (hard-coded as a double)
7784------------------------------------------------------------------------
7785function luaU:DumpNumber(x, D)
7786 self:DumpBlock(self:from_double(x), D)
7787end
7788
7789------------------------------------------------------------------------
7790-- dumps a Lua string (size type is hard-coded)
7791------------------------------------------------------------------------
7792function luaU:DumpString(s, D)
7793 if s == nil then
7794 self:DumpInt(0, D)
7795 else
7796 s = s.."\0" -- include trailing '\0'
7797 self:DumpInt(#s, D)
7798 self:DumpBlock(s, D)
7799 end
7800end
7801
7802------------------------------------------------------------------------
7803-- dumps instruction block from function prototype
7804------------------------------------------------------------------------
7805function luaU:DumpCode(f, D)
7806 local n = f.sizecode
7807 --was DumpVector
7808 self:DumpInt(n, D)
7809 for i = 0, n - 1 do
7810 self:DumpBlock(luaP:Instruction(f.code[i]), D)
7811 end
7812end
7813
7814------------------------------------------------------------------------
7815-- dump constant pool from function prototype
7816-- * bvalue(o), nvalue(o) and rawtsvalue(o) macros removed
7817------------------------------------------------------------------------
7818function luaU:DumpConstants(f, D)
7819 local n = f.sizek
7820 self:DumpInt(n, D)
7821 for i = 0, n - 1 do
7822 local o = f.k[i] -- TValue
7823 local tt = self:ttype(o)
7824 self:DumpChar(tt, D)
7825 if tt == self.LUA_TNIL then
7826 elseif tt == self.LUA_TBOOLEAN then
7827 self:DumpChar(o.value and 1 or 0, D)
7828 elseif tt == self.LUA_TNUMBER then
7829 self:DumpNumber(o.value, D)
7830 elseif tt == self.LUA_TSTRING then
7831 self:DumpString(o.value, D)
7832 else
7833 --lua_assert(0) -- cannot happen
7834 end
7835 end
7836 n = f.sizep
7837 self:DumpInt(n, D)
7838 for i = 0, n - 1 do
7839 self:DumpFunction(f.p[i], f.source, D)
7840 end
7841end
7842
7843------------------------------------------------------------------------
7844-- dump debug information
7845------------------------------------------------------------------------
7846function luaU:DumpDebug(f, D)
7847 local n
7848 n = D.strip and 0 or f.sizelineinfo -- dump line information
7849 --was DumpVector
7850 self:DumpInt(n, D)
7851 for i = 0, n - 1 do
7852 self:DumpInt(f.lineinfo[i], D)
7853 end
7854 n = D.strip and 0 or f.sizelocvars -- dump local information
7855 self:DumpInt(n, D)
7856 for i = 0, n - 1 do
7857 self:DumpString(f.locvars[i].varname, D)
7858 self:DumpInt(f.locvars[i].startpc, D)
7859 self:DumpInt(f.locvars[i].endpc, D)
7860 end
7861 n = D.strip and 0 or f.sizeupvalues -- dump upvalue information
7862 self:DumpInt(n, D)
7863 for i = 0, n - 1 do
7864 self:DumpString(f.upvalues[i], D)
7865 end
7866end
7867
7868------------------------------------------------------------------------
7869-- dump child function prototypes from function prototype
7870------------------------------------------------------------------------
7871function luaU:DumpFunction(f, p, D)
7872 local source = f.source
7873 if source == p or D.strip then source = nil end
7874 self:DumpString(source, D)
7875 self:DumpInt(f.lineDefined, D)
7876 self:DumpInt(f.lastlinedefined, D)
7877 self:DumpChar(f.nups, D)
7878 self:DumpChar(f.numparams, D)
7879 self:DumpChar(f.is_vararg, D)
7880 self:DumpChar(f.maxstacksize, D)
7881 self:DumpCode(f, D)
7882 self:DumpConstants(f, D)
7883 self:DumpDebug(f, D)
7884end
7885
7886------------------------------------------------------------------------
7887-- dump Lua header section (some sizes hard-coded)
7888------------------------------------------------------------------------
7889function luaU:DumpHeader(D)
7890 local h = self:header()
7891 assert(#h == self.LUAC_HEADERSIZE) -- fixed buffer now an assert
7892 self:DumpBlock(h, D)
7893end
7894
7895------------------------------------------------------------------------
7896-- make header (from lundump.c)
7897-- returns the header string
7898------------------------------------------------------------------------
7899function luaU:header()
7900 local x = 1
7901 return self.LUA_SIGNATURE..
7902 string.char(
7903 self.LUAC_VERSION,
7904 self.LUAC_FORMAT,
7905 x, -- endianness (1=little)
7906 4, -- sizeof(int)
7907 4, -- sizeof(size_t)
7908 4, -- sizeof(Instruction)
7909 8, -- sizeof(lua_Number)
7910 0) -- is lua_Number integral?
7911end
7912
7913------------------------------------------------------------------------
7914-- dump Lua function as precompiled chunk
7915-- (lua_State* L, const Proto* f, lua_Writer w, void* data, int strip)
7916-- * w, data are created from make_setS, make_setF
7917------------------------------------------------------------------------
7918function luaU:dump(L, f, w, data, strip)
7919 local D = {} -- DumpState
7920 D.L = L
7921 D.write = w
7922 D.data = data
7923 D.strip = strip
7924 D.status = 0
7925 self:DumpHeader(D)
7926 self:DumpFunction(f, nil, D)
7927 -- added: for a chunk writer writing to a file, this final call with
7928 -- nil data is to indicate to the writer to close the file
7929 D.write(nil, D.data)
7930 return D.status
7931end
7932
7933return luaU]]></ProtectedString>
7934 <BinaryString name="Tags"></BinaryString>
7935 </Properties>
7936 </Item>
7937 <Item class="ModuleScript" referent="RBX860D6B646D15448DA00363761F66761F">
7938 <Properties>
7939 <Content name="LinkedSource"><null></null></Content>
7940 <string name="Name">LuaP</string>
7941 <string name="ScriptGuid">{F6F5E123-B39B-4C8A-B113-E644BAE8AA75}</string>
7942 <ProtectedString name="Source"><![CDATA[--[[--------------------------------------------------------------------
7943
7944 lopcodes.lua
7945 Lua 5 virtual machine opcodes in Lua
7946 This file is part of Yueliang.
7947
7948 Copyright (c) 2006 Kein-Hong Man <khman@users.sf.net>
7949 The COPYRIGHT file describes the conditions
7950 under which this software may be distributed.
7951
7952 See the ChangeLog for more information.
7953
7954----------------------------------------------------------------------]]
7955
7956--[[--------------------------------------------------------------------
7957-- Notes:
7958-- * an Instruction is a table with OP, A, B, C, Bx elements; this
7959-- makes the code easy to follow and should allow instruction handling
7960-- to work with doubles and ints
7961-- * WARNING luaP:Instruction outputs instructions encoded in little-
7962-- endian form and field size and positions are hard-coded
7963--
7964-- Not implemented:
7965-- *
7966--
7967-- Added:
7968-- * luaP:CREATE_Inst(c): create an inst from a number (for OP_SETLIST)
7969-- * luaP:Instruction(i): convert field elements to a 4-char string
7970-- * luaP:DecodeInst(x): convert 4-char string into field elements
7971--
7972-- Changed in 5.1.x:
7973-- * POS_OP added, instruction field positions changed
7974-- * some symbol names may have changed, e.g. LUAI_BITSINT
7975-- * new operators for RK indices: BITRK, ISK(x), INDEXK(r), RKASK(x)
7976-- * OP_MOD, OP_LEN is new
7977-- * OP_TEST is now OP_TESTSET, OP_TEST is new
7978-- * OP_FORLOOP, OP_TFORLOOP adjusted, OP_FORPREP is new
7979-- * OP_TFORPREP deleted
7980-- * OP_SETLIST and OP_SETLISTO merged and extended
7981-- * OP_VARARG is new
7982-- * many changes to implementation of OpMode data
7983----------------------------------------------------------------------]]
7984
7985local luaP = {}
7986
7987--[[
7988===========================================================================
7989 We assume that instructions are unsigned numbers.
7990 All instructions have an opcode in the first 6 bits.
7991 Instructions can have the following fields:
7992 'A' : 8 bits
7993 'B' : 9 bits
7994 'C' : 9 bits
7995 'Bx' : 18 bits ('B' and 'C' together)
7996 'sBx' : signed Bx
7997
7998 A signed argument is represented in excess K; that is, the number
7999 value is the unsigned value minus K. K is exactly the maximum value
8000 for that argument (so that -max is represented by 0, and +max is
8001 represented by 2*max), which is half the maximum for the corresponding
8002 unsigned argument.
8003===========================================================================
8004--]]
8005
8006luaP.OpMode = { iABC = 0, iABx = 1, iAsBx = 2 } -- basic instruction format
8007
8008------------------------------------------------------------------------
8009-- size and position of opcode arguments.
8010-- * WARNING size and position is hard-coded elsewhere in this script
8011------------------------------------------------------------------------
8012luaP.SIZE_C = 9
8013luaP.SIZE_B = 9
8014luaP.SIZE_Bx = luaP.SIZE_C + luaP.SIZE_B
8015luaP.SIZE_A = 8
8016
8017luaP.SIZE_OP = 6
8018
8019luaP.POS_OP = 0
8020luaP.POS_A = luaP.POS_OP + luaP.SIZE_OP
8021luaP.POS_C = luaP.POS_A + luaP.SIZE_A
8022luaP.POS_B = luaP.POS_C + luaP.SIZE_C
8023luaP.POS_Bx = luaP.POS_C
8024
8025------------------------------------------------------------------------
8026-- limits for opcode arguments.
8027-- we use (signed) int to manipulate most arguments,
8028-- so they must fit in LUAI_BITSINT-1 bits (-1 for sign)
8029------------------------------------------------------------------------
8030-- removed "#if SIZE_Bx < BITS_INT-1" test, assume this script is
8031-- running on a Lua VM with double or int as LUA_NUMBER
8032
8033luaP.MAXARG_Bx = math.ldexp(1, luaP.SIZE_Bx) - 1
8034luaP.MAXARG_sBx = math.floor(luaP.MAXARG_Bx / 2) -- 'sBx' is signed
8035
8036luaP.MAXARG_A = math.ldexp(1, luaP.SIZE_A) - 1
8037luaP.MAXARG_B = math.ldexp(1, luaP.SIZE_B) - 1
8038luaP.MAXARG_C = math.ldexp(1, luaP.SIZE_C) - 1
8039
8040-- creates a mask with 'n' 1 bits at position 'p'
8041-- MASK1(n,p) deleted, not required
8042-- creates a mask with 'n' 0 bits at position 'p'
8043-- MASK0(n,p) deleted, not required
8044
8045--[[--------------------------------------------------------------------
8046 Visual representation for reference:
8047
8048 31 | | | 0 bit position
8049 +-----+-----+-----+----------+
8050 | B | C | A | Opcode | iABC format
8051 +-----+-----+-----+----------+
8052 - 9 - 9 - 8 - 6 - field sizes
8053 +-----+-----+-----+----------+
8054 | [s]Bx | A | Opcode | iABx | iAsBx format
8055 +-----+-----+-----+----------+
8056
8057----------------------------------------------------------------------]]
8058
8059------------------------------------------------------------------------
8060-- the following macros help to manipulate instructions
8061-- * changed to a table object representation, very clean compared to
8062-- the [nightmare] alternatives of using a number or a string
8063-- * Bx is a separate element from B and C, since there is never a need
8064-- to split Bx in the parser or code generator
8065------------------------------------------------------------------------
8066
8067-- these accept or return opcodes in the form of string names
8068function luaP:GET_OPCODE(i) return self.ROpCode[i.OP] end
8069function luaP:SET_OPCODE(i, o) i.OP = self.OpCode[o] end
8070
8071function luaP:GETARG_A(i) return i.A end
8072function luaP:SETARG_A(i, u) i.A = u end
8073
8074function luaP:GETARG_B(i) return i.B end
8075function luaP:SETARG_B(i, b) i.B = b end
8076
8077function luaP:GETARG_C(i) return i.C end
8078function luaP:SETARG_C(i, b) i.C = b end
8079
8080function luaP:GETARG_Bx(i) return i.Bx end
8081function luaP:SETARG_Bx(i, b) i.Bx = b end
8082
8083function luaP:GETARG_sBx(i) return i.Bx - self.MAXARG_sBx end
8084function luaP:SETARG_sBx(i, b) i.Bx = b + self.MAXARG_sBx end
8085
8086function luaP:CREATE_ABC(o,a,b,c)
8087 return {OP = self.OpCode[o], A = a, B = b, C = c}
8088end
8089
8090function luaP:CREATE_ABx(o,a,bc)
8091 return {OP = self.OpCode[o], A = a, Bx = bc}
8092end
8093
8094------------------------------------------------------------------------
8095-- create an instruction from a number (for OP_SETLIST)
8096------------------------------------------------------------------------
8097function luaP:CREATE_Inst(c)
8098 local o = c % 64
8099 c = (c - o) / 64
8100 local a = c % 256
8101 c = (c - a) / 256
8102 return self:CREATE_ABx(o, a, c)
8103end
8104
8105------------------------------------------------------------------------
8106-- returns a 4-char string little-endian encoded form of an instruction
8107------------------------------------------------------------------------
8108function luaP:Instruction(i)
8109 if i.Bx then
8110 -- change to OP/A/B/C format
8111 i.C = i.Bx % 512
8112 i.B = (i.Bx - i.C) / 512
8113 end
8114 local I = i.A * 64 + i.OP
8115 local c0 = I % 256
8116 I = i.C * 64 + (I - c0) / 256 -- 6 bits of A left
8117 local c1 = I % 256
8118 I = i.B * 128 + (I - c1) / 256 -- 7 bits of C left
8119 local c2 = I % 256
8120 local c3 = (I - c2) / 256
8121 return string.char(c0, c1, c2, c3)
8122end
8123
8124------------------------------------------------------------------------
8125-- decodes a 4-char little-endian string into an instruction struct
8126------------------------------------------------------------------------
8127function luaP:DecodeInst(x)
8128 local byte = string.byte
8129 local i = {}
8130 local I = byte(x, 1)
8131 local op = I % 64
8132 i.OP = op
8133 I = byte(x, 2) * 4 + (I - op) / 64 -- 2 bits of c0 left
8134 local a = I % 256
8135 i.A = a
8136 I = byte(x, 3) * 4 + (I - a) / 256 -- 2 bits of c1 left
8137 local c = I % 512
8138 i.C = c
8139 i.B = byte(x, 4) * 2 + (I - c) / 512 -- 1 bits of c2 left
8140 local opmode = self.OpMode[tonumber(string.sub(self.opmodes[op + 1], 7, 7))]
8141 if opmode ~= "iABC" then
8142 i.Bx = i.B * 512 + i.C
8143 end
8144 return i
8145end
8146
8147------------------------------------------------------------------------
8148-- Macros to operate RK indices
8149-- * these use arithmetic instead of bit ops
8150------------------------------------------------------------------------
8151
8152-- this bit 1 means constant (0 means register)
8153luaP.BITRK = math.ldexp(1, luaP.SIZE_B - 1)
8154
8155-- test whether value is a constant
8156function luaP:ISK(x) return x >= self.BITRK end
8157
8158-- gets the index of the constant
8159function luaP:INDEXK(x) return x - self.BITRK end
8160
8161luaP.MAXINDEXRK = luaP.BITRK - 1
8162
8163-- code a constant index as a RK value
8164function luaP:RKASK(x) return x + self.BITRK end
8165
8166------------------------------------------------------------------------
8167-- invalid register that fits in 8 bits
8168------------------------------------------------------------------------
8169luaP.NO_REG = luaP.MAXARG_A
8170
8171------------------------------------------------------------------------
8172-- R(x) - register
8173-- Kst(x) - constant (in constant table)
8174-- RK(x) == if ISK(x) then Kst(INDEXK(x)) else R(x)
8175------------------------------------------------------------------------
8176
8177------------------------------------------------------------------------
8178-- grep "ORDER OP" if you change these enums
8179------------------------------------------------------------------------
8180
8181--[[--------------------------------------------------------------------
8182Lua virtual machine opcodes (enum OpCode):
8183------------------------------------------------------------------------
8184name args description
8185------------------------------------------------------------------------
8186OP_MOVE A B R(A) := R(B)
8187OP_LOADK A Bx R(A) := Kst(Bx)
8188OP_LOADBOOL A B C R(A) := (Bool)B; if (C) pc++
8189OP_LOADNIL A B R(A) := ... := R(B) := nil
8190OP_GETUPVAL A B R(A) := UpValue[B]
8191OP_GETGLOBAL A Bx R(A) := Gbl[Kst(Bx)]
8192OP_GETTABLE A B C R(A) := R(B)[RK(C)]
8193OP_SETGLOBAL A Bx Gbl[Kst(Bx)] := R(A)
8194OP_SETUPVAL A B UpValue[B] := R(A)
8195OP_SETTABLE A B C R(A)[RK(B)] := RK(C)
8196OP_NEWTABLE A B C R(A) := {} (size = B,C)
8197OP_SELF A B C R(A+1) := R(B); R(A) := R(B)[RK(C)]
8198OP_ADD A B C R(A) := RK(B) + RK(C)
8199OP_SUB A B C R(A) := RK(B) - RK(C)
8200OP_MUL A B C R(A) := RK(B) * RK(C)
8201OP_DIV A B C R(A) := RK(B) / RK(C)
8202OP_MOD A B C R(A) := RK(B) % RK(C)
8203OP_POW A B C R(A) := RK(B) ^ RK(C)
8204OP_UNM A B R(A) := -R(B)
8205OP_NOT A B R(A) := not R(B)
8206OP_LEN A B R(A) := length of R(B)
8207OP_CONCAT A B C R(A) := R(B).. ... ..R(C)
8208OP_JMP sBx pc+=sBx
8209OP_EQ A B C if ((RK(B) == RK(C)) ~= A) then pc++
8210OP_LT A B C if ((RK(B) < RK(C)) ~= A) then pc++
8211OP_LE A B C if ((RK(B) <= RK(C)) ~= A) then pc++
8212OP_TEST A C if not (R(A) <=> C) then pc++
8213OP_TESTSET A B C if (R(B) <=> C) then R(A) := R(B) else pc++
8214OP_CALL A B C R(A), ... ,R(A+C-2) := R(A)(R(A+1), ... ,R(A+B-1))
8215OP_TAILCALL A B C return R(A)(R(A+1), ... ,R(A+B-1))
8216OP_RETURN A B return R(A), ... ,R(A+B-2) (see note)
8217OP_FORLOOP A sBx R(A)+=R(A+2);
8218 if R(A) <?= R(A+1) then { pc+=sBx; R(A+3)=R(A) }
8219OP_FORPREP A sBx R(A)-=R(A+2); pc+=sBx
8220OP_TFORLOOP A C R(A+3), ... ,R(A+2+C) := R(A)(R(A+1), R(A+2));
8221 if R(A+3) ~= nil then R(A+2)=R(A+3) else pc++
8222OP_SETLIST A B C R(A)[(C-1)*FPF+i] := R(A+i), 1 <= i <= B
8223OP_CLOSE A close all variables in the stack up to (>=) R(A)
8224OP_CLOSURE A Bx R(A) := closure(KPROTO[Bx], R(A), ... ,R(A+n))
8225OP_VARARG A B R(A), R(A+1), ..., R(A+B-1) = vararg
8226----------------------------------------------------------------------]]
8227
8228luaP.opnames = {} -- opcode names
8229luaP.OpCode = {} -- lookup name -> number
8230luaP.ROpCode = {} -- lookup number -> name
8231
8232------------------------------------------------------------------------
8233-- ORDER OP
8234------------------------------------------------------------------------
8235local i = 0
8236for v in string.gmatch([[
8237MOVE LOADK LOADBOOL LOADNIL GETUPVAL
8238GETGLOBAL GETTABLE SETGLOBAL SETUPVAL SETTABLE
8239NEWTABLE SELF ADD SUB MUL
8240DIV MOD POW UNM NOT
8241LEN CONCAT JMP EQ LT
8242LE TEST TESTSET CALL TAILCALL
8243RETURN FORLOOP FORPREP TFORLOOP SETLIST
8244CLOSE CLOSURE VARARG
8245]], "%S+") do
8246 local n = "OP_"..v
8247 luaP.opnames[i] = v
8248 luaP.OpCode[n] = i
8249 luaP.ROpCode[i] = n
8250 i = i + 1
8251end
8252luaP.NUM_OPCODES = i
8253
8254--[[
8255===========================================================================
8256 Notes:
8257 (*) In OP_CALL, if (B == 0) then B = top. C is the number of returns - 1,
8258 and can be 0: OP_CALL then sets 'top' to last_result+1, so
8259 next open instruction (OP_CALL, OP_RETURN, OP_SETLIST) may use 'top'.
8260 (*) In OP_VARARG, if (B == 0) then use actual number of varargs and
8261 set top (like in OP_CALL with C == 0).
8262 (*) In OP_RETURN, if (B == 0) then return up to 'top'
8263 (*) In OP_SETLIST, if (B == 0) then B = 'top';
8264 if (C == 0) then next 'instruction' is real C
8265 (*) For comparisons, A specifies what condition the test should accept
8266 (true or false).
8267 (*) All 'skips' (pc++) assume that next instruction is a jump
8268===========================================================================
8269--]]
8270
8271--[[--------------------------------------------------------------------
8272 masks for instruction properties. The format is:
8273 bits 0-1: op mode
8274 bits 2-3: C arg mode
8275 bits 4-5: B arg mode
8276 bit 6: instruction set register A
8277 bit 7: operator is a test
8278
8279 for OpArgMask:
8280 OpArgN - argument is not used
8281 OpArgU - argument is used
8282 OpArgR - argument is a register or a jump offset
8283 OpArgK - argument is a constant or register/constant
8284----------------------------------------------------------------------]]
8285
8286-- was enum OpArgMask
8287luaP.OpArgMask = { OpArgN = 0, OpArgU = 1, OpArgR = 2, OpArgK = 3 }
8288
8289------------------------------------------------------------------------
8290-- e.g. to compare with symbols, luaP:getOpMode(...) == luaP.OpCode.iABC
8291-- * accepts opcode parameter as strings, e.g. "OP_MOVE"
8292------------------------------------------------------------------------
8293
8294function luaP:getOpMode(m)
8295 return self.opmodes[self.OpCode[m]] % 4
8296end
8297
8298function luaP:getBMode(m)
8299 return math.floor(self.opmodes[self.OpCode[m]] / 16) % 4
8300end
8301
8302function luaP:getCMode(m)
8303 return math.floor(self.opmodes[self.OpCode[m]] / 4) % 4
8304end
8305
8306function luaP:testAMode(m)
8307 return math.floor(self.opmodes[self.OpCode[m]] / 64) % 2
8308end
8309
8310function luaP:testTMode(m)
8311 return math.floor(self.opmodes[self.OpCode[m]] / 128)
8312end
8313
8314-- luaP_opnames[] is set above, as the luaP.opnames table
8315
8316-- number of list items to accumulate before a SETLIST instruction
8317luaP.LFIELDS_PER_FLUSH = 50
8318
8319------------------------------------------------------------------------
8320-- build instruction properties array
8321-- * deliberately coded to look like the C equivalent
8322------------------------------------------------------------------------
8323local function opmode(t, a, b, c, m)
8324 local luaP = luaP
8325 return t * 128 + a * 64 +
8326 luaP.OpArgMask[b] * 16 + luaP.OpArgMask[c] * 4 + luaP.OpMode[m]
8327end
8328
8329-- ORDER OP
8330luaP.opmodes = {
8331-- T A B C mode opcode
8332 opmode(0, 1, "OpArgK", "OpArgN", "iABx"), -- OP_LOADK
8333 opmode(0, 1, "OpArgU", "OpArgU", "iABC"), -- OP_LOADBOOL
8334 opmode(0, 1, "OpArgR", "OpArgN", "iABC"), -- OP_LOADNIL
8335 opmode(0, 1, "OpArgU", "OpArgN", "iABC"), -- OP_GETUPVAL
8336 opmode(0, 1, "OpArgK", "OpArgN", "iABx"), -- OP_GETGLOBAL
8337 opmode(0, 1, "OpArgR", "OpArgK", "iABC"), -- OP_GETTABLE
8338 opmode(0, 0, "OpArgK", "OpArgN", "iABx"), -- OP_SETGLOBAL
8339 opmode(0, 0, "OpArgU", "OpArgN", "iABC"), -- OP_SETUPVAL
8340 opmode(0, 0, "OpArgK", "OpArgK", "iABC"), -- OP_SETTABLE
8341 opmode(0, 1, "OpArgU", "OpArgU", "iABC"), -- OP_NEWTABLE
8342 opmode(0, 1, "OpArgR", "OpArgK", "iABC"), -- OP_SELF
8343 opmode(0, 1, "OpArgK", "OpArgK", "iABC"), -- OP_ADD
8344 opmode(0, 1, "OpArgK", "OpArgK", "iABC"), -- OP_SUB
8345 opmode(0, 1, "OpArgK", "OpArgK", "iABC"), -- OP_MUL
8346 opmode(0, 1, "OpArgK", "OpArgK", "iABC"), -- OP_DIV
8347 opmode(0, 1, "OpArgK", "OpArgK", "iABC"), -- OP_MOD
8348 opmode(0, 1, "OpArgK", "OpArgK", "iABC"), -- OP_POW
8349 opmode(0, 1, "OpArgR", "OpArgN", "iABC"), -- OP_UNM
8350 opmode(0, 1, "OpArgR", "OpArgN", "iABC"), -- OP_NOT
8351 opmode(0, 1, "OpArgR", "OpArgN", "iABC"), -- OP_LEN
8352 opmode(0, 1, "OpArgR", "OpArgR", "iABC"), -- OP_CONCAT
8353 opmode(0, 0, "OpArgR", "OpArgN", "iAsBx"), -- OP_JMP
8354 opmode(1, 0, "OpArgK", "OpArgK", "iABC"), -- OP_EQ
8355 opmode(1, 0, "OpArgK", "OpArgK", "iABC"), -- OP_LT
8356 opmode(1, 0, "OpArgK", "OpArgK", "iABC"), -- OP_LE
8357 opmode(1, 1, "OpArgR", "OpArgU", "iABC"), -- OP_TEST
8358 opmode(1, 1, "OpArgR", "OpArgU", "iABC"), -- OP_TESTSET
8359 opmode(0, 1, "OpArgU", "OpArgU", "iABC"), -- OP_CALL
8360 opmode(0, 1, "OpArgU", "OpArgU", "iABC"), -- OP_TAILCALL
8361 opmode(0, 0, "OpArgU", "OpArgN", "iABC"), -- OP_RETURN
8362 opmode(0, 1, "OpArgR", "OpArgN", "iAsBx"), -- OP_FORLOOP
8363 opmode(0, 1, "OpArgR", "OpArgN", "iAsBx"), -- OP_FORPREP
8364 opmode(1, 0, "OpArgN", "OpArgU", "iABC"), -- OP_TFORLOOP
8365 opmode(0, 0, "OpArgU", "OpArgU", "iABC"), -- OP_SETLIST
8366 opmode(0, 0, "OpArgN", "OpArgN", "iABC"), -- OP_CLOSE
8367 opmode(0, 1, "OpArgU", "OpArgN", "iABx"), -- OP_CLOSURE
8368 opmode(0, 1, "OpArgU", "OpArgN", "iABC"), -- OP_VARARG
8369}
8370-- an awkward way to set a zero-indexed table...
8371luaP.opmodes[0] =
8372 opmode(0, 1, "OpArgR", "OpArgN", "iABC") -- OP_MOVE
8373
8374return luaP]]></ProtectedString>
8375 <BinaryString name="Tags"></BinaryString>
8376 </Properties>
8377 </Item>
8378 <Item class="ModuleScript" referent="RBX8B078DFEAA7F44208ECF5696D66AEBB3">
8379 <Properties>
8380 <Content name="LinkedSource"><null></null></Content>
8381 <string name="Name">Rerubi</string>
8382 <string name="ScriptGuid">{8CDD4A3E-999B-4E2B-ACC4-C871AF954901}</string>
8383 <ProtectedString name="Source"><![CDATA[local Select = select;
8384local Byte = string.byte;
8385local Sub = string.sub;
8386
8387local Opmode = {
8388 {b = 'OpArgR', c='OpArgN'}, {b = 'OpArgK', c='OpArgN'}, {b = 'OpArgU', c='OpArgU'},
8389 {b = 'OpArgR', c='OpArgN'}, {b = 'OpArgU', c='OpArgN'}, {b = 'OpArgK', c='OpArgN'},
8390 {b = 'OpArgR', c='OpArgK'}, {b = 'OpArgK', c='OpArgN'}, {b = 'OpArgU', c='OpArgN'},
8391 {b = 'OpArgK', c='OpArgK'}, {b = 'OpArgU', c='OpArgU'}, {b = 'OpArgR', c='OpArgK'},
8392 {b = 'OpArgK', c='OpArgK'}, {b = 'OpArgK', c='OpArgK'}, {b = 'OpArgK', c='OpArgK'},
8393 {b = 'OpArgK', c='OpArgK'}, {b = 'OpArgK', c='OpArgK'}, {b = 'OpArgK', c='OpArgK'},
8394 {b = 'OpArgR', c='OpArgN'}, {b = 'OpArgR', c='OpArgN'}, {b = 'OpArgR', c='OpArgN'},
8395 {b = 'OpArgR', c='OpArgR'}, {b = 'OpArgR', c='OpArgN'}, {b = 'OpArgK', c='OpArgK'},
8396 {b = 'OpArgK', c='OpArgK'}, {b = 'OpArgK', c='OpArgK'}, {b = 'OpArgR', c='OpArgU'},
8397 {b = 'OpArgR', c='OpArgU'}, {b = 'OpArgU', c='OpArgU'}, {b = 'OpArgU', c='OpArgU'},
8398 {b = 'OpArgU', c='OpArgN'}, {b = 'OpArgR', c='OpArgN'}, {b = 'OpArgR', c='OpArgN'},
8399 {b = 'OpArgN', c='OpArgU'}, {b = 'OpArgU', c='OpArgU'}, {b = 'OpArgN', c='OpArgN'},
8400 {b = 'OpArgU', c='OpArgN'}, {b = 'OpArgU', c='OpArgN'}
8401};
8402
8403local Opcode = { -- Opcode types.
8404 'ABC', 'ABx', 'ABC', 'ABC';
8405 'ABC', 'ABx', 'ABC', 'ABx';
8406 'ABC', 'ABC', 'ABC', 'ABC';
8407 'ABC', 'ABC', 'ABC', 'ABC';
8408 'ABC', 'ABC', 'ABC', 'ABC';
8409 'ABC', 'ABC', 'AsBx', 'ABC';
8410 'ABC', 'ABC', 'ABC', 'ABC';
8411 'ABC', 'ABC', 'ABC', 'AsBx';
8412 'AsBx', 'ABC', 'ABC', 'ABC';
8413 'ABx', 'ABC';
8414};
8415
8416-- rlbi author -> Rerumu
8417-- special thanks;
8418-- @cntkillme for providing faster bit extraction
8419-- @Eternal for being #1 bug finder and providing better float decoder
8420-- @stravant for contributing to the original project this is derived from
8421
8422-- rerubi is an upgrade to the original Lua VM in Lua
8423-- the prime goal of rerubi is to be the fastest:tm: alternative
8424-- to a Lua in Lua bytecode execution
8425
8426local function gBit(Bit, Start, End) -- No tail-calls, yay.
8427 if End then -- Thanks to cntkillme for giving input on this shorter, better approach.
8428 local Res = (Bit / 2 ^ (Start - 1)) % 2 ^ ((End - 1) - (Start - 1) + 1);
8429
8430 return Res - Res % 1;
8431 else
8432 local Plc = 2 ^ (Start - 1);
8433
8434 if (Bit % (Plc + Plc) >= Plc) then
8435 return 1;
8436 else
8437 return 0;
8438 end;
8439 end;
8440end;
8441
8442local function GetMeaning(ByteString)
8443 local Pos = 1;
8444 local gSizet;
8445 local gInt;
8446
8447 local function gBits8() -- Get the next byte in the stream.
8448 local F = Byte(ByteString, Pos, Pos);
8449
8450 Pos = Pos + 1;
8451
8452 return F;
8453 end;
8454
8455 local function gBits32()
8456 local W, X, Y, Z = Byte(ByteString, Pos, Pos + 3);
8457
8458 Pos = Pos + 4;
8459
8460 return (Z * 16777216) + (Y * 65536) + (X * 256) + W;
8461 end;
8462
8463 local function gBits64()
8464 return gBits32() * 4294967296 + gBits32();
8465 end;
8466
8467 local function gFloat()
8468 -- thanks @Eternal for giving me this so I could mangle it in here and have it work
8469 local Left = gBits32();
8470 local Right = gBits32();
8471 local IsNormal = 1
8472 local Mantissa = (gBit(Right, 1, 20) * (2 ^ 32))
8473 + Left;
8474
8475 local Exponent = gBit(Right, 21, 31);
8476 local Sign = ((-1) ^ gBit(Right, 32));
8477
8478 if (Exponent == 0) then
8479 if (Mantissa == 0) then
8480 return Sign * 0 -- +-0
8481 else
8482 Exponent = 1
8483 IsNormal = 0
8484 end
8485 elseif (Exponent == 2047) then
8486 if (Mantissa == 0) then
8487 return Sign * (1 / 0) -- +-Inf
8488 else
8489 return Sign * (0 / 0) -- +-Q/Nan
8490 end
8491 end
8492
8493 -- sign * 2**e-1023 * isNormal.mantissa
8494 return math.ldexp(Sign, Exponent - 1023) * (IsNormal + (Mantissa / (2 ^ 52)))
8495 end;
8496
8497 local function gString(Len)
8498 local Str;
8499
8500 if Len then
8501 Str = Sub(ByteString, Pos, Pos + Len - 1);
8502
8503 Pos = Pos + Len;
8504 else
8505 Len = gSizet();
8506
8507 if (Len == 0) then return; end;
8508
8509 Str = Sub(ByteString, Pos, Pos + Len - 1);
8510
8511 Pos = Pos + Len;
8512 end;
8513
8514 return Str;
8515 end;
8516
8517 local function ChunkDecode()
8518 local Instr = {};
8519 local Const = {};
8520 local Proto = {};
8521 local Chunk = {
8522 Instr = Instr; -- Instructions
8523 Const = Const; -- Constants
8524 Proto = Proto; -- Prototypes
8525 Lines = {}; -- Lines
8526 Name = gString(); -- Grab name string.
8527 FirstL = gInt(); -- First line.
8528 LastL = gInt(); -- Last line.
8529 Upvals = gBits8(); -- Upvalue count.
8530 Args = gBits8(); -- Arg count.
8531 Vargs = gBits8(); -- Vararg type.
8532 Stack = gBits8(); -- Stack.
8533 };
8534 local ConstantReferences = {}; -- for an optimization
8535
8536 if Chunk.Name then
8537 Chunk.Name = Sub(Chunk.Name, 1, -2);
8538 end;
8539
8540
8541 for Idx = 1, gInt() do -- Loading instructions to the chunk.
8542 local Data = gBits32();
8543 local Opco = gBit(Data, 1, 6);
8544 local Type = Opcode[Opco + 1];
8545 local Mode = Opmode[Opco + 1];
8546
8547 local Inst = {
8548 Enum = Opco;
8549 Value = Data;
8550 gBit(Data, 7, 14); -- Register A.
8551 };
8552
8553 if (Type == 'ABC') then -- Most common, basic instruction type.
8554 Inst[2] = gBit(Data, 24, 32);
8555 Inst[3] = gBit(Data, 15, 23);
8556 elseif (Type == 'ABx') then
8557 Inst[2] = gBit(Data, 15, 32);
8558 elseif (Type == 'AsBx') then
8559 Inst[2] = gBit(Data, 15, 32) - 131071;
8560 end;
8561
8562 -- Precompute data for some instructions
8563 do
8564 -- TEST and TESTSET
8565 if Opco == 26 or Opco == 27 then
8566 Inst[3] = Inst[3] == 0;
8567 end
8568
8569 -- EQ, LT, LE
8570 if Opco >= 23 and Opco <= 25 then
8571 Inst[1] = Inst[1] ~= 0;
8572 end
8573
8574 -- Anything that looks at a constant using B
8575 if Mode.b == 'OpArgK' then
8576 Inst[3] = Inst[3] or false; -- Simply to guarantee that Inst[4] is inserted in the array part
8577 if Inst[2] >= 256 then
8578 local Cons = Inst[2] - 256;
8579 Inst[4] = Cons;
8580
8581 local ReferenceData = ConstantReferences[Cons];
8582 if not ReferenceData then
8583 ReferenceData = {};
8584 ConstantReferences[Cons] = ReferenceData;
8585 end
8586
8587 ReferenceData[#ReferenceData + 1] = {Inst = Inst, Register = 4}
8588 end
8589 end
8590
8591 -- Anything that looks at a constant using C
8592 if Mode.c == 'OpArgK' then
8593 Inst[4] = Inst[4] or false -- Simply to guarantee that Inst[5] is inserted in the array part
8594 if Inst[3] >= 256 then
8595 local Cons = Inst[3] - 256;
8596 Inst[5] = Cons;
8597
8598 local ReferenceData = ConstantReferences[Cons];
8599 if not ReferenceData then
8600 ReferenceData = {};
8601 ConstantReferences[Cons] = ReferenceData;
8602 end
8603
8604 ReferenceData[#ReferenceData + 1] = {Inst = Inst, Register = 5}
8605 end
8606 end
8607 end
8608
8609 Instr[Idx] = Inst;
8610 end;
8611
8612 for Idx = 1, gInt() do -- Load constants.
8613 local Type = gBits8();
8614 local Cons;
8615
8616 if (Type == 1) then -- Boolean
8617 Cons = (gBits8() ~= 0);
8618 elseif (Type == 3) then -- Float/Double
8619 Cons = gFloat();
8620 elseif (Type == 4) then
8621 Cons = Sub(gString(), 1, -2);
8622 end;
8623
8624 -- Finish precomputing constants
8625 local Refs = ConstantReferences[Idx - 1];
8626 if Refs then
8627 for i = 1, #Refs do
8628 Refs[i].Inst[Refs[i].Register] = Cons
8629 end
8630 end
8631
8632 -- Write Constant to pool
8633 Const[Idx - 1] = Cons;
8634 end;
8635
8636 for Idx = 1, gInt() do -- Nested function prototypes.
8637 Proto[Idx - 1] = ChunkDecode();
8638 end;
8639
8640 do -- Debugging
8641 local Lines = Chunk.Lines;
8642
8643 for Idx = 1, gInt() do
8644 Lines[Idx] = gBits32();
8645 end;
8646
8647 for _ = 1, gInt() do -- Locals in stack.
8648 gString(); -- Name of local.
8649 gBits32(); -- Starting point.
8650 gBits32(); -- End point.
8651 end;
8652
8653 for _ = 1, gInt() do -- Upvalues.
8654 gString(); -- Name of upvalue.
8655 end;
8656 end;
8657
8658 return Chunk; -- Finished chunk.
8659 end;
8660
8661 do -- Most of this chunk I was too lazy to reformat or change
8662 assert(gString(4) == "\27Lua", "Lua bytecode expected.");
8663 assert(gBits8() == 0x51, "Only Lua 5.1 is supported.");
8664
8665 gBits8(); -- Probably version control.
8666 gBits8(); -- Is small endians.
8667
8668 local IntSize = gBits8(); -- Int size
8669 local Sizet = gBits8(); -- size_t
8670
8671 if (IntSize == 4) then
8672 gInt = gBits32;
8673 elseif (IntSize == 8) then
8674 gInt = gBits64;
8675 else
8676 error('Integer size not supported', 2);
8677 end;
8678
8679 if (Sizet == 4) then
8680 gSizet = gBits32;
8681 elseif (Sizet == 8) then
8682 gSizet = gBits64;
8683 else
8684 error('Sizet size not supported', 2);
8685 end;
8686
8687 assert(gString(3) == "\4\8\0", "Unsupported bytecode target platform");
8688 end;
8689
8690 return ChunkDecode();
8691end;
8692
8693local function _Returns(...)
8694 return Select('#', ...), {...};
8695end;
8696
8697local function Wrap(Chunk, Env, Upvalues)
8698 local Instr = Chunk.Instr;
8699 local Const = Chunk.Const;
8700 local Proto = Chunk.Proto;
8701
8702 local function OnError(Err, Position) -- Handle your errors in whatever way.
8703 local Name = Chunk.Name or 'Code';
8704 local Line = Chunk.Lines[Position] or '?';
8705
8706 error(string.format('%s:%s: %s', Name, Line, tostring(Err)), 0);
8707 end;
8708
8709 return function(...)
8710 -- Returned function to run bytecode chunk (Don't be stupid, you can't setfenv this to work your way).
8711 local InstrPoint, Top = 1, -1;
8712 local Vararg, Varargsz = {}, Select('#', ...) - 1;
8713
8714 local GStack = {};
8715 local Lupvals = {};
8716 local Stack = setmetatable({}, {
8717 __index = GStack;
8718 __newindex = function(_, Key, Value)
8719 if (Key > Top) then
8720 Top = Key;
8721 end;
8722
8723 GStack[Key] = Value;
8724 end;
8725 });
8726
8727 local function Loop()
8728 local Inst, Enum;
8729
8730 while true do
8731 Inst = Instr[InstrPoint];
8732 Enum = Inst.Enum;
8733 InstrPoint = InstrPoint + 1;
8734
8735 if (Enum == 0) then -- MOVE
8736 Stack[Inst[1]] = Stack[Inst[2]];
8737 elseif (Enum == 1) then -- LOADK
8738 Stack[Inst[1]] = Const[Inst[2]];
8739 elseif (Enum == 2) then -- LOADBOOL
8740 Stack[Inst[1]] = (Inst[2] ~= 0);
8741
8742 if (Inst[3] ~= 0) then
8743 InstrPoint = InstrPoint + 1;
8744 end;
8745 elseif (Enum == 3) then -- LOADNIL
8746 local Stk = Stack;
8747
8748 for Idx = Inst[1], Inst[2] do
8749 Stk[Idx] = nil;
8750 end;
8751 elseif (Enum == 4) then -- GETUPVAL
8752 Stack[Inst[1]] = Upvalues[Inst[2]];
8753 elseif (Enum == 5) then -- GETGLOBAL
8754 Stack[Inst[1]] = Env[Const[Inst[2]]];
8755 elseif (Enum == 6) then -- GETTABLE
8756 local Stk = Stack;
8757 Stk[Inst[1]] = Stk[Inst[2]][Inst[5] or Stk[Inst[3]]];
8758 elseif (Enum == 7) then -- SETGLOBAL
8759 Env[Const[Inst[2]]] = Stack[Inst[1]];
8760 elseif (Enum == 8) then -- SETUPVAL
8761 Upvalues[Inst[2]] = Stack[Inst[1]];
8762 elseif (Enum == 9) then -- SETTABLE
8763 local Stk = Stack
8764 Stk[Inst[1]][Inst[4] or Stk[Inst[2]]] = Inst[5] or Stk[Inst[3]]
8765 elseif (Enum == 10) then -- NEWTABLE
8766 Stack[Inst[1]] = {};
8767 elseif (Enum == 11) then -- SELF
8768 local Stk = Stack;
8769 local A = Inst[1];
8770 local B = Stk[Inst[2]];
8771 local C = Inst[5] or Stk[Inst[3]];
8772 Stk[A + 1] = B;
8773 Stk[A] = B[C];
8774 elseif (Enum == 12) then -- ADD
8775 local Stk = Stack;
8776 Stk[Inst[1]] = (Inst[4] or Stk[Inst[2]]) + (Inst[5] or Stk[Inst[3]]);
8777 elseif (Enum == 13) then -- SUB
8778 local Stk = Stack;
8779 Stk[Inst[1]] = (Inst[4] or Stk[Inst[2]]) - (Inst[5] or Stk[Inst[3]]);
8780 elseif (Enum == 14) then -- MUL
8781 local Stk = Stack;
8782 Stk[Inst[1]] = (Inst[4] or Stk[Inst[2]]) * (Inst[5] or Stk[Inst[3]]);
8783 elseif (Enum == 15) then -- DIV
8784 local Stk = Stack;
8785 Stk[Inst[1]] = (Inst[4] or Stk[Inst[2]]) / (Inst[5] or Stk[Inst[3]]);
8786 elseif (Enum == 16) then -- MOD
8787 local Stk = Stack;
8788 Stk[Inst[1]] = (Inst[4] or Stk[Inst[2]]) % (Inst[5] or Stk[Inst[3]]);
8789 elseif (Enum == 17) then -- POW
8790 local Stk = Stack;
8791 Stk[Inst[1]] = (Inst[4] or Stk[Inst[2]]) ^ (Inst[5] or Stk[Inst[3]]);
8792 elseif (Enum == 18) then -- UNM
8793 Stack[Inst[1]] = -Stack[Inst[2]];
8794 elseif (Enum == 19) then -- NOT
8795 Stack[Inst[1]] = (not Stack[Inst[2]]);
8796 elseif (Enum == 20) then -- LEN
8797 Stack[Inst[1]] = #Stack[Inst[2]];
8798 elseif (Enum == 21) then -- CONCAT
8799 local Stk = Stack;
8800 local B = Inst[2];
8801 local K = Stk[B];
8802
8803 for Idx = B + 1, Inst[3] do
8804 K = K .. Stk[Idx];
8805 end;
8806
8807 Stack[Inst[1]] = K;
8808 elseif (Enum == 22) then -- JMP
8809 InstrPoint = InstrPoint + Inst[2];
8810 elseif (Enum == 23) then -- EQ
8811 local Stk = Stack;
8812 local B = Inst[4] or Stk[Inst[2]];
8813 local C = Inst[5] or Stk[Inst[3]];
8814
8815 if (B == C) ~= Inst[1] then
8816 InstrPoint = InstrPoint + 1;
8817 end;
8818 elseif (Enum == 24) then -- LT
8819 local Stk = Stack;
8820 local B = Inst[4] or Stk[Inst[2]];
8821 local C = Inst[5] or Stk[Inst[3]];
8822
8823 if (B < C) ~= Inst[1] then
8824 InstrPoint = InstrPoint + 1;
8825 end;
8826 elseif (Enum == 25) then -- LE
8827 local Stk = Stack;
8828 local B = Inst[4] or Stk[Inst[2]];
8829 local C = Inst[5] or Stk[Inst[3]];
8830
8831 if (B <= C) ~= Inst[1] then
8832 InstrPoint = InstrPoint + 1;
8833 end;
8834 elseif (Enum == 26) then -- TEST
8835 if Inst[3] then
8836 if Stack[Inst[1]] then
8837 InstrPoint = InstrPoint + 1;
8838 end
8839 elseif Stack[Inst[1]] then
8840 else
8841 InstrPoint = InstrPoint + 1;
8842 end
8843 elseif (Enum == 27) then -- TESTSET
8844 local B = Stack[Inst[2]];
8845
8846 if Inst[3] then
8847 if B then
8848 InstrPoint = InstrPoint + 1;
8849 else
8850 Stack[Inst[1]] = B
8851 end
8852 elseif B then
8853 Stack[Inst[1]] = B
8854 else
8855 InstrPoint = InstrPoint + 1;
8856 end
8857 elseif (Enum == 28) then -- CALL
8858 local A = Inst[1];
8859 local B = Inst[2];
8860 local C = Inst[3];
8861 local Stk = Stack;
8862 local Args, Results;
8863 local Limit, Edx;
8864
8865 Args = {};
8866
8867 if (B ~= 1) then
8868 if (B ~= 0) then
8869 Limit = A + B - 1;
8870 else
8871 Limit = Top;
8872 end;
8873
8874 Edx = 0;
8875
8876 for Idx = A + 1, Limit do
8877 Edx = Edx + 1;
8878
8879 Args[Edx] = Stk[Idx];
8880 end;
8881
8882 Limit, Results = _Returns(Stk[A](unpack(Args, 1, Limit - A)));
8883 else
8884 Limit, Results = _Returns(Stk[A]());
8885 end;
8886
8887 Top = A - 1;
8888
8889 if (C ~= 1) then
8890 if (C ~= 0) then
8891 Limit = A + C - 2;
8892 else
8893 Limit = Limit + A - 1;
8894 end;
8895
8896 Edx = 0;
8897
8898 for Idx = A, Limit do
8899 Edx = Edx + 1;
8900
8901 Stk[Idx] = Results[Edx];
8902 end;
8903 end;
8904 elseif (Enum == 29) then -- TAILCALL
8905 local A = Inst[1];
8906 local B = Inst[2];
8907 local Stk = Stack;
8908 local Args, Results;
8909 local Limit;
8910 local Rets = 0;
8911
8912 Args = {};
8913
8914 if (B ~= 1) then
8915 if (B ~= 0) then
8916 Limit = A + B - 1;
8917 else
8918 Limit = Top;
8919 end
8920
8921 for Idx = A + 1, Limit do
8922 Args[#Args + 1] = Stk[Idx];
8923 end
8924
8925 Results = {Stk[A](unpack(Args, 1, Limit - A))};
8926 else
8927 Results = {Stk[A]()};
8928 end;
8929
8930 for Index in pairs(Results) do -- get return count
8931 if (Index > Rets) then
8932 Rets = Index;
8933 end;
8934 end;
8935
8936 return Results, Rets;
8937 elseif (Enum == 30) then -- RETURN
8938 local A = Inst[1];
8939 local B = Inst[2];
8940 local Stk = Stack;
8941 local Edx, Output;
8942 local Limit;
8943
8944 if (B == 1) then
8945 return;
8946 elseif (B == 0) then
8947 Limit = Top;
8948 else
8949 Limit = A + B - 2;
8950 end;
8951
8952 Output = {};
8953 Edx = 0;
8954
8955 for Idx = A, Limit do
8956 Edx = Edx + 1;
8957
8958 Output[Edx] = Stk[Idx];
8959 end;
8960
8961 return Output, Edx;
8962 elseif (Enum == 31) then -- FORLOOP
8963 local A = Inst[1];
8964 local Stk = Stack;
8965
8966 local Step = Stk[A + 2];
8967 local Index = Stk[A] + Step;
8968
8969 Stk[A] = Index;
8970
8971 if (Step > 0) then
8972 if Index <= Stk[A + 1] then
8973 InstrPoint = InstrPoint + Inst[2];
8974
8975 Stk[A + 3] = Index;
8976 end;
8977 else
8978 if Index >= Stk[A + 1] then
8979 InstrPoint = InstrPoint + Inst[2];
8980
8981 Stk[A + 3] = Index;
8982 end
8983 end
8984 elseif (Enum == 32) then -- FORPREP
8985 local A = Inst[1];
8986 local Stk = Stack;
8987
8988 -- As per mirroring the real vm
8989 Stk[A] = assert(tonumber(Stk[A]), '`for` initial value must be a number');
8990 Stk[A + 1] = assert(tonumber(Stk[A + 1]), '`for` limit must be a number');
8991 Stk[A + 2] = assert(tonumber(Stk[A + 2]), '`for` step must be a number');
8992
8993 Stk[A] = Stk[A] - Stk[A + 2];
8994
8995 InstrPoint = InstrPoint + Inst[2];
8996 elseif (Enum == 33) then -- TFORLOOP
8997 local A = Inst[1];
8998 local C = Inst[3];
8999 local Stk = Stack;
9000
9001 local Offset = A + 2;
9002 local Result = {Stk[A](Stk[A + 1], Stk[A + 2])};
9003
9004 for Idx = 1, C do
9005 Stack[Offset + Idx] = Result[Idx];
9006 end;
9007
9008 if (Stk[A + 3] ~= nil) then
9009 Stk[A + 2] = Stk[A + 3];
9010 else
9011 InstrPoint = InstrPoint + 1;
9012 end;
9013 elseif (Enum == 34) then -- SETLIST
9014 local A = Inst[1];
9015 local B = Inst[2];
9016 local C = Inst[3];
9017 local Stk = Stack;
9018
9019 if (C == 0) then
9020 InstrPoint = InstrPoint + 1;
9021 C = Instr[InstrPoint].Value;
9022 end;
9023
9024 local Offset = (C - 1) * 50;
9025 local T = Stk[A]; -- Assuming T is the newly created table.
9026
9027 if (B == 0) then
9028 B = Top - A;
9029 end;
9030
9031 for Idx = 1, B do
9032 T[Offset + Idx] = Stk[A + Idx];
9033 end;
9034 elseif (Enum == 35) then -- CLOSE
9035 local A = Inst[1];
9036 local Cls = {}; -- Slight doubts on any issues this may cause
9037
9038 for Idx = 1, #Lupvals do
9039 local List = Lupvals[Idx];
9040
9041 for Idz = 0, #List do
9042 local Upv = List[Idz];
9043 local Stk = Upv[1];
9044 local Pos = Upv[2];
9045
9046 if (Stk == Stack) and (Pos >= A) then
9047 Cls[Pos] = Stk[Pos];
9048 Upv[1] = Cls; -- @memcorrupt credit me for the spoonfeed
9049 end;
9050 end;
9051 end;
9052 elseif (Enum == 36) then -- CLOSURE
9053 local NewProto = Proto[Inst[2]];
9054 local Stk = Stack;
9055
9056 local Indexes;
9057 local NewUvals;
9058
9059 if (NewProto.Upvals ~= 0) then
9060 Indexes = {};
9061 NewUvals = setmetatable({}, {
9062 __index = function(_, Key)
9063 local Val = Indexes[Key];
9064
9065 return Val[1][Val[2]];
9066 end,
9067 __newindex = function(_, Key, Value)
9068 local Val = Indexes[Key];
9069
9070 Val[1][Val[2]] = Value;
9071 end;
9072 }
9073 );
9074
9075 for Idx = 1, NewProto.Upvals do
9076 local Mvm = Instr[InstrPoint];
9077
9078 if (Mvm.Enum == 0) then -- MOVE
9079 Indexes[Idx - 1] = {Stk, Mvm[2]};
9080 elseif (Mvm.Enum == 4) then -- GETUPVAL
9081 Indexes[Idx - 1] = {Upvalues, Mvm[2]};
9082 end;
9083
9084 InstrPoint = InstrPoint + 1;
9085 end;
9086
9087 Lupvals[#Lupvals + 1] = Indexes;
9088 end;
9089
9090 Stk[Inst[1]] = Wrap(NewProto, Env, NewUvals);
9091 elseif (Enum == 37) then -- VARARG
9092 local A = Inst[1];
9093 local B = Inst[2];
9094 local Stk, Vars = Stack, Vararg;
9095
9096 Top = A - 1;
9097
9098 for Idx = A, A + (B > 0 and B - 1 or Varargsz) do
9099 Stk[Idx] = Vars[Idx - A];
9100 end;
9101 end;
9102 end;
9103 end;
9104
9105 local Args = {...};
9106
9107 for Idx = 0, Varargsz do
9108 if (Idx >= Chunk.Args) then
9109 Vararg[Idx - Chunk.Args] = Args[Idx + 1];
9110 else
9111 Stack[Idx] = Args[Idx + 1];
9112 end;
9113 end;
9114
9115 local A, B, C = pcall(Loop); -- Pcalling to allow yielding
9116
9117 if A then -- We're always expecting this to come out true (because errorless code)
9118 if B and (C > 0) then -- So I flipped the conditions.
9119 return unpack(B, 1, C);
9120 end;
9121
9122 return;
9123 else
9124 OnError(B, InstrPoint - 1); -- Didn't get time to test the `-1` honestly, but I assume it works properly
9125 end;
9126 end;
9127end;
9128
9129return function(BCode, Env) -- lua_function LoadBytecode (string BCode, table Env)
9130 local Buffer = GetMeaning(BCode);
9131
9132 return Wrap(Buffer, Env or getfenv(0)), Buffer;
9133end;
9134]]></ProtectedString>
9135 <BinaryString name="Tags"></BinaryString>
9136 </Properties>
9137 </Item>
9138 <Item class="Script" referent="RBX9BAAC995A4854EFCBB2D1DFE3A152236">
9139 <Properties>
9140 <bool name="Disabled">true</bool>
9141 <Content name="LinkedSource"><null></null></Content>
9142 <string name="Name">Credits</string>
9143 <string name="ScriptGuid">{4B480C1C-A37C-41F3-BE92-D3386E3ACBD8}</string>
9144 <ProtectedString name="Source"><![CDATA[--[[
9145LuLu 5.1 VM (original implementation) - http://lulu.luaforge.net/
9146* Rerubu (rewrite based off LuLu) - https://github.com/Rerumu/Rerubi
9147Yueliang 5 (Lua compiler in Lua) - http://yueliang.luaforge.net/
9148* einsteinK: various bug fixes
9149]]]]></ProtectedString>
9150 <BinaryString name="Tags"></BinaryString>
9151 </Properties>
9152 </Item>
9153 </Item>
9154 </Item>
9155</roblox>