Manipulating strings

Hey everyone, thank you for reading.

Lets say I take this code and turn it into a string:

local function Yeah(arg)
	print(arg)
end

while true do
	print("a")
end

while true do
	if true then
		print("b")	
	end
end

The string would look like this:
local aString = 'local function Yeah(arg) print(arg) end while true do print("a") end while true do if true then print("b") end end'

Now I want to edit the string and add the text “task.wait()” in the right places, which would be before the end that encloses the while loop, like this:

-- As string
local aString = 'local function Yeah(arg) print(arg) end while true do print("a") task.wait() end while true do if true then print("b") end task.wait() end'
	
-- Example code
while true do
	print("a")
	task.wait() -- here
end

while true do
	if true then
		print("b")	
	end
	task.wait() -- here
end

I tried doing it in many possible ways, and I ended with a very ugly code that kinda does what I want, kinda hardcoded and not doing the check on the entire string…
I tried splitting words in a table, counting how many “if” are in between the while and end in order to skip the required ends until find the end that belongs to the while loop, then adding the “task.wait()” text
I tried using the string library functions but Im not getting good results.

Anyone could give me an example on how I could achieve this?

I would like to answer to this question but can you provide the reason behind doing all this? This question is very unrelated with game development purposes. What would you like to achieve for doing all this?

Its for an admin console that let admins to directly run code, like using the /console commandBar.
I just want to perform some checks in the code they are about to run before actually run it, if it contains stuff like a while loop without a wait that could crash the game, I add it into the string before running it

The best solution to running the code on the server would be to convert the code to an abstract syntax tree. Then adding a wait in the right place would be easy and you could even use the AST to run the code without needing to use loadstring. (so you can just add a wait after every so many “steps”)

However the overall/actual best solution would be to run the code on the client and have it communicate updates that it wants to make to the server. So that if the admin makes an infinite loop they only crash theirselves. Because people will always be able to get around any checks you have in place for checking for infinite loops.

1 Like

Honestly I dont have any idea on how to convert the code that they typed into an abstract syntax tree in order to edit it.

If running the code client sided, theres services and stuff that cant be performed on client side, even if I send replication to other clients, they cant run the code if uses serverSided functions/services, I think

Well these ppl is not like they will try to get around the checks, its more like a safe check before someone perform an epic error by mistake

i dont know what i did i forgot

local Pattern = "while %D+ do%D*end"
--[[ i guessed it randomly and it works so it works ]]

local function AddText(String, Pos, Add)
	return string.sub(String, 1, Pos) .. Add .. string.sub(String, Pos + 1, -1)
end

local function MalnipulateThingy(Source)
	local Find = {string.find(Source, Pattern)}

	if Find[1] and Find[2] then
		local Init = 0

		while true do
			Find = {string.find(Source, Pattern, Init)}

			local Start, End = table.unpack(Find)

			if Start and End then
				local CurrentLine = string.split(string.sub(Source, Start, -1), "\n")[1]
				local StringBefore = string.sub(CurrentLine, 1, Start - 1)
				local TabsCount = ({string.gsub(StringBefore, "		", "")})[2] + 1

				Source = AddText(Source, End - 4, "\n" .. string.rep("	", TabsCount) .. "task.wait()")
			else
				break
			end

			Init += 1
		end
	end

	return Source
end

print(MalnipulateThingy([[
while true do  
	print("a")
end
]]))
1 Like

To convert the code to an AST you basically just have to convert something like this
image
to an object tree describing the structure of the code like this (not all expanded because it’s pretty long)

You can look at the AST of different programming languages including lua on https://astexplorer.net/ for inspiration.


What if someone does

for i = 1, os.time() do end

or maybe

for i = 0, math.huge do end

or even harder to detect

local big = 1.8e308
local i = 0
while i < big do i += 1 end

or even worse

function doSomething() return doSomething() end

or nests a lot of shorter loops?

It’s impossible to detect every possible infinite loop. So your only option is to completely circumvent the entire issue and just run the code on the client or at least wait every so many statements with your own lua vm.

2 Likes
local aString = 'local function Yeah(arg) print(arg) end while true do print("a") end while true do if true then print("b") end end'

local function replaceFunc(str:string)
	local useSpace = false
	if str:sub(#str) == " " then
		str = str:sub(1,#str-1)
		useSpace = true
	end
	str = str:sub(1,#str-3) .. "task.wait() end" .. if useSpace then " " else ""
	return str
end

function readString(str:string)
	local output = str:gsub("while %w+ do .- end[%send]*",replaceFunc)
	print(output)
end

readString(aString)

This will output local function Yeah(arg) print(arg) end while true do print("a") task.wait() end while true do if true then print("b") end task.wait() end
which is what you want, I think. If I got that right, then this is solved pretty easily I by using the - string quantifier. This allows us to just use gsubs to replace existing while loops with loops that have waits in them, before the last end.
I didn’t want to try so hard on making it pretty so the spacing thing is just because it matched spaces at the end of the gsub capture sometimes, but the bulk of the work is just the expression being passed in readString

1 Like

Average light mode user…
Kind of scared me for a second tbh.

This works pretty well thank you, manipulating strings confuses me a lot… your code does it very well, and simplified.

Even when I will take a different approach, and probably turn this stuff client sided, idk, I will mark your answer as the solution cause it actually does exactly what I was looking for, thank you!

I understand what you mean, and actually you are right, would be better to run it in client…
I’ve been reading about the approach you suggest of creating a syntax tree and the AST. This is pretty useful https://astexplorer.net/ thank you so much. And honestly yup, sounds complicated, something new to learn about for me.

Its great to get in deep with this topic, seems helpful, but for now, just for a cheap admin console, probably the best idea is client sided as you suggested.

Thank you! Yup it works too, but works similar to mine, it fails if the string has more while loops, so it should be fired more than once to edit the entire string, the code is actually placing all the waits stacked at the end of the string the more loops it has the string

Sadly astexplorer doesn’t have a dark mode…

This topic was automatically closed 14 days after the last reply. New replies are no longer allowed.