If anyone prefers a JavaCC grammar, this is largely based on the calculator in the tutorial and handles all the test cases I’ve seen in this thread. It also supports numbers in the format 2.6E-17.
options
{
STATIC = false ;
}
PARSER_BEGIN(Calculator)
import java.io.PrintStream ;
class Calculator
{
public static void main( String[] args ) throws ParseException, TokenMgrError, NumberFormatException
{
Calculator parser = new Calculator(System.in);
parser.Start( System.out );
}
}
PARSER_END(Calculator)
SKIP : { " " }
TOKEN : { < EOL : "\n" | "\r" | "\r\n" > }
TOKEN : { < PLUS : "+" > }
TOKEN : { < MINUS : "-" > }
TOKEN : { < TIMES : "*" > }
TOKEN : { < DIVIDE : "/" > }
TOKEN : { < POW : "^" > }
TOKEN : { < OPEN_PAR : "(" > }
TOKEN : { < CLOSE_PAR : ")" > }
TOKEN : { < NUMBER : <FP> | <FP> "E" <DIGITS> | <FP> "E-" <DIGITS> > }
TOKEN : { < #FP : <DIGITS> | <DIGITS> "." <DIGITS> | <DIGITS> "." | "." <DIGITS> > }
TOKEN : { < #DIGITS : (["0"-"9"])+ > }
void Start(PrintStream printStream) throws NumberFormatException :
{
double value;
}
{
(
value=Expression() <EOL> {printStream.println(value);}
|
<EOL> {}
)*
<EOF>
}
double Expression() throws NumberFormatException :
{
double i ;
double value ;
}
{
value=Term()
(
<PLUS> i=Term() {value += i;}
|
<MINUS> i=Term() {value -= i;}
)*
{return value;}
}
double Term() throws NumberFormatException :
{
double i ;
double value ;
}
{
value=Exponential()
(
<TIMES> i=Exponential() {value *= i;}
|
<DIVIDE> i=Exponential() {value /= i;}
|
i=NonUnaryExponential() {value *= i;}
)*
{return value;}
}
double Exponential() throws NumberFormatException:
{
double i;
double value;
}
{
value=Primary()
(
<POW> i=Primary() {value = Math.pow(value, i);}
)*
{return value;}
}
/* For use on the right-hand of an implicit multiplication, to avoid ambiguity in the grammar */
double NonUnaryExponential() throws NumberFormatException:
{
double i;
double value;
}
{
value=NonUnaryPrimary()
(
<POW> i=Primary() {value = Math.pow(value, i);}
)*
{return value;}
}
double NonUnaryPrimary() throws NumberFormatException :
{
Token t;
double d;
}
{
t=<NUMBER> {return Double.parseDouble(t.image);}
|
<OPEN_PAR> d=Expression() <CLOSE_PAR> {return d;}
}
double Primary() throws NumberFormatException :
{
double d ;
}
{
d=NonUnaryPrimary() {return d;}
|
<PLUS> d=Primary() {return d;}
|
<MINUS> d=Primary() {return -d;}
}