200行代码实现超轻量级编译器

前言本篇内容主要由the-super-tiny-compiler中的注释翻译而来,该项目实现了一款包含编译器核心组成的极简的编译器。希望能够给想要初步了解编译过程的同学提供到一些帮助。概要本篇和大家一起学习写一款超级简单轻量,去掉注释只有不到200行代码的编译器。 该编译器将类lisp语法函数调用 编译为 类C语言函数调用 如果不熟悉上述的两种语法的其中任意一种,下面给出了简...
摘要由CSDN通过智能技术生成

前言

本篇内容主要由 the-super-tiny-compiler中的注释翻译而来,该项目实现了一款包含编译器核心组成的极简的编译器。希望能够给想要初步了解编译过程的同学提供到一些帮助。

概要

  1. 本篇和大家一起学习写一款超级简单轻量,去掉注释只有不到200行代码的编译器。
  2. 该编译器将类 lisp 语法函数调用 编译为 类C语言函数调用
  3. 如果不熟悉上述的两种语法的其中任意一种,下面给出了简单的介绍
  4. 例如有两个函数 add 和 subtract 他们用对应的语言分别实如余下:
内容 类lisp 类C
2 + 2 (add 2 2) add(2, 2)
4 - 2 (subtract 4 2) subtract(4,2)
2 + ( 4-2 ) (add 2 (subtract 4 2)) add(2, subtract(4,2))
  1. 本篇要实现编译的全部语法如上所示。虽然既不涵盖完整的lisp语法和c语法,但是足够展示一个现代编译器需要的主要组成部分

编译器组成

大部分的编译器可以粗略的划分为3个阶段: 解析 Parsing,翻译 Transformation,代码生成Code Generation

  1. 解析 获取原始代码并将其转化为一个更抽象的代码表示
  2. 翻译 用抽象的代码表示为编译器想要完成的操作做准备
  3. 代码生成 将翻译过的抽象表示转化为新的要编译的代码

解析 Parsing


解析过程通常被分为两个部分: 词法分析语法分析

  1. 词法分析 获取原始代码 ,且将代码分割为一个一个词[token] 由这些词构成的词组用来描述语法,他们可以是数字,文本,标点符号,运算符等等
  2. 语法分析 获取词组[tokens]且将他们重新格式化为一个表示形式,该表示形式描述语法的每个部分及其相互之间的关系。这称为中间表示或抽象语法树。 抽象语法树(简称AST)是一个嵌套很深的对象,它以一种既容易使用又能告诉我们很多信息的方式表示代码。

示例语法

(add 2 (subtract 4 2)) tokens表示如下

    { type: 'paren',  value: '('        },
     { type: 'name',   value: 'add'      },
     { type: 'number', value: '2'        },
     { type: 'paren',  value: '('        },
     { type: 'name',   value: 'subtract' },
     { type: 'number', value: '4'        },
     { type: 'number', value: '2'        },
     { type: 'paren',  value: ')'        },
     { type: 'paren',  value: ')'        },
   ]

抽象语法树表示如下

    {
      type: 'Program',
      body: [{
        type: 'CallExpression',
        name: 'add',
        params: [{
          type: 'NumberLiteral',
          value: '2',
        }, {
          type: 'CallExpression',
          name: 'subtract',
          params: [{
            type: 'NumberLiteral',
            value: '4',
          }, {
            type: 'NumberLiteral',
            value: '2',
          }]
        }]
      }]
    }

翻译

获得抽象语法树后下一个阶段就是翻译转换。同样,这只需要从最后一步中提取AST并对其进行更改。它可以用同一种语言操纵AST,也可以将AST翻译成一种全新的语言。

让我们看看如何转换AST。

你可能会注意到我们的AST中有看起来非常相似的元素。这些对象具有类型属性。每个节点都称为AST节点。这些节点定义了描述树的一个独立部分的属性。

我们有一个数字节点 "NumberLiteral"

{
    type: 'NumberLiteral',
    value: '2',
}

或者一个调用表达式节点

  {
     type: 'CallExpression',
     name: 'subtract',
     params: [...nested nodes here...],
  }

转换AST时,我们可以通过添加/删除/替换属性来操纵节点,可以添加新节点,删除节点,也可以不使用现有的AST直接基于它创建一个全新的AST。

由于我们定位的是新语言,因此我们将专注于创建特定于目标语言的全新AST。

遍历

为了浏览所有这些节点,我们需要能够遍历它们。 如下将通过深度优先方式的遍历AST的每个节点。

{
     type: 'Program',
     body: [{
       type: 'CallExpression',
       name: 'add',
       params: [{
         type: 'NumberLiteral',
         value: '2'
       }, {
         type: 'CallExpression',
         name: 'subtract',
         params: [{
           type: 'NumberLiteral',
           value: '4'
         }, {
           type: 'NumberLiteral',
           value: '2'
         }]
       }]
     }]
   }

因此,对于上述AST,我们将:

  1. Program - 从AST的顶层开始
  2. CallExpression (add) - 转到程序的第一个元素
  3. NumberLiteral (2) - 移至CallExpression参数
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值