tags:

views:

39

answers:

1

I'm trying to create a grammar for multiplying and dividing numbers in which the '*' symbol does not need to be included. I need it to output an AST. So for input like this:

1 2 / 3 4

I want the AST to be

(* (/ (* 1 2) 3) 4)

I've hit upon the following, which uses java code to create the appropriate nodes:

grammar TestProd;

options {
  output = AST;
}

tokens {
  PROD;
}

DIV :   '/';

multExpr: (INTEGER -> INTEGER)
          ( {div = null;}
            div=DIV? b=INTEGER
                ->
                ^({$div == null ? (Object)adaptor.create(PROD, "*") : (Object)adaptor.create(DIV, "/")}
              $multExpr $b))*
    ;

INTEGER: ('0' | '1'..'9' '0'..'9'*);

WHITESPACE: (' ' | '\t')+ { $channel = HIDDEN; };

This works. But is there a better/simpler way?

+1  A: 

Here's a way:

grammar Test;

options {
  backtrack=true;
  output=AST;
}

tokens {
  MUL;
  DIV;
}

parse
  : expr* EOF
  ;

expr
  :  (atom -> atom) 
     ( '/' a=atom -> ^(DIV $expr $a)
     | a=atom     -> ^(MUL $expr $a)
     )*
  ;

atom
  :  Number
  |  '(' expr ')' -> expr
  ;

Number
  :  '0'..'9'+
  ;

Space
  :  (' ' | '\t' | '\r' | '\n') {skip();}
  ;

Tested with:

import org.antlr.runtime.*;
import org.antlr.runtime.tree.Tree;

public class Main {
    public static void main(String[] args) throws Exception {
        String source = "1 2 / 3 4";
        ANTLRStringStream in = new ANTLRStringStream(source);
        TestLexer lexer = new TestLexer(in);
        CommonTokenStream tokens = new CommonTokenStream(lexer);
        TestParser parser = new TestParser(tokens);
        TestParser.parse_return result = parser.parse();
        Tree tree = (Tree)result.getTree();
        System.out.println(tree.toStringTree());
    }
}

produced:

(MUL (DIV (MUL 1 2) 3) 4)
Bart Kiers
I knew there had to be a better way ... seems obvious now. Thanks, Bart!
Dan Becker
@Dan, you're welcome!
Bart Kiers