当前位置: 首页 > 知识库问答 >
问题:

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

毛景曜
2023-03-14

我在为Swift写词法分析器。我使用了ANTLR的语法,但我面临的问题是,我不明白ANTLR是如何决定终端是否应该用空白分隔的。

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

假设我们选了斯威夫特。它还可以使用可选类型(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”之间不是可选的)

所以有两个问题:

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

多谢了。

共有1个答案

霍鸣
2023-03-14

以下是Swift语法中的ANTLRws规则:

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

->channel(HIDDEN)指令告诉lexer将这些令牌放在一个单独的通道上,因此解析器根本看不到它们。您不应该在语法中乱放ws规则--它会变得不可读。

ANTLR的工作分为两个步骤:您有了lexer和解析器。lexer生成标记,解析器试图从这些标记和语法中找出一个具体的语法树。

ANTLR中的lexer是这样工作的:

  • 只要字符与任何lexer规则匹配,就使用字符。
  • 如果有几个规则与您使用的文本匹配,请使用语法中出现的第一个规则
  • 语法中的
  • 文字字符串(如'as')被转换为隐式lexer规则(相当于token_as:'as';,只是名称为'as')。这些将首先出现在lexer规则列表中。

让我们看看当将写成?int(结尾有空格)时这些后果:

  • A...可能匹配标识符“as”
  • 作为...可能匹配标识符“as”
  • 作为?与任何lexer规则都不匹配

因此,您将消费为,这将成为一个令牌。现在您必须决定哪一种将是令牌类型。标识符“as”规则都匹配。'as'是一个隐式的lexer规则,被认为是语法中首先出现的,因此优先。lexer发出带有as类型的文本as的令牌。

下一个令牌。

  • ...可能匹配问题规则
  • ?I与任何规则都不匹配

因此,您从输入中使用并发出带有文本问题类型的令牌。

下一个令牌。

  • i...可能匹配标识符
  • 中...可能匹配标识符
  • int...可能匹配标识符
  • int(后跟空格)与任何内容不匹配

因此,您从输入中使用int并发出带有文本intidentifier类型的令牌。

下一个令牌。

  • 您有一个空格,它与ws规则相匹配。

您消耗了该空间,并在hidden通道上发出ws令牌。解析器不会看到这一点。

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

  • A...可能匹配标识符“as”
  • 作为...可能匹配标识符“as”
  • asi...可能匹配标识符
  • asin...可能匹配标识符
  • asint...可能匹配标识符
  • asint后跟空格与任何lexer规则不匹配。

因此,您从输入流中使用asint,并发出带有文本asintidentifier令牌。

解析器阶段只对它获得的令牌类型感兴趣。它并不关心它们包含什么文本。默认通道外的令牌将被忽略,这意味着以下输入:

  • as?int-令牌:“as”问题标识符
  • 作为?int-令牌:“as”问题ws标识符
  • 作为?int-令牌:“as”ws问题ws标识符

都将导致解析器看到以下令牌类型:'as'questionidentifier,因为ws在单独的通道上。

 类似资料:
  • 问题内容: 我有的: 我有jQuery AJAX函数,可在查询数据库后返回HTML。根据查询结果,该函数将返回HTML代码,或者根据需要返回任何内容(即空白)。 我需要的: 我需要有条件地检查数据何时为空。 我的代码: 我的问题: 我尝试了多种条件,但都无法正确检查数据。根据我的发现,警告消息为空并不表示数据是 空的 不存在的 等于零 长度为零 空值 未定义 如果这些都不是,那么我该如何有条件地检

  • 问题内容: 使用Java 8和lambda,可以很容易地将集合作为流进行迭代,也很容易使用并行流。docs中的两个示例,第二个示例使用parallelStream: 只要我不关心顺序,使用并行会始终有益吗?有人会认为,更快地将工作划分到更多的内核上。 还有其他考虑事项吗?什么时候应该使用并行流,什么时候应该使用非并行? (问这个问题可以引发关于如何以及何时使用并行流的讨论,不是因为我认为始终使用并

  • 问题内容: 这是我得到的: 但是该代码表示​​无法为该方法找到Symbol。我记得Java拥有这样的比较器…有什么建议吗? 问题答案: 是原始数据类型,因此可以与进行比较。 另外,通过使用双引号可以创建常量(),而使用单引号可以是常量()。

  • 问题内容: 为什么或者为什么不? 问题答案: 对于性能而言,尤其是在较大范围内进行迭代时,通常会更好。但是,在某些情况下,你可能更喜欢: 在Python 3,range()做什么用做的,不存在。如果要编写可在Python 2和Python 3上运行的代码,则不能使用。 在某些情况下实际上可以更快-例如。如果多次重复相同的序列。 xrange()每次都必须重新构造整数对象,但是range()将拥有真

  • 在数千项从字符串转换为int的循环中,在调用Integer之前应该检查字符串是否为空。parseInt或应直接依赖NumberFormatException移动到下一个?