笔记 - C#从头开始构建编译器 - 2
视频与PR:https://github.com/terrajobst/minsk/blob/master/docs/episode-02.md
作者是 Immo Landwerth(https://twitter.com/terrajobst),微软 .NET 团队的项目经理。
SRE实战 互联网时代守护先锋,助力企业售后服务体系运筹帷幄!一键直达领取阿里云限量特价优惠。
这一集的主要内容:
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。

