OK I have done enough chores for today and the rest can wait until tomorrow, so I have had a quick look and done minimal tweaks to this to allow for specifying an exact number of repeats as well as a range. Basically when an exact number is specified the min and max number of repeats are set to the same.
To use this with match() or strpos() commands (or st.FindMatch()) simply wrap the regex in this function.
so if you have
match(myString, myRegex, Match:Regular)
where myRegex is say ‘^[0-9]{8}$’ or ‘^{A{3}|B{2}}$’ (which were Mark’s two first examples) or something with a range of repetitions like ‘[0-9]{1,3}’ then instead say:
match(myString, ExpandRegex(myRegex), Match:Regular)
so without further ado:
prototype is:
ExpandRegEx PROCEDURE(string pRegex),string
and the code is:
ExpandRegEx PROCEDURE (string pRegex)
!-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
!
! ExpandRegEx - expand repetitions
! eg. [0-9]{1,3} expands to [0-9][0-9]?[0-9]?
! [0-9]{0,4} expands to [0-9]?[0-9]?[0-9]?[0-9]?
! [0-9]{5} expands to [0-9][0-9][0-9][0-9][0-9]
!
! Version 1.0 written on 15th March 2023 by Geoff Robinson vitesse AT gmail DOT com
! Version 2.0 written on 23rd December 2024 again by Geoff Robinson
! - minimal changes to now allow an exact number of repeats
!
! MIT License
!
! Copyright (c) 2023 Geoffrey C. Robinson
!
! Permission is hereby granted, free of charge, to any person obtaining a copy
! of this software and associated documentation files (the "Software"), to deal
! in the Software without restriction, including without limitation the rights
! to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
! copies of the Software, and to permit persons to whom the Software is
! furnished to do so, subject to the following conditions:
!
! The above copyright notice and this permission notice shall be included in all
! copies or substantial portions of the Software.
!
! THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
! IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
! FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
! AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
! LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
! OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
! SOFTWARE.
!
!-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
ExpandRegEx PROCEDURE (string pRegex) ! Declare Procedure
st StringTheory
ln StringTheory ! line
stTmp StringTheory ! temp worker
c String(1),auto ! char
b byte, over(c)
x long,auto
y long,auto
prevLen long
prevLen2 long
minRepeat long,auto
maxRepeat long,auto
code
st.setValue(pRegex,st:clip)
loop x = 1 to st._dataEnd
c = st.valuePtr[x]
case b
of 91 ! val('[')
y = st.matchBrackets('[',']',x)
if y = x + 1 ! allow for where ] is included char
y = st.findByte(93, y+1) ! find next ']'
end
of 123 ! val('{{')
y = st.matchBrackets('{{','}',x)
else
y = 0
end
if y
st.addLine(st:end,st.valuePtr[x : y])
x = y
else
st.addLine(st:end,c)
end
end
st.free()
loop x = 1 to st.records()
prevLen2 = prevLen
prevLen = st._dataEnd
ln.setValue(st.getLine(x))
st.append(ln)
? assert(ln._dataEnd > 0)
if ln._dataEnd = 1 or |
ln.valuePtr[1] <> '{{' or |
ln.valuePtr[ln._dataEnd] <> '}'
cycle
end
stTmp.setValue(ln)
stTmp.crop(2, stTmp._dataEnd - 1) ! strip { and }
stTmp.split(',')
stTmp.setValue(stTmp.getLine(1))
stTmp.trim()
if not stTmp.isAllDigits() then cycle.
minRepeat = stTmp.getValue()
case stTmp.records()
of 1
maxRepeat = minRepeat ! exact number of repeats eg. {5}
of 2
stTmp.setValue(stTmp.getLine(2))
stTmp.trim()
if not stTmp.isAllDigits() then cycle.
maxRepeat = stTmp.getValue()
if minRepeat > maxRepeat then cycle.
else
cycle
end
st.setLength(prevLen) ! remove repeat group
stTmp.setValue(st.slice(prevLen2+1)) ! tok to be repeated
st.setLength(prevLen2) ! remove tok to be repeated
loop y = 1 to maxRepeat
st.append(stTmp)
if y > minRepeat then st.append('?'). ! optional?
end
end
if st._dataEnd < 1
return ''
else
return st.valuePtr[1 : st._dataEnd]
end