编辑Java编译器
#编程 #java #systems #compiler

编译器在整个职业生涯中总是让我着迷。我想这是编译器采用高级代码并将其迅速转化为执行的内容的能力。我尝试过一次(对于我自己的编程语言)从头开始编写编译器,但它进展不顺利,但我能够从中学到很多东西。快进到今天,我终于编辑了“ OpenJDK17” /“ Corretto-17” Java编译器以添加自己的语法和功能。< / p>

通常用来将高级Java代码编译到.class文件或字节码的“ Javac”命令实际上是用Java编写的。这被称为Bootstrap编译器或“自我编译编译器”。这是用该语言编写的编译器。

Javac编译器在详细的层面上具有多个阶段,但是编译的主要步骤仍然遵守编译器理论 /编译器设计概念。他们是:

  1. 词汇分析(标记)
  2. 语法分析(解析)(生成一个解析树)
  3. 语义分析(类型检查,死亡代码分析等)
  4. 中级代码生成(可选)
  5. 目标代码生成(Bytecode)
  6. 代码优化

生成解析树后,每个其他阶段都是访客。它遵循访问者的设计模式。有一个流访问者(检查死亡代码),有一个Enter访问者(收集方法,类等符号)。有一个attr访问者可以键入检查。等。

要开始编辑Javac编译器,您需要首先在本地设置OpenJDK代码库。不幸的是,我找不到一组用于编译器开发的工具,但在这种情况下,我发现VSCODE是最简单的。 Intellij也有效,但我更喜欢VSCODE。 This page describes how to setup the codebase locally.

我要在此处添加的功能是“ JavaScript的选项链”操作员或“?”。操作员。在浏览了代码后,看来语法必须与三元操作员的语法代码一起编写。该代码在JavacParser.java文件中存在。这是所有解析JCTree发生的地方。

 /** Expression1Rest = ["?" Expression ":" Expression1]
     */
    JCExpression term1Rest(JCExpression t) {
        if (token.kind == QUES) {
            int pos = token.pos;
            nextToken();
            // option chaining
            if (token.kind == DOT) {
                accept(DOT);
                var ident = ident();
                JCExpression returnable = F.at(pos).Conditional(
                        F.at(pos).Parens(F.at(pos).Binary(optag(TokenKind.EQEQ), F.at(pos).Literal(TypeTag.BOT, null), t)),
                        F.at(pos).Literal(TypeTag.BOT, null), F.at(pos).Select(t, ident));
                return term1Rest(returnable);
            }
            // ternary
            JCExpression t1 = term();
            accept(COLON);
            JCExpression t2 = term1();
            return F.at(pos).Conditional(t, t1, t2);
        } else {
            return t;
        }
    }

在这里,语法被称为accept(DOT)。点是Tokens.java

中定义的令牌

此功能的工作方式是,用类似于此代码的自定义树代替JCFieldAccess Select(...)子树:

(t == null) ? null : t.ident;

这是选项链接的工作方式。

此功能如何工作的一个示例就是这样:

代码片段:

public class Test {
        public static void main(String[] args) {
                Car car = new Car();
                Integer wheel1 = car?.w1?.x;
                Integer wheel2 = car?.w2?.x;
                System.out.println("value of wheel1 is: " + wheel1);
                System.out.println("value of wheel2 is: " + wheel2);
        }

        public static class Car {
                Wheel w1 = null;
                Wheel w2 = new Wheel();
                public Car() {
                }
        }

        public static class Wheel {
                int x = 2;
                public Wheel() {
                }
        }
}

Image description

这仍然不是完美的,并且有自己的问题,但是无论它是一个非常有趣的旅程,可以理解编译器的编写方式。我能够从中学到很多东西。

参考:

https://openjdk.org/groups/compiler/doc/hhgtjavac/index.html

我的承诺:

https://github.com/corretto/corretto-17/commit/37b125992c7d2b6fb55216e553168bf7b8a3a4b4