如何在 PEG.js 中为算术表达式生成十进制、十六进制与二进制多行输出

2次阅读

如何在 PEG.js 中为算术表达式生成十进制、十六进制与二进制多行输出

本文介绍如何通过自定义 peg.js 语义动作,将解析后的数值结果格式化为十进制、十六进制和二进制三行字符串输出,并支持多行输入与独立结果分隔。

本文介绍如何通过自定义 peg.js 语义动作,将解析后的数值结果格式化为十进制、十六进制和二进制三行字符串输出,并支持多行输入与独立结果分隔。

在 PEG.js 中,语法解析器的输出并非固定为原始匹配文本,而是可通过语义动作(semantic actions)完全自定义返回值。要实现对算术表达式(如 (3*(5+8)))求值后,分别以十进制(39)、十六进制(27)和二进制(00100111)格式输出于独立行,关键在于:

  1. 正确求值表达式(需递归计算 AST 或使用 eval 安全变体);
  2. 在顶层匹配规则中统一格式化并换行拼接;
  3. 处理多行输入时,确保每行独立解析、独立输出。

以下是一个完整可运行的 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"   = [ tnr]*  // 辅助求值函数(注入至 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); // 输出如:"39n27n00100111"

    对于多行输入(如 “3+5n(2*7)n16″),建议在调用层按行分割并逐行解析:

    input.split('n').map(line => line.trim()).filter(Boolean)   .map(line => parser.parse(line))   .join('nn'); // 行间空行分隔

    总结:PEG.js 的强大之处正在于语义动作的灵活性——它不局限于字符串捕获,而是允许你将任意 JavaScript 逻辑嵌入语法节点。只要确保求值安全、格式控制精确、换行策略清晰,即可优雅实现多进制、多行结构化输出。

text=ZqhQzanResources