Inbetween
Because APM lexes and parses at once, there is no token pass to quietly throw
whitespace away. Instead you say where skipping is allowed, with ., and
what gets skipped, in a block named by the same ..
stmt = "(" . "A" . ")";
. {
spc, nl
};
At every ., everything in the config list is matched repeatedly until none of
them matches any more. Then the parse carries on.
What the skip matches never reaches the AST. It is consumed and dropped, so whitespace and comments do not turn up as nodes you then have to ignore.
Why it is written, not inserted
A tool that skipped whitespace everywhere could not describe a token. Because
. is something you write, the same language describes both:
number = <0:9>+; # no `.` -- digits are adjacent, "1 2" is two numbers
sum := number . "+" . number; # `.` -- "1 + 2" and "1+2" both parse
That is the whole difference between a lexer rule and a grammar rule in APM.
Skipping up to something
. takes everything the config list allows, and a line end is usually in that
list. Sometimes you want the rest of it but not that:
line := (word (. ^ nl) word); # skip, but stop before the line end
line := (word .::until(nl) word); # skip, and take the line end too
A counted . repeats one unit of the config list at a time, so the
terminator gets asked before the newline is swallowed. Written on its own, .
runs the whole list until none of it matches – which is what it has always
done, and what every other . in your grammar still does.
The terminator does not have to be in the config list at all:
line := (word (. ^ ";") word); # skip whitespace up to a semicolon
Important
. is already “as much as there is”, so a counter written on it says the same
thing twice and is refused: .*, .+ and .? are errors. Write . on its
own.
See Counter for ^ and ::until in general.
Where it is easy to forget
Neither a counter nor a permutation skips between its repetitions or members.
Put the . inside the thing being repeated:
list := item+; # items with nothing between them
list := (item .)+; # items separated by the skip
attrs = perm[(a .) (b .)];
And the parser loop does not skip between runs of the start grammar — see Parser.
Important
If . appears anywhere in your grammar you must define the . config
block. There is no default skip set.
See also
Built-in Actions are what usually fill the block. See
Samples for . in a complete parser.