笔记 - C#从头开始构建编译器 - 2
视频与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。