如何用JavaScript实现一个简单的编译器(从源码到AST)?

词法分析将源码拆分为tokens,语法分析构建AST,最终生成类似add(1,multiply(2,3))的表达式树。

如何用javascript实现一个简单的编译器(从源码到ast)?

实现一个简单的编译器,核心第一步是从源码构建出抽象语法树(AST)。这个过程主要包括词法分析(Lexical Analysis)和语法分析(Parsing)。下面用 JavaScript 实现一个极简的编译器,支持类似 add(1, multiply(2, 3)) 这样的表达式,并生成对应的 AST。

词法分析:将源码拆分为 Token

词法分析器(Tokenizer)读取源代码字符串,将其分解为有意义的标记(tokens),比如标识符、括号、数字等。

function tokenizer(input) {  let current = 0;  const tokens = [];  while (current < input.length) {    let char = input[current];    // 处理括号    if (char === '(' || char === ')') {      tokens.push({        type: 'paren',        value: char      });      current++;      continue;    }    // 跳过空白字符    if (/s/.test(char)) {      current++;      continue;    }    // 处理数字(支持多位)    if (/[0-9]/.test(char)) {      let value = '';      while (/[0-9]/.test(input[current])) {        value += input[current++];      }      tokens.push({ type: 'number', value });      continue;    }    // 处理字母(用于函数名如 add, multiply)    if (/[a-z]/i.test(char)) {      let value = '';      while (/[a-z]/i.test(input[current])) {        value += input[current++];      }      tokens.push({ type: 'name', value });      continue;    }    throw new TypeError('未知字符: ' + char);  }  return tokens;}

语法分析:从 Token 构建 AST

语法分析器(Parser)遍历 tokens,根据语法规则构造出 AST。我们假设语法是类似 Lisp 的结构:函数调用写成 (func arg1 arg2)

function parser(tokens) {  let current = 0;  function walk() {    let token = tokens[current];    // 如果是数字,生成 NumberLiteral 节点    if (token.type === 'number') {      current++;      return {        type: 'NumberLiteral',        value: token.value      };    }    // 如果是左括号,说明是函数调用    if (token.type === 'paren' && token.value === '(') {      token = tokens[++current]; // 跳过 '('      // 下一个 token 是函数名      let node = {        type: 'CallExpression',        name: token.value,        params: []      };      token = tokens[++current]; // 跳过函数名      // 收集参数,直到遇到右括号      while (token.type !== 'paren' || token.value !== ')') {        node.params.push(walk()); // 递归解析子节点        token = tokens[current];      }      current++; // 跳过 ')'      return node;    }    throw new TypeError('Unexpected token: ' + token.type);  }  let ast = {    type: 'Program',    body: []  };  while (current < tokens.length) {    ast.body.push(walk());  }  return ast;}

完整示例:从源码到 AST

把上面两个部分组合起来,就可以把一段简单代码转换成 AST。

立即学习“Java免费学习笔记(深入)”;

// 示例输入const input = 'add(1, multiply(2, 3))';// 执行编译步骤const tokens = tokenizer(input);const ast = parser(tokens);console.log(JSON.stringify(ast, null, 2));

输出结果如下:

{  "type": "Program",  "body": [    {      "type": "CallExpression",      "name": "add",      "params": [        {          "type": "NumberLiteral",          "value": "1"        },        {          "type": "CallExpression",          "name": "multiply",          "params": [            {              "type": "NumberLiteral",              "value": "2"            },            {              "type": "NumberLiteral",              "value": "3"            }          ]        }      ]    }  ]}

总结与扩展思路

这个简易编译器完成了从源码到 AST 的基本流程:

Tokenizer 把字符串变成 token 流 Parser 利用递归下降方法构建树形结构 输出符合约定格式的 AST,便于后续遍历、转换或生成代码

你可以在此基础上扩展:

支持更多语法,如变量、操作符(+、-)、if 表达式 加入错误处理和位置信息(行、列) 添加遍历器(traverser)和转换器(transformer)来实现完整编译流程基本上就这些。不复杂但容易忽略细节,比如括号匹配和递归调用的控制。

以上就是如何用JavaScript实现一个简单的编译器(从源码到AST)?的详细内容,更多请关注创想鸟其它相关文章!

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 chuangxiangniao@163.com 举报,一经查实,本站将立刻删除。
发布者:程序猿,转转请注明出处:https://www.chuangxiangniao.com/p/1526067.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2025年12月20日 18:12:24
下一篇 2025年12月20日 18:12:33

相关推荐

发表回复

登录后才能评论
关注微信