视频与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。