如何在Chevrotain解析器中实现数组支持以开发支持数组运算的计算器
实现方案
你提到的按索引多次运行访问器的思路完全可行,Chevrotain场景下有两种低改造成本的实现路径:
方案一:单轮AST遍历 + 广播式运算
该方案仅需遍历一次AST,性能更优:
- 第一步:统一所有叶子节点输出格式
- 普通整数标量统一转为与最长运算数组长度一致的填充数组,例如标量
2对应长度为5的数组时转成[2,2,2,2,2] - 变量节点直接返回映射表中对应的数组,提前校验所有参与运算的数组长度一致,避免维度不匹配报错
- 普通整数标量统一转为与最长运算数组长度一致的填充数组,例如标量
- 第二步:修改运算节点访问器逻辑,对输入的两个数组做逐位运算,示例如下:
// 加法节点访问器示例 addOperation: (node) => { const leftVal = this.visit(node.leftChild) const rightVal = this.visit(node.rightChild) return leftVal.map((item, idx) => item + rightVal[idx]) }
乘法、括号等其他运算节点按相同逻辑改造即可,根节点最终返回的就是完整的结果数组。
方案二:多轮按索引求值(你原思路的落地实现)
该方案不需要修改原有运算节点逻辑,改造成本更低:
- 第一步:修改变量节点访问器,增加可选索引入参:
// 变量节点访问器示例 variableNode: (node, index = null) => { const varName = node.varName const val = map[varName] // 传入索引时返回对应下标的标量,否则返回完整值 return Array.isArray(val) && index !== null ? val[index] : val }
- 第二步:外层按数组长度循环调用访问器,收集结果:
const result = [] // 提前获取所有数组的统一长度 const arrayLength = map.array1.length for (let i = 0; i < arrayLength; i++) { // 每次调用传入当前索引,返回当前位的运算结果 const singleRes = visitor.visit(astRoot, i) result.push(singleRes) }
注意:两种方案都需要提前做边界校验,保证所有参与运算的数组长度一致,避免下标越界问题。
内容的提问来源于stack exchange,提问作者eakl
相关产品推荐
相关产品推荐

