新手稿标记语言Lexer开发求助:ANTLR换行匹配异常
解决ANTLR INSIDE_BRACES模式中换行识别的异常问题
问题背景
我正在开发一款手稿专用标记语言,支持无引号自由文本和指令元数据,示例语法如下:
// Origins section demonstrating advanced directives section origins chapter { :title "Origins of Liberal Thought" :publish true Mill’s influence cannot be overstated. See @Person.bio for his role in shaping modern liberal thought. Refer to [[2025-09-23/1a]] and [@section:book.lsd:prologue]. :sidebar { John Stuart Mill is sometimes called the most influential English‑speaking philosopher of the 19th century. :notes { comment: "This sidebar illustrates the sidebar directive" } } // 更多指令... }
使用ANTLR设置了三种Lexer模式:Default、INSIDE_BRACES、COLON_MODE,其中INSIDE_BRACES模式出现报错:
line 8:28 extraneous input '\n' expecting {':', RBRACE, NL_INSIDE}
推测是从COLON_MODE退出后,换行无法被正确识别为NL_INSIDE规则,且Lexer规则和Parser规则存在逻辑漏洞。
问题分析
- Lexer规则缺陷:
- 原FREETEXT规则
~[ \t\r\n}:] ~[\r\n}]*排除了冒号,导致自由文本中的冒号会被误识别为指令触发符,同时限制了自由文本的合法内容。 - 规则顺序不合理,EMPTY_LINE在NL_INSIDE之后,可能导致行首空白加换行被NL_INSIDE优先匹配,无法被跳过。
- 原FREETEXT规则
- Parser规则缺失:
- 未在section内容的语法规则中允许NL_INSIDE token,导致换行被视为非法输入。
解决方案
1. 修正INSIDE_BRACES模式的Lexer规则
调整规则顺序和FREETEXT定义,确保自由文本支持冒号、空白行被正确跳过、行首冒号才触发指令:
mode INSIDE_BRACES; // 行首右大括号:退出当前模式 RBRACE: {getCharPositionInLine() == 0}? '}' -> popMode; // 空白行(行首仅含空白+换行):跳过 EMPTY_LINE: {getCharPositionInLine() == 0}? [ \t]* [\r\n] -> skip; // 行首空白:跳过 LEADING_WS: {getCharPositionInLine() == 0}? [ \t]+ -> skip; // 行首冒号:进入COLON_MODE COLON_DIRECTIVE: ':' -> pushMode(COLON_MODE), type(COLON); // 自由文本:允许除换行、右大括号外的所有字符(包括冒号) FREETEXT: ~[\r\n}]+; // 保留换行符 NL_INSIDE: '\r'? '\n';
2. 调整Parser规则,允许换行存在
在section内容的语法规则中加入NL_INSIDE,让Parser接受换行作为合法分隔符:
section : 'section' ID ID '{' sectionContent '}' ; // 允许指令、自由文本、换行任意组合 sectionContent : (directive | freetext | NL_INSIDE)* ; directive : COLON ID directiveValue ; // 支持指令的多种值类型(根据需求扩展) directiveValue : STRING | BOOLEAN | '{' directiveContent '}' | arrayValue ; // 指令内部内容同样允许换行 directiveContent : (directive | freetext | NL_INSIDE)* ; arrayValue : '[' (STRING | ID) (',' (STRING | ID))* ']' ; freetext : FREETEXT ; // 基础词法定义 STRING: '"' ~["]* '"'; BOOLEAN: 'true' | 'false'; ID: [a-zA-Z_][a-zA-Z0-9_]*;
3. 确认COLON_MODE的换行退出逻辑
保持COLON_MODE的换行规则不变,确保退出后回到INSIDE_BRACES模式:
mode COLON_MODE; // 其他指令内容规则... NL_COLON: '\r'? '\n' -> popMode;
效果说明
- 自由文本可正常包含冒号、空格等字符,无需引号包裹。
- 空白行被自动跳过,行首冒号正确触发指令模式。
- 换行符被Parser合法识别,不再报多余输入错误。
内容的提问来源于stack exchange,提问作者billmill
相关产品推荐
相关产品推荐

