Question about a particular string pattern

I think I have some brushing up to do with string patterns.

Given an expression like "5+2", I want to find a string pattern which will match that. The answer is "%d+[%+%-%*%^/]%d+", (ignoring the multiple operators supported you can shorten this to "%d+[%+%-%*]%d+" just so it’s more readable and you can understand the problem). My problem is, if I had an expression with multiple operators and operands, like "2+67*36", the pattern "%d+[%+%-%*]%d+" wouldn’t work, because it’s supposed to detect a single operand operator operand expression.

string.match("2+67*36", "%d+[%+%-%*]%d+") --2+67, and not 2+26*36

My question is how would I make a pattern to detect an expression with multiple operations like in the example above. "[%d%+%-%*%d]+" is a solution, but that won’t take count of something like "2++8*36", this is invalid, but the pattern above matches is it.

2 Likes

I don’t believe Lua’s string matching capabilities can solve this alone. Initially, my thought was to do something like %d+[%+%-%*]%d+([%+%-%*]%d+)*. In other words, match number, operator, number, and then optionally, more operator, number patterns (using parentheses notation, but that’s not a thing, sadly). Unfortunately, Lua’s patterns don’t let you repeat patterns/strings (only characters), which renders the matching additional operator, number patterns part impossible. The alternative I found was to manually get more operator, number patterns like so:

local function Match(String)
	local Substring = ""
	local Start, End = string.find(String, "%d+[%+%-%*]%d+")	--> find the initial number, operator, number pattern
	if Start then
		Substring = String:sub(Start, End)
		while true do
			Start, End = string.find(String, "^[%+%-%*]%d+", End + 1)	--> find additional operator, number patterns, ensuring that the patterns come one after the other
			if Start then
				Substring = Substring .. String:sub(Start, End)
			else
				break
			end
		end
		return Substring
	else
		return nil
	end
end

print(Match("2+67*36-45")) 	--> 2+67*36-45
print(Match("2++67*45++5")) --> 67*45
print(Match("2+67**45")) 	--> 2+67
print(Match("2"))			--> nil
print(Match("5+5+"))		--> 5+5

I’m not sure how you would like to handle weird string expressions (see the second and third examples in my code). The way I have it is that it’ll find any number, operator, number pattern and start looking for additional operator, number patterns from there (strictly making sure that these patterns come one after another, and stopping when this behavior is interrupted). This behavior can be modified to check that the initial number, operator, number must come first (and with additional end checks, you can make it so that only correct expressions are accepted). You can also modify the string patterns to accept negative numbers (%-? before the %d+).

Here’s an example that will only accept valid input (returns nil for invalid expressions):

local function StrictMatch(String)
	local Substring = ""
	local Start, End = string.find(String, "^%d+[%+%-%*]%d+")	--> find the initial number, operator, number pattern
	if Start then
		Substring = String:sub(Start, End)
		while true do
			if End == #String then
				return Substring
			end
			Start, End = string.find(String, "^[%+%-%*]%d+", End + 1)	--> find additional operator, number patterns, ensuring that the patterns exist at the beginning
			if Start then
				Substring = Substring .. String:sub(Start, End)
			else
				return nil
			end
		end
		return Substring
	else
		return nil
	end
end

print(StrictMatch("2+67*36-45")) 	--> 2+67*36-45
print(StrictMatch("4+555"))			--> 4+555
print(StrictMatch("2++67*45++5")) 	--> nil
print(StrictMatch("2+67**45")) 		--> nil
print(StrictMatch("2"))				--> nil
print(StrictMatch("5+5+"))			--> nil

I’m curious to see if there’s a more elegant solution though! Hopefully I didn’t overlook anything.

3 Likes

Very sorry for the late response

Thanks a lot for taking your time to write this! Unfortunately you are correct. There is no way you can check for repeating patterns. Only way to do so is by doing what you did, or using string.gmatch. I think what I’m gonna end up doing is use the pattern [%d%+%-%*%d]+ to check if a string has characters that form an expression, and then introduce a function to check if that expression is valid or not. Also, I’m actually doing this for a parser which is intended to be a challenge by someone. Which means I don’t really need to care about sanitizing input, I can just expect that the user would input valid code, and if not the code I wrote can error naturally by itself. Thanks!

1 Like