If you look at the api documentation, the parser-building methods and operators are implemented using by-name parameters which are then cached using lazy val
.
That being said, you still have to be careful about order of initialization. The following code will result in a null reference exception:
val as = a.*
val a = "a"
However the following is fine (because the argument to ~
is by-name):
val as = "a" ~ a.*
val a = "a"
Putting lazy
in front of everything will 'help', but is noisy. Performance should not be a big deal, as generally you will only want to initialize the grammar once. def
expressions will be initialized once for each point they are referenced.
You can be a bit clever and eliminate plumbing for your 'minor' productions using block syntax:
lazy val addExpr = {
val add = expr ~ "+" ~ expr
val sub = expr ~ "-" ~ expr
add | sub
}