Simple Calculator with Antlr.

For quite some time I’ve been interested in creating scripting languages to make existing applications more flexible than thought possible. After writing a couple of parsers I’ve stumbled upon ANTLR (ANother Tool for Language Recognition) which is a Lexer and Parser all in one.

Although I’m aware of the existence of a couple of other tools, such as “yacc, lex,gold parser,.. ” this is the first tool I’m going to try out. I see a appliance to read svg files, and generate code (c#/Java/vb/ada) that will draw the figure.

Here is a preview of what this sample aims to:


But first things first, I longed to create a simple calculator, helping me to asses the possibilities of ANTLR, with a little help from :The Definitive ANTLR Reference: Building Domain-Specific Languages (Pragmatic Programmers) (Rather helpfull), I’ve managed the job quite well.

Since I’ve seen some questions on the Internet requesting a simple example for a simple calculator grammar, here it is:

grammar SimpleCalc;

tokens {
PLUS 	= '+' ;
MINUS	= '-' ;
MULT	= '*' ;
DIV	= '/' ;
RPAREN	= ')' ;
LPAREN	= '(' ;
ASSIGN	= '=' ;
}

/*----------------
* PARSER RULES
*----------------*/

prog :		stat+;
stat :		expr NEWLINE
	|	ID ASSIGN expr NEWLINE
	|	NEWLINE; 			//Do nothing

expr 	:	multExpr ((PLUS | MINUS )multExpr)*;

multExpr:	atom ((MULT | DIV) atom )*;

atom 	:	INT
	|	ID
	|	LPAREN expr RPAREN;

/*----------------
* LEXER RULES
*----------------*/

ID 	:	('a'..'z'|'A'..'Z')+;
INT 	:	'0'..'9'+;
NEWLINE :	'\r'?'\n';
WS 	:	(' '|'\t'|'\n'|'\r')+;

The grammar above can handle expressions such as:

1+2*3
a=4-5
c=a*(6/2)

Now one has to add actions to the rules these will look like:

atom returns[int value] <==Tell Antlr this rule is implemented as a function, returning an int.
: INT {$value = int.Parse($INT.text);} <==The return value '$value' is assign the parsed as an integer string value of int.

Now all you need to do is fill every thing in. When an ID follow by an assignment (a=6) is encounter it will be placed in a Dictionary.

After adding the appropriate actions the calculator is finished, it works internally with an int, so all divisions are integer divisions!. Adaptation for doubles will be no problem at all, but is left to you.

grammar SimpleCalc3;

options
{
language=CSharp;
}

tokens {
PLUS 	= '+' ;
MINUS	= '-' ;
MULT	= '*' ;
DIV	= '/' ;
RPAREN	= ')' ;
LPAREN	= '(' ;
ASSIGN	= '=' ;
}

@members
{
public static System.Collections.Generic.Dictionary IDTable = new System.Collections.Generic.Dictionary();

public static void Main(string[] args)
{
  Console.WriteLine("type '@' to quit...");
  string line = "";
  while (true)
  {
      line = Console.ReadLine();
      if (line.Contains("@"))
        break;

      SimpleCalc3Lexer lex = new SimpleCalc3Lexer(new ANTLRStringStream(line+Environment.NewLine));
      CommonTokenStream tokens = new CommonTokenStream(lex);
      SimpleCalc3Parser parser = new SimpleCalc3Parser(tokens);

      try
      {
        parser.prog();
      }
      catch (RecognitionException e)
      {
        Console.Error.WriteLine(e.StackTrace);
      }
  }
}

}

/*----------------
* PARSER RULES
*----------------*/

prog :	stat+;
stat :	expr NEWLINE			{System.Console.WriteLine($expr.value);}
|	ID ASSIGN expr NEWLINE		{IDTable[$ID.Text] =$expr.value;}
|	NEWLINE; 			//Do nothing

expr returns[int value]
:	a=multExpr {$value = $a.value;} (
PLUS b=multExpr {$value+=$b.value;}
|
MINUS b=multExpr{$value-=$b.value;})*;

multExpr returns[int value]
:	a=atom {$value = $a.value;} (
MULT b=atom {$value*=$b.value;}
|
DIV b=atom{$value/=$b.value;})*;

atom returns[int value]
:	INT				{$value = int.Parse($INT.text);}
|	ID				{if (IDTable.ContainsKey($ID.Text)){$value = IDTable[$ID.Text];}else{System.Console.WriteLine("ID does not exist");}}
|	LPAREN expr RPAREN		{$value = $expr.value;};

/*----------------
* LEXER RULES
*----------------*/

ID :	('a'..'z'|'A'..'Z')+;

INT :	'0'..'9'+;

NEWLINE :	'\r'?'\n';

WS :	(' '|'\t'|'\n'|'\r')+ {Skip();};

If you desire a working example, please download this file, Included are the .net binaries (.dll) from the antlr’s website. When your enviroment path is set correctly to the c# compiler, you will be able to run make.cmd to create the console application.
If you want to download the ANTLR binaries yourself, get them at the original site, look for “ANTLR Tool Binaries”.

enjoy!

*Update 24-aug-2008: Added the grammars to the zip.
*Update 27-apr-2009: Added sample movie.
*Update 13-oct-2009: Syntax Highlighting

Please, if you have questions or comments, let us know.

4 Responses to “Simple Calculator with Antlr.”

Leave a Reply