You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

ANTLR4 JavaScript实现:如何像ANTLR Lab输出语法分析树?

在ANTLR4 JavaScript实现中输出类ANTLR Lab的语法分析树

问题背景

已基于ANTLR4完成JavaScript解析器开发,代码可正常运行,但需要在控制台输出类似ANTLR Lab的结构化语法分析树,而非当前的嵌套数组或原始树对象。现有核心代码如下:

解析器初始化代码

if (selected == "Funktionen") {
      console.log("You selected functions")
      const chars = new antlr4.InputStream(data.stringToLex);
      const lexer = new FunktionLexer(chars);
      const tokens = new antlr4.CommonTokenStream(lexer);
      const parser = new FunktionParser(tokens);
      parser.buildParseTrees = true;
      const tree = parser.start();
      tree.accept(new Visitor()); // 现有访问器仅返回嵌套数组,无格式化输出
}

现有访问器实现

class Visitor {
      visitChildren(ctx) {
        if (!ctx) {
          return;
        }
    
        if (ctx.children) {
          return ctx.children.map(child => {
            if (child.children && child.children.length != 0) {
              return child.accept(this);
            } else {
              return child.getText();
            }
          });
        }
      }
    }

解决方案

要实现类ANTLR Lab的树形输出,核心是区分非终端节点(用规则名标识)和终端节点(用文本标识),并通过缩进体现层级结构。以下提供两种实现方式:

方式1:自定义递归打印函数

直接编写递归函数遍历语法树,控制台打印带缩进的结构:

// 递归打印语法树的函数
function printParseTree(node, indentLevel = 0) {
  const indent = '  '.repeat(indentLevel);
  let nodeLabel;

  // 判断节点类型:非终端节点取规则名,终端节点取文本
  if (node.ruleIndex !== undefined) {
    nodeLabel = FunktionParser.ruleNames[node.ruleIndex];
  } else {
    nodeLabel = node.getText();
  }

  console.log(`${indent}${nodeLabel}`);

  // 递归处理子节点
  if (node.children) {
    node.children.forEach(child => {
      printParseTree(child, indentLevel + 1);
    });
  }
}

// 替换原有访问器调用
if (selected == "Funktionen") {
      console.log("You selected functions")
      const chars = new antlr4.InputStream(data.stringToLex);
      const lexer = new FunktionLexer(chars);
      const tokens = new antlr4.CommonTokenStream(lexer);
      const parser = new FunktionParser(tokens);
      parser.buildParseTrees = true;
      const tree = parser.start();
      printParseTree(tree); // 调用打印函数
}

方式2:修改访问器实现打印逻辑

调整现有Visitor,在访问节点时直接打印结构化内容:

class TreePrinterVisitor {
  constructor() {
    this.indentLevel = 0;
  }

  visitChildren(ctx) {
    if (!ctx || !ctx.children) return;

    // 打印当前非终端节点的规则名
    const ruleName = FunktionParser.ruleNames[ctx.ruleIndex];
    console.log(`${'  '.repeat(this.indentLevel)}${ruleName}`);

    this.indentLevel += 1;
    ctx.children.forEach(child => {
      if (child.children && child.children.length > 0) {
        // 子节点为非终端节点,继续访问
        child.accept(this);
      } else {
        // 子节点为终端节点,直接打印文本
        console.log(`${'  '.repeat(this.indentLevel)}${child.getText()}`);
      }
    });
    this.indentLevel -= 1;
  }
}

// 使用修改后的访问器
if (selected == "Funktionen") {
      console.log("You selected functions")
      const chars = new antlr4.InputStream(data.stringToLex);
      const lexer = new FunktionLexer(chars);
      const tokens = new antlr4.CommonTokenStream(lexer);
      const parser = new FunktionParser(tokens);
      parser.buildParseTrees = true;
      const tree = parser.start();
      tree.accept(new TreePrinterVisitor()); // 使用打印访问器
}

说明

两种方式都会在控制台输出层级分明的语法树,非终端节点显示对应的规则名称(如start、expression等),终端节点显示实际输入文本,缩进层级对应树的嵌套深度,效果与ANTLR Lab的树形展示一致。

内容的提问来源于stack exchange,提问作者Ruffyg

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.02 23:03:07