ANTLR 如何决定终端是否应该用空格分隔?

How ANTLR decides whether terminals should be separated with whitespaces or not?

我正在 Swift 中为 Swift 编写词法分析器。我使用了ANTLR的语法,但我遇到了一个问题,我不明白ANTLR是如何决定终端是否应该用空格分隔的。

语法如下:https://github.com/antlr/grammars-v4/blob/master/swift/Swift.g4

假设我们在 Swift 中进行了转换。它还可以使用可选类型(Int?、String?)和非可选类型(Int、String)进行操作。以下是有效示例:"as? Int"、"as Int"、"as?Int"。无效示例:"asInt"(不是强制转换)。当语法规则中的终端可以用 0 个或多个 WS(空白)符号分隔时,我已经实现了逻辑。但是按照这个逻辑 "asInt" 匹配一个演员表,因为它包含 "as" 和一个类型 "Int" 并且有 0 个或多个 WS 符号。不过应该是无效的。

Swift 语法包含这些规则:

DOT     : '.' ;
LCURLY  : '{' ;
LPAREN  : '(' ;
LBRACK  : '[' ;
RCURLY  : '}' ;
RPAREN  : ')' ;
RBRACK  : ']' ;
COMMA   : ',' ;
COLON   : ':' ;
SEMI    : ';' ;
LT      : '<' ;
GT      : '>' ;
UNDERSCORE : '_' ;
BANG    : '!' ;
QUESTION: '?' ;
AT      : '@' ;
AND     : '&' ;
SUB     : '-' ;
EQUAL   : '=' ;
OR      : '|' ;
DIV     : '/' ;
ADD     : '+' ;
MUL     : '*' ;
MOD     : '%' ;
CARET   : '^' ;
TILDE   : '~' ;

似乎所有这些终端都可以用0 WS符号与其他终端分开,而其他终端则不能(例如"as" +标识符)。

我说得对吗?如果我是对的,问题就解决了。但是可能会有更复杂的逻辑。

现在如果我有规则

WS : [ \n\r\t\u000B\u000C\u0000]+
a : 'str1' b
b : 'str2' c
c : '+' d
d : 'str3'

我使用它们就像它们是这些规则一样:

WS : [ \n\r\t\u000B\u000C\u0000]+
a : WS? 'str1' WS? 'str2' WS? '+' WS? 'str3' WS?

我想他们应该是这样的(我不知道,这就是问题所在):

WS : [ \n\r\t\u000B\u000C\u0000]+
a: 'str1' WS 'str2' WS? '+' WS? 'str3'

(注意 WS 在 'str1' 和 'str2' 之间不是可选的)

所以有 2 个问题:

  1. 我说得对吗?
  2. 我错过了什么?

谢谢。

这是您 Swift 语法中的 ANTLR WS 规则:

WS : [ \n\r\t\u000B\u000C\u0000]+               -> channel(HIDDEN) ;

-> channel(HIDDEN) 指令告诉词法分析器将这些标记放在单独的通道上,因此解析器根本看不到它们。你不应该用 WS 规则乱扔你的语法 - 它会变得不可读。

ANTLR 分两步工作:你有词法分析器和解析器。词法分析器生成标记,解析器尝试从这些标记和语法中找出具体的语法树。

ANTLR 中的词法分析器是这样工作的:

  • 只要符合任何词法分析器规则,就使用字符。
  • 如果有多个规则与您使用的文本相匹配,请使用出现在语法中的第一个规则
  • 语法中的文字字符串(如 'as')被转换为隐式词法分析器规则(等同于 TOKEN_AS: 'as'; 除了名称只是 'as')。这些最终在词法分析器规则列表中第一

示例 1

让我们看看这些在对 as?Int 进行词法分析时的结果(最后是 space):

  • a... 可能匹配 Identifier'as'
  • as... 可能匹配 Identifier'as'
  • as? 不匹配任何词法分析器规则

因此,您消费as,这将成为代币。现在您必须决定哪种令牌类型。 Identifier'as' 规则都匹配。 'as' 是一个隐含的词法分析器规则,被认为首先出现在语法中,因此它具有优先权。词法分析器发出一个标记,其中文本 as 类型为 'as'.

下一个令牌。

  • ?... 可能匹配 QUESTION 规则
  • ?I 不符合任何规则

因此,您从输入中使用 ? 并发出类型为 QUESTION 且文本为 ?.

的标记

下一个令牌。

  • I...可能匹配 Identifier
  • In...可能匹配 Identifier
  • Int...可能匹配 Identifier
  • Int(后跟一个space)不匹配任何东西

因此,您从输入中使用 Int 并发出类型为 Identifier 且文本为 Int.

的标记

下一个令牌。

  • 那里有一个 space,它符合 WS 规则。

您消费 space,并在 HIDDEN 频道上发出 WS 令牌。解析器不会看到这个。

示例 2

现在让我们看看 asInt 是如何标记化的。

  • a... 可能匹配 Identifier'as'
  • as... 可能匹配 Identifier'as'
  • asI...可能匹配 Identifier
  • asIn...可能匹配 Identifier
  • asInt...可能匹配 Identifier
  • asInt 后跟 space 不匹配任何词法分析器规则。

因此,您从输入流中使用 asInt,并发出带有文本 asInt.

Identifier 标记

解析器

解析器阶段对其获取的标记类型感兴趣。它 关心它们包含的文本。默认通道之外的令牌将被忽略,这意味着以下输入:

  • as?Int - 令牌:'as' QUESTION Identifier
  • as? Int - 代币:'as' QUESTION WS Identifier
  • as ? Int - 代币:'as' WS QUESTION WS Identifier

所有这些都会导致解析器看到以下标记类型:'as' QUESTION Identifier,因为 WS 在单独的通道上。