Menu

scScript Pattern Paradigm

Pattern matching, while appearing simple, can in fact be quite complex and require an inordinate amount of processing, searching, and backtracking. While the traditional RE approach hides the process, the Pat system in scScript, like C itself, endeavors to be more procedural, allowing the script writer more insight and control over the matching process and the steps therein. A lot of work can also be offloaded to various callback routines which can themselves create/use Pats and call the (re-entrant) matcher! So a little theoretical understanding of the process goes a long way to ensure correct results and optimize performance. There are often hard ways and easy ways of doing things.
Pat.Match(Buffer Pat/Str Mode/MFunc StartPos FirstPos LastPos RFunc GapRFunc)(PatDict MStartPos MEndPos) REQ REQ opt opt opt opt opt opt opt opt Opt

The Pat.Match function takes a specified Pat or Str and matches it against text from an scEmacs buffer. It requires only the first two arguments, all the rest are optional. The match process begins at StartPos (defaults to 0); if a match is found, it will call the RFunc callback and set the out variables MStartPos and MEndPos to the beginning and end of the match respectively. Pat.Match deals with byte pos, not char counts, and it is important to input byte pos that do not straddle UTF-8 chars. GapRFunc, if provided, is a callback (likeRFunc) that Pat.Match calls for non-matching text it gaps over as it scans the buffer.

The optional FirstPos and LastPos in variables delimit the permissible Buffer boundaries. This might seem unnecessary, but a Pat and its sub-parts can move to and fro within the data and it is sometimes beneficial to explicitly set the boundaries. (FirstPos is inclusive, LastPos is exclusive.)

If a PatDict out variable is provided, it will be set to the PDict created by the match process. Then again, if PatDict already holds a dict, Match will simply use it and add/alter pairs as necessary. While PDict will always be passed on to callback functions, a PatDict out variable (for Pat.Match) is necessary if the calling script itself wants to access it after the Match function returns.

The optional Mode in var determines how Pat.Match operates:

#Anchor: Match at and only at StartPos. Do not look anywhere else!
#Once: (Default mode) Match starting at StartPos. If the Pat/Str does not match, look at the next pos and try again! Continue until a match is found or reach LastPos (default is end-of-buffer).
#Slide: Start like #Once. But after a successful match is found, slide over one char (multiple Pos if sliding over a UTF-8 char) and go again!
#Skip: Start like #Once. But after a successful match is found, skip over it, start from the first Pos after the match, and go again.
MFunc: MFunc callback will determine whether and where to match from again.

The first two options can result in at most a single match, Pat.Match will return int 0 if it fails and int 1 if it succeeds (will also call RFunc if given). The #Anchor option will be faster in case of failure, as there is no scanning for the next potential match.

The next two modes can match very many times and Pat.Match will not return until all is done. Each time a successful match is found, it will invoke the optional RFunc++ callback (if given), set MStartPos and MEndPos, as well as stash data in PDict. But it will then #Slide or #Skip to the next starting position and continue to scan for another match. In that sense, these modes will continue finding all possible matches until the very end. As expected, #Skip will be faster, since it will scan fewer starting positions. When done, Pat.Match will return with the number of successful matches.

++ While RFunc, especially for sub-Pats, appears to be called during a match, it is actually called after the top level pattern match succeeds! Match simply pushes each intermediate success result on to an RStack and calls RFunc on its contents after all the searching and backtracking is done. This is done so the calling script does not get buffeted with RFunc results that become invalid because of #Cons backtracking.

Pat.Match can also allow a callback MFunc to dictate its operating mode, instead of using hard coded values. This is useful if the decision whether to continue (and from whence) depends on the data itself. The callback can return #Done to mean Pat.Match is done (it must have already matched once), #Slide or #Skip to continue at the respective next location, and #Custom if the callback itself sets the next StartPos where matching should resume.

The Match apparatus is re-entrant, so an MFunc may launch another Match to determine the value for StartPos, where the current Match should resume!