public void TestEffectFunctorComposition() { using (StringWriter sw = new StringWriter()) { Console.SetOut(sw); var fzero = LazyFunction.pureFunction(Console.WriteLine); var effect1 = new Effect <int>(fzero) .Map(x => x + 10); var effect = new Effect <int>(fzero) .Map(x => x + 1) .Map(x => x.ToString()); Assert.Equal("", sw.ToString()); var effectComposed = effect.Chain((string data) => effect1.Map(x => $"{x} - {data}")) .Map(x => x.ToUpper()); Assert.Equal("", sw.ToString()); var res = effectComposed.Run(); Assert.Equal("Launch nuclear missiles!\r\nLaunch nuclear missiles!\r\n", sw.ToString()); Assert.Equal("10 - 1", res); } }
public void TestPureLazyFunction() { using (StringWriter sw = new StringWriter()) { Console.SetOut(sw); LazyFunction.pureFunction(Console.WriteLine); Assert.Equal("", sw.ToString()); } }
public void TestImpureLazyFunction() { using (StringWriter sw = new StringWriter()) { Console.SetOut(sw); int res = LazyFunction.impureFunction(Console.WriteLine); Assert.Equal("Launch nuclear missiles!\r\n", sw.ToString()); } }
public void TestEffectFunctor() { using (StringWriter sw = new StringWriter()) { Console.SetOut(sw); var fzero = LazyFunction.pureFunction(Console.WriteLine); var effect = new Effect <int>(fzero) .Map(x => x + 1) .Map(x => x.ToString()); Assert.Equal("", sw.ToString()); var res = effect.Run(); Assert.Equal("Launch nuclear missiles!\r\n", sw.ToString()); Assert.Equal("1", res); } }
/** * Check that the expression has enough numbers and variables to fit the * requirements of the operators and functions, also check * for only 1 result stored at the end of the evaluation. */ internal void Validate(List <Token> rpn) { /*- * Thanks to Norman Ramsey: * http://http://stackoverflow.com/questions/789847/postfix-notation-validation */ // each push on to this stack is a new function scope, with the value of each // layer on the stack being the count of the number of parameters in that scope Stack <int> stack = new Stack <int>(); // push the 'global' scope stack.Push(0); foreach (Token token in rpn) { switch (token.type) { case TokenType.UNARY_OPERATOR: if (stack.Peek() < 1) { throw new ExpressionException("Missing parameter(s) for operator " + token); } break; case TokenType.OPERATOR: if (stack.Peek() < 2) { throw new ExpressionException("Missing parameter(s) for operator " + token); } // pop the operator's 2 parameters and add the result int v = stack.Pop(); stack.Push(v - 2 + 1); break; case TokenType.FUNCTION: bool isMap = false, isReduce = false; string s = token.surface; if (s[0] == '$') { isMap = true; s = s.Substring(1); } if (s[0] == '@') { isReduce = true; s = s.Substring(1); } if (!functions.TryGetValue(s, out LazyFunction tmplazyfun)) { throw new UnknownFunctionInExpressionException(string.Format("Unknown function: {0}", s), s); } LazyFunction f = tmplazyfun; if (f == null) { throw new ExpressionException("Unknown function '" + token + "' at position " + (token.pos + 1)); } int numParams = stack.Pop(); if (isReduce) { numParams++; } if (!f.NumParamsVaries()) { if (numParams != f.NumParams) { throw new ExpressionException("Function " + token + " expected " + f.NumParams + " parameters, got " + numParams); } // TODO check numero parametri della funzione mappata if (isMap && numParams == 0) { throw new ExpressionException("Mapping requires at least one parameter, calling function " + token); } // TODO check numero parametri della funzione ridotta if (isReduce && numParams < 2) { throw new ExpressionException("Reduce requires at least two parameters, calling function " + token); } } if (stack.Count <= 0) { throw new ExpressionException("Too many function calls, maximum scope exceeded"); } // push the result of the function int val = stack.Pop(); stack.Push(val + 1); break; case TokenType.OPEN_PAREN: stack.Push(0); break; case TokenType.OBJPATH: break; default: val = stack.Pop(); stack.Push(val + 1); break; } } if (stack.Count > 1) { throw new ExpressionException("Too many unhandled function parameter lists"); } else if (stack.Peek() > 1) { throw new ExpressionException("Too many numbers or variables"); } else if (stack.Peek() < 1) { throw new ExpressionException("Empty expression"); } }
public void AddLazyFunction(LazyFunction function) { functions[function.Name] = function; }
private LazyNumber LazyEval(string expression) { if (string.IsNullOrEmpty(expression)) { throw new ExpressionException("Empty expression"); } Stack <LazyNumber> stack = new Stack <LazyNumber>(); foreach (Token token in GetRPN(expression)) { switch (token.type) { case TokenType.UNARY_OPERATOR: LazyNumber value = stack.Pop(); stack.Push(operators[token.surface].Eval(value, null)); break; case TokenType.OPERATOR: LazyNumber v2 = stack.Pop(); LazyNumber v1 = stack.Pop(); stack.Push(operators[token.surface].Eval(v1, v2)); break; case TokenType.VARIABLE: LazyNumber lazyNumber; if (variables.ContainsKey(token.surface)) { lazyNumber = new FunLazyNumberS(() => variables[token.surface]); } else { if (vararrays.ContainsKey(token.surface)) { lazyNumber = new FunLazyNumberA(() => vararrays[token.surface]); } else { if (stringVariables.ContainsKey(token.surface)) { lazyNumber = new FunLazyString(() => stringVariables[token.surface]); } else { throw new UnknownVariableInExpressionException("Unknown variable: " + token.ToString(), token.ToString()); } } } stack.Push(lazyNumber); break; case TokenType.OBJPATH: if (!string.IsNullOrEmpty(token.surface)) { LazyNumber avar = stack.Peek(); if (avar is FunLazyString) { var lazyjson = new FunLazyJSON((FunLazyString)avar) { ObjectPath = token.surface }; stack.Pop(); stack.Push(lazyjson); } else { throw new ExpressionException("Object path set to a variable that is not a JSON object"); } } else { throw new ExpressionException("Empty or invalid object path"); } break; case TokenType.FUNCTION: bool isMap = false, isReduce = false; string s = token.surface; if (s[0] == MapChar) { s = s.Substring(1); isMap = true; } if (s[0] == ReduceChar) { s = s.Substring(1); isReduce = true; } LazyFunction f = functions[s]; List <LazyNumber> p = new List <LazyNumber>( !f.NumParamsVaries() ? f.NumParams : 0); // pop parameters off the stack until we hit the start of // this function's parameter list while (!stack.IsEmpty() && stack.Peek() != PARAMS_START) { p.Insert(0, stack.Pop()); } if (stack.Peek() == PARAMS_START) { stack.Pop(); } LazyNumber fResult; if (isMap) { fResult = new FunLazyNumberA(() => { double[] vals = p[0].EvalArray(); if (vals.Length == 0) { return(new double[0]); } double[] res = new double[vals.Length]; int i = 0; foreach (double val in vals) { p[0] = new FunLazyNumberS(() => val); res[i++] = f.LazyEval(p).Eval(); } return(res); }); } else { if (isReduce) { fResult = new FunLazyNumberS(() => { double[] vals = p[0].EvalArray(); if (vals.Length == 0) { return(0.0D); } if (vals.Length == 1) { return(vals[0]); } double res = 0.0D; List <LazyNumber> pred = new List <LazyNumber>(p.Count + 1) { // collection initialization ... ugly ....:) null, null }; if (p.Count > 1) { pred.AddRange(p.GetRange(1, p.Count - 1)); } pred[0] = new FunLazyNumberS(() => vals[0]); pred[1] = new FunLazyNumberS(() => vals[1]); res = f.LazyEval(pred).Eval(); for (int i = 2; i < vals.Length; i++) { pred[0] = new FunLazyNumberS(() => res); pred[1] = new FunLazyNumberS(() => vals[i]); res = f.LazyEval(pred).Eval(); } return(res); }); } else { fResult = f.LazyEval(p); } } stack.Push(fResult); break; case TokenType.OPEN_PAREN: stack.Push(PARAMS_START); break; case TokenType.LITERAL: stack.Push(new FunLazyNumberS(() => StoD(token.surface))); break; case TokenType.STRINGPARAM: FunLazyString tmpNumber = new FunLazyString(() => token.surface); stack.Push(tmpNumber); break; case TokenType.HEX_LITERAL: double hexd = double.NaN; try { hexd = (double)Convert.ToInt64(token.surface, 16); } catch (Exception) { } // no need to worry, just NaN stack.Push(new FunLazyNumberS(() => hexd)); break; } } return(stack.Pop()); }