56 changed files with 2892 additions and 0 deletions
			
			
		| @ -0,0 +1,9 @@ | |||
| namespace EC.Helper.RabbitFunc.Compiler; | |||
| 
 | |||
| [Serializable] | |||
| public class CompilerException : Exception | |||
| { | |||
|     public CompilerException(SourceLocation location, string message) | |||
|         : base(string.Format("line:{0} column:{1}: {2}", location.Line, location.Column, message)) | |||
|     { } | |||
| } | |||
| @ -0,0 +1,57 @@ | |||
| namespace EC.Helper.RabbitFunc.Compiler; | |||
| 
 | |||
| [Serializable] | |||
| public readonly struct IndexSpan : IEquatable<IndexSpan> | |||
| { | |||
|     private readonly int start; | |||
|     private readonly int length; | |||
| 
 | |||
|     public IndexSpan(int start, int length) | |||
|     { | |||
|         if (start < 0) | |||
|             throw new ArgumentOutOfRangeException(nameof(start)); | |||
|         if (length < 0) | |||
|             throw new ArgumentOutOfRangeException(nameof(length)); | |||
| 
 | |||
|         this.start = start; | |||
|         this.length = length; | |||
|     } | |||
| 
 | |||
|     public int Start => start; | |||
| 
 | |||
|     public int End => start + length; | |||
| 
 | |||
|     public int Length => length; | |||
| 
 | |||
|     public bool IsEmpty => length == 0; | |||
| 
 | |||
|     public static bool operator ==(IndexSpan left, IndexSpan right) | |||
|     { | |||
|         return left.start == right.start && left.length == right.length; | |||
|     } | |||
| 
 | |||
|     public static bool operator !=(IndexSpan left, IndexSpan right) | |||
|     { | |||
|         return left.start != right.start || left.length != right.length; | |||
|     } | |||
| 
 | |||
|     public bool Equals(IndexSpan other) | |||
|     { | |||
|         return other.start == start && other.length == length; | |||
|     } | |||
| 
 | |||
|     public override bool Equals(object obj) | |||
|     { | |||
|         return obj is IndexSpan other && Equals(other); | |||
|     } | |||
| 
 | |||
|     public override int GetHashCode() | |||
|     { | |||
|         return (start.GetHashCode() << 16) ^ length.GetHashCode(); | |||
|     } | |||
| 
 | |||
|     public override string ToString() | |||
|     { | |||
|         return string.Format("{{start:{0} length:{1}}}", start, length); | |||
|     } | |||
| } | |||
| @ -0,0 +1,258 @@ | |||
| using EC.Helper.RabbitFunc.Expressions; | |||
| using EC.Helper.RabbitFunc.Syntax; | |||
| 
 | |||
| namespace EC.Helper.RabbitFunc.Compiler; | |||
| 
 | |||
| internal class Parser | |||
| { | |||
|     private readonly Tokenizer tokenizer; | |||
|     private TokenWithSpan lookahead; | |||
|     private TokenWithSpan token; | |||
| 
 | |||
|     public Parser(Tokenizer tokenizer) | |||
|     { | |||
|         if (tokenizer == null) | |||
|             throw new ArgumentNullException(nameof(tokenizer)); | |||
| 
 | |||
|         this.tokenizer = tokenizer; | |||
|         Initialize(); | |||
|     } | |||
| 
 | |||
|     private void Initialize() | |||
|     { | |||
|         FetchLookahead(); | |||
|     } | |||
| 
 | |||
|     private Token NextToken() | |||
|     { | |||
|         token = lookahead; | |||
|         FetchLookahead(); | |||
|         return token.Token; | |||
|     } | |||
| 
 | |||
|     private Token PeekToken() | |||
|     { | |||
|         return this.lookahead.Token; | |||
|     } | |||
| 
 | |||
|     private void FetchLookahead() | |||
|     { | |||
|         lookahead = new TokenWithSpan(tokenizer.NextToken(), tokenizer.TokenSpan); | |||
|     } | |||
| 
 | |||
|     private bool PeekToken(TokenKind kind) | |||
|     { | |||
|         return PeekToken().Kind == kind; | |||
|     } | |||
| 
 | |||
|     private bool PeekToken(Token check) | |||
|     { | |||
|         return PeekToken() == check; | |||
|     } | |||
| 
 | |||
|     private bool MaybeEat(TokenKind kind) | |||
|     { | |||
|         if (PeekToken().Kind == kind) | |||
|         { | |||
|             NextToken(); | |||
|             return true; | |||
|         } | |||
|         return false; | |||
|     } | |||
| 
 | |||
|     private LambdaExpression ParseStatement() | |||
|     { | |||
|         while (true) | |||
|         { | |||
|             switch (PeekToken().Kind) | |||
|             { | |||
|                 case TokenKind.Comment: | |||
|                 case TokenKind.NewLine: | |||
|                     NextToken(); | |||
|                     continue; | |||
|                 case TokenKind.EndOfFile: | |||
|                     return null; | |||
| 
 | |||
|                 default: | |||
|                     return ParseLambdaExpression(); | |||
|             } | |||
|         } | |||
|     } | |||
| 
 | |||
|     private ExpressionType GetBinaryOperator(TokenKind kind) | |||
|     { | |||
|         return kind switch | |||
|         { | |||
|             TokenKind.Add => ExpressionType.Add, | |||
|             TokenKind.Subtract => ExpressionType.Subtract, | |||
|             TokenKind.Multiply => ExpressionType.Multiply, | |||
|             TokenKind.Divide => ExpressionType.Divide, | |||
|             TokenKind.Mod => ExpressionType.Modulo, | |||
|             TokenKind.Power => ExpressionType.Power, | |||
|             TokenKind.LessThan => ExpressionType.LessThan, | |||
|             TokenKind.LessThanOrEqual => ExpressionType.LessThanOrEqual, | |||
|             TokenKind.Equal => ExpressionType.Equal, | |||
|             TokenKind.GreaterThanOrEqual => ExpressionType.GreaterThanOrEqual, | |||
|             TokenKind.GreaterThan => ExpressionType.GreaterThan, | |||
|             TokenKind.NotEqual => ExpressionType.NotEqual, | |||
|             _ => throw new CompilerException(tokenizer.Position, string.Format("operator TokenKind:{0} error!", kind.ToString())), | |||
|         }; | |||
|     } | |||
| 
 | |||
|     private IList<Expression> ParseArguments() | |||
|     { | |||
|         List<Expression> list = new List<Expression>(); | |||
|         if (PeekToken().Kind != TokenKind.RightParen) | |||
|         { | |||
|             do | |||
|             { | |||
|                 list.Add(ParseExpression()); | |||
|             } while (MaybeEat(TokenKind.Comma)); | |||
|         } | |||
| 
 | |||
|         return list; | |||
|     } | |||
| 
 | |||
|     private Expression ParseTerm() | |||
|     { | |||
|         Expression expr = null; | |||
|         Token token = NextToken(); | |||
|         switch (token.Kind) | |||
|         { | |||
|             case TokenKind.Constant: | |||
|                 expr = Expression.Constant(token.Value); | |||
|                 break; | |||
| 
 | |||
|             case TokenKind.Subtract: | |||
|                 expr = Expression.Negate(ParseExpression()); | |||
|                 break; | |||
| 
 | |||
|             case TokenKind.LeftParen: | |||
|                 expr = ParseExpression(); | |||
|                 if (!MaybeEat(TokenKind.RightParen)) | |||
|                     throw new CompilerException(tokenizer.Position, PeekToken().Text); | |||
|                 break; | |||
| 
 | |||
|             case TokenKind.Identifier: | |||
|                 switch (PeekToken().Kind) | |||
|                 { | |||
|                     case TokenKind.LeftParen: | |||
|                         NextToken(); | |||
|                         expr = Expression.Call(null, token.Text, ParseArguments()); | |||
|                         if (!MaybeEat(TokenKind.RightParen)) | |||
|                             throw new CompilerException(tokenizer.Position, PeekToken().Text); | |||
| 
 | |||
|                         break; | |||
| 
 | |||
|                     case TokenKind.Dot: | |||
|                         expr = Expression.Parameter(null, token.Text); | |||
|                         while (MaybeEat(TokenKind.Dot)) | |||
|                         { | |||
|                             token = NextToken(); | |||
|                             if (token.Kind != TokenKind.Identifier) | |||
|                                 throw new CompilerException(tokenizer.Position, PeekToken().Text); | |||
|                             if (PeekToken().Kind == TokenKind.LeftParen) | |||
|                             { | |||
|                                 expr = Expression.Call(expr, token.Text, ParseArguments()); | |||
|                                 if (!MaybeEat(TokenKind.RightParen)) | |||
|                                     throw new CompilerException(tokenizer.Position, PeekToken().Text); | |||
|                                 break; | |||
|                             } | |||
| 
 | |||
|                             expr = Expression.Member(expr, token.Text); | |||
|                         } | |||
|                         if (MaybeEat(TokenKind.LeftParen)) | |||
|                         { | |||
|                             expr = Expression.Call(expr, token.Text, ParseArguments()); | |||
|                             if (!MaybeEat(TokenKind.RightParen)) | |||
|                                 throw new CompilerException(tokenizer.Position, PeekToken().Text); | |||
|                         } | |||
|                         break; | |||
| 
 | |||
|                     default: | |||
|                         expr = Expression.Parameter(typeof(double), token.Text); | |||
|                         break; | |||
|                 } | |||
|                 break; | |||
| 
 | |||
|             case TokenKind.Not: | |||
|                 return Expression.Not(ParseExpression()); | |||
| 
 | |||
|             case TokenKind.IF: | |||
|                 if (!MaybeEat(TokenKind.LeftParen)) | |||
|                     throw new CompilerException(tokenizer.Position, PeekToken().Text); | |||
|                 var test = ParseExpression(); | |||
|                 if (!MaybeEat(TokenKind.Comma)) | |||
|                     throw new CompilerException(tokenizer.Position, PeekToken().Text); | |||
|                 var trueExpre = ParseExpression(); | |||
|                 if (!MaybeEat(TokenKind.Comma)) | |||
|                     throw new CompilerException(tokenizer.Position, PeekToken().Text); | |||
|                 var falseExpre = ParseExpression(); | |||
|                 if (!MaybeEat(TokenKind.RightParen)) | |||
|                     throw new CompilerException(tokenizer.Position, PeekToken().Text); | |||
| 
 | |||
|                 expr = Expression.Condition(test, trueExpre, falseExpre); | |||
|                 break; | |||
|         } | |||
| 
 | |||
|         return expr; | |||
|     } | |||
| 
 | |||
|     private Expression ParseExpression(byte precedence = 0) | |||
|     { | |||
|         Expression leftOperand = ParseTerm(); | |||
|         while (true) | |||
|         { | |||
|             Token token = PeekToken(); | |||
|             if (token is OperatorToken operatorToken && operatorToken.Precedence >= precedence) | |||
|             { | |||
|                 NextToken(); | |||
|                 Expression rightOperand = ParseExpression(checked((byte)(operatorToken.Precedence + 1))); | |||
|                 ExpressionType @operator = GetBinaryOperator(token.Kind); | |||
|                 leftOperand = new BinaryExpression(@operator, leftOperand, rightOperand); | |||
|                 continue; | |||
|             } | |||
| 
 | |||
|             break; | |||
|         } | |||
| 
 | |||
|         return leftOperand; | |||
|     } | |||
| 
 | |||
|     private LambdaExpression ParseLambdaExpression() | |||
|     { | |||
|         Token token = NextToken(); | |||
|         if (token.Kind != TokenKind.Identifier) | |||
|             throw new CompilerException(tokenizer.Position, token.Text); | |||
| 
 | |||
|         var name = token.Text; | |||
|         var parameters = new List<ParameterExpression>(); | |||
|         if (MaybeEat(TokenKind.LeftParen)) | |||
|         { | |||
|             if (!MaybeEat(TokenKind.RightParen)) | |||
|             { | |||
|                 do | |||
|                 { | |||
|                     token = NextToken(); | |||
|                     if (token.Kind != TokenKind.Identifier) | |||
|                         throw new CompilerException(tokenizer.Position, token.Text); | |||
| 
 | |||
|                     parameters.Add(Expression.Parameter(typeof(double), token.Text)); | |||
|                 } while (MaybeEat(TokenKind.Comma)); | |||
| 
 | |||
|                 if (!MaybeEat(TokenKind.RightParen)) | |||
|                     throw new CompilerException(tokenizer.Position, token.Text); | |||
|             } | |||
|         } | |||
|         if (!MaybeEat(TokenKind.Assign)) | |||
|             throw new CompilerException(tokenizer.Position, PeekToken().Text); | |||
| 
 | |||
|         Expression body = ParseExpression(); | |||
|         return Expression.Lambda(name, body, parameters); | |||
|     } | |||
| 
 | |||
|     public LambdaExpression Parse() | |||
|     { | |||
|         return ParseStatement(); | |||
|     } | |||
| } | |||
| @ -0,0 +1,97 @@ | |||
| namespace EC.Helper.RabbitFunc.Compiler; | |||
| 
 | |||
| [Serializable] | |||
| public readonly struct SourceLocation : IEquatable<SourceLocation> | |||
| { | |||
|     public static readonly SourceLocation None = new SourceLocation(0, 16707566, 0, true); | |||
|     public static readonly SourceLocation Invalid = new SourceLocation(0, 0, 0, true); | |||
|     public static readonly SourceLocation MinValue = new SourceLocation(0, 1, 1); | |||
| 
 | |||
|     private readonly int index; | |||
|     private readonly int line; | |||
|     private readonly int column; | |||
| 
 | |||
|     public SourceLocation(int index, int line, int column) | |||
|     { | |||
|         if (index < 0) | |||
|             throw new ArgumentOutOfRangeException(nameof(index)); | |||
|         if (line < 1) | |||
|             throw new ArgumentOutOfRangeException(nameof(line)); | |||
|         if (column < 1) | |||
|             throw new ArgumentOutOfRangeException(nameof(column)); | |||
| 
 | |||
|         this.index = index; | |||
|         this.line = line; | |||
|         this.column = column; | |||
|     } | |||
| 
 | |||
|     private SourceLocation(int index, int line, int column, bool _) | |||
|     { | |||
|         this.index = index; | |||
|         this.line = line; | |||
|         this.column = column; | |||
|     } | |||
| 
 | |||
|     public int Index => index; | |||
| 
 | |||
|     public int Line => line; | |||
| 
 | |||
|     public int Column => column; | |||
| 
 | |||
|     public bool IsValid => line != 0 && column != 0; | |||
| 
 | |||
|     public static bool operator ==(SourceLocation left, SourceLocation right) | |||
|     { | |||
|         return left.index == right.index && left.line == right.line && left.column == right.column; | |||
|     } | |||
| 
 | |||
|     public static bool operator !=(SourceLocation left, SourceLocation right) | |||
|     { | |||
|         return left.index != right.index || left.line != right.line || left.column != right.column; | |||
|     } | |||
| 
 | |||
|     public static bool operator <(SourceLocation left, SourceLocation right) | |||
|     { | |||
|         return left.index < right.index; | |||
|     } | |||
| 
 | |||
|     public static bool operator >(SourceLocation left, SourceLocation right) | |||
|     { | |||
|         return left.index > right.index; | |||
|     } | |||
| 
 | |||
|     public static bool operator <=(SourceLocation left, SourceLocation right) | |||
|     { | |||
|         return left.index <= right.index; | |||
|     } | |||
| 
 | |||
|     public static bool operator >=(SourceLocation left, SourceLocation right) | |||
|     { | |||
|         return left.index >= right.index; | |||
|     } | |||
| 
 | |||
|     public static int Compare(SourceLocation left, SourceLocation right) | |||
|     { | |||
|         return left > right ? 1 : left < right ? -1 : 0; | |||
|     } | |||
| 
 | |||
|     public bool Equals(SourceLocation other) | |||
|     { | |||
|         return other.index == index && other.line == line && other.column == column; | |||
|     } | |||
| 
 | |||
|     public override bool Equals(object obj) | |||
|     { | |||
|         return obj is SourceLocation other && Equals(other); | |||
|     } | |||
| 
 | |||
|     public override int GetHashCode() | |||
|     { | |||
|         return (line << 16) ^ column; | |||
|     } | |||
| 
 | |||
|     public override string ToString() | |||
|     { | |||
|         return string.Format("{{index:{0} line:{1} column:{2}}}", index, line, column); | |||
|     } | |||
| } | |||
| @ -0,0 +1,20 @@ | |||
| using EC.Helper.RabbitFunc.Syntax; | |||
| 
 | |||
| namespace EC.Helper.RabbitFunc.Compiler; | |||
| 
 | |||
| [Serializable] | |||
| internal readonly struct TokenWithSpan | |||
| { | |||
|     private readonly Token token; | |||
|     private readonly IndexSpan span; | |||
| 
 | |||
|     public TokenWithSpan(Token token, IndexSpan span) | |||
|     { | |||
|         this.token = token; | |||
|         this.span = span; | |||
|     } | |||
| 
 | |||
|     public IndexSpan Span => span; | |||
| 
 | |||
|     public Token Token => token; | |||
| } | |||
| @ -0,0 +1,403 @@ | |||
| using EC.Helper.RabbitFunc.Syntax; | |||
| 
 | |||
| namespace EC.Helper.RabbitFunc.Compiler; | |||
| 
 | |||
| internal class Tokenizer | |||
| { | |||
|     #region Attr
 | |||
| 
 | |||
|     private const int DefaultBufferCapacity = 1024; | |||
| 
 | |||
|     private readonly TextReader reader; | |||
|     private char[] buffer; | |||
|     private int start; | |||
|     private int end; | |||
|     private int position; | |||
|     private bool endOfStream; | |||
|     private bool multiEolns; | |||
|     private int tokenEnd; | |||
|     private int tokenStartIndex; | |||
|     private int tokenEndIndex; | |||
|     private List<int> newLineLocations; | |||
|     private SourceLocation initialLocation; | |||
| 
 | |||
|     public bool EndOfStream => endOfStream; | |||
| 
 | |||
|     public int Index => tokenStartIndex + Math.Min(position, end) - start; | |||
| 
 | |||
|     public SourceLocation Position => IndexToLocation(Index); | |||
| 
 | |||
|     public IndexSpan TokenSpan => new(tokenStartIndex, tokenEndIndex - tokenStartIndex); | |||
| 
 | |||
|     private int TokenLength => tokenEnd - start; | |||
| 
 | |||
|     #endregion Attr
 | |||
| 
 | |||
|     public Tokenizer(TextReader reader) | |||
|     { | |||
|         this.reader = reader; | |||
|         Init(DefaultBufferCapacity); | |||
|     } | |||
| 
 | |||
|     private void Init(int bufferCapacity) | |||
|     { | |||
|         multiEolns = true; | |||
|         buffer = new char[bufferCapacity]; | |||
|         newLineLocations = new List<int>(); | |||
|         initialLocation = SourceLocation.MinValue; | |||
|     } | |||
| 
 | |||
|     private static void ResizeInternal(ref char[] array, int newSize, int start, int count) | |||
|     { | |||
|         char[] array2 = (newSize != array.Length) ? new char[newSize] : array; | |||
|         Buffer.BlockCopy(array, start * 2, array2, 0, count * 2); | |||
|         array = array2; | |||
|     } | |||
| 
 | |||
|     private static bool IsNameStart(int ch) | |||
|     { | |||
|         return char.IsLetter((char)ch) || ch == 95; | |||
|     } | |||
| 
 | |||
|     private static bool IsNamePart(int ch) | |||
|     { | |||
|         return char.IsLetterOrDigit((char)ch) || ch == 95; | |||
|     } | |||
| 
 | |||
|     private static Token BadChar(int ch) | |||
|     { | |||
|         return Token.Error(((char)ch).ToString()); | |||
|     } | |||
| 
 | |||
|     private void RefillBuffer() | |||
|     { | |||
|         if (end == buffer.Length) | |||
|         { | |||
|             int newSize = Math.Max(Math.Max((end - start) * 2, buffer.Length), position); | |||
|             ResizeInternal(ref buffer, newSize, start, end - start); | |||
|             end -= start; | |||
|             position -= start; | |||
|             start = 0; | |||
|             //_bufferResized = true;
 | |||
|         } | |||
| 
 | |||
|         end += reader.Read(buffer, end, buffer.Length - end); | |||
|     } | |||
| 
 | |||
|     private int Peek() | |||
|     { | |||
|         if (position >= end) | |||
|         { | |||
|             RefillBuffer(); | |||
|             if (position >= end) | |||
|             { | |||
|                 endOfStream = true; | |||
|                 return -1; | |||
|             } | |||
|         } | |||
| 
 | |||
|         return buffer[position]; | |||
|     } | |||
| 
 | |||
|     private bool NextChar(int ch) | |||
|     { | |||
|         if (Peek() == ch) | |||
|         { | |||
|             position++; | |||
|             return true; | |||
|         } | |||
|         return false; | |||
|     } | |||
| 
 | |||
|     private int NextChar() | |||
|     { | |||
|         int result = Peek(); | |||
|         position++; | |||
|         return result; | |||
|     } | |||
| 
 | |||
|     private int ReadLine() | |||
|     { | |||
|         int num; | |||
|         do | |||
|         { | |||
|             num = NextChar(); | |||
|         } while (num != -1 && !IsEoln(num)); | |||
|         if (num == 10) | |||
|         { | |||
|             newLineLocations.Add(Index); | |||
|         } | |||
|         BufferBack(); | |||
|         return num; | |||
|     } | |||
| 
 | |||
|     private bool IsEoln(int current) | |||
|     { | |||
|         return current == 10 || (current == 13 && multiEolns); | |||
|     } | |||
| 
 | |||
|     private void BufferBack() | |||
|     { | |||
|         SeekRelative(-1); | |||
|     } | |||
| 
 | |||
|     private void SeekRelative(int disp) | |||
|     { | |||
|         position += disp; | |||
|     } | |||
| 
 | |||
|     private void MarkTokenEnd() | |||
|     { | |||
|         tokenEnd = Math.Min(position, end); | |||
|         int num = tokenEnd - start; | |||
|         tokenEndIndex = tokenStartIndex + num; | |||
|     } | |||
| 
 | |||
|     private void DiscardToken() | |||
|     { | |||
|         if (tokenEnd == -1) | |||
|         { | |||
|             MarkTokenEnd(); | |||
|         } | |||
| 
 | |||
|         start = tokenEnd; | |||
|         tokenStartIndex = tokenEndIndex; | |||
|         tokenEnd = -1; | |||
|     } | |||
| 
 | |||
|     private string GetTokenString() | |||
|     { | |||
|         return new string(buffer, start, tokenEnd - start); | |||
|     } | |||
| 
 | |||
|     private string GetTokenSubstring(int offset) | |||
|     { | |||
|         return GetTokenSubstring(offset, tokenEnd - start - offset); | |||
|     } | |||
| 
 | |||
|     private string GetTokenSubstring(int offset, int length) | |||
|     { | |||
|         return new string(buffer, start + offset, length); | |||
|     } | |||
| 
 | |||
|     private int SkipWhiteSpace() | |||
|     { | |||
|         int num; | |||
|         do | |||
|         { | |||
|             num = NextChar(); | |||
|         } while (num == 32 || num == 9); | |||
| 
 | |||
|         BufferBack(); | |||
|         DiscardToken(); | |||
|         SeekRelative(1); | |||
|         return num; | |||
|     } | |||
| 
 | |||
|     private SourceLocation IndexToLocation(int index) | |||
|     { | |||
|         int num = newLineLocations.BinarySearch(index); | |||
|         if (num < 0) | |||
|         { | |||
|             if (num == -1) | |||
|             { | |||
|                 int column = checked(index + initialLocation.Column); | |||
|                 if (TokenLength > 0) column -= TokenLength; | |||
|                 return new SourceLocation(index + initialLocation.Index, initialLocation.Line, column); | |||
|             } | |||
|             num = ~num - 1; | |||
|         } | |||
| 
 | |||
|         int fcolumn = index - newLineLocations[num] + initialLocation.Column; | |||
|         if (TokenLength > 0) fcolumn -= TokenLength; | |||
|         return new SourceLocation(index + initialLocation.Index, num + 1 + initialLocation.Line, fcolumn); | |||
|     } | |||
| 
 | |||
|     private Token ReadName() | |||
|     { | |||
|         BufferBack(); | |||
|         int num = NextChar(); | |||
|         if (!IsNameStart(num)) | |||
|             return BadChar(num); | |||
| 
 | |||
|         while (IsNamePart(num)) | |||
|         { | |||
|             num = this.NextChar(); | |||
|         } | |||
| 
 | |||
|         BufferBack(); | |||
|         MarkTokenEnd(); | |||
|         return Token.Identifier(GetTokenString()); | |||
|     } | |||
| 
 | |||
|     private Token ReadNumber() | |||
|     { | |||
|         int num; | |||
|         do | |||
|         { | |||
|             num = NextChar(); | |||
|         } while (48 <= num && num <= 57); | |||
|         if (num == 46) | |||
|         { | |||
|             do | |||
|             { | |||
|                 num = NextChar(); | |||
|             } while (48 <= num && num <= 57); | |||
|         } | |||
| 
 | |||
|         BufferBack(); | |||
|         MarkTokenEnd(); | |||
|         return Token.Constant(double.Parse(GetTokenString())); | |||
|     } | |||
| 
 | |||
|     private Token ReadComment() | |||
|     { | |||
|         BufferBack(); | |||
|         int num = NextChar(); | |||
|         if (num == 42) | |||
|         { | |||
|             do | |||
|             { | |||
|                 num = NextChar(); | |||
|                 if (num == 10) | |||
|                 { | |||
|                     newLineLocations.Add(Index); | |||
|                 } | |||
|             } while (!(num == 42 && NextChar(47))); | |||
| 
 | |||
|             SeekRelative(-2); | |||
|             MarkTokenEnd(); | |||
|             SeekRelative(2); | |||
|             return Token.Comment(GetTokenSubstring(2)); | |||
|         } | |||
|         else | |||
|         { | |||
|             ReadLine(); | |||
|             MarkTokenEnd(); | |||
|             return Token.Comment(GetTokenSubstring(2)); | |||
|         } | |||
|     } | |||
| 
 | |||
|     public Token NextToken() | |||
|     { | |||
|         DiscardToken(); | |||
|         int num = NextChar(); | |||
|         while (true) | |||
|         { | |||
|             switch (num) | |||
|             { | |||
|                 case -1: | |||
|                     return Tokens.EndOfFileToken; | |||
| 
 | |||
|                 case 10://'\n'
 | |||
|                     newLineLocations.Add(Index); | |||
|                     num = SkipWhiteSpace(); | |||
|                     continue; | |||
|                 case 13://'\r'
 | |||
|                 case 32://' '
 | |||
|                     num = SkipWhiteSpace(); | |||
|                     continue; | |||
|                 case 33://!
 | |||
|                     var notToken = NextChar(61) ? Tokens.NotEqualToken : Tokens.NotToken; | |||
|                     MarkTokenEnd(); | |||
|                     return notToken; | |||
| 
 | |||
|                 case 37://'%'
 | |||
|                     MarkTokenEnd(); | |||
|                     return Tokens.ModToken; | |||
| 
 | |||
|                 case 40://'('
 | |||
|                     MarkTokenEnd(); | |||
|                     return Tokens.LeftParenToken; | |||
| 
 | |||
|                 case 41://')'
 | |||
|                     MarkTokenEnd(); | |||
|                     return Tokens.RightParenToken; | |||
| 
 | |||
|                 case 42://'*'
 | |||
|                     MarkTokenEnd(); | |||
|                     return Tokens.MultiplyToken; | |||
| 
 | |||
|                 case 43://'+'
 | |||
|                     MarkTokenEnd(); | |||
|                     return Tokens.AddToken; | |||
| 
 | |||
|                 case 44://','
 | |||
|                     MarkTokenEnd(); | |||
|                     return Tokens.CommaToken; | |||
| 
 | |||
|                 case 45://'-'
 | |||
|                     MarkTokenEnd(); | |||
|                     return Tokens.SubtractToken; | |||
| 
 | |||
|                 case 46://'.'
 | |||
|                     MarkTokenEnd(); | |||
|                     return Tokens.DotToken; | |||
| 
 | |||
|                 case 47://'/'
 | |||
|                     if (NextChar(42) || NextChar(47)) | |||
|                     { | |||
|                         return ReadComment(); | |||
|                     } | |||
| 
 | |||
|                     MarkTokenEnd(); | |||
|                     return Tokens.DivideToken; | |||
| 
 | |||
|                 case 48://'0'
 | |||
|                 case 49: | |||
|                 case 50: | |||
|                 case 51: | |||
|                 case 52: | |||
|                 case 53: | |||
|                 case 54: | |||
|                 case 55: | |||
|                 case 56: | |||
|                 case 57://'9'
 | |||
|                     return ReadNumber(); | |||
| 
 | |||
|                 case 59://';'
 | |||
|                     MarkTokenEnd(); | |||
|                     return Tokens.NewLineToken; | |||
| 
 | |||
|                 case 60://<
 | |||
|                     var lessToken = NextChar(61) ? Tokens.LessThanOrEqualToken : Tokens.LessThanToken; | |||
|                     MarkTokenEnd(); | |||
|                     return lessToken; | |||
| 
 | |||
|                 case 61://'='
 | |||
|                     var assignOrEqualToken = NextChar(61) ? Tokens.EqualToken : Tokens.AssignToken; | |||
|                     MarkTokenEnd(); | |||
|                     return assignOrEqualToken; | |||
| 
 | |||
|                 case 62://>
 | |||
|                     var greaterToken = NextChar(61) ? Tokens.GreaterThanOrEqualToken : Tokens.GreaterThanToken; | |||
|                     MarkTokenEnd(); | |||
|                     return greaterToken; | |||
| 
 | |||
|                 case 91://'['
 | |||
|                     MarkTokenEnd(); | |||
|                     return Tokens.LeftBracketToken; | |||
| 
 | |||
|                 case 93://']'
 | |||
|                     MarkTokenEnd(); | |||
|                     return Tokens.RightBracketToken; | |||
| 
 | |||
|                 case 94://'^'
 | |||
|                     MarkTokenEnd(); | |||
|                     return Tokens.PowerToken; | |||
| 
 | |||
|                 case 105://i
 | |||
|                     if (NextChar(102)) | |||
|                     { | |||
|                         MarkTokenEnd(); | |||
|                         return Tokens.IFToken; | |||
|                     } | |||
| 
 | |||
|                     return ReadName(); | |||
| 
 | |||
|                 default: | |||
|                     return ReadName(); | |||
|             } | |||
|         } | |||
|     } | |||
| } | |||
| @ -0,0 +1,119 @@ | |||
| using EC.Helper.RabbitFunc.Runtime; | |||
| using System.Text; | |||
| 
 | |||
| namespace EC.Helper.RabbitFunc.Expressions; | |||
| 
 | |||
| public class BinaryExpression : Expression | |||
| { | |||
|     internal BinaryExpression(ExpressionType nodeType, Expression left, Expression right) | |||
|         : base(nodeType) | |||
|     { | |||
|         Left = left; | |||
|         Right = right; | |||
|     } | |||
| 
 | |||
|     public Expression Left { get; } | |||
| 
 | |||
|     public Expression Right { get; } | |||
| 
 | |||
|     public override object Eval(RuntimeContext context) | |||
|     { | |||
|         if (context == null) | |||
|             throw new ArgumentNullException(nameof(context)); | |||
| 
 | |||
|         var leftResult = Left.Eval(context); | |||
|         var rightResult = Right.Eval(context); | |||
|         return NodeType switch | |||
|         { | |||
|             ExpressionType.Add => System.Convert.ToDouble(leftResult) + System.Convert.ToDouble(rightResult), | |||
|             ExpressionType.Subtract => System.Convert.ToDouble(leftResult) - System.Convert.ToDouble(rightResult), | |||
|             ExpressionType.Multiply => System.Convert.ToDouble(leftResult) * System.Convert.ToDouble(rightResult), | |||
|             ExpressionType.Divide => System.Convert.ToDouble(leftResult) / System.Convert.ToDouble(rightResult), | |||
|             ExpressionType.Modulo => System.Convert.ToDouble(leftResult) % System.Convert.ToDouble(rightResult), | |||
|             ExpressionType.Power => Math.Pow(System.Convert.ToDouble(leftResult), System.Convert.ToDouble(rightResult)), | |||
|             ExpressionType.LessThan => System.Convert.ToDouble(leftResult) < System.Convert.ToDouble(rightResult), | |||
|             ExpressionType.LessThanOrEqual => System.Convert.ToDouble(leftResult) <= System.Convert.ToDouble(rightResult), | |||
|             ExpressionType.Equal => leftResult is bool ? System.Convert.ToBoolean(leftResult) == System.Convert.ToBoolean(rightResult) : System.Convert.ToDouble(leftResult) == System.Convert.ToDouble(rightResult), | |||
|             ExpressionType.GreaterThanOrEqual => System.Convert.ToDouble(leftResult) >= System.Convert.ToDouble(rightResult), | |||
|             ExpressionType.GreaterThan => System.Convert.ToDouble(leftResult) > System.Convert.ToDouble(rightResult), | |||
|             ExpressionType.NotEqual => leftResult is bool ? System.Convert.ToBoolean(leftResult) != System.Convert.ToBoolean(rightResult) : System.Convert.ToDouble(leftResult) != System.Convert.ToDouble(rightResult), | |||
|             _ => throw new RuntimeException("unknown operator:" + NodeType.ToString()), | |||
|         }; | |||
|     } | |||
| 
 | |||
|     public override string ToString() | |||
|     { | |||
|         var sb = new StringBuilder(); | |||
|         if (Left is BinaryExpression) | |||
|         { | |||
|             sb.Append('('); | |||
|             sb.Append(Left.ToString()); | |||
|             sb.Append(')'); | |||
|         } | |||
|         else | |||
|         { | |||
|             sb.Append(Left.ToString()); | |||
|         } | |||
|         switch (NodeType) | |||
|         { | |||
|             case ExpressionType.Add: | |||
|                 sb.Append('+'); | |||
|                 break; | |||
| 
 | |||
|             case ExpressionType.Subtract: | |||
|                 sb.Append('-'); | |||
|                 break; | |||
| 
 | |||
|             case ExpressionType.Multiply: | |||
|                 sb.Append('*'); | |||
|                 break; | |||
| 
 | |||
|             case ExpressionType.Divide: | |||
|                 sb.Append('/'); | |||
|                 break; | |||
| 
 | |||
|             case ExpressionType.Modulo: | |||
|                 sb.Append('%'); | |||
|                 break; | |||
| 
 | |||
|             case ExpressionType.Power: | |||
|                 sb.Append('^'); | |||
|                 break; | |||
| 
 | |||
|             case ExpressionType.LessThan: | |||
|                 sb.Append('<'); | |||
|                 break; | |||
| 
 | |||
|             case ExpressionType.LessThanOrEqual: | |||
|                 sb.Append("<="); | |||
|                 break; | |||
| 
 | |||
|             case ExpressionType.Equal: | |||
|                 sb.Append("=="); | |||
|                 break; | |||
| 
 | |||
|             case ExpressionType.GreaterThanOrEqual: | |||
|                 sb.Append(">="); | |||
|                 break; | |||
| 
 | |||
|             case ExpressionType.GreaterThan: | |||
|                 sb.Append('>'); | |||
|                 break; | |||
| 
 | |||
|             case ExpressionType.NotEqual: | |||
|                 sb.Append("!="); | |||
|                 break; | |||
| 
 | |||
|             default: | |||
|                 throw new RuntimeException("unknown operator:" + NodeType.ToString()); | |||
|         } | |||
|         if (Right is BinaryExpression) | |||
|             sb.Append('('); | |||
| 
 | |||
|         sb.Append(Right.ToString()); | |||
|         if (Right is BinaryExpression) | |||
|             sb.Append(')'); | |||
| 
 | |||
|         return sb.ToString(); | |||
|     } | |||
| } | |||
| @ -0,0 +1,32 @@ | |||
| using EC.Helper.RabbitFunc.Runtime; | |||
| 
 | |||
| namespace EC.Helper.RabbitFunc.Expressions; | |||
| 
 | |||
| public class ConditionalExpression : Expression | |||
| { | |||
|     internal ConditionalExpression(Expression test, Expression trueExpression, Expression falseExpression) | |||
|         : base(ExpressionType.MethodCall) | |||
|     { | |||
|         Test = test; | |||
|         TrueExpression = trueExpression; | |||
|         FalseExpression = falseExpression; | |||
|     } | |||
| 
 | |||
|     public override ExpressionType NodeType => ExpressionType.Conditional; | |||
| 
 | |||
|     public Expression Test { get; } | |||
| 
 | |||
|     public Expression TrueExpression { get; } | |||
| 
 | |||
|     public Expression FalseExpression { get; } | |||
| 
 | |||
|     public override object Eval(RuntimeContext context) | |||
|     { | |||
|         return (bool)Test.Eval(context) ? TrueExpression.Eval(context) : FalseExpression.Eval(context); | |||
|     } | |||
| 
 | |||
|     public override string ToString() | |||
|     { | |||
|         return string.Format("if({0},{1},{2})", Test, TrueExpression, FalseExpression); | |||
|     } | |||
| } | |||
| @ -0,0 +1,29 @@ | |||
| using EC.Helper.RabbitFunc.Runtime; | |||
| 
 | |||
| namespace EC.Helper.RabbitFunc.Expressions; | |||
| 
 | |||
| public class ConstantExpression : Expression | |||
| { | |||
|     internal ConstantExpression(object value) | |||
|         : base(ExpressionType.Constant) | |||
|     { | |||
|         Value = value; | |||
|     } | |||
| 
 | |||
|     public object Value { get; } | |||
| 
 | |||
|     public override Type Type => Value?.GetType(); | |||
| 
 | |||
|     public override object Eval(RuntimeContext context) | |||
|     { | |||
|         if (context == null) | |||
|             throw new ArgumentNullException(nameof(context)); | |||
| 
 | |||
|         return Value; | |||
|     } | |||
| 
 | |||
|     public override string ToString() | |||
|     { | |||
|         return Value.ToString(); | |||
|     } | |||
| } | |||
| @ -0,0 +1,238 @@ | |||
| using EC.Helper.RabbitFunc.Runtime; | |||
| 
 | |||
| namespace EC.Helper.RabbitFunc.Expressions; | |||
| 
 | |||
| public abstract class Expression | |||
| { | |||
|     protected Expression() | |||
|     { } | |||
| 
 | |||
|     protected Expression(ExpressionType nodeType) | |||
|     { | |||
|         NodeType = nodeType; | |||
|     } | |||
| 
 | |||
|     public virtual ExpressionType NodeType { get; } | |||
| 
 | |||
|     public virtual Type Type { get; } | |||
| 
 | |||
|     public static ParameterExpression Parameter(Type type, string name) | |||
|     { | |||
|         if (name == null) | |||
|             throw new ArgumentNullException(nameof(name)); | |||
| 
 | |||
|         return new ParameterExpression(ExpressionType.Parameter, type, name); | |||
|     } | |||
| 
 | |||
|     public static ParameterExpression Variable(Type type, string name) | |||
|     { | |||
|         if (name == null) | |||
|             throw new ArgumentNullException(nameof(name)); | |||
| 
 | |||
|         return new ParameterExpression(ExpressionType.Variable, type, name); | |||
|     } | |||
| 
 | |||
|     public static MemberExpression Member(Expression instance, string name) | |||
|     { | |||
|         if (instance == null) | |||
|             throw new ArgumentNullException(nameof(instance)); | |||
|         if (name == null) | |||
|             throw new ArgumentNullException(nameof(name)); | |||
| 
 | |||
|         return new MemberExpression(instance, name); | |||
|     } | |||
| 
 | |||
|     public static MemberExpression Field(Expression instance, string name) | |||
|     { | |||
|         if (instance == null) | |||
|             throw new ArgumentNullException(nameof(instance)); | |||
|         if (name == null) | |||
|             throw new ArgumentNullException(nameof(name)); | |||
| 
 | |||
|         return new MemberExpression(instance, name); | |||
|     } | |||
| 
 | |||
|     public static MemberExpression Property(Expression instance, string name) | |||
|     { | |||
|         if (instance == null) | |||
|             throw new ArgumentNullException(nameof(instance)); | |||
|         if (name == null) | |||
|             throw new ArgumentNullException(nameof(name)); | |||
| 
 | |||
|         return new MemberExpression(instance, name); | |||
|     } | |||
| 
 | |||
|     public static BinaryExpression Assign(Expression left, Expression right) | |||
|     { | |||
|         if (left == null) | |||
|             throw new ArgumentNullException(nameof(left)); | |||
|         if (right == null) | |||
|             throw new ArgumentNullException(nameof(right)); | |||
| 
 | |||
|         return new BinaryExpression(ExpressionType.Assign, left, right); | |||
|     } | |||
| 
 | |||
|     public static ConstantExpression Constant(object value) | |||
|     { | |||
|         return new ConstantExpression(value); | |||
|     } | |||
| 
 | |||
|     public static UnaryExpression Convert(Expression expression, Type type) | |||
|     { | |||
|         if (expression == null) | |||
|             throw new ArgumentNullException(nameof(expression)); | |||
|         if (type == null) | |||
|             throw new ArgumentNullException(nameof(type)); | |||
| 
 | |||
|         return new UnaryExpression(ExpressionType.Constant, expression); | |||
|     } | |||
| 
 | |||
|     public static BinaryExpression Add(Expression left, Expression right) | |||
|     { | |||
|         if (left == null) | |||
|             throw new ArgumentNullException(nameof(left)); | |||
|         if (right == null) | |||
|             throw new ArgumentNullException(nameof(right)); | |||
| 
 | |||
|         return new BinaryExpression(ExpressionType.Add, left, right); | |||
|     } | |||
| 
 | |||
|     public static BinaryExpression Subtract(Expression left, Expression right) | |||
|     { | |||
|         if (left == null) | |||
|             throw new ArgumentNullException(nameof(left)); | |||
|         if (right == null) | |||
|             throw new ArgumentNullException(nameof(right)); | |||
| 
 | |||
|         return new BinaryExpression(ExpressionType.Subtract, left, right); | |||
|     } | |||
| 
 | |||
|     public static BinaryExpression Multiply(Expression left, Expression right) | |||
|     { | |||
|         if (left == null) | |||
|             throw new ArgumentNullException(nameof(left)); | |||
|         if (right == null) | |||
|             throw new ArgumentNullException(nameof(right)); | |||
| 
 | |||
|         return new BinaryExpression(ExpressionType.Multiply, left, right); | |||
|     } | |||
| 
 | |||
|     public static BinaryExpression Divide(Expression left, Expression right) | |||
|     { | |||
|         if (left == null) | |||
|             throw new ArgumentNullException(nameof(left)); | |||
|         if (right == null) | |||
|             throw new ArgumentNullException(nameof(right)); | |||
| 
 | |||
|         return new BinaryExpression(ExpressionType.Divide, left, right); | |||
|     } | |||
| 
 | |||
|     public static BinaryExpression Modulo(Expression left, Expression right) | |||
|     { | |||
|         if (left == null) | |||
|             throw new ArgumentNullException(nameof(left)); | |||
|         if (right == null) | |||
|             throw new ArgumentNullException(nameof(right)); | |||
| 
 | |||
|         return new BinaryExpression(ExpressionType.Modulo, left, right); | |||
|     } | |||
| 
 | |||
|     public static BinaryExpression Power(Expression left, Expression right) | |||
|     { | |||
|         if (left == null) | |||
|             throw new ArgumentNullException(nameof(left)); | |||
|         if (right == null) | |||
|             throw new ArgumentNullException(nameof(right)); | |||
| 
 | |||
|         return new BinaryExpression(ExpressionType.Power, left, right); | |||
|     } | |||
| 
 | |||
|     public static UnaryExpression Negate(Expression expression) | |||
|     { | |||
|         if (expression == null) | |||
|             throw new ArgumentNullException(nameof(expression)); | |||
| 
 | |||
|         return new UnaryExpression(ExpressionType.Negate, expression); | |||
|     } | |||
| 
 | |||
|     public static BinaryExpression ArrayIndex(Expression array, Expression index) | |||
|     { | |||
|         if (array == null) | |||
|             throw new ArgumentNullException(nameof(array)); | |||
|         if (index == null) | |||
|             throw new ArgumentNullException(nameof(index)); | |||
| 
 | |||
|         return new BinaryExpression(ExpressionType.ArrayIndex, array, index); | |||
|     } | |||
| 
 | |||
|     public static UnaryExpression ArrayLength(Expression array) | |||
|     { | |||
|         if (array == null) | |||
|             throw new ArgumentNullException(nameof(array)); | |||
| 
 | |||
|         return new UnaryExpression(ExpressionType.ArrayLength, array); | |||
|     } | |||
| 
 | |||
|     public static MethodCallExpression Call(Expression instance, string methodName, params Expression[] arguments) | |||
|     { | |||
|         if (methodName == null) | |||
|             throw new ArgumentNullException(nameof(methodName)); | |||
| 
 | |||
|         return new MethodCallExpression(instance, methodName, arguments); | |||
|     } | |||
| 
 | |||
|     public static MethodCallExpression Call(Expression instance, string methodName, IList<Expression> arguments) | |||
|     { | |||
|         if (methodName == null) | |||
|             throw new ArgumentNullException(nameof(methodName)); | |||
|         if (arguments == null) | |||
|             throw new ArgumentNullException(nameof(arguments)); | |||
| 
 | |||
|         return new MethodCallExpression(instance, methodName, arguments); | |||
|     } | |||
| 
 | |||
|     public static LambdaExpression Lambda(string name, Expression body, params ParameterExpression[] parameters) | |||
|     { | |||
|         if (name == null) | |||
|             throw new ArgumentNullException(nameof(name)); | |||
|         if (body == null) | |||
|             throw new ArgumentNullException(nameof(body)); | |||
| 
 | |||
|         return new LambdaExpression(name, body, parameters); | |||
|     } | |||
| 
 | |||
|     public static LambdaExpression Lambda(string name, Expression body, IList<ParameterExpression> parameters) | |||
|     { | |||
|         if (name == null) | |||
|             throw new ArgumentNullException(nameof(name)); | |||
|         if (body == null) | |||
|             throw new ArgumentNullException(nameof(body)); | |||
| 
 | |||
|         return new LambdaExpression(name, body, parameters.ToArray()); | |||
|     } | |||
| 
 | |||
|     public static UnaryExpression Not(Expression expression) | |||
|     { | |||
|         if (expression == null) | |||
|             throw new ArgumentNullException(nameof(expression)); | |||
| 
 | |||
|         return new UnaryExpression(ExpressionType.Not, expression); | |||
|     } | |||
| 
 | |||
|     public static ConditionalExpression Condition(Expression condition, Expression trueExpression, Expression falseExpression) | |||
|     { | |||
|         if (condition == null) | |||
|             throw new ArgumentNullException(nameof(condition)); | |||
|         if (trueExpression == null) | |||
|             throw new ArgumentNullException(nameof(trueExpression)); | |||
|         if (falseExpression == null) | |||
|             throw new ArgumentNullException(nameof(falseExpression)); | |||
| 
 | |||
|         return new ConditionalExpression(condition, trueExpression, falseExpression); | |||
|     } | |||
| 
 | |||
|     public virtual object Eval(RuntimeContext context) | |||
|     { | |||
|         throw new NotSupportedException(); | |||
|     } | |||
| } | |||
| @ -0,0 +1,146 @@ | |||
| using EC.Helper.RabbitFunc.Runtime; | |||
| 
 | |||
| namespace EC.Helper.RabbitFunc.Expressions; | |||
| 
 | |||
| internal class ExpressionCompiler | |||
| { | |||
|     private readonly Dictionary<string, LambdaCompiler> lambdas = new Dictionary<string, LambdaCompiler>(); | |||
| 
 | |||
|     private LambdaCompiler GetLambdaCompiler(LambdaExpression expression) | |||
|     { | |||
|         if (!lambdas.TryGetValue(expression.Name, out var lambda)) | |||
|         { | |||
|             lambda = new LambdaCompiler(this, expression); | |||
|             lambdas.Add(expression.Name, lambda); | |||
|         } | |||
|         return lambda; | |||
|     } | |||
| 
 | |||
|     public Delegate Compile(LambdaExpression expression, Type delegateType = null) | |||
|     { | |||
|         var lambda = new LambdaCompiler(this, expression); | |||
|         lambdas.Add(lambda.Name, lambda); | |||
|         return lambda.Compile(delegateType); | |||
|     } | |||
| 
 | |||
|     private sealed class LambdaCompiler | |||
|     { | |||
|         private readonly ExpressionCompiler compiler; | |||
|         private readonly LambdaExpression expression; | |||
|         private readonly List<System.Linq.Expressions.ParameterExpression> parameters; | |||
| 
 | |||
|         public LambdaCompiler(ExpressionCompiler compiler, LambdaExpression expression) | |||
|         { | |||
|             this.compiler = compiler; | |||
|             this.expression = expression; | |||
|             this.parameters = new List<System.Linq.Expressions.ParameterExpression>(); | |||
|             foreach (var parameter in expression.Parameters) | |||
|             { | |||
|                 parameters.Add(System.Linq.Expressions.Expression.Parameter(parameter.Type, parameter.Name)); | |||
|             } | |||
|         } | |||
| 
 | |||
|         public string Name => expression.Name; | |||
| 
 | |||
|         private System.Linq.Expressions.Expression EmitExpression(Expression expression) | |||
|         { | |||
|             switch (expression) | |||
|             { | |||
|                 case ConditionalExpression conditionExpression: | |||
|                     { | |||
|                         var test = EmitExpression(conditionExpression.Test); | |||
|                         var trueExpression = EmitExpression(conditionExpression.TrueExpression); | |||
|                         var falseExpression = EmitExpression(conditionExpression.FalseExpression); | |||
|                         return System.Linq.Expressions.Expression.Condition(test, trueExpression, falseExpression); | |||
|                     } | |||
|                 case BinaryExpression binaryExpression: | |||
|                     { | |||
|                         var left = EmitExpression(binaryExpression.Left); | |||
|                         var right = EmitExpression(binaryExpression.Right); | |||
|                         return expression.NodeType switch | |||
|                         { | |||
|                             ExpressionType.Add => System.Linq.Expressions.Expression.Add(left, right), | |||
|                             ExpressionType.Subtract => System.Linq.Expressions.Expression.Subtract(left, right), | |||
|                             ExpressionType.Multiply => System.Linq.Expressions.Expression.Multiply(left, right), | |||
|                             ExpressionType.Divide => System.Linq.Expressions.Expression.Divide(left, right), | |||
|                             ExpressionType.Modulo => System.Linq.Expressions.Expression.Modulo(left, right), | |||
|                             ExpressionType.Power => System.Linq.Expressions.Expression.Power(left, right), | |||
|                             ExpressionType.LessThan => System.Linq.Expressions.Expression.LessThan(left, right), | |||
|                             ExpressionType.LessThanOrEqual => System.Linq.Expressions.Expression.LessThanOrEqual(left, right), | |||
|                             ExpressionType.Equal => System.Linq.Expressions.Expression.Equal(left, right), | |||
|                             ExpressionType.GreaterThanOrEqual => System.Linq.Expressions.Expression.GreaterThanOrEqual(left, right), | |||
|                             ExpressionType.GreaterThan => System.Linq.Expressions.Expression.GreaterThan(left, right), | |||
|                             ExpressionType.NotEqual => System.Linq.Expressions.Expression.NotEqual(left, right), | |||
|                             _ => throw new LambdaCompilerException("unknown operator char:" + expression.NodeType.ToString()), | |||
|                         }; | |||
|                     } | |||
|                 case ConstantExpression constantExpression: | |||
|                     return System.Linq.Expressions.Expression.Constant(constantExpression.Value); | |||
| 
 | |||
|                 case MethodCallExpression methodCallExpression: | |||
|                     { | |||
|                         var lambda = methodCallExpression.GetLambda(this.expression.Domain); | |||
|                         if (lambda == null) throw new LambdaCompilerException(string.Format("missing method:{0}", methodCallExpression.MethodName)); | |||
|                         if (lambda is SystemLambdaExpression systemLambda) | |||
|                         { | |||
|                             var arguments = new List<System.Linq.Expressions.Expression>(); | |||
|                             foreach (var argument in methodCallExpression.Arguments) | |||
|                             { | |||
|                                 arguments.Add(EmitExpression(argument)); | |||
|                             } | |||
|                             return System.Linq.Expressions.Expression.Call(null, systemLambda.Method, arguments); | |||
|                         } | |||
|                         else | |||
|                         { | |||
|                             var elementType = typeof(object); | |||
|                             var customLambda = compiler.GetLambdaCompiler(lambda); | |||
|                             var arguments = new System.Linq.Expressions.Expression[customLambda.parameters.Count]; | |||
|                             for (var i = 0; i < arguments.Length; i++) | |||
|                             { | |||
|                                 var parameter = customLambda.parameters[i]; | |||
|                                 var argument = EmitExpression(methodCallExpression.Arguments[i]); | |||
|                                 arguments[i] = parameter.Type != elementType | |||
|                                     ? System.Linq.Expressions.Expression.Convert(argument, elementType) | |||
|                                     : argument; | |||
|                             } | |||
| 
 | |||
|                             var custom = customLambda.Compile(); | |||
|                             return System.Linq.Expressions.Expression.Convert( | |||
|                                 System.Linq.Expressions.Expression.Call( | |||
|                                     System.Linq.Expressions.Expression.Constant(custom), | |||
|                                     typeof(Delegate).GetMethod(nameof(Delegate.DynamicInvoke)), | |||
|                                     System.Linq.Expressions.Expression.NewArrayInit(elementType, arguments)), | |||
|                                 lambda.Type); | |||
|                         } | |||
|                     } | |||
|                 case ParameterExpression parameterExpression: | |||
|                     { | |||
|                         var parameter = parameters.Find(p => p.Name == parameterExpression.Name); | |||
|                         if (parameter == null) throw new RuntimeException("Missing member:" + parameterExpression.Name); | |||
|                         return parameter; | |||
|                     } | |||
|                 case UnaryExpression unaryExpression: | |||
|                     { | |||
|                         var operand = EmitExpression(unaryExpression.Operand); | |||
|                         return unaryExpression.NodeType switch | |||
|                         { | |||
|                             ExpressionType.Negate => System.Linq.Expressions.Expression.Negate(operand), | |||
|                             ExpressionType.Not => System.Linq.Expressions.Expression.Not(operand), | |||
|                             _ => throw new RuntimeException("unknown NodeType:" + unaryExpression.NodeType.ToString()), | |||
|                         }; | |||
|                     } | |||
|                 default: | |||
|                     return null; | |||
|             } | |||
|         } | |||
| 
 | |||
|         public Delegate Compile(Type delegateType = null) | |||
|         { | |||
|             var body = EmitExpression(this.expression.Body); | |||
|             var lambda = delegateType == null | |||
|                 ? System.Linq.Expressions.Expression.Lambda(body, this.expression.Name, parameters) | |||
|                 : System.Linq.Expressions.Expression.Lambda(delegateType, body, this.expression.Name, parameters); | |||
|             return lambda.Compile(); | |||
|         } | |||
|     } | |||
| } | |||
| @ -0,0 +1,65 @@ | |||
| namespace EC.Helper.RabbitFunc.Expressions; | |||
| 
 | |||
| public enum ExpressionType : byte | |||
| { | |||
|     None, | |||
|     Add, | |||
|     Subtract, | |||
|     Multiply, | |||
|     Divide, | |||
|     Modulo, | |||
|     Power, | |||
| 
 | |||
|     Constant, | |||
|     ArrayIndex, | |||
|     ArrayLength, | |||
|     Negate, | |||
|     Convert, | |||
|     Assign, | |||
| 
 | |||
|     MemberAccess, | |||
|     Variable, | |||
|     Parameter, | |||
|     Lambda, | |||
|     MethodCall, | |||
| 
 | |||
|     /// <summary>
 | |||
|     /// <
 | |||
|     /// </summary>
 | |||
|     LessThan, | |||
| 
 | |||
|     /// <summary>
 | |||
|     /// <=
 | |||
|     /// </summary>
 | |||
|     LessThanOrEqual, | |||
| 
 | |||
|     /// <summary>
 | |||
|     /// ==
 | |||
|     /// </summary>
 | |||
|     Equal, | |||
| 
 | |||
|     /// <summary>
 | |||
|     /// >=
 | |||
|     /// </summary>
 | |||
|     GreaterThanOrEqual, | |||
| 
 | |||
|     /// <summary>
 | |||
|     /// >
 | |||
|     /// </summary>
 | |||
|     GreaterThan, | |||
| 
 | |||
|     /// <summary>
 | |||
|     /// !=
 | |||
|     /// </summary>
 | |||
|     NotEqual, | |||
| 
 | |||
|     /// <summary>
 | |||
|     /// !
 | |||
|     /// </summary>
 | |||
|     Not, | |||
| 
 | |||
|     /// <summary>
 | |||
|     /// IF
 | |||
|     /// </summary>
 | |||
|     Conditional, | |||
| } | |||
| @ -0,0 +1,9 @@ | |||
| namespace EC.Helper.RabbitFunc.Expressions; | |||
| 
 | |||
| [Serializable] | |||
| public class LambdaCompilerException : Exception | |||
| { | |||
|     public LambdaCompilerException(string message) | |||
|         : base(message) | |||
|     { } | |||
| } | |||
| @ -0,0 +1,75 @@ | |||
| using EC.Helper.RabbitFunc.Runtime; | |||
| using System.Collections.ObjectModel; | |||
| using System.Text; | |||
| 
 | |||
| namespace EC.Helper.RabbitFunc.Expressions; | |||
| 
 | |||
| public class LambdaExpression : Expression | |||
| { | |||
|     internal LambdaExpression(string name, Expression body, params ParameterExpression[] parameters) | |||
|         : base(ExpressionType.Lambda) | |||
|     { | |||
|         Name = name; | |||
|         Parameters = new ReadOnlyCollection<ParameterExpression>(parameters); | |||
|         Body = body; | |||
|     } | |||
| 
 | |||
|     public RabbitDomain Domain { get; internal set; } | |||
| 
 | |||
|     public string Name { get; } | |||
| 
 | |||
|     public ReadOnlyCollection<ParameterExpression> Parameters { get; } | |||
| 
 | |||
|     public Expression Body { get; } | |||
| 
 | |||
|     public override Type Type => typeof(double); | |||
| 
 | |||
|     public Delegate Compile(Type deletageType = null) | |||
|     { | |||
|         var lambdaCompiler = new ExpressionCompiler(); | |||
|         return lambdaCompiler.Compile(this, deletageType); | |||
|     } | |||
| 
 | |||
|     public T Compile<T>() | |||
|     { | |||
|         return (T)(object)Compile(typeof(T)); | |||
|     } | |||
| 
 | |||
|     public override object Eval(RuntimeContext context) | |||
|     { | |||
|         if (context == null) | |||
|             throw new ArgumentNullException(nameof(context)); | |||
| 
 | |||
|         var lambdaContext = new RuntimeContext(context); | |||
|         lambdaContext.Domain = Domain; | |||
|         for (int i = 0; i < Parameters.Count; i++) | |||
|         { | |||
|             var parameter = Parameters[i]; | |||
|             var value = parameter.Eval(context); | |||
|             lambdaContext.Variable(parameter.Name, value); | |||
|         } | |||
| 
 | |||
|         return Body.Eval(lambdaContext); | |||
|     } | |||
| 
 | |||
|     public override string ToString() | |||
|     { | |||
|         var sb = new StringBuilder(); | |||
|         sb.Append(Name).Append('('); | |||
|         using (var e = Parameters.GetEnumerator()) | |||
|         { | |||
|             if (e.MoveNext()) | |||
|             { | |||
|                 sb.Append(e.Current.Name); | |||
|                 while (e.MoveNext()) | |||
|                 { | |||
|                     sb.Append(','); | |||
|                     sb.Append(e.Current.Name); | |||
|                 } | |||
|             } | |||
|         } | |||
| 
 | |||
|         sb.Append(")=").Append(Body.ToString()); | |||
|         return sb.ToString(); | |||
|     } | |||
| } | |||
| @ -0,0 +1,30 @@ | |||
| using EC.Helper.RabbitFunc.Runtime; | |||
| 
 | |||
| namespace EC.Helper.RabbitFunc.Expressions; | |||
| 
 | |||
| public class MemberExpression : Expression | |||
| { | |||
|     internal MemberExpression(Expression instance, string name) | |||
|         : base(ExpressionType.MemberAccess) | |||
|     { | |||
|         Instance = instance; | |||
|         Name = name; | |||
|     } | |||
| 
 | |||
|     public Expression Instance { get; } | |||
| 
 | |||
|     public string Name { get; } | |||
| 
 | |||
|     public override object Eval(RuntimeContext context) | |||
|     { | |||
|         if (context == null) | |||
|             throw new ArgumentNullException(nameof(context)); | |||
| 
 | |||
|         return default(double); | |||
|     } | |||
| 
 | |||
|     public override string ToString() | |||
|     { | |||
|         return Instance == null ? Name : Instance.ToString() + "." + Name; | |||
|     } | |||
| } | |||
| @ -0,0 +1,88 @@ | |||
| using EC.Helper.RabbitFunc.Runtime; | |||
| using System.Collections.ObjectModel; | |||
| using System.Text; | |||
| using MissingMethodException = EC.Helper.RabbitFunc.Runtime.MissingMethodException; | |||
| 
 | |||
| namespace EC.Helper.RabbitFunc.Expressions; | |||
| 
 | |||
| public class MethodCallExpression : Expression | |||
| { | |||
|     internal MethodCallExpression(Expression instance, string methodName, IList<Expression> arguments) | |||
|         : base(ExpressionType.MethodCall) | |||
|     { | |||
|         Instance = instance; | |||
|         MethodName = methodName; | |||
|         Arguments = new ReadOnlyCollection<Expression>(arguments); | |||
|     } | |||
| 
 | |||
|     public Expression Instance { get; } | |||
| 
 | |||
|     public string MethodName { get; } | |||
| 
 | |||
|     public ReadOnlyCollection<Expression> Arguments { get; } | |||
| 
 | |||
|     internal LambdaExpression GetLambda(RabbitDomain domain) | |||
|     { | |||
|         LambdaExpression lambda; | |||
|         if (domain != null) | |||
|         { | |||
|             lambda = domain.GetLambda(MethodName); | |||
|         } | |||
|         else | |||
|         { | |||
|             lambda = Rabbit.GetSystemLambda(MethodName); | |||
|         } | |||
| 
 | |||
|         return lambda; | |||
|     } | |||
| 
 | |||
|     public override object Eval(RuntimeContext context) | |||
|     { | |||
|         if (context == null) | |||
|             throw new ArgumentNullException(nameof(context)); | |||
| 
 | |||
|         if (Instance == null) | |||
|         { | |||
|             var lambdaExpression = GetLambda(context.Domain); | |||
|             if (lambdaExpression == null) | |||
|                 throw new MissingMethodException(string.Format("missing method:{0}", MethodName)); | |||
|             if (Arguments.Count != lambdaExpression.Parameters.Count) | |||
|                 throw new RuntimeException(string.Format("method:{0}. parame count error!", MethodName)); | |||
| 
 | |||
|             var lambdaContext = new RuntimeContext(context); | |||
|             for (int i = 0; i < Arguments.Count; i++) | |||
|             { | |||
|                 var parameter = lambdaExpression.Parameters[i]; | |||
|                 var argument = Arguments[i]; | |||
|                 var value = argument.Eval(context); | |||
|                 lambdaContext.Variable(parameter.Name, value); | |||
|             } | |||
| 
 | |||
|             return lambdaExpression.Body.Eval(lambdaContext); | |||
|         } | |||
|         else | |||
|         { | |||
|             throw new NotSupportedException(); | |||
|         } | |||
|     } | |||
| 
 | |||
|     public override string ToString() | |||
|     { | |||
|         var sb = new StringBuilder(); | |||
|         sb.Append(MethodName).Append('('); | |||
|         using (var e = Arguments.GetEnumerator()) | |||
|         { | |||
|             if (e.MoveNext()) | |||
|             { | |||
|                 sb.Append(e.Current.ToString()); | |||
|                 while (e.MoveNext()) | |||
|                 { | |||
|                     sb.Append(','); | |||
|                     sb.Append(e.Current.ToString()); | |||
|                 } | |||
|             } | |||
|         } | |||
|         sb.Append(')'); | |||
|         return sb.ToString(); | |||
|     } | |||
| } | |||
| @ -0,0 +1,45 @@ | |||
| using EC.Helper.RabbitFunc.Runtime; | |||
| using MissingMemberException = EC.Helper.RabbitFunc.Runtime.MissingMemberException; | |||
| 
 | |||
| namespace EC.Helper.RabbitFunc.Expressions; | |||
| 
 | |||
| public class ParameterExpression : Expression | |||
| { | |||
|     internal ParameterExpression(ExpressionType nodeType, Type type, string name) | |||
|         : base(nodeType) | |||
|     { | |||
|         Type = type; | |||
|         Name = name; | |||
|     } | |||
| 
 | |||
|     public override Type Type { get; } | |||
| 
 | |||
|     public string Name { get; } | |||
| 
 | |||
|     internal static object Access(RuntimeContext context, string name) | |||
|     { | |||
|         var value = context.Access(name); | |||
|         if (value == null) | |||
|             throw new MissingMemberException(string.Format("missing member:{0}", name)); | |||
| 
 | |||
|         return value.Value; | |||
|     } | |||
| 
 | |||
|     internal static T Access<T>(RuntimeContext context, string name) | |||
|     { | |||
|         return (T)Access(context, name); | |||
|     } | |||
| 
 | |||
|     public override object Eval(RuntimeContext context) | |||
|     { | |||
|         if (context == null) | |||
|             throw new ArgumentNullException(nameof(context)); | |||
| 
 | |||
|         return Access(context, Name); | |||
|     } | |||
| 
 | |||
|     public override string ToString() | |||
|     { | |||
|         return Name; | |||
|     } | |||
| } | |||
| @ -0,0 +1,19 @@ | |||
| using System.Reflection; | |||
| 
 | |||
| namespace EC.Helper.RabbitFunc.Expressions; | |||
| 
 | |||
| public abstract class SystemLambdaExpression : LambdaExpression | |||
| { | |||
|     internal SystemLambdaExpression(MethodInfo method, string name, Expression body, params ParameterExpression[] parameters) | |||
|         : base(name, body, parameters) | |||
|     { | |||
|         if (method == null) | |||
|             throw new ArgumentNullException(nameof(method)); | |||
| 
 | |||
|         Method = method; | |||
|     } | |||
| 
 | |||
|     public MethodInfo Method { get; } | |||
| 
 | |||
|     public override Type Type => Method.ReturnType; | |||
| } | |||
| @ -0,0 +1,48 @@ | |||
| using EC.Helper.RabbitFunc.Runtime; | |||
| 
 | |||
| namespace EC.Helper.RabbitFunc.Expressions; | |||
| 
 | |||
| public class UnaryExpression : Expression | |||
| { | |||
|     internal UnaryExpression(ExpressionType type, Expression operand) | |||
|         : base(type) | |||
|     { | |||
|         Operand = operand; | |||
|     } | |||
| 
 | |||
|     public Expression Operand { get; } | |||
| 
 | |||
|     public override object Eval(RuntimeContext context) | |||
|     { | |||
|         if (context == null) | |||
|             throw new ArgumentNullException(nameof(context)); | |||
| 
 | |||
|         object value = Operand.Eval(context); | |||
|         switch (NodeType) | |||
|         { | |||
|             case ExpressionType.Negate: | |||
|                 return (-(double)value); | |||
| 
 | |||
|             case ExpressionType.Not: | |||
|                 return (!(bool)value); | |||
| 
 | |||
|             default: | |||
|                 throw new RuntimeException("unknown unary:" + NodeType.ToString()); | |||
|         } | |||
|     } | |||
| 
 | |||
|     public override string ToString() | |||
|     { | |||
|         switch (NodeType) | |||
|         { | |||
|             case ExpressionType.Negate: | |||
|                 return Operand is BinaryExpression ? "-(" + Operand.ToString() + ")" : "-" + Operand.ToString(); | |||
| 
 | |||
|             case ExpressionType.Not: | |||
|                 return Operand is BinaryExpression ? "!(" + Operand.ToString() + ")" : "!" + Operand.ToString(); | |||
| 
 | |||
|             default: | |||
|                 throw new RuntimeException("unknown operator:" + NodeType.ToString()); | |||
|         } | |||
|     } | |||
| } | |||
| @ -0,0 +1,21 @@ | |||
| using EC.Helper.RabbitFunc.Expressions; | |||
| using EC.Helper.RabbitFunc.Runtime; | |||
| 
 | |||
| namespace EC.Helper.RabbitFunc.Extern; | |||
| 
 | |||
| [ExternLambda] | |||
| internal class AbsLambdaExpression : SystemLambdaExpression | |||
| { | |||
|     public AbsLambdaExpression() | |||
|         : base(typeof(Math).GetMethod(nameof(Math.Abs), new Type[] { typeof(double) }), "abs", new BodyExpression(), | |||
|               Expression.Parameter(typeof(double), "x")) | |||
|     { } | |||
| 
 | |||
|     private class BodyExpression : Expression | |||
|     { | |||
|         public override object Eval(RuntimeContext context) | |||
|         { | |||
|             return Math.Abs(ParameterExpression.Access<double>(context, "x")); | |||
|         } | |||
|     } | |||
| } | |||
| @ -0,0 +1,21 @@ | |||
| using EC.Helper.RabbitFunc.Expressions; | |||
| using EC.Helper.RabbitFunc.Runtime; | |||
| 
 | |||
| namespace EC.Helper.RabbitFunc.Extern; | |||
| 
 | |||
| [ExternLambda] | |||
| internal class AcosLambdaExpression : SystemLambdaExpression | |||
| { | |||
|     public AcosLambdaExpression() | |||
|         : base(typeof(Math).GetMethod(nameof(Math.Acos), new Type[] { typeof(double) }), "acos", new BodyExpression(), | |||
|               Expression.Parameter(typeof(double), "x")) | |||
|     { } | |||
| 
 | |||
|     private class BodyExpression : Expression | |||
|     { | |||
|         public override object Eval(RuntimeContext context) | |||
|         { | |||
|             return Math.Acos(ParameterExpression.Access<double>(context, "x")); | |||
|         } | |||
|     } | |||
| } | |||
| @ -0,0 +1,21 @@ | |||
| using EC.Helper.RabbitFunc.Expressions; | |||
| using EC.Helper.RabbitFunc.Runtime; | |||
| 
 | |||
| namespace EC.Helper.RabbitFunc.Extern; | |||
| 
 | |||
| [ExternLambda] | |||
| internal class AsinLambdaExpression : SystemLambdaExpression | |||
| { | |||
|     public AsinLambdaExpression() | |||
|         : base(typeof(Math).GetMethod(nameof(Math.Asin), new Type[] { typeof(double) }), "asin", new BodyExpression(), | |||
|               Expression.Parameter(typeof(double), "x")) | |||
|     { } | |||
| 
 | |||
|     private class BodyExpression : Expression | |||
|     { | |||
|         public override object Eval(RuntimeContext context) | |||
|         { | |||
|             return Math.Asin(ParameterExpression.Access<double>(context, "x")); | |||
|         } | |||
|     } | |||
| } | |||
| @ -0,0 +1,21 @@ | |||
| using EC.Helper.RabbitFunc.Expressions; | |||
| using EC.Helper.RabbitFunc.Runtime; | |||
| 
 | |||
| namespace EC.Helper.RabbitFunc.Extern; | |||
| 
 | |||
| [ExternLambda] | |||
| internal class AtanLambdaExpression : SystemLambdaExpression | |||
| { | |||
|     public AtanLambdaExpression() | |||
|         : base(typeof(Math).GetMethod(nameof(Math.Atan), new Type[] { typeof(double) }), "atan", new BodyExpression(), | |||
|               Expression.Parameter(typeof(double), "x")) | |||
|     { } | |||
| 
 | |||
|     private class BodyExpression : Expression | |||
|     { | |||
|         public override object Eval(RuntimeContext context) | |||
|         { | |||
|             return Math.Atan(ParameterExpression.Access<double>(context, "x")); | |||
|         } | |||
|     } | |||
| } | |||
| @ -0,0 +1,21 @@ | |||
| using EC.Helper.RabbitFunc.Expressions; | |||
| using EC.Helper.RabbitFunc.Runtime; | |||
| 
 | |||
| namespace EC.Helper.RabbitFunc.Extern; | |||
| 
 | |||
| [ExternLambda] | |||
| internal class CeilingLambdaExpression : SystemLambdaExpression | |||
| { | |||
|     public CeilingLambdaExpression() | |||
|         : base(typeof(Math).GetMethod(nameof(Math.Ceiling), new Type[] { typeof(double) }), "ceiling", new BodyExpression(), | |||
|               Expression.Parameter(typeof(double), "x")) | |||
|     { } | |||
| 
 | |||
|     private class BodyExpression : Expression | |||
|     { | |||
|         public override object Eval(RuntimeContext context) | |||
|         { | |||
|             return Math.Ceiling(ParameterExpression.Access<double>(context, "x")); | |||
|         } | |||
|     } | |||
| } | |||
| @ -0,0 +1,21 @@ | |||
| using EC.Helper.RabbitFunc.Expressions; | |||
| using EC.Helper.RabbitFunc.Runtime; | |||
| 
 | |||
| namespace EC.Helper.RabbitFunc.Extern; | |||
| 
 | |||
| [ExternLambda] | |||
| internal class CosLambdaExpression : SystemLambdaExpression | |||
| { | |||
|     public CosLambdaExpression() | |||
|         : base(typeof(Math).GetMethod(nameof(Math.Cos), new Type[] { typeof(double) }), "cos", new BodyExpression(), | |||
|               Expression.Parameter(typeof(double), "x")) | |||
|     { } | |||
| 
 | |||
|     private class BodyExpression : Expression | |||
|     { | |||
|         public override object Eval(RuntimeContext context) | |||
|         { | |||
|             return Math.Cos(ParameterExpression.Access<double>(context, "x")); | |||
|         } | |||
|     } | |||
| } | |||
| @ -0,0 +1,21 @@ | |||
| using EC.Helper.RabbitFunc.Expressions; | |||
| using EC.Helper.RabbitFunc.Runtime; | |||
| 
 | |||
| namespace EC.Helper.RabbitFunc.Extern; | |||
| 
 | |||
| [ExternLambda] | |||
| internal class CoshLambdaExpression : SystemLambdaExpression | |||
| { | |||
|     public CoshLambdaExpression() | |||
|         : base(typeof(Math).GetMethod(nameof(Math.Cosh), new Type[] { typeof(double) }), "cosh", new BodyExpression(), | |||
|               Expression.Parameter(typeof(double), "x")) | |||
|     { } | |||
| 
 | |||
|     private class BodyExpression : Expression | |||
|     { | |||
|         public override object Eval(RuntimeContext context) | |||
|         { | |||
|             return Math.Cosh(ParameterExpression.Access<double>(context, "x")); | |||
|         } | |||
|     } | |||
| } | |||
| @ -0,0 +1,21 @@ | |||
| using EC.Helper.RabbitFunc.Expressions; | |||
| using EC.Helper.RabbitFunc.Runtime; | |||
| 
 | |||
| namespace EC.Helper.RabbitFunc.Extern; | |||
| 
 | |||
| [ExternLambda] | |||
| internal class ExpLambdaExpression : SystemLambdaExpression | |||
| { | |||
|     public ExpLambdaExpression() | |||
|         : base(typeof(Math).GetMethod(nameof(Math.Exp), new Type[] { typeof(double) }), "exp", new BodyExpression(), | |||
|               Expression.Parameter(typeof(double), "x")) | |||
|     { } | |||
| 
 | |||
|     private class BodyExpression : Expression | |||
|     { | |||
|         public override object Eval(RuntimeContext context) | |||
|         { | |||
|             return Math.Exp(ParameterExpression.Access<double>(context, "x")); | |||
|         } | |||
|     } | |||
| } | |||
| @ -0,0 +1,5 @@ | |||
| namespace EC.Helper.RabbitFunc.Extern; | |||
| 
 | |||
| [AttributeUsage(AttributeTargets.Class, AllowMultiple = false)] | |||
| internal class ExternLambdaAttribute : Attribute | |||
| { } | |||
| @ -0,0 +1,21 @@ | |||
| using EC.Helper.RabbitFunc.Expressions; | |||
| using EC.Helper.RabbitFunc.Runtime; | |||
| 
 | |||
| namespace EC.Helper.RabbitFunc.Extern; | |||
| 
 | |||
| [ExternLambda] | |||
| internal class FloorLambdaExpression : SystemLambdaExpression | |||
| { | |||
|     public FloorLambdaExpression() | |||
|         : base(typeof(Math).GetMethod(nameof(Math.Floor), new Type[] { typeof(double) }), "floor", new BodyExpression(), | |||
|               Expression.Parameter(typeof(double), "x")) | |||
|     { } | |||
| 
 | |||
|     private class BodyExpression : Expression | |||
|     { | |||
|         public override object Eval(RuntimeContext context) | |||
|         { | |||
|             return Math.Floor(ParameterExpression.Access<double>(context, "x")); | |||
|         } | |||
|     } | |||
| } | |||
| @ -0,0 +1,24 @@ | |||
| using EC.Helper.RabbitFunc.Expressions; | |||
| using EC.Helper.RabbitFunc.Runtime; | |||
| 
 | |||
| namespace EC.Helper.RabbitFunc.Extern; | |||
| 
 | |||
| [ExternLambda] | |||
| internal class LogLambdaExpression : SystemLambdaExpression | |||
| { | |||
|     public LogLambdaExpression() | |||
|         : base(typeof(Math).GetMethod(nameof(Math.Log), new Type[] { typeof(double), typeof(double) }), "log", new BodyExpression(), | |||
|               Expression.Parameter(typeof(double), "x"), | |||
|               Expression.Parameter(typeof(double), "e")) | |||
|     { } | |||
| 
 | |||
|     private class BodyExpression : Expression | |||
|     { | |||
|         public override object Eval(RuntimeContext context) | |||
|         { | |||
|             var x = ParameterExpression.Access<double>(context, "x"); | |||
|             var e = ParameterExpression.Access<double>(context, "e"); | |||
|             return Math.Log(x, e); | |||
|         } | |||
|     } | |||
| } | |||
| @ -0,0 +1,24 @@ | |||
| using EC.Helper.RabbitFunc.Expressions; | |||
| using EC.Helper.RabbitFunc.Runtime; | |||
| 
 | |||
| namespace EC.Helper.RabbitFunc.Extern; | |||
| 
 | |||
| [ExternLambda] | |||
| internal class MaxLambdaExpression : SystemLambdaExpression | |||
| { | |||
|     public MaxLambdaExpression() | |||
|         : base(typeof(Math).GetMethod(nameof(Math.Max), new Type[] { typeof(double), typeof(double) }), "max", new BodyExpression(), | |||
|               Expression.Parameter(typeof(double), "left"), | |||
|               Expression.Parameter(typeof(double), "right")) | |||
|     { } | |||
| 
 | |||
|     private class BodyExpression : Expression | |||
|     { | |||
|         public override object Eval(RuntimeContext context) | |||
|         { | |||
|             var left = ParameterExpression.Access<double>(context, "left"); | |||
|             var right = ParameterExpression.Access<double>(context, "right"); | |||
|             return Math.Max(left, right); | |||
|         } | |||
|     } | |||
| } | |||
| @ -0,0 +1,24 @@ | |||
| using EC.Helper.RabbitFunc.Expressions; | |||
| using EC.Helper.RabbitFunc.Runtime; | |||
| 
 | |||
| namespace EC.Helper.RabbitFunc.Extern; | |||
| 
 | |||
| [ExternLambda] | |||
| internal class MinLambdaExpression : SystemLambdaExpression | |||
| { | |||
|     public MinLambdaExpression() | |||
|         : base(typeof(Math).GetMethod(nameof(Math.Min), new Type[] { typeof(double), typeof(double) }), "min", new BodyExpression(), | |||
|               Expression.Parameter(typeof(double), "left"), | |||
|               Expression.Parameter(typeof(double), "right")) | |||
|     { } | |||
| 
 | |||
|     private class BodyExpression : Expression | |||
|     { | |||
|         public override object Eval(RuntimeContext context) | |||
|         { | |||
|             var left = ParameterExpression.Access<double>(context, "left"); | |||
|             var right = ParameterExpression.Access<double>(context, "right"); | |||
|             return Math.Min(left, right); | |||
|         } | |||
|     } | |||
| } | |||
| @ -0,0 +1,24 @@ | |||
| using EC.Helper.RabbitFunc.Expressions; | |||
| using EC.Helper.RabbitFunc.Runtime; | |||
| 
 | |||
| namespace EC.Helper.RabbitFunc.Extern; | |||
| 
 | |||
| [ExternLambda] | |||
| internal class RoundLambdaExpression : SystemLambdaExpression | |||
| { | |||
|     public RoundLambdaExpression() | |||
|         : base(typeof(Math).GetMethod(nameof(Math.Round), new Type[] { typeof(double), typeof(int) }), "round", new BodyExpression(), | |||
|               Expression.Parameter(typeof(double), "value"), | |||
|               Expression.Parameter(typeof(double), "digits")) | |||
|     { } | |||
| 
 | |||
|     private class BodyExpression : Expression | |||
|     { | |||
|         public override object Eval(RuntimeContext context) | |||
|         { | |||
|             var value = ParameterExpression.Access<double>(context, "value"); | |||
|             var digits = (int)ParameterExpression.Access<double>(context, "digits"); | |||
|             return Math.Round(value, digits); | |||
|         } | |||
|     } | |||
| } | |||
| @ -0,0 +1,21 @@ | |||
| using EC.Helper.RabbitFunc.Expressions; | |||
| using EC.Helper.RabbitFunc.Runtime; | |||
| 
 | |||
| namespace EC.Helper.RabbitFunc.Extern; | |||
| 
 | |||
| [ExternLambda] | |||
| internal class SinLambdaExpression : SystemLambdaExpression | |||
| { | |||
|     public SinLambdaExpression() | |||
|         : base(typeof(Math).GetMethod(nameof(Math.Sin), new Type[] { typeof(double) }), "sin", new BodyExpression(), | |||
|               Expression.Parameter(typeof(double), "x")) | |||
|     { } | |||
| 
 | |||
|     private class BodyExpression : Expression | |||
|     { | |||
|         public override object Eval(RuntimeContext context) | |||
|         { | |||
|             return Math.Sin(ParameterExpression.Access<double>(context, "x")); | |||
|         } | |||
|     } | |||
| } | |||
| @ -0,0 +1,21 @@ | |||
| using EC.Helper.RabbitFunc.Expressions; | |||
| using EC.Helper.RabbitFunc.Runtime; | |||
| 
 | |||
| namespace EC.Helper.RabbitFunc.Extern; | |||
| 
 | |||
| [ExternLambda] | |||
| internal class SinhLambdaExpression : SystemLambdaExpression | |||
| { | |||
|     public SinhLambdaExpression() | |||
|         : base(typeof(Math).GetMethod(nameof(Math.Sinh), new Type[] { typeof(double) }), "sinh", new BodyExpression(), | |||
|               Expression.Parameter(typeof(double), "x")) | |||
|     { } | |||
| 
 | |||
|     private class BodyExpression : Expression | |||
|     { | |||
|         public override object Eval(RuntimeContext context) | |||
|         { | |||
|             return Math.Sinh(ParameterExpression.Access<double>(context, "x")); | |||
|         } | |||
|     } | |||
| } | |||
| @ -0,0 +1,21 @@ | |||
| using EC.Helper.RabbitFunc.Expressions; | |||
| using EC.Helper.RabbitFunc.Runtime; | |||
| 
 | |||
| namespace EC.Helper.RabbitFunc.Extern; | |||
| 
 | |||
| [ExternLambda] | |||
| internal class SqrtLambdaExpression : SystemLambdaExpression | |||
| { | |||
|     public SqrtLambdaExpression() | |||
|         : base(typeof(Math).GetMethod(nameof(Math.Sqrt), new Type[] { typeof(double) }), "sqrt", new BodyExpression(), | |||
|               Expression.Parameter(typeof(double), "x")) | |||
|     { } | |||
| 
 | |||
|     private class BodyExpression : Expression | |||
|     { | |||
|         public override object Eval(RuntimeContext context) | |||
|         { | |||
|             return Math.Sqrt(ParameterExpression.Access<double>(context, "x")); | |||
|         } | |||
|     } | |||
| } | |||
| @ -0,0 +1,21 @@ | |||
| using EC.Helper.RabbitFunc.Expressions; | |||
| using EC.Helper.RabbitFunc.Runtime; | |||
| 
 | |||
| namespace EC.Helper.RabbitFunc.Extern; | |||
| 
 | |||
| [ExternLambda] | |||
| internal class TanLambdaExpression : SystemLambdaExpression | |||
| { | |||
|     public TanLambdaExpression() | |||
|         : base(typeof(Math).GetMethod(nameof(Math.Tan), new Type[] { typeof(double) }), "tan", new BodyExpression(), | |||
|               Expression.Parameter(typeof(double), "x")) | |||
|     { } | |||
| 
 | |||
|     private class BodyExpression : Expression | |||
|     { | |||
|         public override object Eval(RuntimeContext context) | |||
|         { | |||
|             return Math.Tan(ParameterExpression.Access<double>(context, "x")); | |||
|         } | |||
|     } | |||
| } | |||
| @ -0,0 +1,21 @@ | |||
| using EC.Helper.RabbitFunc.Expressions; | |||
| using EC.Helper.RabbitFunc.Runtime; | |||
| 
 | |||
| namespace EC.Helper.RabbitFunc.Extern; | |||
| 
 | |||
| [ExternLambda] | |||
| internal class TanhLambdaExpression : SystemLambdaExpression | |||
| { | |||
|     public TanhLambdaExpression() | |||
|         : base(typeof(Math).GetMethod(nameof(Math.Tanh), new Type[] { typeof(double) }), "tanh", new BodyExpression(), | |||
|               Expression.Parameter(typeof(double), "x")) | |||
|     { } | |||
| 
 | |||
|     private class BodyExpression : Expression | |||
|     { | |||
|         public override object Eval(RuntimeContext context) | |||
|         { | |||
|             return Math.Tanh(ParameterExpression.Access<double>(context, "x")); | |||
|         } | |||
|     } | |||
| } | |||
| @ -0,0 +1,84 @@ | |||
| using EC.Helper.RabbitFunc.Compiler; | |||
| using EC.Helper.RabbitFunc.Expressions; | |||
| using EC.Helper.RabbitFunc.Extern; | |||
| using System.Reflection; | |||
| 
 | |||
| namespace EC.Helper.RabbitFunc; | |||
| 
 | |||
| public class Rabbit | |||
| { | |||
|     private static readonly Dictionary<string, SystemLambdaExpression> systemFunctions = new Dictionary<string, SystemLambdaExpression>(); | |||
| 
 | |||
|     static Rabbit() | |||
|     { | |||
|         var assembly = Assembly.GetExecutingAssembly(); | |||
|         foreach (var type in assembly.GetTypes()) | |||
|         { | |||
|             var attributes = type.GetCustomAttributes(typeof(ExternLambdaAttribute), false); | |||
|             if (attributes.Length > 0) | |||
|             { | |||
|                 var constructor = type.GetConstructor(Type.EmptyTypes); | |||
|                 if (constructor != null) | |||
|                     Register((SystemLambdaExpression)constructor.Invoke(null)); | |||
|             } | |||
|         } | |||
|     } | |||
| 
 | |||
|     internal static void Register(SystemLambdaExpression lambda) | |||
|     { | |||
|         if (lambda == null) | |||
|             throw new ArgumentNullException(nameof(lambda)); | |||
| 
 | |||
|         systemFunctions[lambda.Name] = lambda; | |||
|     } | |||
| 
 | |||
|     internal static bool TryGetSystemLambda(string name, out SystemLambdaExpression? lambda) | |||
|     { | |||
|         return systemFunctions.TryGetValue(name, out lambda); | |||
|     } | |||
| 
 | |||
|     internal static SystemLambdaExpression? GetSystemLambda(string name) | |||
|     { | |||
|         systemFunctions.TryGetValue(name, out SystemLambdaExpression? lambda); | |||
|         return lambda; | |||
|     } | |||
| 
 | |||
|     public static LambdaExpression CompileFromSource(string source) | |||
|     { | |||
|         if (source == null) | |||
|             throw new ArgumentNullException(nameof(source)); | |||
| 
 | |||
|         LambdaExpression? lambda = null; | |||
|         using var reader = new StringReader(source); | |||
|         var tokenizer = new Tokenizer(reader); | |||
|         var parser = new Parser(tokenizer); | |||
|         while (!tokenizer.EndOfStream) | |||
|         { | |||
|             var expression = parser.Parse(); | |||
|             if (expression == null) break; | |||
|             else lambda = expression; | |||
|         } | |||
| 
 | |||
|         return lambda ?? throw new CompilerException(tokenizer.Position, "The formula was not found"); | |||
|     } | |||
| 
 | |||
|     public static LambdaExpression CompileFromFile(string path) | |||
|     { | |||
|         if (path == null) | |||
|             throw new ArgumentNullException(nameof(path)); | |||
| 
 | |||
|         LambdaExpression? lambda = null; | |||
|         using var stream = new FileStream(path, FileMode.Open, FileAccess.Read); | |||
|         using var reader = new StreamReader(stream, System.Text.Encoding.UTF8); | |||
|         var tokenizer = new Tokenizer(reader); | |||
|         var parser = new Parser(tokenizer); | |||
|         while (!tokenizer.EndOfStream) | |||
|         { | |||
|             var expression = parser.Parse(); | |||
|             if (expression == null) break; | |||
|             else lambda = expression; | |||
|         } | |||
| 
 | |||
|         return lambda ?? throw new CompilerException(tokenizer.Position, "The formula was not found"); | |||
|     } | |||
| } | |||
| @ -0,0 +1,83 @@ | |||
| using EC.Helper.RabbitFunc.Compiler; | |||
| using EC.Helper.RabbitFunc.Expressions; | |||
| 
 | |||
| namespace EC.Helper.RabbitFunc; | |||
| 
 | |||
| public class RabbitDomain | |||
| { | |||
|     private readonly Dictionary<string, LambdaExpression> lambdas = new Dictionary<string, LambdaExpression>(); | |||
| 
 | |||
|     public void Register(LambdaExpression lambda) | |||
|     { | |||
|         if (lambda == null) | |||
|             throw new ArgumentNullException(nameof(lambda)); | |||
| 
 | |||
|         lambdas[lambda.Name] = lambda; | |||
|     } | |||
| 
 | |||
|     public bool TryGetLambda(string name, out LambdaExpression? lambda) | |||
|     { | |||
|         if (lambdas.TryGetValue(name, out lambda)) | |||
|         { | |||
|             return true; | |||
|         } | |||
|         if (Rabbit.TryGetSystemLambda(name, out var systemLambda)) | |||
|         { | |||
|             lambda = systemLambda; | |||
|             return true; | |||
|         } | |||
| 
 | |||
|         return false; | |||
|     } | |||
| 
 | |||
|     public LambdaExpression? GetLambda(string name) | |||
|     { | |||
|         TryGetLambda(name, out LambdaExpression? lambda); | |||
|         return lambda; | |||
|     } | |||
| 
 | |||
|     public LambdaExpression CompileFromSource(string source) | |||
|     { | |||
|         if (source == null) | |||
|             throw new ArgumentNullException(nameof(source)); | |||
| 
 | |||
|         LambdaExpression? lambda = null; | |||
|         using var reader = new StringReader(source); | |||
|         var tokenizer = new Tokenizer(reader); | |||
|         var parser = new Parser(tokenizer); | |||
|         while (!tokenizer.EndOfStream) | |||
|         { | |||
|             var expression = parser.Parse(); | |||
|             if (expression == null) break; | |||
| 
 | |||
|             expression.Domain = this; | |||
|             Register(expression); | |||
|             lambda = expression; | |||
|         } | |||
| 
 | |||
|         return lambda ?? throw new CompilerException(tokenizer.Position, "The formula was not found"); | |||
|     } | |||
| 
 | |||
|     public LambdaExpression CompileFromFile(string path) | |||
|     { | |||
|         if (path == null) | |||
|             throw new ArgumentNullException(nameof(path)); | |||
| 
 | |||
|         LambdaExpression? lambda = null; | |||
|         using var stream = new FileStream(path, FileMode.Open, FileAccess.Read); | |||
|         using var reader = new StreamReader(stream, System.Text.Encoding.UTF8); | |||
|         var tokenizer = new Tokenizer(reader); | |||
|         var parser = new Parser(tokenizer); | |||
|         while (!tokenizer.EndOfStream) | |||
|         { | |||
|             var expression = parser.Parse(); | |||
|             if (expression == null) break; | |||
| 
 | |||
|             expression.Domain = this; | |||
|             Register(expression); | |||
|             lambda = expression; | |||
|         } | |||
| 
 | |||
|         return lambda ?? throw new CompilerException(tokenizer.Position, "The formula was not found"); | |||
|     } | |||
| } | |||
| @ -0,0 +1,45 @@ | |||
| using EC.Helper.RabbitFunc.Compiler; | |||
| using EC.Helper.RabbitFunc.Expressions; | |||
| 
 | |||
| namespace EC.Helper.RabbitFunc; | |||
| 
 | |||
| public class RabbitFuncUtil | |||
| { | |||
|     public static LambdaExpression CompileFromSource(string source) | |||
|     { | |||
|         if (source == null) throw new ArgumentNullException(nameof(source)); | |||
| 
 | |||
|         LambdaExpression? lambda = null; | |||
|         using var reader = new StringReader(source); | |||
|         var tokenizer = new Tokenizer(reader); | |||
|         var parser = new Parser(tokenizer); | |||
|         while (!tokenizer.EndOfStream) | |||
|         { | |||
|             var expression = parser.Parse(); | |||
|             if (expression == null) break; | |||
|             lambda = expression; | |||
|         } | |||
| 
 | |||
|         return lambda ?? throw new CompilerException(tokenizer.Position, "The formula was not found"); | |||
|     } | |||
| 
 | |||
|     public static LambdaExpression CompileFromFile(string path) | |||
|     { | |||
|         if (path == null) | |||
|             throw new ArgumentNullException(nameof(path)); | |||
| 
 | |||
|         LambdaExpression? lambda = null; | |||
|         using var stream = new FileStream(path, FileMode.Open, FileAccess.Read); | |||
|         using var reader = new StreamReader(stream, System.Text.Encoding.UTF8); | |||
|         var tokenizer = new Tokenizer(reader); | |||
|         var parser = new Parser(tokenizer); | |||
|         while (!tokenizer.EndOfStream) | |||
|         { | |||
|             var expression = parser.Parse(); | |||
|             if (expression == null) break; | |||
|             lambda = expression; | |||
|         } | |||
| 
 | |||
|         return lambda ?? throw new CompilerException(tokenizer.Position, "The formula was not found"); | |||
|     } | |||
| } | |||
| @ -0,0 +1,23 @@ | |||
| using System.Runtime.Serialization; | |||
| 
 | |||
| namespace EC.Helper.RabbitFunc.Runtime | |||
| { | |||
|     [Serializable] | |||
|     public class MemberAccessException : SystemException | |||
|     { | |||
|         public MemberAccessException() | |||
|         { } | |||
| 
 | |||
|         public MemberAccessException(string message) | |||
|             : base(message) | |||
|         { } | |||
| 
 | |||
|         public MemberAccessException(string message, Exception innerException) | |||
|             : base(message, innerException) | |||
|         { } | |||
| 
 | |||
|         protected MemberAccessException(SerializationInfo info, StreamingContext context) | |||
|             : base(info, context) | |||
|         { } | |||
|     } | |||
| } | |||
| @ -0,0 +1,23 @@ | |||
| using System.Runtime.Serialization; | |||
| 
 | |||
| namespace EC.Helper.RabbitFunc.Runtime | |||
| { | |||
|     [Serializable] | |||
|     public class MissingMemberException : MemberAccessException | |||
|     { | |||
|         public MissingMemberException() | |||
|         { } | |||
| 
 | |||
|         public MissingMemberException(string message) | |||
|             : base(message) | |||
|         { } | |||
| 
 | |||
|         public MissingMemberException(string message, Exception innerException) | |||
|             : base(message, innerException) | |||
|         { } | |||
| 
 | |||
|         protected MissingMemberException(SerializationInfo info, StreamingContext context) | |||
|             : base(info, context) | |||
|         { } | |||
|     } | |||
| } | |||
| @ -0,0 +1,23 @@ | |||
| using System.Runtime.Serialization; | |||
| 
 | |||
| namespace EC.Helper.RabbitFunc.Runtime | |||
| { | |||
|     [Serializable] | |||
|     public class MissingMethodException : MissingMemberException | |||
|     { | |||
|         public MissingMethodException() | |||
|         { } | |||
| 
 | |||
|         public MissingMethodException(string message) | |||
|             : base(message) | |||
|         { } | |||
| 
 | |||
|         public MissingMethodException(string message, Exception innerException) | |||
|             : base(message, innerException) | |||
|         { } | |||
| 
 | |||
|         protected MissingMethodException(SerializationInfo info, StreamingContext context) | |||
|             : base(info, context) | |||
|         { } | |||
|     } | |||
| } | |||
| @ -0,0 +1,91 @@ | |||
| namespace EC.Helper.RabbitFunc.Runtime; | |||
| 
 | |||
| public class RuntimeContext | |||
| { | |||
|     private readonly Dictionary<string, RuntimeVariable> variables = new Dictionary<string, RuntimeVariable>(); | |||
|     private readonly RuntimeContext parent; | |||
| 
 | |||
|     public RuntimeContext() | |||
|     { } | |||
| 
 | |||
|     internal RuntimeContext(RuntimeContext parent) | |||
|     { | |||
|         if (parent == null) | |||
|             throw new ArgumentNullException(nameof(parent)); | |||
| 
 | |||
|         this.parent = parent; | |||
|         this.Domain = parent.Domain; | |||
|     } | |||
| 
 | |||
|     public object this[string name] | |||
|     { | |||
|         set | |||
|         { | |||
|             if (name == null) | |||
|                 throw new ArgumentNullException(nameof(name)); | |||
| 
 | |||
|             variables[name] = new RuntimeVariable(value); | |||
|         } | |||
|     } | |||
| 
 | |||
|     internal RabbitDomain Domain { get; set; } | |||
| 
 | |||
|     private RuntimeContext FindCurrentOrParent(string name) | |||
|     { | |||
|         return variables.ContainsKey(name) ? this : parent?.FindCurrentOrParent(name); | |||
|     } | |||
| 
 | |||
|     public void Variable(string name, object value) | |||
|     { | |||
|         if (name == null) | |||
|             throw new ArgumentNullException(nameof(name)); | |||
| 
 | |||
|         variables[name] = new RuntimeVariable(value); | |||
|     } | |||
| 
 | |||
|     public RuntimeVariable Assign(string name, object value) | |||
|     { | |||
|         if (name == null) | |||
|             throw new ArgumentNullException(nameof(name)); | |||
| 
 | |||
|         var context = FindCurrentOrParent(name); | |||
|         if (context == null) return null; | |||
|         if (!context.variables.TryGetValue(name, out RuntimeVariable rv)) return null; | |||
| 
 | |||
|         rv.SetValue(value); | |||
|         return rv; | |||
|     } | |||
| 
 | |||
|     public RuntimeVariable Access(string name) | |||
|     { | |||
|         if (name == null) | |||
|             throw new ArgumentNullException(nameof(name)); | |||
| 
 | |||
|         var context = FindCurrentOrParent(name); | |||
|         if (context == null) return null; | |||
|         if (!context.variables.TryGetValue(name, out RuntimeVariable rv)) return null; | |||
|         return rv; | |||
|     } | |||
| 
 | |||
|     public class RuntimeVariable | |||
|     { | |||
|         public RuntimeVariable() | |||
|         { } | |||
| 
 | |||
|         public RuntimeVariable(object value) | |||
|         { | |||
|             Value = value; | |||
|             IsAssigned = true; | |||
|         } | |||
| 
 | |||
|         public object Value { get; set; } | |||
| 
 | |||
|         public bool IsAssigned { get; set; } | |||
| 
 | |||
|         public void SetValue(object value) | |||
|         { | |||
|             Value = value; | |||
|             IsAssigned = true; | |||
|         } | |||
|     } | |||
| } | |||
| @ -0,0 +1,23 @@ | |||
| using System.Runtime.Serialization; | |||
| 
 | |||
| namespace EC.Helper.RabbitFunc.Runtime | |||
| { | |||
|     [Serializable] | |||
|     public class RuntimeException : Exception | |||
|     { | |||
|         public RuntimeException() | |||
|         { } | |||
| 
 | |||
|         public RuntimeException(string message) | |||
|             : base(message) | |||
|         { } | |||
| 
 | |||
|         public RuntimeException(string message, Exception innerException) | |||
|             : base(message, innerException) | |||
|         { } | |||
| 
 | |||
|         protected RuntimeException(SerializationInfo info, StreamingContext context) | |||
|             : base(info, context) | |||
|         { } | |||
|     } | |||
| } | |||
| @ -0,0 +1,23 @@ | |||
| using System.Runtime.Serialization; | |||
| 
 | |||
| namespace EC.Helper.RabbitFunc.Runtime | |||
| { | |||
|     [Serializable] | |||
|     public class SystemException : Exception | |||
|     { | |||
|         public SystemException() | |||
|         { } | |||
| 
 | |||
|         public SystemException(string message) | |||
|             : base(message) | |||
|         { } | |||
| 
 | |||
|         public SystemException(string message, Exception innerException) | |||
|             : base(message, innerException) | |||
|         { } | |||
| 
 | |||
|         protected SystemException(SerializationInfo info, StreamingContext context) | |||
|             : base(info, context) | |||
|         { } | |||
|     } | |||
| } | |||
| @ -0,0 +1,14 @@ | |||
| namespace EC.Helper.RabbitFunc.Syntax; | |||
| 
 | |||
| internal class CommentToken : Token | |||
| { | |||
|     private string comment; | |||
| 
 | |||
|     public CommentToken(string comment) | |||
|         : base(TokenKind.Comment) | |||
|     { | |||
|         this.comment = comment; | |||
|     } | |||
| 
 | |||
|     public override string Text => comment; | |||
| } | |||
| @ -0,0 +1,16 @@ | |||
| namespace EC.Helper.RabbitFunc.Syntax; | |||
| 
 | |||
| internal class ConstantToken : Token | |||
| { | |||
|     private double value; | |||
| 
 | |||
|     public ConstantToken(double value) | |||
|         : base(TokenKind.Constant) | |||
|     { | |||
|         this.value = value; | |||
|     } | |||
| 
 | |||
|     public override string Text => value.ToString(); | |||
| 
 | |||
|     public override double Value => value; | |||
| } | |||
| @ -0,0 +1,16 @@ | |||
| namespace EC.Helper.RabbitFunc.Syntax; | |||
| 
 | |||
| internal class ErrorToken : Token | |||
| { | |||
|     private string message; | |||
| 
 | |||
|     public ErrorToken(string message) | |||
|         : base(TokenKind.Error) | |||
|     { | |||
|         this.message = message; | |||
|     } | |||
| 
 | |||
|     public override string Text => message; | |||
| 
 | |||
|     public string Message => message; | |||
| } | |||
| @ -0,0 +1,14 @@ | |||
| namespace EC.Helper.RabbitFunc.Syntax; | |||
| 
 | |||
| internal class IdentifierToken : Token | |||
| { | |||
|     private string name; | |||
| 
 | |||
|     public IdentifierToken(string name) | |||
|         : base(TokenKind.Identifier) | |||
|     { | |||
|         this.name = name; | |||
|     } | |||
| 
 | |||
|     public override string Text => name; | |||
| } | |||
| @ -0,0 +1,18 @@ | |||
| namespace EC.Helper.RabbitFunc.Syntax; | |||
| 
 | |||
| internal class OperatorToken : Token | |||
| { | |||
|     private string @operator; | |||
|     private byte precedence; | |||
| 
 | |||
|     public OperatorToken(TokenKind kind, string @operator, byte precedence) | |||
|         : base(kind) | |||
|     { | |||
|         this.@operator = @operator; | |||
|         this.precedence = precedence; | |||
|     } | |||
| 
 | |||
|     public override string Text => @operator; | |||
| 
 | |||
|     public byte Precedence => precedence; | |||
| } | |||
| @ -0,0 +1,14 @@ | |||
| namespace EC.Helper.RabbitFunc.Syntax; | |||
| 
 | |||
| internal class SymbolToken : Token | |||
| { | |||
|     private string symbol; | |||
| 
 | |||
|     public SymbolToken(TokenKind kind, string symbol) | |||
|         : base(kind) | |||
|     { | |||
|         this.symbol = symbol; | |||
|     } | |||
| 
 | |||
|     public override string Text => symbol; | |||
| } | |||
| @ -0,0 +1,55 @@ | |||
| namespace EC.Helper.RabbitFunc.Syntax; | |||
| 
 | |||
| internal abstract class Token | |||
| { | |||
|     private TokenKind kind; | |||
| 
 | |||
|     protected Token(TokenKind kind) | |||
|     { | |||
|         this.kind = kind; | |||
|     } | |||
| 
 | |||
|     public abstract string Text { get; } | |||
| 
 | |||
|     public TokenKind Kind => kind; | |||
| 
 | |||
|     public virtual double Value | |||
|     { | |||
|         get => throw new System.NotSupportedException(); | |||
|     } | |||
| 
 | |||
|     public static Token Error(string message) | |||
|     { | |||
|         return new ErrorToken(message); | |||
|     } | |||
| 
 | |||
|     public static Token Operator(TokenKind kind, string @operator, byte precedence) | |||
|     { | |||
|         return new OperatorToken(kind, @operator, precedence); | |||
|     } | |||
| 
 | |||
|     public static Token Symbol(TokenKind kind, string symbol) | |||
|     { | |||
|         return new SymbolToken(kind, symbol); | |||
|     } | |||
| 
 | |||
|     public static Token Constant(double value) | |||
|     { | |||
|         return new ConstantToken(value); | |||
|     } | |||
| 
 | |||
|     public static Token Comment(string text) | |||
|     { | |||
|         return new CommentToken(text); | |||
|     } | |||
| 
 | |||
|     public static Token Identifier(string name) | |||
|     { | |||
|         return new IdentifierToken(name); | |||
|     } | |||
| 
 | |||
|     public override string ToString() | |||
|     { | |||
|         return Text.ToString(); | |||
|     } | |||
| } | |||
| @ -0,0 +1,113 @@ | |||
| namespace EC.Helper.RabbitFunc.Syntax; | |||
| 
 | |||
| internal enum TokenKind : byte | |||
| { | |||
|     EndOfFile, | |||
|     NewLine, | |||
|     Error, | |||
|     Identifier, | |||
|     Constant, | |||
|     Comment, | |||
| 
 | |||
|     /// <summary>
 | |||
|     /// +
 | |||
|     /// </summary>
 | |||
|     Add, | |||
| 
 | |||
|     /// <summary>
 | |||
|     /// -
 | |||
|     /// </summary>
 | |||
|     Subtract, | |||
| 
 | |||
|     /// <summary>
 | |||
|     /// *
 | |||
|     /// </summary>
 | |||
|     Multiply, | |||
| 
 | |||
|     /// <summary>
 | |||
|     /// /
 | |||
|     /// </summary>
 | |||
|     Divide, | |||
| 
 | |||
|     /// <summary>
 | |||
|     /// %
 | |||
|     /// </summary>
 | |||
|     Mod, | |||
| 
 | |||
|     /// <summary>
 | |||
|     /// ^
 | |||
|     /// </summary>
 | |||
|     Power, | |||
| 
 | |||
|     /// <summary>
 | |||
|     /// ,
 | |||
|     /// </summary>
 | |||
|     Comma, | |||
| 
 | |||
|     /// <summary>
 | |||
|     /// =
 | |||
|     /// </summary>
 | |||
|     Assign, | |||
| 
 | |||
|     /// <summary>
 | |||
|     /// .
 | |||
|     /// </summary>
 | |||
|     Dot, | |||
| 
 | |||
|     /// <summary>
 | |||
|     /// (
 | |||
|     /// </summary>
 | |||
|     LeftParen, | |||
| 
 | |||
|     /// <summary>
 | |||
|     /// )
 | |||
|     /// </summary>
 | |||
|     RightParen, | |||
| 
 | |||
|     /// <summary>
 | |||
|     /// [
 | |||
|     /// </summary>
 | |||
|     LeftBracket, | |||
| 
 | |||
|     /// <summary>
 | |||
|     /// ]
 | |||
|     /// </summary>
 | |||
|     RightBracket, | |||
| 
 | |||
|     /// <summary>
 | |||
|     /// !
 | |||
|     /// </summary>
 | |||
|     Not, | |||
| 
 | |||
|     /// <summary>
 | |||
|     /// <
 | |||
|     /// </summary>
 | |||
|     LessThan, | |||
| 
 | |||
|     /// <summary>
 | |||
|     /// <=
 | |||
|     /// </summary>
 | |||
|     LessThanOrEqual, | |||
| 
 | |||
|     /// <summary>
 | |||
|     /// ==
 | |||
|     /// </summary>
 | |||
|     Equal, | |||
| 
 | |||
|     /// <summary>
 | |||
|     /// >=
 | |||
|     /// </summary>
 | |||
|     GreaterThanOrEqual, | |||
| 
 | |||
|     /// <summary>
 | |||
|     /// >
 | |||
|     /// </summary>
 | |||
|     GreaterThan, | |||
| 
 | |||
|     /// <summary>
 | |||
|     /// !=
 | |||
|     /// </summary>
 | |||
|     NotEqual, | |||
| 
 | |||
|     IF, | |||
| } | |||
| @ -0,0 +1,32 @@ | |||
| namespace EC.Helper.RabbitFunc.Syntax; | |||
| 
 | |||
| internal static class Tokens | |||
| { | |||
|     public static readonly Token EndOfFileToken = Token.Symbol(TokenKind.EndOfFile, "<eof>"); | |||
|     public static readonly Token NewLineToken = Token.Symbol(TokenKind.NewLine, "<newline>"); | |||
| 
 | |||
|     public static readonly Token AddToken = Token.Operator(TokenKind.Add, "+", 4); | |||
|     public static readonly Token SubtractToken = Token.Operator(TokenKind.Subtract, "-", 4); | |||
|     public static readonly Token MultiplyToken = Token.Operator(TokenKind.Multiply, "*", 5); | |||
|     public static readonly Token DivideToken = Token.Operator(TokenKind.Divide, "/", 5); | |||
|     public static readonly Token ModToken = Token.Operator(TokenKind.Mod, "%", 5); | |||
|     public static readonly Token PowerToken = Token.Operator(TokenKind.Power, "^", 6); | |||
|     public static readonly Token CommaToken = Token.Symbol(TokenKind.Comma, ","); | |||
|     public static readonly Token AssignToken = Token.Symbol(TokenKind.Assign, "="); | |||
|     public static readonly Token DotToken = Token.Symbol(TokenKind.Dot, "."); | |||
| 
 | |||
|     public static readonly Token LeftParenToken = Token.Symbol(TokenKind.LeftParen, "("); | |||
|     public static readonly Token RightParenToken = Token.Symbol(TokenKind.RightParen, ")"); | |||
|     public static readonly Token LeftBracketToken = Token.Symbol(TokenKind.LeftBracket, "["); | |||
|     public static readonly Token RightBracketToken = Token.Symbol(TokenKind.RightBracket, "]"); | |||
| 
 | |||
|     public static readonly Token NotToken = Token.Symbol(TokenKind.Not, "!"); | |||
|     public static readonly Token LessThanToken = Token.Operator(TokenKind.LessThan, "<", 2); | |||
|     public static readonly Token LessThanOrEqualToken = Token.Operator(TokenKind.LessThanOrEqual, "<=", 2); | |||
|     public static readonly Token EqualToken = Token.Operator(TokenKind.Equal, "==", 1); | |||
|     public static readonly Token GreaterThanOrEqualToken = Token.Operator(TokenKind.GreaterThanOrEqual, ">=", 2); | |||
|     public static readonly Token GreaterThanToken = Token.Operator(TokenKind.GreaterThan, ">", 2); | |||
|     public static readonly Token NotEqualToken = Token.Operator(TokenKind.NotEqual, "!=", 1); | |||
| 
 | |||
|     public static readonly Token IFToken = Token.Symbol(TokenKind.IF, "if"); | |||
| } | |||
					Loading…
					
					
				
		Reference in new issue