fajiao
2 years ago
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