Skip to content

Commit

Permalink
new
Browse files Browse the repository at this point in the history
  • Loading branch information
javalc6 committed Dec 27, 2022
1 parent 27f0da6 commit e84403f
Show file tree
Hide file tree
Showing 11 changed files with 590 additions and 1 deletion.
53 changes: 52 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1 +1,52 @@
# Expression-Parser
# Expression Parser
The class ExpressionParserPlus implements expression parser. It supports parsing of expressions with numbers and booleans values.

Numeric expressions may contain brackets ( ), operators *,-,/,+,"and" and "or" and numbers with optional decimal point and values "true" and "false".
In addition it is possible to use constants "PI" and "E", functions sin(), cos(), tan(), log(), exp(), sqrt().

# Methods
The following methods are provided to parse and evaluate math expressions:
```
parseExpression(String expr): parses the given expression and returns a binary tree representing the parse expression;
evaluate(Node p): evaluates the binary tree containing an expression;
visit(Node p): visits the binary tree containing an expression;
```

# Syntax Rules
The parser use the following rules to parse math expressions:
```
<expression> ::= <conditional or expr> [ "?" <expression> ":" <expression> ]
<conditional or expr> ::= <boolean term> { "or" <boolean term> }*
<boolean term> ::= <equality relation> { "and" <equality relation> }*
<equality relation> ::= <relation expression> "==" <relation expression> | <relation expression> "!=" <relation expression> | <relation expression>
<relation expression> ::= <simple expression> "<" <simple expression> | <simple expression> "<=" <simple expression> | <simple expression> ">=" <simple expression> | <simple expression> ">" <simple expression>
<simple expression> ::= <term> { ("+"|"-") <term> }*
<term> ::= <factor> { ("*"|"/") <factor> }*
<factor> ::= <boolean> | <constant> | <unary function> "(" <expression> ")" | "(" <expression> ")" | "!" <factor> | "-" <factor> | <number>
<boolean> ::= "false" | "true"
<constant> == "PI" | "E"
<unary function> == "sin" | "cos" | "tan" | "log" | "exp" | "sqrt"
<number> ::= { <digit> }+ [ "." { <digit> }* ]
<digit> ::= "0" | "1" | "2" | "3" | "4" | "5" | "6" | "7" | "8" | "9"
```

# Example

Parsing the expression (sin(PI*0.25)+cos(PI*0.25))*sqrt(2) you obtain the following binary tree:

```
*
/ \
/ \
+ sqrt
/ \ |
sin cos 2
| |
* *
/ \ / \
PI 0.25 PI 0.25
```
The evaluation of this binary tree returns the value 2, as expected.

68 changes: 68 additions & 0 deletions src/BinaryNode.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
public class BinaryNode extends Node {//Node element with two childs
protected Node left;
protected Node right;

BinaryNode(Type type) {
super(type);
left = null;
right = null;
}

public Object evaluate() {
switch(type) {
case add:
return (Double)left.evaluate() + (Double)right.evaluate();
case subtract:
return (Double)left.evaluate() - (Double)right.evaluate();
case multiply:
return (Double)left.evaluate() * (Double)right.evaluate();
case divide:
return (Double)left.evaluate() / (Double)right.evaluate();
case or:
return (Boolean)left.evaluate() || (Boolean)right.evaluate();
case and:
return (Boolean)left.evaluate() && (Boolean)right.evaluate();
case equal:
return left.evaluate().equals(right.evaluate());
case unequal:
return !left.evaluate().equals(right.evaluate());
case lt:
return (Double)left.evaluate() < (Double)right.evaluate();
case lte:
return (Double)left.evaluate() <= (Double)right.evaluate();
case gt:
return (Double)left.evaluate() > (Double)right.evaluate();
case gte:
return (Double)left.evaluate() >= (Double)right.evaluate();
default://will never happen
throw new RuntimeException("unexpected type: " + type);
}
}

public void visit() {
switch(type) {
case add:
case subtract:
case multiply:
case divide:
case and:
case or:
case equal:
case unequal:
case lt:
case lte:
case gt:
case gte:
System.out.print('(');
left.visit();
System.out.print(" " + type + " ");
right.visit();
System.out.print(')');
break;
default://will never happen
throw new RuntimeException("unexpected type: " + type);
}
}


}//end of class Node
233 changes: 233 additions & 0 deletions src/ExpressionParserPlus.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,233 @@
/* Class that implements expression parser. It supports parsing of expressions with numbers and booleans values.
Numeric expressions may contain brackets ( ), operators *,-,/,+,"and" and "or" and numbers with optional decimal point and values "true" and "false".
In addition it is possible to use constants "PI" and "E", functions sin(), cos(), tan(), log(), exp(), sqrt().
Rules for expressions:
<expression> ::= <conditional or expr> [ "?" <expression> ":" <expression> ]
<conditional or expr> ::= <boolean term> { "or" <boolean term> }*
<boolean term> ::= <equality relation> { "and" <equality relation> }*
<equality relation> ::= <relation expression> "==" <relation expression> | <relation expression> "!=" <relation expression> | <relation expression>
<relation expression> ::= <simple expression> "<" <simple expression> | <simple expression> "<=" <simple expression> | <simple expression> ">=" <simple expression> | <simple expression> ">" <simple expression>
<simple expression> ::= <term> { ("+"|"-") <term> }*
<term> ::= <factor> { ("*"|"/") <factor> }*
<factor> ::= <boolean> | <constant> | <unary function> "(" <expression> ")" | "(" <expression> ")" | "!" <factor> | "-" <factor> | <number>
<boolean> ::= "false" | "true"
<constant> == "PI" | "E"
<unary function> == "sin" | "cos" | "tan" | "log" | "exp" | "sqrt"
<number> ::= { <digit> }+ [ "." { <digit> }* ]
<digit> ::= "0" | "1" | "2" | "3" | "4" | "5" | "6" | "7" | "8" | "9"
27-12-2022: initial release: storing expression as generic tree
*/
import java.util.HashMap;

public class ExpressionParserPlus {

final static int MaxNumOfDigit = 38; // max number of digits for numbers

public static void main(String[] args) {//demo part of the ExpressionParserPlus class
ExpressionParserPlus ep = new ExpressionParserPlus();
try {
Node p1 = ep.parseExpression("log(exp((sin(PI/4)+cos(PI/4))/(sqrt(2)*tan(PI/4))))");
ep.visit(p1);
System.out.println();
System.out.println("Result: "+ep.evaluate(p1));//expected result: 1.0
System.out.println();

Node p2 = ep.parseExpression("(0 == 0) and (0 != 1) and !false or ((true == false) and (2*2 < 5))");
ep.visit(p2);
System.out.println();
System.out.println("Result: "+ep.evaluate(p2));//expected result: true
System.out.println();

Node p3 = ep.parseExpression("5 > 4 ? 1 + 2 + 3 : 2 * 2");
ep.visit(p3);
System.out.println();
System.out.println("Result: "+ep.evaluate(p3));//expected result: 6
System.out.println();


} catch (Exception ex) {
System.err.println("Exception: " + ex.getMessage());//ex.printStackTrace();
}
}

//public method that evaluates an expression stored in tree 'p', returns either Double or Boolean
public Object evaluate(Node p) {//@NonNull Node p
return p.evaluate();
}

//public method that visits expression stored in tree 'p'
public void visit(Node p) {//@NonNull Node p
p.visit();
}

//public method that returns the parsed expression as a tree
public Node parseExpression(String expr) throws Exception {
StringHolder sh = new StringHolder(expr);
Node result = expression(sh);
if (sh.chars_available())
throw new Exception("unexpected characters: " + expr.substring(sh.pointer));
return result;
}

//protected area
protected Node expression(StringHolder sh) throws Exception {
return conditional_expression(sh);
}

protected Node conditional_expression(StringHolder sh) throws Exception {
Node pcoe = conditional_or_expr(sh);
if (sh.nextSequence("?")) {
Node pe = expression(sh);
if (sh.nextSequence(":")) {
TernaryNode p = new TernaryNode(Type.conditional_expression);
p.left = pcoe;
pcoe = p;
p.center = pe;
p.right = conditional_expression(sh);
} else throw new Exception("missing :");
}
return pcoe;
}

protected Node conditional_or_expr(StringHolder sh) throws Exception {
Node pbt = boolean_term(sh);
while (sh.nextSequence("or")) {
BinaryNode p = new BinaryNode(Type.or);
p.left = pbt;
pbt = p;
p.right = boolean_term(sh);
}
return pbt;
}

protected Node boolean_term(StringHolder sh) throws Exception {
Node per = equality_relation(sh);
while (sh.nextSequence("and")) {
BinaryNode p = new BinaryNode(Type.and);
p.left = per;
per = p;
p.right = equality_relation(sh);
}
return per;
}

protected Node equality_relation(StringHolder sh) throws Exception {
Node pre = relation_expression(sh);
Type type;
if (sh.nextSequence("==")) {
type = Type.equal;
} else if (sh.nextSequence("!=")) {
type = Type.unequal;
} else return pre;
BinaryNode p = new BinaryNode(type);
p.left = pre;
pre = p;
p.right = relation_expression(sh);
return pre;
}

protected Node relation_expression(StringHolder sh) throws Exception {
Node se = simple_expression(sh);
Type type;
if (sh.nextSequence(">")) {
type = Type.gt;
} else if (sh.nextSequence(">=")) {
type = Type.gte;
} else if (sh.nextSequence("<")) {
type = Type.lt;
} else if (sh.nextSequence("<=")) {
type = Type.lte;
} else return se;
BinaryNode p = new BinaryNode(type);
p.left = se;
se = p;
p.right = simple_expression(sh);
return se;
}

protected Node simple_expression(StringHolder sh) throws Exception {
Node pt = term(sh);
Character ch;
while ((ch = sh.getChar("+-")) != null) {
BinaryNode p = new BinaryNode(ch == '+' ? Type.add : Type.subtract);
p.left = pt;
pt = p;
p.right = term(sh);
}
return pt;
}

protected Node term(StringHolder sh) throws Exception {
Node pf = factor(sh);
Character ch;
while ((ch = sh.getChar("*/")) != null) {
BinaryNode p = new BinaryNode(ch == '*' ? Type.multiply : Type.divide);
p.left = pf;
pf = p;
p.right = factor(sh);
}
return pf;
}

protected Node factor(StringHolder sh) throws Exception {
String identifier = sh.getIdentifier();
if (identifier != null) {
if (identifier.equals("false") || identifier.equals("true"))
return new NodeIdentifier(identifier);
else if (identifier.equals("PI") || identifier.equals("E"))
return new NodeIdentifier(identifier);
else if (identifier.equals("sin") || identifier.equals("cos") || identifier.equals("tan") || identifier.equals("log") || identifier.equals("exp") || identifier.equals("sqrt")) {
Character ch = sh.getChar("(");
if (ch != null) { // ch == "("
UnaryNodeIdentifier ni = new UnaryNodeIdentifier(identifier);
ni.child = expression(sh);
if (sh.getChar(")") == null)
throw new Exception("missing ) bracket");
return ni;
} else throw new Exception("missing ( bracket");
} else throw new Exception("unknown identifier: "+identifier);
} else {
Character ch = sh.getChar("(!-0123456789");
if (ch == null)
if (sh.chars_available())
throw new Exception("unexpected character: " + sh.expr.charAt(sh.pointer));
else throw new Exception("unexpected end of expression");
if (ch == '(') {
Node n1 = expression(sh);
if (sh.getChar(")") == null)
throw new Exception("missing ) bracket");
return n1;
} else if (ch == '!') {
UnaryNode n2 = new UnaryNode(Type.not);
n2.child = factor(sh);
return n2;
} else if (ch == '-') {
UnaryNode n3 = new UnaryNode(Type.minus);
n3.child = factor(sh);
return n3;
} else if (ch >= '0' && ch <= '9') {
double num = 0; int nDigits = 0;
while (ch != null) {
if (nDigits < MaxNumOfDigit) {
num = num * 10 + ch - '0';
nDigits++;
} else num = num * 10;
ch = sh.getChar("0123456789");
}
if (sh.getChar(".") != null) {//ch != null --> ch == '.'
double m = 1;
while ((ch = sh.getChar("0123456789")) != null) {
m = m * 0.1;
num = num + (ch - '0') * m;
}
}
return new NodeDouble(num);
} else return null;
}
}

}
12 changes: 12 additions & 0 deletions src/Node.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
abstract public class Node {//Abstract node element of the tree
protected final Type type;

Node(Type type) {//generic node constructor
this.type = type;
}

abstract public Object evaluate();//returns either Double or Boolean

abstract public void visit();

}//end of class Node
17 changes: 17 additions & 0 deletions src/NodeDouble.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
class NodeDouble extends Node {//Node element holding a number
protected double num;

NodeDouble(double num) {//node constructor of type number
super(Type.number);
this.num = num;
}

public Object evaluate() {
return num;
}

public void visit() {
System.out.print(num);
}

}//end of class NodeDouble
Loading

0 comments on commit e84403f

Please sign in to comment.