module calclang; // -*-C-*-ish import IO; import Parse; import Dict; abstract data Expr = Var(String name) | Assign(String varname, Expr val) | Num(Int num) | Infix(Op op,Expr l, Expr r); data Op = Plus | Minus | Times | Divide; Expr parseExpr(var ParseState st) = (parseAssign `or` parsePlusMinus `or` parseMultDiv `or` parseBrackets `or` parseVar `or` parseNum)(st); Expr parseVar(var ParseState st) = Var(identifier(st)); Expr parseNum(var ParseState st) { num = Num(integer(st)); return num; } Expr parseBrackets(var ParseState st) { c = char('(',st); exp = parseExpr(st); c = char(')',st); return exp; } Expr parsePlusMinus(var ParseState st) { left = (parseMultDiv `or` parseBrackets `or` parseNum `or` parseVar)(st); // putStrLn(st.input); whitespace(st); opch = ((char@('+')) `or` (char@('-')))(st); whitespace(st); if (opch=='+') { op = Plus; } else if (opch=='-') { op = Minus; } right = parseExpr(st); return Infix(op,left,right); } Expr parseAssign(var ParseState st) { name = identifier(st); whitespace(st); opch = char('=',st); whitespace(st); exp = parseExpr(st); return Assign(name, exp); } Expr parseMultDiv(var ParseState st) { left = (parseBrackets `or` parseNum `or` parseVar)(st); whitespace(st); opch = ((char@('*')) `or` (char@('/')))(st); whitespace(st); if (opch=='*') { op = Times; } else if (opch=='/') { op = Divide; } right = (parseBrackets `or` parseMultDiv `or` parseNum `or` parseVar)(st); return Infix(op,left,right); } type Context = Dict<String,Int>; Exception NoSuchVariable(String v); Int eval(Context ctxt, Expr e) { case e of { Num(x) -> return x; | Var(v) -> case lookup(ctxt,v) of { nothing -> throw(NoSuchVariable(v)); | just(val) -> return val; } | Assign(v,expr) -> ev = eval(ctxt,expr); add(ctxt, v, ev); return ev; | Infix(op,l,r) -> case op of { Plus -> return eval(ctxt,l)+eval(ctxt,r); | Minus -> return eval(ctxt,l)-eval(ctxt,r); | Times -> return eval(ctxt,l)*eval(ctxt,r); | Divide -> return eval(ctxt,l)/eval(ctxt,r); } } } Exception CalcParseError(String err, Int p); public Int calcExpr(Context ctxt, String strexp) { case parse(parseExpr, strexp) of { Success(expr) -> return eval(ctxt, expr); | ParseError(err,l,p) -> throw(CalcParseError(err,p)); } }