Yecc语法解析问题:将ChordPro文件转换为结构化数据
ChordPro转结构化数据的yecc解析问题解决方法
问题背景
用leex和yecc实现ChordPro文件转结构化数据,lexer生成的Token序列符合预期,但yecc解析器输出未达到目标格式,无法生成包含标题、创作者、和弦及歌词行的结构化Map,核心问题是对yecc的Token读取逻辑及语法规则推导机制理解不足。
示例ChordPro输入
@songexample """ {title:Take It Easy} {st:Eagles} Well I'm a [G]runnin' down the road try'n to loosen my load I've got seven women [D]on my [C]mind """
当前yecc输出
{:ok, {:song, {:properties, [ title: ~c"Take It Easy", st: ~c"Eagles", text: ~c"Well I'm a ", chord: ~c"[G]", text: ~c"runnin' down the road try'n to loosen my load", text: ~c"I've got seven women ", chord: ~c"[D]", text: ~c"on my ", chord: ~c"[C]", text: ~c"mind" ]}}}
期望输出格式
%{ title: "Take it Easy", by: "Eagles", chords: ["G", "D", "C"], lines: [ %{ line: [ %{ linetype: "lyric", content: "Well I'm a" }, %{ linetype: "chord", content: "G" }, %{ linetype: "lyric", content: "runnin' down the road try'n to loosen my load" } ] }, %{ line: [ %{ linetype: "lyric", content: "I've got seven women" }, %{ linetype: "chord", content: "D" }, %{ linetype: "lyric", content: "on my" }, %{ linetype: "chord", content: "C" }, %{ linetype: "lyric", content: "mind" } ] } ] }
核心解决思路
1. 重构语法规则层级,区分元数据与歌词行
当前输出把所有内容混在properties里,说明语法规则没有分层。要明确划分结构:
- 顶层规则:
song -> metadata_section lyric_lines metadata_section专门处理{key:value}格式的元数据指令,比如将{title:xxx}映射到title字段,{st:xxx}映射到by字段lyric_lines处理多行歌词,每行对应一个line结构,包含交替的歌词和和弦片段
2. 优化Token处理细节
- 和弦Token:在lexer或语法规则中移除
[],直接提取内部和弦名(比如将[G]转为"G"),避免后续处理冗余符号 - 文本与换行:lexer要识别换行符为
LINE_ENDToken,用来分割歌词行,防止多行文本被合并成一个文本Token
3. 在语法动作中直接构建目标Map
- 处理元数据时,将键值对转换为Map的键值(如
st转by) - 处理每一行歌词时,收集交替的
lyric和chord片段,构建成%{line: [...]},再将所有行存入lines数组 - 同时收集所有和弦到
chords数组中(可按需去重或按顺序存储)
4. 理解yecc的推导逻辑
- yecc是LALR(1)解析器,规则要从高层到低层定义,先定义整体结构,再拆分细节
- 规则优先级:元数据指令要优先于歌词行处理,避免把指令误判为文本
- 结果聚合:在规则的动作部分(
{:ok, ...})要正确收集子规则的输出,比如把多行歌词用列表拼接,把元数据合并到同一个Map里
内容的提问来源于stack exchange,提问作者Martin Sarosi
相关产品推荐
相关产品推荐

