視頻與PR:https://github.com/terrajobst/minsk/blob/master/docs/episode-02.md 作者是 Immo Landwerth(https://twitter.com/terrajobst),微軟 .NET 團隊的項目經理。 這一集的主要內容: ...
視頻與PR:https://github.com/terrajobst/minsk/blob/master/docs/episode-02.md
作者是 Immo Landwerth(https://twitter.com/terrajobst),微軟 .NET 團隊的項目經理。
這一集的主要內容:
1.添加 Binder,充當語義分析作用。
Binder 基於 SyntaxTree,大體上 SyntaxKind.XXX_Expression => Bind_XXX_Expression。
在 SyntaxTree 中,運算符只是個枚舉值(即也就只是個符號),而在 Binder 中必須賦予更加具體的語義。
比如:
> SyntaxKind.PlusToken => BoundBinaryOperatorKind.Addition (“+”代表累加)
> SyntaxKind.AmpersandAmpersandToken => BoundBinaryOperatorKind.LogicalAnd(“&&”代表邏輯與)
在 Binder 中,運算符有更加寬泛的含義,如果是二元運算符,必須可以獲取其符號的 SyntaxKind、BoundBinaryOperatorKind、LeftType、RightType、ResultType。計算結果的類型代表了該二元表達式的類型。
以 BoundBinaryOperator 作為具體實現:
using System; using Minsk.CodeAnalysis.Syntax; namespace Minsk.CodeAnalysis.Binding { internal sealed class BoundBinaryOperator { private BoundBinaryOperator(SyntaxKind syntaxKind, BoundBinaryOperatorKind kind, Type type) : this(syntaxKind, kind, type, type, type) { } private BoundBinaryOperator(SyntaxKind syntaxKind, BoundBinaryOperatorKind kind, Type operandType, Type resultType) : this(syntaxKind, kind, operandType, operandType, resultType) { } private BoundBinaryOperator(SyntaxKind syntaxKind, BoundBinaryOperatorKind kind, Type leftType, Type rightType, Type resultType) { SyntaxKind = syntaxKind; Kind = kind; LeftType = leftType; RightType = rightType; Type = resultType; } public SyntaxKind SyntaxKind { get; } public BoundBinaryOperatorKind Kind { get; } public Type LeftType { get; } public Type RightType { get; } public Type Type { get; } private static BoundBinaryOperator[] _operators = { new BoundBinaryOperator(SyntaxKind.PlusToken, BoundBinaryOperatorKind.Addition, typeof(int)), new BoundBinaryOperator(SyntaxKind.MinusToekn, BoundBinaryOperatorKind.Subtraction, typeof(int)), new BoundBinaryOperator(SyntaxKind.StarToken, BoundBinaryOperatorKind.Multiplication, typeof(int)), new BoundBinaryOperator(SyntaxKind.SlashToken, BoundBinaryOperatorKind.Division, typeof(int)), new BoundBinaryOperator(SyntaxKind.EqualsEqualsToken, BoundBinaryOperatorKind.Equals, typeof(int), typeof(bool)), new BoundBinaryOperator(SyntaxKind.BangEqualsToken, BoundBinaryOperatorKind.NotEquals, typeof(int), typeof(bool)), new BoundBinaryOperator(SyntaxKind.AmpersandAmpersandToken, BoundBinaryOperatorKind.LogicalAnd, typeof(bool)), new BoundBinaryOperator(SyntaxKind.PipePipeToken, BoundBinaryOperatorKind.LogicalOr, typeof(bool)), new BoundBinaryOperator(SyntaxKind.EqualsEqualsToken, BoundBinaryOperatorKind.Equals, typeof(bool)), new BoundBinaryOperator(SyntaxKind.BangEqualsToken, BoundBinaryOperatorKind.NotEquals, typeof(bool)), }; public static BoundBinaryOperator Bind(SyntaxKind syntaxKind, Type leftType, Type rightType) { foreach (var op in _operators) { if (op.SyntaxKind == syntaxKind && op.LeftType == leftType && op.RightType == rightType) return op; } return null; } } }
以及 BoundBinaryExpression 的實現:
using System; namespace Minsk.CodeAnalysis.Binding { internal sealed class BoundBinaryExpression : BoundExpression { public BoundBinaryExpression(BoundExpression left, BoundBinaryOperator op, BoundExpression right) { Left = left; Op = op; Right = right; } public override Type Type => Op.Type; public override BoundNodeKind Kind => BoundNodeKind.BinaryExpression; public BoundExpression Left { get; } public BoundBinaryOperator Op { get; } public BoundExpression Right { get; } } }
2.Evaluator 不再基於 SyntaxTree 求值,而是基於 Binder 求值。
3.優先順序更加通用的做法。
namespace Minsk.CodeAnalysis.Syntax { internal static class SyntaxFacts { public static int GetUnaryOperatorPrecedence(this SyntaxKind kind) { switch (kind) { case SyntaxKind.PlusToken: case SyntaxKind.MinusToekn: case SyntaxKind.BangToken: return 6; default: return 0; } } public static int GetBinaryOperatorPrecedence(this SyntaxKind kind) { switch (kind) { case SyntaxKind.StarToken: case SyntaxKind.SlashToken: return 5; case SyntaxKind.PlusToken: case SyntaxKind.MinusToekn: return 4; case SyntaxKind.EqualsEqualsToken: case SyntaxKind.BangEqualsToken: return 3; case SyntaxKind.AmpersandAmpersandToken: return 2; case SyntaxKind.PipePipeToken: return 1; default: return 0; } } internal static SyntaxKind GetKeyWordKind(string text) { switch (text) { case "true": return SyntaxKind.TrueKeyword; case "false": return SyntaxKind.FalseKeyword; default: return SyntaxKind.IdentifierToken; } } } }
結合優先順序可以更加深刻理解遞歸下降分析的思路。
4.實現了 Boolean 類型,以及其他的運算符。
C#語言點:
1.擴展方法。將 this XXX 作為 static 函數的第一個成員,然後該函數成為 XXX 的成員函數。這也是一般意義上實現類成員函數的方法。
2.庫函數
public static class Enumerable { public static IEnumerable<TSource> Concat<TSource>(this IEnumerable<TSource> first, IEnumerable<TSource> second); }
在System.Linq中,庫為 Enumerable 擴展了很多方法,見第一點。
工具:
VS的代碼轉換技巧,比如快速對邏輯表達式取反、快速將 if 轉為 switch。