public override object Evaluate(object[] Parameters) { var value = MathConverter.ConvertToDouble(input.Evaluate(Parameters)); if (value == null) { return(null); } else { return(formula(value.Value)); } }
public static object Average(IEnumerable <object> args) { dynamic sum = 0.0; var count = 0; foreach (double?arg in args.Select(p => MathConverter.ConvertToDouble(p))) { if (arg.HasValue) { count++; sum += arg; } } if (count == 0) { return(null); } return(sum / count); }
/// <summary> /// The actual convert method, for zero or more parameters. /// </summary> public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture) { Func <double, object> convert; if (parameter is string) { // Get the parameter var param = parameter is string?parameter as string : parameter.ToString(); // Get the syntax tree from the expression. We will cache the results to save time for future parsing of the same expression // by the same MathConverter. var x = ParseParameter(param); switch (targetType.FullName) { case "System.Object": switch (x.Length) { case 1: return(x[0].Evaluate(values)); default: throw new NotSupportedException(string.Format("The parameter specifies {0} values; Double supports only one", x.Length)); } case "System.Double": switch (x.Length) { case 1: return(MathConverter.ConvertToDouble(x[0].Evaluate(values))); default: throw new NotSupportedException(string.Format("The parameter specifies {0} values; Double supports only one", x.Length)); } case "System.Windows.CornerRadius": switch (x.Length) { case 1: return(new CornerRadius(MathConverter.ConvertToDouble(x[0].Evaluate(values)).Value)); case 4: return(new CornerRadius(MathConverter.ConvertToDouble(x[0].Evaluate(values)).Value, MathConverter.ConvertToDouble(x[1].Evaluate(values)).Value, MathConverter.ConvertToDouble(x[2].Evaluate(values)).Value, MathConverter.ConvertToDouble(x[3].Evaluate(values)).Value)); default: throw new NotSupportedException(string.Format("The parameter specifies {0} values; CornerRadius supports only one or four", x.Length)); } case "System.Windows.GridLength": switch (x.Length) { case 1: return(new GridLength(MathConverter.ConvertToDouble(x[0].Evaluate(values)).Value)); default: throw new NotSupportedException(string.Format("The parameter specifies {0} values; GridLength supports only one", x.Length)); } case "System.Windows.Thickness": switch (x.Length) { case 1: return(new Thickness(MathConverter.ConvertToDouble(x[0].Evaluate(values)).Value)); case 2: return(new Thickness(MathConverter.ConvertToDouble(x[0].Evaluate(values)).Value, MathConverter.ConvertToDouble(x[1].Evaluate(values)).Value, MathConverter.ConvertToDouble(x[0].Evaluate(values)).Value, MathConverter.ConvertToDouble(x[1].Evaluate(values)).Value)); case 4: return(new Thickness(MathConverter.ConvertToDouble(x[0].Evaluate(values)).Value, MathConverter.ConvertToDouble(x[1].Evaluate(values)).Value, MathConverter.ConvertToDouble(x[2].Evaluate(values)).Value, MathConverter.ConvertToDouble(x[3].Evaluate(values)).Value)); default: throw new NotSupportedException(string.Format("The parameter specifies {0} values; Thickness supports only one, two, or four", x.Length)); } case "System.Windows.Rect": switch (x.Length) { case 4: return(new Rect(MathConverter.ConvertToDouble(x[0].Evaluate(values)).Value, MathConverter.ConvertToDouble(x[1].Evaluate(values)).Value, MathConverter.ConvertToDouble(x[2].Evaluate(values)).Value, MathConverter.ConvertToDouble(x[3].Evaluate(values)).Value)); default: throw new NotSupportedException(string.Format("The parameter specifies {0} values; Rect supports only four", x.Length)); } case "System.Windows.Size": switch (x.Length) { case 2: return(new Size(MathConverter.ConvertToDouble(x[0].Evaluate(values)).Value, MathConverter.ConvertToDouble(x[1].Evaluate(values)).Value)); default: throw new NotSupportedException(string.Format("You supplied {0} values; Size supports only two", x.Length)); } case "System.Windows.Point": switch (x.Length) { case 2: return(new Point(MathConverter.ConvertToDouble(x[0].Evaluate(values)).Value, MathConverter.ConvertToDouble(x[1].Evaluate(values)).Value)); default: throw new NotSupportedException(string.Format("You supplied {0} values; Point supports only two", x.Length)); } case "System.Boolean": switch (x.Length) { case 1: return((bool?)x[0].Evaluate(values)); default: throw new NotSupportedException(string.Format("You supplied {0} values; Boolean supports only one", x.Length)); } case "System.String": switch (x.Length) { case 1: var val = x[0].Evaluate(values); if (val is string) { return(val); } else if (val == null) { return(null); } else { return(val.ToString()); } default: throw new NotSupportedException(string.Format("You supplied {0} values; string supports only one", x.Length)); } case "System.Uri": switch (x.Length) { case 1: var val = x[0].Evaluate(values); if (val is string) { return(new Uri(val as string)); } else if (val == null) { return(null); } else { return(new Uri(val.ToString())); } default: throw new NotSupportedException($"You supplied {values.Length} values; Uri supports only one"); } case "System.Single": convert = p => System.Convert.ToSingle(p); break; case "System.Int32": convert = p => System.Convert.ToInt32(p); break; case "System.Int64": convert = p => System.Convert.ToInt64(p); break; case "System.Decimal": convert = p => System.Convert.ToDecimal(p); break; case "System.Byte": convert = p => System.Convert.ToByte(p); break; case "System.SByte": convert = p => System.Convert.ToSByte(p); break; case "System.Char": convert = p => (char)System.Convert.ToInt32(p); break; case "System.Int16": convert = p => System.Convert.ToInt16(p); break; case "System.UInt16": convert = p => System.Convert.ToUInt16(p); break; case "System.UInt32": convert = p => System.Convert.ToUInt32(p); break; case "System.UInt64": convert = p => System.Convert.ToUInt64(p); break; default: if (targetType == typeof(double?)) { return(MathConverter.ConvertToDouble(x[0].Evaluate(values))); } // We don't know what to return, so let's evaluate the parameter and try to convert it. var evaluatedValue = x[0].Evaluate(values); if ((targetType.IsGenericType && targetType.GetGenericTypeDefinition() == typeof(Nullable <>))) { // We're supposed to return a Nullable<T> where T : struct if (evaluatedValue == null) { return(null); } else { return(System.Convert.ChangeType(evaluatedValue, targetType.GetGenericArguments()[0])); } } return(System.Convert.ChangeType(evaluatedValue, targetType)); } var value = x[0].Evaluate(values); if (value == null) { return(null); } else { return(convert(ConvertToDouble(value).Value)); } } else if (parameter == null) { switch (targetType.FullName) { case "System.Object": switch (values.Length) { case 1: return(ConvertToObject(values[0])); default: throw new NotSupportedException($"You supplied {values.Length} values; Object supports only one or one"); } case "System.Double": switch (values.Length) { case 1: return(ConvertToDouble(values[0])); default: throw new NotSupportedException($"You supplied {values.Length} values; Double supports only one or one"); } case "System.Windows.CornerRadius": switch (values.Length) { case 1: return(new CornerRadius(ConvertToDouble(values[0]).Value)); case 4: return(new CornerRadius(ConvertToDouble(values[0]).Value, ConvertToDouble(values[1]).Value, ConvertToDouble(values[2]).Value, ConvertToDouble(values[3]).Value)); default: throw new NotSupportedException($"You supplied {values.Length} values; GridLength supports only one or four"); } case "System.Windows.GridLength": switch (values.Length) { case 1: return(new GridLength(ConvertToDouble(values[0]).Value)); default: throw new NotSupportedException($"You supplied {values.Length} values; GridLength supports only one"); } case "System.Windows.Thickness": switch (values.Length) { case 1: return(new Thickness(ConvertToDouble(values[0]).Value)); case 2: return(new Thickness(ConvertToDouble(values[0]).Value, ConvertToDouble(values[1]).Value, ConvertToDouble(values[0]).Value, ConvertToDouble(values[1]).Value)); case 4: return(new Thickness(ConvertToDouble(values[0]).Value, ConvertToDouble(values[1]).Value, ConvertToDouble(values[2]).Value, ConvertToDouble(values[3]).Value)); default: throw new NotSupportedException($"You supplied {values.Length} values; Thickness supports only one, two, or four"); } case "System.Windows.Rect": switch (values.Length) { case 4: return(new Rect(ConvertToDouble(values[0]).Value, ConvertToDouble(values[1]).Value, ConvertToDouble(values[2]).Value, ConvertToDouble(values[3]).Value)); default: throw new NotSupportedException($"You supplied {values.Length} values; Rect supports only four"); } case "System.Windows.Size": switch (values.Length) { case 2: return(new Size(ConvertToDouble(values[0]).Value, ConvertToDouble(values[1]).Value)); default: throw new NotSupportedException($"You supplied {values.Length} values; Size supports only two"); } case "System.Windows.Point": switch (values.Length) { case 2: return(new Point(ConvertToDouble(values[0]).Value, ConvertToDouble(values[1]).Value)); default: throw new NotSupportedException($"You supplied {values.Length} values; Point supports only two"); } case "System.Boolean": switch (values.Length) { case 1: if (values[0] is string) { return(bool.Parse(values[0] as string)); } return((bool?)values[0]); default: throw new NotSupportedException($"You supplied {values.Length} values; boolean supports only one"); } case "System.String": switch (values.Length) { case 1: if (values[0] is string) { return(values[0]); } else if (values[0] == null) { return(null); } else { return(values[0].ToString()); } default: throw new NotSupportedException($"You supplied {values.Length} values; string supports only one"); } case "System.Uri": switch (values.Length) { case 1: if (values[0] is string) { return(new Uri(values[0] as string)); } else if (values[0] == null) { return(null); } else { return(new Uri(values[0].ToString())); } default: throw new NotSupportedException($"You supplied {values.Length} values; Uri supports only one"); } case "System.Single": convert = p => System.Convert.ToSingle(p); break; case "System.Int32": convert = p => System.Convert.ToInt32(p); break; case "System.Int64": convert = p => System.Convert.ToInt64(p); break; case "System.Decimal": convert = p => System.Convert.ToDecimal(p); break; case "System.Byte": convert = p => System.Convert.ToByte(p); break; case "System.SByte": convert = p => System.Convert.ToSByte(p); break; case "System.Char": convert = p => (char)System.Convert.ToInt32(p); break; case "System.Int16": convert = p => System.Convert.ToInt16(p); break; case "System.UInt16": convert = p => System.Convert.ToUInt16(p); break; case "System.UInt32": convert = p => System.Convert.ToUInt32(p); break; case "System.UInt64": convert = p => System.Convert.ToUInt64(p); break; default: switch (values.Length) { case 1: if (targetType == typeof(double?)) { return(ConvertToDouble(values[0])); } if ((targetType.IsGenericType && targetType.GetGenericTypeDefinition() == typeof(Nullable <>))) { // We're supposed to return a Nullable<T> where T : struct if (values[0] == null) { return(null); } else { return(System.Convert.ChangeType(values[0], targetType.GetGenericArguments()[0])); } } return(System.Convert.ChangeType(values[0], targetType)); //throw new NotSupportedException(string.Format("You cannot convert to a {0}", targetType.Name)); default: throw new NotSupportedException($"You supplied {values.Length} values; {targetType.FullName} supports only one"); } } switch (values.Length) { case 1: var value = values[0]; if (value == null) { return(null); } else { return(convert(ConvertToDouble(value).Value)); } default: throw new NotSupportedException($"You supplied {values.Length} values; {targetType.FullName} supports only one"); } } else { throw new ArgumentException("The Converter Parameter must be a string.", "parameter"); } }
private AbstractSyntaxTree Primary() { var t = scanner.GetToken(); switch (t.TokenType) { case TokenType.Number: return(new ConstantNumberNode(double.Parse((t as LexicalToken).Lex, NumberStyles.Number, CultureInfo.InvariantCulture))); case TokenType.Minus: return(new NegativeNode(Primary())); case TokenType.Not: return(new NotNode(Primary())); case TokenType.X: return(new VariableNode(0)); case TokenType.Y: return(new VariableNode(1)); case TokenType.Z: return(new VariableNode(2)); case TokenType.String: return(new StringNode((t as LexicalToken).Lex)); case TokenType.Lexical: var lex = (t as LexicalToken).Lex; Func <object> formula0 = null; Func <double, double> formula1 = null; Func <object, object, object> formula2 = null; Func <IEnumerable <object>, object> formulaN = null; switch (lex) { case "null": return(new NullNode()); case "NOW": case "Now": case "now": formula0 = () => DateTime.Now; break; case "COS": case "Cos": case "cos": formula1 = Math.Cos; break; case "SIN": case "Sin": case "sin": formula1 = Math.Sin; break; case "TAN": case "Tan": case "tan": formula1 = Math.Tan; break; case "ABS": case "Abs": case "abs": formula1 = Math.Abs; break; case "ACOS": case "ACos": case "Acos": case "acos": case "ARCCOS": case "ArcCos": case "Arccos": case "arccos": formula1 = Math.Acos; break; case "ASIN": case "ASin": case "Asin": case "asin": case "ARCSIN": case "ArcSin": case "Arcsin": case "arcsin": formula1 = Math.Asin; break; case "ATAN": case "ATan": case "Atan": case "atan": case "ARCTAN": case "ArcTan": case "Arctan": case "arctan": formula1 = Math.Atan; break; case "CEIL": case "Ceil": case "ceil": case "CEILING": case "Ceiling": case "ceiling": formula1 = Math.Ceiling; break; case "FLOOR": case "Floor": case "floor": formula1 = Math.Floor; break; case "SQRT": case "Sqrt": case "sqrt": formula1 = Math.Sqrt; break; case "DEGREES": case "Degrees": case "degrees": case "DEG": case "Deg": case "deg": formula1 = x => x / Math.PI * 180; break; case "RADIANS": case "Radians": case "radians": case "RAD": case "Rad": case "rad": formula1 = x => x / 180 * Math.PI; break; case "ROUND": case "Round": case "round": formula2 = (x, y) => { var a = MathConverter.ConvertToDouble(x); var b = MathConverter.ConvertToDouble(y); if (a.HasValue && b.HasValue) { if (b.Value == (int)b.Value) { return(Math.Round(a.Value, (int)b.Value)); } else { throw new Exception(string.Format("Error calling Math.Round({0}, {1}):\r\n{1} is not an integer.", a, y)); } } else { return(null); } }; break; case "ATAN2": case "ATan2": case "Atan2": case "atan2": case "ARCTAN2": case "ArcTan2": case "Arctan2": case "arctan2": formula2 = (x, y) => { var a = MathConverter.ConvertToDouble(x); var b = MathConverter.ConvertToDouble(y); if (a.HasValue && b.HasValue) { return(Math.Atan2(a.Value, b.Value)); } else { return(null); } }; break; case "LOG": case "Log": case "log": formula2 = (x, y) => { var a = MathConverter.ConvertToDouble(x); var b = MathConverter.ConvertToDouble(y); if (a.HasValue && b.HasValue) { return(Math.Log(a.Value, b.Value)); } else { return(null); } }; break; case "ISNULL": case "IsNull": case "Isnull": case "isnull": case "IFNULL": case "IfNull": case "Ifnull": case "ifnull": formula2 = (x, y) => ReferenceEquals(x, null) ? y : x; break; case "AND": case "And": case "and": formulaN = FormulaNodeN.And; break; case "NOR": case "Nor": case "nor": formulaN = FormulaNodeN.Nor; break; case "OR": case "Or": case "or": formulaN = FormulaNodeN.Or; break; case "MAX": case "Max": case "max": formulaN = FormulaNodeN.Max; break; case "MIN": case "Min": case "min": formulaN = FormulaNodeN.Min; break; case "AVG": case "Avg": case "avg": case "AVERAGE": case "Average": case "average": formulaN = FormulaNodeN.Average; break; case "PI": case "pi": return(new ConstantNumberNode(Math.PI)); case "E": case "e": return(new ConstantNumberNode(Math.E)); case "FORMAT": case "Format": case "format": formulaN = FormulaNodeN.Format; break; default: var err = lex + " is an invalid formula name"; throw new ParsingException(scanner.Position, err, new NotSupportedException(err)); } if (formula0 != null) { var ex = lex + " is a formula that takes zero arguments. You must call it like this: \"" + lex + "()\""; if (scanner.GetToken().TokenType != TokenType.LParen) { throw new ParsingException(scanner.Position, ex); } if (scanner.GetToken().TokenType != TokenType.RParen) { throw new ParsingException(scanner.Position, ex); } return(new FormulaNode0(lex, formula0)); } else if (formula1 != null) { // Create a formula1. var ex = lex + " is a formula that takes one argument. You must specify the arguments like this: \"" + lex + "(3)\""; if (scanner.GetToken().TokenType != TokenType.LParen) { throw new ParsingException(scanner.Position, ex); } AbstractSyntaxTree arg; try { arg = Conditional(); } catch (Exception e) { throw new ParsingException(scanner.Position, ex, new Exception(ex, e)); } if (scanner.GetToken().TokenType != TokenType.RParen) { throw new ParsingException(scanner.Position, ex); } return(new FormulaNode1(lex, formula1, arg)); } else if (formula2 != null) { // Create a formula2. var ex = lex + " is a formula that takes two arguments. You must specify the arguments like this: \"" + lex + "(3;2)\""; if (lex == "round") { ex = "round is a formula that takes one or two argments. You must specify the argument(s) like this: round(4.693) or round(4.693;2)"; } if (scanner.GetToken().TokenType != TokenType.LParen) { throw new ParsingException(scanner.Position, ex); } AbstractSyntaxTree arg1, arg2; try { arg1 = Conditional(); } catch (Exception inner) { throw new ParsingException(scanner.Position, ex, inner); } switch (scanner.GetToken().TokenType) { case TokenType.Semicolon: try { arg2 = Conditional(); } catch (Exception inner) { throw new ParsingException(scanner.Position, ex, inner); } if (scanner.GetToken().TokenType == TokenType.RParen) { return(new FormulaNode2(lex, formula2, arg1, arg2)); } break; case TokenType.RParen: if (lex == "round") { return(new FormulaNode2(lex, formula2, arg1, new ConstantNumberNode(0))); } break; } throw new ParsingException(scanner.Position, ex); } else { // Create a formulaN. if (scanner.GetToken().TokenType != TokenType.LParen) { throw new ParsingException(scanner.Position, "You must specify arguments for " + lex + ". Those arguments must be enclosed in parentheses."); } var trees = new List <AbstractSyntaxTree>(); if (scanner.GetToken().TokenType == TokenType.RParen) { throw new ParsingException(scanner.Position, "You must specify at least one argument for " + lex + "."); } else { scanner.PutBackToken(); } while (true) { try { trees.Add(Conditional()); } catch (Exception e) { throw new ParsingException(scanner.Position, "Error parsing arguments for " + lex + ".", e); } var type = scanner.GetToken().TokenType; switch (type) { case TokenType.RParen: return(new FormulaNodeN(lex, formulaN, trees)); case TokenType.Semicolon: break; default: throw new ParsingException(scanner.Position, "Error parsing arguments for " + lex + ". Invalid character: " + type + ". Expected either a comma, semicolon, or right parenthesis."); } } } case TokenType.LBracket: t = scanner.GetToken(); var exc = new Exception("Variable accessors should come in the form [i], where i is an integer."); int i; if (t is LexicalToken) { if (int.TryParse((t as LexicalToken).Lex, out i)) { try { return(new VariableNode(i)); } finally { if (scanner.GetToken().TokenType != TokenType.RBracket) { throw new ParsingException(scanner.Position, exc.Message, exc); } } } } throw exc; case TokenType.LParen: var cond = Conditional(); if (scanner.GetToken().TokenType != TokenType.RParen) { throw new ParsingException(scanner.Position, "Mismatching parentheses"); } return(cond); default: throw new ParsingException(scanner.Position, "Invalid conversion string."); } }
private AbstractSyntaxTree Primary() { var t = scanner.GetToken(); switch (t.TokenType) { case TokenType.Number: return(new ConstantNumberNode(double.Parse((t as LexicalToken).Lex, NumberStyles.Number, CultureInfo.InvariantCulture))); case TokenType.Minus: return(new NegativeNode(Primary())); case TokenType.Not: return(new NotNode(Primary())); case TokenType.X: return(new VariableNode(0)); case TokenType.Y: return(new VariableNode(1)); case TokenType.Z: return(new VariableNode(2)); case TokenType.String: return(new StringNode((t as LexicalToken).Lex)); case TokenType.InterpolatedString: var token = t as InterpolatedStringToken; return(new FormulaNodeN("Format", FormulaNodeN.Format, new AbstractSyntaxTree[] { new StringNode(token.Lex) }.Union(token.Arguments))); case TokenType.Lexical: var lex = (t as LexicalToken).Lex; Func <object> formula0 = null; Func <double, double> formula1 = null; Func <object, object> formula1_obj = null; Func <object, object, object> formula2 = null; Func <IEnumerable <object>, object> formulaN = null; switch (lex.ToLower()) { case "null": return(new NullNode()); case "true": return(new ValueNode(true)); case "false": return(new ValueNode(false)); default: switch (lex.ToLower()) { case "now": formula0 = () => DateTime.Now; break; case "cos": formula1 = Math.Cos; break; case "sin": formula1 = Math.Sin; break; case "tan": formula1 = Math.Tan; break; case "abs": formula1 = Math.Abs; break; case "acos": case "arccos": formula1 = Math.Acos; break; case "asin": case "arcsin": formula1 = Math.Asin; break; case "atan": case "arctan": formula1 = Math.Atan; break; case "ceil": case "ceiling": formula1 = Math.Ceiling; break; case "floor": formula1 = Math.Floor; break; case "sqrt": formula1 = Math.Sqrt; break; case "degrees": case "deg": formula1 = x => x / Math.PI * 180; break; case "radians": case "rad": formula1 = x => x / 180 * Math.PI; break; case "tolower": case "lcase": formula1_obj = x => x == null ? null : $"{x}".ToLowerInvariant(); break; case "toupper": case "ucase": formula1_obj = x => x == null ? null : $"{x}".ToUpperInvariant(); break; case "round": formula2 = (x, y) => { var a = MathConverter.ConvertToDouble(x); var b = MathConverter.ConvertToDouble(y); if (a.HasValue && b.HasValue) { if (b.Value == (int)b.Value) { return(Math.Round(a.Value, (int)b.Value)); } else { throw new Exception(string.Format("Error calling Math.Round({0}, {1}):\r\n{1} is not an integer.", a, y)); } } else { return(null); } }; break; case "atan2": case "arctan2": formula2 = (x, y) => { var a = MathConverter.ConvertToDouble(x); var b = MathConverter.ConvertToDouble(y); if (a.HasValue && b.HasValue) { return(Math.Atan2(a.Value, b.Value)); } else { return(null); } }; break; case "log": formula2 = (x, y) => { var a = MathConverter.ConvertToDouble(x); var b = MathConverter.ConvertToDouble(y); if (a.HasValue && b.HasValue) { return(Math.Log(a.Value, b.Value)); } else { return(null); } }; break; case "isnull": case "ifnull": formula2 = (x, y) => ReferenceEquals(x, null) ? y : x; break; case "and": formulaN = FormulaNodeN.And; break; case "nor": formulaN = FormulaNodeN.Nor; break; case "or": formulaN = FormulaNodeN.Or; break; case "max": formulaN = FormulaNodeN.Max; break; case "min": formulaN = FormulaNodeN.Min; break; case "avg": case "average": formulaN = FormulaNodeN.Average; break; case "pi": return(new ConstantNumberNode(Math.PI)); case "e": return(new ConstantNumberNode(Math.E)); case "format": formulaN = FormulaNodeN.Format; break; case "concat": formulaN = FormulaNodeN.Concat; break; case "join": formulaN = FormulaNodeN.Join; break; case "contains": formula2 = (x, y) => { if (x is IEnumerable <dynamic> ) { return((x as IEnumerable <dynamic>).Contains(y)); } else if (x is string) { return((x as string).Contains(y as dynamic)); } else { return(null); } }; break; default: var err = $"{lex} is an invalid formula name"; throw new ParsingException(scanner.Position, err, new NotSupportedException(err)); } break; } if (formula0 != null) { var ex = $"{lex} is a formula that takes zero arguments. You must call it like this: \"{lex}()\""; if (scanner.GetToken().TokenType != TokenType.LParen) { throw new ParsingException(scanner.Position, ex); } if (scanner.GetToken().TokenType != TokenType.RParen) { throw new ParsingException(scanner.Position, ex); } return(new FormulaNode0(lex, formula0)); } else if (formula1 != null || formula1_obj != null) { // Create a formula1. var ex = $"{lex} is a formula that takes one argument. You must specify the arguments like this: \"{lex}(3)\""; if (scanner.GetToken().TokenType != TokenType.LParen) { throw new ParsingException(scanner.Position, ex); } AbstractSyntaxTree arg; try { arg = Conditional(); } catch (Exception e) { throw new ParsingException(scanner.Position, ex, new Exception(ex, e)); } if (scanner.GetToken().TokenType != TokenType.RParen) { throw new ParsingException(scanner.Position, ex); } if (formula1 != null) { formula1_obj = x => { var val = MathConverter.ConvertToDouble(x); if (val.HasValue) { return(formula1(val.Value)); } else { return(null); } }; } return(new FormulaNode1(lex, formula1_obj, arg)); } else if (formula2 != null) { // Create a formula2. var ex = $"{lex} is a formula that takes two argument. You must specify the arguments like this: \"{lex}(3;2)\""; if (lex.ToLower() == "round") { ex = "round is a formula that takes one or two argments. You must specify the argument(s) like this: round(4.693) or round(4.693;2)"; } if (scanner.GetToken().TokenType != TokenType.LParen) { throw new ParsingException(scanner.Position, ex); } AbstractSyntaxTree arg1, arg2; try { arg1 = Conditional(); } catch (Exception inner) { throw new ParsingException(scanner.Position, ex, inner); } switch (scanner.GetToken().TokenType) { case TokenType.Semicolon: try { arg2 = Conditional(); } catch (Exception inner) { throw new ParsingException(scanner.Position, ex, inner); } if (scanner.GetToken().TokenType == TokenType.RParen) { return(new FormulaNode2(lex, formula2, arg1, arg2)); } break; case TokenType.RParen: if (lex.ToLower() == "round") { return(new FormulaNode2(lex, formula2, arg1, new ConstantNumberNode(0))); } break; } throw new ParsingException(scanner.Position, ex); } else { // Create a formulaN. if (scanner.GetToken().TokenType != TokenType.LParen) { throw new ParsingException(scanner.Position, $"You must specify arguments for {lex}. Those arguments must be enclosed in parentheses."); } var trees = new List <AbstractSyntaxTree>(); if (scanner.GetToken().TokenType == TokenType.RParen) { throw new ParsingException(scanner.Position, $"You must specify at least one argument for {lex}."); } else { scanner.PutBackToken(); } while (true) { try { trees.Add(Conditional()); } catch (Exception e) { throw new ParsingException(scanner.Position, $"Error parsing arguments for {lex}.", e); } var type = scanner.GetToken().TokenType; switch (type) { case TokenType.RParen: return(new FormulaNodeN(lex, formulaN, trees)); case TokenType.Semicolon: break; default: throw new ParsingException(scanner.Position, $"Error parsing arguments for {lex}. Invalid character: {type}. Expected either a comma, semicolon, or right parenthesis."); } } } case TokenType.LBracket: t = scanner.GetToken(); var exc = new Exception("Variable accessors should come in the form [i], where i is an integer."); int i; if (t is LexicalToken) { if (int.TryParse((t as LexicalToken).Lex, out i)) { try { return(new VariableNode(i)); } finally { if (scanner.GetToken().TokenType != TokenType.RBracket) { throw new ParsingException(scanner.Position, exc.Message, exc); } } } } throw exc; case TokenType.LParen: var cond = Conditional(); if (scanner.GetToken().TokenType != TokenType.RParen) { throw new ParsingException(scanner.Position, "Mismatching parentheses"); } return(cond); default: throw new ParsingException(scanner.Position, "Invalid conversion string."); } }
private AbstractSyntaxTree Primary() { var t = scanner.GetToken(); switch (t.TokenType) { case TokenType.Number: return(new ConstantNumberNode(double.Parse((t as LexicalToken).Lex, NumberStyles.Number, CultureInfo.InvariantCulture))); case TokenType.Minus: return(new NegativeNode(Primary())); case TokenType.Not: return(new NotNode(Primary())); case TokenType.X: return(new VariableNode(0)); case TokenType.Y: return(new VariableNode(1)); case TokenType.Z: return(new VariableNode(2)); case TokenType.String: return(new StringNode((t as LexicalToken).Lex)); case TokenType.InterpolatedString: var token = t as InterpolatedStringToken; return(new FormulaNodeN("Format", FormulaNodeN.Format, new AbstractSyntaxTree[] { new StringNode(token.Lex) }.Union(token.Arguments))); case TokenType.Lexical: var lex = (t as LexicalToken).Lex; Func <object> formula0 = null; Func <double, double> formula1 = null; Func <object, object> formula1_obj = null; Func <object, object, object> formula2 = null; Func <IEnumerable <object>, object> formulaN = null; switch (lex.ToLower()) { case "null": return(new NullNode()); case "pi": return(new ConstantNumberNode(Math.PI)); case "e": return(new ConstantNumberNode(Math.E)); case "true": return(new ValueNode(true)); case "false": return(new ValueNode(false)); default: switch (lex.ToLower()) { case "now": formula0 = () => DateTime.Now; break; case "cos": formula1 = Math.Cos; break; case "sin": formula1 = Math.Sin; break; case "tan": formula1 = Math.Tan; break; case "abs": formula1 = Math.Abs; break; case "acos": case "arccos": formula1 = Math.Acos; break; case "asin": case "arcsin": formula1 = Math.Asin; break; case "atan": case "arctan": formula1 = Math.Atan; break; case "ceil": case "ceiling": formula1 = Math.Ceiling; break; case "floor": formula1 = Math.Floor; break; case "sqrt": formula1 = Math.Sqrt; break; case "degrees": case "deg": formula1 = x => x / Math.PI * 180; break; case "radians": case "rad": formula1 = x => x / 180 * Math.PI; break; case "tolower": case "lcase": formula1_obj = x => x == null ? null : $"{x}".ToLowerInvariant(); break; case "toupper": case "ucase": formula1_obj = x => x == null ? null : $"{x}".ToUpperInvariant(); break; case "startswith": formula2 = (x, y) => x is string str1 && (y is string || y?.ToString().Length > 0) ? str1.StartsWith($"{y}") : new bool?(); break; case "endswith": formula2 = (x, y) => x is string str1 && (y is string || y?.ToString().Length > 0) ? str1.EndsWith($"{y}") : new bool?(); break; case "visibleorcollapsed": formula1_obj = x => x is bool && (bool)x == true ? Visibility.Visible : Visibility.Collapsed; break; case "visibleorhidden": formula1_obj = x => x is bool && (bool)x == true ? Visibility.Visible : Visibility.Hidden; break; case "round": formula2 = (x, y) => { var a = MathConverter.ConvertToDouble(x); var b = MathConverter.ConvertToDouble(y); if (a.HasValue && b.HasValue) { if (b.Value == (int)b.Value) { return(Math.Round(a.Value, (int)b.Value)); } else { throw new Exception($"Error calling Math.Round({a}, {y}):\r\n{y} is not an integer."); } } else { return(null); } }; break; case "atan2": case "arctan2": formula2 = (x, y) => { var a = MathConverter.ConvertToDouble(x); var b = MathConverter.ConvertToDouble(y); if (a.HasValue && b.HasValue) { return(Math.Atan2(a.Value, b.Value)); } else { return(null); } }; break; case "log": formula2 = (x, y) => { var a = MathConverter.ConvertToDouble(x); var b = MathConverter.ConvertToDouble(y); if (a.HasValue && b.HasValue) { return(Math.Log(a.Value, b.Value)); } else { return(null); } }; break; case "isnull": case "ifnull": formula2 = (x, y) => ReferenceEquals(x, null) ? y : x; break; case "and": formulaN = FormulaNodeN.And; break; case "nor": formulaN = FormulaNodeN.Nor; break; case "or": formulaN = FormulaNodeN.Or; break; case "max": formulaN = FormulaNodeN.Max; break; case "min": formulaN = FormulaNodeN.Min; break; case "avg": case "average": formulaN = FormulaNodeN.Average; break; case "format": formulaN = FormulaNodeN.Format; break; case "concat": formulaN = FormulaNodeN.Concat; break; case "join": formulaN = FormulaNodeN.Join; break; case "contains": formula2 = (x, y) => { if (x is IEnumerable <dynamic> ) { return((x as IEnumerable <dynamic>).Contains(y)); } else if (x is string str1 && (y is string || $"{y}".Length > 0)) { return(str1.Contains($"{y}")); }
/// <summary> /// The actual convert method, for zero or more parameters. /// </summary> public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture) { if (parameter is string) { // Get the parameter var param = parameter is string?parameter as string : parameter.ToString(); // Get the syntax tree from the expression. We will cache the results to save time for future parsing of the same expression // by the same MathConverter. var x = ParseParameter(param); switch (targetType.FullName) { case "System.Object": switch (x.Length) { case 1: return(x[0].Evaluate(values)); default: throw new NotSupportedException(string.Format("The parameter specifies {0} values; Double supports only one", x.Length)); } case "System.Double": switch (x.Length) { case 1: return(MathConverter.ConvertToDouble(x[0].Evaluate(values))); default: throw new NotSupportedException(string.Format("The parameter specifies {0} values; Double supports only one", x.Length)); } case "System.Windows.CornerRadius": switch (x.Length) { case 1: return(new CornerRadius(MathConverter.ConvertToDouble(x[0].Evaluate(values)).Value)); case 4: return(new CornerRadius(MathConverter.ConvertToDouble(x[0].Evaluate(values)).Value, MathConverter.ConvertToDouble(x[1].Evaluate(values)).Value, MathConverter.ConvertToDouble(x[2].Evaluate(values)).Value, MathConverter.ConvertToDouble(x[3].Evaluate(values)).Value)); default: throw new NotSupportedException(string.Format("The parameter specifies {0} values; CornerRadius supports only one or four", x.Length)); } case "System.Windows.GridLength": switch (x.Length) { case 1: return(new GridLength(MathConverter.ConvertToDouble(x[0].Evaluate(values)).Value)); default: throw new NotSupportedException(string.Format("The parameter specifies {0} values; GridLength supports only one", x.Length)); } case "System.Windows.Thickness": switch (x.Length) { case 1: return(new Thickness(MathConverter.ConvertToDouble(x[0].Evaluate(values)).Value)); case 2: return(new Thickness(MathConverter.ConvertToDouble(x[0].Evaluate(values)).Value, MathConverter.ConvertToDouble(x[1].Evaluate(values)).Value, MathConverter.ConvertToDouble(x[0].Evaluate(values)).Value, MathConverter.ConvertToDouble(x[1].Evaluate(values)).Value)); case 4: return(new Thickness(MathConverter.ConvertToDouble(x[0].Evaluate(values)).Value, MathConverter.ConvertToDouble(x[1].Evaluate(values)).Value, MathConverter.ConvertToDouble(x[2].Evaluate(values)).Value, MathConverter.ConvertToDouble(x[3].Evaluate(values)).Value)); default: throw new NotSupportedException(string.Format("The parameter specifies {0} values; Thickness supports only one, two, or four", x.Length)); } case "System.Windows.Rect": switch (x.Length) { case 4: return(new Rect(MathConverter.ConvertToDouble(x[0].Evaluate(values)).Value, MathConverter.ConvertToDouble(x[1].Evaluate(values)).Value, MathConverter.ConvertToDouble(x[2].Evaluate(values)).Value, MathConverter.ConvertToDouble(x[3].Evaluate(values)).Value)); default: throw new NotSupportedException(string.Format("The parameter specifies {0} values; Rect supports only four", x.Length)); } case "System.Windows.Size": switch (x.Length) { case 2: return(new Size(MathConverter.ConvertToDouble(x[0].Evaluate(values)).Value, MathConverter.ConvertToDouble(x[1].Evaluate(values)).Value)); default: throw new NotSupportedException(string.Format("You supplied {0} values; Size supports only two", x.Length)); } case "System.Windows.Point": switch (x.Length) { case 2: return(new Point(MathConverter.ConvertToDouble(x[0].Evaluate(values)).Value, MathConverter.ConvertToDouble(x[1].Evaluate(values)).Value)); default: throw new NotSupportedException(string.Format("You supplied {0} values; Point supports only two", x.Length)); } case "System.Boolean": switch (x.Length) { case 1: return((bool?)x[0].Evaluate(values)); default: throw new NotSupportedException(string.Format("You supplied {0} values; Boolean supports only one", x.Length)); } case "System.String": switch (x.Length) { case 1: var val = x[0].Evaluate(values); if (val is string) { return(val); } else if (val == null) { return(null); } else { return(val.ToString()); } default: throw new NotSupportedException(string.Format("You supplied {0} values; string supports only one", x.Length)); } default: if (targetType == typeof(double?)) { return(MathConverter.ConvertToDouble(x[0].Evaluate(values))); } // We don't know what to return, so let's just evaluate the parameter and return what it returns. return(x[0].Evaluate(values)); //throw new NotSupportedException(string.Format("You cannot convert to a {0}", targetType.Name)); } } else if (parameter == null) { switch (targetType.FullName) { case "System.Object": switch (values.Length) { case 1: return(ConvertToObject(values[0])); default: throw new NotSupportedException(string.Format("The parameter specifies {0} values; Object supports only one", values.Length)); } case "System.Double": switch (values.Length) { case 1: return(ConvertToDouble(values[0])); default: throw new NotSupportedException(string.Format("The parameter specifies {0} values; Double supports only one", values.Length)); } case "System.Windows.CornerRadius": switch (values.Length) { case 1: return(new CornerRadius(ConvertToDouble(values[0]).Value)); case 4: return(new CornerRadius(ConvertToDouble(values[0]).Value, ConvertToDouble(values[1]).Value, ConvertToDouble(values[2]).Value, ConvertToDouble(values[3]).Value)); default: throw new NotSupportedException(string.Format("You supplied {0} values; CornerRadius supports only one or four", values.Length)); } case "System.Windows.GridLength": switch (values.Length) { case 1: return(new GridLength(ConvertToDouble(values[0]).Value)); default: throw new NotSupportedException(string.Format("You supplied {0} values; GridLength supports only one", values.Length)); } case "System.Windows.Thickness": switch (values.Length) { case 1: return(new Thickness(ConvertToDouble(values[0]).Value)); case 2: return(new Thickness(ConvertToDouble(values[0]).Value, ConvertToDouble(values[1]).Value, ConvertToDouble(values[0]).Value, ConvertToDouble(values[1]).Value)); case 4: return(new Thickness(ConvertToDouble(values[0]).Value, ConvertToDouble(values[1]).Value, ConvertToDouble(values[2]).Value, ConvertToDouble(values[3]).Value)); default: throw new NotSupportedException(string.Format("You supplied {0} values; Thickness supports only one, two, or four", values.Length)); } case "System.Windows.Rect": switch (values.Length) { case 4: return(new Rect(ConvertToDouble(values[0]).Value, ConvertToDouble(values[1]).Value, ConvertToDouble(values[2]).Value, ConvertToDouble(values[3]).Value)); default: throw new NotSupportedException(string.Format("You supplied {0} values; Rect supports only four", values.Length)); } case "System.Windows.Size": switch (values.Length) { case 2: return(new Size(ConvertToDouble(values[0]).Value, ConvertToDouble(values[1]).Value)); default: throw new NotSupportedException(string.Format("You supplied {0} values; Size supports only two", values.Length)); } case "System.Windows.Point": switch (values.Length) { case 2: return(new Point(ConvertToDouble(values[0]).Value, ConvertToDouble(values[1]).Value)); default: throw new NotSupportedException(string.Format("You supplied {0} values; Point supports only two", values.Length)); } case "System.Boolean": switch (values.Length) { case 1: return((bool?)values[0]); default: throw new NotSupportedException(string.Format("You supplied {0} values; Boolean supports only one", values.Length)); } case "System.String": switch (values.Length) { case 1: if (values[0] is string) { return(values[0]); } else if (values[0] == null) { return(null); } else { return(values[0].ToString()); } default: throw new NotSupportedException(string.Format("You supplied {0} values; string supports only one", values.Length)); } default: if (targetType == typeof(double?)) { return(ConvertToDouble(values[0])); } throw new NotSupportedException(string.Format("You cannot convert to a {0}", targetType.Name)); } } else { throw new ArgumentException("The Converter Parameter must be a string.", "parameter"); } }