#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. Удачи с вашим проектом.