private Variable ExecuteOperator(Operator op, Stack<Variable> expr, Stack<Operator> ops) { Kerbulator.DebugLine("executing: "+ op.id); if(op.arity == Arity.BINARY && expr.Count < 2) throw new Exception("operator "+ op.id +" needs a number or expression on both the left and the right hand side"); else if(op.arity == Arity.UNARY && expr.Count < 1) throw new Exception("operator "+ op.id +" needs a number or expression on the right hand side"); else if(op.arity == Arity.BOTH) throw new Exception("arity of "+ op.id +" still undefined"); Variable a,b; switch(op.id) { case "+": b = expr.Pop(); a = expr.Pop(); return ApplyBinaryFunction(a, b, delegate(double c, double d) { return c + d; }); case "-": b = expr.Pop(); if(op.arity == Arity.UNARY) return new Variable(VarType.NUMBER, -b.val); else { a = expr.Pop(); return new Variable(VarType.NUMBER, a.val - b.val); } case "*": case "·": case "×": b = expr.Pop(); a = expr.Pop(); return new Variable(VarType.NUMBER, a.val * b.val); case "/": case "÷": b = expr.Pop(); a = expr.Pop(); return new Variable(VarType.NUMBER, a.val / b.val); case "%": b = expr.Pop(); a = expr.Pop(); return new Variable(VarType.NUMBER, a.val % b.val); case "^": b = expr.Pop(); a = expr.Pop(); return new Variable(VarType.NUMBER, Math.Pow(a.val, b.val)); case "√": b = expr.Pop(); if(op.arity == Arity.UNARY) return new Variable(VarType.NUMBER, Math.Sqrt(b.val)); else { a = expr.Pop(); return new Variable(VarType.NUMBER, Math.Pow(a.val, 1/b.val)); } case "⌊": return new Variable(VarType.NUMBER, Math.Floor(expr.Pop().val)); case "⌈": return new Variable(VarType.NUMBER, Math.Ceiling(expr.Pop().val)); case "|": return new Variable(VarType.NUMBER, Math.Abs(expr.Pop().val)); case "func": b = expr.Pop(); Variable func = expr.Pop(); List<Variable> arguments = new List<Variable>(); arguments.Add(b); return ExecuteFunction(func, arguments); default: throw new Exception("Unknown operator: "+ op.id); } }
protected Variable ExecuteExpression() { Stack<Variable> expr = new Stack<Variable>(); Stack<Operator> ops = new Stack<Operator>(); Token t = null; while(tokens.Count > 0 && tokens.Peek().type != TokenType.END) { t = tokens.Peek(); Kerbulator.DebugLine("Token: "+ Enum.GetName(typeof(TokenType), t.type) +": "+ t.val); if(t.type == TokenType.BRACE) { // Determine whether it's a left or right brace bool isLeft = false; switch(t.val) { case "(": case "{": case "⌊": case "⌈": isLeft = true; break; case "|": isLeft = !PossiblyValidExpression(expr, ops); if(isLeft) Kerbulator.DebugLine("| is left brace"); else { Kerbulator.DebugLine("| is right brace"); } break; } // If it's a left brace, start a sub-expression if(isLeft) { Consume(); // Execute sub-expression Kerbulator.DebugLine("Starting subexpression"); Variable subexpr = ExecuteExpression(); Kerbulator.DebugLine("Answer of subexpression: "+ subexpr.ToString()); expr.Push(subexpr); // Consume right brace. Execute operation if any switch(t.val) { case "(": Consume(")"); break; case "{": Consume("}"); break; case "⌊": Consume("⌋"); ops.Push(operators[t.val]); break; case "⌈": Consume("⌉"); ops.Push(operators[t.val]); break; case "|": Consume("|"); ops.Push(operators[t.val]); break; } } else { // It's a right brace, just end the sub-expression break; } } else if(t.type == TokenType.NUMBER) { expr.Push( new Variable(VarType.NUMBER, Double.Parse(t.val, System.Globalization.CultureInfo.InvariantCulture)) ); Consume(); } else if(t.type == TokenType.OPERATOR) { Operator op = operators[t.val]; // Handle ambiguous cases of arity if(op.arity == Arity.BOTH) { if(PossiblyValidExpression(expr, ops) ) { op = new Operator(op.id, op.precidence, Arity.BINARY); Kerbulator.DebugLine(op.id +" is binary."); } else { op = new Operator(op.id, 3, Arity.UNARY); Kerbulator.DebugLine(op.id +" is unary."); } } // Handle operators with higher precidence while(ops.Count > 0) { Operator prevOp = ops.Peek(); if(op.arity == Arity.BINARY && prevOp.precidence >= op.precidence) { try { expr.Push( ExecuteOperator(ops.Pop(), expr, ops) ); } catch(Exception e) { throw new Exception(t.pos + e.Message); } } else break; } ops.Push(op); Consume(); } else if(t.type == TokenType.IDENTIFIER) { Variable var; if(locals.ContainsKey(t.val)) var = locals[t.val]; else if(functions.ContainsKey(t.val)) var = new Variable(t.val, VarType.USER_FUNCTION, null); else if(globals.ContainsKey(t.val)) var = globals[t.val]; else throw new Exception(t.pos +"undefined variable or function: "+ t.val); Consume(); if(var.type == VarType.FUNCTION || var.type == VarType.USER_FUNCTION) { List<Variable> arguments = new List<Variable>(); // Check for argument list if(tokens.Peek().val == "(") { Consume(); Kerbulator.DebugLine("Arguments specified"); while(tokens.Peek().val != ")") { Kerbulator.DebugLine("Starting subexpression"); Variable subexpr = ExecuteExpression(); Kerbulator.DebugLine("Answer of subexpression: "+ subexpr.ToString()); arguments.Add(subexpr); if(tokens.Peek().val != ")") Consume(TokenType.COMMA); } Consume(")"); // Execute function right now with the given argument list try { Variable result = ExecuteFunction(var, arguments); Kerbulator.DebugLine("Result of function: "+ result.ToString()); expr.Push(result); } catch(Exception e) { throw new Exception(e.Message +" (called from "+ t.func +", line "+ t.line +")"); } } else { int numArgs = 0; if(var.type == VarType.FUNCTION) numArgs = globals[t.val].numArgs; else numArgs = functions[t.val].Ins.Count; if(numArgs == 0) { // Function doesn't take arguments, execute right now with empty argument list try { Variable result = ExecuteFunction(var, new List<Variable>()); Kerbulator.DebugLine("Result of function: "+ result.ToString()); expr.Push(result); } catch(Exception e) { throw new Exception(e.Message +" (called from "+ t.func +", line "+ t.line +")"); } } else { // Push the execution of the function onto the stack Kerbulator.DebugLine("No arguments specified"); expr.Push(var); ops.Push(operators["func"]); } } } else { expr.Push(var); } } else if(t.type == TokenType.LIST) { if(t.val == "]") break; // Right brace ends list List<Variable> elements = new List<Variable>(); Consume(); while(tokens.Peek().val != "]") { Kerbulator.DebugLine("Starting subexpression"); Variable subexpr = ExecuteExpression(); Kerbulator.DebugLine("Answer of subexpression: "+ subexpr.ToString()); elements.Add(subexpr); if(tokens.Peek().val != "]") Consume(TokenType.COMMA); } Consume("]"); expr.Push(new Variable("", VarType.LIST, elements)); } else break; } // Handle remaining ops while(ops.Count > 0) { Operator op = ops.Pop(); try { expr.Push( ExecuteOperator(op, expr, ops) ); } catch(Exception e) { if(t == null) throw new Exception(e.Message); else throw new Exception(t.pos + e.Message); } } if(expr.Count > 1) { if(t == null) throw new Exception("malformed expression"); else throw new Exception(t.pos + "malformed expression"); } return expr.Pop(); }