欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

笔记 - C#从头开始构建编译器 - 2

程序员文章站 2022-06-28 20:10:57
视频与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。