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