基于JS怎么实现一个小型编译器


这篇文章主要讲解了“基于JS怎么实现一个小型编译器”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“基于JS怎么实现一个小型编译器”吧!the-super-tiny-compiler是一个代码行数只有不到 200 行的超级小型的 compiler,但通过这个 compiler 能学习到最基础的 compile 原理,包括 babel 也是基于这样的原理来进行开发的。仓库本身的例子是将一组 lisp 风格的函数语法编译成了 C 风格的函数语法,举例子来说:这就大概是这次 compiler 需要完成的事情,可能看上去语法不是很完整,但是也能够演示现代编译器的主要部分思想了。大多数的 Compilers 都会把编译过程分成三个主要的过程: parse、transform 以及 code generate:parse 主要是将源码转换成一种更抽象的代码表达transform 则是将上面抽象的表达进行任意 compiler 想进行的操作code generate 将 transform 处理之后的代码生成一种新的代码parse 主要分为两个步骤: 词法分析以及语法分析。词法分析将源码根据表达切分一个个的 tokens,tokens 是一组用来描述单独语法的对象,可以是 numbers、labels、punctuation、operators 等语法分析则是将词法分析生成的 tokens 进行重新编排得到一个叫做抽象语法树(AST)的结构,AST 是一种易于使用且能展示完整信息的嵌套树状结构。例如前面提到的add 2 (subtract 4 2)表达式被词法分析处理之后生成的 tokens 大概是:语法分析处理出来的 AST 结构为:transform 主要就是拿到 parse 得到的抽象语法树,并基于此做出一些修改。tranform 这个过程既可以基于当前语言的风格去修改 ast 也可以使用一种新的语言风格。下面基于前面的 ast 结构来展示 transform 这个过程的工作流程。可以看到,ast 里面的元素看起来都很相似,这些元素组成了 ast 的子结点,这些子结点的数据结构类型描述了代码中的一个单独的部分(例如变量、声明语句,表达式等等)。例如上面提到的类型是NumberLiteral的节点:或者更复杂一点的子节点类型:在 transfrom 这个过程中,我们可以通过增/删/改来操作抽象语法树结点,或者可以直接基于当前的抽象语法树创建一个新的出来。既然这里我们的目标是将输入的代码转换成一种新的语言风格的代码(Lisp -> C),所以这里会创建一个针对新语言的全新 AST 出来。因此这里我们需要明确一下修改 AST 的操作:为了能处理所有的结点,我们可以用深度优先搜索对其进行遍历:遍历流程是这样的:Program – 从 AST 的顶结点开始CallExpression (add) – Program 的第一个子元素NumberLiteral (2) – CallExpression (add) 的第一个子元素CallExpression (subtract) – CallExpression (add) 的第二个子元素NumberLiteral (4) – CallExpression (subtract) 的第一个子元素NumberLiteral (2) – CallExpression (subtract) 的第二个子元素如果直接在 ast 内部操作而不是产生一个新的 ast,可能就需要介绍所有的种类的抽象。但目前来看,访问所有结点的方法已经足够了。访问(visiting) 这个词代表一种在对象结构内对元素进行操作的模式。这里我们可以创建一个 visitor 对象,这个对象包括一些方法用于接收不同的结点。例如:因此当我们遍历 ast 的时候,如果匹配到了对应 type 的结点,可以调用 visitor 中的方法来处理。Compiler 的最后一个阶段就是 generate, 这个阶段做的事情可能会和 transformation 重叠,但是代码生成最主要的部分还是根据 AST 来输出代码。Generate 有几种不同的工作方式,有些 Compilers 会重用之前生成的 token,有些则会创建独立的代码表示,以便于线性输出代码,但接下来我们还是着重于使用之前生成好的 AST。我们的生成器需要知道如何打印 AST 中的所有类型结点,然后递归调用自身,知道所有的代码都被打印到一个很长的字符串中。以上就是 Compiler 所有的部分了,但并不是所有的 Compiler 都是这样,不同的 compiler 目的不同免费云主机域名,所以也可能需要不同的步骤。接下来就开始代码的编写:按照前面的理论分析,我们一步先进行 parser 这个阶段里面的词法分析器(tokenizer)。这个函数接收一个字符串,然后将其分割成由 token 组成的数组:ex:(add 2 (substract 4 2))=>[{ type: 'paren', value: '('}, ...]因此可以编写这样的一个函数:词法分析器接收语法分析得到的 token 数组,然后将其转换成 AST 结构。例如:[{ type: 'paren', value: '(' }, ...]=>{ type: 'Program', body: [...] }通过语法分析得到 ast 之后,接下来需要一个遍历器 (visitors) 去遍历结点。然后当遇到某个类型的结点的时候,可以调用 visitors 中对应的类型处理函数:因此我们的代码可以这样写:转换器配合上面的遍历器来一起使用,它接收之前构建好的 ast,然后将其和 visitor 一起传入遍历器中,从而得到一个全新的 AST 出来。原始的 AST 结构为(add 2 (subtract 4 2)):转换之后生成的 AST 结构为(add(2, subtract(4, 2))):接下来我们可以这样编写对应的转换器代码:代码生成器同样是个递归函数,最后会将 AST 中的每个结点打印到一个大的字符串中:到这一步,基本上所有的流程就已经完成了,我们可以创建一个 compiler 函数,通过调用上面的函数就可以完成整个 compiler 的工作了:input => tokenizer => tokenstokens => parser => astast => transformer => newAstnewAst => generator => output代码只需要以下简单几步即可:我们可以输入前面的几组测试例子,能保证得到的结果是正确的。感谢各位的阅读,以上就是“基于JS怎么实现一个小型编译器”的内容了,经过本文的学习后,相信大家对基于JS怎么实现一个小型编译器这一问题有了更深刻的体会,具体使用情况还需要大家实践验证。这里是百云主机,小编将为大家推送更多相关知识点的文章,欢迎关注!

相关推荐: golang怎么利用map实现数组去重

这篇文章主要讲解了“golang怎么利用map实现数组去重”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“golang怎么利用map实现数组去重”吧!可以利用go中,map数据类型的key唯一的属性,来对数组去重…

免责声明:本站发布的图片视频文字,以转载和分享为主,文章观点不代表本站立场,本站不承担相关法律责任;如果涉及侵权请联系邮箱:360163164@qq.com举报,并提供相关证据,经查实将立刻删除涉嫌侵权内容。

(0)
打赏 微信扫一扫 微信扫一扫
上一篇 04/19 16:51
下一篇 04/19 16:52

相关推荐