From 67475eb6a8d6ee9a59de1f01671bb366c1bb540e Mon Sep 17 00:00:00 2001 From: Brian Hassel Date: Fri, 21 Aug 2015 13:59:39 -0400 Subject: [PATCH] Added new TimeSpan type and XOR operator --- Converters/TimeSpanTypeConverter.cs | 40 +++++ Engine/Engine.cs | 4 + Engine/Token.cs | 4 + Engine/Tokenizer.cs | 24 ++- HiSystems.Interpreter.csproj | 195 +++++++++++----------- Literals/Boolean.cs | 4 + Literals/DateTime.cs | 12 +- Literals/TimeSpan.cs | 94 +++++++++++ Operators/AddOperator.cs | 12 +- Operators/EqualToOperator.cs | 4 +- Operators/GreaterThanOperator.cs | 4 +- Operators/GreaterThanOrEqualToOperator.cs | 4 +- Operators/LessThanOperator.cs | 4 +- Operators/LessThanOrEqualToOperator.cs | 4 +- Operators/SubtractOperator.cs | 10 +- Operators/XorOperator.cs | 29 ++++ 16 files changed, 329 insertions(+), 119 deletions(-) create mode 100644 Converters/TimeSpanTypeConverter.cs create mode 100644 Literals/TimeSpan.cs create mode 100644 Operators/XorOperator.cs diff --git a/Converters/TimeSpanTypeConverter.cs b/Converters/TimeSpanTypeConverter.cs new file mode 100644 index 0000000..3f2897e --- /dev/null +++ b/Converters/TimeSpanTypeConverter.cs @@ -0,0 +1,40 @@ +using System; +using System.ComponentModel; +using System.Globalization; + +namespace HiSystems.Interpreter.Converters { + public class TimeSpanTypeConverter : TypeConverter + { + public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType) + { + return sourceType == typeof(System.TimeSpan); + } + + public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType) + { + return destinationType == typeof(TimeSpan) || destinationType == typeof(Literal); ; + } + + public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value) + { + var date = default(TimeSpan); + if (value is System.TimeSpan) + { + date = (System.TimeSpan)value; + } + + return new TimeSpan(date); + } + + public override object ConvertTo(ITypeDescriptorContext context, CultureInfo culture, object value, Type destinationType) + { + var date = value as TimeSpan; + if (date != null) + { + return (System.TimeSpan)date; + } + + return default(System.TimeSpan); + } + } +} \ No newline at end of file diff --git a/Engine/Engine.cs b/Engine/Engine.cs index cbd5630..39e8d8a 100644 --- a/Engine/Engine.cs +++ b/Engine/Engine.cs @@ -157,6 +157,7 @@ private class ReservedWord new OperatorAndPrecedence() { Operation = new EqualToOperator(), Precedence = 3 }, new OperatorAndPrecedence() { Operation = new NotEqualToOperator(), Precedence = 3 }, new OperatorAndPrecedence() { Operation = new AndOperator(), Precedence = 2 }, + new OperatorAndPrecedence() { Operation = new XorOperator(), Precedence = 2 }, new OperatorAndPrecedence() { Operation = new OrOperator(), Precedence = 1 } }; @@ -309,6 +310,9 @@ private List TranslateTokens(List tokens, List case TokenType.DateTime: translatedTokens.Add(new ConstructToken(new DateTime(System.DateTime.Parse(token.Value)))); break; + case TokenType.TimeSpan: + translatedTokens.Add(new ConstructToken(new TimeSpan(System.TimeSpan.Parse(token.Value)))); + break; case TokenType.Other: var operationForToken = allOperators .Select(item => item.Operation) diff --git a/Engine/Token.cs b/Engine/Token.cs index ccefa76..1282f6d 100644 --- a/Engine/Token.cs +++ b/Engine/Token.cs @@ -66,6 +66,10 @@ internal enum TokenType /// DateTime, + /// + /// Value is surrounded by ` characters. + /// + TimeSpan, /// /// Any other token that is not one of the other token types specified in this enum. /// Usually a special character such as '*' or '^'. diff --git a/Engine/Tokenizer.cs b/Engine/Tokenizer.cs index a9df6c6..ec0d5f6 100644 --- a/Engine/Tokenizer.cs +++ b/Engine/Tokenizer.cs @@ -37,7 +37,8 @@ public static List Parse(string expression) const char RightParenthesis = ')'; const char Comma = ','; const char NumericNegative = '-'; - const char DateTimeDelimiter = '#'; + const char DateTimeDelimiter = '#'; + const char TimeSpanDelimiter = '`'; var whitespaceCharacters = new[] { ' ', '\t' }; var numericCharacters = new[] { '.', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9' }; @@ -46,7 +47,8 @@ public static List Parse(string expression) var textDelimiters = new[] { '\"', '\'' }; bool isNumericNegative = false; bool parsingText = false; - bool parsingDateTime = false; + bool parsingDateTime = false; + bool parsingTimeSpan = false; var tokens = new List(); var currentTokenType = TokenType.Other; @@ -97,7 +99,23 @@ public static List Parse(string expression) } else characterString = character.ToString(); - } + } + else if (character == TimeSpanDelimiter || parsingTimeSpan) + { + if (!parsingTimeSpan) // started parsing + { + characterTokenType = TokenType.TimeSpan; + characterString = String.Empty; // consume character + parsingTimeSpan = true; + } + else if (character == TimeSpanDelimiter) // finished parsing + { + characterString = String.Empty; // consume character + parsingTimeSpan = false; + } + else + characterString = character.ToString(); + } else if (whitespaceCharacters.Contains(character)) { characterTokenType = TokenType.Whitespace; diff --git a/HiSystems.Interpreter.csproj b/HiSystems.Interpreter.csproj index 632cba7..4a5292e 100644 --- a/HiSystems.Interpreter.csproj +++ b/HiSystems.Interpreter.csproj @@ -1,103 +1,106 @@ - - - - Debug - AnyCPU - 8.0.30703 - 2.0 - {65D25F1E-BD7D-4B5E-8457-DD8A4813DD30} - Library - Properties - HiSystems.Interpreter - HiSystems.Interpreter - v3.5 - 512 - - - true - full - false - bin\Debug\ - DEBUG;TRACE - prompt - 4 - - - pdbonly - true - bin\Release\ - TRACE - prompt - 4 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + Debug + AnyCPU + 8.0.30703 + 2.0 + {65D25F1E-BD7D-4B5E-8457-DD8A4813DD30} + Library + Properties + HiSystems.Interpreter + HiSystems.Interpreter + v3.5 + 512 + + + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + --> \ No newline at end of file diff --git a/Literals/Boolean.cs b/Literals/Boolean.cs index 3a839d9..94fa0bc 100644 --- a/Literals/Boolean.cs +++ b/Literals/Boolean.cs @@ -73,6 +73,10 @@ public static implicit operator Boolean(bool value) return value1.value | value2.value; } + public static Boolean operator ^(Boolean value1, Boolean value2) + { + return value1.value ^ value2.value; + } public static Boolean operator!(Boolean value) { return new Boolean(!value.value); diff --git a/Literals/DateTime.cs b/Literals/DateTime.cs index e3085e7..9455ffe 100644 --- a/Literals/DateTime.cs +++ b/Literals/DateTime.cs @@ -69,19 +69,19 @@ public static implicit operator DateTime(System.DateTime value) return !AreEqual(value1, value2); } - public static DateTime operator+(DateTime date, Number days) + public static DateTime operator+(DateTime date, TimeSpan timeSpan) { - return new DateTime(date.value.AddDays((double)days)); + return new DateTime(date.value.Add(timeSpan)); } - public static DateTime operator-(DateTime date, Number days) + public static DateTime operator-(DateTime date, TimeSpan timeSpan) { - return new DateTime(date.value.AddDays(-(double)days)); + return new DateTime(date.value.Subtract(timeSpan)); } - public static Number operator-(DateTime date1, DateTime date2) + public static TimeSpan operator-(DateTime date1, DateTime date2) { - return new Number(Convert.ToDecimal((date1.value - date2.value).TotalDays)); + return new TimeSpan(date1.value - date2.value); } public static Boolean operator>(DateTime value1, DateTime value2) diff --git a/Literals/TimeSpan.cs b/Literals/TimeSpan.cs new file mode 100644 index 0000000..38e02f6 --- /dev/null +++ b/Literals/TimeSpan.cs @@ -0,0 +1,94 @@ +using System.ComponentModel; +using HiSystems.Interpreter.Converters; + +namespace HiSystems.Interpreter { + /// + /// Represents an immutable date / time value. + /// + [TypeConverter(typeof(DateTimeTypeConverter))] + public class TimeSpan : Literal + { + private System.TimeSpan value; + + public TimeSpan(System.TimeSpan value) + { + this.value = value; + } + + public override string ToString() + { + return value.ToString(); + } + + public static implicit operator System.TimeSpan(TimeSpan timeSpan) + { + return timeSpan.value; + } + + public static implicit operator TimeSpan(System.TimeSpan value) + { + return new TimeSpan(value); + } + + public static Boolean operator ==(TimeSpan value1, TimeSpan value2) + { + return AreEqual(value1, value2); + } + + public static Boolean operator !=(TimeSpan value1, TimeSpan value2) + { + return !AreEqual(value1, value2); + } + + public static TimeSpan operator +(TimeSpan date1, TimeSpan date2) + { + return new TimeSpan(date1.value + date2.value); + } + + public static TimeSpan operator -(TimeSpan date1, TimeSpan date2) + { + return new TimeSpan(date1.value - date2.value); + } + + public static Boolean operator >(TimeSpan value1, TimeSpan value2) + { + return new Boolean(value1.value > value2.value); + } + + public static Boolean operator >=(TimeSpan value1, TimeSpan value2) + { + return new Boolean(value1.value >= value2.value); + } + + public static Boolean operator <(TimeSpan value1, TimeSpan value2) + { + return new Boolean(value1.value < value2.value); + } + + public static Boolean operator <=(TimeSpan value1, TimeSpan value2) + { + return new Boolean(value1.value <= value2.value); + } + + public override bool Equals(object obj) + { + if (obj == null || !(obj is TimeSpan)) + return false; + else + return AreEqual(this, (TimeSpan)obj); + } + + public override int GetHashCode() + { + return base.GetHashCode(); + } + + private static Boolean AreEqual(TimeSpan value1, TimeSpan value2) + { + if (ReferenceEquals(value1, null) || ReferenceEquals(value2, null)) + return new Boolean(false); + else + return new Boolean(value1.value == value2.value); + } + } +} \ No newline at end of file diff --git a/Operators/AddOperator.cs b/Operators/AddOperator.cs index 7e063b3..0796568 100644 --- a/Operators/AddOperator.cs +++ b/Operators/AddOperator.cs @@ -48,12 +48,14 @@ internal override Literal Execute(IConstruct argument1, IConstruct argument2) if (argument1Transformed is Number && argument2Transformed is Number) return ((Number)argument1Transformed) + ((Number)argument2Transformed); - else if (argument1Transformed is DateTime && argument2Transformed is Number) - return ((DateTime)argument1Transformed) + ((Number)argument2Transformed); + else if (argument1Transformed is DateTime && argument2Transformed is TimeSpan) + return ((DateTime)argument1Transformed) + ((TimeSpan)argument2Transformed); else if (argument1Transformed is Text && argument2Transformed is Text) - return ((Text)argument1Transformed) + ((Text)argument2Transformed); - else - 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)); + return ((Text)argument1Transformed) + ((Text)argument2Transformed); + else if (argument1Transformed is TimeSpan && argument2Transformed is TimeSpan) + return ((TimeSpan)argument1Transformed) + ((TimeSpan)argument2Transformed); + else + throw new InvalidOperationException(String.Format("Add operator requires arguments of type Number, DateTime, TimeSpan or Text. Argument types are {0} {1}.", argument1Transformed.GetType().Name, argument2Transformed.GetType().Name)); } public override string Token diff --git a/Operators/EqualToOperator.cs b/Operators/EqualToOperator.cs index dd9a690..8c11265 100644 --- a/Operators/EqualToOperator.cs +++ b/Operators/EqualToOperator.cs @@ -54,10 +54,12 @@ internal override Literal Execute(IConstruct argument1, IConstruct argument2) return ((Boolean)argument1Transformed) == ((Boolean)argument2Transformed); else if (argument1Transformed is DateTime && argument2Transformed is DateTime) return ((DateTime)argument1Transformed) == ((DateTime)argument2Transformed); + else if (argument1Transformed is TimeSpan && argument2Transformed is TimeSpan) + return ((TimeSpan)argument1Transformed) == ((TimeSpan)argument2Transformed); else if (argument1Transformed is Text && argument2Transformed is Text) return ((Text)argument1Transformed) == ((Text)argument2Transformed); else - 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)); + throw new InvalidOperationException(String.Format("Equality operator requires arguments of type Number, DateTime, TimeSpan or Boolean. Argument types are {0} {1}.", argument1Transformed.GetType().Name, argument2Transformed.GetType().Name)); } public override string Token diff --git a/Operators/GreaterThanOperator.cs b/Operators/GreaterThanOperator.cs index b44973f..a097dc3 100644 --- a/Operators/GreaterThanOperator.cs +++ b/Operators/GreaterThanOperator.cs @@ -48,8 +48,10 @@ internal override Literal Execute(IConstruct argument1, IConstruct argument2) return ((Number)argument1Transformed) > ((Number)argument2Transformed); else if (argument1Transformed is DateTime && argument2Transformed is DateTime) return ((DateTime)argument1Transformed) > ((DateTime)argument2Transformed); + else if (argument1Transformed is TimeSpan && argument2Transformed is TimeSpan) + return ((TimeSpan)argument1Transformed) > ((TimeSpan)argument2Transformed); else - 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)); + throw new InvalidOperationException(String.Format("Greater than operator requires arguments of type Number, TimeSpan or DateTime. Argument types are {0} {1}.", argument1Transformed.GetType().Name, argument2Transformed.GetType().Name)); } public override string Token diff --git a/Operators/GreaterThanOrEqualToOperator.cs b/Operators/GreaterThanOrEqualToOperator.cs index abab192..47524a4 100644 --- a/Operators/GreaterThanOrEqualToOperator.cs +++ b/Operators/GreaterThanOrEqualToOperator.cs @@ -48,8 +48,10 @@ internal override Literal Execute(IConstruct argument1, IConstruct argument2) return ((Number)argument1Transformed) >= ((Number)argument2Transformed); else if (argument1Transformed is DateTime && argument2Transformed is DateTime) return ((DateTime)argument1Transformed) >= ((DateTime)argument2Transformed); + else if (argument1Transformed is TimeSpan && argument2Transformed is TimeSpan) + return ((TimeSpan)argument1Transformed) >= ((TimeSpan)argument2Transformed); else - 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)); + throw new InvalidOperationException(String.Format("Greater than or equal to operator requires arguments of type Number, TimeSpan or DateTime. Argument types are {0} {1}.", argument1Transformed.GetType().Name, argument2Transformed.GetType().Name)); } public override string Token diff --git a/Operators/LessThanOperator.cs b/Operators/LessThanOperator.cs index b5ad33a..80429fe 100644 --- a/Operators/LessThanOperator.cs +++ b/Operators/LessThanOperator.cs @@ -48,8 +48,10 @@ internal override Literal Execute(IConstruct argument1, IConstruct argument2) return ((Number)argument1Transformed) < ((Number)argument2Transformed); else if (argument1Transformed is DateTime && argument2Transformed is DateTime) return ((DateTime)argument1Transformed) < ((DateTime)argument2Transformed); + else if (argument1Transformed is TimeSpan && argument2Transformed is TimeSpan) + return ((TimeSpan)argument1Transformed) < ((TimeSpan)argument2Transformed); else - 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)); + throw new InvalidOperationException(String.Format("Less than operator requires arguments of type Number, TimeSpan or DateTime. Argument types are {0} {1}.", argument1Transformed.GetType().Name, argument2Transformed.GetType().Name)); } public override string Token diff --git a/Operators/LessThanOrEqualToOperator.cs b/Operators/LessThanOrEqualToOperator.cs index a9e9d6f..3751f6d 100644 --- a/Operators/LessThanOrEqualToOperator.cs +++ b/Operators/LessThanOrEqualToOperator.cs @@ -48,8 +48,10 @@ internal override Literal Execute(IConstruct argument1, IConstruct argument2) return ((Number)argument1Transformed) <= ((Number)argument2Transformed); else if (argument1Transformed is DateTime && argument2Transformed is DateTime) return ((DateTime)argument1Transformed) <= ((DateTime)argument2Transformed); + else if (argument1Transformed is TimeSpan && argument2Transformed is TimeSpan) + return ((TimeSpan)argument1Transformed) <= ((TimeSpan)argument2Transformed); else - 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)); + throw new InvalidOperationException(String.Format("Less than or equal to operator requires arguments of type Number, TimeSpan or DateTime. Argument types are {0} {1}.", argument1Transformed.GetType().Name, argument2Transformed.GetType().Name)); } public override string Token diff --git a/Operators/SubtractOperator.cs b/Operators/SubtractOperator.cs index 47bd278..f1f6adf 100644 --- a/Operators/SubtractOperator.cs +++ b/Operators/SubtractOperator.cs @@ -48,12 +48,14 @@ internal override Literal Execute(IConstruct argument1, IConstruct argument2) if (argument1Transformed is Number && argument2Transformed is Number) return ((Number)argument1Transformed) - ((Number)argument2Transformed); - else if (argument1Transformed is DateTime && argument2Transformed is Number) - return ((DateTime)argument1Transformed) - ((Number)argument2Transformed); + else if (argument1Transformed is DateTime && argument2Transformed is TimeSpan) + return ((DateTime)argument1Transformed) - ((TimeSpan)argument2Transformed); else if (argument1Transformed is DateTime && argument2Transformed is DateTime) - return (((DateTime)argument1Transformed) - ((DateTime)argument2Transformed)); + return (((DateTime)argument1Transformed) - ((DateTime)argument2Transformed)); + else if (argument1Transformed is TimeSpan && argument2Transformed is TimeSpan) + return ((TimeSpan)argument1Transformed) - ((TimeSpan)argument2Transformed); else - 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)); + throw new InvalidOperationException(String.Format("Subtract operator requires arguments of type Number, DateTime, or TimeSpan. Argument types are {0} {1}.", argument1Transformed.GetType().Name, argument2Transformed.GetType().Name)); } public override string Token diff --git a/Operators/XorOperator.cs b/Operators/XorOperator.cs new file mode 100644 index 0000000..622882d --- /dev/null +++ b/Operators/XorOperator.cs @@ -0,0 +1,29 @@ +namespace HiSystems.Interpreter { + /// + /// Logical Xor operator. + /// Usage: booleanValue XOR booleanValue + /// Example: true XOR false + /// + public class XorOperator : Operator + { + public XorOperator() + { + } + + /// + /// Non-zero arguments are considered true. + /// + internal override Literal Execute(IConstruct argument1, IConstruct argument2) + { + return base.GetTransformedConstruct(argument1) ^ base.GetTransformedConstruct(argument2); + } + + public override string Token + { + get + { + return "XOR"; + } + } + } +} \ No newline at end of file