├── .gitignore ├── AssemblyInfo.cs ├── Constructs ├── FunctionOperation.cs ├── IConstruct.cs ├── Operation.cs └── Variable.cs ├── Converters ├── BooleanTypeConverter.cs ├── DateTimeTypeConverter.cs ├── NumberTypeConverter.cs └── TextTypeConverter.cs ├── Engine ├── Engine.cs ├── Expression.cs ├── ExpressionParseOnDemand.cs ├── ExpressionParsed.cs ├── Token.cs └── Tokenizer.cs ├── Functions ├── ArrayFunction.cs ├── Average.cs ├── Format.cs ├── Function.cs ├── If.cs ├── Len.cs ├── Max.cs ├── Min.cs ├── Sum.cs └── Today.cs ├── HiSystems.Interpreter.MonoTouch.csproj ├── HiSystems.Interpreter.csproj ├── HiSystems.Interpreter.sln ├── Library └── PeekableEnumerator.cs ├── Literals ├── Array.cs ├── Boolean.cs ├── DateTime.cs ├── Literal.cs ├── Number.cs └── Text.cs ├── Operators ├── AddOperator.cs ├── AndOperator.cs ├── DivideOperator.cs ├── EqualToOperator.cs ├── GreaterThanOperator.cs ├── GreaterThanOrEqualToOperator.cs ├── LessThanOperator.cs ├── LessThanOrEqualToOperator.cs ├── ModulusOperator.cs ├── MultiplyOperator.cs ├── NotEqualToOperator.cs ├── Operator.cs ├── OrOperator.cs └── SubtractOperator.cs └── readme.md /.gitignore: -------------------------------------------------------------------------------- 1 | 2 | #ignore thumbnails created by windows 3 | Thumbs.db 4 | #Ignore files build by Visual Studio 5 | *.obj 6 | *.exe 7 | *.pdb 8 | *.user 9 | *.aps 10 | *.pch 11 | *.vspscc 12 | *_i.c 13 | *_p.c 14 | *.ncb 15 | *.suo 16 | *.tlb 17 | *.tlh 18 | *.bak 19 | *.cache 20 | *.ilk 21 | *.log 22 | [Bb]in 23 | [Dd]ebug*/ 24 | *.lib 25 | *.sbr 26 | obj/ 27 | [Rr]elease*/ 28 | _ReSharper*/ 29 | [Tt]est[Rr]esult* 30 | -------------------------------------------------------------------------------- /AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | 5 | [assembly: AssemblyTitle("Interperter")] 6 | [assembly: AssemblyDescription("")] 7 | [assembly: AssemblyConfiguration("")] 8 | [assembly: AssemblyCompany("HiSystems")] 9 | [assembly: AssemblyProduct("HiSystems.Interperter")] 10 | [assembly: AssemblyCopyright("HiSystems Copyright © 2012")] 11 | [assembly: AssemblyTrademark("")] 12 | [assembly: AssemblyCulture("")] 13 | [assembly: ComVisible(false)] 14 | [assembly: AssemblyVersion("1.2.0.0")] 15 | [assembly: AssemblyFileVersion("1.2.0.0")] 16 | -------------------------------------------------------------------------------- /Constructs/FunctionOperation.cs: -------------------------------------------------------------------------------- 1 | /* _________________________________________________ 2 | 3 | (c) Hi-Integrity Systems 2012. All rights reserved. 4 | www.hisystems.com.au - Toby Wicks 5 | github.com/hisystems/Interpreter 6 | 7 | Licensed under the Apache License, Version 2.0 (the "License"); 8 | you may not use this file except in compliance with the License. 9 | You may obtain a copy of the License at 10 | 11 | http://www.apache.org/licenses/LICENSE-2.0 12 | 13 | Unless required by applicable law or agreed to in writing, software 14 | distributed under the License is distributed on an "AS IS" BASIS, 15 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | See the License for the specific language governing permissions and 17 | limitations under the License. 18 | ___________________________________________________ */ 19 | 20 | using System; 21 | using System.Collections.Generic; 22 | using System.Linq; 23 | using System.Text; 24 | 25 | namespace HiSystems.Interpreter 26 | { 27 | /// 28 | /// Performs the actual execution of a function, resolving and passing all of the function arguments. 29 | /// 30 | public class FunctionOperation : IConstruct 31 | { 32 | private Function function; 33 | private IConstruct[] arguments = null; 34 | 35 | public FunctionOperation(Function function, IConstruct[] arguments) 36 | { 37 | if (function == null) 38 | throw new ArgumentNullException(); 39 | 40 | this.function = function; 41 | this.arguments = arguments; 42 | } 43 | 44 | Literal IConstruct.Transform() 45 | { 46 | // Translation of the arguments does not occur here - it occurs in the Function. 47 | // Sometimes it is necessary that the arguments are not translated, for example by functions that interpret variables as meaning something else. 48 | 49 | return function.Execute(this.arguments); 50 | } 51 | 52 | /// 53 | /// The function that will be executed when this construct is transformed. 54 | /// 55 | public Function Function 56 | { 57 | get 58 | { 59 | return this.function; 60 | } 61 | } 62 | 63 | /// 64 | /// The arguments supplied to the function 65 | /// 66 | public IConstruct[] Arguments 67 | { 68 | get 69 | { 70 | return this.arguments; 71 | } 72 | } 73 | 74 | public override string ToString() 75 | { 76 | return this.function.ToString(); 77 | } 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /Constructs/IConstruct.cs: -------------------------------------------------------------------------------- 1 | /* _________________________________________________ 2 | 3 | (c) Hi-Integrity Systems 2012. All rights reserved. 4 | www.hisystems.com.au - Toby Wicks 5 | github.com/hisystems/Interpreter 6 | 7 | Licensed under the Apache License, Version 2.0 (the "License"); 8 | you may not use this file except in compliance with the License. 9 | You may obtain a copy of the License at 10 | 11 | http://www.apache.org/licenses/LICENSE-2.0 12 | 13 | Unless required by applicable law or agreed to in writing, software 14 | distributed under the License is distributed on an "AS IS" BASIS, 15 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | See the License for the specific language governing permissions and 17 | limitations under the License. 18 | ___________________________________________________ */ 19 | 20 | using System; 21 | using System.Collections.Generic; 22 | using System.Linq; 23 | using System.Text; 24 | 25 | namespace HiSystems.Interpreter 26 | { 27 | /// 28 | /// Interface for all constructs that return a value of some kind: 29 | /// function, literal, operation, variable, array. 30 | /// 31 | public interface IConstruct 32 | { 33 | /// 34 | /// Transforms/executes and returns the value from this construct: 35 | /// For Functions: executes the function and returns the result as a literal. 36 | /// For Operations: returns the result of the mathematical or logical operation and returns a literal (Number, Boolean for example). 37 | /// For Variables: returns the value from the variable's associated construct - i.e. calls Variable.Construct.Transform(). 38 | /// For Literals: returns the literal value i.e. itself. 39 | /// For Arrays: returns the array value, i.e itself. 40 | /// 41 | Literal Transform(); 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /Constructs/Operation.cs: -------------------------------------------------------------------------------- 1 | /* _________________________________________________ 2 | 3 | (c) Hi-Integrity Systems 2012. All rights reserved. 4 | www.hisystems.com.au - Toby Wicks 5 | github.com/hisystems/Interpreter 6 | 7 | Licensed under the Apache License, Version 2.0 (the "License"); 8 | you may not use this file except in compliance with the License. 9 | You may obtain a copy of the License at 10 | 11 | http://www.apache.org/licenses/LICENSE-2.0 12 | 13 | Unless required by applicable law or agreed to in writing, software 14 | distributed under the License is distributed on an "AS IS" BASIS, 15 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | See the License for the specific language governing permissions and 17 | limitations under the License. 18 | ___________________________________________________ */ 19 | 20 | 21 | using System; 22 | using System.Collections.Generic; 23 | using System.Linq; 24 | using System.Text; 25 | 26 | namespace HiSystems.Interpreter 27 | { 28 | /// 29 | /// Represents a left hand side value an operation and a right hand side value. 30 | /// 31 | public class Operation : IConstruct 32 | { 33 | private const int PrecedenceUndefined = -1; 34 | 35 | private IConstruct leftValue; 36 | private Operator @operator; 37 | private IConstruct rightValue; 38 | private int precedence = PrecedenceUndefined; 39 | 40 | internal Operation() 41 | { 42 | } 43 | 44 | /// 45 | /// Indicates the left hand side value, and right hand side values 46 | /// and the operation to be applied to the left hand side and right 47 | /// hand side values. 48 | /// 49 | public Operation(IConstruct leftValue, Operator operation, IConstruct rightValue) 50 | { 51 | if (leftValue == null || rightValue == null || operation == null) 52 | throw new ArgumentNullException(); 53 | 54 | this.leftValue = leftValue; 55 | this.@operator = operation; 56 | this.rightValue = rightValue; 57 | } 58 | 59 | Literal IConstruct.Transform () 60 | { 61 | return this.@operator.Execute(this.leftValue, this.rightValue); 62 | } 63 | 64 | /// 65 | /// Represents the relatively ordering precedence for this expression. 66 | /// The precedence is not an absolute value but a relative value. 67 | /// If two expressions have the same precedence then they are evaluated from left to right. 68 | /// The highest precedence value indicates that the expression will be executed first. 69 | /// 70 | internal int Precedence 71 | { 72 | get 73 | { 74 | return this.precedence; 75 | } 76 | 77 | set 78 | { 79 | if (value <= PrecedenceUndefined) 80 | throw new ArgumentException(value.ToString()); 81 | 82 | this.precedence = value; 83 | } 84 | } 85 | 86 | internal bool PrecedenceIsSet 87 | { 88 | get 89 | { 90 | return this.precedence != PrecedenceUndefined; 91 | } 92 | } 93 | 94 | /// 95 | /// The left-hand-side value that the operation will use. 96 | /// 97 | public IConstruct LeftValue 98 | { 99 | get 100 | { 101 | return this.leftValue; 102 | } 103 | 104 | internal set 105 | { 106 | if (value == null) 107 | throw new ArgumentNullException(); 108 | 109 | this.leftValue = value; 110 | } 111 | } 112 | 113 | /// 114 | /// The operator that will be executed when this operation is executed. 115 | /// 116 | public Operator Operator 117 | { 118 | get 119 | { 120 | return this.@operator; 121 | } 122 | 123 | internal set 124 | { 125 | if (value == null) 126 | throw new ArgumentNullException(); 127 | 128 | this.@operator = value; 129 | } 130 | } 131 | 132 | /// 133 | /// The right-hand-side value that the operation will use. 134 | /// 135 | public IConstruct RightValue 136 | { 137 | get 138 | { 139 | return this.rightValue; 140 | } 141 | 142 | internal set 143 | { 144 | if (value == null) 145 | throw new ArgumentNullException(); 146 | 147 | this.rightValue = value; 148 | } 149 | } 150 | 151 | public override string ToString() 152 | { 153 | return this.leftValue.ToString() + " " + this.@operator.Token + " " + this.rightValue.ToString(); 154 | } 155 | 156 | // /// 157 | // /// Returns all of the items in the expression tree (including this root node). 158 | // /// 159 | // public IConstruct[] GetAllItems() 160 | // { 161 | // var items = new List(); 162 | // 163 | // GetAllItems(items, this); 164 | // 165 | // return items.ToArray(); 166 | // } 167 | // 168 | // private void GetAllItems(List items, IConstruct value) 169 | // { 170 | // items.Add(value); 171 | // 172 | // if (value is Operation) 173 | // { 174 | // GetAllItems(items, ((Operation)value).LeftValue); 175 | // GetAllItems(items, ((Operation)value).RightValue); 176 | // } 177 | // } 178 | } 179 | } 180 | -------------------------------------------------------------------------------- /Constructs/Variable.cs: -------------------------------------------------------------------------------- 1 | /* _________________________________________________ 2 | 3 | (c) Hi-Integrity Systems 2012. All rights reserved. 4 | www.hisystems.com.au - Toby Wicks 5 | github.com/hisystems/Interpreter 6 | 7 | Licensed under the Apache License, Version 2.0 (the "License"); 8 | you may not use this file except in compliance with the License. 9 | You may obtain a copy of the License at 10 | 11 | http://www.apache.org/licenses/LICENSE-2.0 12 | 13 | Unless required by applicable law or agreed to in writing, software 14 | distributed under the License is distributed on an "AS IS" BASIS, 15 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | See the License for the specific language governing permissions and 17 | limitations under the License. 18 | ___________________________________________________ */ 19 | 20 | using System; 21 | using System.Collections.Generic; 22 | using System.Linq; 23 | using System.Text; 24 | 25 | namespace HiSystems.Interpreter 26 | { 27 | /// 28 | /// Represents a variable value that is resolved when first accessed. 29 | /// 30 | public class Variable : IConstruct 31 | { 32 | private string name; 33 | private IConstruct construct; 34 | 35 | /// 36 | /// 37 | public Variable(string name) 38 | : this(name, null) 39 | { 40 | } 41 | 42 | /// 43 | /// 44 | public Variable(string name, IConstruct value) 45 | { 46 | if (String.IsNullOrEmpty(name)) 47 | throw new ArgumentNullException("Name"); 48 | 49 | this.name = name; 50 | this.construct = value; 51 | } 52 | 53 | public string Name 54 | { 55 | get 56 | { 57 | return this.name; 58 | } 59 | } 60 | 61 | /// 62 | /// The associated literal value that should be associated with this variable. 63 | /// This value should be set before the IConstruct.Transform() function is called. 64 | /// 65 | public IConstruct Value 66 | { 67 | get 68 | { 69 | return this.construct; 70 | } 71 | 72 | set 73 | { 74 | if (value == null) 75 | throw new ArgumentNullException(); 76 | 77 | this.construct = value; 78 | } 79 | } 80 | 81 | Literal IConstruct.Transform() 82 | { 83 | if (this.construct == null) 84 | throw new InvalidOperationException(String.Format("Variable {0} has not been set", this.name)); 85 | 86 | return this.construct.Transform(); 87 | } 88 | 89 | public override string ToString() 90 | { 91 | return this.name + ": " + (this.construct == null ? String.Empty : this.construct.ToString()); 92 | } 93 | } 94 | } 95 | -------------------------------------------------------------------------------- /Converters/BooleanTypeConverter.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.ComponentModel; 3 | using System.Globalization; 4 | 5 | namespace HiSystems.Interpreter.Converters 6 | { 7 | public class BooleanTypeConverter : TypeConverter 8 | { 9 | public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType) 10 | { 11 | return sourceType == typeof(bool); 12 | } 13 | 14 | public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType) 15 | { 16 | return destinationType == typeof(Boolean) || destinationType == typeof(Literal); 17 | } 18 | 19 | public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value) 20 | { 21 | var boolean = value as Boolean; 22 | return boolean == null ? default(bool) : (bool)boolean; 23 | } 24 | 25 | public override object ConvertTo(ITypeDescriptorContext context, CultureInfo culture, object value, Type destinationType) 26 | { 27 | var boolean = (bool)value; 28 | return (Boolean)boolean; 29 | } 30 | } 31 | } -------------------------------------------------------------------------------- /Converters/DateTimeTypeConverter.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.ComponentModel; 3 | using System.Globalization; 4 | 5 | namespace HiSystems.Interpreter.Converters 6 | { 7 | public class DateTimeTypeConverter : TypeConverter 8 | { 9 | public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType) 10 | { 11 | return sourceType == typeof (System.DateTime); 12 | } 13 | 14 | public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType) 15 | { 16 | return destinationType == typeof(DateTime) || destinationType == typeof(Literal); ; 17 | } 18 | 19 | public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value) 20 | { 21 | var date = default(DateTime); 22 | if (value is System.DateTime) 23 | { 24 | date = (System.DateTime) value; 25 | } 26 | 27 | return new DateTime(date); 28 | } 29 | 30 | public override object ConvertTo(ITypeDescriptorContext context, CultureInfo culture, object value, Type destinationType) 31 | { 32 | var date = value as DateTime; 33 | if (date != null) 34 | { 35 | return (System.DateTime)date; 36 | } 37 | 38 | return default(System.DateTime); 39 | } 40 | } 41 | } -------------------------------------------------------------------------------- /Converters/NumberTypeConverter.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.ComponentModel; 4 | using System.Globalization; 5 | using System.Linq; 6 | 7 | namespace HiSystems.Interpreter.Converters 8 | { 9 | public class NumberTypeConverter : TypeConverter 10 | { 11 | private readonly IEnumerable _supportedTypes = new[] {typeof(int), typeof(double), typeof(decimal), typeof(Literal)}; 12 | 13 | public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType) 14 | { 15 | return _supportedTypes.Contains(destinationType); 16 | } 17 | 18 | public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType) 19 | { 20 | return sourceType == typeof(Number); 21 | } 22 | 23 | public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value) 24 | { 25 | return value == null ? new Number(0) : new Number((decimal)value); 26 | } 27 | 28 | public override object ConvertTo(ITypeDescriptorContext context, CultureInfo culture, object value, Type destinationType) 29 | { 30 | var number = value as Number; 31 | 32 | if (destinationType == typeof(int)) 33 | { 34 | return number == null ? default(int) : (int)(decimal)number; 35 | } 36 | 37 | if (destinationType == typeof(decimal)) 38 | { 39 | return number == null ? default(decimal) : (decimal)number; 40 | } 41 | 42 | return number == null ? default(double) : (double)number; 43 | } 44 | } 45 | } -------------------------------------------------------------------------------- /Converters/TextTypeConverter.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.ComponentModel; 3 | using System.Globalization; 4 | 5 | namespace HiSystems.Interpreter.Converters 6 | { 7 | public class TextTypeConverter : TypeConverter 8 | { 9 | public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType) 10 | { 11 | return sourceType == typeof(string); 12 | } 13 | 14 | public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType) 15 | { 16 | return destinationType == typeof(string) || destinationType == typeof(Literal); 17 | } 18 | 19 | public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value) 20 | { 21 | return new Text((value as string) ?? string.Empty); 22 | } 23 | 24 | public override object ConvertTo(ITypeDescriptorContext context, CultureInfo culture, object value, Type destinationType) 25 | { 26 | var text = value as Text; 27 | return text == null ? string.Empty : (string) text; 28 | } 29 | } 30 | } -------------------------------------------------------------------------------- /Engine/Engine.cs: -------------------------------------------------------------------------------- 1 | /* _________________________________________________ 2 | 3 | (c) Hi-Integrity Systems 2012. All rights reserved. 4 | www.hisystems.com.au - Toby Wicks 5 | github.com/hisystems/Interpreter 6 | 7 | Licensed under the Apache License, Version 2.0 (the "License"); 8 | you may not use this file except in compliance with the License. 9 | You may obtain a copy of the License at 10 | 11 | http://www.apache.org/licenses/LICENSE-2.0 12 | 13 | Unless required by applicable law or agreed to in writing, software 14 | distributed under the License is distributed on an "AS IS" BASIS, 15 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | See the License for the specific language governing permissions and 17 | limitations under the License. 18 | ___________________________________________________ */ 19 | 20 | using System; 21 | using System.Collections.Generic; 22 | using System.Linq; 23 | using System.Text; 24 | using System.Diagnostics; 25 | 26 | namespace HiSystems.Interpreter 27 | { 28 | /// 29 | /// The interpreter engine interprets single expressions that can contain; 30 | /// - variables 31 | /// - functions (custom functions also) 32 | /// - operators (mathematical / logical) 33 | /// - literals (numbers, dates, strings) 34 | /// - parentheses - for precedence 35 | /// The Parse function will interpret the expression and return an Expression object. 36 | /// The expression can be supplied with the appropriate variables. 37 | /// And then executed via the Expression.Execute() function. 38 | /// Example expressions: 39 | /// 'IF(A > B, A, B)' -- requires calling Expression.Variables["A"] = (Number)1; Expression.Variables["B"] = (Number)2; 40 | /// 'true or false' 41 | /// '(1 + 2) * 3 / 4' 42 | /// 43 | /// See readme.md for further examples. 44 | /// 45 | public class Engine 46 | { 47 | private class OperatorAndPrecedence 48 | { 49 | public Operator Operation; 50 | public int Precedence; 51 | } 52 | 53 | private abstract class TranslatedToken 54 | { 55 | } 56 | 57 | private class ConstructToken : TranslatedToken 58 | { 59 | private IConstruct value; 60 | 61 | public ConstructToken(IConstruct value) 62 | { 63 | if (value == null) 64 | throw new ArgumentNullException(); 65 | 66 | this.value = value; 67 | } 68 | 69 | public IConstruct Value 70 | { 71 | get 72 | { 73 | return this.value; 74 | } 75 | } 76 | 77 | public override string ToString() 78 | { 79 | return this.value.ToString(); 80 | } 81 | } 82 | 83 | private class OperatorToken : TranslatedToken 84 | { 85 | private Operator operation; 86 | 87 | public OperatorToken(Operator operation) 88 | { 89 | if (operation == null) 90 | throw new ArgumentNullException(); 91 | 92 | this.operation = operation; 93 | } 94 | 95 | public Operator Value 96 | { 97 | get 98 | { 99 | return this.operation; 100 | } 101 | } 102 | 103 | public override string ToString() 104 | { 105 | return this.operation.Token.ToString(); 106 | } 107 | } 108 | 109 | private class LeftParenthesisToken : TranslatedToken 110 | { 111 | public override string ToString() 112 | { 113 | return "("; 114 | } 115 | } 116 | 117 | private class RightParenthesisToken : TranslatedToken 118 | { 119 | public override string ToString() 120 | { 121 | return ")"; 122 | } 123 | } 124 | 125 | private class ReservedWord 126 | { 127 | /// 128 | /// The word that identifies the reserved word. 129 | /// 130 | public string Word; 131 | 132 | /// 133 | /// The construct that identifies the keyword. 134 | /// 135 | public IConstruct Construct; 136 | } 137 | 138 | /// 139 | /// 140 | /// 141 | /// The precendence level is a relative level and is used to indicate the relative. 142 | /// A higher integer value means that it is performed first / before other operations. 143 | /// Operations with the same precedence level are executed left to right. 144 | /// Any new operations should be added to this list so that the tokens can be appropriately parsed 145 | /// by the parsing engine. 146 | /// 147 | private static OperatorAndPrecedence[] allOperators = new [] { 148 | new OperatorAndPrecedence() { Operation = new MultiplyOperator(), Precedence = 6 }, 149 | new OperatorAndPrecedence() { Operation = new DivideOperator(), Precedence = 6 }, 150 | new OperatorAndPrecedence() { Operation = new ModulusOperator(), Precedence = 6 }, 151 | new OperatorAndPrecedence() { Operation = new AddOperator(), Precedence = 5 }, 152 | new OperatorAndPrecedence() { Operation = new SubtractOperator(), Precedence = 5 }, 153 | new OperatorAndPrecedence() { Operation = new LessThanOperator(), Precedence = 4 }, 154 | new OperatorAndPrecedence() { Operation = new LessThanOrEqualToOperator(), Precedence = 4 }, 155 | new OperatorAndPrecedence() { Operation = new GreaterThanOperator(), Precedence = 4 }, 156 | new OperatorAndPrecedence() { Operation = new GreaterThanOrEqualToOperator(), Precedence = 4 }, 157 | new OperatorAndPrecedence() { Operation = new EqualToOperator(), Precedence = 3 }, 158 | new OperatorAndPrecedence() { Operation = new NotEqualToOperator(), Precedence = 3 }, 159 | new OperatorAndPrecedence() { Operation = new AndOperator(), Precedence = 2 }, 160 | new OperatorAndPrecedence() { Operation = new OrOperator(), Precedence = 1 } 161 | }; 162 | 163 | /// 164 | /// Indicates which tokens of type Identifier need to be translated into special tokens. 165 | /// 166 | private static ReservedWord[] reservedWords = new [] 167 | { 168 | new ReservedWord() { Word = "true", Construct = new Boolean(true) }, 169 | new ReservedWord() { Word = "false", Construct = new Boolean(false) } 170 | }; 171 | 172 | /// 173 | /// 174 | private static int parenthesisPrecendence; 175 | private List allFunctions = new List(); 176 | 177 | static Engine() 178 | { 179 | // Parentheses have higher precedence than all operations 180 | parenthesisPrecendence = allOperators 181 | .Select(item => item.Precedence) 182 | .Max() + 1; 183 | } 184 | 185 | /// 186 | /// Creates a new engine that can be used to parse expressions. 187 | /// 188 | public Engine() 189 | { 190 | Register(new Sum()); 191 | Register(new Average()); 192 | Register(new If()); 193 | Register(new Format()); 194 | Register(new Max()); 195 | Register(new Min()); 196 | Register(new ArrayFunction()); 197 | Register(new Today()); 198 | Register(new Len()); 199 | } 200 | 201 | /// 202 | /// Registers the function, so that the function can be utilised by the engine. 203 | /// Must be called before Engine.Parse() otherwise the function will not be recognised. 204 | /// 205 | public void Register(Function function) 206 | { 207 | allFunctions.Add(function); 208 | } 209 | 210 | /// 211 | /// Parses the expression and prepares it for execution. 212 | /// The returned Expression can then be populated with variables if necessary 213 | /// and then executed via Expression.Execute(). 214 | /// 215 | public Expression Parse(string expression) 216 | { 217 | var variablesList = new List(); 218 | 219 | return new ExpressionParsed(expression, ParseToConstruct(expression, variablesList), variablesList); 220 | } 221 | 222 | /// 223 | /// Returns an expression that is not compiled, thereby deferring parsing/compilation into the future and decreasing load time. 224 | /// The expression is compiled on demand / just-in-time on the first call to Expression.Execute() or Expression.Variables. 225 | /// Any syntax errors with the expression will NOT be thrown from this function, only on the first call to Execute() or Variables. 226 | /// Usage of the returned Expression is identical to an Expression returned from Engine.Parse(). 227 | /// 228 | public Expression ParseOnDemand(string expression) 229 | { 230 | return new ExpressionParseOnDemand(ParseToConstruct, expression); 231 | } 232 | 233 | /// 234 | /// Parses the expression and prepares it for execution. 235 | /// 236 | private IConstruct ParseToConstruct(string expression, List currentVariables) 237 | { 238 | return GetConstructFromTokens(Tokenizer.Parse(expression), currentVariables); 239 | } 240 | 241 | /// 242 | /// Creates the construct from a set of tokens. 243 | /// This is used to parse an entire expression and also an expression from a function's argument. 244 | /// 245 | private IConstruct GetConstructFromTokens(List tokens, List currentVariables) 246 | { 247 | // Translate the tokens to meaningful tokens such as a variables, functions and operators 248 | // Unknown or unexpected tokens will cause an exception to be thrown 249 | var translatedTokens = TranslateTokens(tokens, currentVariables); 250 | 251 | // If there is only one item in the expression (i.e. a function or number and no operations) 252 | if (translatedTokens.Count == 1) 253 | { 254 | return ((ConstructToken)translatedTokens[0]).Value; 255 | } 256 | else 257 | { 258 | // Converts the tokens to the initial flat tree structure. 259 | // The tree structure is flat (one level) and each Expression node is returned in the list. 260 | var expressions = TranslateTokensToOperations(translatedTokens); 261 | 262 | // Using the parentheses from the translated tokens determine the expression ordering 263 | SetExpressionPrecedenceFromParentheses(expressions.GetEnumerator(), translatedTokens.GetEnumerator(), depth: 0); 264 | 265 | // Set the ordering precedence based on the 266 | SetExpressionPrecedenceFromOperators(expressions.GetEnumerator(), translatedTokens.GetEnumerator()); 267 | 268 | // Enumerate through the ordered nodes and branch tree appropriately 269 | return TranslateToTreeUsingPrecedence(expressions); 270 | } 271 | } 272 | 273 | /// 274 | /// Translates the tokens into meaningful functions, operations and values. 275 | /// 276 | private List TranslateTokens(List tokens, List currentVariables) 277 | { 278 | var translatedTokens = new List(); 279 | var tokensEnum = new PeekableEnumerator(tokens); 280 | 281 | while (tokensEnum.MoveNext()) 282 | { 283 | var token = tokensEnum.Current; 284 | 285 | switch (token.Type) 286 | { 287 | case TokenType.Number: 288 | translatedTokens.Add(new ConstructToken(Number.Parse(token.Value))); 289 | break; 290 | case TokenType.Identifier: 291 | var operationForTokenIdentifier = allOperators 292 | .Select(item => item.Operation) 293 | .SingleOrDefault(item => item.Token.Equals(token.Value)); 294 | 295 | if (operationForTokenIdentifier != null) 296 | translatedTokens.Add(new OperatorToken(operationForTokenIdentifier)); 297 | else 298 | translatedTokens.Add(new ConstructToken(TranslateIdentifierToken(tokensEnum, currentVariables))); 299 | break; 300 | case TokenType.LeftParenthesis: 301 | translatedTokens.Add(new LeftParenthesisToken()); 302 | break; 303 | case TokenType.RightParenthesis: 304 | translatedTokens.Add(new RightParenthesisToken()); 305 | break; 306 | case TokenType.Text: 307 | translatedTokens.Add(new ConstructToken(new Text(token.Value))); 308 | break; 309 | case TokenType.DateTime: 310 | translatedTokens.Add(new ConstructToken(new DateTime(System.DateTime.Parse(token.Value)))); 311 | break; 312 | case TokenType.Other: 313 | var operationForToken = allOperators 314 | .Select(item => item.Operation) 315 | .SingleOrDefault(item => item.Token.Equals(token.Value)); 316 | 317 | if (operationForToken != null) 318 | translatedTokens.Add(new OperatorToken(operationForToken)); 319 | else 320 | throw new InvalidOperationException(token.Value + " in an unknown operation"); 321 | 322 | break; 323 | default: 324 | throw new NotImplementedException(); 325 | } 326 | } 327 | 328 | return translatedTokens; 329 | } 330 | 331 | /// 332 | /// Translates the tokens to a set of operations. Each operation points to two child nodes of type ConstructToken. 333 | /// For example, 1 + 2 * 3 equates to 2 Operations: 334 | /// Operation1: LeftNode = 1, RightNode = 2. 335 | /// Operation2: LeftNode = 2, RightNode = 3, 336 | /// Expression 1 and 2 both link to the same Value (node 2). 337 | /// This link is eventually broken and re-linked as the tree structure is created basd on the operation ordering. 338 | /// 339 | /// 340 | /// Ignores processing of any other tokens that are not values or operations (parentheses for example) as 341 | /// they are required for the ordering aspect of the translation process. 342 | /// 343 | private static List TranslateTokensToOperations(List tokens) 344 | { 345 | var expectingOperation = false; 346 | var expressions = new List(); 347 | var currentExpression = new Operation(); 348 | 349 | foreach (var token in tokens) 350 | { 351 | if (token is ConstructToken) // function, variable or number 352 | { 353 | if (expectingOperation) 354 | throw new InvalidOperationException("Expecting operation not a value; " + token.ToString()); 355 | 356 | expectingOperation = true; // on next iteration an operator is expected 357 | } 358 | else if (token is OperatorToken) 359 | { 360 | if (!expectingOperation) 361 | throw new InvalidOperationException("Expecting value not an operation; " + token.ToString()); 362 | 363 | expectingOperation = false; // on next iteration an operator is not expected 364 | } 365 | 366 | if (token is ConstructToken) // function, variable or number 367 | { 368 | if (currentExpression.LeftValue == null) 369 | currentExpression.LeftValue = ((ConstructToken)token).Value; 370 | else if (currentExpression.RightValue == null) 371 | { 372 | currentExpression.RightValue = ((ConstructToken)token).Value; 373 | expressions.Add(currentExpression); 374 | currentExpression = new Operation(); 375 | currentExpression.LeftValue = ((ConstructToken)token).Value; 376 | } 377 | } 378 | else if (token is OperatorToken) 379 | { 380 | currentExpression.Operator = ((OperatorToken)token).Value; 381 | } 382 | } 383 | 384 | return expressions; 385 | } 386 | 387 | /// 388 | /// Sets the precedence of the operators based on the parentheses defined in the token sequence. 389 | /// Each time a left parenthesis is found, the depth incremented and precedence set to a multiple (parenthesisPrecendence * depth). 390 | /// This is because within each parenthesis the precedence is higher, but there still needs to be precedence for the operators. 391 | /// The precedence for the operators is set on a subsequent pass. 392 | /// For example: 1 + (2 + 3 * 4), items 3 and 4 must be given higher precedence than the 2 + 3 because of the order of operations. 393 | /// So, if parenthesisPrecendence is 10, + is 1, * is 2 then precedence for the operators + + * would be 1,11,12. 394 | /// For 1 + 2 + 3 * 4, then the precedence for the operators + + * would be 1,1,2. 395 | /// 396 | private static void SetExpressionPrecedenceFromParentheses(IEnumerator expressions, IEnumerator translatedTokens, int depth) 397 | { 398 | while (translatedTokens.MoveNext()) 399 | { 400 | var token = translatedTokens.Current; 401 | 402 | if (token is LeftParenthesisToken) 403 | { 404 | SetExpressionPrecedenceFromParentheses(expressions, translatedTokens, depth + 1); 405 | } 406 | else if (token is OperatorToken) 407 | { 408 | expressions.MoveNext(); 409 | // find the associated expression that is using the operator object instance 410 | var expression = expressions.Current; 411 | 412 | if (expression.PrecedenceIsSet) 413 | throw new InvalidOperationException("Expression precedence should not be set"); 414 | 415 | // Set the precedence explicitly considering this is the first pass: 'expression.PrecedenceSet == false' 416 | expression.Precedence = depth * parenthesisPrecendence; 417 | } 418 | else if (token is RightParenthesisToken) 419 | { 420 | break; 421 | } 422 | } 423 | 424 | if (depth > 0 && !(translatedTokens.Current is RightParenthesisToken)) 425 | throw new InvalidOperationException("Missing ending right parenthesis token"); 426 | } 427 | 428 | /// 429 | /// Sets the precedence of the operators based on the associated precedence. 430 | /// Assumes that previous passes (for the parentheses) have already set the precedence. 431 | /// Therefore, the value is incremented by the associated precedence. 432 | /// 433 | /// 434 | private static void SetExpressionPrecedenceFromOperators(IEnumerator expressions, IEnumerator translatedTokens) 435 | { 436 | while (translatedTokens.MoveNext()) 437 | { 438 | var token = translatedTokens.Current; 439 | 440 | if (token is OperatorToken) 441 | { 442 | expressions.MoveNext(); 443 | // find the associated expression that is using the operator object instance 444 | var expression = expressions.Current; 445 | expression.Precedence += allOperators.Where(item => item.Operation == ((OperatorToken)token).Value).Single().Precedence; 446 | } 447 | } 448 | } 449 | 450 | /// 451 | /// Builds the tree from a flattened tree with doubly linked nodes / items to a list a tree with one expression at the top. 452 | /// Basically, high precedence items are processed first and pushed to the bottom of the tree 453 | /// so that they are executed first (their results feed into the other expressions, which feeds into other results etc.). 454 | /// The tree is built based on the precedence value setup on previous passes. 455 | /// 456 | /// 457 | /// 458 | private static Operation TranslateToTreeUsingPrecedence(List expressions) 459 | { 460 | var expressionsOrdered = expressions 461 | .Select((item, index) => new { Expression = item, LeftToRightIndex = index }) 462 | .OrderByDescending(item => item.Expression.Precedence) 463 | .ThenBy(item => item.LeftToRightIndex) // for expressions with the same precedence order from left to right 464 | .Select(item => item.Expression) // remove the LeftToRightIndex now that it has been ordered correctly 465 | .GetEnumerator(); 466 | 467 | while (expressionsOrdered.MoveNext() && expressions.Count > 1) 468 | { 469 | var orderedExpression = expressionsOrdered.Current; 470 | var orderedExpressionIndex = expressions.IndexOf(orderedExpression); 471 | 472 | // If there is an expression before this expression in the normal left to right index 473 | // then get it to point to this expression rather than the Value node that is shared with another expression. 474 | // Effectively, the orderedExpression is "pushed" to the bottom of the tree 475 | if (orderedExpressionIndex > 0) 476 | { 477 | var previousExpression = expressions[orderedExpressionIndex - 1]; 478 | previousExpression.RightValue = orderedExpression; 479 | } 480 | 481 | // If there is an expression after this expression in the normal left to right index 482 | // then get it to point to this expression rather than the Value node that is shared with another expression. 483 | // Effectively, the orderedExpression is "pushed" to the bottom of the tree 484 | if (orderedExpressionIndex < expressions.Count - 1) 485 | { 486 | var nextExpression = expressions[orderedExpressionIndex + 1]; 487 | nextExpression.LeftValue = orderedExpression; 488 | } 489 | 490 | // this expression has been pushed to the bottom of the tree (for an upside down tree) 491 | expressions.Remove(orderedExpression); 492 | } 493 | 494 | return expressions[0]; 495 | } 496 | 497 | /// 498 | /// Enumerates through the tokens, searching for tokens XX,XX,XX where XX are arguments to the function and possibly a sub expression. 499 | /// When a ')' is found then the end of the arguments is presumed to have been found. 500 | /// 501 | /// Expected to be currently pointing to the name of the function. The next token SHOULD be a '(' 502 | private IConstruct[] GetFunctionArguments(PeekableEnumerator tokensEnum, List currentVariables) 503 | { 504 | var arguments = new List(); 505 | var functionName = tokensEnum.Current.Value; 506 | 507 | if (!(tokensEnum.MoveNext() && tokensEnum.Current.Type == TokenType.LeftParenthesis)) 508 | throw new InvalidOperationException(String.Format("{0} arguments; first token should be '(' not '{1}'", functionName, tokensEnum.Current.Value)); 509 | else if (tokensEnum.Current.Type == TokenType.LeftParenthesis && tokensEnum.CanPeek && tokensEnum.Peek.Type == TokenType.RightParenthesis) 510 | // No arguments were specified - empty parentheses were specified 511 | tokensEnum.MoveNext(); // consume the left parenthesis token and point it to the right parenthesis token - i.e. the end of the function 512 | else 513 | { 514 | bool reachedEndOfArguments = false; 515 | 516 | while (!reachedEndOfArguments) 517 | { 518 | arguments.Add(GetConstructFromTokens(GetFunctionArgumentTokens(functionName, tokensEnum, currentVariables), currentVariables)); 519 | 520 | // tokensEnum.Current will be the last token processed by GetFunctionArgumentTokens() 521 | if (tokensEnum.Current.Type == TokenType.RightParenthesis) 522 | reachedEndOfArguments = true; 523 | } 524 | } 525 | 526 | return arguments.ToArray(); 527 | } 528 | 529 | /// 530 | /// Gets the function's next argument's tokens by traversing the tokens until the next , or ) is found (which is not within a function). 531 | /// Does not return the , or ) character that terminated the argument expression - it is also consumed. 532 | /// 533 | /// Only used in order to provide useful exceptions / errors. 534 | /// Should be pointing to the token that indicates the start of a function argument; either a ( or , character. 535 | private static List GetFunctionArgumentTokens(string functionName, PeekableEnumerator tokensEnum, List currentVariables) 536 | { 537 | var argumentTokens = new List (); 538 | 539 | int functionDepth = 0; 540 | bool reachedEndOfArgument = false; 541 | 542 | while (!reachedEndOfArgument && tokensEnum.MoveNext()) 543 | { 544 | var token = tokensEnum.Current; 545 | 546 | // found the argument's terminating comma or right parenthesis 547 | if (functionDepth == 0 && (token.Type == TokenType.Comma || token.Type == TokenType.RightParenthesis)) 548 | reachedEndOfArgument = true; 549 | else 550 | { 551 | argumentTokens.Add(token); 552 | 553 | if (token.Type == TokenType.LeftParenthesis) 554 | functionDepth++; 555 | else if (token.Type == TokenType.RightParenthesis) 556 | functionDepth--; 557 | } 558 | } 559 | 560 | if (argumentTokens.Count == 0) 561 | throw new InvalidOperationException(String.Format("{0} has an empty argument", functionName)); 562 | else if (!reachedEndOfArgument) 563 | throw new InvalidOperationException(String.Format("{0} is missing a terminating argument character; ',' or ')'", functionName)); 564 | 565 | return argumentTokens; 566 | } 567 | 568 | /// 569 | /// Translates an identifier as either a function, variable name or key word. 570 | /// A function will match a registered function name and have a left parenthesis token following it, otherwise it is a variable. 571 | /// 572 | private IConstruct TranslateIdentifierToken (PeekableEnumerator tokensEnum, List currentVariables) 573 | { 574 | var identifierToken = tokensEnum.Current; 575 | 576 | var reservedWordForToken = reservedWords.SingleOrDefault(reserverWord => identifierToken == reserverWord.Word); 577 | 578 | if (reservedWordForToken != null) 579 | { 580 | return reservedWordForToken.Construct; 581 | } 582 | else 583 | { 584 | if (tokensEnum.CanPeek && tokensEnum.Peek.Type == TokenType.LeftParenthesis) 585 | { 586 | var functionForToken = allFunctions.SingleOrDefault(aFunction => identifierToken == aFunction.Name); 587 | 588 | if (functionForToken == null) 589 | throw new InvalidOperationException(String.Format("Function '{0}' is undefined", identifierToken)); 590 | else 591 | return new FunctionOperation(functionForToken, GetFunctionArguments(tokensEnum, currentVariables)); 592 | } 593 | else 594 | { 595 | // ensure there is only one Variable instance for the same variable name 596 | var variable = currentVariables.SingleOrDefault(aVariable => identifierToken == aVariable.Name); 597 | 598 | if (variable == null) 599 | { 600 | var newVariable = new Variable(tokensEnum.Current.Value); 601 | currentVariables.Add(newVariable); 602 | return newVariable; 603 | } 604 | else 605 | return variable; 606 | } 607 | } 608 | } 609 | } 610 | } 611 | -------------------------------------------------------------------------------- /Engine/Expression.cs: -------------------------------------------------------------------------------- 1 | /* _________________________________________________ 2 | 3 | (c) Hi-Integrity Systems 2012. All rights reserved. 4 | www.hisystems.com.au - Toby Wicks 5 | github.com/hisystems/Interpreter 6 | 7 | Licensed under the Apache License, Version 2.0 (the "License"); 8 | you may not use this file except in compliance with the License. 9 | You may obtain a copy of the License at 10 | 11 | http://www.apache.org/licenses/LICENSE-2.0 12 | 13 | Unless required by applicable law or agreed to in writing, software 14 | distributed under the License is distributed on an "AS IS" BASIS, 15 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | See the License for the specific language governing permissions and 17 | limitations under the License. 18 | ___________________________________________________ */ 19 | 20 | using System; 21 | using System.Collections.Generic; 22 | using System.Linq; 23 | using System.Text; 24 | 25 | namespace HiSystems.Interpreter 26 | { 27 | /// 28 | /// Represents an expression and the variables defined in the expression. 29 | /// This is the base class for an already compiled expression or a just-in-time compiled expression. 30 | /// 31 | public abstract class Expression : IConstruct 32 | { 33 | /// 34 | /// Only used for error reporting and debugging as the ToString return value. 35 | /// 36 | private string expression; 37 | 38 | /// 39 | /// Initializes a new instance of the class. 40 | /// 41 | /// 42 | /// Root construct in the expression tree. Calling IConstruct.Transform will return the actual value. 43 | /// 44 | internal Expression(string expression) 45 | { 46 | if (String.IsNullOrEmpty(expression)) 47 | throw new ArgumentNullException(); 48 | 49 | this.expression = expression; 50 | } 51 | 52 | /// 53 | /// Returns the calculated value for the expression. 54 | /// Any variables should be set before calling this function. 55 | /// Will typically return a Number or Boolean literal value (depending on the type of expression). 56 | /// 57 | public abstract Literal Execute(); 58 | 59 | /// 60 | /// Returns the calculated value for the expression. 61 | /// Any variables should be set before calling this function. 62 | /// Casts the return value to type T (of type Literal) 63 | /// 64 | public T Execute() where T : Literal 65 | { 66 | Literal result = this.Execute(); 67 | 68 | if (!(result is T)) 69 | throw new InvalidCastException(String.Format("Return value from '{0}' is of type {1}, not of type {2}", this.expression, result.GetType().Name, typeof(T).Name)); 70 | 71 | return (T)result; 72 | } 73 | 74 | /// 75 | /// Returns a dictionary containing all of the variables that were defined in the expression. 76 | /// If a variable is defined in multiple locations only one variable object is available in the dictionary. 77 | /// Variables are tokens/identifiers that could not be resolved to an operator or function name. 78 | /// Each variable should be assigned a value i.e: Variables["MyVariable"].Literal = (Number)1; 79 | /// 80 | public abstract IDictionary Variables { get; } 81 | 82 | /// 83 | /// The original / source expression which this expression represents. 84 | /// 85 | protected string Source 86 | { 87 | get 88 | { 89 | return this.expression; 90 | } 91 | } 92 | 93 | /// 94 | /// Converts a string value to an Expression that when Execute()'d will return the same Text literal value. 95 | /// 96 | public static implicit operator Expression(string stringLiteral) 97 | { 98 | return new ExpressionParsed("\"" + stringLiteral + "\"", new Text(stringLiteral), new List()); 99 | } 100 | 101 | /// 102 | /// Converts a bool value to an Expression that when Execute()'d will return the same bool literal value. 103 | /// 104 | public static implicit operator Expression(bool value) 105 | { 106 | return new ExpressionParsed(value.ToString(), new Boolean(value), new List()); 107 | } 108 | 109 | /// Returns a distinct list of variables from the expression. 110 | /// 111 | protected static IDictionary TranslateVariablesToDictionary(List variables) 112 | { 113 | return variables.ToDictionary(variable => variable.Name, variable => variable); 114 | } 115 | 116 | /// 117 | /// Allows for an expression to also be considered as a construct. 118 | /// Used when an expression references an identifier which is also a separate expression. 119 | /// Allowing an expression to reference a chain of expressions. 120 | /// 121 | Literal IConstruct.Transform() 122 | { 123 | return this.Execute(); 124 | } 125 | 126 | public override string ToString() 127 | { 128 | return expression; 129 | } 130 | } 131 | } 132 | -------------------------------------------------------------------------------- /Engine/ExpressionParseOnDemand.cs: -------------------------------------------------------------------------------- 1 | /* _________________________________________________ 2 | 3 | (c) Hi-Integrity Systems 2012. All rights reserved. 4 | www.hisystems.com.au - Toby Wicks 5 | github.com/hisystems/Interpreter 6 | 7 | Licensed under the Apache License, Version 2.0 (the "License"); 8 | you may not use this file except in compliance with the License. 9 | You may obtain a copy of the License at 10 | 11 | http://www.apache.org/licenses/LICENSE-2.0 12 | 13 | Unless required by applicable law or agreed to in writing, software 14 | distributed under the License is distributed on an "AS IS" BASIS, 15 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | See the License for the specific language governing permissions and 17 | limitations under the License. 18 | ___________________________________________________ */ 19 | 20 | using System; 21 | using System.Collections.Generic; 22 | 23 | namespace HiSystems.Interpreter 24 | { 25 | /// 26 | /// Represents an expression where compiling / parsing is deferred to when required. 27 | /// 28 | internal class ExpressionParseOnDemand : Expression 29 | { 30 | internal delegate IConstruct ParseToConstructDelegate(string expression, List variables); 31 | 32 | /// 33 | /// The callback that will convert the string expression to a construct when required. 34 | /// 35 | private ParseToConstructDelegate parseToConstruct; 36 | 37 | /// 38 | /// The root construct of the expression tree. 39 | /// When null indicates that the expression has not been parsed. 40 | /// 41 | private IConstruct construct = null; 42 | 43 | /// 44 | /// All of the variables defined in the expression. 45 | /// 46 | private IDictionary variables; 47 | 48 | public ExpressionParseOnDemand(ParseToConstructDelegate parseToConstruct, string expression) 49 | : base(expression) 50 | { 51 | if (parseToConstruct == null) 52 | throw new ArgumentNullException(); 53 | 54 | this.parseToConstruct = parseToConstruct; 55 | } 56 | 57 | /// 58 | /// Returns the calculated value for the expression. 59 | /// Any variables should be set before calling this function. 60 | /// Will typically return a Number or Boolean literal value (depending on the type of expression). 61 | /// 62 | public override Literal Execute() 63 | { 64 | EnsureExpressionParsed(); 65 | 66 | return construct.Transform(); 67 | } 68 | 69 | /// 70 | /// Returns a dictionary containing all of the variables that were defined in the expression. 71 | /// If a variable is defined in multiple locations only one variable object is available in the dictionary. 72 | /// Variables are tokens/identifiers that could not be resolved to an operator or function name. 73 | /// Each variable should be assigned a value i.e: Variables["MyVariable"].Literal = (Number)1; 74 | /// 75 | public override IDictionary Variables 76 | { 77 | get 78 | { 79 | EnsureExpressionParsed(); 80 | 81 | return this.variables; 82 | } 83 | } 84 | 85 | /// 86 | /// Parses the expression if it has not yet been parsed. 87 | /// 88 | private void EnsureExpressionParsed() 89 | { 90 | if (this.construct == null) 91 | { 92 | var variablesList = new List(); 93 | this.construct = this.parseToConstruct(base.Source, variablesList); 94 | this.variables = TranslateVariablesToDictionary(variablesList); 95 | } 96 | } 97 | } 98 | } -------------------------------------------------------------------------------- /Engine/ExpressionParsed.cs: -------------------------------------------------------------------------------- 1 | /* _________________________________________________ 2 | 3 | (c) Hi-Integrity Systems 2012. All rights reserved. 4 | www.hisystems.com.au - Toby Wicks 5 | github.com/hisystems/Interpreter 6 | 7 | Licensed under the Apache License, Version 2.0 (the "License"); 8 | you may not use this file except in compliance with the License. 9 | You may obtain a copy of the License at 10 | 11 | http://www.apache.org/licenses/LICENSE-2.0 12 | 13 | Unless required by applicable law or agreed to in writing, software 14 | distributed under the License is distributed on an "AS IS" BASIS, 15 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | See the License for the specific language governing permissions and 17 | limitations under the License. 18 | ___________________________________________________ */ 19 | 20 | using System; 21 | using System.Collections.Generic; 22 | 23 | namespace HiSystems.Interpreter 24 | { 25 | /// 26 | /// Represents an expression that has been parsed and can be executed immediately without compilation/parsing. 27 | /// 28 | internal class ExpressionParsed : Expression 29 | { 30 | /// 31 | /// The root construct of the expression tree. 32 | /// 33 | private IConstruct construct; 34 | 35 | /// 36 | /// All of the variables defined in the expression. 37 | /// 38 | private IDictionary variables; 39 | 40 | internal ExpressionParsed(string expression, IConstruct value, List variables) 41 | : base(expression) 42 | { 43 | if (value == null) 44 | throw new ArgumentNullException(); 45 | 46 | this.construct = value; 47 | this.variables = TranslateVariablesToDictionary(variables); 48 | } 49 | 50 | /// 51 | /// Returns the calculated value for the expression. 52 | /// Any variables should be set before calling this function. 53 | /// Will typically return a Number or Boolean literal value (depending on the type of expression). 54 | /// 55 | public override Literal Execute() 56 | { 57 | return construct.Transform(); 58 | } 59 | 60 | /// 61 | /// Returns a dictionary containing all of the variables that were defined in the expression. 62 | /// If a variable is defined in multiple locations only one variable object is available in the dictionary. 63 | /// Variables are tokens/identifiers that could not be resolved to an operator or function name. 64 | /// Each variable should be assigned a value i.e: Variables["MyVariable"].Literal = (Number)1; 65 | /// 66 | public override IDictionary Variables 67 | { 68 | get 69 | { 70 | return this.variables; 71 | } 72 | } 73 | } 74 | } 75 | 76 | -------------------------------------------------------------------------------- /Engine/Token.cs: -------------------------------------------------------------------------------- 1 | /* _________________________________________________ 2 | 3 | (c) Hi-Integrity Systems 2012. All rights reserved. 4 | www.hisystems.com.au - Toby Wicks 5 | github.com/hisystems/Interpreter 6 | 7 | Licensed under the Apache License, Version 2.0 (the "License"); 8 | you may not use this file except in compliance with the License. 9 | You may obtain a copy of the License at 10 | 11 | http://www.apache.org/licenses/LICENSE-2.0 12 | 13 | Unless required by applicable law or agreed to in writing, software 14 | distributed under the License is distributed on an "AS IS" BASIS, 15 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | See the License for the specific language governing permissions and 17 | limitations under the License. 18 | ___________________________________________________ */ 19 | 20 | using System; 21 | using System.Collections.Generic; 22 | using System.Linq; 23 | using System.Text; 24 | 25 | namespace HiSystems.Interpreter 26 | { 27 | internal enum TokenType 28 | { 29 | /// 30 | /// Number 31 | /// 32 | Number, 33 | 34 | /// 35 | /// Function, variable name or reserved word 36 | /// 37 | Identifier, 38 | 39 | /// 40 | /// '(' character. 41 | /// 42 | LeftParenthesis, 43 | 44 | /// 45 | /// ')' character. 46 | /// 47 | RightParenthesis, 48 | 49 | /// 50 | /// ',' character. 51 | /// 52 | Comma, 53 | 54 | /// 55 | /// String literal, surrounded/delimited by a " character 56 | /// 57 | Text, 58 | 59 | /// 60 | /// Only used during parsing - the tokenizer will not return a whitespace token. 61 | /// 62 | Whitespace, 63 | 64 | /// 65 | /// Value is surrounded by # characters. 66 | /// 67 | DateTime, 68 | 69 | /// 70 | /// Any other token that is not one of the other token types specified in this enum. 71 | /// Usually a special character such as '*' or '^'. 72 | /// 73 | Other 74 | } 75 | 76 | /// 77 | /// Represents a character or string that is a unique token type. 78 | /// 79 | internal class Token 80 | { 81 | private string value; 82 | private TokenType type; 83 | 84 | public Token(string token, TokenType type) 85 | { 86 | if (token == null) 87 | throw new ArgumentNullException(); 88 | 89 | this.value = token; 90 | this.type = type; 91 | } 92 | 93 | public Token() 94 | { 95 | this.value = String.Empty; 96 | this.type = TokenType.Other; 97 | } 98 | 99 | public Token(char token, TokenType type) 100 | { 101 | this.value = token.ToString(); 102 | this.type = type; 103 | } 104 | 105 | internal string Value 106 | { 107 | get 108 | { 109 | return this.value; 110 | } 111 | } 112 | 113 | internal TokenType Type 114 | { 115 | get 116 | { 117 | return this.type; 118 | } 119 | } 120 | 121 | public override string ToString() 122 | { 123 | return this.value; 124 | } 125 | 126 | public override bool Equals(Object value) 127 | { 128 | if (value is string) 129 | return AreEqual(this, (string)value); 130 | else 131 | return false; 132 | } 133 | 134 | public static bool operator ==(Token token1, string token2) 135 | { 136 | return AreEqual(token1, token2); 137 | } 138 | 139 | public static bool operator !=(Token token1, string token2) 140 | { 141 | return !AreEqual(token1, token2); 142 | } 143 | 144 | private static bool AreEqual(Token token1, string token2) 145 | { 146 | if (Object.Equals(token1, null) && Object.Equals(token2, null)) 147 | return true; 148 | else if (Object.Equals(token1, null) || Object.Equals(token2, null)) 149 | return false; 150 | else 151 | return token1.value.Equals(token2, StringComparison.InvariantCultureIgnoreCase); 152 | } 153 | 154 | public override int GetHashCode () 155 | { 156 | return base.GetHashCode (); 157 | } 158 | } 159 | } 160 | -------------------------------------------------------------------------------- /Engine/Tokenizer.cs: -------------------------------------------------------------------------------- 1 | /* _________________________________________________ 2 | 3 | (c) Hi-Integrity Systems 2012. All rights reserved. 4 | www.hisystems.com.au - Toby Wicks 5 | github.com/hisystems/Interpreter 6 | 7 | Licensed under the Apache License, Version 2.0 (the "License"); 8 | you may not use this file except in compliance with the License. 9 | You may obtain a copy of the License at 10 | 11 | http://www.apache.org/licenses/LICENSE-2.0 12 | 13 | Unless required by applicable law or agreed to in writing, software 14 | distributed under the License is distributed on an "AS IS" BASIS, 15 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | See the License for the specific language governing permissions and 17 | limitations under the License. 18 | ___________________________________________________ */ 19 | 20 | using System; 21 | using System.Collections.Generic; 22 | using System.Linq; 23 | using System.Text; 24 | 25 | namespace HiSystems.Interpreter 26 | { 27 | internal static class Tokenizer 28 | { 29 | /// 30 | /// Preliminary tokenization of the expression. 31 | /// Tokenizes numeric values, alpha values, parentheses, commas and other tokens. 32 | /// Any whitespace is removed. 33 | /// 34 | public static List Parse(string expression) 35 | { 36 | const char LeftParenthesis = '('; 37 | const char RightParenthesis = ')'; 38 | const char Comma = ','; 39 | const char NumericNegative = '-'; 40 | const char DateTimeDelimiter = '#'; 41 | 42 | var whitespaceCharacters = new[] { ' ', '\t' }; 43 | var numericCharacters = new[] { '.', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9' }; 44 | var identifierCharacters = new[] { '_', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z' }; 45 | var identifierSecondaryCharacters = new[] { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9' }; // other characters that can be used as identifiers - but cannot be a starting character 46 | var textDelimiters = new[] { '\"', '\'' }; 47 | bool isNumericNegative = false; 48 | bool parsingText = false; 49 | bool parsingDateTime = false; 50 | 51 | var tokens = new List(); 52 | var currentTokenType = TokenType.Other; 53 | var currentToken = String.Empty; 54 | char currentTextDelimiter = '\0'; 55 | var characterTokenType = TokenType.Other; 56 | var expressionEnumerator = new PeekableEnumerator(expression); 57 | var characterString = String.Empty; 58 | 59 | while (expressionEnumerator.MoveNext()) 60 | { 61 | var tokenIsSeparateCharacter = false; 62 | var character = expressionEnumerator.Current; 63 | 64 | // if the character is a '-' and the subsequent character is a numeric character then this is a negative number. 65 | // otherwise it is some other character TokenType.Other -- probably a subtraction operator. 66 | isNumericNegative = character == NumericNegative && expressionEnumerator.CanPeek && numericCharacters.Contains(expressionEnumerator.Peek); 67 | 68 | if (textDelimiters.Contains(character) || parsingText) 69 | { 70 | if (textDelimiters.Contains(character) && !parsingText) // started parsing 71 | { 72 | characterTokenType = TokenType.Text; 73 | characterString = String.Empty; // consume character 74 | currentTextDelimiter = character; 75 | parsingText = true; 76 | } 77 | else if (character == currentTextDelimiter && parsingText) // finished parsing 78 | { 79 | characterString = String.Empty; // consume character 80 | parsingText = false; 81 | } 82 | else 83 | characterString = character.ToString(); 84 | } 85 | else if (character == DateTimeDelimiter || parsingDateTime) 86 | { 87 | if (!parsingDateTime) // started parsing 88 | { 89 | characterTokenType = TokenType.DateTime; 90 | characterString = String.Empty; // consume character 91 | parsingDateTime = true; 92 | } 93 | else if (character == DateTimeDelimiter) // finished parsing 94 | { 95 | characterString = String.Empty; // consume character 96 | parsingDateTime = false; 97 | } 98 | else 99 | characterString = character.ToString(); 100 | } 101 | else if (whitespaceCharacters.Contains(character)) 102 | { 103 | characterTokenType = TokenType.Whitespace; 104 | characterString = String.Empty; // consume character 105 | } 106 | else if (identifierCharacters.Contains(character) || (currentTokenType == TokenType.Identifier && identifierSecondaryCharacters.Contains(character))) 107 | { 108 | characterTokenType = TokenType.Identifier; 109 | characterString = character.ToString(); 110 | } 111 | else if (numericCharacters.Contains(character) || isNumericNegative) 112 | { 113 | characterTokenType = TokenType.Number; 114 | characterString = character.ToString(); 115 | } 116 | else if (character == LeftParenthesis) 117 | { 118 | characterTokenType = TokenType.LeftParenthesis; 119 | characterString = character.ToString(); 120 | tokenIsSeparateCharacter = true; 121 | } 122 | else if (character == RightParenthesis) 123 | { 124 | characterTokenType = TokenType.RightParenthesis; 125 | characterString = character.ToString(); 126 | tokenIsSeparateCharacter = true; 127 | } 128 | else if (character == Comma) 129 | { 130 | characterTokenType = TokenType.Comma; 131 | characterString = character.ToString(); 132 | tokenIsSeparateCharacter = true; 133 | } 134 | else 135 | { 136 | characterTokenType = TokenType.Other; 137 | characterString = character.ToString(); 138 | } 139 | 140 | if (currentTokenType == characterTokenType && !tokenIsSeparateCharacter) 141 | currentToken += characterString; 142 | else 143 | { 144 | if (currentToken.Length > 0 || currentTokenType == TokenType.Text) 145 | tokens.Add(new Token(currentToken, currentTokenType)); 146 | 147 | currentToken = characterString; 148 | currentTokenType = characterTokenType; 149 | } 150 | } 151 | 152 | if (currentToken.Length > 0 || currentTokenType == TokenType.Text) 153 | tokens.Add(new Token(currentToken, currentTokenType)); 154 | 155 | return tokens; 156 | } 157 | } 158 | } 159 | -------------------------------------------------------------------------------- /Functions/ArrayFunction.cs: -------------------------------------------------------------------------------- 1 | /* _________________________________________________ 2 | 3 | (c) Hi-Integrity Systems 2012. All rights reserved. 4 | www.hisystems.com.au - Toby Wicks 5 | github.com/hisystems/Interpreter 6 | 7 | Licensed under the Apache License, Version 2.0 (the "License"); 8 | you may not use this file except in compliance with the License. 9 | You may obtain a copy of the License at 10 | 11 | http://www.apache.org/licenses/LICENSE-2.0 12 | 13 | Unless required by applicable law or agreed to in writing, software 14 | distributed under the License is distributed on an "AS IS" BASIS, 15 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | See the License for the specific language governing permissions and 17 | limitations under the License. 18 | ___________________________________________________ */ 19 | 20 | using System; 21 | using System.Collections.Generic; 22 | using System.Linq; 23 | using System.Text; 24 | 25 | namespace HiSystems.Interpreter 26 | { 27 | /// 28 | /// Returns an array from a list of items. 29 | /// Usage: Array(item1, item2, ...) 30 | /// Example: Array(1, 2, 3) 31 | /// 32 | public class ArrayFunction : Function 33 | { 34 | public override string Name 35 | { 36 | get 37 | { 38 | return "Array"; 39 | } 40 | } 41 | 42 | public override Literal Execute(IConstruct[] arguments) 43 | { 44 | base.EnsureArgumentCountIsAtLeast(arguments, 1); 45 | 46 | return new Array(arguments.Select(argument => argument.Transform()).ToArray()); 47 | } 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /Functions/Average.cs: -------------------------------------------------------------------------------- 1 | /* _________________________________________________ 2 | 3 | (c) Hi-Integrity Systems 2012. All rights reserved. 4 | www.hisystems.com.au - Toby Wicks 5 | github.com/hisystems/Interpreter 6 | 7 | Licensed under the Apache License, Version 2.0 (the "License"); 8 | you may not use this file except in compliance with the License. 9 | You may obtain a copy of the License at 10 | 11 | http://www.apache.org/licenses/LICENSE-2.0 12 | 13 | Unless required by applicable law or agreed to in writing, software 14 | distributed under the License is distributed on an "AS IS" BASIS, 15 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | See the License for the specific language governing permissions and 17 | limitations under the License. 18 | ___________________________________________________ */ 19 | 20 | using System; 21 | using System.Collections.Generic; 22 | using System.Linq; 23 | using System.Text; 24 | 25 | namespace HiSystems.Interpreter 26 | { 27 | /// 28 | /// Accepts one argument of type Array containing objects of type Number. 29 | /// Usage: AVG(array) 30 | /// Example: Avg(Array(1, 2, 3)) 31 | /// 32 | public class Average : Function 33 | { 34 | public override string Name 35 | { 36 | get 37 | { 38 | return "AVG"; 39 | } 40 | } 41 | 42 | public override Literal Execute(IConstruct[] arguments) 43 | { 44 | base.EnsureArgumentCountIs(arguments, 1); 45 | 46 | return new Number(base.GetTransformedArgumentDecimalArray(arguments, argumentIndex:0).Average()); 47 | } 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /Functions/Format.cs: -------------------------------------------------------------------------------- 1 | /* _________________________________________________ 2 | 3 | (c) Hi-Integrity Systems 2012. All rights reserved. 4 | www.hisystems.com.au - Toby Wicks 5 | github.com/hisystems/Interpreter 6 | 7 | Licensed under the Apache License, Version 2.0 (the "License"); 8 | you may not use this file except in compliance with the License. 9 | You may obtain a copy of the License at 10 | 11 | http://www.apache.org/licenses/LICENSE-2.0 12 | 13 | Unless required by applicable law or agreed to in writing, software 14 | distributed under the License is distributed on an "AS IS" BASIS, 15 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | See the License for the specific language governing permissions and 17 | limitations under the License. 18 | ___________________________________________________ */ 19 | 20 | using System; 21 | using System.Collections.Generic; 22 | using System.Linq; 23 | using System.Text; 24 | 25 | namespace HiSystems.Interpreter 26 | { 27 | /// 28 | /// The format for numeric values utilises the standard or custom numeric string formats. 29 | /// If format is omitted then the value is converted to the most appropriate string representation. 30 | /// Usage: Format(value [, format]) 31 | /// Example: Format(1, '0.0') 32 | /// 33 | public class Format : Function 34 | { 35 | public override string Name 36 | { 37 | get 38 | { 39 | return "Format"; 40 | } 41 | } 42 | 43 | public override Literal Execute(IConstruct[] arguments) 44 | { 45 | base.EnsureArgumentCountIsBetween(arguments, 1, 2); 46 | 47 | var value = base.GetTransformedArgument(arguments, argumentIndex: 0); 48 | 49 | string format; 50 | 51 | if (arguments.Length > 1) 52 | format = base.GetTransformedArgument(arguments, argumentIndex: 1); 53 | else 54 | format = null; 55 | 56 | if (value is Number) 57 | { 58 | if (format == null) 59 | return (Text)((Number)value).ToString(); 60 | else 61 | return (Text)((Number)value).ToString(format); 62 | } 63 | else if (value is HiSystems.Interpreter.DateTime) 64 | { 65 | if (format == null) 66 | return (Text)((HiSystems.Interpreter.DateTime)value).ToString(); 67 | else 68 | return (Text)((HiSystems.Interpreter.DateTime)value).ToString(format); 69 | } 70 | else 71 | throw new NotImplementedException(value.GetType().Name); 72 | } 73 | } 74 | } 75 | 76 | -------------------------------------------------------------------------------- /Functions/Function.cs: -------------------------------------------------------------------------------- 1 | /* _________________________________________________ 2 | 3 | (c) Hi-Integrity Systems 2012. All rights reserved. 4 | www.hisystems.com.au - Toby Wicks 5 | github.com/hisystems/Interpreter 6 | 7 | Licensed under the Apache License, Version 2.0 (the "License"); 8 | you may not use this file except in compliance with the License. 9 | You may obtain a copy of the License at 10 | 11 | http://www.apache.org/licenses/LICENSE-2.0 12 | 13 | Unless required by applicable law or agreed to in writing, software 14 | distributed under the License is distributed on an "AS IS" BASIS, 15 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | See the License for the specific language governing permissions and 17 | limitations under the License. 18 | ___________________________________________________ */ 19 | 20 | using System; 21 | using System.Collections.Generic; 22 | using System.Linq; 23 | using System.Text; 24 | 25 | namespace HiSystems.Interpreter 26 | { 27 | /// 28 | /// Represents a function that can be executed with a set of function arguments and return a function value. 29 | /// 30 | public abstract class Function 31 | { 32 | /// 33 | /// The unique name of the function that is used to identify the function in the token stream. 34 | /// 35 | public abstract string Name { get; } 36 | 37 | public abstract Literal Execute(IConstruct[] arguments); 38 | 39 | public Function() 40 | { 41 | } 42 | 43 | /// 44 | /// Throws an exception if the number of arguments supplied does not match the expected number of arguments. 45 | /// 46 | protected void EnsureArgumentCountIs(IConstruct[] arguments, int expectedArgumentCount) 47 | { 48 | if (expectedArgumentCount != arguments.Length) 49 | throw new InvalidOperationException(String.Format("{0} has been supplied {1} arguments, but expects {2} arguments", this.Name, arguments.Length, expectedArgumentCount)); 50 | } 51 | 52 | /// 53 | /// Throws an exception if the number of arguments supplied is less then the minimum required number of arguments. 54 | /// Useful when the function can receive optional or a variable number of arguments. 55 | /// 56 | protected void EnsureArgumentCountIsAtLeast(IConstruct[] arguments, int minimumArgumentCount) 57 | { 58 | if (minimumArgumentCount > arguments.Length) 59 | throw new InvalidOperationException(String.Format("{0} has been supplied {1} arguments, but expects at least {2} arguments", this.Name, arguments.Length, minimumArgumentCount)); 60 | } 61 | 62 | /// 63 | /// Throws an exception if the number of arguments supplied is less then the minimum required number of arguments. 64 | /// Or throws an exception if the number of arguments supplied is greater then the maximum allowable number of arguments. 65 | /// Useful when the function can receive optional or a variable number of arguments. 66 | /// 67 | protected void EnsureArgumentCountIsBetween(IConstruct[] arguments, int minimumArgumentCount, int maximumArgumentCount) 68 | { 69 | EnsureArgumentCountIsAtLeast(arguments, minimumArgumentCount); 70 | 71 | if (maximumArgumentCount < arguments.Length) 72 | throw new InvalidOperationException(String.Format("{0} has been supplied {1} arguments, but expects at most {2} arguments", this.Name, arguments.Length, maximumArgumentCount)); 73 | } 74 | 75 | /// 76 | /// Returns an array of decimal values from a construct which must be of type Array. 77 | /// Minimise the use of this function because it will traverse and execute the entire expression tree if the construct represents an operation or function. 78 | /// 0 based index - error messages are reported as 1 based indexes 79 | /// 80 | protected decimal[] GetTransformedArgumentDecimalArray(IConstruct[] arguments, int argumentIndex) 81 | { 82 | return this.GetTransformedArgumentArray(arguments, argumentIndex).Select(number => (decimal)number).ToArray(); 83 | } 84 | 85 | /// 86 | /// Returns an array of T values from a construct which must be of type Array. 87 | /// Minimise the use of this function because it will traverse and execute the entire expression tree if the construct represents an operation or function. 88 | /// 0 based index - error messages are reported as 1 based indexes 89 | /// 90 | protected T[] GetTransformedArgumentArray(IConstruct[] arguments, int argumentIndex) where T : Literal 91 | { 92 | var argument = GetArgument(arguments, argumentIndex); 93 | var transformedArgument = argument.Transform(); 94 | 95 | var transformedArray = CastArgumentToType(transformedArgument, argumentIndex).Select(construct => construct.Transform()); 96 | 97 | if (!transformedArray.All(item => item is T)) 98 | throw new InvalidOperationException(String.Format("{0} argument {1} does not contain only {2} values and cannot be used with the {3} function", argument.ToString(), argumentIndex + 1, typeof(T).Name, this.Name)); 99 | 100 | return transformedArray.Cast().ToArray(); 101 | } 102 | 103 | /// 104 | /// Gets the argument and transforms/executes it and returns it as of type T. 105 | /// If the transformed result is not of type T then an exception is thrown. 106 | /// Minimise the use of this function because it will traverse and execute the entire expression tree if the construct represents an operation or function. 107 | /// 0 based index - error messages are reported as 1 based indexes 108 | /// 109 | protected T GetTransformedArgument(IConstruct[] arguments, int argumentIndex) where T : Literal 110 | { 111 | var argument = GetArgument(arguments, argumentIndex); 112 | var transformedArgument = argument.Transform(); 113 | 114 | return CastArgumentToType(transformedArgument, argumentIndex); 115 | } 116 | 117 | /// 118 | /// Returns the argument (un-transformed). 119 | /// Throws an exception if there is no argument at the index. 120 | /// 121 | /// 0 based index - error messages are reported as 1 based indexes 122 | private IConstruct GetArgument(IConstruct[] arguments, int argumentIndex) 123 | { 124 | if (argumentIndex >= arguments.Length) 125 | throw new ArgumentException(String.Format("Function {0} is missing argument {1}.", this.Name, argumentIndex + 1)); 126 | 127 | return arguments[argumentIndex]; 128 | } 129 | 130 | /// 131 | /// Throws an exception if the argument passed, via the argument index is not the expected type. 132 | /// ArgumentIndex is only used for the error message and should match the index of the argument that is passed. 133 | /// 134 | private T CastArgumentToType(IConstruct argument, int argumentIndex) 135 | { 136 | if (!(argument is T)) 137 | throw new InvalidOperationException(String.Format("Argument {1}: {0} is not of type {2} and cannot be used with the {3} function", argument.ToString(), argumentIndex + 1, typeof(T).Name, this.Name)); 138 | 139 | return (T)argument; 140 | } 141 | 142 | public override string ToString() 143 | { 144 | return this.Name + "()"; 145 | } 146 | } 147 | } 148 | -------------------------------------------------------------------------------- /Functions/If.cs: -------------------------------------------------------------------------------- 1 | /* _________________________________________________ 2 | 3 | (c) Hi-Integrity Systems 2012. All rights reserved. 4 | www.hisystems.com.au - Toby Wicks 5 | github.com/hisystems/Interpreter 6 | 7 | Licensed under the Apache License, Version 2.0 (the "License"); 8 | you may not use this file except in compliance with the License. 9 | You may obtain a copy of the License at 10 | 11 | http://www.apache.org/licenses/LICENSE-2.0 12 | 13 | Unless required by applicable law or agreed to in writing, software 14 | distributed under the License is distributed on an "AS IS" BASIS, 15 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | See the License for the specific language governing permissions and 17 | limitations under the License. 18 | ___________________________________________________ */ 19 | 20 | using System; 21 | using System.Collections.Generic; 22 | using System.Linq; 23 | using System.Text; 24 | 25 | namespace HiSystems.Interpreter 26 | { 27 | /// 28 | /// Based on the condition / expression returns the true or false result. 29 | /// Usage: If(condition, trueResult, falseResult) 30 | /// Example: If(1 = 1, 'Yes', 'No') 31 | /// 32 | public class If : Function 33 | { 34 | public override string Name 35 | { 36 | get 37 | { 38 | return "If"; 39 | } 40 | } 41 | 42 | public override Literal Execute(IConstruct[] arguments) 43 | { 44 | base.EnsureArgumentCountIs(arguments, 3); 45 | 46 | var condition = base.GetTransformedArgument(arguments, argumentIndex: 0); 47 | 48 | if (condition) 49 | return base.GetTransformedArgument(arguments, argumentIndex: 1); 50 | else 51 | return base.GetTransformedArgument(arguments, argumentIndex: 2); 52 | } 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /Functions/Len.cs: -------------------------------------------------------------------------------- 1 | /* _________________________________________________ 2 | 3 | (c) Hi-Integrity Systems 2012. All rights reserved. 4 | www.hisystems.com.au - Toby Wicks 5 | github.com/hisystems/Interpreter 6 | 7 | Licensed under the Apache License, Version 2.0 (the "License"); 8 | you may not use this file except in compliance with the License. 9 | You may obtain a copy of the License at 10 | 11 | http://www.apache.org/licenses/LICENSE-2.0 12 | 13 | Unless required by applicable law or agreed to in writing, software 14 | distributed under the License is distributed on an "AS IS" BASIS, 15 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | See the License for the specific language governing permissions and 17 | limitations under the License. 18 | ___________________________________________________ */ 19 | 20 | using System; 21 | using System.Collections.Generic; 22 | using System.Linq; 23 | using System.Text; 24 | 25 | namespace HiSystems.Interpreter 26 | { 27 | /// 28 | /// Accepts one argument of type string for which the length is returned. 29 | /// Usage: Len(text) 30 | /// Example: Len('abc') 31 | /// 32 | public class Len : Function 33 | { 34 | public override string Name 35 | { 36 | get 37 | { 38 | return "LEN"; 39 | } 40 | } 41 | 42 | public override Literal Execute(IConstruct[] arguments) 43 | { 44 | base.EnsureArgumentCountIs(arguments, 1); 45 | 46 | return new Number(base.GetTransformedArgument(arguments, argumentIndex:0).Length); 47 | } 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /Functions/Max.cs: -------------------------------------------------------------------------------- 1 | /* _________________________________________________ 2 | 3 | (c) Hi-Integrity Systems 2012. All rights reserved. 4 | www.hisystems.com.au - Toby Wicks 5 | github.com/hisystems/Interpreter 6 | 7 | Licensed under the Apache License, Version 2.0 (the "License"); 8 | you may not use this file except in compliance with the License. 9 | You may obtain a copy of the License at 10 | 11 | http://www.apache.org/licenses/LICENSE-2.0 12 | 13 | Unless required by applicable law or agreed to in writing, software 14 | distributed under the License is distributed on an "AS IS" BASIS, 15 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | See the License for the specific language governing permissions and 17 | limitations under the License. 18 | ___________________________________________________ */ 19 | 20 | using System; 21 | using System.Collections.Generic; 22 | using System.Linq; 23 | using System.Text; 24 | 25 | namespace HiSystems.Interpreter 26 | { 27 | /// 28 | /// Returns the maximum numeric value from an array. 29 | /// Usage: Max(array) 30 | /// Example: Max(Array(1, 2, 3)) 31 | /// 32 | public class Max : Function 33 | { 34 | public override string Name 35 | { 36 | get 37 | { 38 | return "MAX"; 39 | } 40 | } 41 | 42 | public override Literal Execute(IConstruct[] arguments) 43 | { 44 | base.EnsureArgumentCountIs(arguments, 1); 45 | 46 | return new Number(base.GetTransformedArgumentDecimalArray(arguments, argumentIndex:0).Max()); 47 | } 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /Functions/Min.cs: -------------------------------------------------------------------------------- 1 | /* _________________________________________________ 2 | 3 | (c) Hi-Integrity Systems 2012. All rights reserved. 4 | www.hisystems.com.au - Toby Wicks 5 | github.com/hisystems/Interpreter 6 | 7 | Licensed under the Apache License, Version 2.0 (the "License"); 8 | you may not use this file except in compliance with the License. 9 | You may obtain a copy of the License at 10 | 11 | http://www.apache.org/licenses/LICENSE-2.0 12 | 13 | Unless required by applicable law or agreed to in writing, software 14 | distributed under the License is distributed on an "AS IS" BASIS, 15 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | See the License for the specific language governing permissions and 17 | limitations under the License. 18 | ___________________________________________________ */ 19 | 20 | using System; 21 | using System.Collections.Generic; 22 | using System.Linq; 23 | using System.Text; 24 | 25 | namespace HiSystems.Interpreter 26 | { 27 | /// 28 | /// Returns the minimum numeric value from an array. 29 | /// Usage: Min(array) 30 | /// Example: Min(Array(1, 2, 3)) 31 | /// 32 | public class Min : Function 33 | { 34 | public override string Name 35 | { 36 | get 37 | { 38 | return "Min"; 39 | } 40 | } 41 | 42 | public override Literal Execute(IConstruct[] arguments) 43 | { 44 | base.EnsureArgumentCountIs(arguments, 1); 45 | 46 | return new Number(base.GetTransformedArgumentDecimalArray(arguments, argumentIndex:0).Min()); 47 | } 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /Functions/Sum.cs: -------------------------------------------------------------------------------- 1 | /* _________________________________________________ 2 | 3 | (c) Hi-Integrity Systems 2012. All rights reserved. 4 | www.hisystems.com.au - Toby Wicks 5 | github.com/hisystems/Interpreter 6 | 7 | Licensed under the Apache License, Version 2.0 (the "License"); 8 | you may not use this file except in compliance with the License. 9 | You may obtain a copy of the License at 10 | 11 | http://www.apache.org/licenses/LICENSE-2.0 12 | 13 | Unless required by applicable law or agreed to in writing, software 14 | distributed under the License is distributed on an "AS IS" BASIS, 15 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | See the License for the specific language governing permissions and 17 | limitations under the License. 18 | ___________________________________________________ */ 19 | 20 | using System; 21 | using System.Collections.Generic; 22 | using System.Linq; 23 | using System.Text; 24 | 25 | namespace HiSystems.Interpreter 26 | { 27 | /// 28 | /// Returns the sum total of the numeric values from an array. 29 | /// Usage: Sum(array) 30 | /// Example: Sum(Array(1, 2, 3)) 31 | /// 32 | public class Sum : Function 33 | { 34 | public override string Name 35 | { 36 | get 37 | { 38 | return "SUM"; 39 | } 40 | } 41 | 42 | public override Literal Execute(IConstruct[] arguments) 43 | { 44 | base.EnsureArgumentCountIs(arguments, 1); 45 | 46 | return new Number(base.GetTransformedArgumentDecimalArray(arguments, argumentIndex:0).Sum()); 47 | } 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /Functions/Today.cs: -------------------------------------------------------------------------------- 1 | /* _________________________________________________ 2 | 3 | (c) Hi-Integrity Systems 2012. All rights reserved. 4 | www.hisystems.com.au - Toby Wicks 5 | github.com/hisystems/Interpreter 6 | 7 | Licensed under the Apache License, Version 2.0 (the "License"); 8 | you may not use this file except in compliance with the License. 9 | You may obtain a copy of the License at 10 | 11 | http://www.apache.org/licenses/LICENSE-2.0 12 | 13 | Unless required by applicable law or agreed to in writing, software 14 | distributed under the License is distributed on an "AS IS" BASIS, 15 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | See the License for the specific language governing permissions and 17 | limitations under the License. 18 | ___________________________________________________ */ 19 | 20 | using System; 21 | using System.Collections.Generic; 22 | using System.Linq; 23 | using System.Text; 24 | 25 | namespace HiSystems.Interpreter 26 | { 27 | /// 28 | /// Returns today's date. 29 | /// Usage: Today() 30 | /// 31 | public class Today : Function 32 | { 33 | public override string Name 34 | { 35 | get 36 | { 37 | return "TODAY"; 38 | } 39 | } 40 | 41 | public override Literal Execute(IConstruct[] arguments) 42 | { 43 | base.EnsureArgumentCountIs(arguments, 0); 44 | 45 | return new DateTime(System.DateTime.Today); 46 | } 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /HiSystems.Interpreter.MonoTouch.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Debug 5 | AnyCPU 6 | 8.0.30703 7 | 2.0 8 | {B7F045FA-2E47-40EE-9501-B407C52D9D19} 9 | {FEACFBD2-3405-455C-9665-78FE426C6842};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} 10 | Library 11 | HiSystems.Interpreter 12 | HiSystems-Interpreter 13 | Xamarin.iOS 14 | v1.0 15 | 16 | 17 | True 18 | full 19 | False 20 | bin\Debug 21 | DEBUG; 22 | prompt 23 | 4 24 | False 25 | 26 | 27 | none 28 | True 29 | bin\Release 30 | prompt 31 | 4 32 | False 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | -------------------------------------------------------------------------------- /HiSystems.Interpreter.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | Debug 5 | AnyCPU 6 | 8.0.30703 7 | 2.0 8 | {65D25F1E-BD7D-4B5E-8457-DD8A4813DD30} 9 | Library 10 | Properties 11 | HiSystems.Interpreter 12 | HiSystems.Interpreter 13 | v3.5 14 | 512 15 | 16 | 17 | true 18 | full 19 | false 20 | bin\Debug\ 21 | DEBUG;TRACE 22 | prompt 23 | 4 24 | 25 | 26 | pdbonly 27 | true 28 | bin\Release\ 29 | TRACE 30 | prompt 31 | 4 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 103 | -------------------------------------------------------------------------------- /HiSystems.Interpreter.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 11.00 3 | # Visual Studio 2010 4 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "HiSystems.Interpreter", "HiSystems.Interpreter.csproj", "{65D25F1E-BD7D-4B5E-8457-DD8A4813DD30}" 5 | EndProject 6 | Global 7 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 8 | Debug|Any CPU = Debug|Any CPU 9 | Release|Any CPU = Release|Any CPU 10 | EndGlobalSection 11 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 12 | {65D25F1E-BD7D-4B5E-8457-DD8A4813DD30}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 13 | {65D25F1E-BD7D-4B5E-8457-DD8A4813DD30}.Debug|Any CPU.Build.0 = Debug|Any CPU 14 | {65D25F1E-BD7D-4B5E-8457-DD8A4813DD30}.Release|Any CPU.ActiveCfg = Release|Any CPU 15 | {65D25F1E-BD7D-4B5E-8457-DD8A4813DD30}.Release|Any CPU.Build.0 = Release|Any CPU 16 | EndGlobalSection 17 | GlobalSection(SolutionProperties) = preSolution 18 | HideSolutionNode = FALSE 19 | EndGlobalSection 20 | EndGlobal 21 | -------------------------------------------------------------------------------- /Library/PeekableEnumerator.cs: -------------------------------------------------------------------------------- 1 | /* _________________________________________________ 2 | 3 | (c) Hi-Integrity Systems 2012. All rights reserved. 4 | www.hisystems.com.au - Toby Wicks 5 | github.com/hisystems/Interpreter 6 | 7 | Licensed under the Apache License, Version 2.0 (the "License"); 8 | you may not use this file except in compliance with the License. 9 | You may obtain a copy of the License at 10 | 11 | http://www.apache.org/licenses/LICENSE-2.0 12 | 13 | Unless required by applicable law or agreed to in writing, software 14 | distributed under the License is distributed on an "AS IS" BASIS, 15 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | See the License for the specific language governing permissions and 17 | limitations under the License. 18 | ___________________________________________________ */ 19 | 20 | using System; 21 | using System.Collections.Generic; 22 | using System.Linq; 23 | using System.Text; 24 | 25 | namespace HiSystems.Interpreter 26 | { 27 | internal class PeekableEnumerator : IEnumerator 28 | { 29 | private IEnumerator peekingEnumerator; 30 | private T current = default(T); 31 | private T peeked; 32 | 33 | public PeekableEnumerator(IEnumerable enumerable) 34 | { 35 | this.peekingEnumerator = enumerable.GetEnumerator(); 36 | MoveNextInternal(); 37 | } 38 | 39 | public T Current 40 | { 41 | get 42 | { 43 | return current; 44 | } 45 | } 46 | 47 | /// 48 | /// Returns the item ahead of the current item being enumerated. 49 | /// 50 | public T Peek 51 | { 52 | get 53 | { 54 | return peeked; 55 | } 56 | } 57 | 58 | /// 59 | /// Indicates whether there is another item ahead of the current item. 60 | /// 61 | public bool CanPeek 62 | { 63 | get 64 | { 65 | return peeked != null; 66 | } 67 | } 68 | 69 | object System.Collections.IEnumerator.Current 70 | { 71 | get 72 | { 73 | return current; 74 | } 75 | } 76 | 77 | public bool MoveNext() 78 | { 79 | this.current = this.peeked; 80 | 81 | // if reached end of the enumeration 82 | if (Object.Equals(this.current, default(T))) 83 | return false; 84 | else 85 | { 86 | MoveNextInternal(); 87 | return true; 88 | } 89 | } 90 | 91 | private void MoveNextInternal() 92 | { 93 | if (this.peekingEnumerator.MoveNext()) 94 | this.peeked = this.peekingEnumerator.Current; 95 | else 96 | this.peeked = default(T); 97 | } 98 | 99 | public void Reset() 100 | { 101 | this.peekingEnumerator.Reset(); 102 | 103 | MoveNextInternal(); 104 | } 105 | 106 | public void Dispose() 107 | { 108 | this.peekingEnumerator.Dispose(); 109 | } 110 | } 111 | } 112 | -------------------------------------------------------------------------------- /Literals/Array.cs: -------------------------------------------------------------------------------- 1 | /* _________________________________________________ 2 | 3 | (c) Hi-Integrity Systems 2012. All rights reserved. 4 | www.hisystems.com.au - Toby Wicks 5 | github.com/hisystems/Interpreter 6 | 7 | Licensed under the Apache License, Version 2.0 (the "License"); 8 | you may not use this file except in compliance with the License. 9 | You may obtain a copy of the License at 10 | 11 | http://www.apache.org/licenses/LICENSE-2.0 12 | 13 | Unless required by applicable law or agreed to in writing, software 14 | distributed under the License is distributed on an "AS IS" BASIS, 15 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | See the License for the specific language governing permissions and 17 | limitations under the License. 18 | ___________________________________________________ */ 19 | 20 | using System; 21 | using System.Collections.Generic; 22 | using System.Linq; 23 | using System.Text; 24 | 25 | namespace HiSystems.Interpreter 26 | { 27 | /// 28 | /// Represents an array of constructs. 29 | /// Potentially an array of literals, variables or functions. 30 | /// 31 | public class Array : Literal, IEnumerable 32 | { 33 | private List items = new List(); 34 | 35 | public Array(decimal[] values) 36 | { 37 | items.AddRange(values.Select(item => (Number)item).ToArray()); 38 | } 39 | 40 | public Array(IConstruct[] values) 41 | { 42 | items.AddRange(values); 43 | } 44 | 45 | public static implicit operator List(Array array) 46 | { 47 | return array.items; 48 | } 49 | 50 | public static implicit operator Array(IConstruct[] constructs) 51 | { 52 | return new Array(constructs); 53 | } 54 | 55 | public override string ToString() 56 | { 57 | return "Array"; 58 | } 59 | 60 | System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator () 61 | { 62 | return items.GetEnumerator(); 63 | } 64 | 65 | IEnumerator IEnumerable.GetEnumerator () 66 | { 67 | return items.GetEnumerator(); 68 | } 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /Literals/Boolean.cs: -------------------------------------------------------------------------------- 1 | /* _________________________________________________ 2 | 3 | (c) Hi-Integrity Systems 2012. All rights reserved. 4 | www.hisystems.com.au - Toby Wicks 5 | github.com/hisystems/Interpreter 6 | 7 | Licensed under the Apache License, Version 2.0 (the "License"); 8 | you may not use this file except in compliance with the License. 9 | You may obtain a copy of the License at 10 | 11 | http://www.apache.org/licenses/LICENSE-2.0 12 | 13 | Unless required by applicable law or agreed to in writing, software 14 | distributed under the License is distributed on an "AS IS" BASIS, 15 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | See the License for the specific language governing permissions and 17 | limitations under the License. 18 | ___________________________________________________ */ 19 | 20 | using System.Collections.Generic; 21 | using System.ComponentModel; 22 | using System.Linq; 23 | using System.Text; 24 | using HiSystems.Interpreter.Converters; 25 | 26 | namespace HiSystems.Interpreter 27 | { 28 | /// 29 | /// Represents an immutable boolean value. 30 | /// 31 | [TypeConverter(typeof(BooleanTypeConverter))] 32 | public class Boolean : Literal 33 | { 34 | private bool value; 35 | 36 | public Boolean(bool value) 37 | { 38 | this.value = value; 39 | } 40 | 41 | public override string ToString() 42 | { 43 | return value.ToString(); 44 | } 45 | 46 | public static implicit operator bool(Boolean boolean) 47 | { 48 | return boolean.value; 49 | } 50 | 51 | public static implicit operator Boolean(bool value) 52 | { 53 | return new Boolean(value); 54 | } 55 | 56 | public static Boolean operator==(Boolean value1, Boolean value2) 57 | { 58 | return AreEqual(value1, value2); 59 | } 60 | 61 | public static Boolean operator!=(Boolean value1, Boolean value2) 62 | { 63 | return !AreEqual(value1, value2); 64 | } 65 | 66 | public static Boolean operator&(Boolean value1, Boolean value2) 67 | { 68 | return value1.value & value2.value; 69 | } 70 | 71 | public static Boolean operator|(Boolean value1, Boolean value2) 72 | { 73 | return value1.value | value2.value; 74 | } 75 | 76 | public static Boolean operator!(Boolean value) 77 | { 78 | return new Boolean(!value.value); 79 | } 80 | 81 | public static bool operator true(Boolean value) 82 | { 83 | return value.value; 84 | } 85 | 86 | public static bool operator false(Boolean value) 87 | { 88 | return !value.value; 89 | } 90 | 91 | public override int GetHashCode() 92 | { 93 | return base.GetHashCode(); 94 | } 95 | 96 | public override bool Equals (object obj) 97 | { 98 | if (obj == null || !(obj is Boolean)) 99 | return false; 100 | else 101 | return AreEqual(this, (Boolean)obj); 102 | } 103 | 104 | private static Boolean AreEqual(Boolean value1, Boolean value2) 105 | { 106 | if (ReferenceEquals(value1, null) || ReferenceEquals(value2, null)) 107 | return new Boolean(false); 108 | else 109 | return new Boolean(value1.value == value2.value); 110 | } 111 | } 112 | } 113 | -------------------------------------------------------------------------------- /Literals/DateTime.cs: -------------------------------------------------------------------------------- 1 | /* _________________________________________________ 2 | 3 | (c) Hi-Integrity Systems 2012. All rights reserved. 4 | www.hisystems.com.au - Toby Wicks 5 | github.com/hisystems/Interpreter 6 | 7 | Licensed under the Apache License, Version 2.0 (the "License"); 8 | you may not use this file except in compliance with the License. 9 | You may obtain a copy of the License at 10 | 11 | http://www.apache.org/licenses/LICENSE-2.0 12 | 13 | Unless required by applicable law or agreed to in writing, software 14 | distributed under the License is distributed on an "AS IS" BASIS, 15 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | See the License for the specific language governing permissions and 17 | limitations under the License. 18 | ___________________________________________________ */ 19 | 20 | using System; 21 | using System.Collections.Generic; 22 | using System.ComponentModel; 23 | using System.Linq; 24 | using System.Text; 25 | using HiSystems.Interpreter.Converters; 26 | 27 | namespace HiSystems.Interpreter 28 | { 29 | /// 30 | /// Represents an immutable date / time value. 31 | /// 32 | [TypeConverter(typeof(DateTimeTypeConverter))] 33 | public class DateTime : Literal 34 | { 35 | private System.DateTime value; 36 | 37 | public DateTime(System.DateTime value) 38 | { 39 | this.value = value; 40 | } 41 | 42 | public override string ToString() 43 | { 44 | return value.ToString(); 45 | } 46 | 47 | public string ToString(string format) 48 | { 49 | return value.ToString(format); 50 | } 51 | 52 | public static implicit operator System.DateTime(DateTime date) 53 | { 54 | return date.value; 55 | } 56 | 57 | public static implicit operator DateTime(System.DateTime value) 58 | { 59 | return new DateTime(value); 60 | } 61 | 62 | public static Boolean operator==(DateTime value1, DateTime value2) 63 | { 64 | return AreEqual(value1, value2); 65 | } 66 | 67 | public static Boolean operator!=(DateTime value1, DateTime value2) 68 | { 69 | return !AreEqual(value1, value2); 70 | } 71 | 72 | public static DateTime operator+(DateTime date, Number days) 73 | { 74 | return new DateTime(date.value.AddDays((double)days)); 75 | } 76 | 77 | public static DateTime operator-(DateTime date, Number days) 78 | { 79 | return new DateTime(date.value.AddDays(-(double)days)); 80 | } 81 | 82 | public static Number operator-(DateTime date1, DateTime date2) 83 | { 84 | return new Number(Convert.ToDecimal((date1.value - date2.value).TotalDays)); 85 | } 86 | 87 | public static Boolean operator>(DateTime value1, DateTime value2) 88 | { 89 | return new Boolean(value1.value > value2.value); 90 | } 91 | 92 | public static Boolean operator>=(DateTime value1, DateTime value2) 93 | { 94 | return new Boolean(value1.value >= value2.value); 95 | } 96 | 97 | public static Boolean operator<(DateTime value1, DateTime value2) 98 | { 99 | return new Boolean(value1.value < value2.value); 100 | } 101 | 102 | public static Boolean operator<=(DateTime value1, DateTime value2) 103 | { 104 | return new Boolean(value1.value <= value2.value); 105 | } 106 | 107 | public override bool Equals(object obj) 108 | { 109 | if (obj == null || !(obj is DateTime)) 110 | return false; 111 | else 112 | return AreEqual(this, (DateTime)obj); 113 | } 114 | 115 | public override int GetHashCode() 116 | { 117 | return base.GetHashCode(); 118 | } 119 | 120 | private static Boolean AreEqual(DateTime value1, DateTime value2) 121 | { 122 | if (ReferenceEquals(value1, null) || ReferenceEquals(value2, null)) 123 | return new Boolean(false); 124 | else 125 | return new Boolean(value1.value == value2.value); 126 | } 127 | } 128 | } 129 | -------------------------------------------------------------------------------- /Literals/Literal.cs: -------------------------------------------------------------------------------- 1 | /* _________________________________________________ 2 | 3 | (c) Hi-Integrity Systems 2012. All rights reserved. 4 | www.hisystems.com.au - Toby Wicks 5 | github.com/hisystems/Interpreter 6 | 7 | Licensed under the Apache License, Version 2.0 (the "License"); 8 | you may not use this file except in compliance with the License. 9 | You may obtain a copy of the License at 10 | 11 | http://www.apache.org/licenses/LICENSE-2.0 12 | 13 | Unless required by applicable law or agreed to in writing, software 14 | distributed under the License is distributed on an "AS IS" BASIS, 15 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | See the License for the specific language governing permissions and 17 | limitations under the License. 18 | ___________________________________________________ */ 19 | 20 | using System; 21 | 22 | namespace HiSystems.Interpreter 23 | { 24 | /// 25 | /// Base class for all literals / concrete values, such as a number, boolean, string etc. 26 | /// 27 | public abstract class Literal : IConstruct 28 | { 29 | /// 30 | /// A literal does not need to be transformed or executed like a function or operation. 31 | /// So return this construct as is. 32 | /// 33 | Literal IConstruct.Transform() 34 | { 35 | return this; 36 | } 37 | } 38 | } 39 | 40 | -------------------------------------------------------------------------------- /Literals/Number.cs: -------------------------------------------------------------------------------- 1 | /* _________________________________________________ 2 | 3 | (c) Hi-Integrity Systems 2012. All rights reserved. 4 | www.hisystems.com.au - Toby Wicks 5 | github.com/hisystems/Interpreter 6 | 7 | Licensed under the Apache License, Version 2.0 (the "License"); 8 | you may not use this file except in compliance with the License. 9 | You may obtain a copy of the License at 10 | 11 | http://www.apache.org/licenses/LICENSE-2.0 12 | 13 | Unless required by applicable law or agreed to in writing, software 14 | distributed under the License is distributed on an "AS IS" BASIS, 15 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | See the License for the specific language governing permissions and 17 | limitations under the License. 18 | ___________________________________________________ */ 19 | 20 | using System; 21 | using System.ComponentModel; 22 | using System.Text; 23 | using HiSystems.Interpreter.Converters; 24 | 25 | namespace HiSystems.Interpreter 26 | { 27 | /// 28 | /// Represents an immutable numeric value. 29 | /// 30 | [TypeConverter(typeof(NumberTypeConverter))] 31 | public class Number : Literal 32 | { 33 | private decimal value; 34 | 35 | public Number(decimal value) 36 | { 37 | this.value = value; 38 | } 39 | 40 | public override string ToString() 41 | { 42 | return value.ToString(); 43 | } 44 | 45 | public string ToString(string format) 46 | { 47 | return value.ToString(format); 48 | } 49 | 50 | public static implicit operator decimal(Number number) 51 | { 52 | return number.value; 53 | } 54 | 55 | public static implicit operator double(Number number) 56 | { 57 | return (double)number.value; 58 | } 59 | 60 | public static implicit operator Number(decimal value) 61 | { 62 | return new Number(value); 63 | } 64 | 65 | public static implicit operator Number(double value) 66 | { 67 | return new Number((decimal)value); 68 | } 69 | 70 | public static implicit operator Number(int value) 71 | { 72 | return new Number((decimal)value); 73 | } 74 | 75 | public static Number Parse(string value) 76 | { 77 | return new Number(Decimal.Parse(value)); 78 | } 79 | 80 | public static Boolean operator==(Number value1, Number value2) 81 | { 82 | return AreEqual(value1, value2); 83 | } 84 | 85 | public static Boolean operator!=(Number value1, Number value2) 86 | { 87 | return !AreEqual(value1, value2); 88 | } 89 | 90 | public static Number operator+(Number value1, Number value2) 91 | { 92 | return new Number(value1.value + value2.value); 93 | } 94 | 95 | public static Number operator-(Number value1, Number value2) 96 | { 97 | return new Number(value1.value - value2.value); 98 | } 99 | 100 | public static Number operator/(Number value1, Number value2) 101 | { 102 | return new Number(value1.value / value2.value); 103 | } 104 | 105 | public static Boolean operator>(Number value1, Number value2) 106 | { 107 | return new Boolean(value1.value > value2.value); 108 | } 109 | 110 | public static Boolean operator>=(Number value1, Number value2) 111 | { 112 | return new Boolean(value1.value >= value2.value); 113 | } 114 | 115 | public static Boolean operator<(Number value1, Number value2) 116 | { 117 | return new Boolean(value1.value < value2.value); 118 | } 119 | 120 | public static Boolean operator<=(Number value1, Number value2) 121 | { 122 | return new Boolean(value1.value <= value2.value); 123 | } 124 | 125 | public static Number operator*(Number value1, Number value2) 126 | { 127 | return new Number(value1.value * value2.value); 128 | } 129 | 130 | public static Number operator%(Number value1, Number value2) 131 | { 132 | return new Number(value1.value % value2.value); 133 | } 134 | 135 | public override bool Equals (object obj) 136 | { 137 | if (obj == null || !(obj is Number)) 138 | return false; 139 | else 140 | return AreEqual(this, (Number)obj); 141 | } 142 | 143 | private static Boolean AreEqual(Number value1, Number value2) 144 | { 145 | if (ReferenceEquals(value1, null) || ReferenceEquals(value2, null)) 146 | return new Boolean(false); 147 | else 148 | return new Boolean(value1.value == value2.value); 149 | } 150 | 151 | public override int GetHashCode() 152 | { 153 | return base.GetHashCode(); 154 | } 155 | } 156 | } 157 | -------------------------------------------------------------------------------- /Literals/Text.cs: -------------------------------------------------------------------------------- 1 | /* _________________________________________________ 2 | 3 | (c) Hi-Integrity Systems 2012. All rights reserved. 4 | www.hisystems.com.au - Toby Wicks 5 | github.com/hisystems/Interpreter 6 | 7 | Licensed under the Apache License, Version 2.0 (the "License"); 8 | you may not use this file except in compliance with the License. 9 | You may obtain a copy of the License at 10 | 11 | http://www.apache.org/licenses/LICENSE-2.0 12 | 13 | Unless required by applicable law or agreed to in writing, software 14 | distributed under the License is distributed on an "AS IS" BASIS, 15 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | See the License for the specific language governing permissions and 17 | limitations under the License. 18 | ___________________________________________________ */ 19 | 20 | using System; 21 | using System.ComponentModel; 22 | using HiSystems.Interpreter.Converters; 23 | 24 | namespace HiSystems.Interpreter 25 | { 26 | [TypeConverter(typeof(TextTypeConverter))] 27 | public class Text : Literal 28 | { 29 | private string value; 30 | 31 | public Text(string value) 32 | { 33 | this.value = value; 34 | } 35 | 36 | public static implicit operator string(Text text) 37 | { 38 | return text.value; 39 | } 40 | 41 | public static implicit operator Text(string text) 42 | { 43 | return new Text(text); 44 | } 45 | 46 | public static Boolean operator==(Text value1, Text value2) 47 | { 48 | return AreEqual(value1, value2); 49 | } 50 | 51 | public static Boolean operator!=(Text value1, Text value2) 52 | { 53 | return !AreEqual(value1, value2); 54 | } 55 | 56 | public static Text operator+(Text value1, Text value2) 57 | { 58 | return new Text(value1.value + value2.value); 59 | } 60 | 61 | /// 62 | /// Returns the length of the text. 63 | /// 64 | public int Length 65 | { 66 | get 67 | { 68 | return this.value.Length; 69 | } 70 | } 71 | 72 | public override bool Equals(object obj) 73 | { 74 | if (obj == null || !(obj is Text)) 75 | return false; 76 | else 77 | return AreEqual(this, (Text)obj); 78 | } 79 | 80 | private static Boolean AreEqual(Text value1, Text value2) 81 | { 82 | if (ReferenceEquals(value1, null) || ReferenceEquals(value2, null)) 83 | return new Boolean(false); 84 | else 85 | return new Boolean(value1.value.Equals(value2.value, StringComparison.InvariantCulture)); 86 | } 87 | 88 | public override int GetHashCode() 89 | { 90 | return base.GetHashCode(); 91 | } 92 | 93 | public override string ToString() 94 | { 95 | return value.ToString(); 96 | } 97 | } 98 | } 99 | 100 | -------------------------------------------------------------------------------- /Operators/AddOperator.cs: -------------------------------------------------------------------------------- 1 | /* _________________________________________________ 2 | 3 | (c) Hi-Integrity Systems 2012. All rights reserved. 4 | www.hisystems.com.au - Toby Wicks 5 | github.com/hisystems/Interpreter 6 | 7 | Licensed under the Apache License, Version 2.0 (the "License"); 8 | you may not use this file except in compliance with the License. 9 | You may obtain a copy of the License at 10 | 11 | http://www.apache.org/licenses/LICENSE-2.0 12 | 13 | Unless required by applicable law or agreed to in writing, software 14 | distributed under the License is distributed on an "AS IS" BASIS, 15 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | See the License for the specific language governing permissions and 17 | limitations under the License. 18 | ___________________________________________________ */ 19 | 20 | using System; 21 | using System.Collections.Generic; 22 | using System.Linq; 23 | using System.Text; 24 | 25 | namespace HiSystems.Interpreter 26 | { 27 | /// 28 | /// Adds two numeric values, text or a date and numeric value (adds days). 29 | /// Usage: 30 | /// numericValue + numericValue 31 | /// dateTime + numericDays 32 | /// text + textToConcatenate 33 | /// Examples: 34 | /// 1 + 2 35 | /// #2000-01-01# + 1 36 | /// 'ab' + 'c' 37 | /// 38 | public class AddOperator : Operator 39 | { 40 | public AddOperator() 41 | { 42 | } 43 | 44 | internal override Literal Execute(IConstruct argument1, IConstruct argument2) 45 | { 46 | var argument1Transformed = base.GetTransformedConstruct(argument1); 47 | var argument2Transformed = base.GetTransformedConstruct(argument2); 48 | 49 | if (argument1Transformed is Number && argument2Transformed is Number) 50 | return ((Number)argument1Transformed) + ((Number)argument2Transformed); 51 | else if (argument1Transformed is DateTime && argument2Transformed is Number) 52 | return ((DateTime)argument1Transformed) + ((Number)argument2Transformed); 53 | else if (argument1Transformed is Text && argument2Transformed is Text) 54 | return ((Text)argument1Transformed) + ((Text)argument2Transformed); 55 | else 56 | throw new InvalidOperationException(String.Format("Add operator requires arguments of type Number, DateTime or Text. Argument types are {0} {1}.", argument1Transformed.GetType().Name, argument2Transformed.GetType().Name)); 57 | } 58 | 59 | public override string Token 60 | { 61 | get 62 | { 63 | return "+"; 64 | } 65 | } 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /Operators/AndOperator.cs: -------------------------------------------------------------------------------- 1 | /* _________________________________________________ 2 | 3 | (c) Hi-Integrity Systems 2012. All rights reserved. 4 | www.hisystems.com.au - Toby Wicks 5 | github.com/hisystems/Interpreter 6 | 7 | Licensed under the Apache License, Version 2.0 (the "License"); 8 | you may not use this file except in compliance with the License. 9 | You may obtain a copy of the License at 10 | 11 | http://www.apache.org/licenses/LICENSE-2.0 12 | 13 | Unless required by applicable law or agreed to in writing, software 14 | distributed under the License is distributed on an "AS IS" BASIS, 15 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | See the License for the specific language governing permissions and 17 | limitations under the License. 18 | ___________________________________________________ */ 19 | 20 | using System; 21 | using System.Collections.Generic; 22 | using System.Linq; 23 | using System.Text; 24 | 25 | namespace HiSystems.Interpreter 26 | { 27 | /// 28 | /// Logical And operator. 29 | /// Usage: booleanValue AND booleanValue 30 | /// Example: true AND false 31 | /// 32 | public class AndOperator : Operator 33 | { 34 | public AndOperator() 35 | { 36 | } 37 | 38 | /// 39 | /// Non-zero arguments are considered true. 40 | /// 41 | internal override Literal Execute(IConstruct argument1, IConstruct argument2) 42 | { 43 | return base.GetTransformedConstruct(argument1) && base.GetTransformedConstruct(argument2); 44 | } 45 | 46 | public override string Token 47 | { 48 | get 49 | { 50 | return "AND"; 51 | } 52 | } 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /Operators/DivideOperator.cs: -------------------------------------------------------------------------------- 1 | /* _________________________________________________ 2 | 3 | (c) Hi-Integrity Systems 2012. All rights reserved. 4 | www.hisystems.com.au - Toby Wicks 5 | github.com/hisystems/Interpreter 6 | 7 | Licensed under the Apache License, Version 2.0 (the "License"); 8 | you may not use this file except in compliance with the License. 9 | You may obtain a copy of the License at 10 | 11 | http://www.apache.org/licenses/LICENSE-2.0 12 | 13 | Unless required by applicable law or agreed to in writing, software 14 | distributed under the License is distributed on an "AS IS" BASIS, 15 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | See the License for the specific language governing permissions and 17 | limitations under the License. 18 | ___________________________________________________ */ 19 | 20 | using System; 21 | using System.Collections.Generic; 22 | using System.Linq; 23 | using System.Text; 24 | 25 | namespace HiSystems.Interpreter 26 | { 27 | /// 28 | /// Divides numeric values. 29 | /// Usage: numericValue / numericValue 30 | /// Example: 1 / 2 31 | /// 32 | public class DivideOperator : Operator 33 | { 34 | public DivideOperator() 35 | { 36 | } 37 | 38 | internal override Literal Execute(IConstruct argument1, IConstruct argument2) 39 | { 40 | var argument2Value = base.GetTransformedConstruct(argument2); 41 | 42 | if (argument2Value == 0) 43 | throw new DivideByZeroException(argument2.ToString()); 44 | 45 | return base.GetTransformedConstruct(argument1) / argument2Value; 46 | } 47 | 48 | public override string Token 49 | { 50 | get 51 | { 52 | return "/"; 53 | } 54 | } 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /Operators/EqualToOperator.cs: -------------------------------------------------------------------------------- 1 | /* _________________________________________________ 2 | 3 | (c) Hi-Integrity Systems 2012. All rights reserved. 4 | www.hisystems.com.au - Toby Wicks 5 | github.com/hisystems/Interpreter 6 | 7 | Licensed under the Apache License, Version 2.0 (the "License"); 8 | you may not use this file except in compliance with the License. 9 | You may obtain a copy of the License at 10 | 11 | http://www.apache.org/licenses/LICENSE-2.0 12 | 13 | Unless required by applicable law or agreed to in writing, software 14 | distributed under the License is distributed on an "AS IS" BASIS, 15 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | See the License for the specific language governing permissions and 17 | limitations under the License. 18 | ___________________________________________________ */ 19 | 20 | using System; 21 | using System.Collections.Generic; 22 | using System.Linq; 23 | using System.Text; 24 | 25 | namespace HiSystems.Interpreter 26 | { 27 | /// 28 | /// Compares two numeric, text, boolean or datetime values. 29 | /// Usage: 30 | /// numericValue = numericValue 31 | /// booleanValue = booleanValue 32 | /// text = text 33 | /// dateTime = dateTime 34 | /// Examples: 35 | /// 1 = 2 36 | /// true = false 37 | /// 'a' = 'b' 38 | /// #2000-1-1# = #2000-1-2# 39 | /// 40 | public class EqualToOperator : Operator 41 | { 42 | public EqualToOperator() 43 | { 44 | } 45 | 46 | internal override Literal Execute(IConstruct argument1, IConstruct argument2) 47 | { 48 | var argument1Transformed = base.GetTransformedConstruct(argument1); 49 | var argument2Transformed = base.GetTransformedConstruct(argument2); 50 | 51 | if (argument1Transformed is Number && argument2Transformed is Number) 52 | return ((Number)argument1Transformed) == ((Number)argument2Transformed); 53 | else if (argument1Transformed is Boolean && argument2Transformed is Boolean) 54 | return ((Boolean)argument1Transformed) == ((Boolean)argument2Transformed); 55 | else if (argument1Transformed is DateTime && argument2Transformed is DateTime) 56 | return ((DateTime)argument1Transformed) == ((DateTime)argument2Transformed); 57 | else if (argument1Transformed is Text && argument2Transformed is Text) 58 | return ((Text)argument1Transformed) == ((Text)argument2Transformed); 59 | else 60 | throw new InvalidOperationException(String.Format("Equality operator requires arguments of type Number, DateTime or Boolean. Argument types are {0} {1}.", argument1Transformed.GetType().Name, argument2Transformed.GetType().Name)); 61 | } 62 | 63 | public override string Token 64 | { 65 | get 66 | { 67 | return "="; 68 | } 69 | } 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /Operators/GreaterThanOperator.cs: -------------------------------------------------------------------------------- 1 | /* _________________________________________________ 2 | 3 | (c) Hi-Integrity Systems 2012. All rights reserved. 4 | www.hisystems.com.au - Toby Wicks 5 | github.com/hisystems/Interpreter 6 | 7 | Licensed under the Apache License, Version 2.0 (the "License"); 8 | you may not use this file except in compliance with the License. 9 | You may obtain a copy of the License at 10 | 11 | http://www.apache.org/licenses/LICENSE-2.0 12 | 13 | Unless required by applicable law or agreed to in writing, software 14 | distributed under the License is distributed on an "AS IS" BASIS, 15 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | See the License for the specific language governing permissions and 17 | limitations under the License. 18 | ___________________________________________________ */ 19 | 20 | using System; 21 | using System.Collections.Generic; 22 | using System.Linq; 23 | using System.Text; 24 | 25 | namespace HiSystems.Interpreter 26 | { 27 | /// 28 | /// Compares two numeric or datetime values. 29 | /// Usage: 30 | /// numericValue > numericValue 31 | /// dateTime > dateTime 32 | /// Examples: 33 | /// 1 > 2 34 | /// #2000-01-02# > #2000-01-01# 35 | /// 36 | public class GreaterThanOperator : Operator 37 | { 38 | public GreaterThanOperator() 39 | { 40 | } 41 | 42 | internal override Literal Execute(IConstruct argument1, IConstruct argument2) 43 | { 44 | var argument1Transformed = base.GetTransformedConstruct(argument1); 45 | var argument2Transformed = base.GetTransformedConstruct(argument2); 46 | 47 | if (argument1Transformed is Number && argument2Transformed is Number) 48 | return ((Number)argument1Transformed) > ((Number)argument2Transformed); 49 | else if (argument1Transformed is DateTime && argument2Transformed is DateTime) 50 | return ((DateTime)argument1Transformed) > ((DateTime)argument2Transformed); 51 | else 52 | throw new InvalidOperationException(String.Format("Greater than operator requires arguments of type Number or DateTime. Argument types are {0} {1}.", argument1Transformed.GetType().Name, argument2Transformed.GetType().Name)); 53 | } 54 | 55 | public override string Token 56 | { 57 | get 58 | { 59 | return ">"; 60 | } 61 | } 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /Operators/GreaterThanOrEqualToOperator.cs: -------------------------------------------------------------------------------- 1 | /* _________________________________________________ 2 | 3 | (c) Hi-Integrity Systems 2012. All rights reserved. 4 | www.hisystems.com.au - Toby Wicks 5 | github.com/hisystems/Interpreter 6 | 7 | Licensed under the Apache License, Version 2.0 (the "License"); 8 | you may not use this file except in compliance with the License. 9 | You may obtain a copy of the License at 10 | 11 | http://www.apache.org/licenses/LICENSE-2.0 12 | 13 | Unless required by applicable law or agreed to in writing, software 14 | distributed under the License is distributed on an "AS IS" BASIS, 15 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | See the License for the specific language governing permissions and 17 | limitations under the License. 18 | ___________________________________________________ */ 19 | 20 | using System; 21 | using System.Collections.Generic; 22 | using System.Linq; 23 | using System.Text; 24 | 25 | namespace HiSystems.Interpreter 26 | { 27 | /// 28 | /// Compares two numeric or datetime values. 29 | /// Usage: 30 | /// numericValue >= numericValue 31 | /// dateTime >= dateTime 32 | /// Examples: 33 | /// 1 >= 2 34 | /// #2000-01-02# >= #2000-01-01# 35 | /// 36 | public class GreaterThanOrEqualToOperator : Operator 37 | { 38 | public GreaterThanOrEqualToOperator() 39 | { 40 | } 41 | 42 | internal override Literal Execute(IConstruct argument1, IConstruct argument2) 43 | { 44 | var argument1Transformed = base.GetTransformedConstruct(argument1); 45 | var argument2Transformed = base.GetTransformedConstruct(argument2); 46 | 47 | if (argument1Transformed is Number && argument2Transformed is Number) 48 | return ((Number)argument1Transformed) >= ((Number)argument2Transformed); 49 | else if (argument1Transformed is DateTime && argument2Transformed is DateTime) 50 | return ((DateTime)argument1Transformed) >= ((DateTime)argument2Transformed); 51 | else 52 | throw new InvalidOperationException(String.Format("Greater than or equal to operator requires arguments of type Number or DateTime. Argument types are {0} {1}.", argument1Transformed.GetType().Name, argument2Transformed.GetType().Name)); 53 | } 54 | 55 | public override string Token 56 | { 57 | get 58 | { 59 | return ">="; 60 | } 61 | } 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /Operators/LessThanOperator.cs: -------------------------------------------------------------------------------- 1 | /* _________________________________________________ 2 | 3 | (c) Hi-Integrity Systems 2012. All rights reserved. 4 | www.hisystems.com.au - Toby Wicks 5 | github.com/hisystems/Interpreter 6 | 7 | Licensed under the Apache License, Version 2.0 (the "License"); 8 | you may not use this file except in compliance with the License. 9 | You may obtain a copy of the License at 10 | 11 | http://www.apache.org/licenses/LICENSE-2.0 12 | 13 | Unless required by applicable law or agreed to in writing, software 14 | distributed under the License is distributed on an "AS IS" BASIS, 15 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | See the License for the specific language governing permissions and 17 | limitations under the License. 18 | ___________________________________________________ */ 19 | 20 | using System; 21 | using System.Collections.Generic; 22 | using System.Linq; 23 | using System.Text; 24 | 25 | namespace HiSystems.Interpreter 26 | { 27 | /// 28 | /// Compares two numeric or datetime values. 29 | /// Usage: 30 | /// numericValue < numericValue 31 | /// dateTime < dateTime 32 | /// Examples: 33 | /// 1 < 2 34 | /// #2000-01-02# < #2000-01-01# 35 | /// 36 | public class LessThanOperator : Operator 37 | { 38 | public LessThanOperator() 39 | { 40 | } 41 | 42 | internal override Literal Execute(IConstruct argument1, IConstruct argument2) 43 | { 44 | var argument1Transformed = base.GetTransformedConstruct(argument1); 45 | var argument2Transformed = base.GetTransformedConstruct(argument2); 46 | 47 | if (argument1Transformed is Number && argument2Transformed is Number) 48 | return ((Number)argument1Transformed) < ((Number)argument2Transformed); 49 | else if (argument1Transformed is DateTime && argument2Transformed is DateTime) 50 | return ((DateTime)argument1Transformed) < ((DateTime)argument2Transformed); 51 | else 52 | throw new InvalidOperationException(String.Format("Less than operator requires arguments of type Number or DateTime. Argument types are {0} {1}.", argument1Transformed.GetType().Name, argument2Transformed.GetType().Name)); 53 | } 54 | 55 | public override string Token 56 | { 57 | get 58 | { 59 | return "<"; 60 | } 61 | } 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /Operators/LessThanOrEqualToOperator.cs: -------------------------------------------------------------------------------- 1 | /* _________________________________________________ 2 | 3 | (c) Hi-Integrity Systems 2012. All rights reserved. 4 | www.hisystems.com.au - Toby Wicks 5 | github.com/hisystems/Interpreter 6 | 7 | Licensed under the Apache License, Version 2.0 (the "License"); 8 | you may not use this file except in compliance with the License. 9 | You may obtain a copy of the License at 10 | 11 | http://www.apache.org/licenses/LICENSE-2.0 12 | 13 | Unless required by applicable law or agreed to in writing, software 14 | distributed under the License is distributed on an "AS IS" BASIS, 15 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | See the License for the specific language governing permissions and 17 | limitations under the License. 18 | ___________________________________________________ */ 19 | 20 | using System; 21 | using System.Collections.Generic; 22 | using System.Linq; 23 | using System.Text; 24 | 25 | namespace HiSystems.Interpreter 26 | { 27 | /// 28 | /// Compares two numeric or datetime values. 29 | /// Usage: 30 | /// numericValue <= numericValue 31 | /// dateTime <= dateTime 32 | /// Examples: 33 | /// 1 <= 2 34 | /// #2000-01-02# <= #2000-01-0 35 | /// 36 | public class LessThanOrEqualToOperator : Operator 37 | { 38 | public LessThanOrEqualToOperator() 39 | { 40 | } 41 | 42 | internal override Literal Execute(IConstruct argument1, IConstruct argument2) 43 | { 44 | var argument1Transformed = base.GetTransformedConstruct(argument1); 45 | var argument2Transformed = base.GetTransformedConstruct(argument2); 46 | 47 | if (argument1Transformed is Number && argument2Transformed is Number) 48 | return ((Number)argument1Transformed) <= ((Number)argument2Transformed); 49 | else if (argument1Transformed is DateTime && argument2Transformed is DateTime) 50 | return ((DateTime)argument1Transformed) <= ((DateTime)argument2Transformed); 51 | else 52 | throw new InvalidOperationException(String.Format("Less than or equal to operator requires arguments of type Number or DateTime. Argument types are {0} {1}.", argument1Transformed.GetType().Name, argument2Transformed.GetType().Name)); 53 | } 54 | 55 | public override string Token 56 | { 57 | get 58 | { 59 | return "<="; 60 | } 61 | } 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /Operators/ModulusOperator.cs: -------------------------------------------------------------------------------- 1 | /* _________________________________________________ 2 | 3 | (c) Hi-Integrity Systems 2012. All rights reserved. 4 | www.hisystems.com.au - Toby Wicks 5 | github.com/hisystems/Interpreter 6 | 7 | Licensed under the Apache License, Version 2.0 (the "License"); 8 | you may not use this file except in compliance with the License. 9 | You may obtain a copy of the License at 10 | 11 | http://www.apache.org/licenses/LICENSE-2.0 12 | 13 | Unless required by applicable law or agreed to in writing, software 14 | distributed under the License is distributed on an "AS IS" BASIS, 15 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | See the License for the specific language governing permissions and 17 | limitations under the License. 18 | ___________________________________________________ */ 19 | 20 | using System; 21 | using System.Collections.Generic; 22 | using System.Linq; 23 | using System.Text; 24 | 25 | namespace HiSystems.Interpreter 26 | { 27 | /// 28 | /// Returns the modulus from two numeric values. 29 | /// Usage: numericValue % numericValue 30 | /// Example: 1 % 2 31 | /// 32 | public class ModulusOperator : Operator 33 | { 34 | public ModulusOperator() 35 | { 36 | } 37 | 38 | internal override Literal Execute(IConstruct argument1, IConstruct argument2) 39 | { 40 | return base.GetTransformedConstruct(argument1) % base.GetTransformedConstruct(argument2); 41 | } 42 | 43 | public override string Token 44 | { 45 | get 46 | { 47 | return "%"; 48 | } 49 | } 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /Operators/MultiplyOperator.cs: -------------------------------------------------------------------------------- 1 | /* _________________________________________________ 2 | 3 | (c) Hi-Integrity Systems 2012. All rights reserved. 4 | www.hisystems.com.au - Toby Wicks 5 | github.com/hisystems/Interpreter 6 | 7 | Licensed under the Apache License, Version 2.0 (the "License"); 8 | you may not use this file except in compliance with the License. 9 | You may obtain a copy of the License at 10 | 11 | http://www.apache.org/licenses/LICENSE-2.0 12 | 13 | Unless required by applicable law or agreed to in writing, software 14 | distributed under the License is distributed on an "AS IS" BASIS, 15 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | See the License for the specific language governing permissions and 17 | limitations under the License. 18 | ___________________________________________________ */ 19 | 20 | using System; 21 | using System.Collections.Generic; 22 | using System.Linq; 23 | using System.Text; 24 | 25 | namespace HiSystems.Interpreter 26 | { 27 | /// 28 | /// Multiplies two numeric values. 29 | /// Usage: numericValue * numericValue 30 | /// Example: 1 * 2 31 | /// 32 | public class MultiplyOperator : Operator 33 | { 34 | public MultiplyOperator() 35 | { 36 | } 37 | 38 | internal override Literal Execute(IConstruct argument1, IConstruct argument2) 39 | { 40 | return base.GetTransformedConstruct(argument1) * base.GetTransformedConstruct(argument2); 41 | } 42 | 43 | public override string Token 44 | { 45 | get 46 | { 47 | return "*"; 48 | } 49 | } 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /Operators/NotEqualToOperator.cs: -------------------------------------------------------------------------------- 1 | /* _________________________________________________ 2 | 3 | (c) Hi-Integrity Systems 2012. All rights reserved. 4 | www.hisystems.com.au - Toby Wicks 5 | github.com/hisystems/Interpreter 6 | 7 | Licensed under the Apache License, Version 2.0 (the "License"); 8 | you may not use this file except in compliance with the License. 9 | You may obtain a copy of the License at 10 | 11 | http://www.apache.org/licenses/LICENSE-2.0 12 | 13 | Unless required by applicable law or agreed to in writing, software 14 | distributed under the License is distributed on an "AS IS" BASIS, 15 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | See the License for the specific language governing permissions and 17 | limitations under the License. 18 | ___________________________________________________ */ 19 | 20 | using System; 21 | using System.Collections.Generic; 22 | using System.Linq; 23 | using System.Text; 24 | 25 | namespace HiSystems.Interpreter 26 | { 27 | /// 28 | /// Compares two numeric, text, boolean or datetime values. 29 | /// Usage: 30 | /// numericValue <> numericValue 31 | /// booleanValue <> booleanValue 32 | /// text <> text 33 | /// dateTime = dateTime 34 | /// Examples: 35 | /// 1 <> 2 36 | /// true <> false 37 | /// 'a' <> 'b' 38 | /// #2000-1-1# <> #2000-1-2# 39 | /// 40 | public class NotEqualToOperator : Operator 41 | { 42 | private EqualToOperator equalToOperator = new EqualToOperator(); 43 | 44 | public NotEqualToOperator() 45 | { 46 | } 47 | 48 | internal override Literal Execute(IConstruct argument1, IConstruct argument2) 49 | { 50 | return !(Boolean)equalToOperator.Execute(argument1, argument2); 51 | } 52 | 53 | public override string Token 54 | { 55 | get 56 | { 57 | return "<>"; 58 | } 59 | } 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /Operators/Operator.cs: -------------------------------------------------------------------------------- 1 | /* _________________________________________________ 2 | 3 | (c) Hi-Integrity Systems 2012. All rights reserved. 4 | www.hisystems.com.au - Toby Wicks 5 | github.com/hisystems/Interpreter 6 | 7 | Licensed under the Apache License, Version 2.0 (the "License"); 8 | you may not use this file except in compliance with the License. 9 | You may obtain a copy of the License at 10 | 11 | http://www.apache.org/licenses/LICENSE-2.0 12 | 13 | Unless required by applicable law or agreed to in writing, software 14 | distributed under the License is distributed on an "AS IS" BASIS, 15 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | See the License for the specific language governing permissions and 17 | limitations under the License. 18 | ___________________________________________________ */ 19 | 20 | using System; 21 | using System.Collections.Generic; 22 | using System.Linq; 23 | using System.Text; 24 | 25 | namespace HiSystems.Interpreter 26 | { 27 | /// 28 | /// Base class for all arithmetic / logical / equality operators. 29 | /// 30 | public abstract class Operator 31 | { 32 | /// 33 | /// The unique token that indicates this operation. 34 | /// For special character tokens (non alpha-numeric characters) this can be at most 2 characters. 35 | /// For example '*' for multiply, or '/' for divide. 36 | /// 37 | public abstract string Token { get; } 38 | 39 | /// 40 | /// Should execute the operation and return the appropriate construct. 41 | /// 42 | internal abstract Literal Execute(IConstruct argument1, IConstruct argument2); 43 | 44 | /// 45 | /// Gets the construct and transforms/executes it and returns it as of type T. 46 | /// If the transformed result is not of type T then an exception is thrown. 47 | /// Minimise the use of this function because it will traverse and execute the entire expression tree if the construct represents an operation or function. 48 | /// 49 | protected T GetTransformedConstruct(IConstruct construct) where T : Literal 50 | { 51 | var transformedConstruct = construct.Transform(); 52 | 53 | return CastConstructToType(transformedConstruct); 54 | } 55 | 56 | /// 57 | /// Throws an exception if the argument passed is not the expected type. 58 | /// 59 | private T CastConstructToType(IConstruct construct) 60 | { 61 | if (!(construct is T)) 62 | throw new InvalidOperationException(String.Format("{0} construct is not of type {1} and cannot be used with the {2} operator", construct.ToString(), typeof(T).Name, this.Token)); 63 | 64 | return (T)construct; 65 | } 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /Operators/OrOperator.cs: -------------------------------------------------------------------------------- 1 | /* _________________________________________________ 2 | 3 | (c) Hi-Integrity Systems 2012. All rights reserved. 4 | www.hisystems.com.au - Toby Wicks 5 | github.com/hisystems/Interpreter 6 | 7 | Licensed under the Apache License, Version 2.0 (the "License"); 8 | you may not use this file except in compliance with the License. 9 | You may obtain a copy of the License at 10 | 11 | http://www.apache.org/licenses/LICENSE-2.0 12 | 13 | Unless required by applicable law or agreed to in writing, software 14 | distributed under the License is distributed on an "AS IS" BASIS, 15 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | See the License for the specific language governing permissions and 17 | limitations under the License. 18 | ___________________________________________________ */ 19 | 20 | using System; 21 | using System.Collections.Generic; 22 | using System.Linq; 23 | using System.Text; 24 | 25 | namespace HiSystems.Interpreter 26 | { 27 | /// 28 | /// Logical Or operator. 29 | /// Usage: booleanValue OR booleanValue 30 | /// Example: true OR false 31 | /// 32 | public class OrOperator : Operator 33 | { 34 | public OrOperator() 35 | { 36 | } 37 | 38 | /// 39 | /// Non-zero arguments are considered true. 40 | /// 41 | internal override Literal Execute(IConstruct argument1, IConstruct argument2) 42 | { 43 | return base.GetTransformedConstruct(argument1) || base.GetTransformedConstruct(argument2); 44 | } 45 | 46 | public override string Token 47 | { 48 | get 49 | { 50 | return "OR"; 51 | } 52 | } 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /Operators/SubtractOperator.cs: -------------------------------------------------------------------------------- 1 | /* _________________________________________________ 2 | 3 | (c) Hi-Integrity Systems 2012. All rights reserved. 4 | www.hisystems.com.au - Toby Wicks 5 | github.com/hisystems/Interpreter 6 | 7 | Licensed under the Apache License, Version 2.0 (the "License"); 8 | you may not use this file except in compliance with the License. 9 | You may obtain a copy of the License at 10 | 11 | http://www.apache.org/licenses/LICENSE-2.0 12 | 13 | Unless required by applicable law or agreed to in writing, software 14 | distributed under the License is distributed on an "AS IS" BASIS, 15 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | See the License for the specific language governing permissions and 17 | limitations under the License. 18 | ___________________________________________________ */ 19 | 20 | using System; 21 | using System.Collections.Generic; 22 | using System.Linq; 23 | using System.Text; 24 | 25 | namespace HiSystems.Interpreter 26 | { 27 | /// 28 | /// Subtracts one numeric value from another or the number of days from a datetime value, or the difference between two dates. 29 | /// Usage: 30 | /// numericValue - numericValue 31 | /// dateTime - numericDays 32 | /// dateTime - dateTime 33 | /// Examples: 34 | /// 1 - 2 35 | /// #2000-01-01# - 1 36 | /// #2000-01-02# - #2000-01-01# 37 | /// 38 | public class SubtractOperator : Operator 39 | { 40 | public SubtractOperator() 41 | { 42 | } 43 | 44 | internal override Literal Execute(IConstruct argument1, IConstruct argument2) 45 | { 46 | var argument1Transformed = base.GetTransformedConstruct(argument1); 47 | var argument2Transformed = base.GetTransformedConstruct(argument2); 48 | 49 | if (argument1Transformed is Number && argument2Transformed is Number) 50 | return ((Number)argument1Transformed) - ((Number)argument2Transformed); 51 | else if (argument1Transformed is DateTime && argument2Transformed is Number) 52 | return ((DateTime)argument1Transformed) - ((Number)argument2Transformed); 53 | else if (argument1Transformed is DateTime && argument2Transformed is DateTime) 54 | return (((DateTime)argument1Transformed) - ((DateTime)argument2Transformed)); 55 | else 56 | throw new InvalidOperationException(String.Format("Subtract operator requires arguments of type Number or DateTime. Argument types are {0} {1}.", argument1Transformed.GetType().Name, argument2Transformed.GetType().Name)); 57 | } 58 | 59 | public override string Token 60 | { 61 | get 62 | { 63 | return "-"; 64 | } 65 | } 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | Interpreter 2 | =========== 3 | 4 | Overview 5 | -------- 6 | The Interpreter is an expression interpreter written in pure C#. It parses any mathematical or logical expression and returns a result. The return result depends on the return type of the last function / operation. An expression can contain variables that can be supplied before the expression is executed and the result returned. 7 | 8 | Video Tutorial 9 | -------------- 10 | The video overview and tutorial is available here: http://youtu.be/D9UGHPQpKP4 11 | 12 | ### Examples: 13 | 1. Passing a variable to the expression. The example below parses the expression and creates the expression tree via Engine.Parse(). The variables are then supplied to the expression and the expression executed via Execute(). 14 | ```csharp 15 | var expression = new Engine().Parse("SUM(A) * 2 - B"); 16 | expression.Variables["A"].Value = new Array(new decimal[] { 1, 2, 3, 4 }); 17 | expression.Variables["B"].Value = new Number(10); 18 | decimal result = expression.Execute(); 19 | ``` 20 | 21 | 2. Using an IF function: 22 | ```csharp 23 | decimal result = new Engine().Parse("IF(1 < 2, 10, 20)").Execute(); 24 | ``` 25 | 26 | 3. Custom functions can provide support for accessing data from a database: 27 | ```csharp 28 | class GetMyDataFunction : Function 29 | { 30 | public override string Name 31 | { 32 | get 33 | { 34 | return "GETMYDATA"; 35 | } 36 | } 37 | 38 | public override Literal Execute(IConstruct[] arguments) 39 | { 40 | base.EnsureArgumentCountIs(arguments, 2); 41 | 42 | var tableName = base.GetTransformedArgument(arguments, 0); 43 | var fieldName = base.GetTransformedArgument(arguments, 1); 44 | 45 | // Retrieve data using tableName and fieldName and return Array. 46 | // This return value can then be used by any functions that accept Array as an argument such as SUM(). 47 | // return new Array(new decimal[] { 1, 2, 3, 4 }); 48 | } 49 | } 50 | 51 | var engine = new Engine(); 52 | engine.Register(new GetMyDataFunction()); 53 | decimal result = Engine.Parse("SUM(GETMYDATA('MyTable', 'MyField'))").Execute(); 54 | ``` 55 | 56 | 4. Custom functions that manipulate values: 57 | ```csharp 58 | class NegateNumber : Function 59 | { 60 | public override string Name 61 | { 62 | get 63 | { 64 | return "NEGATE"; 65 | } 66 | } 67 | 68 | public override Literal Execute(IConstruct[] arguments) 69 | { 70 | base.EnsureArgumentCountIs(arguments, 1); 71 | 72 | decimal inputValue = base.GetTransformedArgument(arguments, argumentIndex: 0); 73 | 74 | return new Number(-inputValue); 75 | } 76 | } 77 | 78 | var engine = new Engine(); 79 | engine.Register(new NegateNumber()); 80 | decimal result = Engine.Parse("NEGATE(1)").Execute(); 81 | ``` 82 | 83 | 5. Working with numbers: 84 | ```csharp 85 | var expression = new Engine().Parse("Sum(Array(1, 2, 3, 4, 5)) / 2"); 86 | decimal result = expression.Execute(); 87 | ``` 88 | 89 | 6. Working with text: 90 | ```csharp 91 | var expression = new Engine().Parse("'$ ' + Format(Amount, '0.00')"); 92 | expression.Variables["Amount"].Value = (Number)1; 93 | string result = expression.Execute(); 94 | ``` 95 | 96 | 7. Working with dates: 97 | ```csharp 98 | var expression = new Engine().Parse("Today() > #2000-1-1#"); 99 | bool result = expression.Execute(); 100 | ``` 101 | 102 | 8. Working with logical operators and parentheses: 103 | ```csharp 104 | var expression = new Engine().Parse("1 < 2 AND (2 > 3 OR 3 < 4)"); 105 | bool result = expression.Execute(); 106 | ``` 107 | 108 | 9. Executing multiple expressions: 109 | ```csharp 110 | var engine = new Engine(); 111 | 112 | var expression1 = engine.Parse("A + 2"); 113 | expression1.Variables["A"].Value = (Number)1; 114 | 115 | var expression2 = engine.Parse("Expression1 + 3"); 116 | expression2.Variables["Expression1"].Value = expression1; 117 | 118 | decimal result = expression2.Execute(); 119 | ``` 120 | 121 | ### Supported Functions 122 | * SUM(array) 123 | * AVG(array) 124 | * IF(condition, trueResult, falseResult) 125 | * Array(item1, item2, ...) 126 | * Format(value [, format]) -- Formats a number or date/time 127 | * Len(text) -- returns the length of a string 128 | * Custom functions can be created by extending Function and registered it via `Engine.Register(Function)` 129 | 130 | ### Supported data types (can be extended) 131 | * Number/decimal 132 | - Example: 1.0 133 | * Boolean 134 | - Supports constants 'true' and 'false' 135 | - Example: true <> false 136 | * Array 137 | - Can contain all data types 138 | - Data types can be mixed in the same array 139 | - Example: Array(1, 2, 3, 4) 140 | * Text 141 | - Delimited by " or ' characters 142 | - Exampe: 'ABC' 143 | * Date/Time 144 | - Surrounded by '#' characters 145 | - Example: #2000-01-30 12:30:03# 146 | 147 | ### Supported functions (can be extended) 148 | * If(condition, trueResult, falseResult) 149 | - Example: If(1 > 2, 10, 20) 150 | * Max(array) 151 | - Example: Max(Array(1, 2, 3)) 152 | * Min(array) 153 | - Example: Min(Array(1, 2, 3)) 154 | * Sum(array): 155 | - Example: Sum(Array(1, 2, 3)) 156 | * Today: 157 | - Returns the date component (no time component) for today. 158 | - Example: Today() + 1 -- returns the date for tomorrow 159 | * Len(text) 160 | - Returns the length of a string 161 | - Example: Len('abc') -- returns 3 162 | 163 | ### Supported Operations (can be extended) 164 | * + - addition (numbers, date/time + number, string concatenation) 165 | * - - subtraction (numbers, date/time - number) 166 | * / - divide 167 | * * - multiply 168 | * = - equal 169 | * <> - not equal to 170 | * < - less than 171 | * > - greater than 172 | * >= - greater than or equal to 173 | * <= - less than or equal to 174 | * OR - logical or 175 | * AND - logical and 176 | 177 | Supported Platforms 178 | ------------------- 179 | Supported platforms are Windows and MonoTouch/Mono. 180 | 181 | License 182 | ------- 183 | Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 184 | 185 | Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. 186 | 187 | Unit Tests 188 | ---------- 189 | The unit test project is available in a separate repository on [Github here](https://github.com/hisystems/Interpreter-UnitTests). It is also a good resource for examples on how to utilise the library. To run the unit tests project in conjunction with the library it must be located in the same directory as the library. 190 | 191 | For example: 192 | 193 | /Interpreter 194 | /Interpreter.UnitTests 195 | --------------------------------------------------------------------------------