Как работать с пробелами в грамматике? - PullRequest
1 голос
/ 17 мая 2019

Я хочу создать парсер / лексер для упрощенной версии LISP. Вот спецификации бизонов / лексеров:

/* Lexer file */
"(" {return OP;}
")" {return CP;}
[0-9]+ {return NUM;}
["][a-zA-Z]*["] { return STR; }
[ \n\r\f]     { /*do nothing*/}
. {return INVALID_TOKEN;}

/* Bison file */
start_expr: components_list

components_list : /*nothing*/
     | components_list component

 component : OP STR NUM CP

Такая строка соответствует грамматике ("f" 1) ("f"1)( "f" 1)( "f" 1 ). Но выражение ("f"1) выглядит довольно ужасно для меня, я решил добавить явно грамматические разделители к грамматике (использование WHITESPACE токена типа [ \n\r\f]+). Примерно так:

opt_wspace : /*nothing*/
   | WHITESPACE

start_expr: components_list

components_list : /*nothing*/
     | components_list component

 component : OP opt_wspace STR WHITESPACE NUM opt_wspace CP

Но теперь (для меня) грамматика выглядит ужасно, но выражения вида ("f"1) запрещены. Еще один момент: теперь я могу легко ошибиться в грамматике. Например, такие выражения не будут проанализированы ("f" 1) ("f" 1) (я забыл добавить использование opt_wspace в components_list).

Итак, мой основной вопрос - как работать с разделителями / пробелами в грамматике? Я посмотрел грамматику python (https://github.com/python/cpython/blob/master/Grammar/Grammar), но, похоже, в нем нет упоминаний о пробельных выражениях / токенах. Вот небольшая цитата:

stmt: simple_stmt | compound_stmt

simple_stmt: small_stmt (';' small_stmt) * [';'] NEWLINE

small_stmt: (expr_stmt | del_stmt | pass_stmt | flow_stmt | import_stmt | global_stmt | nonlocal_stmt | assert_stmt)

expr_stmt: testlist_star_expr (annassign | augassign (yield_expr | тест-лист) | [('=' (yield_expr | testlist_star_expr)) + [TYPE_COMMENT]])

1 Ответ

1 голос
/ 17 мая 2019

Ни один из известных мне багов (или вообще языков программирования в целом) не заставляет вас ставить пробелы между токенами, как это. Например, такие вещи, как (display"hello") или (format t"~d"42) работают нормально в Scheme и Common Lisp соответственно. Так что то, что вы пытаетесь сделать, обычно не делается, и я бы порекомендовал просто не делать этого.

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

[0-9]+ {return NUM;}
["][^"]*["] { return STR; }
(["][^"]*["]|[0-9]+){2,} { return INVALID_TOKEN; }

Таким образом, INVALID_TOKEN будет генерироваться всякий раз, когда несколько строк или чисел появляются рядом друг с другом без каких-либо промежуточных значений. Шаблон для этого будет становиться все более и более сложным, поскольку вы добавляете больше типов токенов, которые вы не хотите использовать рядом друг с другом (например, идентификаторы).

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

...