Grammatica Reference Manual

Examples

These examples come from the test/src/grammar and test/src/java directories. The source code has been simplified in these examples in order to be easier to read. Please refer to the original source code in the above directories for the complete compilable versions. Also note that the corresponding C# examples are found in the test/src/csharp directory.

The figure below contains a complete and working example of a grammar file for a simple arithmetic language.

%header%

GRAMMARTYPE = "LL"

DESCRIPTION = "A grammar for a simple arithmetic language."

AUTHOR      = "Per Cederberg, <per at percederberg dot net>"
VERSION     = "1.0"
DATE        = "10 June 2003"

LICENSE     = "Permission is granted to copy this document verbatim in any
               medium, provided that this copyright notice is left intact."

COPYRIGHT   = "Copyright (c) 2003 Per Cederberg. All rights reserved."


%tokens%

ADD                          = "+"
SUB                          = "-"
MUL                          = "*"
DIV                          = "/"
LEFT_PAREN                   = "("
RIGHT_PAREN                  = ")"
NUMBER                       = <<[0-9]+>>
IDENTIFIER                   = <<[a-z]>>
WHITESPACE                   = <<[ \t\n\r]+>> %ignore%


%productions%

Expression = Term [ExpressionTail] ;

ExpressionTail = "+" Expression
               | "-" Expression ;

Term = Factor [TermTail] ;
     
TermTail = "*" Term
         | "/" Term ;

Factor = Atom
       | "(" Expression ")" ;

Atom = NUMBER
     | IDENTIFIER ;

Figure 1. A grammar for a simple arithmetic language.

To create a parser for the grammar above, the parser source code must first be created. If the grammar above has been stored in the file arithmetic.grammar, this can be accomplished with the following command:

# java -jar lib/grammatica-1.4.jar arithmetic.grammar --javaoutput test

This will create the files ArithmeticAnalyzer.java, ArithmeticConstants.java, ArithmeticParser.java, and ArithmeticTokenizer.java in the test subdirectory. These files contain the source code for the parser. In order to call the parser, the method in the figure below can be inserted into another Java class.

private Node parseArithmetic(String input) {
    Parser  parser = null;

    parser = new ArithmeticParser(new StringReader(input));
    return parser.parse();
}

Figure 2. The source code for a method parsing an arithmetic string.

In the case of the arithmetic language, is it also interesting to analyze and evaluate the contents of the string. This can be done by subclassing the ArithmeticAnalyzer class and overloading the callback methods for the relevant productions. A small example of this can be seen in the figure below.

class ArithmeticCalculator extends ArithmeticAnalyzer {

    protected Node exitNumber(Token node) {
        node.addValue(new Integer(node.getImage()));
        return node;
    }

    protected Node exitExpression(Production node) {
        ArrayList  values = getChildValues(node);
        Integer    value1;
        Integer    value2;
        String     op;
        int        result;
        
        if (values.size() == 1) {
            result = ((Integer) values.get(0)).intValue();
        } else {
            value1 = (Integer) values.get(0);
            value2 = (Integer) values.get(2);
            op = (String) values.get(1);
            result = operate(op, value1, value2);
        }
        node.addValue(new Integer(result));
        return node;
    }

    protected Node exitExpressionTail(Production node) {
        node.addValues(getChildValues(node));
        return node;
    }

    protected Node exitTerm(Production node) {
        ArrayList  values = getChildValues(node);
        Integer    value1;
        Integer    value2;
        String     op;
        int        result;
        
        if (values.size() == 1) {
            result = ((Integer) values.get(0)).intValue();
        } else {
            value1 = (Integer) values.get(0);
            value2 = (Integer) values.get(2);
            op = (String) values.get(1);
            result = operate(op, value1, value2);
        }
        node.addValue(new Integer(result));
        return node;
    }

    protected Node exitTermTail(Production node) {
        node.addValues(getChildValues(node));
        return node;
    }

    protected Node exitFactor(Production node) throws ParseException {
        int  result;
        
        if (node.getChildCount() == 1) {
            result = getIntValue(getChildAt(node, 0), 0);
        } else {
            result = getIntValue(getChildAt(node, 1), 0);
        }
        node.addValue(new Integer(result));
        return node;
    }

    protected Node exitAtom(Production node) {
        node.addValues(getChildValues(node));
        return node;
    }

Figure 3. The partial (and incomplete) source code for an analyzer calculating the result of an arithmetic expression.