如何处理 antlr3 语法中标记之间的空格

How to handle whitespace between tokens in an antlr3 grammar

我对 antlr3 语法中的空格处理有疑问。这是语法的精简版:

grammar SLiMScript;

inputFile   :
        NEWLINE*
        sectionOutput?
        ;

sectionOutput   :   '#OUTPUT' NEWLINE+ outputLine+ ;
outputLine  :   (output_all) NEWLINE+ ;
output_all  :   NUMBER 'A' STRING? ;

NEWLINE :   ('\r'? '\n') ;

NUMBER :        ('0' | (DIGIT_1 DIGIT_0*)) ('.' DIGIT_0*)? EXPONENT? ;
fragment EXPONENT : ('e'|'E') ('+'|'-')? DIGIT_0+ ;
fragment DIGIT_0    :   '0'..'9' ;
fragment DIGIT_1    :   '1'..'9' ;

STRING :    '"' (~('"'|'\n'|'\r'|'\'))* '"' ;

WS  :       ( ' ' | '\t' ) { skip(); } ;

这是一个简单的输入文件:

#OUTPUT 
1000 A "foo bar baz"

一般来说,我希望去掉空格;因此,语法末尾的空格规则。但是,我确实希望令牌之间需要空格。例如,如果您查看输出文件,我不希望 1000A"foo" 合法;我希望令牌之间需要空格。但是,必须在语法中的任何地方明确指定这一点会非常痛苦。我不能既吃蛋糕又吃;如果我保留我的空白剥离规则,那么我不能将我的语句规则更改为:

output_all  :   NUMBER WS 'A' (WS STRING)? ;

因为那时空白标记已经被剥离;没有留给规则匹配的空格。也许我别无选择,只能摆脱隐式的空白剥离,而是在整个语法中的每一对标记之间放置一个 WS 引用,以获得我想要的行为。但肯定有更好的方法...?

像 C 这样的语言是如何做到的?可以写static int foo但不能写staticintfoo;为什么不?此类语言的语法如何在这样的标记之间强制使用空格?我猜这是因为 staticintfoo 被标记为标识符,大概是因为该规则排在第一位;该字符串还将匹配标记 staticint 和(标识符)foo,但在此之前,它们会被吞噬为一个大标识符,这会导致错误因为该标识符未定义。有没有办法在我的情况下做类似的事情?通过使无空格版本导致导致错误的替代解释来隐式要求标记之间的空格?我真的没有看到一个优雅的方式来做到这一点。

我读过 Parr 的书 Language Implementation Patterns 和 The Definitive ANTLR Reference,我认为我或多或少地理解了它们,但我觉得我对如何实际设计一个实用的语法缺乏很好的回顾各种具体的应用场合。像 The Art of LL(*) Grammar Design 这样的书。有这样的书吗?

没有更好的办法。您是否希望删除空格。你不能同时弄湿和保持干爽。

如果你真的想在(某些)标记之间强制使用空格,那么你必须在所有地方都使用 WS,这是没有办法的。不过,我怀疑你的意图。通常,简单地忽略空格就可以很好地工作,除了一些定义非常奇怪的语言,如 Python 或 FORTRAN,其中缩进是语言的一部分。

作为一种解决方法,并且只有当您有非常具体的情况想要避免时(例如 1000A),您才可以定义一个专门匹配此输入的词法分析器规则,并让它 return在任何地方都无效的标记,导致解析器出现语法错误。