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