如何用JavaCC和JJTree条件创建节点?求更优性能方案
优化JavaCC/JJTree函数节点构建的方案
针对你遇到的问题——既要支持函数名与左括号间的空格、优化语法高亮,又要避免语义前瞻带来的性能损耗,这里提供几个可行的优化方向:
1. 用哈希表+Switch语句替代链式语义前瞻
先预存所有函数名到静态哈希集合中,解析时快速判断标识符是否为函数名,再通过Switch语句直接生成对应节点,避免遍历80条语义前瞻规则:
步骤1:初始化函数名集合
在解析器类的静态代码块中初始化函数名(统一转小写以支持IGNORE_CASE):
private static final Set<String> FUNCTION_NAMES = new HashSet<>(); static { FUNCTION_NAMES.add("upper"); FUNCTION_NAMES.add("lower"); FUNCTION_NAMES.add("capitalize"); FUNCTION_NAMES.add("trim"); FUNCTION_NAMES.add("indexof"); FUNCTION_NAMES.add("lastindexof"); // 添加剩余70+个函数名 }
步骤2:修改语法规则
在JJTree中直接通过语义动作处理节点生成,替代链式LOOKAHEAD:
Node function(): { Token funcToken; Node argsNode; Node funcNode; String funcName; } { LOOKAHEAD(2) funcToken=<ID> <LPAREN> { funcName = funcToken.image.toLowerCase(); if (!FUNCTION_NAMES.contains(funcName)) { throw new ParseException("Unknown function: " + funcToken.image); } argsNode = ArgumentList(); <RPAREN> // 根据函数名生成对应节点 switch(funcName) { case "upper": funcNode = new Upper(argsNode); break; case "lower": funcNode = new Lower(argsNode); break; case "capitalize": funcNode = new Capitalize(argsNode); break; case "trim": funcNode = new Trim(argsNode); break; case "indexof": funcNode = new IndexOf(argsNode); break; case "lastindexof": funcNode = new LastIndexOf(argsNode); break; // 剩余函数的case分支 default: funcNode = null; // 理论上不会走到这里 } } { return funcNode; } }
这种方式的性能接近原Token方案的Switch判断,因为Java对字符串Switch会编译为哈希表查找,避免了线性遍历。
2. 代码生成简化重复工作
由于有近80个函数,手动编写Switch分支或规则会非常繁琐,可以用简单的代码生成脚本(比如Python、Shell):
- 维护一个函数名与节点类名的映射文件(如
func_map.txt) - 编写脚本自动生成静态集合的初始化代码、Switch分支代码,甚至JJTree的规则片段
这样既保证准确性,又减少重复劳动。
3. 兼顾Token层面的优化(可选)
如果希望保留Token层面的部分优势,同时支持空格,可以修改Token管理器的规则,用MORE和语义动作临时缓存标识符,再判断后续是否为左括号:
TOKEN [IGNORE_CASE] : { <LPAREN: "(" > | <RPAREN: ")" > | <ID: <LETTER> ( <LETTER> | <DIGIT> | "." )* > } // 额外添加语义动作,在Token管理器中判断ID后是否紧跟LPAREN(允许空格) // 注意:这种方式需要自定义Token管理器逻辑,复杂度较高,适合对性能要求极高的场景
不过这种方式会增加Token管理器的复杂度,一般推荐第一种方案即可。
内容的提问来源于stack exchange,提问作者opeongo
相关产品推荐
相关产品推荐

