Free maths expression parser?

Thanks for pitching in everyone, so far all of the bugs mentioned in this thread have been resolved. Since most of the debugging/testing was done by multiple people, development time for this was just a mere ~4 hours

@CommanderKeith … sorry for stealing your thread :slight_smile: Hopefully the snippet will make up for it

Quick recap, since it’s several posts up:

If anyone else can break it, please share

The test jar lacks some action listener for that text field. If you add it one can just press return at the end. :wink:

It’s getting better and better. Nice job getting it done in such a short time.

You should put it up in shared code when it’s finished, I’m sure it would be of use to many people, especially considering JEP costs $550 (http://www.singularsys.com/order/) and people probably only need it for the kind of calcs that you’re one can do.

This is great, thanks for sharing. Variables and constants would be great though ::slight_smile:

I might opensource my parser, which also has variables and multiple-argument functions (bound to Java methods).

It was my futile attempt at making a fancy scripting language. :persecutioncomplex:

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;}
}

Cool, the more options the better!

Thanks everyone for the help.

It claims 2^3^4 is 4096. It should be 2.41785164E24.

I see your point, but thats debatable. Do math parsers using the ^ notation usually treat it as being written
234
or
(23)4

?

Wikipedia suggests it should be right-associative, so here’s a tweaked version of the JavaCC grammar:

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=Exponential() {return 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=Exponential() {return 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;}
}

Good question. =)

Google treats it as 2^(3^4), at least. Don’t know about the rest. It makes the most sense to me.