PDA

View Full Version : LUA "Regex"



celestian
January 18th, 2019, 01:05
So I know LUA doesn't use a "complete" regular expression feature but I am wondering how people handle something like this.



local sLevel, sSpells = sText:match("(first|second|third|fourth|fifth|sixth|seventh) (['’,%w%d%s%-%(%)]+)");


In regex that would match the string first or second or third/etc... and capture both into sLevel and sSpells.

Unfortunately that doesn't work in LUA.



local sDice,_, sSign,sMod = sDiceText:match((%d+[Dd]%d+)(([%-+])(%d+))?)


This would match "1d6+2" and if no modifier just "1d6". This "(something)?" doesn't seem to be possible in LUA either. You can use single character tests with ? like "[%.$]?" and it'll work but trying to do it for a captured "(string)?" doesn't.

How do you guys work around these limitations?

For the last case I think this might be a good way.



local sDice = sDiceText:match(%d+[Dd]%d+[%-+]+%d+) or sDice:match(%d+[Dd]%d+)


And then I'd further split it up to get the number, size, sign and modifiers.

Am I just missing some obvious way to do this sorta thing or is it really just adding a lot more checks to do what I'm trying to get the solution? The (check|check2|check3) really bites me at times :(

gamerhawaii
January 18th, 2019, 02:43
I might not be interpreting your question correctly but:
local sDice,sDie,sSign,sMod = string.match(sDiceText, "(%d+)%s*[Dd]+%s*(%d+)%s*([-%+]*)%s*(%d*)")
(For example, " 13 D 100 - 17 " returns 13, 100, -, 17. "1d6+2" returns 1, 6, +, 2. "1d6" returns 1, 6, empty, empty)

celestian
January 18th, 2019, 04:23
I might not be interpreting your question correctly but:
local sDice,sDie,sSign,sMod = string.match(sDiceText, "(%d+)%s*[Dd]+%s*(%d+)%s*([-%+]*)%s*(%d*)")
(For example, " 13 D 100 - 17 " returns 13, 100, -, 17. "1d6+2" returns 1, 6, +, 2. "1d6" returns 1, 6, empty, empty)

I suppose I picked a bad example... although your conclusion still stands. I can't think of an example where this wouldn't work. I can add more checks of the vars and figure out what is actually there. I'll need to test gmatch for this tho because I think I ran into problems with that one.

You don't happen to know of a way around the (check1|check2|...) do ya? ;)

gamerhawaii
January 18th, 2019, 05:04
Sorry, I do not know an easy way for something like (check1|check2|...) in Lua in FG.

But check out "manager_string.lua" in the CORE ruleset. It seems to have functions that you might be able to use to do what you need (or least use them as a starting point in creating your own utility function to do it.) It also has some functions for dice expressions.

However, in your first example, one guess is to just capture the first word as the spell level and then the rest as the spell text. (You did not show an exact example of what you are trying to parse, so I am just guessing for a solution for that particular case.)

celestian
January 18th, 2019, 05:05
So my testing has found that it doesn't work right.

This won't match "2 slams (1d10)".



for sCount,sDChar,sSize,sSign,sMod in string.gmatch(sDamageRaw,"(%d+)([dD%-])(%d+)([%+%-])(%d+)") do
...
end


If I change it to "2 slams (1d10+1)" it will. I understand why it will not match previous but if I could do something like "(%d+)([dD%-])(%d+)([%+%-])?(%d+)?" it would.

gamerhawaii
January 18th, 2019, 05:20
This won't match "2 slams (1d10)".

Ok, so what is that supposed to give and is that the whole text of the line? That is, is "2 slams (1d10)" in the middle of the line or the whole text. And what do you want back?

I say this because if I use the exact expression I wrote above it gives me back sDice=1 and sDie=10. That is what I assumed you wanted. In your example, you changed the expression from what I had. For example, you removed the "*" after the "[-%+]" which means that a + or - is going to be required, not optional. Also, you made the modifier not optional by changing the "*" to a "+". Also, gmatch and match do not work the same. I used match. What I wrote does not work if using gmatch.

Try this:
for sCount,sDChar,sSize,sSign,sMod in string.gmatch(sDamageRaw,"(%d+)([dD])(%d+)([%+-]*)(%d*)") do

sDamageRaw = "blah blah blah 2 slams (1d10) blah blah blah" gives 1, d, 10, empty, empty for the five variables

Moon Wizard
January 18th, 2019, 05:34
I've had to work around it by using word parsing; and then comparing the parsed words. That's what I did in a lot of the auto-parsing code in 3.5E, 4E and 5E. Look for StringManager.parseWords and StringManager.isWord as well as a few other functions used throughout those rulesets.

Regards,
JPG

gamerhawaii
January 18th, 2019, 05:38
Yes, StringManager is the "manager_string.lua" file. I should have mentioned that. Thanks for the clarification and the additional insight.

celestian
January 18th, 2019, 13:58
"(%d+)([dD])(%d+)([%+-]*)(%d*)"

You know I missed the bit where you used * where I used +. I wasn't aware it worked like that for [blah]* or %w*. I'll test that and see if I have better luck. Thanks for the tip!

celestian
January 18th, 2019, 15:37
This is what I ended up with that works. It flips through the string, finds a match then removes it from the raw string, adds it to the attacks table and then checks for another and runs through the process till all dice strings are found.



for sCount,sDChar,sSize,sSign,sMod in string.gmatch(sDamageRaw,"(%d+)([dD%-])(%d+)([%+%-]?)(%d*)") do
if (sCount ~= nil) and sSize ~= nil and sSign ~= nil and sMod ~= nil then
local sDice = sCount .. sDChar .. sSize .. sSign .. sMod;
sDamageRaw = string.gsub(sDamageRaw,sDice,"",1);
table.insert(aAttacks, sDice);
elseif (sCount ~= nil and sSize ~= nil) then
local sDice = sCount .. sDChar .. sSize;
sDamageRaw = string.gsub(sDamageRaw,sDice,"",1);
table.insert(aAttacks, sDice);
end
end


For my other "or" issue where I use (this match|or this match|or this match) I've worked out this. It will capture the 2 different styles of stat block entries for spells memorized (if they exist). It's a bit more work but does function.



local sSpellFilter = "['’,%w%d%s%-%(%)]+";
local aLevelsAsName = { "first","second","third","fourth","fifth","sixth","seventh","eighth","ninth"};

-- 'spells memorized: first level: command, cure light wounds (x2), remove fear, sanctuary second level: hold person (x2), resist fire, silence 15' radius, slow poison third level: dispel magic, prayer, bestow curse fourth level: cure serious wounds'
-- or
-- 'spells memorized: level 1: detect magic, magic missile (x2), unseen servant level 2: detect invisibility, invisibility, web level 3: dispel magic, haste, lightning bolt level 4: charm monster, polymorph self level 5: teleport '
for i=1,9,1 do
spells[i] = sTextLower:match(aLevelsAsName[i] .. " level: (".. sSpellFilter .. ") %w+ level:")
or sTextLower:match(aLevelsAsName[i] .. " level: (".. sSpellFilter .. ")")
or sTextLower:match("level " .. i .. ": (".. sSpellFilter .. ") level %d+:")
or sTextLower:match("level " .. i .. ": (".. sSpellFilter .. ")");
end