Help with strings :[

So I am currently working on a plugin that allows for custom commands, what I mean by that, is that any time you type something out in the script editor, then close the script editor it gets translated to code that lua can read.

My problem is that when you look at the script, turning it back to the simple command.
This is what the command looks like

game:GetCS('Name',true,true,30) -- Arguments are the name, if it should wait for the service to load, if it should require the service, and then the last one is wait time.

Then once you close the script, this is what you get:

require(game.ServerStorage:WaitForChild('CustomServices',30):WaitForChild('Name',30))

Now again, the problem is turning it back to that command we had to begin with, especially when we take into account that the command wont always start on the first couple of characters of that line of the code, for instance, what if we use it as a variable?

local CustomService = require(game.ServerStorage:WaitForChild('CustomServices',30):WaitForChild('Name',30))

Finding this becomes harder when we take into account the arguments, for instance if the first bool value is false, the WaitForChild’s are removed, or if I change the name, well no duh the names gonna change, same can be said for the rest of the values.

So how can I go about finding this? Converting it back to the GetCS is the easy part, the hard part, again, is just finding it.

Would something like this work? I’m assuming (maybe incorrectly) that the string you’re checking contains only the single line and not multiple lines of code.

--!strict
local function ConvertToCSFormat(Code: string): string
	local Name: string?, TimeToWait: string? = "None", "-1"
	
	local ShouldWait = Code:find(":WaitForChild%(") ~= nil
	local ShouldRequire = Code:find("require") ~= nil
	
	if ShouldWait then
		Name, TimeToWait = Code:match("WaitForChild%('(%w+)',%s*(%d+)%)")
	else
		Name = Code:match("game%.ServerStorage%.(%w+)")
		TimeToWait = "-1" -- You lose information here. You can't know what the original wait time was unless you post metadata as comments?
	end
	
	local Output = ("game:GetCS('%s', %*, %*, %s)"):format(Name :: string, ShouldWait, ShouldRequire, TimeToWait :: string)
	return Output
end

local Examples = {
	"require(game.ServerStorage:WaitForChild('CustomServices', 30):WaitForChild('Name', 30))",
	"local CustomService = require(game.ServerStorage:WaitForChild('CustomServices', 30):WaitForChild('Name', 30))",
	"game.ServerStorage:WaitForChild('CustomServices', 30):WaitForChild('Name', 30)",
	"local CustomService = game.ServerStorage:WaitForChild('CustomServices', 30):WaitForChild('Name', 30)",
	"require(game.ServerStorage.CustomServices.Name)",
	"local CustomService = require(game.ServerStorage.CustomServices.Name)",
	"game.ServerStorage.CustomServices.Name",
	"local CustomService = game.ServerStorage.CustomServices.Name"
}

for _, Example in Examples do
	print(ConvertToCSFormat(Example))
end 

My main concern is that for cases where you choose not to wait for the service to load, you’re removing information about the time to wait. That means you can never recover that information when converting back, unless you stored metadata as comments or something

This sorta could work, at least for finding the name, require, and wait for child, I think the problem here is telling if the line of code contains the command, which was the main part I struggled on figuring out.

As with endless variations of what the command could be, it becomes quite hard to determine if it contains said command

By this, do you mean being able to tell if the line was generated using game:GetCS?

no, what I mean is that we dont know if the user is going to type local CustomService before the command, they could type local ice, heck they might not even define it as a variable.

Not to mention the name of the Custom Service could be anything, its not always just Name, as there are unlimited possibilities of what it could be.

Idk if that made any sense, but I sure hope it does

By any chance do you have any other ideas?

Sorry, I still might not be understanding it completely. Is the main problem you’re describing that we don’t know whether the user manually wrote the code or if they used the command to generate the code? For example

local CustomService = require(game.ServerStorage:WaitForChild('CustomServices',30):WaitForChild('Name',30))

Just by looking at this, there’s no real way to know if the user wrote this out manually or used game:GetCS('Name', true, true, 30).

The code I posted above should handle cases where users put local varName before the command or use a different service name.

1 Like

Ill try it out, cause from reading it, it may work,

Thats true, but personally I’m not manually writing that out, so if people didnt like it they can just disable it. :]

you need to build a lexer, parser and interpreter for this stuff since you are basically reading a custom scripting language

pretty tough stuff unless u did it before in other languages

id heard this is good but cant say https://craftinginterpreters.com/

2 Likes

May I ask what this does?

Also the function only errors, I get this error:

user_Frodevs Hub.rbxmx.Frodevs Hub.Modules.HubControl.BaseCommands.Studio.Script Editor:72: invalid argument #2 to 'format' (string expected, got nil)

I’ve even right before :format is used set the second argument (aka ShouldWait to ‘true’) so idk whats up with it

That tells the typechecker to use strict type checking. It’s just a way to enforce types and make sure variable/function types are what they should be. It doesn’t (or shouldn’t) affect the runtime behavior at all

The second argument is actually referring to Name (the first argument being the format string itself). I casted it to string with the incorrect assumption that Code:match will always return a value even though it’s possible that it can be nil. In fact, the typechecker specifically tells you that Name can be nil if you remove the :: string so that’s my bad.

What did you pass as Code when you ran into the errors?

Edit: It’s possible you run into the same error with TimeToWait since I explicitly casted that to string as well when it could be nil

1 Like