
本文详解如何通过 peg.js 语法定义与语义动作,将解析结果同时格式化为十进制、十六进制和二进制,并以换行分隔输出,支持多行输入与结构化响应。
本文详解如何通过 peg.js 语法定义与语义动作,将解析结果同时格式化为十进制、十六进制和二进制,并以换行分隔输出,支持多行输入与结构化响应。
PEG.js 是一个功能强大的 JavaScript 解析器生成器,它允许开发者通过简洁的语法定义语言结构,并在匹配成功时执行自定义 JavaScript 代码(即“语义动作”)来构造输出。要实现对算术表达式(如 (3*(5+8)))的一次解析、三重格式化输出(十进制 \n 十六进制 \n 二进制),关键在于:在顶层表达式规则的语义动作中统一计算并拼接多行字符串,而非分散处理各数字字面量。
以下是一个完整、可运行的 PEG.js 语法示例,支持基础四则运算、括号嵌套,并确保最终结果以三行形式输出(十进制、小写十六进制、8位补零二进制):
// PEG.js grammar for multi-format arithmetic output
Start
= _ expr:Expression _ {
const value = expr;
return `${value}\n${value.toString(16)}\n${value.toString(2).padStart(8, '0')}`;
}
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]* ✅ 说明与要点:
- Start 是入口规则,在匹配完整表达式后,获取其数值 value,并一次性生成三行字符串:value(十进制)、value.toString(16)(十六进制,小写)、value.toString(2).padStart(8, '0')(二进制,左补零至8位)。你可根据需要调整位宽(如 padStart(16, '0'))。
- 所有运算符优先级与结合性已通过递归下降结构正确建模(Expression → Term → Factor),确保 3*(5+8) 被正确解析为 39。
- Integer 规则使用 parseInt(..., 10) 显式指定进制,避免隐式转换歧义;_ 规则处理任意空白(含换行符),天然支持多行输入(例如输入 1+2\n*3 或跨行表达式,只要语法合法即可解析)。
⚠️ 注意事项:
- PEG.js 默认不支持直接输出换行符 \n 到浏览器控制台或在线编辑器的“Output”面板——请确保你的运行环境(如自定义 HTML 页面或 Node.js 脚本)正确渲染换行(例如用
包裹输出,或 console.log(result))。
- 若需大数支持(> Number.MAX_SAFE_INTEGER),应引入 BigInt 并统一使用 BigInt() 构造与运算,此时 .toString(2) 等方法仍可用,但需注意所有操作数均为 BigInt 类型。
- 十六进制前缀 0x 和二进制前缀 0b 不属于标准输出要求,示例中未添加;如需,可改为:`${value}\n0x${value.toString(16)}\n0b${value.toString(2).padStart(8, '0')}`。
? 总结: PEG.js 的强大之处正在于将语法解析与语义构造解耦。与其尝试在每个数字节点分别输出多行(导致重复/错乱),不如在语义最清晰的顶层节点(如 Start)集中处理——一次计算、三次格式化、一行拼接。这种模式简洁、可控,且易于扩展(例如增加八进制、ASCII 字符等输出行)。









