как использовать завершающий контекст в читаемых шаблонах с помощью Flex?

#flex-lexer

#flex-lexer

Вопрос:

В Flex я могу использовать завершающий шаблон в определении имени, подобном этому:

 NAME  foo$|bar
  

и это проходит flex.

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

 NAME  (?x: foo$ | bar )
  

но теперь это не работает flex, потому что, согласно руководству, "‘$’, cannot be grouped inside parentheses" .

ИМХО, это глупо, разрешать какую-то конструкцию, но не позволять описывать ее читабельно.

Как я могу использовать завершающий контекст с читаемым шаблоном в Flex?

Ответ №1:

Во-первых, чтобы ответить на ваш вопрос: «Как я могу использовать завершающий контекст с читаемым шаблоном в Flex?». Если вы настаиваете на том, что шаблоны доступны для чтения только в том случае, если они заполнены пробелами, тогда ответ будет «Вы не можете».Извините, но так оно и есть. (?x: Флаг был взломан в flex в какой-то момент, и все еще есть много неровностей.

В некотором смысле это не имеет значения, поскольку вы не можете использовать $оператор как часть одной альтернативы в r|s регулярном выражении. Таким образом, даже если бы вы могли использовать «читаемый синтаксис», это не означало бы того, что вы намеревались. Вы, конечно, можете использовать следующий «читаемый синтаксис» (по крайней мере, я думаю, что он читаемый). Это означает что-то другое, но это единственное использование $ оператора, который поддерживает flex:

 NAME (?x: foo | bar )$
  

Ниже приведены несколько примечаний.


В Flex я могу использовать завершающий шаблон в определении имени, подобном этому:

 NAME  foo$|bar
  

Нет, вы не можете. Или, лучше сказать, вы можете написать это, но это не связано с завершающим контекстом, потому что:

… ‘$’, который не встречается в конце правила, теряет свои особые свойства и рассматривается как обычный символ.

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

Это правда (и немного любопытно), что flex отклонит:

 NAME  (?x: foo$ | bar )
  

хотя он будет принимать:

 NAME  (?x: foo$| bar )
  

Я бы рискнул и сказал, что это ошибка. A $распознается как завершающий оператор контекста, только если он находится в конце шаблона. Однако код, который проверяет это, просто проверяет, является ли следующий символ пробелом, потому что шаблоны заканчиваются на первом символе пробела. (Шаблон не анализируется в определении; он анализируется, когда он фактически включен в некоторый шаблон правила.) Тест не проверяет, находится ли $внутри (?x: блока, поэтому в

 (?x: foo$ | bar )
  

это $завершающий оператор контекста, который является синтаксической ошибкой (оператор должен отображаться в самом конце шаблона), в то время как в

 (?x: foo$| bar )
  

это $просто обычный символ, который является законным, но, возможно, неожиданным.


Наконец, небольшое примечание: следующее полностью законно, и $будет рассматриваться как завершающий контекстный оператор, при условии, что определение используется в самом конце шаблона:

 NAME  bar|foo$
  

Однако, вероятно, это также не означает то, что вы думаете. Завершающий оператор контекста имеет более низкий приоритет, чем оператор чередования, поэтому, пока расширение находится в конце шаблона, оно анализируется так, как если бы оно было написано

 NAME  (bar|foo)$
  

Я бы настоятельно рекомендовал не использовать такое определение. (На самом деле, я обычно не рекомендую использовать определения, отчасти из-за всех этих причуд.) Определение, которое заканчивается на a$, вставляется в шаблон ссылки без заключения в круглые скобки (чтобы $его можно было рассматривать как оператор). Это приводит к разного рода неожиданному поведению. Например, если вы пишете:

 NAME  bar|foo$
  

а затем использовать его:

 x{NAME}y       /* Some action */
  

Конечный результат будет таким, как если бы вы написали

 xbar|foo"$"y   /* Some action */
  

(Без круглых скобок, но $это обычный символ.)

С другой стороны, если вы используете его так:

 x{NAME}        /* Some action */
  

Это как если бы вы написали

 xbar|foo$      /* Some action */
  

в котором $является конечным оператором контекста, но из-за низкого приоритета этого оператора он в конечном итоге эквивалентен

 (xbar|foo)$    /* Some action */
  

Маловероятно, что какое-либо из этих расширений было тем, что вы хотели, и еще менее вероятно, что кто-либо, читающий ваш код, будет ожидать этих результатов.

Комментарии:

1. спасибо, это очень информативно. После того, как я написал вопрос, я сам понял, что вы написали, что завершающий контекст нельзя использовать в определениях, только в правилах. Прочитав ваш ответ и проведя дополнительный анализ моей проблемы, я теперь пришел к выводу, что мне лучше не использовать flex, а написать свой собственный лексер и подключить его стандартным интерфейсом к bison.

2. @Mark: вы можете использовать завершающий контекст в определении, если настаиваете. В конце концов, определение — это всего лишь макрос — единственное, что применяет flex к интеллекту, — это (обычно) окружить расширение круглыми скобками, и, как указывает ответ, это часто неправильно. Итак, если вы хотите использовать завершающий контекст в определении, вам нужно использовать определение только в конце правила, и вы должны сами вставить круглые скобки: definition: FOO_OR_BAR_AT_EOL (foo|bar)$ use: //.*{FOO_OR_BAR_AT_EOL} {puts("comment ends with foo");} will делать в значительной степени то, что, похоже, оно будет делать….

3. … но я бы этого не сделал. Мне также не нравится использовать макросы в C, и я использую определения в Flex только тогда, когда есть очевидная выгода. Однако каждому свое. Несмотря на несколько грубых моментов, я лично считаю, что flex значительно экономит время. YMMV. Удачи с вашим проектом.