如何用JavaScript实现一个简单的解释器或虚拟机?

如何用JavaScript实现一个简单的解释器或虚拟机?
最新回答
语物忉灼秋

2026-10-31 15:26:46

用JavaScript实现一个简单的解释器或虚拟机,可通过构建极简算术表达式解释器并扩展为栈式虚拟机完成。核心步骤包括定义AST结构、编写递归下降解析器、实现解释器求值,最后扩展为字节码编译与虚拟机执行。

  • 定义AST结构设计支持数字与加减乘除的抽象语法树(AST),包含两种节点类型:

    NumberNode:表示数字,如 { type: 'Number', value: 3 }。

    BinaryOpNode:表示二元操作,如 { type: 'BinaryOp', operator: '+', left: ..., right: ... }。示例AST:

    { type: 'BinaryOp', operator: '+', left: { type: 'Number', value: 3 }, right: { type: 'BinaryOp', operator: '*', left: { type: 'Number', value: 4 }, right: { type: 'Number', value: 2 } }}
  • 编写递归下降解析器将字符串表达式转换为AST,处理运算符优先级(乘除优先于加减):

    function parse(expression) { let pos = 0; function peek() { return expression[pos] || null; } function consume() { return expression[pos++]; } function skipWhitespace() { while (peek() === ' ') consume(); } function parseNumber() { let num = ''; while (/[0-9]/.test(peek())) num += consume(); return { type: 'Number', value: parseInt(num, 10) }; } function parseFactor() { skipWhitespace(); if (/[0-9]/.test(peek())) return parseNumber(); else if (peek() === '(') { consume(); const node = parseExpression(); consume(); return node; } } function parseTerm() { let node = parseFactor(); while (peek() === '*' || peek() === '/') { const op = consume(); node = { type: 'BinaryOp', operator: op, left: node, right: parseFactor() }; } return node; } function parseExpression() { let node = parseTerm(); while (peek() === '+' || peek() === '-') { const op = consume(); node = { type: 'BinaryOp', operator: op, left: node, right: parseTerm() }; } return node; } return parseExpression();}
  • 实现解释器求值递归遍历AST并计算结果:

    function evaluate(ast) { switch (ast.type) { case 'Number': return ast.value; case 'BinaryOp': const left = evaluate(ast.left); const right = evaluate(ast.right); switch (ast.operator) { case '+': return left + right; case '-': return left - right; case '*': return left * right; case '/': return left / right; } }}

    使用示例:

    const code = "3 + 4 * 2";const ast = parse(code);const result = evaluate(ast);console.log(result); // 输出 11
  • 扩展为栈式虚拟机

    编译AST为字节码:function compile(ast) { const instructions = []; function emit(op, value) { instructions.push({ op, value }); } function walk(node) { switch (node.type) { case 'Number': emit('PUSH', node.value); break; case 'BinaryOp': walk(node.left); walk(node.right); emit('OP', node.operator); break; } } walk(ast); return instructions;}

    实现基于栈的虚拟机:function runVM(instructions) { const stack = []; for (const instr of instructions) { switch (instr.op) { case 'PUSH': stack.push(instr.value); break; case 'OP': const b = stack.pop(); const a = stack.pop(); let result; switch (instr.value) { case '+': result = a + b; break; case '-': result = a - b; break; case '*': result = a * b; break; case '/': result = a / b; break; } stack.push(result); break; } } return stack[0];}

    运行示例:

    const ast = parse("3 + 4 * 2");const bytecode = compile(ast);const vmResult = runVM(bytecode);console.log(vmResult); // 输出 11
  • 关键思想与扩展方向

    代码即数据:AST和字节码将代码表示为可操作的数据结构。

    逐步求值:解释器直接计算,虚拟机通过栈模拟执行流程。

    后续扩展:可加入变量、函数、作用域等特性,逐步构建完整语言运行时。

此实现覆盖了从词法分析、语法解析到解释执行或编译运行的全流程,是构建小型语言的基础路径。