I'm trying to parse a file format, using the excellent parboiled2 library, in which the presence of some fields is dependent upon the value of one or more fields already processed.
For example, say I have two fields, the first of which is a flag indicating whether the second is present. That is, if the first field is true, then the second field (which is an integer value, in this example) is present and must be processed - but if it's false, then the second field isn't present at all. Note that this second field isn't optional - it either must be processed (if the first field is true) or must not be processed (if the first field is false).
So, if a third field (which we'll assume is always present) is a quoted string, both of the following lines are valid:
true 52 "Some quoted string"
false "Some other quoted string"
But this would be invalid:
false 25 "Yet another quoted string"
Ignoring the third field, how do I write a rule to parse the first two? (I can't tell from the documentation, and Googling hasn't helped so far...)
UPDATE: I should clarify that I can't use rules like the following, because the format I'm parsing is actually a lot more complicated than my example:
import org.parboiled2._
class MyParser(override val input: ParserInput)
extends Parser {
def ws = // whitepsace rule, puts nothing on the stack.
def intField = // parse integer field, pushes Int onto stack...
def dependentFields = rule {
("true" ~ ws ~ intField) | "false" ~> //etc.
}
}
UPDATE 2: I've revised the following to make my intent clearer:
What I'm looking for is a valid equivalent to the following (non-existent) rule that performs a match only if a condition is satisfied:
import org.parboiled2._
class MyParser(input: ParserInput)
extends Parser {
def ws = // whitepsace rule, puts nothing on the stack.
def intField = // parse integer field, pushes Int onto stack...
def boolField = // parse boolean field, pushes Boolean onto stack...
def dependentFields = rule {
boolField ~> {b =>
// Match "ws ~ intField" only if b is true. If match succeeds, push Some(Int); if match
// fails, the rule fails. If b is false, pushes None without attempting the match.
conditional(b, ws ~ intField)
}
}
}
That is, ws ~ intField is only matched if boolField results in a true value. Is something like this possible?
Yes, you can implement such a function with the help of
testparser action:According to the Meta-Rules section of the documentation, it can work only by passing a function to produce rules. You'd have to define
dependentFieldsrule as follows:Update:
While
test(pred) ~ opt1 | opt2is a common technique, it does backtrack and tries to applyopt2, iftestis successfultest, butopt1fails. Here are two possible solutions to prevent such backtracking.You can use
~!~rule combinator, that has "cut" semantics and prohibits backtracking over itself:Or you actually use
ifoutside of a rule to check the boolean argument and return one of two possible rules: