Menu

scScript Pattern Format

scScript is very flexible with its handling of function args, so Pat definition functions, like Pat.Seq or Pat.Rep can take very many arguments. These are broken into 3 sets: (appearing in order)

  1. Op Args, (Min, Max, etc.) mandatory initial args for a few Pat types.
  2. Flags/Pat Args, (Flags and Str/Pat/PFunc args) defining the meat for most Pat.
  3. Stash Directives, (each also needs a value) for any Pat that needs them.

Op Args (1) always come first. They are not optional, they must be present and exactly as specified for the particular Pat types that require them. Most Pats do not require any and should not have them. So Rep needs Min and Max to establish the bounds of repetition (e.g. from 1 to 27 reps), and Iter needs the Cnt (to set both Min and Max), but Seq or Alt take no such Op Args.

Pat Args (2) define templates that actually match against text data. These come just after the Op Args, if any, and define what the Pat is looking for. Pat Args are usually Strings or Pats, but can also be callback scScript PFuncs (closures) that can succeed/fail directly (return 0 to fail or non-0 Ints to succeed), or return other Strings or Pats for matching.

Integer Flags are also mixed in with Pat Args. They are easy to spot, as they are pre-defined integers, like #Cons or #Neg. Every time a Flag is encountered, it affects the operation of all subsequent Pat Args (or the entire Pat), so it is very typical to have one or more Flags at the beginning. Flags can be listed individually or combined together with | (bit-or) or + (add), as they are bit encoded and additive. Default Flags are normally left out, but are necessary if a previously set Flag has to be countermanded for subsequent args.

Note: Op (#Min, #Max), Mode (#Self, #Cons), and Result (#NoRes, #Res) flags should come before any Pat Args as these apply to the Pat itself, and not its args!

Pat Flags are sticky, once set they apply to all subsequent args, until countermanded. This is especially important for #Dyn and #Neg, as they change the interpretation and logic of all following ops.

++++ Flags in general only operate inside the Pat that contains them. They cannot penetrate sub-Pats to alter their behavior.

For example Pat.Seq(Pat.#Exact, Pat.Char("aA")) will match all accented forms of the letters a or A, because Pat.Char will happily match them despite the #Exact flag in the superior Pat.Seq. (Despite appearances, #Neg is no exception, it just negates the result of the sub-pat match! So in Pat.Seq(Pat.#Neg, Pat.Char(...)), it will make the Seq fail if the Pat.Char succeeds!)

Stash Directives (Dirs for short) (3) are typically instructions to store (stash) information when the Pat matches (or in the case of Location Directives, when the Pat is defined). The matching process maintains a PDict to store this information and its contents can be accessed during or after the match. The initial purpose was to store useful bits of the match for the calling script, but it turns out the match process itself (and callbacks) want this data too! So this information is stashed as soon as an individual Pat matches, even if in the big picture, the outermost Pat has not matched yet. The to-and-fro searching behavior of considerate Pats means this data may be stashed (and possibly become invalid) many times!

A good use case is stashing info in one sub-Pat and using it dynamically in a subsequent one. Consider a Pat to match scScript long strings. These begin with |> with 0 to 8 intermediate - (dash) characters, such as |--->. The string must end with the exact mirror image delimiter.

A simple Pat to match the opening delimiter is: POpen = Pat.Seq("|", Pat.Rep(0, 8, Pat.#Self + Pat.#Max, "-"), ">");

But how would a PClose know what the POpen matched?

The easiest approach is for POpen to stash the match string using an SFunc callback: POpen = Pat.Seq("|", Pat.Rep(0, 8, Pat.#Self + Pat.#Max, "-"), ">", Pat.#S_Func, StashRev);

StashRev is a one-line SFunc written to store the reversed matching string in PDict under "EndLS": Function XStashRevX(PDict)() {PDict.EndLS = Sys.Reverse(Pat.GetStr()); Return 1;}

Pat.GetStr is an accessor function for use inside callbacks. As the name suggests, it returns the Str matched in the current Pat. But unfortunately, reversing takes a little more work, as |--> reversed would just be >--| and the arrow head needs flipping around! A correct approach would be to get the string CLen and construct a delimiter string: Function StashRev(PDict)() { PDict.EndLS = Str.Form("<", Str.CLen(Pat.GetStr()) - 2, "-", 1, "|"); Return 1; }

Str.Form concatenates 1 copy of "<" to N-2 copies of "-" (where N is the opening string length) and 1 copy of "|", thus forming the ending delimiter for long comments. Now PClose can be written as: PClose = Pat.Seq(Pat.#Dyn, "EndLS");

This will use the #Dyn key (EndLS) to find the data string "<---|" in PDict, then use it to match buffer data. So the PClose will not match against "EndLS", but the value stored in PDict.EndLS.

An improved approach would be to stash the S_Cnt in Rep and use it directly instead of counting chars in the POpen string! (This is also shown in SampleSC/Pat1.sc.)

Note:#S_Name and #S_Data are Location Directives and associate the Name and Data with the Pat at definition time, regardless of whether it matches anything. The former is quite useful for debugging and display while the latter can carry a payload into the match!