Camera Information System
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

258 lines
8.3 KiB

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