如何用JavaScript结合PEG.js解析带上下文变量的ODK XForm计算表达式?
实现ODK XForm计算表达式解析器的PEG.js方案
核心思路
PEG.js可以直接生成带语义动作的解析器,将用户输入的表达式解析成结构化的AST(抽象语法树),再通过遍历AST结合上下文执行计算,完全规避直接执行用户代码的安全风险。针对concat("hello", ${some_field})这类表达式,我们可以分语法解析和AST执行两步实现需求。
步骤1:编写PEG.js语法规则
先针对ODK XForm计算表达式的核心语法(函数调用、变量引用、字符串字面量)定义PEG.js规则,解析时直接生成描述执行逻辑的AST节点。
创建odk-expression.pegjs文件,内容如下:
// 起始入口 start = expression // 表达式:支持函数调用、变量、字符串字面量 expression = functionCall / variable / stringLiteral // 函数调用规则:匹配类似concat(arg1, arg2)的结构 functionCall = identifier "(" (expression ("," expression)*)? ")" { return { type: 'functionCall', name: text().split('(')[0], args: Array.from(arguments[2] || []) }; } // 变量引用规则:匹配${some_field}格式 variable = "${" identifier "}" { return { type: 'variable', name: text().slice(2, -1) }; } // 字符串字面量规则:匹配带双引号的字符串 stringLiteral = "\"" chars:([^"]*) "\"" { return { type: 'string', value: chars.join('') }; } // 标识符规则:匹配函数名、变量名 identifier = [a-zA-Z_][a-zA-Z0-9_]*
用PEG.js命令行工具生成解析器:
pegjs odk-expression.pegjs
生成的odk-expression.js就是可直接导入的解析模块。
步骤2:实现AST执行器
编写execute函数,遍历解析得到的AST,结合上下文和预设的ODK内置函数完成计算:
const Parser = require('./odk-expression'); // 预设ODK内置函数白名单 const builtInFunctions = { concat: (...args) => args.join(''), // 可扩展其他ODK函数:sum、if、boolean等 }; function execute(ast, context) { switch (ast.type) { case 'functionCall': // 先递归解析所有参数 const resolvedArgs = ast.args.map(arg => execute(arg, context)); if (!builtInFunctions[ast.name]) { throw new Error(`未知函数: ${ast.name}`); } return builtInFunctions[ast.name](...resolvedArgs); case 'variable': if (!(ast.name in context)) { throw new Error(`未找到变量: ${ast.name}`); } return context[ast.name]; case 'string': return ast.value; default: throw new Error(`未知节点类型: ${ast.type}`); } } // 使用示例 const expr = 'concat("hello", ${some_field})'; const ast = Parser.parse(expr); const result = execute(ast, { some_field: 'world' }); console.log(result); // 输出 "helloworld"
关键安全保障
- 禁用动态代码执行:完全通过AST遍历执行逻辑,绝不使用
eval或new Function处理用户输入。 - 函数白名单机制:仅允许预设的ODK内置函数,禁止调用任意JS全局函数。
- 上下文隔离:仅暴露用户指定的变量,避免访问敏感全局对象。
扩展建议
- 补充更多ODK语法支持:添加数字字面量、算术/逻辑运算、条件表达式等规则,完善PEG.js语法定义。
- 优化错误提示:在PEG.js中添加错误捕获规则,返回更精准的解析错误位置和信息。
- AST缓存:对重复执行的表达式缓存解析后的AST,提升执行效率。
内容的提问来源于stack exchange,提问作者twoLeftFeet
相关产品推荐
相关产品推荐

