编译器设计概述
引言
你拥有十年使用高级 JavaScript Web 3.0 开发蒸汽朋克软件的经验,你是一位企业家、咨询明星和工程师……但你是否曾想过你的计算机是如何“读取”、“解释”和“执行”你的程序的?本文旨在帮助你轻松理解计算机科学中这一至关重要的部分。
我知道我在这里做了一些简化,也可能做了一些不恰当的比较,但我只是想给自己和其他人提供一个思维模型。现在免责声明完毕,我们可以开始深入探讨了,牵着我的手,让我们一起飞翔吧!
概要
那么,现在来对编译器做一个非常概括的概述:
高级语言——它可以是 JavaScript、Python、C++,或者你喜欢的任何语言等等。简而言之,它只是一个字符串。
编译器——基于上一步,您应该已经知道编译器的输入是字符串,输出是汇编语言。因此,我们可以把编译器看作一个简单的函数,它接收一个字符串并生成另一个字符串。
Assembly code string = compiler(high level language string)
最后一个问题是——什么是汇编代码?
汇编语言——就像不同操作系统的使用手册;换句话说,它是一套适用于具有特定架构(例如 x86 架构)的机器的指令集。
这是我们的最后一步:
机器代码——实际上就是 0 和 1。
但你可以提一下,我在最后一个模块中放置了两个部件——装载机/连接器。
仅供参考,我们来简单介绍一下。
链接器是一个程序,它将编译器/汇编器生成的目标文件和其他代码片段组合起来,生成一个带有特定.exe扩展名的可执行文件。在目标文件中,链接器会搜索并添加执行文件所需的所有库。它还会管理每个模块代码的内存空间。
加载器是一个特殊的程序,它从链接器接收可执行文件作为输入,将其加载到主内存中,并准备好供计算机执行的代码。加载器会为程序分配内存空间,甚至建立对象之间的符号引用。它负责在操作系统中加载程序和库。嵌入式计算机系统没有加载器。
(我从这个网站复制了这两个定义)
深入兔子洞
那么,让我们更进一步,深入研究编译器部分,这对我们来说现在更有趣。
还有一张图片值得欣赏:
词法分析——去除空格、无用符号,并返回词法单元流。例如:
输入字符串
SELECT * FROM "docs";
你可以把数据流看作一个迭代器,可以从中提取下一个标记。以下是一些伪代码:
Lexer.next() -> { kind: Keyword, type: "SELECT" }
Lexer.next() -> { kind: Asterisk }
Lexer.next() -> { kind: Keyword, type: "FROM" }
Lexer.next() -> { kind: Char, value: "\"" }
Lexer.next() -> { kind: String, value: "docs" }
Lexer.next() -> { kind: Char, value: "\"" }
所以它lexer代表了我们语言或字母表中的有效字符。
为了更好地理解,我们先来解析一个简单的字符串:
x = a + b * c;
所以,作为人类,我们可以轻松解析和理解这类语句,但对计算机来说却并非易事。我们已经讨论了解析器的用途,现在让我们来解析上面的语句。我们尝试简化一下:
id = id + id * id
其中 id 是一个标识符,如果我们尝试将其表示为数组,它可能看起来像这样:
[
{ type: 'id', value: x },
{ type: 'symbol', value: '=' },
{ type: 'id', value: 'a' },
{ type: 'symbol', value: '+' },
{ type: 'id', value: 'b' },
{ type: 'symbol', value: '*' },
{ type: 'id', value: 'c' },
{ type: 'symbol', value: ';' },
]
解析器——使用上下文无关文法(我们将在下一部分介绍这个定义)来创建解析树。
别紧张,我们会介绍很多复杂的定义和术语,如果你觉得有点迷糊,没关系。接下来的章节会涵盖所有与解析相关的内容。
因此,基于语言语法,解析器会创建解析树,这有助于我们找到语句的正确执行顺序。
让我们来看一个BNF 表示法的语法示例。
S ⭢ id = E
E ⭢ E + T
| T
T ⭢ T * F
| F
F ⭢ id
基于此语法,我们可以构建一个分析树:
语义分析器——验证解析树以查找语法错误。
中间码生成器——生成三地址码
例如,它可能看起来像这样:
t1 = b * c;
t2 = a + t1;
x = t2;
代码优化器(CO) ——优化三地址代码。
t1 = b * c;
x = a + t1;
目标代码生成器——生成汇编器可以理解的代码。
一些随机的伪架构:
mul R1, R2
add R0, R2
mov R2, x
其中 a = R0,b = R1,c = R2
欢迎提问、表达意见或疑虑,也欢迎讨论你的观点。分享、订阅,一起编写代码,而不是挑起战争。❤️
如果你发现错误,我很乐意改正或者向你学习——请告诉我。
你可以通过推特私信我,或者在领英上联系我。我一直都乐于结识新朋友,拓展人脉,把握新机遇。
文章来源:https://dev.to/frolovdev/high-level-overview-of-compiler-design-2l0o


