「9+」 Java 编译器插件教程 101

写了上万行 Java 代码,相比你已经对 Java “木纳呆板"的语法恨之入骨了。 那么,有没有一种可能,我是说可能,我们可以给 Java 编译器写插件? 如果你要在面包店里买面包,你最好跟店员说你要买哪块面包。本文所指的 Java 编译器 均为 OpenJDK 自带的那个 javac,而不是其他的前端编译器实现。 前言 自从 Java 8 起,Oracle 就将插件系统引入了 javac 中,因此是可以写 Javac 插件的。 虽然写一个 Javac 插件并且修改代码是可能的,但是这方面的资料很少(国内就更少了),并且大多内容重复(教你写个 HelloWorld 然后就结束),其次 javac 的东西也不是很好摸,毕竟不是公开 API( JDK 9+ 开始已经不暴露在外了)。 恰逢今年还没写技术类的博客,就拿来磨磨刀吧。 先从插件说起 上文刚刚提到了一个词,插件。它其实正是 com.sun.source.util.Plugin。通过这个类,我们可以让 Javac 在编译时加载我们的代码。 加载了代码,那么做什么呢?所以我们立一个目标:给所有 @Jsonized 标注过的类都生成一个可以输出 json 的 toString() 方法。 什么意思呢?就比如说这样一个类: public class MyResponse { private final String message; private final boolean success; public MyResponse(String message, boolean success){ this.message = message; this.success = success; } } 然后你想把它序列化成 Json。 var response = new MyResponse("\"Success!\" Took me 114514 yr 1919810 m",false).toString(); response.equals(""" {"message": "\"Success!\" Took me 114514 yr 1919810 m", "success": false} """); 又快又便捷,还不需要第三方类库,这就是我们的目标。 ...

July 6, 2022 · 7 min · 1436 words

从零开始的编译器生涯

近日一屑高二学生无聊动手写起了编译器….这是他的珍贵作战记录 0x01 理论基础 我摊牌,我没有看任何编译原理相关的书籍,因此这篇文章并不能作为严格的参考资料,甚至很多地方可能是错误的。 编译器,编译器,就是把高级语言的代码编译成另一种形式(class,asm,二进制,IR),而他在编译成另一种形式之前大概需要过这么个流程: Lex -> Parse -> Compile 接下来逐步讲解这个过程。 Lexer 就是分词器,输入用户提供的代码接着把他分成 tokens,也就是 tokenstream。 你肯定看不懂上面那句话的意思,让我们来点实例: a.value 里的那个 ArrayList 就是一个 token stream,str 是被解析的代码。不难发现,语句被 Lexer 按顺序进行了分类以及数据的分割,如 a 被识别为了一个 Identify (标记)。 因此也可以归纳出来 Token 大致的代码长啥样: @AllArgsConstructor @Getter public class Token { private int line; private Type type; private String content; } Parser Lexer 从源码中提取出 token stream 后将会交给 Parser 处理,它负责对 token stream 进行解析,生成一个 AST (Abstract Syntax Tree),也就是 抽象语法树。 这张图直观的描述了这一过程,你可以看到它以树状的形式表现编程语言的语法结构,树上的每个节点都表示源代码中的一种结构。之所以说语法是“抽象”的,是因为这里的语法并不会表示出真实语法中出现的每个细节。 接着,AST 将会丢给代码生成器用于生成代码,但是一般会先对 AST 进行优化,例如 常量折叠 Static Analyzing 但在这之前,我们还有一些问题要解决。 其实这玩意我是和 Parser 写一块的 试想一下,如果有这样一行代码: ...

October 1, 2021 · 7 min · 1396 words