視頻與PR:https://github.com/terrajobst/minsk/blob/master/docs/episode-03.md 作者是 Immo Landwerth(https://twitter.com/terrajobst),微軟 .NET 團隊的項目經理。 這一集前半段主要是 ...
視頻與PR:https://github.com/terrajobst/minsk/blob/master/docs/episode-03.md
作者是 Immo Landwerth(https://twitter.com/terrajobst),微軟 .NET 團隊的項目經理。
這一集前半段主要是重構代碼,後半段的主要內容:
1. 變數與賦值表達式
2. 加強診斷信息
Parser 非常清晰
using System.Collections.Generic; namespace Minsk.CodeAnalysis.Syntax { internal sealed class Parser { private readonly SyntaxToken[] _tokens; private int _position; private DiagnosticBag _diagnostics = new DiagnosticBag(); public Parser(string text) { var tokens = new List<SyntaxToken>(); var lexer = new Lexer(text); SyntaxToken token; do { token = lexer.Lex(); if (token.Kind != SyntaxKind.WhiteSpaceToken && token.Kind != SyntaxKind.BadToken) tokens.Add(token); } while (token.Kind != SyntaxKind.EndOfFileToken); _tokens = tokens.ToArray(); _diagnostics.AddRange(lexer.Diagnostics); } public DiagnosticBag Diagnostics => _diagnostics; private SyntaxToken Peek(int offset) { var index = _position + offset; if (index >= _tokens.Length) return _tokens[_tokens.Length - 1]; return _tokens[index]; } private SyntaxToken Current => Peek(0); private SyntaxToken NextToken() { var token = Current; _position++; return token; } private SyntaxToken MatchToken(SyntaxKind kind) { if (Current.Kind == kind) return NextToken(); _diagnostics.ReportUnexpectedToken(Current.Span, Current.Kind, kind); return new SyntaxToken(kind, Current.Position, null, null); } public SyntaxTree Parse() { var expression = ParseExpression(); var endOfFileToken = MatchToken(SyntaxKind.EndOfFileToken); return new SyntaxTree(_diagnostics, expression, endOfFileToken); } private ExpressionSyntax ParseExpression() { return ParseAssignmentExpression(); } private ExpressionSyntax ParseAssignmentExpression() { if (Peek(0).Kind == SyntaxKind.IdentifierToken && Peek(1).Kind == SyntaxKind.EqualsToken) { var identifierToken = NextToken(); var equalsToken = NextToken(); var right = ParseAssignmentExpression(); return new AssignmentExpressionSyntax(identifierToken, equalsToken, right); } return ParseBinaryExpression(); } private ExpressionSyntax ParseBinaryExpression(int parentPrecedence = 0) { ExpressionSyntax left; var unaryOperatorPrecedence = Current.Kind.GetUnaryOperatorPrecedence(); if (unaryOperatorPrecedence != 0 && unaryOperatorPrecedence >= parentPrecedence) { var operatorToken = NextToken(); var operand = ParseBinaryExpression(unaryOperatorPrecedence); left = new UnaryExpressionSyntax(operatorToken, operand); } else left = ParsePrimaryExpression(); while (true) { var precedence = Current.Kind.GetBinaryOperatorPrecedence(); if (precedence == 0 || precedence <= parentPrecedence) break; var operatorToken = NextToken(); var right = ParseBinaryExpression(precedence); left = new BinaryExpressionSyntax(left, operatorToken, right); } return left; } private ExpressionSyntax ParsePrimaryExpression() { switch (Current.Kind) { case SyntaxKind.OpenParenthesisToken: { var left = NextToken(); var expression = ParseExpression(); var right = MatchToken(SyntaxKind.CloseParenthesisToken); return new ParenthesizedExpressionSyntax(left, expression, right); } case SyntaxKind.TrueKeyword: case SyntaxKind.FalseKeyword: { var keywordToken = NextToken(); var value = keywordToken.Kind == SyntaxKind.TrueKeyword; return new LiteralExpressionSyntax(keywordToken, value); } case SyntaxKind.IdentifierToken: { var identifierToken = NextToken(); return new NameExpressionSyntax(identifierToken); } default: { var numberToken = MatchToken(SyntaxKind.NumberToken); return new LiteralExpressionSyntax(numberToken); } } } } }
作為語義分析的 Binder 也非常清晰
using System; using System.Collections.Generic; using System.Linq; using Minsk.CodeAnalysis.Syntax; namespace Minsk.CodeAnalysis.Binding { internal sealed class Binder { private readonly DiagnosticBag _diagnostics = new DiagnosticBag (); private readonly Dictionary<VariableSymbol, object> _variables; public Binder(Dictionary<VariableSymbol, object> variables) { _variables = variables; } public DiagnosticBag Diagnostics => _diagnostics; public BoundExpression BindExpression(ExpressionSyntax syntax) { switch (syntax.Kind) { case SyntaxKind.ParenthesizedExpression: return BindParenthesizedExpression((ParenthesizedExpressionSyntax)syntax); case SyntaxKind.LiteralExpression: return BindLiteralExpression((LiteralExpressionSyntax)syntax); case SyntaxKind.NameExpression: return BindNameExpression((NameExpressionSyntax)syntax); case SyntaxKind.AssignmentExpression: return BindAssignmentExpression((AssignmentExpressionSyntax)syntax); case SyntaxKind.UnaryExpression: return BindUnaryExpression((UnaryExpressionSyntax)syntax); case SyntaxKind.BinaryExpression: return BindBinaryExpression((BinaryExpressionSyntax)syntax); default: throw new Exception($"Unexpected syntax {syntax.Kind}"); } } private BoundExpression BindParenthesizedExpression(ParenthesizedExpressionSyntax syntax) { return BindExpression(syntax.Expression); } private BoundExpression BindLiteralExpression(LiteralExpressionSyntax syntax) { var value = syntax.Value ?? 0; return new BoundLiteralExpression(value); } private BoundExpression BindNameExpression(NameExpressionSyntax syntax) { var name = syntax.IdentifierToken.Text; var variable = _variables.Keys.FirstOrDefault(v => v.Name == name); if (variable == null) { _diagnostics.ReportUndefinedName(syntax.IdentifierToken.Span, name); return new BoundLiteralExpression(0); } return new BoundVariableExpression(variable); } private BoundExpression BindAssignmentExpression(AssignmentExpressionSyntax syntax) { var name = syntax.IdentifierToken.Text; var boundExpression = BindExpression(syntax.Expression); var existingVariable = _variables.Keys.FirstOrDefault(v => v.Name == name); if (existingVariable != null) _variables.Remove(existingVariable); var variable = new VariableSymbol(name, boundExpression.Type); _variables[variable] = null; return new BoundAssignmentExpression(variable, boundExpression); } private BoundExpression BindUnaryExpression(UnaryExpressionSyntax syntax) { var boundOperand = BindExpression(syntax.Operand); var boundOperator = BoundUnaryOperator.Bind(syntax.OperatorToken.Kind, boundOperand.Type); if (boundOperator == null) { _diagnostics.ReportUndefinedUnaryOperator(syntax.OperatorToken.Span, syntax.OperatorToken.Text, boundOperand.Type); return boundOperand; } return new BoundUnaryExpression(boundOperator, boundOperand); } private BoundExpression BindBinaryExpression(BinaryExpressionSyntax syntax) { var boundLeft = BindExpression(syntax.Left); var boundRight = BindExpression(syntax.Right); var boundOperator = BoundBinaryOperator.Bind(syntax.OperatorToken.Kind, boundLeft.Type, boundRight.Type); if (boundOperator == null) { _diagnostics.ReportUndefinedBinaryOperator(syntax.OperatorToken.Span, syntax.OperatorToken.Text, boundLeft.Type, boundRight.Type); return boundLeft; } return new BoundBinaryExpression(boundLeft, boundOperator, boundRight); } } }
C#語言點:
public static class Enumerable { public static TSource FirstOrDefault<TSource>(this IEnumerable<TSource> source, Func<TSource, bool> predicate); }
FirstOrDefault 可以使用謂詞作為判斷條件,Binder 的 55 行使用了 Lambda 表達式。