
本文介绍如何通过自定义 peg.js 语义动作,将解析后的数值结果格式化为十进制、十六进制和二进制三行字符串输出,并支持多行输入与独立结果分隔。
本文介绍如何通过自定义 peg.js 语义动作,将解析后的数值结果格式化为十进制、十六进制和二进制三行字符串输出,并支持多行输入与独立结果分隔。
在 PEG.js 中,语法解析器的输出并非固定为原始匹配文本,而是可通过语义动作(semantic actions)完全自定义返回值。要实现对算术表达式(如 (3*(5+8)))求值后,分别以十进制(39)、十六进制(27)和二进制(00100111)格式输出于独立行,关键在于:
- 正确求值表达式(需递归计算 AST 或使用 eval 安全变体);
- 在顶层匹配规则中统一格式化并换行拼接;
- 处理多行输入时,确保每行独立解析、独立输出。
以下是一个完整可运行的 PEG.js 语法示例(兼容 PEG.js 0.10+):
// arithmetic.pegjs
Start
= _ expr:Expression _ {
const val = evalExpr(expr); // 安全求值函数见下文
return `${val}\n${val.toString(16)}\n${val.toString(2).padStart(8, '0')}`;
}
Expression
= left:Term op:("+" / "-") right:Expression {
return { type: "binary", op, left, right };
}
/ Term
Term
= left:Factor op:("*" / "/") right:Term {
return { type: "binary", op, left, right };
}
/ Factor
Factor
= "(" _ expr:Expression _ ")" { return expr; }
/ number:Integer { return number; }
Integer "integer"
= digits:[0-9]+ { return parseInt(digits.join(""), 10); }
_ "whitespace"
= [ \t\n\r]*
// 辅助求值函数(注入至 parser options)⚠️ 重要说明:
- eval() 在浏览器环境存在安全风险,生产环境应使用白名单算术解析器(如 mathjs.evaluate 或手写递归下降求值器)。本文为简洁起见使用 evalExpr 占位,实际需替换为安全实现(见下方参考);
- 二进制补零(.padStart(8, '0'))确保输出为标准 8 位格式,可根据需求调整(如 padStart(32, '0'));
- 换行符 \n 是纯文本换行,若在 HTML 中渲染需配合
或 white-space: pre-wrap 样式。</li></ul><p>✅ <strong>安全求值函数参考(推荐集成):</strong> </p><div class="aritcle_card flexRow"> <div class="artcardd flexRow"> <a class="aritcle_card_img" href="/ai/1023" title="Zeemo AI"><img src="https://img.php.cn/upload/ai_manual/000/000/000/175680038041661.png" alt="Zeemo AI" onerror="this.onerror='';this.src='/static/lhimages/moren/morentu.png'" ></a> <div class="aritcle_card_info flexColumn"> <a href="/ai/1023" title="Zeemo AI">Zeemo AI</a> <p>一款专业的视频字幕制作和视频处理工具</p> </div> <a href="/ai/1023" title="Zeemo AI" class="aritcle_card_btn flexRow flexcenter"><b></b><span>下载</span> </a> </div> </div><pre class="brush:php;toolbar:false;">function evalExpr(ast) { if (typeof ast === 'number') return ast; if (ast.type !== 'binary') throw new Error('Invalid AST'); const left = evalExpr(ast.left); const right = evalExpr(ast.right); switch (ast.op) { case '+': return left + right; case '-': return left - right; case '*': return left * right; case '/': return Math.trunc(left / right); // 整除避免浮点 default: throw new Error(`Unknown operator: ${ast.op}`); } }最后,在生成解析器时注入该函数:
const parser = peg.generate(sourceCode, { allowedStartRules: ["Start"], grammarSource: sourceCode, plugins: [], output: "source", }); // 使用时: const result = parser.parse(inputString, { evaluator: evalExpr // 传入上下文 }); console.log(result); // 输出如:"39\n27\n00100111"对于多行输入(如 "3+5\n(2*7)\n16"),建议在调用层按行分割并逐行解析:
input.split('\n').map(line => line.trim()).filter(Boolean) .map(line => parser.parse(line)) .join('\n\n'); // 行间空行分隔总结:PEG.js 的强大之处正在于语义动作的灵活性——它不局限于字符串捕获,而是允许你将任意 JavaScript 逻辑嵌入语法节点。只要确保求值安全、格式控制精确、换行策略清晰,即可优雅实现多进制、多行结构化输出。









