简易编译器的架构及实现

前言


最近因为要编写一些Web应用,打算重拾JavaScript(以下简称JS),因此选择使用实现一个简易的编译器来熟悉项目的编写。
编译的源代码则是选择了这个世界上最优雅,最古老的语言——Lisp,其拥有良好的闭包性,规律的语法规则,以及纯粹的语言逻辑让其成为了目标语言的不二之选。要解析出现代编程语言的语法树可能比较困难,但是Lisp则是真正地实现了所见即所得,它是如此的优秀以至于我不惜偏离主题也要向大家介绍这个强大的语言。

原理


概念上来讲,编译器的主要任务就是将一种语言翻译成另一种语言的程序,复杂的语言学话题暂且不论,要让一段复杂的程序被程序所认识,我们需要将其中的词法、语法以及语义准确地分析出来,并通过语法树将源码精准地翻译为目标语言(一般为机器语言或者低级语言)。
以Lisp的基本语法为例:

1
(Function Arg1 Arg2)

函数被包含在括号内,并紧跟着参数,对于这个函数进行语义分析后可以得到结果:
(‘开始符’ | Function’函数名’ | Arg1’参数名’ | ‘Arg2‘参数名’ | )’结束符’
再对其进行语法分析后可以得到以下语法树:
Function
:Arg1
:Arg2
再通过这个语法树生成目标代码即可得到结果

具体实现


该编译器实现的核心思想是将 Common-Lisp 代码转化为 JavaScript 格式,然后使用 JavaScript 引擎执行该代码。

概念上来讲,编译器的主要任务就是将一种语言翻译成另一种语言的程序,复杂的语言学话题暂且不论,要让一段复杂的程序被程序所认识,我们需要将其中的词法、语法以及语义准确地分析出来,并通过语法树将源码精准地翻译为目标语言(一般为机器语言或者低级语言)。

在转换过程中,为了保证转换后的代码能够在 JavaScript 中正确执行,该编译器需要做许多辅助处理工作,比如:处理 Common-Lisp 代码中的函数调用、变量声明、条件分支等等语法元素。

实现效果


输入:

输出:

一些小瑕疵


因为目前该编译器使用堆栈的形式对源语言进行语法分析,所以下面这种形式的Common-Lisp语句将无法分析:

(Function (Function a b)(Function c d))

在编译器分析完第一个函数后,会将下一个读取到的函数直接压在上面(于是生成的语法树就错误地变成了嵌套形式)