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

答案是通过JavaScript实现一个极简算术表达式解释器和虚拟机:首先定义支持数字与加减乘除的AST结构,接着编写递归下降解析器将字符串转为AST,然后实现递归求值函数evaluate对AST进行解释执行,最后扩展为式虚拟机,通过compile将AST转为字节码指令,再由runVM基于栈执行得出结果。整个过程体现了“代码即数据”和“逐步求值”的核心思想,构成了构建小型语言运行时的基础路径。

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

实现一个简单的解释器或虚拟机,核心是理解“代码即数据”和“逐步求值”的思想。JavaScript 由于其动态性和灵活性,非常适合用来构建小型语言的执行环境。下面通过一个极简的算术表达式解释器来说明基本流程。

定义语言结构

我们先设计一种极简的语言,只支持加减乘除和数字。比如表达式:3 + 4 * 2。为了便于处理,先将其转化为抽象语法树(AST)。

AST 节点可以是:

NumberNode:表示一个数字BinaryOpNode:表示二元操作(如 +, -, *, /)

示例 AST 结构:

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

{  type: 'BinaryOp',  operator: '+',  left: { type: 'Number', value: 3 },  right: {    type: 'BinaryOp',    operator: '*',    left: { type: 'Number', value: 4 },    right: { type: 'Number', value: 2 }  }}

编写解析器(Parser)

将字符串转换为 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();}

实现解释器(Evaluator)

解释器遍历 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

扩展为简单虚拟机(VM)

如果想更接近虚拟机模型,可以用栈来执行指令。

先将 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

基本上就这些。从词法分析、语法解析到解释执行或编译运行,这是构建语言的基础路径。不复杂但容易忽略细节,比如运算符优先级和错误处理。后续可加入变量、函数、作用域等特性,逐步演变为完整的小型语言运行时。

以上就是如何用JavaScript实现一个简单的解释器或虚拟机?的详细内容,更多请关注创想鸟其它相关文章!

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

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

相关推荐

  • JavaScript邮政编码四位数字验证:正则表达式陷阱与解决方案

    本教程旨在解决JavaScript中邮政编码验证时,正则表达式^[0-9]d{4}$错误匹配五位数字的问题。我们将深入解析该正则表达式的构成,并提供正确的四位数字验证模式^d{4}$,结合实际代码示例,确保邮政编码输入仅包含四位数字且全部为数字,从而提升前端表单验证的准确性。 理解现有问题:正则表达…

    2025年12月20日
    000
  • 精确校验四位数字邮政编码:JavaScript正则表达式实践

    本教程详细介绍了如何使用JavaScript和正则表达式精确验证四位数字的邮政编码。文章分析了常见的正则表达式错误,并提供了正确的^d{4}$模式及其在前端验证逻辑中的应用。通过示例代码,读者将学习如何确保用户输入仅包含不多不少的四位数字,从而提升数据质量和用户体验。 邮政编码验证的重要性 在web…

    2025年12月20日
    000
  • JavaScript:通过对象结构化数据实现数组分类名称的显示教程

    在JavaScript中,将多个一维数组组合成多维数组时,直接获取并显示每个一维数组的原始变量名是一个常见挑战。本文将探讨为何传统的多维数组结构无法直接实现此目的,并提供一种更优的解决方案:通过将一维数组作为对象的属性值,利用对象的键(key)来代表并显示所需的分类名称,从而实现清晰、语义化的数据展…

    2025年12月20日
    000
  • JavaScript实现可折叠图片显示/隐藏功能教程

    本教程详细介绍了如何使用JavaScript和HTML创建一个可折叠的图片显示/隐藏功能。通过引入一个状态变量来管理图片当前是展开还是折叠,结合按钮点击事件动态切换图片的可见性及按钮文本,实现用户友好的交互式内容展示,适用于在网页中按需显示或隐藏图片资源。 1. 功能概述与核心思路 在网页开发中,有…

    2025年12月20日 好文分享
    000
  • JavaScript中罗马数字转换的陷阱:for…in循环与对象属性顺序

    本文深入探讨了JavaScript中实现罗马数字转换时,因for…in循环对对象属性的迭代顺序不当而导致的常见问题。核心在于JavaScript对整数型键的特殊处理,它会按数值升序遍历这些键,而非按定义顺序。我们将通过对比两种代码实现,详细解释这一机制如何破坏贪婪算法的逻辑,并提供正确的…

    2025年12月20日
    000
  • JavaScript中根据图像索引计算计数器:实现每3个图像递增1的逻辑

    本文旨在探讨如何在JavaScript中根据图像索引(`imact`)精确计算一个计数器(`cont`),使其每当`imact`达到3的倍数时,`cont`的值递增1。文章将深入分析用户期望的计数器行为,并提供两种实现方法:一种是推荐的直接数学运算,确保计数器始终与图像索引保持同步;另一种是基于条件…

    2025年12月20日
    000
  • 优化Next.js应用:禁用不必要的子页面预加载

    本文旨在解决Next.js应用中因默认预加载行为导致的不必要资源消耗问题,特别是当子页面涉及昂贵的外部数据读取时。通过在组件上设置prefetch={false}属性,开发者可以有效阻止Next.js在父页面加载时预加载子页面数据,从而优化性能、降低服务器请求和数据费用,实现更精细的资源管理。 理解…

    2025年12月20日
    000
  • JavaScript 中的闭包为何会导致内存泄漏,又该如何避免?

    闭包因保留对外部变量的引用而延长其生命周期,若内部函数被长期持有且未及时释放,如赋值全局变量、未解绑事件监听或定时器,会导致本应回收的内存无法释放,从而引发内存泄漏;例如createLargeClosure返回的函数持续引用largeData,造成内存占用;避免方法包括减少闭包中大对象引用、及时清理…

    2025年12月20日
    000
  • JavaScript 的动态类型系统在类型转换时遵循怎样的隐式规则?

    JavaScript隐式转换依据上下文自动转类型,+操作符遇字符串触发字符串拼接,算术运算符强制转数字,布尔环境判断真/假值,==进行松散相等比较时执行类型转换,对象转原始值优先调用valueOf再toString,可自定义Symbol.toPrimitive控制行为。 JavaScript 的动态…

    2025年12月20日
    000
  • 显示 JavaScript 多维数组中一维数组的变量名

    本文介绍了如何在 JavaScript 中遍历一个包含多个一维数组的多维数组,并显示每个一维数组的变量名。通过使用对象来存储数组,并利用对象的属性名来表示变量名,可以方便地在循环中输出数组名和数组元素。本文提供了详细的代码示例和解释,帮助读者理解和应用这种方法。 在 JavaScript 中,直接将…

    2025年12月20日
    000
  • 掌握Bootstrap下拉菜单的精确关闭控制:JavaScript初始化方法

    本文详细阐述了如何解决Bootstrap响应式导航栏中下拉菜单在点击外部区域时无法自动关闭的问题。尽管使用了data-bs-auto-close=”outside”属性,但有时仍需通过JavaScript显式初始化bootstrap.Dropdown组件,并配置autoClo…

    2025年12月20日
    000
  • JavaScript中动态创建对象属性名:计算属性名与赋值技巧

    本文详细阐述了在JavaScript中如何动态地创建对象属性名。针对直接使用模板字符串作为键的常见误区,教程介绍了两种核心方法:利用ES6的计算属性名(Computed Property Names)语法在对象字面量中直接定义动态键,以及通过后续的方括号赋值操作动态添加属性,并提供了清晰的代码示例和…

    2025年12月20日
    000
  • 深入理解JavaScript递归:高效统计嵌套对象与数组数量

    本文详细探讨了如何使用JavaScript递归函数来高效统计复杂嵌套对象中包含的对象和数组数量。通过一个具体的示例,我们将深入分析递归调用的工作原理,特别是 count += recursiveFunctionCall() 这种累加赋值操作在多层级计数中的关键作用,帮助开发者掌握递归在处理复杂数据结…

    2025年12月20日
    000
  • JavaScript 的日期与时间处理为何推荐使用 Moment.js 的替代品?

    Moment.js因体积大、不可变性差及停止维护已被淘汰,推荐使用date-fns或Day.js等更轻量、高效的现代替代方案。 JavaScript 原生的日期处理能力有限,而 Moment.js 曾是社区广泛使用的解决方案。但随着技术发展,Moment.js 的缺点逐渐显现,现在更推荐使用其现代替…

    2025年12月20日
    000
  • 解决Axios响应拦截器返回undefined问题的实用指南

    本文深入探讨了在使用Axios进行API请求时,尽管响应拦截器已正确处理数据,但前端接收到的响应却为undefined的常见问题。核心原因在于API封装函数未能正确返回Axios实例生成的Promise对象。通过对比错误和正确的API封装方式,特别是箭头函数的使用,文章提供了清晰的解决方案,并强调了…

    2025年12月20日
    000
  • 如何编写符合无障碍(A11y)标准的JavaScript交互代码?

    答案是编写无障碍JavaScript交互需确保键盘可访问、合理管理焦点、正确使用ARIA属性,并避免破坏屏幕阅读器体验,例如通过监听keydown事件支持键盘操作,模态框打开时转移并限制焦点,动态内容更新时利用aria-live通知用户,优先使用语义化HTML标签,配合自动化工具与手动测试保障可访问…

    2025年12月20日
    000
  • 如何将JavaScript对象高效转换为具有特定键名的新数组

    本文将指导您如何将单个JavaScript对象高效转换为一个包含特定键名映射的新数组。文章将纠正常见的循环误区,并展示如何结合使用 Array.prototype.push() 和 Array.prototype.map() 方法,实现简洁且正确的对象键值转换与数组封装,确保数据结构符合预期。 在j…

    2025年12月20日
    000
  • 修复CSS按钮点击时移动问题的教程

    本文旨在解决CSS按钮在点击时发生位置偏移的问题,该问题通常由按钮不同状态下边框样式或内边距的变化导致。通过深入分析CSS盒模型与布局原理,本教程将详细介绍如何利用vertical-align属性稳定按钮的垂直位置,并提供完整的代码示例和最佳实践,确保按钮在交互过程中保持视觉上的稳定性。 问题描述:…

    2025年12月20日
    000
  • JavaScript中实现对象数组的SQL式分组与聚合

    本文将详细介绍如何在JavaScript中对对象数组进行分组和聚合操作,以实现类似于SQL SUM 和 GROUP BY 的功能。我们将通过一个具体的案例,演示如何根据 ProjectType 字段对数据进行分组,并计算每个组的 Amount 和 Hours 总和,最终生成结构化的结果,这对于在Re…

    2025年12月20日
    000
  • JavaScript动态创建Bootstrap元素:解决样式未生效的视觉假象

    在通过JavaScript动态向DOM添加带有Bootstrap类的HTML元素时,开发者常误以为其样式未生效。这并非Bootstrap样式缺失,而是由于动态创建的元素(如按钮或段落)缺乏必要的文本内容。Bootstrap组件的许多样式依赖于其内部内容来正确渲染尺寸和布局,因此,内容缺失会导致元素显…

    2025年12月20日
    000

发表回复

登录后才能评论
关注微信