【问题标题】:Compiler Constuction编译器构造
【发布时间】:2014-01-11 12:58:53
【问题描述】:

我正在做编译器的设计和构建。我一直想知道为什么我们实际上需要编译器中的六个阶段。有人可以指出在编译过程中有多个阶段的一些优点吗?

【问题讨论】:

  • 您具体指的是哪些阶段?这样的列表有无数个略有不同。
  • 这是一个程序,即将大任务分解成小任务的一种方式...
  • 只有六个?荒谬的!应该比六个多得多。请参阅此以获得灵感:cs.indiana.edu/~dyb/pubs/nano-jfp.pdf

标签: compiler-construction compilation compiler-errors compiler-warnings compiler-optimization


【解决方案1】:

正如 delnan 所说,您应该明确说明您正在谈论的阶段。它们没有那么标准化,以至于我们确切地知道您正在谈论的六个。编译器仍然是一个普通程序,和普通程序一样,可能会反映其作者的风格。

部分答案是因为编译是一个非常困难的问题。我曾经有完全相同的问题,但是对于您正在考虑的 6 阶段方案中可能只考虑一个阶段的问题:解析。为什么大家总是先把输入流分成tokens(lexing),然后才给tokens序列赋予结构(parsing)?为什么人们教它,好像它是唯一的方法?是否有定理说必须这样做?

回想起来,答案是可能有其他方法可以做到这一点,但发现这种方法是可行的,而且由于将线性文本转换为结构化 AST 的问题是一个非常困难的问题(当抽象地考虑时,没有我们积累的知识),任何将初始问题细分为明确识别的子任务都会有所帮助。如果某个特定的细分部门过去有效,为什么不继续这样做呢?

当然,解析本身只是将文本转换为可执行代码这一极其困难的问题中的一个子任务。我怀疑在编译器设计的每个级别都使用了相同的经验方法。我们现在的做法反映了过去尝试解决该问题的历史。

【讨论】:

  • 感谢您的回答。
  • 这个特定的(词法分析,然后是解析)长期以来被认为远非最佳甚至合理。而且,不,编译是非常琐碎的问题,而且它大多是微不足道的,因为与大多数其他编程问题不同,它可以(并且很容易)将其拆分为良好分离的阶段。编译器的复杂性被严重高估了。
  • @PascalCuoq,我知道,无 lexerless 与传统解析有点圣战,但尽管如此,你不得不承认,如今 lexerless 正在获得更广泛的认可。
【解决方案2】:

简单地说就是模块化。因此,当您想为不同的语言或目标机器编写编译器时,您只需更改这些模块即可。

【讨论】:

  • 感谢您的回答。
  • 这并不能解释为什么是六个。您只需要两个(也许三个):前端更改为新语言,后端更改为新目标。 (第三个是中间端,在两种情况下都保持不变。)
  • 请允许我引用 Andrew W. Appel 的话:“如果设计者关注基本的抽象和接口,任何大型软件系统都更容易理解和实现。”
  • + "将编译器分解成这么多的部分可以重用组件。例如,要更改编译器为其生成机器语言的目标机器,只需替换框架布局和指令选择模块。要更改正在编译的源语言,只需更改通过 Translate 向上的模块。编译器可以在抽象语法接口处附加到面向语言的语法编辑器。"
【解决方案3】:

编写单遍编译器是绝对可能的。这可能不是一个好主意,但有可能,所以不,我们“不需要”六次通过。

正如@transporter_room_3 正确指出的那样,拥有多个通道(并且让这些通道与定义明确的接口一起工作,通常是通过让所有中间通道接收和发出相同的 IR)只会使其更易于阅读,因此设计和编写以及更改和维护编译器。

至于为什么正好是六个——我认为这个数字没有任何一致意见,而且肯定有人会将每个不同的优化模块视为一个单独的通道,因此他们会很高兴地改变通行次数频繁。我假设您目前正在学习编译器课程,而您的老师刚刚告诉您您使用了六个阶段?那么,你的答案就是:为了让学习更容易,我们通常不会从一开始就学习/教授完全一般的画面。我们首先查看一些限制区域,然后检查哪些边界仅用于学习,哪些是问题固有的。或者你会在开始数数之前教一个 4 到 6 岁的孩子关于集合和幺半群吗?

【讨论】:

  • 感谢您的回答。
猜你喜欢
  • 2023-04-11
  • 1970-01-01
  • 2016-09-25
  • 2019-04-16
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-10-04
  • 2020-11-25
相关资源
最近更新 更多