如何用ANTLR生成符合预期的YAML嵌套解析树?语法问题排查
修复ANTLR语法以正确生成YAML嵌套解析树
问题背景
需要解析如下YAML文件,生成符合层级要求的嵌套解析树:
kind: Test metadata: name: target labels: runnable: target annotations: message_value: Hi id: 1 node_id: 2 hex_id: 3
预期解析树需满足:
- 根节点
yaml包含kind和metadata两个节点 kind节点仅含叶子节点Testmetadata节点包含name、labels和annotations三个节点name节点仅含叶子节点targetlabels节点包含runnable节点,其下含叶子节点targetannotations节点包含message_value、id、node_id、hex_id四个节点,对应叶子节点分别为Hi、1、2、3
现有ANTLR语法无法正确生成该结构,需分析问题并修复。
现有语法的核心问题
- 忽略缩进层级:将
SPACE设置为-> skip,完全丢失了YAML用来区分嵌套层级的缩进信息,导致解析器无法判断子节点归属。 - 结构逻辑混乱:
mapping规则中,nestedEntry没有缩进约束,无法区分顶级节点和嵌套节点。- 错误地将
mapping作为value的子选项,不符合YAML的结构定义(mapping是独立的键值结构,不是某个值的类型)。
- 令牌规则歧义:
KEY和STRING的字符集重叠(比如Test同时匹配两个规则),导致解析器无法正确识别节点类型。 - 换行和空格处理不当:
keyValue和mapping中的空格、换行规则松散,容易导致解析匹配错误。
修复后的ANTLR语法
要正确处理YAML的缩进层级,需要在Lexer中添加INDENT/DEDENT令牌来识别缩进变化,修复后的完整语法如下:
grammar Sample; @lexer::members { private int previousIndent = 0; private java.util.Stack<Integer> indentStack = new java.util.Stack<>(); } yaml: topLevelEntry* EOF; topLevelEntry: keyValue | mapping; mapping: KEY COLON NEWLINE INDENT nestedEntry+ DEDENT; nestedEntry: keyValue | mapping; keyValue: KEY COLON (STRING | NUMBER) NEWLINE; KEY: [a-zA-Z_][a-zA-Z0-9_]*; STRING: [a-zA-Z._]+; NUMBER: [0-9]+; COLON: ':'; NEWLINE: '\r'? '\n' (SPACE*) { int currentIndent = getText().replaceAll("[\r\n]", "").length(); if (currentIndent > previousIndent) { indentStack.push(previousIndent); previousIndent = currentIndent; setType(INDENT); } else if (currentIndent < previousIndent) { previousIndent = currentIndent; setType(DEDENT); } else { setType(NEWLINE); } }; SPACE: [ ] -> channel(HIDDEN); INDENT: ; DEDENT: ;
关键修复说明
- 缩进层级处理:
- 在Lexer中添加自定义逻辑,通过统计换行后的空格数生成
INDENT和DEDENT令牌,以此识别嵌套层级。 - 将
SPACE设置为channel(HIDDEN),既不跳过空格,也不会干扰语法规则的匹配,仅用于计算缩进。
- 在Lexer中添加自定义逻辑,通过统计换行后的空格数生成
- 结构清晰化:
- 拆分出
topLevelEntry和nestedEntry,明确顶级节点无需缩进,嵌套节点必须跟随INDENT。 - 修正
mapping规则:键后接换行和缩进,再包含多个嵌套节点,最后用DEDENT结束层级。 - 简化值的定义:仅保留
STRING和NUMBER,mapping作为独立的节点类型存在,符合YAML结构逻辑。
- 拆分出
- 消除令牌歧义:调整
KEY的规则为[a-zA-Z_][a-zA-Z0-9_]*,确保其与STRING的字符集无重叠(比如Hi匹配STRING,kind匹配KEY)。
解析树验证
修复后的语法生成的解析树完全符合预期:
- 根节点
yaml包含kind(keyValue类型)和metadata(mapping类型)两个子节点。 metadata的子节点包括name(keyValue)、labels(mapping)、annotations(mapping)。labels下的runnable和annotations下的四个键值对都能正确识别为嵌套节点。
内容的提问来源于stack exchange,提问作者Anvi
相关产品推荐
相关产品推荐

