private Variable ExecuteFunction(Variable func, List<Variable> arguments) { Kerbulator.DebugLine("Executing function: "+ func.id); if(func.type == VarType.FUNCTION) { if(arguments.Count != func.numArgs) throw new Exception("Function "+ func.id +" takes "+ func.numArgs +" arguments, but "+ arguments.Count +" were supplied."); double result = 0; switch(func.id) { case "abs": return ApplyUnaryFunction(arguments[0], Math.Abs); case "acos": return ApplyUnaryFunction(arguments[0], Math.Acos); case "asin": return ApplyUnaryFunction(arguments[0], Math.Asin); case "atan": return ApplyUnaryFunction(arguments[0], Math.Atan); case "ceil": return ApplyUnaryFunction(arguments[0], Math.Ceiling); case "cos": return ApplyUnaryFunction(arguments[0], Math.Cos); case "exp": return ApplyUnaryFunction(arguments[0], Math.Exp); case "floor": return ApplyUnaryFunction(arguments[0], Math.Floor); case "ln": case "log": return ApplyUnaryFunction(arguments[0], Math.Log); case "log10": return ApplyUnaryFunction(arguments[0], Math.Log10); case "max": return new Variable(VarType.NUMBER, Math.Max(arguments[0].val, arguments[1].val)); case "min": return new Variable(VarType.NUMBER, Math.Min(arguments[0].val, arguments[1].val)); case "pow": return new Variable(VarType.NUMBER, Math.Pow(arguments[0].val, arguments[1].val)); case "round": if(arguments[1].type != VarType.NUMBER) throw new Exception("Function round takes a number as second parameter."); return ApplyUnaryFunction(arguments[0], delegate(double val) { return Math.Round(val, (int)arguments[1].val); }); case "sign": return ApplyUnaryFunction(arguments[0], delegate(double val) { return (double)Math.Sign(val); }); case "sin": return ApplyUnaryFunction(arguments[0], Math.Sin); case "sqrt": return ApplyUnaryFunction(arguments[0], Math.Sqrt); case "tan": return ApplyUnaryFunction(arguments[0], Math.Tan); case "len": if(arguments[0].type != VarType.LIST) throw new Exception("Function len expects a list as input."); return new Variable(VarType.NUMBER, (double)arguments[0].elements.Count); case "norm": if(arguments[0].type != VarType.LIST) throw new Exception("Function norm expects a list as input."); double length = 0; foreach(Variable e in arguments[0].elements) { if(e.type != VarType.NUMBER) throw new Exception("Function norm cannot handle nested lists."); length += e.val * e.val; } length = Math.Sqrt(length); List<Variable> elements = new List<Variable>(arguments[0].elements.Count); foreach(Variable e in arguments[0].elements) elements.Add(new Variable(e.id, VarType.NUMBER, e.val/length)); return new Variable(VarType.LIST, elements); case "dot": if(arguments[0].type != VarType.LIST || arguments[1].type != VarType.LIST) throw new Exception("Function dot expects two lists as input."); if(arguments[0].elements.Count != arguments[1].elements.Count) throw new Exception("Function dot requires two lists of the same size."); result = 0; for(int i=0; i<arguments[0].elements.Count; i++) { Variable a = arguments[0].elements[i]; Variable b = arguments[1].elements[i]; if(a.type != VarType.NUMBER || b.type != VarType.NUMBER) throw new Exception("Function dot cannot handle nested lists."); result += a.val * b.val; } return new Variable(VarType.NUMBER, result); case "cross": if(arguments[0].type != VarType.LIST || arguments[1].type != VarType.LIST) throw new Exception("Function cross expects two lists as input."); if(arguments[0].elements.Count != 3 || arguments[1].elements.Count != 3) throw new Exception("Function cross requires two lists of length 3."); List<Variable> x = arguments[0].elements; List<Variable> y = arguments[1].elements; for(int i=0; i<x.Count; i++) { if(x[i].type != VarType.NUMBER || y[i].type != VarType.NUMBER) throw new Exception("Function cross cannot handle nested lists."); } List<Variable>z = new List<Variable>(new[]{ new Variable(VarType.NUMBER, x[1].val * y[2].val - x[2].val * y[1].val), new Variable(VarType.NUMBER, x[2].val * y[0].val - x[0].val * y[2].val), new Variable(VarType.NUMBER, x[0].val * y[1].val - x[1].val * y[0].val) }); return new Variable(VarType.LIST, z); default: throw new Exception("unknown build-in function: "+ func.id); } } else { // User function Kerbulator.DebugLine("Executing "+ func.id); Function f = functions[func.id]; List<Variable> result = f.Execute(arguments, operators, globals, functions); if(f.InError) throw new Exception(f.ErrorString); if(result.Count == 1) return result[0]; else { return new Variable(VarType.LIST, result); } } }
public void AddGlobal(Variable v) { if(globals.ContainsKey(v.id)) globals[v.id] = v; else globals.Add(v.id, v); }
private Variable ApplyBinaryFunction(Variable a, Variable b, BinaryFunction action) { if(a.type == VarType.NUMBER && b.type == VarType.NUMBER) return new Variable("", VarType.NUMBER, action(a.val, b.val)); else if(a.type == VarType.NUMBER && b.type == VarType.LIST) { List<Variable> newElements = new List<Variable>(b.elements.Count); foreach(Variable e in b.elements) newElements.Add(ApplyBinaryFunction(a, e, action)); return new Variable("", VarType.LIST, newElements); } else if(a.type == VarType.LIST && b.type == VarType.NUMBER) { List<Variable> newElements = new List<Variable>(a.elements.Count); foreach(Variable e in a.elements) newElements.Add(ApplyBinaryFunction(e, b, action)); return new Variable("", VarType.LIST, newElements); } else if(a.type == VarType.LIST && b.type == VarType.LIST) { if(a.elements.Count != b.elements.Count) throw new Exception("Trying to perform a binary operation on lists of unequal size."); List<Variable> newElements = new List<Variable>(a.elements.Count); for(int i=0; i<a.elements.Count; i++) newElements.Add(ApplyBinaryFunction(a.elements[i], b.elements[i], action)); return new Variable("", VarType.LIST, newElements); } throw new Exception("Trying to perform an operation on invalid types."); }
private Variable ApplyUnaryFunction(Variable v, UnaryFunction action) { if(v.type == VarType.NUMBER) return new Variable(v.id, v.type, action(v.val)); else if(v.type == VarType.LIST) { List<Variable> newElements = new List<Variable>(v.elements.Count); foreach(Variable e in v.elements) newElements.Add(ApplyUnaryFunction(e, action)); return new Variable(v.id, v.type, newElements); } throw new Exception("Trying to perform an operation on invalid type."); }
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(); }
public List<Variable> Execute(List<Variable> arguments, Dictionary<string, Operator> operators, Dictionary<string, Variable> globals, Dictionary<string, Function> functions) { List<Variable> result = new List<Variable>(); Queue<Token> oldTokens = new Queue<Token>(tokens); try { this.InError = false; this.locals = new Dictionary<string, Variable>(); this.operators = operators; this.globals = globals; this.functions = functions; if(ins.Count != arguments.Count) throw new Exception("function "+ this.id +" takes "+ ins.Count +" arguments, but "+ arguments.Count +" were supplied"); for(int i=0; i<ins.Count; i++) locals.Add(ins[i], arguments[i].Copy(ins[i])); Variable lastVal = new Variable(VarType.NUMBER, 0.0); while(tokens.Count > 0) { Variable val = ExecuteStatement(); if(val != null) lastVal = val; Consume(TokenType.END); } if(outs.Count > 0) { foreach(string id in outs) { if(!locals.ContainsKey(id)) throw new Exception("output variable "+ id +" is not defined in the code of function "+ this.id); result.Add(locals[id]); } } else result.Add(lastVal); } catch(Exception e) { this.InError = true; this.ErrorString = e.Message; } tokens = oldTokens; return result; }
public static void AddBool(Kerbulator kalc, string id, bool v) { double val = v ? 1.0 : 0.0; Variable g = new Variable(id, VarType.NUMBER, val); kalc.AddGlobal(g); }
public static void AddVector3d(Kerbulator kalc, string id, Vector3d v) { Variable x = new Variable("x", VarType.NUMBER, v.x); Variable y = new Variable("y", VarType.NUMBER, v.y); Variable z = new Variable("z", VarType.NUMBER, v.z); List<Variable> elements = new List<Variable>(3); elements.Add(x); elements.Add(y); elements.Add(z); Variable g = new Variable(id, VarType.LIST, elements); kalc.AddGlobal(g); }
public static void AddDouble(Kerbulator kalc, string id, double v) { Variable g = new Variable(id, VarType.NUMBER, v); kalc.AddGlobal(g); }