tbRegExp Package
Author: GCuser99. Version: 1.7.0.0. On GitHub: GCuser99/tbRegExp, in the folder Package.
The tbRegExp contributed package provides regular expressions for twinBASIC projects, as a replacement for the RegExp object of VBScript. It follows the same object model. A RegExp is given a pattern and options, its Execute method returns a MatchCollection of Match objects, and each Match holds the text captured by the groups of the pattern as a SubMatches object. Positions are zero-based, as in VBScript.
Warning
A match can be at most about 3,300 characters long. A longer one is reported cut short, later in the string, or not at all, and no error is raised. The engine gives up a search from one starting position after 10,000 steps, where a step is one operation of the compiled pattern, and then starts again one character later. The limit is fixed inside the package, and a program cannot change it. A pattern that repeats one character, such as a*, .* or [^,]*, takes three steps for each character it matches, so its longest match is 3,331 characters, and a pattern with more to do for each character has a shorter longest match. On a string of 5,000 a, a* finds a match that starts at position 1669 and is 3,331 characters long, where VBScript’s RegExp finds the whole string from position 0, and ^.*$ finds no match at all, so Test returns False. Each starting position that fails costs up to 10,000 steps, so a search over a long run is slow as well: a* over a run of 9,500 characters takes several seconds, and returns a match of 3,331 characters from position 6169. This is how version 1.7.0.0 behaves.
Dim re As New RegExp
Dim m As Match
re.Pattern = "a*"
Set m = re.Execute(String(5000, "a"))(0)
Debug.Print m.FirstIndex & ", " & m.Length ' 1669, 3331
re.Pattern = "^.*$"
Debug.Print re.Test(String(5000, "a")) ' False
The matching is done by the vba-regex engine of sihlfall, which the package includes as a private module, StaticRegex.
Beyond VBScript’s object, RegExp has the DotAll and Flags options, the Split method, and IsValid, LastError and LastErrorNumber, which report an invalid pattern before it is used. Replace reads a $ in the replacement string as VBScript does, and adds $<name> for a named group.
Pattern syntax
A pattern is written as for VBScript’s RegExp. tbRegExp also accepts:
- named groups,
(?<name>...): SubMatches.Item returns one by its name, and Replace inserts one as$<name>. A name starts with a letter or an underscore and goes on with letters, digits and underscores; a pattern with any other name is not valid; - lookbehind,
(?<=...)and(?<!...), as well as lookahead; - the options
i,mandsinside the pattern:(?i)for the rest of the pattern,(?i:...)for one group.
Dim re As New RegExp
re.Pattern = "(?<year>\d{4})-(?<month>\d{2})"
Debug.Print re.Execute("2026-10")(0).SubMatches("month") ' 10
Debug.Print re.Replace("2026-10", "$<month>/$<year>") ' 10/2026
re.Pattern = "(?<=\$)\d+"
Debug.Print re.Execute("costs $123")(0).Value ' 123
re.Pattern = "(?i:abc)DEF"
Debug.Print re.Test("ABCDEF") ' True
Debug.Print re.Test("ABCdef") ' False
A pattern that holds an atomic group (?>...) or a conditional is not valid: IsValid returns False. A named backreference \k<name>, \A, \Z and \p{...} are not supported either, but they do not make the pattern invalid, because a backslash before a letter that names no escape stands for the letter itself, as in VBScript: \k<name> matches the text k<name>, and \A matches A. The pattern \p{L} is invalid for another reason: {L} does not start a quantifier (see below).
The escapes that name a control character or a character code are read as in VBScript:
\cfollowed by a letter, in either case, is a control character:\cJand\CJare the line feed. A\cfollowed by anything else makes the pattern invalid;\0is the character with code 0,\0followed by octal digits, such as\012, is that code, and\8and\9are the digits themselves;\1to\9and longer numbers are backreferences when the pattern has that many groups. Otherwise the digits are read as an octal character code, so\101isAand(a)\2isafollowed by the character with code 2; IsValid returns True for both. A backreference to a group that has not yet taken part in the match, such as\1(a), matches no characters; VBScript’s RegExp finds no match for such a pattern;\f,\n,\rand\t, inside a character class as well, are the form feed, line feed, carriage return and tab.
Dim re As New RegExp
re.Pattern = "\101"
Debug.Print re.Test("A") ' True
re.Pattern = "\CJ"
Debug.Print re.Test(vbLf) ' True
re.Pattern = "\k<n>"
Debug.Print re.IsValid ' True
Debug.Print re.Test("k<n>") ' True
re.Pattern = "(a)\2"
Debug.Print re.Test("a" & ChrW(2)) ' True
\w matches only the ASCII letters and digits and _, as in VBScript, so \w+ finds caf in café.
A { has to start a quantifier: {n}, {n,}, {n,m} or {,m}, which means {0,m}. A pattern in which a { starts none, such as a{ or a{x}, is not valid; write \{ for the character. VBScript reads a { that starts no quantifier, and {,m}, as ordinary text. A ] or } that closes nothing is an ordinary character, as in VBScript.
Dim re As New RegExp
re.Pattern = "(?<_part2>\d)"
Debug.Print re.IsValid ' True
re.Pattern = "(?<2part>\d)"
Debug.Print re.IsValid ' False
re.Pattern = "a{x}"
Debug.Print re.IsValid ' False
re.Pattern = "a\{x}"
Debug.Print re.Execute("a{x}")(0).Value ' a{x}
Classes
- Match – holds one match that a RegExp found in a string: the matched text, where it starts, how many characters it has, and the text captured by its groups
- FirstIndex – returns the zero-based position where the match starts
- Length – returns the number of characters in the match
- SubMatches – returns the text captured by the groups of the pattern
- Value – returns the matched text (default member)
- MatchCollection – holds the Match objects that a regular expression finds in a string
- RegExp – searches strings with a regular expression: finds the matches, tests for one, replaces them and splits a string at them
- DotAll – returns or sets whether
.matches a line feed - Execute – returns the matches in a string
- Flags – returns or sets the four options as a string of letters
- Global – returns or sets whether all matches are used
- IgnoreCase – returns or sets whether letter case is ignored
- IsValid – returns whether the pattern compiles
- LastError – returns the message of the compile error
- LastErrorNumber – returns the number of the compile error
- MultiLine – returns or sets whether
^and$match at line boundaries - Pattern – returns or sets the regular expression
- Replace – replaces the matches in a string
- Split – splits a string at the matches
- Test – returns whether a string contains a match
- DotAll – returns or sets whether
- SubMatches – holds the strings that the capture groups of a pattern captured in one Match