⚠️ Warning: This is a draft ⚠️
This means it might contain formatting issues, incorrect code, conceptual problems, or other severe issues.
If you want to help to improve and eventually enable this page, please fork RosettaGit's repository and open a merge request on GitHub.
{{task}}[[Category:Recursion]] Create a program which parses and evaluates arithmetic expressions.
;Requirements:
- An [[wp:Abstract_syntax_tree|abstract-syntax tree]] (AST) for the expression must be created from parsing the input.
- The AST must be used in evaluation, also, so the input may not be directly evaluated (e.g. by calling eval or a similar language feature.)
- The expression will be a string or list of symbols like "(1+3)*7".
- The four symbols + - * / must be supported as binary operators with conventional precedence rules.
- Precedence-control parentheses must also be supported.
;Note: For those who don't remember, mathematical precedence is as follows:
- Parentheses
- Multiplication/Division (left to right)
- Addition/Subtraction (left to right)
;C.f:
- [[24 game Player]].
- [[Parsing/RPN calculator algorithm]].
- [[Parsing/RPN to infix conversion]].
11l
[[wp:Pratt parser|Pratt parser]]
T Symbol
String id
Int lbp
Int nud_bp
Int led_bp
(ASTNode -> ASTNode) nud
(ASTNode, ASTNode -> ASTNode) led
F set_nud_bp(nud_bp, nud)
.nud_bp = nud_bp
.nud = nud
F set_led_bp(led_bp, led)
.led_bp = led_bp
.led = led
T ASTNode
Symbol& symbol
Int value
ASTNode? first_child
ASTNode? second_child
F eval()
S .symbol.id
‘(number)’
R .value
‘+’
R .first_child.eval() + .second_child.eval()
‘-’
R I .second_child == N {-.first_child.eval()} E .first_child.eval() - .second_child.eval()
‘*’
R .first_child.eval() * .second_child.eval()
‘/’
R .first_child.eval() / .second_child.eval()
‘(’
R .first_child.eval()
E
assert(0B)
R 0
Dict[String, Symbol] symbol_table
Array[String] tokens
V tokeni = -1
ASTNode token_node
F advance(sid = ‘’)
I sid != ‘’
assert(:token_node.symbol.id == sid)
:tokeni++
:token_node = ASTNode()
I :tokeni == tokens.len
:token_node.symbol = :symbol_table[‘(end)’]
R
V token = :tokens[:tokeni]
:token_node.symbol = :symbol_table[I token.is_digit() {‘(number)’} E token]
I token.is_digit()
:token_node.value = Int(token)
F expression(rbp = 0)
ASTNode t = :token_node
advance()
V left = t.symbol.nud(t)
L rbp < :token_node.symbol.lbp
t = :token_node
advance()
left = t.symbol.led(t, left)
R left
F parse(expr_str) -> ASTNode
:tokens = re:‘\s*(\d+|.)’.find_strings(expr_str)
:tokeni = -1
advance()
R expression()
F symbol(id, bp = 0) -> &
I !(id C :symbol_table)
V s = Symbol()
s.id = id
s.lbp = bp
:symbol_table[id] = s
R :symbol_table[id]
F infix(id, bp)
F led(ASTNode self, left)
self.first_child = left
self.second_child = expression(self.symbol.led_bp)
R self
symbol(id, bp).set_led_bp(bp, led)
F prefix(id, bp)
F nud(ASTNode self)
self.first_child = expression(self.symbol.nud_bp)
R self
symbol(id).set_nud_bp(bp, nud)
infix(‘+’, 1)
infix(‘-’, 1)
infix(‘*’, 2)
infix(‘/’, 2)
prefix(‘-’, 3)
symbol(‘(number)’).nud = self -> self
symbol(‘(end)’)
F nud_parens(ASTNode self)
V expr = expression()
advance(‘)’)
R expr
symbol(‘(’).nud = nud_parens
symbol(‘)’)
L(expr_str) [‘-2 / 2 + 4 + 3 * 2’,
‘2 * (3 + (4 * 5 + (6 * 7) * 8) - 9) * 10’]
print(expr_str‘ = ’parse(expr_str).eval())
{{out}}
-2 / 2 + 4 + 3 * 2 = 9
2 * (3 + (4 * 5 + (6 * 7) * 8) - 9) * 10 = 7000
Ada
See [[Arithmetic Evaluator/Ada]].
ALGOL 68
{{works with|ALGOL 68|Revision 1 - no extensions to language used}} {{works with|ALGOL 68G|Any - tested with release [http://sourceforge.net/projects/algol68/files/algol68g/algol68g-1.18.0/algol68g-1.18.0-9h.tiny.el5.centos.fc11.i386.rpm/download 1.18.0-9h.tiny]}} {{wont work with|ELLA ALGOL 68|Any (with appropriate job cards) - tested with release [http://sourceforge.net/projects/algol68/files/algol68toc/algol68toc-1.8.8d/algol68toc-1.8-8d.fc9.i386.rpm/download 1.8-8d] - A68RS has not implemented forward declarations}}
INT base=10;
MODE FIXED = LONG REAL; # numbers in the format 9,999.999 #
#IF build abstract syntax tree and then EVAL tree #
MODE AST = UNION(NODE, FIXED);
MODE NUM = REF AST;
MODE NODE = STRUCT(NUM a, PROC (FIXED,FIXED)FIXED op, NUM b);
OP EVAL = (NUM ast)FIXED:(
CASE ast IN
(FIXED num): num,
(NODE fork): (op OF fork)(EVAL( a OF fork), EVAL (b OF fork))
ESAC
);
OP + = (NUM a,b)NUM: ( HEAP AST := NODE(a, (FIXED a,b)FIXED:a+b, b) );
OP - = (NUM a,b)NUM: ( HEAP AST := NODE(a, (FIXED a,b)FIXED:a-b, b) );
OP * = (NUM a,b)NUM: ( HEAP AST := NODE(a, (FIXED a,b)FIXED:a*b, b) );
OP / = (NUM a,b)NUM: ( HEAP AST := NODE(a, (FIXED a,b)FIXED:a/b, b) );
OP **= (NUM a,b)NUM: ( HEAP AST := NODE(a, (FIXED a,b)FIXED:a**b, b) );
#ELSE simply use REAL arithmetic with no abstract syntax tree at all # CO
MODE NUM = FIXED, AST = FIXED;
OP EVAL = (FIXED num)FIXED: num;
#FI# END CO
MODE LEX = PROC (TOK)NUM;
MODE MONADIC =PROC (NUM)NUM;
MODE DIADIC = PROC (NUM,NUM)NUM;
MODE TOK = CHAR;
MODE ACTION = UNION(STACKACTION, LEX, MONADIC, DIADIC);
MODE OPVAL = STRUCT(INT prio, ACTION action);
MODE OPITEM = STRUCT(TOK token, OPVAL opval);
[256]STACKITEM stack;
MODE STACKITEM = STRUCT(NUM value, OPVAL op);
MODE STACKACTION = PROC (REF STACKITEM)VOID;
PROC begin = (REF STACKITEM top)VOID: prio OF op OF top -:= +10;
PROC end = (REF STACKITEM top)VOID: prio OF op OF top -:= -10;
OP ** = (COMPL a,b)COMPL: complex exp(complex ln(a)*b);
[8]OPITEM op list :=(
# OP PRIO ACTION #
("^", (8, (NUM a,b)NUM: a**b)),
("*", (7, (NUM a,b)NUM: a*b)),
("/", (7, (NUM a,b)NUM: a/b)),
("+", (6, (NUM a,b)NUM: a+b)),
("-", (6, (NUM a,b)NUM: a-b)),
("(",(+10, begin)),
(")",(-10, end)),
("?", (9, LEX:SKIP))
);
PROC op dict = (TOK op)REF OPVAL:(
# This can be unrolled to increase performance #
REF OPITEM candidate;
FOR i TO UPB op list WHILE
candidate := op list[i];
# WHILE # op /= token OF candidate DO
SKIP
OD;
opval OF candidate
);
PROC build ast = (STRING expr)NUM:(
INT top:=0;
PROC compress ast stack = (INT prio, NUM in value)NUM:(
NUM out value := in value;
FOR loc FROM top BY -1 TO 1 WHILE
REF STACKITEM stack top := stack[loc];
# WHILE # ( top >= LWB stack | prio <= prio OF op OF stack top | FALSE ) DO
top := loc - 1;
out value :=
CASE action OF op OF stack top IN
(MONADIC op): op(value OF stack top), # not implemented #
(DIADIC op): op(value OF stack top,out value)
ESAC
OD;
out value
);
NUM value := NIL;
FIXED num value;
INT decimal places;
FOR i TO UPB expr DO
TOK token = expr[i];
REF OPVAL this op := op dict(token);
CASE action OF this op IN
(STACKACTION action):(
IF prio OF thisop = -10 THEN
value := compress ast stack(0, value)
FI;
IF top >= LWB stack THEN
action(stack[top])
FI
),
(LEX):( # a crude lexer #
SHORT INT digit = ABS token - ABS "0";
IF 0<= digit AND digit < base THEN
IF NUM(value) IS NIL THEN # first digit #
decimal places := 0;
value := HEAP AST := num value := digit
ELSE
NUM(value) := num value := IF decimal places = 0
THEN
num value * base + digit
ELSE
decimal places *:= base;
num value + digit / decimal places
FI
FI
ELIF token = "." THEN
decimal places := 1
ELSE
SKIP # and ignore spaces and any unrecognised characters #
FI
),
(MONADIC): SKIP, # not implemented #
(DIADIC):(
value := compress ast stack(prio OF this op, value);
IF top=UPB stack THEN index error FI;
stack[top+:=1]:=STACKITEM(value, this op);
value:=NIL
)
ESAC
OD;
compress ast stack(-max int, value)
);
test:(
printf(($" euler's number is about: "g(-long real width,long real width-2)l$,
EVAL build ast("1+1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+1/15)/14)/13)/12)/11)/10)/9)/8)/7)/6)/5)/4)/3)/2")));
SKIP EXIT
index error:
printf(("Stack over flow"))
)
{{out}}
euler's number is about: 2.71828182845899446428546958
AutoHotkey
{{works with|AutoHotkey_L}}
/*
hand coded recursive descent parser
expr : term ( ( PLUS | MINUS ) term )* ;
term : factor ( ( MULT | DIV ) factor )* ;
factor : NUMBER | '(' expr ')';
*/
calcLexer := makeCalcLexer()
string := "((3+4)*(7*9)+3)+4"
tokens := tokenize(string, calcLexer)
msgbox % printTokens(tokens)
ast := expr()
msgbox % printTree(ast)
msgbox % expression := evalTree(ast)
filedelete expression.ahk
fileappend, % "msgbox % " expression, expression.ahk
run, expression.ahk
return
expr()
{
global tokens
ast := object(1, "expr")
if node := term()
ast._Insert(node)
loop
{
if peek("PLUS") or peek("MINUS")
{
op := getsym()
newop := object(1, op.type, 2, op.value)
node := term()
ast._Insert(newop)
ast._Insert(node)
}
Else
Break
}
return ast
}
term()
{
global tokens
tree := object(1, "term")
if node := factor()
tree._Insert(node)
loop
{
if peek("MULT") or peek("DIV")
{
op := getsym()
newop := object(1, op.type, 2, op.value)
node := factor()
tree._Insert(newop)
tree._Insert(node)
}
else
Break
}
return tree
}
factor()
{
global tokens
if peek("NUMBER")
{
token := getsym()
tree := object(1, token.type, 2, token.value)
return tree
}
else if peek("OPEN")
{
getsym()
tree := expr()
if peek("CLOSE")
{
getsym()
return tree
}
else
error("miss closing parentheses ")
}
else
error("no factor found")
}
peek(type, n=1)
{
global tokens
if (tokens[n, "type"] == type)
return 1
}
getsym(n=1)
{
global tokens
return token := tokens._Remove(n)
}
error(msg)
{
global tokens
msgbox % msg " at:`n" printToken(tokens[1])
}
printTree(ast)
{
if !ast
return
n := 0
loop
{
n += 1
if !node := ast[n]
break
if !isobject(node)
treeString .= node
else
treeString .= printTree(node)
}
return ("(" treeString ")" )
}
evalTree(ast)
{
if !ast
return
n := 1
loop
{
n += 1
if !node := ast[n]
break
if !isobject(node)
treeString .= node
else
treeString .= evalTree(node)
}
if (n == 3)
return treeString
return ("(" treeString ")" )
}
#include calclex.ahk
calclex.ahk
tokenize(string, lexer)
{
stringo := string ; store original string
locationInString := 1
size := strlen(string)
tokens := object()
start:
Enum := Lexer._NewEnum()
While Enum[type, value] ; loop through regular expression lexing rules
{
if (1 == regexmatch(string, value, tokenValue))
{
token := object()
token.pos := locationInString
token.value := tokenValue
token.length := strlen(tokenValue)
token.type := type
tokens._Insert(token)
locationInString += token.length
string := substr(string, token.length + 1)
goto start
}
continue
}
if (locationInString < size)
msgbox % "unrecognized token at " substr(stringo, locationInstring)
return tokens
}
makeCalcLexer()
{
calcLexer := object()
PLUS := "\+"
MINUS := "-"
MULT := "\*"
DIV := "/"
OPEN := "\("
CLOSE := "\)"
NUMBER := "\d+"
WS := "[ \t\n]+"
END := "\."
RULES := "PLUS,MINUS,MULT,DIV,OPEN,CLOSE,NUMBER,WS,END"
loop, parse, rules, `,
{
type := A_LoopField
value := %A_LoopField%
calcLexer._Insert(type, value)
}
return calcLexer
}
printTokens(tokens)
{
loop % tokens._MaxIndex()
{
tokenString .= printToken(tokens[A_Index]) "`n`n"
}
return tokenString
}
printToken(token)
{
string := "pos= " token.pos "`nvalue= " token.value "`ntype= " token.type
return string
}
BBC BASIC
{{works with|BBC BASIC for Windows}}
Expr$ = "1 + 2 * (3 + (4 * 5 + 6 * 7 * 8) - 9) / 10"
PRINT "Input = " Expr$
AST$ = FNast(Expr$)
PRINT "AST = " AST$
PRINT "Value = " ;EVAL(AST$)
END
DEF FNast(RETURN in$)
LOCAL ast$, oper$
REPEAT
ast$ += FNast1(in$)
WHILE ASC(in$)=32 in$ = MID$(in$,2) : ENDWHILE
oper$ = LEFT$(in$,1)
IF oper$="+" OR oper$="-" THEN
ast$ += oper$
in$ = MID$(in$,2)
ELSE
EXIT REPEAT
ENDIF
UNTIL FALSE
= "(" + ast$ + ")"
DEF FNast1(RETURN in$)
LOCAL ast$, oper$
REPEAT
ast$ += FNast2(in$)
WHILE ASC(in$)=32 in$ = MID$(in$,2) : ENDWHILE
oper$ = LEFT$(in$,1)
IF oper$="*" OR oper$="/" THEN
ast$ += oper$
in$ = MID$(in$,2)
ELSE
EXIT REPEAT
ENDIF
UNTIL FALSE
= "(" + ast$ + ")"
DEF FNast2(RETURN in$)
LOCAL ast$
WHILE ASC(in$)=32 in$ = MID$(in$,2) : ENDWHILE
IF ASC(in$)<>40 THEN = FNnumber(in$)
in$ = MID$(in$,2)
ast$ = FNast(in$)
in$ = MID$(in$,2)
= ast$
DEF FNnumber(RETURN in$)
LOCAL ch$, num$
REPEAT
ch$ = LEFT$(in$,1)
IF INSTR("0123456789.", ch$) THEN
num$ += ch$
in$ = MID$(in$,2)
ELSE
EXIT REPEAT
ENDIF
UNTIL FALSE
= num$
{{out}}
Input = 1 + 2 * (3 + (4 * 5 + 6 * 7 * 8) - 9) / 10
AST = ((1)+(2*((3)+(((4*5)+(6*7*8)))-(9))/10))
Value = 71
C
See [[Arithmetic Evaluator/C]].
C++
{{Works with|g++|4.1.2 20061115 (prerelease) (SUSE Linux)}}
{{libheader|Boost.Spirit|1.8.4}}
#include <boost/spirit.hpp>
#include <boost/spirit/tree/ast.hpp>
#include <string>
#include <cassert>
#include <iostream>
#include <istream>
#include <ostream>
using boost::spirit::rule;
using boost::spirit::parser_tag;
using boost::spirit::ch_p;
using boost::spirit::real_p;
using boost::spirit::tree_node;
using boost::spirit::node_val_data;
// The grammar
struct parser: public boost::spirit::grammar<parser>
{
enum rule_ids { addsub_id, multdiv_id, value_id, real_id };
struct set_value
{
set_value(parser const& p): self(p) {}
void operator()(tree_node<node_val_data<std::string::iterator,
double> >& node,
std::string::iterator begin,
std::string::iterator end) const
{
node.value.value(self.tmp);
}
parser const& self;
};
mutable double tmp;
template<typename Scanner> struct definition
{
rule<Scanner, parser_tag<addsub_id> > addsub;
rule<Scanner, parser_tag<multdiv_id> > multdiv;
rule<Scanner, parser_tag<value_id> > value;
rule<Scanner, parser_tag<real_id> > real;
definition(parser const& self)
{
using namespace boost::spirit;
addsub = multdiv
>> *((root_node_d[ch_p('+')] | root_node_d[ch_p('-')]) >> multdiv);
multdiv = value
>> *((root_node_d[ch_p('*')] | root_node_d[ch_p('/')]) >> value);
value = real | inner_node_d[('(' >> addsub >> ')')];
real = leaf_node_d[access_node_d[real_p[assign_a(self.tmp)]][set_value(self)]];
}
rule<Scanner, parser_tag<addsub_id> > const& start() const
{
return addsub;
}
};
};
template<typename TreeIter>
double evaluate(TreeIter const& i)
{
double op1, op2;
switch (i->value.id().to_long())
{
case parser::real_id:
return i->value.value();
case parser::value_id:
case parser::addsub_id:
case parser::multdiv_id:
op1 = evaluate(i->children.begin());
op2 = evaluate(i->children.begin()+1);
switch(*i->value.begin())
{
case '+':
return op1 + op2;
case '-':
return op1 - op2;
case '*':
return op1 * op2;
case '/':
return op1 / op2;
default:
assert(!"Should not happen");
}
default:
assert(!"Should not happen");
}
return 0;
}
// the read/eval/write loop
int main()
{
parser eval;
std::string line;
while (std::cout << "Expression: "
&& std::getline(std::cin, line)
&& !line.empty())
{
typedef boost::spirit::node_val_data_factory<double> factory_t;
boost::spirit::tree_parse_info<std::string::iterator, factory_t> info =
boost::spirit::ast_parse<factory_t>(line.begin(), line.end(),
eval, boost::spirit::space_p);
if (info.full)
{
std::cout << "Result: " << evaluate(info.trees.begin()) << std::endl;
}
else
{
std::cout << "Error in expression." << std::endl;
}
}
};
Clojure
(def precedence '{* 0, / 0
+ 1, - 1})
(defn order-ops
"((A x B) y C) or (A x (B y C)) depending on precedence of x and y"
[[A x B y C & more]]
(let [ret (if (<= (precedence x)
(precedence y))
(list (list A x B) y C)
(list A x (list B y C)))]
(if more
(recur (concat ret more))
ret)))
(defn add-parens
"Tree walk to add parens. All lists are length 3 afterwards."
[s]
(clojure.walk/postwalk
#(if (seq? %)
(let [c (count %)]
(cond (even? c) (throw (Exception. "Must be an odd number of forms"))
(= c 1) (first %)
(= c 3) %
(>= c 5) (order-ops %)))
%)
s))
(defn make-ast
"Parse a string into a list of numbers, ops, and lists"
[s]
(-> (format "'(%s)" s)
(.replaceAll , "([*+-/])" " $1 ")
load-string
add-parens))
(def ops {'* *
'+ +
'- -
'/ /})
(def eval-ast
(partial clojure.walk/postwalk
#(if (seq? %)
(let [[a o b] %]
((ops o) a b))
%)))
(defn evaluate [s]
"Parse and evaluate an infix arithmetic expression"
(eval-ast (make-ast s)))
user> (evaluate "1 + 2*(3 - 2*(3 - 2)*((2 - 4)*5 - 22/(7 + 2*(3 - 1)) - 1)) + 1")
60
Common Lisp
The following code processes the data in a pipeline of steps which are combined in the evaluate
function.
First, the string is converted into a sequence of tokens, represented as a list. Operator tokens are represented directly by the corresponding Lisp symbols, and the integer terms are represented by Lisp integer objects. The symbols :lparen
and :rparen
represent the the parentheses. So for instance the input
"1*(3+2)"
tokenizes as (1 * :lparen 3 + 2 :rparen)
.
Next, that sequence of tokens is then transformed by eliminating the parentheses. Subsequences of the form :lparen ... :rparen
with a sublist containing the tokens between the :lparen
and :rparen
. The sequence now has an intermediate tree structure, in which parenthesized fragments like 1 + 2 * 3 + 4 / 9
still remain flat.
At this point, another processing stage parses the operator precedence, and fully parenthesizes fragments, turning (1 + 2 / 3 + 5)
into (1 + (2 / 3) + 5)
. The result is a Lisp-ified infix representation.
Finally, this infix representation can be easily converted to prefix, forming the final AST which is a Lisp expression.
(Lisp expressions '''are''' abstract syntax trees!) This representation evaluates directly with eval
.
This implementation can read integers, and produce integral and rational values.
(defun tokenize-stream (stream)
(labels ((whitespace-p (char)
(find char #(#\space #\newline #\return #\tab)))
(consume-whitespace ()
(loop while (whitespace-p (peek-char nil stream nil #\a))
do (read-char stream)))
(read-integer ()
(loop while (digit-char-p (peek-char nil stream nil #\space))
collect (read-char stream) into digits
finally (return (parse-integer (coerce digits 'string))))))
(consume-whitespace)
(let* ((c (peek-char nil stream nil nil)))
(token (case c
(nil nil)
(#\( :lparen)
(#\) :rparen)
(#\* '*)
(#\/ '/)
(#\+ '+)
(#\- '-)
(otherwise
(unless (digit-char-p c)
(cerror "Skip it." "Unexpected character ~w." c)
(read-char stream)
(return-from tokenize-stream
(tokenize-stream stream)))
(read-integer)))))
(unless (or (null token) (integerp token))
(read-char stream))
token)))
(defun group-parentheses (tokens &optional (delimited nil))
(do ((new-tokens '()))
((endp tokens)
(when delimited
(cerror "Insert it." "Expected right parenthesis."))
(values (nreverse new-tokens) '()))
(let ((token (pop tokens)))
(case token
(:lparen
(multiple-value-bind (group remaining-tokens)
(group-parentheses tokens t)
(setf new-tokens (cons group new-tokens)
tokens remaining-tokens)))
(:rparen
(if (not delimited)
(cerror "Ignore it." "Unexpected right parenthesis.")
(return (values (nreverse new-tokens) tokens))))
(otherwise
(push token new-tokens))))))
(defun group-operations (expression)
(flet ((gop (exp) (group-operations exp)))
(if (integerp expression)
expression
(destructuring-bind (a &optional op1 b op2 c &rest others)
expression
(unless (member op1 '(+ - * / nil))
(error "syntax error: in expr ~a expecting operator, not ~a"
expression op1))
(unless (member op2 '(+ - * / nil))
(error "syntax error: in expr ~a expecting operator, not ~a"
expression op2))
(cond
((not op1) (gop a))
((not op2) `(,(gop a) ,op1 ,(gop b)))
(t (let ((a (gop a)) (b (gop b)) (c (gop c)))
(if (and (member op1 '(+ -)) (member op2 '(* /)))
(gop `(,a ,op1 (,b ,op2 ,c) ,@others))
(gop `((,a ,op1 ,b) ,op2 ,c ,@others))))))))))
(defun infix-to-prefix (expression)
(if (integerp expression)
expression
(destructuring-bind (a op b) expression
`(,op ,(infix-to-prefix a) ,(infix-to-prefix b)))))
(defun evaluate (string)
(with-input-from-string (in string)
(eval
(infix-to-prefix
(group-operations
(group-parentheses
(loop for token = (tokenize-stream in)
until (null token)
collect token)))))))
Examples
(evaluate "1 - 5 * 2 / 20 + 1") 3/2
(evaluate "(1 - 5) * 2 / (20 + 1)") -8/21
(evaluate "2 * (3 + ((5) / (7 - 11)))") 7/2
(evaluate "(2 + 3) / (10 - 5)") 1
Examples of error handling
> (evaluate "(3 * 2) a - (1 + 2) / 4")
Error: Unexpected character a.
1 (continue) Skip it.
2 (abort) Return to level 0.
3 Return to top loop level 0.
Type :b for backtrace, :c <option number> to proceed, or :? for other options
: 1 > :c 1
21/4
> (evaluate "(3 * 2) - (1 + 2) / (4")
Error: Expected right parenthesis.
1 (continue) Insert it.
2 (abort) Return to level 0.
3 Return to top loop level 0.
Type :b for backtrace, :c <option number> to proceed, or :? for other options
: 1 > :c 1
21/4
D
After the AST tree is constructed, a visitor pattern is used to display the AST structure and calculate the expression value.
import std.stdio, std.string, std.ascii, std.conv, std.array,
std.exception, std.traits;
struct Stack(T) {
T[] data;
alias data this;
void push(T top) pure nothrow @safe { data ~= top; }
T pop(bool discard = true)() pure @nogc @safe {
immutable static exc = new immutable(Exception)("Stack Empty");
if (data.empty)
throw exc;
auto top = data[$ - 1];
static if (discard)
data.popBack;
return top;
}
}
enum Type { Num, OBkt, CBkt, Add, Sub, Mul, Div }
immutable opChar = ["#", "(", ")", "+", "-", "*", "/"];
immutable opPrec = [ 0, -9, -9, 1, 1, 2, 2];
abstract class Visitor { void visit(XP e) pure @safe; }
final class XP {
immutable Type type;
immutable string str;
immutable int pos; // Optional, to dispaly AST struct.
XP LHS, RHS;
this(string s=")", int p = -1) pure nothrow @safe {
str = s;
pos = p;
auto localType = Type.Num;
foreach_reverse (immutable t; [EnumMembers!Type[1 .. $]])
if (opChar[t] == s)
localType = t;
this.type = localType;
}
override int opCmp(Object other) pure @safe {
auto rhs = cast(XP)other;
enforce(rhs !is null);
return opPrec[type] - opPrec[rhs.type];
}
void accept(Visitor v) pure @safe { v.visit(this); }
}
final class AST {
XP root;
Stack!XP opr, num;
string xpr, token;
int xpHead, xpTail;
void joinXP(XP x) pure @safe {
x.RHS = num.pop;
x.LHS = num.pop;
num.push(x);
}
string nextToken() pure @safe {
while (xpHead < xpr.length && xpr[xpHead] == ' ')
xpHead++; // Skip spc.
xpTail = xpHead;
if (xpHead < xpr.length) {
token = xpr[xpTail .. xpTail + 1];
switch (token) {
case "(", ")", "+", "-", "*", "/": // Valid non-number.
xpTail++;
return token;
default: // Should be number.
if (token[0].isDigit) {
while (xpTail < xpr.length && xpr[xpTail].isDigit())
xpTail++;
return xpr[xpHead .. xpTail];
} // Else may be error.
} // End switch.
}
if (xpTail < xpr.length)
throw new Exception("Invalid Char <" ~ xpr[xpTail] ~ ">");
return null;
} // End nextToken.
AST parse(in string s) /*@safe*/ {
bool expectingOP;
xpr = s;
try {
xpHead = xpTail = 0;
num = opr = null;
root = null;
opr.push(new XP); // CBkt, prevent evaluate null OP precedence.
while ((token = nextToken) !is null) {
XP tokenXP = new XP(token, xpHead);
if (expectingOP) { // Process OP-alike XP.
switch (token) {
case ")":
while (opr.pop!false.type != Type.OBkt)
joinXP(opr.pop);
opr.pop;
expectingOP = true;
break;
case "+", "-", "*", "/":
while (tokenXP <= opr.pop!false)
joinXP(opr.pop());
opr.push(tokenXP);
expectingOP = false;
break;
default:
throw new Exception("Expecting Operator or ), not <"
~ token ~ ">");
}
} else { // Process Num-alike XP.
switch (token) {
case "+", "-", "*", "/", ")":
throw new Exception("Expecting Number or (, not <"
~ token ~ ">");
case "(":
opr.push(tokenXP);
expectingOP = false;
break;
default: // Number.
num.push(tokenXP);
expectingOP = true;
}
}
xpHead = xpTail;
} // End while.
while (opr.length > 1) // Join pending Op.
joinXP(opr.pop);
} catch(Exception e) {
writefln("%s\n%s\n%s^", e.msg, xpr, " ".replicate(xpHead));
root = null;
return this;
}
if (num.length != 1) { // Should be one XP left.
"Parse Error...".writefln;
root = null;
} else {
root = num.pop;
}
return this;
} // End Parse.
} // End class AST.
// To display AST fancy struct.
void ins(ref char[][] s, in string v, in int p, in int l)
pure nothrow @safe {
if (l + 1 > s.length)
s.length++;
while (s[l].length < p + v.length + 1)
s[l] ~= " ";
s[l][p .. p + v.length] = v[];
}
final class CalcVis : Visitor {
int result, level;
string resultStr;
char[][] Tree;
static void opCall(AST a) @safe {
if (a && a.root) {
auto c = new CalcVis;
a.root.accept(c);
foreach (immutable i; 1 .. c.Tree.length) { // More fancy.
bool flipflop = false;
enum char mk = '.';
foreach (immutable j; 0 .. c.Tree[i].length) {
while (j >= c.Tree[i - 1].length)
c.Tree[i - 1] ~= " ";
immutable c1 = c.Tree[i][j];
immutable c2 = c.Tree[i - 1][j];
if (flipflop && (c1 == ' ') && c2 == ' ')
c.Tree[i - 1][j] = mk;
if (c1 != mk && c1 != ' ' &&
(j == 0 || !isDigit(c.Tree[i][j - 1])))
flipflop = !flipflop;
}
}
foreach (const t; c.Tree)
t.writefln;
writefln("\n%s ==>\n%s = %s", a.xpr, c.resultStr, c.result);
} else
"Evalute invalid or null Expression.".writefln;
}
// Calc. the value, display AST struct and eval order.
override void visit(XP xp) @safe {
ins(Tree, xp.str, xp.pos, level);
level++;
if (xp.type == Type.Num) {
resultStr ~= xp.str;
result = xp.str.to!int;
} else {
resultStr ~= "(";
xp.LHS.accept(this);
immutable int lhs = result;
resultStr ~= opChar[xp.type];
xp.RHS.accept(this);
resultStr ~= ")";
switch (xp.type) {
case Type.Add: result = lhs + result; break;
case Type.Sub: result = lhs - result; break;
case Type.Mul: result = lhs * result; break;
case Type.Div: result = lhs / result; break;
default: throw new Exception("Invalid type");
}
}
level--;
}
}
void main(string[] args) /*@safe*/ {
immutable exp0 = "1 + 2*(3 - 2*(3 - 2)*((2 - 4)*5" ~
" - 22/(7 + 2*(3 - 1)) - 1)) + 1";
immutable exp = (args.length > 1) ? args[1 .. $].join(' ') : exp0;
new AST().parse(exp).CalcVis; // Should be 60.
}
{{out}}
........................................................+.
.+.. 1
1 *...
2 .-..........
3 .......*................................
*... ....................-.
2 .-. ..-... 1
3 2 ...* /...
.-. 5 22 .+..
2 4 7 *...
2 .-.
3 1
1 + 2*(3 - 2*(3 - 2)*((2 - 4)*5 - 22/(7 + 2*(3 - 1)) - 1)) + 1 ==>
((1+(2*(3-((2*(3-2))*((((2-4)*5)-(22/(7+(2*(3-1)))))-1)))))+1) = 60
E
While the task requirements specify not evaluating using the language's built-in eval, they don't say that you have to write your own ''parser''...
def LiteralExpr := <elang:evm.makeLiteralExpr>.asType()
def arithEvaluate(expr :String) {
def ast := eParser(expr)
def evalAST(ast) {
return switch (ast) {
match e`@a + @b` { evalAST(a) + evalAST(b) }
match e`@a - @b` { evalAST(a) - evalAST(b) }
match e`@a * @b` { evalAST(a) * evalAST(b) }
match e`@a / @b` { evalAST(a) / evalAST(b) }
match e`-@a` { -(evalAST(a)) }
match l :LiteralExpr { l.getValue() }
}
}
return evalAST(ast)
}
Parentheses are handled by the parser.
? arithEvaluate("1 + 2")
# value: 3
? arithEvaluate("(1 + 2) * 10 / 100")
# value: 0.3
? arithEvaluate("(1 + 2 / 2) * (5 + 5)")
# value: 20.0
Elena
ELENA 4.x :
import system'routines;
import extensions;
import extensions'text;
class Token
{
object theValue;
rprop int Level;
constructor new(int level)
{
theValue := new StringWriter();
Level := level + 9;
}
append(ch)
{
theValue.write(ch)
}
Number = theValue.toReal();
}
class Node
{
prop object Left;
prop object Right;
rprop int Level;
constructor new(int level)
{
Level := level
}
}
class SummaryNode : Node
{
constructor new(int level)
<= new(level + 1);
Number = Left.Number + Right.Number;
}
class DifferenceNode : Node
{
constructor new(int level)
<= new(level + 1);
Number = Left.Number - Right.Number;
}
class ProductNode : Node
{
constructor new(int level)
<= new(level + 2);
Number = Left.Number * Right.Number;
}
class FractionNode : Node
{
constructor new(int level)
<= new(level + 2);
Number = Left.Number / Right.Number;
}
class Expression
{
rprop int Level;
prop object Top;
constructor new(int level)
{
Level := level
}
prop object Right
{
get() = Top;
set(object node)
{
Top := node
}
}
get Number() => Top;
}
singleton operatorState
{
eval(ch)
{
ch =>
$40 { // (
^ __target.newBracket().gotoStarting()
}
: {
^ __target.newToken().append(ch).gotoToken()
}
}
}
singleton tokenState
{
eval(ch)
{
ch =>
$41 { // )
^ __target.closeBracket().gotoToken()
}
$42 { // *
^ __target.newProduct().gotoOperator()
}
$43 { // +
^ __target.newSummary().gotoOperator()
}
$45 { // -
^ __target.newDifference().gotoOperator()
}
$47 { // /
^ __target.newFraction().gotoOperator()
}
: {
^ __target.append:ch
}
}
}
singleton startState
{
eval(ch)
{
ch =>
$40 { // (
^ __target.newBracket().gotoStarting()
}
$45 { // -
^ __target.newToken().append("0").newDifference().gotoOperator()
}
: {
^ __target.newToken().append:ch.gotoToken()
}
}
}
class Scope
{
object theState;
int theLevel;
object theParser;
object theToken;
object theExpression;
constructor new(parser)
{
theState := startState;
theLevel := 0;
theExpression := Expression.new(0);
theParser := parser
}
newToken()
{
theToken := theParser.appendToken(theExpression, theLevel)
}
newSummary()
{
theToken := nil;
theParser.appendSummary(theExpression, theLevel)
}
newDifference()
{
theToken := nil;
theParser.appendDifference(theExpression, theLevel)
}
newProduct()
{
theToken := nil;
theParser.appendProduct(theExpression, theLevel)
}
newFraction()
{
theToken := nil;
theParser.appendFraction(theExpression, theLevel)
}
newBracket()
{
theToken := nil;
theLevel := theLevel + 10;
theParser.appendSubexpression(theExpression, theLevel)
}
closeBracket()
{
if (theLevel < 10)
{ InvalidArgumentException.new:"Invalid expression".raise() };
theLevel := theLevel - 10
}
append(ch)
{
if(ch >= $48 && ch < $58)
{
theToken.append:ch
}
else
{
InvalidArgumentException.new:"Invalid expression".raise()
}
}
append(string s)
{
s.forEach:(ch){ self.append:ch }
}
gotoStarting()
{
theState := startState
}
gotoToken()
{
theState := tokenState
}
gotoOperator()
{
theState := operatorState
}
get Number() => theExpression;
dispatch() => theState;
}
class Parser
{
appendToken(object expression, int level)
{
var token := Token.new(level);
expression.Top := self.append(expression.Top, token);
^ token
}
appendSummary(object expression, int level)
{
expression.Top := self.append(expression.Top, SummaryNode.new(level))
}
appendDifference(object expression, int level)
{
expression.Top := self.append(expression.Top, DifferenceNode.new(level))
}
appendProduct(object expression, int level)
{
expression.Top := self.append(expression.Top, ProductNode.new(level))
}
appendFraction(object expression, int level)
{
expression.Top := self.append(expression.Top, FractionNode.new(level))
}
appendSubexpression(object expression, int level)
{
expression.Top := self.append(expression.Top, Expression.new(level))
}
append(object lastNode, object newNode)
{
if(nil == lastNode)
{ ^ newNode };
if (newNode.Level <= lastNode.Level)
{ newNode.Left := lastNode; ^ newNode };
var parent := lastNode;
var current := lastNode.Right;
while (nil != current && newNode.Level > current.Level)
{ parent := current; current := current.Right };
if (nil == current)
{
parent.Right := newNode
}
else
{
newNode.Left := current; parent.Right := newNode
};
^ lastNode
}
run(text)
{
var scope := Scope.new(self);
text.forEach:(ch){ scope.eval:ch };
^ scope.Number
}
}
public program()
{
var text := new StringWriter();
var parser := new Parser();
while (console.readLine().saveTo(text).Length > 0)
{
try
{
console.printLine("=",parser.run:text)
}
catch(Exception e)
{
console.writeLine(e.Printable)
//console.writeLine:"Invalid Expression"
};
text.clear()
}
}
Emacs Lisp
#!/usr/bin/env emacs --script
;; -*- mode: emacs-lisp; lexical-binding: t -*-
;;> ./arithmetic-evaluation '(1 + 2) * 3'
(defun advance ()
(let ((rtn (buffer-substring-no-properties (point) (match-end 0))))
(goto-char (match-end 0))
rtn))
(defvar current-symbol nil)
(defun next-symbol ()
(when (looking-at "[ \t\n]+")
(goto-char (match-end 0)))
(cond
((eobp)
(setq current-symbol 'eof))
((looking-at "[0-9]+")
(setq current-symbol (string-to-number (advance))))
((looking-at "[-+*/()]")
(setq current-symbol (advance)))
((looking-at ".")
(error "Unknown character '%s'" (advance)))))
(defun accept (sym)
(when (equal sym current-symbol)
(next-symbol)
t))
(defun expect (sym)
(unless (accept sym)
(error "Expected symbol %s, but found %s" sym current-symbol))
t)
(defun p-expression ()
" expression = term { ('+' | '-') term } . "
(let ((rtn (p-term)))
(while (or (equal current-symbol "+") (equal current-symbol "-"))
(let ((op current-symbol)
(left rtn))
(next-symbol)
(setq rtn (list op left (p-term)))))
rtn))
(defun p-term ()
" term = factor { ('*' | '/') factor } . "
(let ((rtn (p-factor)))
(while (or (equal current-symbol "*") (equal current-symbol "/"))
(let ((op current-symbol)
(left rtn))
(next-symbol)
(setq rtn (list op left (p-factor)))))
rtn))
(defun p-factor ()
" factor = constant | variable | '(' expression ')' . "
(let (rtn)
(cond
((numberp current-symbol)
(setq rtn current-symbol)
(next-symbol))
((accept "(")
(setq rtn (p-expression))
(expect ")"))
(t (error "Syntax error")))
rtn))
(defun ast-build (expression)
(let (rtn)
(with-temp-buffer
(insert expression)
(goto-char (point-min))
(next-symbol)
(setq rtn (p-expression))
(expect 'eof))
rtn))
(defun ast-eval (v)
(pcase v
((pred numberp) v)
(`("+" ,a ,b) (+ (ast-eval a) (ast-eval b)))
(`("-" ,a ,b) (- (ast-eval a) (ast-eval b)))
(`("*" ,a ,b) (* (ast-eval a) (ast-eval b)))
(`("/" ,a ,b) (/ (ast-eval a) (float (ast-eval b))))
(_ (error "Unknown value %s" v))))
(dolist (arg command-line-args-left)
(let ((ast (ast-build arg)))
(princ (format " ast = %s\n" ast))
(princ (format " value = %s\n" (ast-eval ast)))
(terpri)))
(setq command-line-args-left nil)
{{out}}
$ ./arithmetic-evaluation '(1 + 2) * 3'
ast = (* (+ 1 2) 3)
value = 9
$ ./arithmetic-evaluation '1 + 2 * (3 + (4 * 5 + 6 * 7 * 8) - 9) / 10'
ast = (+ 1 (/ (* 2 (- (+ 3 (+ (* 4 5) (* (* 6 7) 8))) 9)) 10))
value = 71.0
$ ./arithmetic-evaluation '1 + 2*(3 - 2*(3 - 2)*((2 - 4)*5 - 22/(7 + 2*(3 - 1)) - 1)) + 1'
ast = (+ (+ 1 (* 2 (- 3 (* (* 2 (- 3 2)) (- (- (* (- 2 4) 5) (/ 22 (+ 7 (* 2 (- 3 1))))) 1))))) 1)
value = 60.0
$ ./arithmetic-evaluation '(1 + 2) * 10 / 100'
ast = (/ (* (+ 1 2) 10) 100)
value = 0.3
ERRE
PROGRAM EVAL
!
! arithmetic expression evaluator
!
!$KEY
LABEL 98,100,110
DIM STACK$[50]
PROCEDURE DISEGNA_STACK
!$RCODE="LOCATE 3,1"
!$RCODE="COLOR 0,7"
PRINT(TAB(35);"S T A C K";TAB(79);)
!$RCODE="COLOR 7,0"
FOR TT=1 TO 38 DO
IF TT>=20 THEN
!$RCODE="LOCATE 3+TT-19,40"
ELSE
!$RCODE="LOCATE 3+TT,1"
END IF
IF TT=NS THEN PRINT(">";) ELSE PRINT(" ";) END IF
PRINT(RIGHT$(STR$(TT),2);"³ ";STACK$[TT];" ")
END FOR
REPEAT
GET(Z$)
UNTIL LEN(Z$)<>0
END PROCEDURE
PROCEDURE COMPATTA_STACK
IF NS>1 THEN
R=1
WHILE R<NS DO
IF INSTR(OP_LIST$,STACK$[R])=0 AND INSTR(OP_LIST$,STACK$[R+1])=0 THEN
FOR R1=R TO NS-1 DO
STACK$[R1]=STACK$[R1+1]
END FOR
NS=NS-1
END IF
R=R+1
END WHILE
END IF
DISEGNA_STACK
END PROCEDURE
PROCEDURE CALC_ARITM
L=NS1
WHILE L<=NS2 DO
IF STACK$[L]="^" THEN
IF L>=NS2 THEN GOTO 100 END IF
N1#=VAL(STACK$[L-1]) N2#=VAL(STACK$[L+1]) NOP=NOP-1
IF STACK$[L]="^" THEN
RI#=N1#^N2#
END IF
STACK$[L-1]=STR$(RI#)
N=L
WHILE N<=NS2-2 DO
STACK$[N]=STACK$[N+2]
N=N+1
END WHILE
NS2=NS2-2
L=NS1-1
END IF
L=L+1
END WHILE
L=NS1
WHILE L<=NS2 DO
IF STACK$[L]="*" OR STACK$[L]="/" THEN
IF L>=NS2 THEN GOTO 100 END IF
N1#=VAL(STACK$[L-1]) N2#=VAL(STACK$[L+1]) NOP=NOP-1
IF STACK$[L]="*" THEN RI#=N1#*N2# ELSE RI#=N1#/N2# END IF
STACK$[L-1]=STR$(RI#)
N=L
WHILE N<=NS2-2 DO
STACK$[N]=STACK$[N+2]
N=N+1
END WHILE
NS2=NS2-2
L=NS1-1
END IF
L=L+1
END WHILE
L=NS1
WHILE L<=NS2 DO
IF STACK$[L]="+" OR STACK$[L]="-" THEN
EXIT IF L>=NS2
N1#=VAL(STACK$[L-1]) N2#=VAL(STACK$[L+1]) NOP=NOP-1
IF STACK$[L]="+" THEN RI#=N1#+N2# ELSE RI#=N1#-N2# END IF
STACK$[L-1]=STR$(RI#)
N=L
WHILE N<=NS2-2 DO
STACK$[N]=STACK$[N+2]
N=N+1
END WHILE
NS2=NS2-2
L=NS1-1
END IF
L=L+1
END WHILE
100:
IF NOP<2 THEN ! operator priority
DB#=VAL(STACK$[NS1])
ELSE
IF NOP<3 THEN
DB#=VAL(STACK$[NS1+2])
ELSE
DB#=VAL(STACK$[NS1+4])
END IF
END IF
END PROCEDURE
PROCEDURE SVOLGI_PAR
NPA=NPA-1
FOR J=NS TO 1 STEP -1 DO
EXIT IF STACK$[J]="("
END FOR
IF J=0 THEN
NS1=1 NS2=NS CALC_ARITM
NERR=7
ELSE
FOR R=J TO NS-1 DO
STACK$[R]=STACK$[R+1]
END FOR
NS1=J NS2=NS-1 CALC_ARITM
IF NS1=2 THEN NS1=1 STACK$[1]=STACK$[2] END IF
NS=NS1
COMPATTA_STACK
END IF
END PROCEDURE
BEGIN
OP_LIST$="+-*/^("
NOP=0 NPA=0 NS=1 K$=""
STACK$[1]="@" ! init stack
PRINT(CHR$(12);)
INPUT(LINE,EXPRESSION$)
FOR W=1 TO LEN(EXPRESSION$) DO
LOOP
CODE=ASC(MID$(EXPRESSION$,W,1))
IF (CODE>=48 AND CODE<=57) OR CODE=46 THEN
K$=K$+CHR$(CODE)
W=W+1 IF W>LEN(EXPRESSION$) THEN GOTO 98 END IF
ELSE
EXIT IF K$=""
IF NS>1 OR (NS=1 AND STACK$[1]<>"@") THEN NS=NS+1 END IF
IF FLAG=0 THEN STACK$[NS]=K$ ELSE STACK$[NS]=STR$(VAL(K$)*FLAG) END IF
K$="" FLAG=0
EXIT
END IF
END LOOP
IF CODE=43 THEN K$="+" END IF
IF CODE=45 THEN K$="-" END IF
IF CODE=42 THEN K$="*" END IF
IF CODE=47 THEN K$="/" END IF
IF CODE=94 THEN K$="^" END IF
CASE CODE OF
43,45,42,47,94->
IF MID$(EXPRESSION$,W+1,1)="-" THEN FLAG=-1 W=W+1 END IF
IF INSTR(OP_LIST$,STACK$[NS])<>0 THEN
NERR=5
ELSE
NS=NS+1 STACK$[NS]=K$ NOP=NOP+1
IF NOP>=2 THEN
FOR J=NS TO 1 STEP -1 DO
IF STACK$[J]<>"(" THEN
CONTINUE FOR
END IF
IF J<NS-2 THEN
EXIT
ELSE
GOTO 110
END IF
END FOR
NS1=J+1 NS2=NS CALC_ARITM
NS=NS2 STACK$[NS]=K$
REGISTRO_X#=VAL(STACK$[NS-1])
END IF
END IF
110:
END ->
40->
IF NS>1 OR (NS=1 AND STACK$[1]<>"@") THEN NS=NS+1 END IF
STACK$[NS]="(" NPA=NPA+1
IF MID$(EXPRESSION$,W+1,1)="-" THEN FLAG=-1 W=W+1 END IF
END ->
41->
SVOLGI_PAR
IF NERR=7 THEN
NERR=0 NOP=0 NPA=0 NS=1
ELSE
IF NERR=0 OR NERR=1 THEN
DB#=VAL(STACK$[NS])
REGISTRO_X#=DB#
ELSE
NOP=0 NPA=0 NS=1
END IF
END IF
END ->
OTHERWISE
NERR=8
END CASE
K$=""
DISEGNA_STACK
END FOR
98:
IF K$<>"" THEN
IF NS>1 OR (NS=1 AND STACK$[1]<>"@") THEN NS=NS+1 END IF
IF FLAG=0 THEN STACK$[NS]=K$ ELSE STACK$[NS]=STR$(VAL(K$)*FLAG) END IF
END IF
DISEGNA_STACK
IF INSTR(OP_LIST$,STACK$[NS])<>0 THEN
NERR=6
ELSE
WHILE NPA<>0 DO
SVOLGI_PAR
END WHILE
IF NERR<>7 THEN NS1=1 NS2=NS CALC_ARITM END IF
END IF
NS=1 NOP=0 NPA=0
!$RCODE="LOCATE 23,1"
IF NERR>0 THEN PRINT("Internal Error #";NERR) ELSE PRINT("Value is ";DB#) END IF
END PROGRAM
This solution is based on a stack: as a plus there is a power (^) operator. Unary operator "-" is accepted. Program shows the stack after every operation and you must press a key to go on (this feature can be avoided by removing the final REPEAT..UNTIL loop at the end of "DISEGNA_STACK" procedure).
Factor
USING: accessors kernel locals math math.parser peg.ebnf ;
IN: rosetta.arith
TUPLE: operator left right ;
TUPLE: add < operator ; C: <add> add
TUPLE: sub < operator ; C: <sub> sub
TUPLE: mul < operator ; C: <mul> mul
TUPLE: div < operator ; C: <div> div
EBNF: expr-ast
spaces = [\n\t ]*
digit = [0-9]
number = (digit)+ => [[ string>number ]]
value = spaces number:n => [[ n ]]
| spaces "(" exp:e spaces ")" => [[ e ]]
fac = fac:a spaces "*" value:b => [[ a b <mul> ]]
| fac:a spaces "/" value:b => [[ a b <div> ]]
| value
exp = exp:a spaces "+" fac:b => [[ a b <add> ]]
| exp:a spaces "-" fac:b => [[ a b <sub> ]]
| fac
main = exp:e spaces !(.) => [[ e ]]
;EBNF
GENERIC: eval-ast ( ast -- result )
M: number eval-ast ;
: recursive-eval ( ast -- left-result right-result )
[ left>> eval-ast ] [ right>> eval-ast ] bi ;
M: add eval-ast recursive-eval + ;
M: sub eval-ast recursive-eval - ;
M: mul eval-ast recursive-eval * ;
M: div eval-ast recursive-eval / ;
: evaluate ( string -- result )
expr-ast eval-ast ;
=={{header|F_Sharp|F#}}== Using FsLex and FsYacc from the F# PowerPack, we implement this with multiple source files:
AbstractSyntaxTree.fs
:
module AbstractSyntaxTree
type Expression =
| Int of int
| Plus of Expression * Expression
| Minus of Expression * Expression
| Times of Expression * Expression
| Divide of Expression * Expression
Lexer.fsl
:
{
module Lexer
open Parser // we need the terminal tokens from the Parser
let lexeme = Lexing.LexBuffer<_>.LexemeString
}
let intNum = '-'? ['0'-'9']+
let whitespace = ' ' | '\t'
let newline = '\n' | '\r' '\n'
rule token = parse
| intNum { INT (lexeme lexbuf |> int) }
| '+' { PLUS }
| '-' { MINUS }
| '*' { TIMES }
| '/' { DIVIDE }
| '(' { LPAREN }
| ')' { RPAREN }
| whitespace { token lexbuf }
| newline { lexbuf.EndPos <- lexbuf.EndPos.NextLine; token lexbuf }
| eof { EOF }
| _ { failwithf "unrecognized input: '%s'" <| lexeme lexbuf }
Parser.fsy
:
%{
open AbstractSyntaxTree
%}
%start Expr
// terminal tokens
%token <int> INT
%token PLUS MINUS TIMES DIVIDE LPAREN RPAREN
%token EOF
// associativity and precedences
%left PLUS MINUS
%left TIMES DIVIDE
// return type of Expr
%type <Expression> Expr
%%
Expr: INT { Int $1 }
| Expr PLUS Expr { Plus ($1, $3) }
| Expr MINUS Expr { Minus ($1, $3) }
| Expr TIMES Expr { Times ($1, $3) }
| Expr DIVIDE Expr { Divide ($1, $3) }
| LPAREN Expr RPAREN { $2 }
Program.fs
:
open AbstractSyntaxTree
open Lexer
open Parser
let parse txt =
txt
|> Lexing.LexBuffer<_>.FromString
|> Parser.Expr Lexer.token
let rec eval = function
| Int i -> i
| Plus (a,b) -> eval a + eval b
| Minus (a,b) -> eval a - eval b
| Times (a,b) -> eval a * eval b
| Divide (a,b) -> eval a / eval b
do
"((11+15)*15)*2-(3)*4*1"
|> parse
|> eval
|> printfn "%d"
FreeBASIC
'Arithmetic evaluation
'
'Create a program which parses and evaluates arithmetic expressions.
'
'Requirements
'
' * An abstract-syntax tree (AST) for the expression must be created from parsing the
' input.
' * The AST must be used in evaluation, also, so the input may not be directly evaluated
' (e.g. by calling eval or a similar language feature.)
' * The expression will be a string or list of symbols like "(1+3)*7".
' * The four symbols + - * / must be supported as binary operators with conventional
' precedence rules.
' * Precedence-control parentheses must also be supported.
'
'Standard mathematical precedence should be followed:
'
' Parentheses
' Multiplication/Division (left to right)
' Addition/Subtraction (left to right)
'
' test cases:
' 2*-3--4+-0.25 : returns -2.25
' 1 + 2 * (3 + (4 * 5 + 6 * 7 * 8) - 9) / 10 : returns 71
enum
false = 0
true = -1
end enum
enum Symbol
unknown_sym
minus_sym
plus_sym
lparen_sym
rparen_sym
number_sym
mul_sym
div_sym
unary_minus_sym
unary_plus_sym
done_sym
eof_sym
end enum
type Tree
as Tree ptr leftp, rightp
op as Symbol
value as double
end type
dim shared sym as Symbol
dim shared tokenval as double
dim shared usr_input as string
declare function expr(byval p as integer) as Tree ptr
function isdigit(byval ch as string) as long
return ch <> "" and Asc(ch) >= Asc("0") and Asc(ch) <= Asc("9")
end function
sub error_msg(byval msg as string)
print msg
system
end sub
' tokenize the input string
sub getsym()
do
if usr_input = "" then
line input usr_input
usr_input += chr(10)
endif
dim as string ch = mid(usr_input, 1, 1) ' get the next char
usr_input = mid(usr_input, 2) ' remove it from input
sym = unknown_sym
select case ch
case " ": continue do
case chr(10), "": sym = done_sym: return
case "+": sym = plus_sym: return
case "-": sym = minus_sym: return
case "*": sym = mul_sym: return
case "/": sym = div_sym: return
case "(": sym = lparen_sym: return
case ")": sym = rparen_sym: return
case else
if isdigit(ch) then
dim s as string = ""
dim dot as integer = 0
do
s += ch
if ch = "." then dot += 1
ch = mid(usr_input, 1, 1) ' get the next char
usr_input = mid(usr_input, 2) ' remove it from input
loop while isdigit(ch) orelse ch = "."
if ch = "." or dot > 1 then error_msg("bogus number")
usr_input = ch + usr_input ' prepend the char to input
tokenval = val(s)
sym = number_sym
end if
return
end select
loop
end sub
function make_node(byval op as Symbol, byval leftp as Tree ptr, byval rightp as Tree ptr) as Tree ptr
dim t as Tree ptr
t = callocate(len(Tree))
t->op = op
t->leftp = leftp
t->rightp = rightp
return t
end function
function is_binary(byval op as Symbol) as integer
select case op
case mul_sym, div_sym, plus_sym, minus_sym: return true
case else: return false
end select
end function
function prec(byval op as Symbol) as integer
select case op
case unary_minus_sym, unary_plus_sym: return 100
case mul_sym, div_sym: return 90
case plus_sym, minus_sym: return 80
case else: return 0
end select
end function
function primary as Tree ptr
dim t as Tree ptr = 0
select case sym
case minus_sym, plus_sym
dim op as Symbol = sym
getsym()
t = expr(prec(unary_minus_sym))
if op = minus_sym then return make_node(unary_minus_sym, t, 0)
if op = plus_sym then return make_node(unary_plus_sym, t, 0)
case lparen_sym
getsym()
t = expr(0)
if sym <> rparen_sym then error_msg("expecting rparen")
getsym()
return t
case number_sym
t = make_node(sym, 0, 0)
t->value = tokenval
getsym()
return t
case else: error_msg("expecting a primary")
end select
end function
function expr(byval p as integer) as Tree ptr
dim t as Tree ptr = primary()
while is_binary(sym) andalso prec(sym) >= p
dim t1 as Tree ptr
dim op as Symbol = sym
getsym()
t1 = expr(prec(op) + 1)
t = make_node(op, t, t1)
wend
return t
end function
function eval(byval t as Tree ptr) as double
if t <> 0 then
select case t->op
case minus_sym: return eval(t->leftp) - eval(t->rightp)
case plus_sym: return eval(t->leftp) + eval(t->rightp)
case mul_sym: return eval(t->leftp) * eval(t->rightp)
case div_sym: return eval(t->leftp) / eval(t->rightp)
case unary_minus_sym: return -eval(t->leftp)
case unary_plus_sym: return eval(t->leftp)
case number_sym: return t->value
case else: error_msg("unexpected tree node")
end select
end if
return 0
end function
do
getsym()
if sym = eof_sym then exit do
if sym = done_sym then continue do
dim t as Tree ptr = expr(0)
print"> "; eval(t)
if sym = eof_sym then exit do
if sym <> done_sym then error_msg("unexpected input")
loop
{{out}}
>calc
1 + 2 * (3 + (4 * 5 + 6 * 7 * 8) - 9) / 10
> 71
Go
See [[Arithmetic Evaluator/Go]]
Groovy
Solution:
enum Op {
ADD('+', 2),
SUBTRACT('-', 2),
MULTIPLY('*', 1),
DIVIDE('/', 1);
static {
ADD.operation = { a, b -> a + b }
SUBTRACT.operation = { a, b -> a - b }
MULTIPLY.operation = { a, b -> a * b }
DIVIDE.operation = { a, b -> a / b }
}
final String symbol
final int precedence
Closure operation
private Op(String symbol, int precedence) {
this.symbol = symbol
this.precedence = precedence
}
String toString() { symbol }
static Op fromSymbol(String symbol) {
Op.values().find { it.symbol == symbol }
}
}
interface Expression {
Number evaluate();
}
class Constant implements Expression {
Number value
Constant (Number value) { this.value = value }
Constant (String str) {
try { this.value = str as BigInteger }
catch (e) { this.value = str as BigDecimal }
}
Number evaluate() { value }
String toString() { "${value}" }
}
class Term implements Expression {
Op op
Expression left, right
Number evaluate() { op.operation(left.evaluate(), right.evaluate()) }
String toString() { "(${op} ${left} ${right})" }
}
void fail(String msg, Closure cond = {true}) {
if (cond()) throw new IllegalArgumentException("Cannot parse expression: ${msg}")
}
Expression parse(String expr) {
def tokens = tokenize(expr)
def elements = groupByParens(tokens, 0)
parse(elements)
}
List tokenize(String expr) {
def tokens = []
def constStr = ""
def captureConstant = { i ->
if (constStr) {
try { tokens << new Constant(constStr) }
catch (NumberFormatException e) { fail "Invalid constant '${constStr}' near position ${i}" }
constStr = ''
}
}
for(def i = 0; i<expr.size(); i++) {
def c = expr[i]
def constSign = c in ['+','-'] && constStr.empty && (tokens.empty || tokens[-1] != ')')
def isConstChar = { it in ['.'] + ('0'..'9') || constSign }
if (c in ([')'] + Op.values()*.symbol) && !constSign) { captureConstant(i) }
switch (c) {
case ~/\s/: break
case isConstChar: constStr += c; break
case Op.values()*.symbol: tokens << Op.fromSymbol(c); break
case ['(',')']: tokens << c; break
default: fail "Invalid character '${c}' at position ${i+1}"
}
}
captureConstant(expr.size())
tokens
}
List groupByParens(List tokens, int depth) {
def deepness = depth
def tokenGroups = []
for (def i = 0; i < tokens.size(); i++) {
def token = tokens[i]
switch (token) {
case '(':
fail("'(' too close to end of expression") { i+2 > tokens.size() }
def subGroup = groupByParens(tokens[i+1..-1], depth+1)
tokenGroups << subGroup[0..-2]
i += subGroup[-1] + 1
break
case ')':
fail("Unbalanced parens, found extra ')'") { deepness == 0 }
tokenGroups << i
return tokenGroups
default:
tokenGroups << token
}
}
fail("Unbalanced parens, unclosed groupings at end of expression") { deepness != 0 }
def n = tokenGroups.size()
fail("The operand/operator sequence is wrong") { n%2 == 0 }
(0..<n).each {
def i = it
fail("The operand/operator sequence is wrong") { (i%2 == 0) == (tokenGroups[i] instanceof Op) }
}
tokenGroups
}
Expression parse(List elements) {
while (elements.size() > 1) {
def n = elements.size()
fail ("The operand/operator sequence is wrong") { n%2 == 0 }
def groupLoc = (0..<n).find { i -> elements[i] instanceof List }
if (groupLoc != null) {
elements[groupLoc] = parse(elements[groupLoc])
continue
}
def opLoc = (0..<n).find { i -> elements[i] instanceof Op && elements[i].precedence == 1 } \
?: (0..<n).find { i -> elements[i] instanceof Op && elements[i].precedence == 2 }
if (opLoc != null) {
fail ("Operator out of sequence") { opLoc%2 == 0 }
def term = new Term(left:elements[opLoc-1], op:elements[opLoc], right:elements[opLoc+1])
elements[(opLoc-1)..(opLoc+1)] = [term]
continue
}
}
return elements[0] instanceof List ? parse(elements[0]) : elements[0]
}
Test:
def testParse = {
def ex = parse(it)
print """
Input: ${it}
AST: ${ex}
value: ${ex.evaluate()}
"""
}
testParse('1+1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+1/15)/14)/13)/12)/11)/10)/9)/8)/7)/6)/5)/4)/3)/2')
assert (parse('1+1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+1/15)/14)/13)/12)/11)/10)/9)/8)/7)/6)/5)/4)/3)/2')
.evaluate() - Math.E).abs() < 0.0000000000001
testParse('1 + 2*(3 - 2*(3 - 2)*((2 - 4)*5 - 22/(7 + 2*(3 - 1)) - 1)) + 1')
testParse('1 - 5 * 2 / 20 + 1')
testParse('(1 - 5) * 2 / (20 + 1)')
testParse('2 * (3 + ((5) / (7 - 11)))')
testParse('(2 + 3) / (10 - 5)')
testParse('(1 + 2) * 10 / 100')
testParse('(1 + 2 / 2) * (5 + 5)')
testParse('2*-3--4+-.25')
testParse('2*(-3)-(-4)+(-.25)')
testParse('((11+15)*15)*2-(3)*4*1')
testParse('((11+15)*15)* 2 + (3) * -4 *1')
testParse('(((((1)))))')
testParse('-35')
println()
try { testParse('((11+15)*1') } catch (e) { println e }
try { testParse('((11+15)*1)))') } catch (e) { println e }
try { testParse('((11+15)*x)') } catch (e) { println e }
try { testParse('+++++') } catch (e) { println e }
try { testParse('1 /') } catch (e) { println e }
try { testParse('1++') } catch (e) { println e }
try { testParse('*1') } catch (e) { println e }
try { testParse('/ 1 /') } catch (e) { println e }
{{out}}
Input: 1+1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+1/15)/14)/13)/12)/11)/10)/9)/8)/7)/6)/5)/4)/3)/2 AST: (+ (+ 1 1) (/ (+ 1 (/ (+ 1 (/ (+ 1 (/ (+ 1 (/ (+ 1 (/ (+ 1 (/ (+ 1 (/ (+ 1 (/ (+ 1 (/ (+ 1 (/ (+ 1 (/ (+ 1 (/ (+ 1 (/ 1 15)) 14)) 13)) 12)) 11)) 10)) 9)) 8)) 7)) 6)) 5)) 4)) 3)) 2)) value: 2.7182818284589946 Input: 1 + 2*(3 - 2*(3 - 2)*((2 - 4)*5 - 22/(7 + 2*(3 - 1)) - 1)) + 1 AST: (+ (+ 1 (* 2 (- 3 (* (* 2 (- 3 2)) (- (- (* (- 2 4) 5) (/ 22 (+ 7 (* 2 (- 3 1))))) 1))))) 1) value: 60 Input: 1 - 5 * 2 / 20 + 1 AST: (+ (- 1 (/ (* 5 2) 20)) 1) value: 1.5 Input: (1 - 5) * 2 / (20 + 1) AST: (/ (* (- 1 5) 2) (+ 20 1)) value: -0.3809523810 Input: 2 * (3 + ((5) / (7 - 11))) AST: (* 2 (+ 3 (/ 5 (- 7 11)))) value: 3.50 Input: (2 + 3) / (10 - 5) AST: (/ (+ 2 3) (- 10 5)) value: 1 Input: (1 + 2) * 10 / 100 AST: (/ (* (+ 1 2) 10) 100) value: 0.3 Input: (1 + 2 / 2) * (5 + 5) AST: (* (+ 1 (/ 2 2)) (+ 5 5)) value: 20 Input: 2*-3--4+-.25 AST: (+ (- (* 2 -3) -4) -0.25) value: -2.25 Input: 2*(-3)-(-4)+(-.25) AST: (+ (- (* 2 -3) -4) -0.25) value: -2.25 Input: ((11+15)*15)*2-(3)*4*1 AST: (- (* (* (+ 11 15) 15) 2) (* (* 3 4) 1)) value: 768 Input: ((11+15)*15)* 2 + (3) * -4 *1 AST: (+ (* (* (+ 11 15) 15) 2) (* (* 3 -4) 1)) value: 768 Input: (((((1))))) AST: 1 value: 1 Input: -35 AST: -35 value: -35 java.lang.IllegalArgumentException: Cannot parse expression: Unbalanced parens, unclosed groupings at end of expression java.lang.IllegalArgumentException: Cannot parse expression: Unbalanced parens, found extra ')' java.lang.IllegalArgumentException: Cannot parse expression: Invalid character 'x' at position 10 java.lang.IllegalArgumentException: Cannot parse expression: Invalid constant '+' near position 1 java.lang.IllegalArgumentException: Cannot parse expression: The operand/operator sequence is wrong java.lang.IllegalArgumentException: Cannot parse expression: Invalid constant '+' near position 3 java.lang.IllegalArgumentException: Cannot parse expression: The operand/operator sequence is wrong java.lang.IllegalArgumentException: Cannot parse expression: The operand/operator sequence is wrong ``` ## Haskell ```haskell {-# LANGUAGE FlexibleContexts #-} import Text.Parsec import Text.Parsec.Expr import Text.Parsec.Combinator import Data.Functor import Data.Function (on) data Exp = Num Int | Add Exp Exp | Sub Exp Exp | Mul Exp Exp | Div Exp Exp expr :: Stream s m Char => ParsecT s u m Exp expr = buildExpressionParser table factor where table = [ [op "*" Mul AssocLeft, op "/" Div AssocLeft] , [op "+" Add AssocLeft, op "-" Sub AssocLeft] ] op s f = Infix (f <$ string s) factor = (between `on` char) '(' ')' expr <|> (Num . read <$> many1 digit) eval :: Integral a => Exp -> a eval (Num x) = fromIntegral x eval (Add a b) = eval a + eval b eval (Sub a b) = eval a - eval b eval (Mul a b) = eval a * eval b eval (Div a b) = eval a `div` eval b solution :: Integral a => String -> a solution = either (const (error "Did not parse")) eval . parse expr "" main :: IO () main = print $ solution "(1+3)*7" ``` {{Out}} ```txt 28 ``` =={{header|Icon}} and {{header|Unicon}}== A compact recursive descent parser using Hanson's device. This program * handles left and right associativity and different precedences * is ready to handle any number of infix operators without adding more functions to handle the precedences * accepts integers, reals, and radix constants (e.g. 3r10 is 3 in base 3) * currently accepts the Icon operators + - * / % (remainder) and ^ (exponentiation) and unary operators + and - * string invocation is used to evaluate binary operators hence other Icon binary operators (including handle multiple character ones) can be easily added * uses Icon style type coercion on operands * represents the AST as a nested list eliminating unneeded parenthesis * Notice that the code looks remarkably like a typical grammar, rather than being an opaque cryptic solution * Does not rely on any library to silently solve 1/2 the problem; in fact, this code would probably suit being put into a library almost verbatim ```Icon procedure main() #: simple arithmetical parser / evaluator write("Usage: Input expression = Abstract Syntax Tree = Value, ^Z to end.") repeat { writes("Input expression : ") if not writes(line := read()) then break if map(line) ? { (x := E()) & pos(0) } then write(" = ", showAST(x), " = ", evalAST(x)) else write(" rejected") } end procedure evalAST(X) #: return the evaluated AST local x if type(X) == "list" then { x := evalAST(get(X)) while x := get(X)(x, evalAST(get(X) | stop("Malformed AST."))) } return \x | X end procedure showAST(X) #: return a string representing the AST local x,s s := "" every x := !X do s ||:= if type(x) == "list" then "(" || showAST(x) || ")" else x return s end ######## # When you're writing a big parser, a few utility recognisers are very useful # procedure ws() # skip optional whitespace suspend tab(many(' \t')) | "" end procedure digits() suspend tab(many(&digits)) end procedure radixNum(r) # r sets the radix static chars initial chars := &digits || &lcase suspend tab(many(chars[1 +: r])) end ######## global token record HansonsDevice(precedence,associativity) procedure opinfo() static O initial { O := HansonsDevice([], table(&null)) # parsing table put(O.precedence, ["+", "-"], ["*", "/", "%"], ["^"]) # Lowest to Highest precedence every O.associativity[!!O.precedence] := 1 # default to 1 for LEFT associativity O.associativity["^"] := 0 # RIGHT associativity } return O end procedure E(k) #: Expression local lex, pL static opT initial opT := opinfo() /k := 1 lex := [] if not (pL := opT.precedence[k]) then # this op at this level? put(lex, F()) else { put(lex, E(k + 1)) while ws() & put(lex, token := =!pL) do put(lex, E(k + opT.associativity[token])) } suspend if *lex = 1 then lex[1] else lex # strip useless [] end procedure F() #: Factor suspend ws() & ( # skip optional whitespace, and ... (="+" & F()) | # unary + and a Factor, or ... (="-" || V()) | # unary - and a Value, or ... (="-" & [-1, "*", F()]) | # unary - and a Factor, or ... 2(="(", E(), ws(), =")") | # parenthesized subexpression, or ... V() # just a value ) end procedure V() #: Value local r suspend ws() & numeric( # skip optional whitespace, and ... =(r := 1 to 36) || ="r" || radixNum(r) | # N-based number, or ... digits() || (="." || digits() | "") || exponent() # plain number with optional fraction ) end procedure exponent() suspend tab(any('eE')) || =("+" | "-" | "") || digits() | "" end ``` {{out|Sample Output}} ```txt #matheval.exe Usage: Input expression = Abstract Syntax Tree = Value, ^Z to end. Input expression : 1 1 = 1 = 1 Input expression : -1 -1 = -1 = -1 Input expression : (-15/2.0) (-15/2.0) = -15/2.0 = -7.5 Input expression : -(15/2.0) -(15/2.0) = -1*(15/2.0) = -7.5 Input expression : 2+(3-4)*6/5^2^3%3 2+(3-4)*6/5^2^3%3 = 2+((3-4)*6/(5^(2^3))%3) = 2 Input expression : 1+2+3+4 1+2+3+4 = 1+2+3+4 = 10 Input expression : ((((2))))+3*5 ((((2))))+3*5 = 2+(3*5) = 17 Input expression : 3r10*3 3r10*3 = 3r10*3 = 9 Input expression : ^Z ``` ## J Note that once you get beyond a few basic arithmetic operations, what we commonly call "mathematical precedence" stops making sense, and primary value for this kind of precedence has been that it allows polynomials to be expressed simply (but expressing polynomials as a sequence of coefficients, one for each exponent, is even simpler). Nevertheless, this task deals only with simple arithmetic, so this kind of precedence is an arguably appropriate choice for this task. The implementation here uses a shift/reduce parser to build a tree structure (which J happens to support) for evaluation: ```j parse=:parse_parser_ eval=:monad define 'gerund structure'=:y gerund@.structure ) coclass 'parser' classify=: '$()*/+-'&(((>:@#@[ # 2:) #: 2 ^ i.)&;:) rules=: '' patterns=: ,"0 assert 1 addrule=: dyad define rules=: rules,;:x patterns=: patterns,+./@classify"1 y ) 'Term' addrule '$()', '0', '+-',: '0' 'Factor' addrule '$()+-', '0', '*/',: '0' 'Parens' addrule '(', '*/+-0', ')',: ')*/+-0$' rules=: rules,;:'Move' buildTree=: monad define words=: ;:'$',y queue=: classify '$',y stack=: classify '$$$$' tokens=: ]&.>i.#words tree=: '' while.(#queue)+.6<#stack do. rule=: rules {~ i.&1 patterns (*./"1)@:(+./"1) .(*."1)4{.stack rule`:6'' end. 'syntax' assert 1 0 1 1 1 1 -: {:"1 stack gerund=: literal&.> (<,'%') (I. words=<,'/')} words gerund;1{tree ) literal=:monad define ::] ".'t=.',y 5!:1<'t' ) Term=: Factor=: monad define stack=: ({.stack),(classify '0'),4}.stack tree=: ({.tree),(<1 2 3{tree),4}.tree ) Parens=: monad define stack=: (1{stack),3}.stack tree=: (1{tree),3}.tree ) Move=: monad define 'syntax' assert 0<#queue stack=: ({:queue),stack queue=: }:queue tree=: ({:tokens),tree tokens=: }:tokens ) parse=:monad define tmp=: conew 'parser' r=: buildTree__tmp y coerase tmp r ) ``` example use: ```j eval parse '1+2*3/(4-5+6)' 2.2 ``` You can also display the syntax tree, for example: ```j parse '2*3/(4-5)' ┌─────────────────────────────────────────────────────┬───────────────────┐ │┌───┬───────┬───┬───────┬───┬─┬───────┬───┬───────┬─┐│┌───────┬─┬───────┐│ ││┌─┐│┌─────┐│┌─┐│┌─────┐│┌─┐│(│┌─────┐│┌─┐│┌─────┐│)│││┌─┬─┬─┐│4│┌─┬─┬─┐││ │││$│││┌─┬─┐│││*│││┌─┬─┐│││%││ ││┌─┬─┐│││-│││┌─┬─┐││ ││││1│2│3││ ││6│7│8│││ ││└─┘│││0│2│││└─┘│││0│3│││└─┘│ │││0│4│││└─┘│││0│5│││ │││└─┴─┴─┘│ │└─┴─┴─┘││ ││ ││└─┴─┘││ ││└─┴─┘││ │ ││└─┴─┘││ ││└─┴─┘││ ││└───────┴─┴───────┘│ ││ │└─────┘│ │└─────┘│ │ │└─────┘│ │└─────┘│ ││ │ │└───┴───────┴───┴───────┴───┴─┴───────┴───┴───────┴─┘│ │ └─────────────────────────────────────────────────────┴───────────────────┘ ``` At the top level, the first box is a list of terminals, and the second box represents their parsed structure within the source sentence, with numbers indexing the respective terminals. Within the list of terminals - each terminal is contained with a box. Punctuation is simply the punctuation string (left or right parenthesis). Operators are strings inside of boxes (the leading $ "operator" in this example is not really an operator - it's just a placeholder that was used to help in the parsing). Numeric values are a box inside of a box where the inner box carries two further boxes. The first indicates data type ('0' for numbers) and the second carries the value. ## Java Uses the [[Arithmetic/Rational/Java|BigRational class]] to handle arbitrary-precision numbers (rational numbers since basic arithmetic will result in rational values). ```java import java.util.Stack; public class ArithmeticEvaluation { public interface Expression { BigRational eval(); } public enum Parentheses {LEFT} public enum BinaryOperator { ADD('+', 1), SUB('-', 1), MUL('*', 2), DIV('/', 2); public final char symbol; public final int precedence; BinaryOperator(char symbol, int precedence) { this.symbol = symbol; this.precedence = precedence; } public BigRational eval(BigRational leftValue, BigRational rightValue) { switch (this) { case ADD: return leftValue.add(rightValue); case SUB: return leftValue.subtract(rightValue); case MUL: return leftValue.multiply(rightValue); case DIV: return leftValue.divide(rightValue); } throw new IllegalStateException(); } public static BinaryOperator forSymbol(char symbol) { for (BinaryOperator operator : values()) { if (operator.symbol == symbol) { return operator; } } throw new IllegalArgumentException(String.valueOf(symbol)); } } public static class Number implements Expression { private final BigRational number; public Number(BigRational number) { this.number = number; } @Override public BigRational eval() { return number; } @Override public String toString() { return number.toString(); } } public static class BinaryExpression implements Expression { public final Expression leftOperand; public final BinaryOperator operator; public final Expression rightOperand; public BinaryExpression(Expression leftOperand, BinaryOperator operator, Expression rightOperand) { this.leftOperand = leftOperand; this.operator = operator; this.rightOperand = rightOperand; } @Override public BigRational eval() { BigRational leftValue = leftOperand.eval(); BigRational rightValue = rightOperand.eval(); return operator.eval(leftValue, rightValue); } @Override public String toString() { return "(" + leftOperand + " " + operator.symbol + " " + rightOperand + ")"; } } private static void createNewOperand(BinaryOperator operator, Stackoperands) { Expression rightOperand = operands.pop(); Expression leftOperand = operands.pop(); operands.push(new BinaryExpression(leftOperand, operator, rightOperand)); } public static Expression parse(String input) { int curIndex = 0; boolean afterOperand = false; Stack operands = new Stack<>(); Stack