Browse Source

feat: 添加 RabbitFunc 模块

master
fajiao 2 years ago
parent
commit
6dd68031b5
  1. 9
      EC.Helper/RabbitFunc/Compiler/CompilerException.cs
  2. 57
      EC.Helper/RabbitFunc/Compiler/IndexSpan.cs
  3. 258
      EC.Helper/RabbitFunc/Compiler/Parser.cs
  4. 97
      EC.Helper/RabbitFunc/Compiler/SourceLocation.cs
  5. 20
      EC.Helper/RabbitFunc/Compiler/TokenWithSpan.cs
  6. 403
      EC.Helper/RabbitFunc/Compiler/Tokenizer.cs
  7. 119
      EC.Helper/RabbitFunc/Expressions/BinaryExpression.cs
  8. 32
      EC.Helper/RabbitFunc/Expressions/ConditionalExpression.cs
  9. 29
      EC.Helper/RabbitFunc/Expressions/ConstantExpression.cs
  10. 238
      EC.Helper/RabbitFunc/Expressions/Expression.cs
  11. 146
      EC.Helper/RabbitFunc/Expressions/ExpressionCompiler.cs
  12. 65
      EC.Helper/RabbitFunc/Expressions/ExpressionType.cs
  13. 9
      EC.Helper/RabbitFunc/Expressions/LambdaCompilerException.cs
  14. 75
      EC.Helper/RabbitFunc/Expressions/LambdaExpression.cs
  15. 30
      EC.Helper/RabbitFunc/Expressions/MemberExpression.cs
  16. 88
      EC.Helper/RabbitFunc/Expressions/MethodCallExpression.cs
  17. 45
      EC.Helper/RabbitFunc/Expressions/ParameterExpression.cs
  18. 19
      EC.Helper/RabbitFunc/Expressions/SystemLambdaExpression.cs
  19. 48
      EC.Helper/RabbitFunc/Expressions/UnaryExpression.cs
  20. 21
      EC.Helper/RabbitFunc/Extern/AbsLambdaExpression.cs
  21. 21
      EC.Helper/RabbitFunc/Extern/AcosLambdaExpression.cs
  22. 21
      EC.Helper/RabbitFunc/Extern/AsinLambdaExpression.cs
  23. 21
      EC.Helper/RabbitFunc/Extern/AtanLambdaExpression.cs
  24. 21
      EC.Helper/RabbitFunc/Extern/CeilingLambdaExpression.cs
  25. 21
      EC.Helper/RabbitFunc/Extern/CosLambdaExpression.cs
  26. 21
      EC.Helper/RabbitFunc/Extern/CoshLambdaExpression.cs
  27. 21
      EC.Helper/RabbitFunc/Extern/ExpLambdaExpression.cs
  28. 5
      EC.Helper/RabbitFunc/Extern/ExternLambdaAttribute.cs
  29. 21
      EC.Helper/RabbitFunc/Extern/FloorLambdaExpression.cs
  30. 24
      EC.Helper/RabbitFunc/Extern/LogLambdaExpression.cs
  31. 24
      EC.Helper/RabbitFunc/Extern/MaxLambdaExpression.cs
  32. 24
      EC.Helper/RabbitFunc/Extern/MinLambdaExpression.cs
  33. 24
      EC.Helper/RabbitFunc/Extern/RoundLambdaExpression.cs
  34. 21
      EC.Helper/RabbitFunc/Extern/SinLambdaExpression.cs
  35. 21
      EC.Helper/RabbitFunc/Extern/SinhLambdaExpression.cs
  36. 21
      EC.Helper/RabbitFunc/Extern/SqrtLambdaExpression.cs
  37. 21
      EC.Helper/RabbitFunc/Extern/TanLambdaExpression.cs
  38. 21
      EC.Helper/RabbitFunc/Extern/TanhLambdaExpression.cs
  39. 84
      EC.Helper/RabbitFunc/Rabbit.cs
  40. 83
      EC.Helper/RabbitFunc/RabbitDomain.cs
  41. 45
      EC.Helper/RabbitFunc/RabbitFuncUtil.cs
  42. 23
      EC.Helper/RabbitFunc/Runtime/MemberAccessException.cs
  43. 23
      EC.Helper/RabbitFunc/Runtime/MissingMemberException.cs
  44. 23
      EC.Helper/RabbitFunc/Runtime/MissingMethodException.cs
  45. 91
      EC.Helper/RabbitFunc/Runtime/RuntimeContext.cs
  46. 23
      EC.Helper/RabbitFunc/Runtime/RuntimeException.cs
  47. 23
      EC.Helper/RabbitFunc/Runtime/SystemException.cs
  48. 14
      EC.Helper/RabbitFunc/Syntax/CommentToken.cs
  49. 16
      EC.Helper/RabbitFunc/Syntax/ConstantToken.cs
  50. 16
      EC.Helper/RabbitFunc/Syntax/ErrorToken.cs
  51. 14
      EC.Helper/RabbitFunc/Syntax/IdentifierToken.cs
  52. 18
      EC.Helper/RabbitFunc/Syntax/OperatorToken.cs
  53. 14
      EC.Helper/RabbitFunc/Syntax/SymbolToken.cs
  54. 55
      EC.Helper/RabbitFunc/Syntax/Token.cs
  55. 113
      EC.Helper/RabbitFunc/Syntax/TokenKind.cs
  56. 32
      EC.Helper/RabbitFunc/Syntax/Tokens.cs

9
EC.Helper/RabbitFunc/Compiler/CompilerException.cs

@ -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))
{ }
}

57
EC.Helper/RabbitFunc/Compiler/IndexSpan.cs

@ -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);
}
}

258
EC.Helper/RabbitFunc/Compiler/Parser.cs

@ -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();
}
}

97
EC.Helper/RabbitFunc/Compiler/SourceLocation.cs

@ -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);
}
}

20
EC.Helper/RabbitFunc/Compiler/TokenWithSpan.cs

@ -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;
}

403
EC.Helper/RabbitFunc/Compiler/Tokenizer.cs

@ -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();
}
}
}
}

119
EC.Helper/RabbitFunc/Expressions/BinaryExpression.cs

@ -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();
}
}

32
EC.Helper/RabbitFunc/Expressions/ConditionalExpression.cs

@ -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);
}
}

29
EC.Helper/RabbitFunc/Expressions/ConstantExpression.cs

@ -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();
}
}

238
EC.Helper/RabbitFunc/Expressions/Expression.cs

@ -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();
}
}

146
EC.Helper/RabbitFunc/Expressions/ExpressionCompiler.cs

@ -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();
}
}
}

65
EC.Helper/RabbitFunc/Expressions/ExpressionType.cs

@ -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>
/// &lt;
/// </summary>
LessThan,
/// <summary>
/// &lt;=
/// </summary>
LessThanOrEqual,
/// <summary>
/// ==
/// </summary>
Equal,
/// <summary>
/// &gt;=
/// </summary>
GreaterThanOrEqual,
/// <summary>
/// &gt;
/// </summary>
GreaterThan,
/// <summary>
/// !=
/// </summary>
NotEqual,
/// <summary>
/// !
/// </summary>
Not,
/// <summary>
/// IF
/// </summary>
Conditional,
}

9
EC.Helper/RabbitFunc/Expressions/LambdaCompilerException.cs

@ -0,0 +1,9 @@
namespace EC.Helper.RabbitFunc.Expressions;
[Serializable]
public class LambdaCompilerException : Exception
{
public LambdaCompilerException(string message)
: base(message)
{ }
}

75
EC.Helper/RabbitFunc/Expressions/LambdaExpression.cs

@ -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();
}
}

30
EC.Helper/RabbitFunc/Expressions/MemberExpression.cs

@ -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;
}
}

88
EC.Helper/RabbitFunc/Expressions/MethodCallExpression.cs

@ -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();
}
}

45
EC.Helper/RabbitFunc/Expressions/ParameterExpression.cs

@ -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;
}
}

19
EC.Helper/RabbitFunc/Expressions/SystemLambdaExpression.cs

@ -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;
}

48
EC.Helper/RabbitFunc/Expressions/UnaryExpression.cs

@ -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());
}
}
}

21
EC.Helper/RabbitFunc/Extern/AbsLambdaExpression.cs

@ -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"));
}
}
}

21
EC.Helper/RabbitFunc/Extern/AcosLambdaExpression.cs

@ -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"));
}
}
}

21
EC.Helper/RabbitFunc/Extern/AsinLambdaExpression.cs

@ -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"));
}
}
}

21
EC.Helper/RabbitFunc/Extern/AtanLambdaExpression.cs

@ -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"));
}
}
}

21
EC.Helper/RabbitFunc/Extern/CeilingLambdaExpression.cs

@ -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"));
}
}
}

21
EC.Helper/RabbitFunc/Extern/CosLambdaExpression.cs

@ -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"));
}
}
}

21
EC.Helper/RabbitFunc/Extern/CoshLambdaExpression.cs

@ -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"));
}
}
}

21
EC.Helper/RabbitFunc/Extern/ExpLambdaExpression.cs

@ -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"));
}
}
}

5
EC.Helper/RabbitFunc/Extern/ExternLambdaAttribute.cs

@ -0,0 +1,5 @@
namespace EC.Helper.RabbitFunc.Extern;
[AttributeUsage(AttributeTargets.Class, AllowMultiple = false)]
internal class ExternLambdaAttribute : Attribute
{ }

21
EC.Helper/RabbitFunc/Extern/FloorLambdaExpression.cs

@ -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"));
}
}
}

24
EC.Helper/RabbitFunc/Extern/LogLambdaExpression.cs

@ -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);
}
}
}

24
EC.Helper/RabbitFunc/Extern/MaxLambdaExpression.cs

@ -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);
}
}
}

24
EC.Helper/RabbitFunc/Extern/MinLambdaExpression.cs

@ -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);
}
}
}

24
EC.Helper/RabbitFunc/Extern/RoundLambdaExpression.cs

@ -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);
}
}
}

21
EC.Helper/RabbitFunc/Extern/SinLambdaExpression.cs

@ -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"));
}
}
}

21
EC.Helper/RabbitFunc/Extern/SinhLambdaExpression.cs

@ -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"));
}
}
}

21
EC.Helper/RabbitFunc/Extern/SqrtLambdaExpression.cs

@ -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"));
}
}
}

21
EC.Helper/RabbitFunc/Extern/TanLambdaExpression.cs

@ -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"));
}
}
}

21
EC.Helper/RabbitFunc/Extern/TanhLambdaExpression.cs

@ -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"));
}
}
}

84
EC.Helper/RabbitFunc/Rabbit.cs

@ -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");
}
}

83
EC.Helper/RabbitFunc/RabbitDomain.cs

@ -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");
}
}

45
EC.Helper/RabbitFunc/RabbitFuncUtil.cs

@ -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");
}
}

23
EC.Helper/RabbitFunc/Runtime/MemberAccessException.cs

@ -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)
{ }
}
}

23
EC.Helper/RabbitFunc/Runtime/MissingMemberException.cs

@ -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)
{ }
}
}

23
EC.Helper/RabbitFunc/Runtime/MissingMethodException.cs

@ -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)
{ }
}
}

91
EC.Helper/RabbitFunc/Runtime/RuntimeContext.cs

@ -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;
}
}
}

23
EC.Helper/RabbitFunc/Runtime/RuntimeException.cs

@ -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)
{ }
}
}

23
EC.Helper/RabbitFunc/Runtime/SystemException.cs

@ -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)
{ }
}
}

14
EC.Helper/RabbitFunc/Syntax/CommentToken.cs

@ -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;
}

16
EC.Helper/RabbitFunc/Syntax/ConstantToken.cs

@ -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;
}

16
EC.Helper/RabbitFunc/Syntax/ErrorToken.cs

@ -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;
}

14
EC.Helper/RabbitFunc/Syntax/IdentifierToken.cs

@ -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;
}

18
EC.Helper/RabbitFunc/Syntax/OperatorToken.cs

@ -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;
}

14
EC.Helper/RabbitFunc/Syntax/SymbolToken.cs

@ -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;
}

55
EC.Helper/RabbitFunc/Syntax/Token.cs

@ -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();
}
}

113
EC.Helper/RabbitFunc/Syntax/TokenKind.cs

@ -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>
/// &lt;
/// </summary>
LessThan,
/// <summary>
/// &lt;=
/// </summary>
LessThanOrEqual,
/// <summary>
/// ==
/// </summary>
Equal,
/// <summary>
/// &gt;=
/// </summary>
GreaterThanOrEqual,
/// <summary>
/// &gt;
/// </summary>
GreaterThan,
/// <summary>
/// !=
/// </summary>
NotEqual,
IF,
}

32
EC.Helper/RabbitFunc/Syntax/Tokens.cs

@ -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…
Cancel
Save