如何在ANTLR4中实现远距离令牌的FIFO式关联?
用ANTLR4实现FIFO顺序的元素配对解析
语法文件设计
首先编写PairGrammar.g4语法,定义输入的结构:左边是逗号分隔的数字列表,中间用冒号分隔,右边是逗号分隔的字母列表:
grammar PairGrammar; // 顶层规则:匹配完整的配对结构 pairList : numberList ':' letterList EOF ; // 数字列表规则 numberList : NUMBER (',' NUMBER)* ; // 字母列表规则 letterList : LETTER (',' LETTER)* ; // 词法规则 NUMBER : [0-9]+; // 匹配阿拉伯数字 LETTER : [A-Z]+; // 匹配大写字母 WS : [ \t\r\n]+ -> skip; // 忽略空白字符
Java端的配对逻辑实现
ANTLR的pushmode是FILO机制,适合嵌套结构的上下文切换,并不适配这种线性的FIFO配对场景。我们可以通过**解析树监听器(ParseTreeListener)**收集两边的元素,再按顺序关联:
1. 定义配对监听器
实现PairGrammarListener,在遍历语法树时收集数字和字母,完成FIFO配对:
import org.antlr.v4.runtime.tree.ParseTreeListener; import org.antlr.v4.runtime.tree.TerminalNode; import java.util.ArrayList; import java.util.List; public class PairListener implements PairGrammarListener { private final List<String> numbers = new ArrayList<>(); private final List<String> letters = new ArrayList<>(); private final List<Pair> pairs = new ArrayList<>(); // 进入数字列表节点时,收集所有数字 @Override public void enterNumberList(PairGrammarParser.NumberListContext ctx) { for (TerminalNode numNode : ctx.NUMBER()) { numbers.add(numNode.getText()); } } // 进入字母列表节点时,收集字母并完成配对 @Override public void enterLetterList(PairGrammarParser.LetterListContext ctx) { for (TerminalNode letterNode : ctx.LETTER()) { letters.add(letterNode.getText()); } // 按索引一一配对(FIFO顺序),取两边长度的最小值避免越界 int matchCount = Math.min(numbers.size(), letters.size()); for (int i = 0; i < matchCount; i++) { pairs.add(new Pair(numbers.get(i), letters.get(i))); } } // 获取最终的配对结果 public List<Pair> getPairs() { return pairs; } // 定义配对实体类,用于后续创建业务对象 public static class Pair { private final String number; private final String letter; public Pair(String number, String letter) { this.number = number; this.letter = letter; } @Override public String toString() { return number + "与" + letter + "关联"; } // 可添加getter方法供业务逻辑调用 public String getNumber() { return number; } public String getLetter() { return letter; } } }
2. 主程序解析与测试
编写主类来解析输入文本,触发监听器处理逻辑:
import org.antlr.v4.runtime.CharStream; import org.antlr.v4.runtime.CharStreams; import org.antlr.v4.runtime.CommonTokenStream; import org.antlr.v4.runtime.tree.ParseTree; import org.antlr.v4.runtime.tree.ParseTreeWalker; public class PairParserDemo { public static void main(String[] args) { String input = "1,2,3:A,B,C"; // 1. 构建词法分析器 CharStream charStream = CharStreams.fromString(input); PairGrammarLexer lexer = new PairGrammarLexer(charStream); // 2. 构建令牌流 CommonTokenStream tokens = new CommonTokenStream(lexer); // 3. 构建语法分析器 PairGrammarParser parser = new PairGrammarParser(tokens); // 4. 解析生成语法树 ParseTree parseTree = parser.pairList(); // 5. 遍历语法树,触发监听器逻辑 ParseTreeWalker walker = new ParseTreeWalker(); PairListener listener = new PairListener(); walker.walk(listener, parseTree); // 6. 输出配对结果,或用于创建业务对象 for (PairListener.Pair pair : listener.getPairs()) { System.out.println(pair); // 示例:用配对结果创建业务对象 // BusinessObject obj = new BusinessObject(pair.getNumber(), pair.getLetter()); } } }
运行结果
执行主程序后,会输出:
1与A关联 2与B关联 3与C关联
核心思路说明
- 无需使用
pushmode:模式栈适用于XML标签嵌套这类需要上下文切换的场景,而这里是线性的前后列表关联,通过收集元素后按索引配对即可实现FIFO逻辑。 - 监听器机制优势:ANTLR的ParseTreeListener允许我们在遍历语法树的特定节点时执行自定义逻辑,完美适配“先收集左边元素,再处理右边元素并关联”的需求。
内容的提问来源于stack exchange,提问作者JavaStudent
相关产品推荐
相关产品推荐

