2015-03-28 5 views
0

У меня вопрос об обработке пробелов в грамматике antlr3. Вот урезанная версия грамматики:Как обрабатывать пробелы между токенами в грамматике 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 получает токенизацию как идентификатор, по-видимому, потому что это первое правило; эта строка также будет соответствовать токенам static, int и (идентификатор) foo, но до этого они могут быть поглощены как один большой идентификатор, и это вызывает ошибку, поскольку этот идентификатор не определен. Есть ли способ сделать что-то подобное в моей ситуации? Чтобы неявно требовать пробелы между токенами, создавая версию без пробелов, вызывают альтернативную интерпретацию, которая приводит к ошибке? Я действительно не вижу изящного способа сделать это.

Я читал книги Парра. Языковые шаблоны реализации и окончательный ANTLR Reference, и я более или менее понимаю их, я думаю, но я чувствую, что мне не хватает хорошего обзора того, как на самом деле разрабатывать практическую грамматику для различных конкретных прикладных ситуаций. Какая-то книга, например, «Искусство LL (*)». Есть ли такая книга?

+1

Способ, которым работает ваш C, заключается в том, что лексеры, как правило, едят максимально возможную строку, соответствующую токену. Поэтому 'staticintfoo' - это идентификатор, а не три токена,« статические »,« int »,« foo ». Тогда сообщение об ошибке будет указано, что 'staticintfoo' является необъявленным идентификатором, а не синтаксической ошибкой. –

ответ

1

Нет лучшего способа. Либо вы хотите, чтобы пробелы были удалены или нет. Вы не можете промокнуть и оставаться сухими одновременно.

Если вы действительно хотите принудительно выполнить пробелы между (определенными) токенами, то вам придется принимать WS везде, никоим образом не обойти это. Хотя, я сомневаюсь в ваших намерениях. Обычно он работает очень хорошо, чтобы просто игнорировать пробелы, за исключением некоторых очень странно определенных lanugages, таких как Python или FORTRAN, где отступ является частью языка.

В качестве обходного пути и только в том случае, если у вас есть очень конкретные случаи, которые вы хотите избежать (например, 1000A), вы можете определить правило лексера, которое соответствует именно этому вводу, и дать ему вернуть токен, который нигде не действителен, вызывая синтаксис ошибка в синтаксическом анализаторе.