
本文详解如何在 peg.js 语法规则中通过语义动作(semantic actions)动态计算并格式化表达式结果,实现单次解析后按行输出十进制、十六进制和二进制三种表示形式,并支持多行输入与对应多行输出。
本文详解如何在 peg.js 语法规则中通过语义动作(semantic actions)动态计算并格式化表达式结果,实现单次解析后按行输出十进制、十六进制和二进制三种表示形式,并支持多行输入与对应多行输出。
PEG.js 允许在语法规则末尾添加 JavaScript 语义动作(即花括号 {...} 中的代码),用于自定义匹配后的返回值。要实现如 (3*(5+8)) → 39\n27\n00100111 的三行输出(十进制、十六进制小写无前缀、二进制八位补零),关键在于:在顶层表达式规则的语义动作中,对计算结果执行三次格式化,并用换行符 \n 连接。
以下是一个完整、可运行的 PEG.js 示例(适配最新 PEG.js v0.10+):
// arithmetic.pegjs
Start
= _ expr:Expression _ { return formatResult(expr); }
Expression
= left:Term op:("+" / "-") right:Expression
{ return op === "+" ? left + right : left - right; }
/ Term
Term
= left:Factor op:("*" / "/") right:Term
{ return op === "*" ? left * right : left / right; }
/ Factor
Factor
= "(" _ expr:Expression _ ")" { return expr; }
/ number:Integer { return number; }
Integer
= digits:[0-9]+ { return parseInt(digits.join(""), 10); }
_ "whitespace"
= [ \t\n\r]*
// 辅助函数:统一格式化数值结果
formatResult = (value) => {
const dec = String(value);
const hex = value.toString(16).toLowerCase(); // 如:39 → "27"
const bin = value.toString(2).padStart(8, "0"); // 补零至8位:39 → "00100111"
return `${dec}\n${hex}\n${bin}`;
};✅ 使用说明:
- 将上述语法粘贴至 PEG.js Online Editor 或本地构建工具中;
- 输入 (3*(5+8)),解析结果将严格输出为:
39 27 00100111
- 支持多行输入(如 15\n(2+3)),只要主规则 Start 匹配完整表达式(或扩展为 Start = __ expr:(Expression / "") __ 并循环处理),即可逐行解析并拼接输出(需在生成器代码中额外处理换行分隔逻辑)。
⚠️ 注意事项:
- parseInt(text(), 10) 在原始答案中存在风险:text() 返回原始字符串(含空格/括号),应避免直接调用;本例采用结构化解析(Integer 规则先提取纯数字再转换),更健壮;
- 二进制补零位数(如 padStart(8, "0"))可根据需求调整(16、32 或动态适配);
- 若需十六进制带 0x 前缀或大写,改用 value.toString(16).toUpperCase().padStart(2, "0");
- PEG.js 不原生支持多入口匹配,如需批量处理多行输入,建议在调用 parser.parse(input) 后,用 input.split('\n').map(line => parser.parse(line.trim())).join('\n') 封装。
掌握语义动作与 JavaScript 类型转换的组合,即可灵活控制 PEG.js 的任意输出形态——从调试信息到机器可读格式,皆在一“return”之间。










