meta data for this page
The new SCAN Statement
JavaCC 21 introduces a new SCAN
statement that is designed to supersede the legacy LOOKAHEAD
. It is a key part of the newer streamlined syntax. Like LOOKAHEAD
, SCAN
exists to specify conditions at choice points that override the default resolution mechanism of scanning ahead exactly one token. The new SCAN
construct provides a superset of the functionality of LOOKAHEAD
and tends to more succinct and readable. (Sometimes dramatically so.)
Note, however, that the older LOOKAHEAD
syntax still works and will work for the foreseeable feature.
Here is a rundown of the differences between SCAN
and the older LOOKAHEAD
.
The parameters of SCAN (assuming there are any) are not enclosed in parentheses.
Thus, where you previously wrote:
LOOKAHEAD(3) Foo()
you would now write:
SCAN 3 Foo
Where you previously wrote things like:
LOOKAHEAD("foo" ("bar"|"baz")) FooBar()
you now write:
SCAN "foo" ("bar"|"baz") => FooBar
Note that the arrow is necessary in the above to separate the lookahead expansion from the expansion to be parsed. You can also write:
SCAN 3 => Foo
but the arrow is optional because it is not necessary to disambiguate anything.
The parameters of SCAN (assuming there are more than one) are not separated by a comma.
Thus, where you previously wrote:
LOOKAHEAD(Foo(), {someCondition()}) FooBar()
you now write:
SCAN {someCondition()} Foo => FooBar
(Note that the order was changed because in actual code, the semantic lookahead is actually evaluated first. At least in my implementation!)
Where you would previously write:
LOOKAHEAD(1, {someCondition}) Foo()
you now write:
SCAN 1 {someCondition} => Foo
A little point to note is that the new SCAN construct assumes indefinite scanahead by default. Thus, with the legacy LOOKAHEAD,
LOOKAHEAD({someCondition}) Foo()
is the equivalent of writing:
SCAN 0 {someCondition} => Foo
In the legacy lookahead, if you only have semantic lookahead, the number of tokens to be scanned is assumed to be zero, i.e. if the condition is true, you automatically go into the following expansion. JavaCC 21 makes the opposite assumption in these spots. Unless you specify otherwise, we assume unlimited lookahead.
The SCAN construct allows you to use contextual predicates.
The contextual_predicates construct allows you to express conditions based on scanning backwards in the lookahead/call stack. This can only be used in conjunction with the newer SCAN
statement, not with the legacy LOOKAHEAD
construct.
You can find more information here.
Addendum: the SCAN-less SCAN?
Even the newer SCAN construct has some redundancies. There is no obvious reason to oblige anybody write even:
SCAN 3 Foobar
We could permit:
3 Foobar
It is trivial to allow this, but I have refrained for now, since I think there is a balance to be drawn between overly verbose constructs and the overly cryptic. I think that legacy JavaCC syntax, by and large, is overly verbose, and there has been a clear need to streamline it, but I don’t want to go too far. It is quite likely that I will go for an in-between option of allowing:
3 => Foobar
But again, that is not currently available. As of this writing, you can write:
SCAN 3 Foobar
or:
SCAN 3 => Foobar
In general, the possibility of making even the word SCAN
optional is quite clear in some cases. What was previously written as:
LOOKAHEAD(Foo()) Foo()
can now be written as:
SCAN => Foo
or:
SCAN Foo
Again, it is quite feasible to allow simply:
=> Foo
In this case, because I think it is so common, we decided to allow this. And, in fact, you can see that this is already used in internal development, for example here.
Recap
The new SCAN
instruction in JavaCC 21 totally supersedes the legacy tool's LOOKAHEAD
. It allows you to specify in the following order:
- numerical lookahead, i.e. the maximum number of tokens to scan ahead
- semantic lookahead, i.e. arbitrary java code enclosed in {..}
- a contextual_predicates predicate
Caveats:
- The
SCAN
construct can specify all (or none) of the above, except that numerical lookahead and syntactic lookahead are mutually exclusive. - If no numerical or syntactic lookahead is specified, the generated code will scan ahead an unlimited number of tokens. This differs from the legacy tool.
Note that an empty SCAN statement can be simply written alternatively with a lone arrow, i.e. ⇒
.