Search code examples
javaparsingparser-generatorparboiled

Using variables in parboiled


I'm attempting to create a simple XML parser using the parboiled Java library.

The following code attempts to use a variable to verify that the closing tag contains the same identifier as the opening tag.

class SimpleXmlParser2 extends BaseParser<Object> {
    Rule Expression() {
        StringVar id = new StringVar();
        return Sequence(OpenElement(id), ElementContent(), CloseElement(id));
    }

    Rule OpenElement(StringVar id) {        
        return Sequence('<', Identifier(), ACTION(id.set(match())), '>');
    }

    Rule CloseElement(StringVar id) {    
        return Sequence("</", id.get(), '>');                               
    }

    Rule ElementContent() {
        return ZeroOrMore(NoneOf("<>"));
    }

    Rule Identifier() {
        return OneOrMore(CharRange('A', 'z'));
    }    
}

The above, however, fails with the error message org.parboiled.errors.GrammarException: 'null' cannot be automatically converted to a parser Rule, when I create the ParseRunner.

It would appear that I have a basic misunderstanding of how variables should be used in parboiled. Can anyone help me resolve this?


Solution

  • Came up with an answer. Including it here for any other parboiled newbie who may struggle with the same issue.

    The problem was that any access to the variable's content must happen in a parser action to ensure that it takes place in the parse-phase, rather than in the rule construction phase.

    The following changes to the program above, ensures that parsing fails if an element identifier is unmatched.

    Rule CloseElement(StringVar id) {    
        return Sequence("</", Identifier(), matchStringVar(id), '>');                               
    }
    
    Action matchStringVar(final StringVar var) {
        return new Action() {
            public boolean run(Context ctx) {
                String match = ctx.getMatch();
                return match.equals(var.get());
            }
        };
    }