Как ANTLR решает, должны ли терминалы быть разделены пробелами или нет?

Я пишу лексический анализатор в Swift для Swift. Я использовал грамматику ANTLR, но столкнулся с проблемой, заключающейся в том, что я не понимаю, как ANTLR решает, должны ли терминалы быть разделены пробелами.

Вот грамматика: https://github.com/antlr/grammars-v4/blob/master/swift/Swift.g4

Предположим, у нас есть кастинг в Swift. Он также может работать с необязательными типами (Int?, String?) И с необязательными типами (Int, String). Вот допустимые примеры: "как? Int", "как Int", "как?Int". Неверные примеры: "asInt" (это не приведение). Я реализовал логику, когда терминалы в правилах грамматики могут быть разделены 0 или более символами WS (пробелами). Но с этой логикой asInt соответствует приведению, потому что он содержит "as" и тип "Int" и имеет 0 или более символов WS. Но это должно быть недействительным.

Свифт грамматика содержит эти правила:

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, а другие нет (например, "как" + идентификатор).

Я прав? Если я прав, проблема решена. Но может быть более сложная логика.

Теперь, если у меня есть правила

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. Что я пропустил?

Благодарю.

1 ответ

Решение

Вот АНТЛР WS правило в вашей грамматике Swift:

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

-> channel(HIDDEN) инструкция говорит лексеру поместить эти токены в отдельный канал, чтобы анализатор их вообще не видел. Вы не должны засорять свою грамматику WS правила - это стало бы нечитаемым.

ANTLR работает в два этапа: у вас есть лексер и парсер. Лексер создает токены, и синтаксический анализатор пытается определить конкретное синтаксическое дерево из этих токенов и грамматики.

Лексер в ANTLR работает так:

  • Поглощайте персонажей, если они соответствуют любому правилу лексера.
  • Если несколько правил соответствуют тексту, который вы использовали, используйте первое, которое появляется в грамматике
  • Буквальные строки в грамматике (например, 'as') превращаются в неявные правила лексера (эквивалентно TOKEN_AS: 'as'; кроме названия будет просто 'as'). Они оказываются первыми в списке правил лексера.

Пример 1

Давайте посмотрим последствия этого при лексинге as?Int (с пробелом в конце):

  • a... потенциально совпадает Identifier а также 'as'
  • as... потенциально совпадает Identifier а также 'as'
  • as? не соответствует ни одному правилу лексера

Поэтому вы потребляете as, который станет жетоном. Теперь вы должны решить, какой тип токена будет. И то и другое Identifier а также 'as' правила совпадают. 'as' является неявным правилом лексера и считается первым в грамматике, поэтому оно имеет приоритет. Лексер испускает токен с текстом as типа 'as',

Следующий токен.

  • ?... потенциально соответствует QUESTION правило
  • ?I не соответствует ни одному правилу

Поэтому вы потребляете ? из ввода и выдать токен типа QUESTION с текстом ?,

Следующий токен.

  • I... потенциально совпадает Identifier
  • In... потенциально совпадает Identifier
  • Int... потенциально совпадает Identifier
  • Int (сопровождается пробелом) ничего не соответствует

Поэтому вы потребляете Int из ввода и выдать токен типа Identifier с текстом Int,

Следующий токен.

  • У вас там есть место, оно соответствует WS править.

Вы потребляете это пространство и излучаете WS знак на HIDDEN канал. Парсер этого не увидит.

Пример 2

Теперь посмотрим как asInt является токенизированным

  • a... потенциально совпадает Identifier а также 'as'
  • as... потенциально совпадает Identifier а также 'as'
  • asI... потенциально совпадает Identifier
  • asIn... потенциально совпадает Identifier
  • asInt... потенциально совпадает Identifier
  • asInt сопровождаемый пробелом не соответствует ни одному правилу лексера.

Поэтому вы потребляете asInt из входного потока, и испускают Identifier токен с текстом asInt,

Парсер

Этап парсера интересует только типы токенов, которые он получает. Неважно, какой текст они содержат. Токены вне канала по умолчанию игнорируются, что означает следующие входные данные:

  • as?Int - токены: 'as'QUESTIONIdentifier
  • as? Int - токены: 'as'QUESTIONWSIdentifier
  • as ? Int - токены: 'as'WSQUESTIONWSIdentifier

Все это приведет к тому, что парсер увидит следующие типы токенов: 'as'QUESTIONIdentifier, как WS находится на отдельном канале.

Другие вопросы по тегам