[antlr-interest] Heterogeneous AST - simple calculator parser

koud0002 robertkoudelka at power-solutions.com.au
Thu Feb 7 16:03:07 PST 2002


I am building a parser and an interpreter for a tool to validate data 
in a database.
I want to be able to specify validation rules for table columns (eg. 
> TODAY AND < #7-FEB-2002#)
 
I would like to use ANTLR to help me build the parser so I have 
started off with a simple grammar for a calculator.
I got (+, -, *, /) operators working. However, I have a difficulty 
incorporating unary minus into the grammar.
I tried to use actions to build parts of the AST but my attempt were 
unsuccessful.
Would you be able to point me in the right direction? Thank you.
 
I am using heterogeneous ASTs because I want to become familiar with 
this concept as I feel that I will need to use them later in the 
validator tool.
 
My grammar (calc.g), the AST node classes, a test program and a batch 
file to build the parser, compile it and run the Calc test program 
follow this message.
 
Sorry about bothering you with my problems but I would really 
appreciate your help.
 
Robert Koudelka



// File: calc.g
class CalcParser extends Parser;
options {
  k = 4;                           // four token lookahead
  codeGenMakeSwitchThreshold = 2;  // Some optimizations
  codeGenBitsetTestThreshold = 3;
  defaultErrorHandler = false;     // Don't generate parser error 
handlers
  ASTLabelType = "CalcAST";
	buildAST = true;
}

tokens {
  UNARY_MINUS<AST=UnaryMinusNode>;

  PLUS<AST=PlusNode>;
  MINUS<AST=MinusNode>;
  STAR<AST=MultNode>;
  DIV<AST=DivNode>;
  INT<AST=IntNode>;
}


expression
  : logicalExpression SEMI!
  ;

logicalExpression
  : relExpression ((AND^ | OR^) relExpression)*
  ;

relExpression
  : simpleExpression ((LT^ | LE^ | EQ^ | GE^ | GT^ | NEQ^) 
simpleExpression)*
  ;

simpleExpression
  : MINUS t:term!
     {
       System.out.println("Got unary");
       #simpleExpression = #([UNARY_MINUS, "-"], #t);

//       #simpleExpression = antlr.ASTFactory.create(new 
UnaryMinusNode(LT(1))), #t);
//       #simpleExpression = new UnaryMinusNode(t);
     }
  | term ( (PLUS^ | MINUS^) term )*
  ;

term
  : factor ( (STAR^ | DIV^) factor )*
  ;

factor
  : INT
  | LPAREN! logicalExpression RPAREN!
  ;


class CalcTreeWalker extends TreeParser;

expression returns [float r]
{
	float a,b;
	r=0;
}
	:	#(PLUS a=expression b=expression)	 { r = a+b; }
	|	#(MINUS a=expression b=expression) { r = a-b; }
	|	#(UNARY_MINUS a=expression)
    {
      r = (-1) * a;
    }
	|	#(STAR a=expression b=expression)  { r = a*b; }
	|	#(DIV a=expression b=expression)   { r = a/b; }
	|	#(LT a=expression b=expression)    { if (a < b) r = 
1; else r = 0; }
	|	#(LE a=expression b=expression)    { if (a <= b) r = 
1; else r = 0; }
	|	#(EQ a=expression b=expression)    { if (a == b) r = 
1; else r = 0; }
	|	#(GE a=expression b=expression)    { if (a >= b) r = 
1; else r = 0; }
	|	#(GT a=expression b=expression)    { if (a > b) r = 
1; else r = 0; }
	|	#(NEQ a=expression b=expression)   { if (a != b) r = 
1; else r = 0; }
	|	#(AND a=expression b=expression)   { if ((a > 0) && 
(b > 0))  r = 1; else r = 0; }
	|	#(OR a=expression b=expression)    { if ((a > 0) || 
(b > 0))  r = 1; else r = 0; }
	|	i:INT                              { r = (float)
Integer.parseInt(i.getText()); }
	;


class CalcLexer extends Lexer;
options {
  charVocabulary = '\0'..'\377';
  testLiterals = false;   // don't automatically test for literals
  k = 4;                  // four characters of lookahead
  caseSensitive = true;
  caseSensitiveLiterals = true;
}


WS
	:	(' '
	|	'\t'
	|	'\n'
	|	'\r')
		{ _ttype = Token.SKIP; }
	;

SEMI         options {paraphrase = ";";}     : ';'   ;
STAR         options {paraphrase = "*";}     : '*'   ;
PLUS         options {paraphrase = "+";}     : '+'   ;
MINUS        options {paraphrase = "-";}     : '-'   ;
DIV          options {paraphrase = "/";}     : '/'   ;
AND          options {paraphrase = "AND";}   : "AND" ;
OR           options {paraphrase = "OR";}    : "OR"  ;
EQ           options {paraphrase = "=";}     : '='   ;
NEQ          options {paraphrase = "<>";}    : "<>"  ;
LT           options {paraphrase = "<";}     : '<'   ;
LE           options {paraphrase = "<=";}    : "<="  ;
GE           options {paraphrase = ">=";}    : ">="  ;
GT           options {paraphrase = ">";}     : '>'   ;
LPAREN       options {paraphrase = "(";}     : '('   ;
RPAREN       options {paraphrase = ")";}     : ')'   ;


protected
DIGIT : '0'..'9';
INT   : (DIGIT)+;




// File: Calc.java
import java.io.*;
import antlr.CommonAST;
import antlr.collections.AST;
import antlr.RecognitionException;
import antlr.TokenStreamException;

class Calc {

	public static void main(String[] args) throws Exception {
		boolean b = true;

    try {
      if (args.length > 0) {
        String filename = args[0];
        if (processEquationFile(filename)) {
          return;
        }
      }

      float value = 0;
      BufferedReader reader = null;
      while (true) {
        System.out.print("Enter an expression: ");
        reader = new BufferedReader(new InputStreamReader(System.in));
        value = parseExpression(reader, "<stdin>");
        System.out.println("value is " + value);
      }
		}
		catch (TokenStreamException e) {
			System.err.println("exception: " + e);
		}
		catch (RecognitionException e) {
			System.err.println("exception: " + e);
		}
	}


  private static float parseExpression(Reader reader, String 
filename) {
    float r = 0;
    try {
      CalcLexer lexer = new CalcLexer(reader);
      lexer.setFilename(filename);
      CalcParser parser = new CalcParser(lexer);
      parser.setFilename(filename);
      parser.setASTNodeType("CalcAST");
      parser.setASTNodeClass("CalcAST");

      parser.expression(); // Parse the input expression
//      AST t = parser.getAST();
      CalcAST t = (CalcAST)parser.getAST();

      System.out.println("Tree in LIST notation: " + t.toStringTree
());
      CalcTreeWalker walker = new CalcTreeWalker();

      // Traverse the tree created by the parser
      r = walker.expression(t);

		} catch (TokenStreamException e) {
			System.err.println("exception: " + e);
		} catch (RecognitionException e) {
			System.err.println("exception: " + e);
		}

    return r;
  } // method parseExpression


  private static boolean processEquationFile(String filename) throws 
Exception {
    File f = new File(filename);
    if (f.exists() == false) {
      System.out.println("File: " + f.getAbsoluteFile() + " does not 
exist!");
      return false;
    }
    BufferedReader fileReader = new BufferedReader(new FileReader(f));
    StringReader reader = null;
    String line = null;
    float value = 0;
    while ((line = fileReader.readLine()) != null) {
      if (line.startsWith("//") || (line.length() == 0)) {
        continue;
      }
      System.out.println("\nProcessing equation: " + line);
      reader = new StringReader(line);
      value = parseExpression(reader, "<stdin>");
      System.out.println("value is " + value);
    }

    return true;
  } // method processEquationFile

} // class Calc



// File: CalcAST.java
import antlr.*;
import antlr.collections.AST;

public class CalcAST extends BaseAST {
  protected Token token;

  public CalcAST() {
  } // constructor

  public CalcAST(Token token) {
    this.token = token;
//    System.out.println("CalcAST(" + token + ") constructor");
  } // constructor

  public String getText() {
    if (this.token == null) {
      System.out.println("null token in CalcAST.getText()");
      return "";
    }
    return token.getText();
  }

  // satisfy abstract methods from BaseAST
  public void initialize(int parm1, String parm2) { }
  public void initialize(Token parm1) { }
  public void initialize(AST parm1) { }

  public int value() { return 0; }

} // class CalcAST



// File: BinaryOperatorAST.java
import antlr.*;
import antlr.collections.AST;

public abstract class BinaryOperatorAST extends CalcAST {

  public BinaryOperatorAST(Token token) {
    super(token);
  }

  public CalcAST left() {
    return (CalcAST) getFirstChild();
  }

  public CalcAST right() {
    CalcAST t = left();
    if (t == null) {
      return null;
    }
    return (CalcAST) t.getNextSibling();
  }

} // class BinaryOperatorAST



// File: IntNode
import antlr.*;
import antlr.collections.AST;

public class IntNode extends CalcAST {

  public IntNode(Token token) {
    super(token);
  }

  public int getType() {
    return CalcParserTokenTypes.INT;
  }

  public String toString() {
    return " " + value();
  }

  public int value() {
    return Integer.parseInt(token.getText());
  }

} // class IntNode



// File: PlusNode.java
import antlr.*;
import antlr.collections.AST;

public class PlusNode extends BinaryOperatorAST {

  public PlusNode(Token token) {
    super(token);
  }

  public int getType() {
    return CalcParserTokenTypes.PLUS;
  }

  public String toString() {
    return " +";
  }

  public int value() {
    if (left() == null) {
      System.out.println("Error: PlusNode (no operands)!");
      return 0;
    }

    int val = left().value();
    if (right() != null) { // binary plus
      val += right().value();
    }
    return val;
  }

} // class PlusNode



// File: MinusNode.java
import antlr.*;
import antlr.collections.AST;

public class MinusNode extends BinaryOperatorAST {

  public MinusNode(Token token) {
    super(token);
  }

  public int getType() {
    return CalcParserTokenTypes.MINUS;
  }

  public String toString() {
    return " -";
  }

  public int value() {
    if (left() == null) {
      System.out.println("Error: MinusNode (no operands)!");
      return 0;
    }

    int val = left().value();
    if (right() != null) { // binary plus
      val -= right().value();
    } else { // unary minus
      val = -val;
    }
    return val;
  }

} // class MinusNode



// File: MultNode.java
import antlr.*;
import antlr.collections.AST;

public class MultNode extends BinaryOperatorAST {

  public MultNode(Token token) {
    super(token);
  }

  public int getType() {
    return CalcParserTokenTypes.STAR;
  }

  public String toString() {
    return " *";
  }

  public int value() {
    return left().value() * right().value();
  }

} // class MultNode



// File: DivNode.java
import antlr.*;
import antlr.collections.AST;

public class DivNode extends BinaryOperatorAST {

  public DivNode(Token token) {
    super(token);
  }

  public int getType() {
    return CalcParserTokenTypes.DIV;
  }

  public String toString() {
    return " /";
  }

  public int value() {
    return left().value() / right().value();
  }

} // class DivNode



// File: UnaryMinusNode.java
import antlr.*;
import antlr.collections.AST;

public class UnaryMinusNode extends CalcAST {

  public UnaryMinusNode(Token token) {
    super(token);
    System.out.println("UnaryMinusNode(" + token + ") constructor");
  }

  public int getType() {
    return CalcParserTokenTypes.UNARY_MINUS;
  }

  public String toString() {
    return " - " + ((CalcAST) getFirstChild()).toString();
  }

  public int value() {
    System.out.println("UnaryMinusNode.value()");

    int val = ((CalcAST) getFirstChild()).value();
    System.out.println("UnaryMinusNode.value(): " + -val);
    return -val;
  }

} // class UnaryMinusNode



// File: 1.expr
 1;
 3 + 2;
 4 * 3 + 10 / 5;
 4 * ( 3 + 10 ) / 5;
 8 + 7 * 2;
 -1;
// (- 8) + 7 * 2;
// - ( 8 + 7 * 2);




// File: run.bat
set CLASSPATH=.;f:\java\antlr\antlr-2.7.1
java antlr.Tool calc.g

javac *.java

rem 1.expr is a file containing expressions
java Calc 1.expr



 

Your use of Yahoo! Groups is subject to http://docs.yahoo.com/info/terms/ 



More information about the antlr-interest mailing list