Skip to content

Design considerations

Daus Salar edited this page Jul 7, 2015 · 6 revisions

Introduction

One of the fundamental differences between grappa and other parser packages, such as ANTLR and JavaCC, is that grammars (what grappa called parsers) are in pure Java; there is no DSL (Domain Specific Language) other than the ones you create via the name of your rule methods (if that can be considered a DSL at all).

The way grappa works (basically, it creates a subclass of your parser class, or classes, see below) does imply some limitations, which will be expressed first because they are important to keep in mind. On the other hand, the fact that this is Java means you have all Java at your disposal; as such, you need not think "grammar" when writing parsers. Some consequences of which are expressed below.

Modifiers and visibility

The rules are the following:

  • your parser classes cannot be final; inner parser classes cannot be private;
  • your rule methods cannot be private or final;
  • methods you wish to use as boolean expressions in rules cannot be private (but they can be final);
  • instance variables you wish to use in rules cannot be private (but they can be final).

The reason is that when you create a parser, what you obtain is a subclass of your original parser. See here for more details.

Keep your parsers small

Parsers are Java classes; you can inherit them, and even use other parsers in parsers. Do not hesitate to split!

This works fine, for instance:

public class OuterParser
    extends EventBusParser<Object>
{
    protected final InnerParser inner
        = Parboiled.createParser(InnerParser.class);

    Rule myRule()
    {
        return sequence("foo", inner.someRule());
    }
}

Basically, don't write a 100.000 lines grammar; there's no point! If you have a whole programming language to parse, for instance, you can make one parser for a variable, another for literal values etc etc, and use them in one "main" parser.

Rule methods are Java methods

Here again, do not hesitate; methods can take arguments, they can have variables, etc etc. The only rule to a rule method is that it must return a Rule.

They can even make calls to other methods returning booleans in rules themselves. For instance:

public class IntParser
    extends EventBusParser<Object>
{
    Rule anInt()
    {
        return sequence(
            sequence(optional('-'), oneOrMore(digit())),
            isInt(match())
        );
    }

    /*
     * Reminder: cannot be private since it is used in a rule method!
     */
    protected final boolean isInt(final String match)
    {
        try {
            Integer.parseInt(match);
            return true;
        } catch (NumberFormatException ignored) {
            return false;
        }
    }
}

Last but not least: use Guava!

Grappa depends on Guava. This is by design. Some people think that this library is "heavy" in that the size of the jar is over 2 MiB.

But what is 2 MiB today? And especially, what is 2 MiB when you have all the power of this library at your disposal?