scScript Pattern Errors
Pattern matching, as you have witnessed, is inherently complex, so it should be no surprise
that there are a few common errors:
- Using RFunc without adding #Res to the main Pats, **
- Making Rep #Self without switching to #Max (it is normally #Min)
- Confusing ByteCount with CharCount and #Rel with #Abs.
- Using a #Cons Pat in a #Cons Rep (will slow things down drastically).
The first mistake is often quite a head scratcher, as the match records no results and does not call the RFunc callback. While many such problems can be seemingly fixed with smarter defaults, the problems they introduce, when the smart defaults are wrong, would be even harder to catch! Perhaps a better solution would be to have an optional check function to warn if the pattern match is suspect.
The At Pat, although a convenient predicate for matching, can also cause a few problems,
especially if the particular Match is called with explicit StartPos
and FirstPos values. For example, Pat.At(2) will default to #Rel,
so counts 2 characters from FirstPos given to the Match function.
But a preceeding Pat.Go(2) will have advanced 2 characters from
StartPos, perhaps a very different location. Similarly, Pat.At(2,
Pat.#Abs) will count from the very beginning of the buffer, Pos 0,
regardless of FirstPos or StartPos.
In summary, #Rel (default) for Go, counts characters from the current Match
position, which will be StartPos in a fresh Match. But #Rel for At counts
chars from FirstPos/LastPos. Similarly, #Abs for Go measures from
FirstPos/LastPos given to the Match, but #Abs for At measures from
the very beginning/end of the Buffer. So #Abs in Go generally corresponds to #Rel in At!
(One can also argue for a future #Byte count option in Go and At Pats.)
A final common error is altering the Match buffer during the match. It is quite tempting
to call Ed.Del or Ed.Write (even Ed.Xfer) from an
SFunc or RFunc. But the low-level machinery will not expect or understand such buffer
changes! If nothing else, this will alter the buffer length and the current Match position,
typically leading to an infinite loop or (more often) a crash. An innocuous Pat
like At(-1) cannot function if the buffer length is changed! The only workable
approach is to use the Match buffer as the source template and copy/xfer as necessary to a
new buffer, skipping all that should be deleted.
**I had a bewildering error when Goto was used in a
test file to jump (over other code) directly to a Match call I wanted to study. Alas,
the overzealous Goto I wrote also jumped over the RFunc
definition/assignment, so the Match function was being fed int 0 instead of the
expected RFunc closure! Match was perfectly happy and ran normally, no errors or warnings,
but no RFunc was ever called. Turns out the compiler saw the
RFunc Function and declared its variable, but the function innards was not defined at
runtime because of the Goto, so it kept its initial value of
int 0.