编译原理实战课
宫文学
北京原点代码 CEO
26065 人已学习
新⼈⾸单¥59
登录后,你可以任选4讲全文学习
课程目录
已完结/共 55 讲
真实编译器解析篇 (19讲)
编译原理实战课
15
15
1.0x
00:00/00:00
登录|注册

09 | Java编译器(一):手写的编译器有什么优势?

推导解析过程
Java的词法分析和语法分析
熟悉与编译有关的模块、包和类
一课一思
重点
运算符优先级解析器
消除左递归的算法
自顶向下的递归下降算法
标识符和关键字的词法规则重叠问题
readToken()方法
有限自动机
调用Java编译器的示例代码
Java编译器本身是用Java写的
javac命令
可调用编译器功能
自举
课程小结
语法分析器
词法分析器
初步了解Java的编译器
优势
Java编译器

该思维导图由 AI 生成,仅供参考

你好,我是宫文学。
从今天开始呢,我会带着你去考察实际编译器的具体实现机制,你可以从中学习和印证编译原理的基础知识,进而加深你对编译原理的理解。
我们探险的第一站,是很多同学都很熟悉的 Java 语言,我们一起来看看它的编译器里都有什么奥秘。我从 97 年就开始用它,算是比较早了。当时,我就对它的“一次编译,到处运行”留下了很深的印象,我在 Windows 下写的程序,编译完毕以后放到 Solaris 上就能跑。现在看起来这可能不算什么,但在当年,我在 Windows 和 Unix 下写程序用的工具可是完全不同的。
到现在,Java 已经是一门非常成熟的语言了,而且它也在不断进化,与时俱进,泛型、函数式编程、模块化等特性陆续都增加了进来。在服务端编程领域,它也变得非常普及。
与此同时,Java 的编译器和虚拟机中所采用的技术,也比 20 年前发生了天翻地覆的变化。对于这么一门成熟的、广泛普及的、又不断焕发新生机的语言来说,研究它的编译技术会带来两个好处:一方面,Java 编译器所采用的技术肯定是比较成熟的、靠谱的,你在实现自己的编译功能时,完全可以去参考和借鉴;另一方面,你可以借此深入了解 Java 的编译过程,借此去实现一些高级的功能,比方说,按需生成字节码,就像 Spring 这类工具一样。
确认放弃笔记?
放弃后所记笔记将不保留。
新功能上线,你的历史笔记已初始化为私密笔记,是否一键批量公开?
批量公开的笔记不会为你同步至部落
公开
同步至部落
取消
完成
0/2000
荧光笔
直线
曲线
笔记
复制
AI
  • 深入了解
  • 翻译
    • 英语
    • 中文简体
    • 中文繁体
    • 法语
    • 德语
    • 日语
    • 韩语
    • 俄语
    • 西班牙语
    • 阿拉伯语
  • 解释
  • 总结

本文深入探讨了Java编译器的实际实现机制,旨在帮助读者学习和印证编译原理的基础知识,加深对编译原理的理解。作者首先介绍了Java语言的发展和普及,以及编译器和虚拟机技术的变化。接着,文章初步介绍了Java的编译器,解释了javac命令的使用和Java编译器的自举现象。文章还提到了JavaCompiler的实现和源代码结构,建议读者关注com.sun.source.tree包和com.sun.tools.javac.parser包的源代码,以便更好地理解Java编译器的实现原理。通过对词法分析器和语法分析器的探索,读者可以更好地理解Java编译器的工作原理和实现细节。文章还介绍了Java词法分析器的有限自动机和处理关键字与标识符冲突的技术点。建议读者在IDE中采用调试模式跟踪执行,以更直观地理解Java词法分析的过程和结果。整体而言,本文为读者提供了深入了解Java编译器的概要信息,并指引读者如何深入探索Java编译器的源代码结构和实现原理。 文章还介绍了Java编译器采用的是递归下降算法,通过对解析表达式的调用层次和具体解析过程的分析,展示了Java编译器对优先级和结合性的处理。文章指出Java编译器采用了典型的消除左递归的算法,并通过一个循环处理多种不同优先级的操作符,同时保证了优先级的正确性。此外,文章还详细解释了Java编译器的算法是如何借助一个工作区,自底向上地组装AST,并介绍了这种处理表达式优先级的解析方法,即“运算符优先级解析器(Operator-Precedence Parser)”。这些内容展示了Java编译器在处理复杂表达式时的高效性和灵活性,为读者提供了深入了解Java编译器实现原理的技术细节。

仅可试看部分内容,如需阅读全部内容,请付费购买文章所属专栏
《编译原理实战课》
新⼈⾸单¥59
立即购买
登录 后留言

全部留言(10)

  • 最新
  • 精选
  • ヾ(◍°∇°◍)ノ゙
    jabel这种让jdk8支持高版本java语法的项目也是用到了编译技术了吧

    作者回复: 没错。 这是作者吃透了Java编译器基础上做的很有想象力的一个工作。非常不错。值得玩一下,借此看别人是如何Hack Java的编译器的。

    2020-06-24
    5
  • minghu6
    说编译器成熟后就要自举,这绝对是一个槽点太多以至于无处下嘴的迷思,任何脚踏实地的人不应该迷信这种东西. 首先,不管你如何自举,不可能根本摆脱依赖某些原生操作平台上的工具链的支持,当然也不排除有些人通过重新造轮子的方法把这些工具链全部重新实现一遍,但那只是意味着要额外安装一份儿二进制文件. 对于开发团队来讲,他需要额外管理一个boot build的版本机制,对于用户来讲可怕的来了,他需要先安装一份儿预先编译的版本的然后再把它升级,如果不巧没有合适的直接可以用于升级的预编译的版本,那他将不得不做一个人肉递归,从更早的版本甚至自举前的版本开始逐步升级.由于自举造成的麻烦这不知道浪费了多少人的生命在额外的research&&download, 实际上几个激进自举的语言实现比如Go,比如Clozure CL用起来都有些反直觉,让人花费高时间成本,从一个很不舒服的地方开始. 那么好了,自举有实际的好处吗? 并没有,因为对于一个语言关键得是解决某一类领域的问题具有优势, 不管这个优势是开发效率高\性能表现好\还是上手简单招工容易等等. 人们关注得是否有杀手级应用和大公司的背书,而不是它是否完成了自举. 最后, 个人认为自举在国外流行主要是来源于早期部分黑客"非我不用"的这种有宗教狂热色彩的值得商榷的风格的演化,如果一门语言本身就是用系统原生语言比如C或者其他系统级编程语言比如C++/Rust写的,那我不认为有自举的必要.

    作者回复: 洋洋洒洒一大篇,但读起来并不拗口。个人感觉你表达能力超强。 是的,大部分情况下没有必要重新造轮子,但少量场景下,还就是非造不可。 像浏览器里的JS引擎,大家一开始为了实现起来快,就拿llvm怼在后端。但腾出手来,还是要换掉,因为JS引擎对编译速度等的要求实在llvm不满足。 前端工具也是如此,基本的需求都行。可有时你就是想字斟句酌地扣性能,比如SQL过滤器,可能你就要手写。 工具链也是。我最近在设计一些几乎在裸设备上运行的场景,工具链不说重写吧,也要做相当的改造。 当然,对这些少量场景,肯定是有资金支持的,也意味着它有特殊的价值。否则就没必要做这个事情了。

    2021-06-15
    2
  • 易昊
    老师请教一个问题,最近在看Javac的源码中的词法分析部分,其中Tokens.java中,enum Tag定义有一个是NAMED,我不理解这个NAMED Tag是做什么用的,并且看enum TokenKind的定义,似乎仅有assert, boolean, byte, char, double, enum, float, int, long, short, super, this, void, true, false, null, _ 是对应的NAMED tag,想弄明白为什么会这样设计。

    作者回复: 非常高兴你能真正潜入到代码中去探索。 其实你顺着代码多看一下,在代码里搜索一下,就能明白它的用途。 enum Tag是在Tokens.java中定义的,用在Token类中。而Token类有几个子类,其中一个是NamedToken类,实现了Token的name()方法。而NamedToken是在JavaTokenizer.java类中创建的。 接下来,你就看看在代码里什么地方用到了Token.name()方法就好了。 比如,在JavacParser.java中,在生成AST的标识符节点的时候,就会取出Token.name(),作为标识符的名称。 至于boolen、byte这些,我相信也会在类型分析中用到Token.name()的。我只是猜测,这里并没有去看代码,你可以去验证一下。 BTW,我看上述代码时,对Java的Token类的设计也产生了一点小兴趣。它定义了几个方法,比如name()和stringVal(),但自己又不实现,而是放在子类中去实现。这种设计方法可以拿来为自己所用。这也是阅读别人的代码带来的收益吧!

    2021-02-07
    2
  • wusiration
    补交作业,没有看下一讲的答案.... ​ odStack opStack 后续运算符 step1: a step2: a,b > * step3: a,b,2 >,* + step4: a,b*2 > + step5: a>b*2,3 + step6: a>b*2+7

    作者回复: 做作业的好同学! 然后你可以跟参考答案对照一下,再把思路过一遍!

    2020-07-01
    2
  • lion_fly
    老师,我在debugJava编译器的代码: 在源码的注释里面出现了这样的内容 Qualident = Ident { DOT [Annotations] Ident } Java编译器的这种文法是什么文法,感觉不是上下文无关文法

    作者回复: 这个是上下文无关的文法。 大家可能会使用不同的写法。比如,有的人用产生式的写法,有的人用EBNF的写法,关键看本质。不同的写法本质上是同构的。

    2021-01-21
    1
  • Apsaras
    step1: a step2: a,b > * step3: a,b,2 >,* + step4: a,b*2 >,+ step5: a,b*2,3 >,+ step6: a,b*2+3 step7: a>b*2+3

    作者回复: 我刚给出了参考答案(在下一讲中),你可以对比一下。 略有差异,但看得出你是理解了这个算法的!

    2020-06-23
    1
  • 冬天里的懒猫
    原来竟然是这样。。。 用了这么多年的java,从来没有想过编译器是如何实现的。这篇课程非常有用。

    作者回复: 如果掌握了这种hack的手法,其实通过编译器可以更快地掌握一门语言的各种特性的实质。

    2020-10-28
  • chris
    老师能否介绍一下如何建一个ide工程阅读源码?

    作者回复: 我习惯用IDEA。在IDEA中建立任何一个Java工程,都需要指定一个JDK。而JDK里就包含了源代码。在IDEA中的"External Libraries"目录,就可以打开JDK中的各个模块,查看其源代码。这样,我们就可以在调试程序的时候,跟踪到JDK的源代码里面了。 其他IDE也大致差不多。

    2020-06-23
    2
  • Geek_71d4ac
    交作业 step1: a step2: a,b > * step3: a,b,2 >,* + step4: a,b*2 > + step5:a, b*2,3 >,+ step6:a, b*2+3 > step7: a >b *2+3

    作者回复: 非常好。 看得出你掌握得很扎实。 我刚在下一讲中给出了参考答案。

    2020-06-22
  • mikewt
    老师 javacc编译器跟这个有啥关系 为啥java不用javacc编译
    2023-08-04归属地:上海
    1
收起评论
显示
设置
留言
10
收藏
沉浸
阅读
分享
手机端
快捷键
回顶部