Add all the A tasks

This commit is contained in:
Ingy döt Net 2013-04-10 14:58:50 -07:00
parent 2dd7375f96
commit 051504d65b
1608 changed files with 18584 additions and 0 deletions

View file

@ -0,0 +1,20 @@
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]].

View file

@ -0,0 +1,3 @@
---
category:
- Recursion

View file

@ -0,0 +1,142 @@
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"))
)

View file

@ -0,0 +1,150 @@
/*
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

View file

@ -0,0 +1,67 @@
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
}

View file

@ -0,0 +1,53 @@
(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

View file

@ -0,0 +1,29 @@
import Text.ParserCombinators.Parsec
import Text.ParserCombinators.Parsec.Expr
data Exp = Num Int
| Add Exp Exp
| Sub Exp Exp
| Mul Exp Exp
| Div Exp Exp
expr = buildExpressionParser table factor
table = [[op "*" (Mul) AssocLeft, op "/" (Div) AssocLeft]
,[op "+" (Add) AssocLeft, op "-" (Sub) AssocLeft]]
where op s f assoc = Infix (do string s; return f) assoc
factor = do char '(' ; x <- expr ; char ')'
return x
<|> do ds <- many1 digit
return $ Num (read ds)
evaluate (Num x) = fromIntegral x
evaluate (Add a b) = (evaluate a) + (evaluate b)
evaluate (Sub a b) = (evaluate a) - (evaluate b)
evaluate (Mul a b) = (evaluate a) * (evaluate b)
evaluate (Div a b) = (evaluate a) `div` (evaluate b)
solution exp = case parse expr [] exp of
Right expr -> evaluate expr
Left _ -> error "Did not parse"

View file

@ -0,0 +1,138 @@
import java.util.Stack;
public class ArithmeticEvaluation
{
public static enum Parentheses { LEFT, RIGHT }
public static enum BinaryOperator
{
ADD('+', 1) {
public BigRational eval(BigRational leftValue, BigRational rightValue) { return leftValue.add(rightValue); }
},
SUB('-', 1) {
public BigRational eval(BigRational leftValue, BigRational rightValue) { return leftValue.subtract(rightValue); }
},
MUL('*', 2) {
public BigRational eval(BigRational leftValue, BigRational rightValue) { return leftValue.multiply(rightValue); }
},
DIV('/', 2) {
public BigRational eval(BigRational leftValue, BigRational rightValue) { return leftValue.divide(rightValue); }
};
public final char symbol;
public final int precedence;
BinaryOperator(char symbol, int precedence)
{
this.symbol = symbol;
this.precedence = precedence;
}
public abstract BigRational eval(BigRational leftValue, BigRational rightValue);
}
public static class BinaryExpression
{
public Object leftOperand = null;
public BinaryOperator operator = null;
public Object rightOperand = null;
public BinaryExpression(Object leftOperand, BinaryOperator operator, Object rightOperand)
{
this.leftOperand = leftOperand;
this.operator = operator;
this.rightOperand = rightOperand;
}
public BigRational eval()
{
BigRational leftValue = (leftOperand instanceof BinaryExpression) ? ((BinaryExpression)leftOperand).eval() : (BigRational)leftOperand;
BigRational rightValue = (rightOperand instanceof BinaryExpression) ? ((BinaryExpression)rightOperand).eval() : (BigRational)rightOperand;
return operator.eval(leftValue, rightValue);
}
public String toString()
{ return "(" + leftOperand + " " + operator.symbol + " " + rightOperand + ")"; }
}
public static void createNewOperand(BinaryOperator operator, Stack<Object> operands)
{
Object rightOperand = operands.pop();
operands.push(new BinaryExpression(operands.pop(), operator, rightOperand));
return;
}
public static Object createExpression(String inputString)
{
int curIndex = 0;
boolean afterOperand = false;
Stack<Object> operands = new Stack<Object>();
Stack<Object> operators = new Stack<Object>();
inputStringLoop:
while (curIndex < inputString.length())
{
int startIndex = curIndex;
char c = inputString.charAt(curIndex++);
if (Character.isWhitespace(c))
continue;
if (afterOperand)
{
if (c == ')')
{
Object operator = null;
while (!operators.isEmpty() && ((operator = operators.pop()) != Parentheses.LEFT))
createNewOperand((BinaryOperator)operator, operands);
continue;
}
afterOperand = false;
for (BinaryOperator operator : BinaryOperator.values())
{
if (c == operator.symbol)
{
while (!operators.isEmpty() && (operators.peek() != Parentheses.LEFT) && (((BinaryOperator)operators.peek()).precedence >= operator.precedence))
createNewOperand((BinaryOperator)operators.pop(), operands);
operators.push(operator);
continue inputStringLoop;
}
}
throw new IllegalArgumentException();
}
if (c == '(')
{
operators.push(Parentheses.LEFT);
continue;
}
afterOperand = true;
while (curIndex < inputString.length())
{
c = inputString.charAt(curIndex);
if (((c < '0') || (c > '9')) && (c != '.'))
break;
curIndex++;
}
operands.push(BigRational.valueOf(inputString.substring(startIndex, curIndex)));
}
while (!operators.isEmpty())
{
Object operator = operators.pop();
if (operator == Parentheses.LEFT)
throw new IllegalArgumentException();
createNewOperand((BinaryOperator)operator, operands);
}
Object expression = operands.pop();
if (!operands.isEmpty())
throw new IllegalArgumentException();
return expression;
}
public static void main(String[] args)
{
String[] testExpressions = { "2+3", "2+3/4", "2*3-4", "2*(3+4)+5/6", "2 * (3 + (4 * 5 + (6 * 7) * 8) - 9) * 10", "2*-3--4+-.25" };
for (String testExpression : testExpressions)
{
Object expression = createExpression(testExpression);
System.out.println("Input: \"" + testExpression + "\", AST: \"" + expression + "\", eval=" + (expression instanceof BinaryExpression ? ((BinaryExpression)expression).eval() : expression));
}
}
}

View file

@ -0,0 +1,55 @@
function evalArithmeticExp(s) {
s = s.replace(/\s/g,'').replace(/^\+/,'');
var rePara = /\([^\(\)]*\)/;
var exp = s.match(rePara);
while (exp = s.match(rePara)) {
s = s.replace(exp[0], evalExp(exp[0]));
}
return evalExp(s);
function evalExp(s) {
s = s.replace(/[\(\)]/g,'');
var reMD = /\d+\.?\d*\s*[\*\/]\s*[+-]?\d+\.?\d*/;
var reM = /\*/;
var reAS = /-?\d+\.?\d*\s*[\+-]\s*[+-]?\d+\.?\d*/;
var reA = /\d\+/;
var exp;
while (exp = s.match(reMD)) {
s = exp[0].match(reM)? s.replace(exp[0], multiply(exp[0])) : s.replace(exp[0], divide(exp[0]));
}
while (exp = s.match(reAS)) {
s = exp[0].match(reA)? s.replace(exp[0], add(exp[0])) : s.replace(exp[0], subtract(exp[0]));
}
return '' + s;
function multiply(s, b) {
b = s.split('*');
return b[0] * b[1];
}
function divide(s, b) {
b = s.split('/');
return b[0] / b[1];
}
function add(s, b) {
s = s.replace(/^\+/,'').replace(/\++/,'+');
b = s.split('+');
return Number(b[0]) + Number(b[1]);
}
function subtract(s, b) {
s = s.replace(/\+-|-\+/g,'-');
if (s.match(/--/)) {
return add(s.replace(/--/,'+'));
}
b = s.split('-');
return b.length == 3? -1 * b[1] - b[2] : b[0] - b[1];
}
}
}

View file

@ -0,0 +1,40 @@
require"lpeg"
P, R, C, S, V = lpeg.P, lpeg.R, lpeg.C, lpeg.S, lpeg.V
--matches arithmetic expressions and returns a syntax tree
expression = P{"expr";
ws = P" "^0,
number = C(R"09"^1) * V"ws",
lp = "(" * V"ws",
rp = ")" * V"ws",
sym = C(S"+-*/") * V"ws",
more = (V"sym" * V"expr")^0,
expr = V"number" * V"more" + V"lp" * lpeg.Ct(V"expr" * V"more") * V"rp" * V"more"}
--evaluates a tree
function eval(expr)
--empty
if type(expr) == "string" or type(expr) == "number" then return expr + 0 end
--arithmetic functions
tb = {["+"] = function(a,b) return eval(a) + eval(b) end,
["-"] = function(a,b) return eval(a) - eval(b) end,
["*"] = function(a,b) return eval(a) * eval(b) end,
["/"] = function(a,b) return eval(a) / eval(b) end}
--you could add ^ or other operators to this pretty easily
for i, v in ipairs{"*/", "+-"} do
for s, u in ipairs(expr) do
local k = type(u) == "string" and C(S(v)):match(u)
if k then
expr[s-1] = tb[k](expr[s-1],expr[s+1])
table.remove(expr, s)
table.remove(expr, s)
end
end
end
return expr[1]
end
print(eval{expression:match(io.read())})

View file

@ -0,0 +1,61 @@
sub ev
# Evaluates an arithmetic expression like "(1+3)*7" and returns
# its value.
{my $exp = shift;
# Delete all meaningless characters. (Scientific notation,
# infinity, and not-a-number aren't supported.)
$exp =~ tr {0-9.+-/*()} {}cd;
return ev_ast(astize($exp));}
{my $balanced_paren_regex;
$balanced_paren_regex = qr
{\( ( [^()]+ | (??{$balanced_paren_regex}) )+ \)}x;
# ??{ ... } interpolates lazily (only when necessary),
# permitting recursion to arbitrary depths.
sub astize
# Constructs an abstract syntax tree by recursively
# transforming textual arithmetic expressions into array
# references of the form [operator, left oprand, right oprand].
{my $exp = shift;
# If $exp is just a number, return it as-is.
$exp =~ /[^0-9.]/ or return $exp;
# If parentheses surround the entire expression, get rid of
# them.
$exp = substr($exp, 1, -1)
while $exp =~ /\A($balanced_paren_regex)\z/;
# Replace stuff in parentheses with placeholders.
my @paren_contents;
$exp =~ s {($balanced_paren_regex)}
{push(@paren_contents, $1);
"[p$#paren_contents]"}eg;
# Scan for operators in order of increasing precedence,
# preferring the rightmost.
$exp =~ m{(.+) ([+-]) (.+)}x or
$exp =~ m{(.+) ([*/]) (.+)}x or
# The expression must've been malformed somehow.
# (Note that unary minus isn't supported.)
die "Eh?: [$exp]\n";
my ($op, $lo, $ro) = ($2, $1, $3);
# Restore the parenthetical expressions.
s {\[p(\d+)\]} {($paren_contents[$1])}eg
foreach $lo, $ro;
# And recurse.
return [$op, astize($lo), astize($ro)];}}
{my %ops =
('+' => sub {$_[0] + $_[1]},
'-' => sub {$_[0] - $_[1]},
'*' => sub {$_[0] * $_[1]},
'/' => sub {$_[0] / $_[1]});
sub ev_ast
# Evaluates an abstract syntax tree of the form returned by
# &astize.
{my $ast = shift;
# If $ast is just a number, return it as-is.
ref $ast or return $ast;
# Otherwise, recurse.
my ($op, @operands) = @$ast;
$_ = ev_ast($_) foreach @operands;
return $ops{$op}->(@operands);}}

View file

@ -0,0 +1,23 @@
(de ast (Str)
(let *L (str Str "")
(aggregate) ) )
(de aggregate ()
(let X (product)
(while (member (car *L) '("+" "-"))
(setq X (list (intern (pop '*L)) X (product))) )
X ) )
(de product ()
(let X (term)
(while (member (car *L) '("*" "/"))
(setq X (list (intern (pop '*L)) X (term))) )
X ) )
(de term ()
(let X (pop '*L)
(cond
((num? X) X)
((= "+" X) (term))
((= "-" X) (list '- (term)))
((= "(" X) (prog1 (aggregate) (pop '*L)))) ) ) )

View file

@ -0,0 +1,5 @@
: (ast "1+2+3*-4/(1+2)")
-> (+ (+ 1 2) (/ (* 3 (- 4)) (+ 1 2)))
: (ast "(1+2+3)*-4/(1+2)")
-> (/ (* (+ (+ 1 2) 3) (- 4)) (+ 1 2))

View file

@ -0,0 +1,50 @@
% Lexer
numeric(X) :- 48 =< X, X =< 57.
not_numeric(X) :- 48 > X ; X > 57.
lex1([], []).
lex1([40|Xs], ['('|Ys]) :- lex1(Xs, Ys).
lex1([41|Xs], [')'|Ys]) :- lex1(Xs, Ys).
lex1([43|Xs], ['+'|Ys]) :- lex1(Xs, Ys).
lex1([45|Xs], ['-'|Ys]) :- lex1(Xs, Ys).
lex1([42|Xs], ['*'|Ys]) :- lex1(Xs, Ys).
lex1([47|Xs], ['/'|Ys]) :- lex1(Xs, Ys).
lex1([X|Xs], [N|Ys]) :- numeric(X), N is X - 48, lex1(Xs, Ys).
lex2([], []).
lex2([X], [X]).
lex2([Xa,Xb|Xs], [Xa|Ys]) :- atom(Xa), lex2([Xb|Xs], Ys).
lex2([Xa,Xb|Xs], [Xa|Ys]) :- number(Xa), atom(Xb), lex2([Xb|Xs], Ys).
lex2([Xa,Xb|Xs], [Y|Ys]) :- number(Xa), number(Xb), N is Xa * 10 + Xb, lex2([N|Xs], [Y|Ys]).
% Parser
oper(1, *, X, Y, X * Y). oper(1, /, X, Y, X / Y).
oper(2, +, X, Y, X + Y). oper(2, -, X, Y, X - Y).
num(D) --> [D], {number(D)}.
expr(0, Z) --> num(Z).
expr(0, Z) --> {Z = (X)}, ['('], expr(2, X), [')'].
expr(N, Z) --> {succ(N0, N)}, {oper(N, Op, X, Y, Z)}, expr(N0, X), [Op], expr(N, Y).
expr(N, Z) --> {succ(N0, N)}, expr(N0, Z).
parse(Tokens, Expr) :- expr(2, Expr, Tokens, []).
% Evaluator
evaluate(E, E) :- number(E).
evaluate(A + B, E) :- evaluate(A, Ae), evaluate(B, Be), E is Ae + Be.
evaluate(A - B, E) :- evaluate(A, Ae), evaluate(B, Be), E is Ae - Be.
evaluate(A * B, E) :- evaluate(A, Ae), evaluate(B, Be), E is Ae * Be.
evaluate(A / B, E) :- evaluate(A, Ae), evaluate(B, Be), E is Ae / Be.
% Solution
calculator(String, Value) :-
lex1(String, Tokens1),
lex2(Tokens1, Tokens2),
parse(Tokens2, Expression),
evaluate(Expression, Value).
% Example use
% calculator("(3+50)*7-9", X).

View file

@ -0,0 +1,116 @@
import operator
class AstNode(object):
def __init__( self, opr, left, right ):
self.opr = opr
self.l = left
self.r = right
def eval(self):
return self.opr(self.l.eval(), self.r.eval())
class LeafNode(object):
def __init__( self, valStrg ):
self.v = int(valStrg)
def eval(self):
return self.v
class Yaccer(object):
def __init__(self):
self.operstak = []
self.nodestak =[]
self.__dict__.update(self.state1)
def v1( self, valStrg ):
# Value String
self.nodestak.append( LeafNode(valStrg))
self.__dict__.update(self.state2)
#print 'push', valStrg
def o2( self, operchar ):
# Operator character or open paren in state1
def openParen(a,b):
return 0 # function should not be called
opDict= { '+': ( operator.add, 2, 2 ),
'-': (operator.sub, 2, 2 ),
'*': (operator.mul, 3, 3 ),
'/': (operator.div, 3, 3 ),
'^': ( pow, 4, 5 ), # right associative exponentiation for grins
'(': ( openParen, 0, 8 )
}
operPrecidence = opDict[operchar][2]
self.redeuce(operPrecidence)
self.operstak.append(opDict[operchar])
self.__dict__.update(self.state1)
# print 'pushop', operchar
def syntaxErr(self, char ):
# Open Parenthesis
print 'parse error - near operator "%s"' %char
def pc2( self,operchar ):
# Close Parenthesis
# reduce node until matching open paren found
self.redeuce( 1 )
if len(self.operstak)>0:
self.operstak.pop() # pop off open parenthesis
else:
print 'Error - no open parenthesis matches close parens.'
self.__dict__.update(self.state2)
def end(self):
self.redeuce(0)
return self.nodestak.pop()
def redeuce(self, precidence):
while len(self.operstak)>0:
tailOper = self.operstak[-1]
if tailOper[1] < precidence: break
tailOper = self.operstak.pop()
vrgt = self.nodestak.pop()
vlft= self.nodestak.pop()
self.nodestak.append( AstNode(tailOper[0], vlft, vrgt))
# print 'reduce'
state1 = { 'v': v1, 'o':syntaxErr, 'po':o2, 'pc':syntaxErr }
state2 = { 'v': syntaxErr, 'o':o2, 'po':syntaxErr, 'pc':pc2 }
def Lex( exprssn, p ):
bgn = None
cp = -1
for c in exprssn:
cp += 1
if c in '+-/*^()': # throw in exponentiation (^)for grins
if bgn is not None:
p.v(p, exprssn[bgn:cp])
bgn = None
if c=='(': p.po(p, c)
elif c==')':p.pc(p, c)
else: p.o(p, c)
elif c in ' \t':
if bgn is not None:
p.v(p, exprssn[bgn:cp])
bgn = None
elif c in '0123456789':
if bgn is None:
bgn = cp
else:
print 'Invalid character in expression'
if bgn is not None:
p.v(p, exprssn[bgn:cp])
bgn = None
if bgn is not None:
p.v(p, exprssn[bgn:cp+1])
bgn = None
return p.end()
expr = raw_input("Expression:")
astTree = Lex( expr, Yaccer())
print expr, '=',astTree.eval()

View file

@ -0,0 +1,24 @@
>>> import ast
>>>
>>> expr="2 * (3 -1) + 2 * 5"
>>> node = ast.parse(expr, mode='eval')
>>> print(ast.dump(node).replace(',', ',\n'))
Expression(body=BinOp(left=BinOp(left=Num(n=2),
op=Mult(),
right=BinOp(left=Num(n=3),
op=Sub(),
right=Num(n=1))),
op=Add(),
right=BinOp(left=Num(n=2),
op=Mult(),
right=Num(n=5))))
>>> code_object = compile(node, filename='<string>', mode='eval')
>>> eval(code_object)
14
>>> # lets modify the AST by changing the 5 to a 6
>>> node.body.right.right.n
5
>>> node.body.right.right.n = 6
>>> code_object = compile(node, filename='<string>', mode='eval')
>>> eval(code_object)
16

View file

@ -0,0 +1,117 @@
/*REXX pgm evaluates an infix-type arithmetic expression & shows result.*/
nchars = '0123456789.eEdDqQ' /*possible parts of a #, sans ± */
e='***error!***'; $=' '; doubleOps='&|*/'; z=
parse arg x 1 ox1; if x='' then call serr 'no input was specified.'
x=space(x); L=length(x); x=translate(x,'()()',"[]{}")
j=0; do forever; j=j+1; if j>L then leave; _=substr(x,j,1); _2=getX()
newT=pos(_,' ()[]{}^÷')\==0; if newT then do; z=z _ $; iterate; end
possDouble=pos(_,doubleOps)\==0 /*is _ a possible double operator*/
if possDouble then do /*is this a possible double oper?*/
if _2==_ then do /*yup, it's one of 'em.*/
_=_||_ /*use a double operator*/
x=overlay($,x,Nj) /*blank out the*/
end /* 2nd symbol.*/
z=z _ $; iterate
end
if _=='+' | _=="-" then do; p_=word(z,words(z)) /*last Z token*/
if p_=='(' then z=z 0 /handle unary ±*/
z=z _ $; iterate
end
lets=0; sigs=0; #=_
do j=j+1 to L; _=substr(x,j,1) /*build a valid number.*/
if lets==1 & sigs==0 then if _=='+' | _=='-' then do; sigs=1
#=# || _
iterate
end /*exp*/
if pos(_,nchars)==0 then leave
lets=lets+datatype(_,'M') /*keep track of # of exponents. */
#=# || translate(_,'EEEEE','eDdQq') /*keep buildingthe num.*/
end /*j*/
j=j-1
if \datatype(#,'N') then call serr 'invalid number: ' #
z=z # $
end /*forever*/
_=word(z,1); if _=='+' | _=='-' then z=0 z /*handle unary cases.*/
x='(' space(z) ') '; tokens=words(x) /*force stacking for expression. */
do i=1 for tokens; @.i=word(x,i); end /*i*/ /*assign input tokens*/
L=max(20,length(x)) /*use 20 for the min show width. */
op=')(-+/*^'; rOp=substr(op,3); p.=; s.=; n=length(op); epr=; stack=
do i=1 for n; _=substr(op,i,1); s._=(i+1)%2; p._=s._+(i==n); end /*i*/
/*[↑] assign operator priorities.*/
do #=1 for tokens; ?=@.# /*process each token from @. list*/
if ?=='**' then ?="^" /*convert REXX-type exponentation*/
select /*@.# is: (, operator, ), operand*/
when ?=='(' then stack='(' stack
when isOp(?) then do /*is token an operator?*/
!=word(stack,1) /*get token from stack.*/
do while !\==')' & s.!>=p.?; epr=epr ! /*add*/
stack=subword(stack,2); /*del token from stack.*/
!=word(stack,1) /*get token from stack.*/
end /*while ···)*/
stack=? stack /*add token to stack.*/
end
when ?==')' then do; !=word(stack,1) /*get token from stack.*/
do while !\=='('; epr=epr ! /*add to epr.*/
stack=subword(stack,2) /*del token from stack.*/
!=word(stack,1) /*get token from stack.*/
end /*while ···( */
stack=subword(stack,2) /*del token from stack.*/
end
otherwise epr=epr ? /*add operand to epr. */
end /*select*/
end /*#*/
epr=space(epr stack); tokens=words(epr); x=epr; z=; stack=
do i=1 for tokens; @.i=word(epr,i); end /*i*/ /*assign input tokens*/
dop='/ // % ÷'; bop='& | &&' /*division ops; binary operands*/
aop='- + * ^ **' dop bop; lop=aop '||' /*arithmetic ops; legal operands*/
do #=1 for tokens; ?=@.#; ??=? /*process each token from @. list*/
w=words(stack); b=word(stack,max(1,w)) /*stack count; last entry.*/
a=word(stack,max(1,w-1)) /*stack's "first" operand.*/
division =wordpos(?,dop)\==0 /*flag: doing a division.*/
arith =wordpos(?,aop)\==0 /*flag: doing arithmetic.*/
bitOp =wordpos(?,bop)\==0 /*flag: doing binary math*/
if datatype(?,'N') then do; stack=stack ?; iterate; end
if wordpos(?,lop)==0 then do; z=e 'illegal operator:' ?; leave; end
if w<2 then do; z=e 'illegal epr expression.'; leave; end
if ?=='^' then ??="**" /*REXXify ^ ──► ** (make legal)*/
if ?=='÷' then ??="/" /*REXXify ÷ ──► / (make legal)*/
if division & b=0 then do; z=e 'division by zero: ' b; leave; end
if bitOp & \isBit(a) then do; z=e "token isn't logical: " a; leave; end
if bitOp & \isBit(b) then do; z=e "token isn't logical: " b; leave; end
select /*perform arith. operation*/
when ??=='+' then y = a + b
when ??=='-' then y = a - b
when ??=='*' then y = a * b
when ??=='/' | ??=="÷" then y = a / b
when ??=='//' then y = a // b
when ??=='%' then y = a % b
when ??=='^' | ??=="**" then y = a ** b
when ??=='||' then y = a || b
otherwise z=e 'invalid operator:' ?; leave
end /*select*/
if datatype(y,'W') then y=y/1 /*normalize number with ÷ by 1.*/
_=subword(stack,1,w-2); stack=_ y /*rebuild the stack with answer. */
end /*#*/
if word(z,1)==e then stack= /*handle special case of errors. */
z=space(z stack) /*append any residual entries. */
say 'answer' z /*display the answer (result). */
parse source upper . how . /*invoked via C.L. or REXX pgm?*/
if how=='COMMAND' | ,
\datatype(z,'W') then exit /*stick a fork in it, we're done.*/
return z /*return Z ──► invoker (RESULT).*/
/*──────────────────────────────────subroutines─────────────────────────*/
isBit: return arg(1)==0 | arg(1)==1 /*returns 1 if arg1 is bin bit.*/
isOp: return pos(arg(1),rOp)\==0 /*is argument1 a "real" operator?*/
serr: say; say e arg(1); say; exit 13 /*issue an error message with txt*/
/*──────────────────────────────────GETX subroutine─────────────────────*/
getX: do Nj=j+1 to length(x); _n=substr(x,Nj,1); if _n==$ then iterate
if _n==$ then iterate; return substr(x,Nj,1) /*ignore blanks*/
end /*Nj*/
return $ /*reached end-of-tokens, return $*/

View file

@ -0,0 +1,96 @@
$op_priority = {"+" => 0, "-" => 0, "*" => 1, "/" => 1}
$op_function = {
"+" => lambda {|x, y| x + y},
"-" => lambda {|x, y| x - y},
"*" => lambda {|x, y| x * y},
"/" => lambda {|x, y| x / y}}
class TreeNode
attr_accessor :info, :left, :right
def initialize(info)
@info = info
end
def leaf?
@left.nil? and @right.nil?
end
def to_s(order)
if leaf?
@info
else
left_s, right_s = @left.to_s(order), @right.to_s(order)
strs = case order
when :prefix then [@info, left_s, right_s]
when :infix then [left_s, @info, right_s]
when :postfix then [left_s, right_s, @info]
else []
end
"(" + strs.join(" ") + ")"
end
end
def eval
if !leaf? and operator?(@info)
$op_function[@info].call(@left.eval, @right.eval)
else
@info.to_f
end
end
end
def tokenize(exp)
exp
.gsub('(', ' ( ')
.gsub(')', ' ) ')
.split(' ')
end
def operator?(token)
$op_priority.has_key?(token)
end
def pop_connect_push(op_stack, node_stack)
temp = op_stack.pop
temp.right = node_stack.pop
temp.left = node_stack.pop
node_stack.push(temp)
end
def infix_exp_to_tree(exp)
tokens = tokenize(exp)
op_stack, node_stack = [], []
tokens.each do |token|
if operator?(token)
# clear stack of higher priority operators
until (op_stack.empty? or
op_stack.last.info == "(" or
$op_priority[op_stack.last.info] < $op_priority[token])
pop_connect_push(op_stack, node_stack)
end
op_stack.push(TreeNode.new(token))
elsif token == "("
op_stack.push(TreeNode.new(token))
elsif token == ")"
while op_stack.last.info != "("
pop_connect_push(op_stack, node_stack)
end
# throw away the '('
op_stack.pop
else
node_stack.push(TreeNode.new(token))
end
end
until op_stack.empty?
pop_connect_push(op_stack, node_stack)
end
node_stack.last
end

View file

@ -0,0 +1,8 @@
exp = "1 + 2 - 3 * (4 / 6)"
puts("Original: " + exp)
tree = infix_exp_to_tree(exp)
puts("Prefix: " + tree.to_s(:prefix))
puts("Infix: " + tree.to_s(:infix))
puts("Postfix: " + tree.to_s(:postfix))
puts("Result: " + tree.eval.to_s)

View file

@ -0,0 +1,46 @@
package org.rosetta.arithmetic_evaluator.scala
object ArithmeticParser extends scala.util.parsing.combinator.RegexParsers {
def readExpression(input: String) : Option[()=>Int] = {
parseAll(expr, input) match {
case Success(result, _) =>
Some(result)
case other =>
println(other)
None
}
}
private def expr : Parser[()=>Int] = {
(term<~"+")~expr ^^ { case l~r => () => l() + r() } |
(term<~"-")~expr ^^ { case l~r => () => l() - r() } |
term
}
private def term : Parser[()=>Int] = {
(factor<~"*")~term ^^ { case l~r => () => l() * r() } |
(factor<~"/")~term ^^ { case l~r => () => l() / r() } |
factor
}
private def factor : Parser[()=>Int] = {
"("~>expr<~")" |
"\\d+".r ^^ { x => () => x.toInt } |
failure("Expected a value")
}
}
object Main {
def main(args: Array[String]) {
println("""Please input the expressions. Type "q" to quit.""")
var input: String = ""
do {
input = readLine("> ")
if (input != "q") {
ArithmeticParser.readExpression(input).foreach(f => println(f()))
}
} while (input != "q")
}
}

View file

@ -0,0 +1,44 @@
namespace import tcl::mathop::*
proc ast str {
# produce abstract syntax tree for an expression
regsub -all {[-+*/()]} $str { & } str ;# "tokenizer"
s $str
}
proc s {args} {
# parse "(a + b) * c + d" to "+ [* [+ a b] c] d"
if {[llength $args] == 1} {set args [lindex $args 0]}
if [regexp {[()]} $args] {
eval s [string map {( "\[s " ) \]} $args]
} elseif {"*" in $args} {
s [s_group $args *]
} elseif {"/" in $args} {
s [s_group $args /]
} elseif {"+" in $args} {
s [s_group $args +]
} elseif {"-" in $args} {
s [s_group $args -]
} else {
string map {\{ \[ \} \]} [join $args]
}
}
proc s_group {list op} {
# turn ".. a op b .." to ".. {op a b} .."
set pos [lsearch -exact $list $op]
set p_1 [- $pos 1]
set p1 [+ $pos 1]
lreplace $list $p_1 $p1 \
[list $op [lindex $list $p_1] [lindex $list $p1]]
}
#-- Test suite
foreach test [split {
ast 2-2
ast 1-2-3
ast (1-2)-3
ast 1-(2-3)
ast (1+2)*3
ast (1+2)/3-4*5
ast ((1+2)/3-4)*5
} \n] {
puts "$test ..... [eval $test] ..... [eval [eval $test]]"
}