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

使用antlr4-c3实现代码补全:如何获取递归作用域中的函数名?

使用ANTLR4-C3获取嵌套函数名称实现代码补全

需求说明

需要基于ANTLR4-C3实现代码补全,要求获取当前上下文的函数名称列表。例如在表达式foo(bar(...))的省略号位置,希望得到候选列表["foo", "bar"]。

语法定义

grammar Scope;

expression:
      function
    | INT
;


function:
    NAME '(' expression ')'
;

NAME: [a-z]+;
INT:  [0-9]+;
WS:        [ \n\r\t] -> channel(HIDDEN);

尝试代码

const inputStream = CharStreams.fromString("foo(bar(123))");
const lexer = new ScopeLexer(inputStream);
const tokenStream = new CommonTokenStream(lexer);

const parser = new ScopeParser(tokenStream);
const errorListener = new ErrorListener();
parser.addErrorListener(errorListener);
parser.expression();

const core = new c3.CodeCompletionCore(parser);

core.preferredRules = new Set([ScopeParser.RULE_function]);

const candidates = core.collectCandidates(5);

console.log("=================== rules ======================");
for (const [key, value] of candidates.rules) {
    console.log(key, value);
    let functions = candidates.rules.get(ScopeParser.RULE_function)?.ruleList;
    console.log(functions);
}

问题现象

执行代码后仅返回规则相关信息,无法获取函数名称列表:

=================== rules ======================
1 { startTokenIndex: 0, ruleList: [ 0 ] }
[ 0 ]

开启调试信息后,可见内部已生成函数调用栈,但未保留到候选结果中。

解决方案

ANTLR4-C3默认仅收集当前位置可匹配的补全候选(规则/令牌),不会主动提取已解析的历史函数名称。要获取目标列表,需主动遍历解析树收集已匹配的NAME令牌:

  1. 保存解析树
    修改解析代码,保存生成的解析树节点:

    const parseTree = parser.expression(); // 保存解析树
    
  2. 遍历解析树收集函数名称
    编写递归遍历函数,提取所有function节点下的NAME令牌:

    function collectFunctionNames(node) {
      const names = [];
      // 判断当前节点是否为函数节点
      if (node instanceof ScopeParser.FunctionContext) {
        // 提取函数名称令牌
        const nameToken = node.NAME();
        if (nameToken) {
          names.push(nameToken.getText());
        }
        // 递归遍历子表达式中的函数
        const exprNode = node.expression();
        if (exprNode) {
          names.push(...collectFunctionNames(exprNode));
        }
      } else if (node.children) {
        // 遍历所有子节点
        for (const child of node.children) {
          names.push(...collectFunctionNames(child));
        }
      }
      return names;
    }
    
    // 调用函数获取名称列表
    const functionNames = collectFunctionNames(parseTree);
    console.log("函数名称列表:", functionNames); // 输出 ["foo", "bar"]
    
  3. 结合补全场景扩展
    针对JSONPath过滤器的绝对路径需求,可扩展遍历逻辑:维护一个路径栈,进入函数时将名称压入栈,离开时弹出,这样在补全位置就能获取当前的绝对路径名称列表。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.07 11:35:22