Я пытаюсь создать грамматику для анализа некоторых формул, подобных Excel, которые я разработал, где специальный символ в начале строки обозначает другой источник. Например, $
может означать строку, поэтому " $This is text
" будет рассматриваться как строковый ввод в программе и &
может означать функцию, поэтому &foo()
может рассматриваться как вызов внутренней функции foo
.
Проблема, с которой я сталкиваюсь, заключается в том, как правильно построить грамматику. Например, это упрощенная версия как MWE:
grammar = r'''start: instruction
?instruction: simple
| func
STARTSYMBOL: "!"|"#"|"$"|"&"|"~"
SINGLESTR: (LETTER+|DIGIT+|"_"|" ")*
simple: STARTSYMBOL [SINGLESTR] (WORDSEP SINGLESTR)*
ARGSEP: ",," // argument separator
WORDSEP: "," // word separator
CONDSEP: ";;" // condition separator
STAR: "*"
func: STARTSYMBOL SINGLESTR "(" [simple|func] (ARGSEP simple|func)* ")"
%import common.LETTER
%import common.WORD
%import common.DIGIT
%ignore ARGSEP
%ignore WORDSEP
'''
parser = lark.Lark(grammar, parser='earley')
Таким образом, с этой грамматикой, такие вещи , как: $This is a string
, &foo()
, &foo(#arg1)
, &foo($arg1,,#arg2)
и &foo(!w1,w2,w3,,!w4,w5,w6)
все разобраны , как и ожидалось. Но если я хочу добавить больше гибкости своему simple
терминалу, то мне нужно начать возиться с SINGLESTR
определением токена, что не удобно.
Что я пробовал
Часть, которую я не могу обойти, состоит в том, что если я хочу иметь строку, включающую скобки (которые являются литералами func
), то я не могу обработать их в моей текущей ситуации.
- Если я добавлю круглые скобки
SINGLESTR
, то получуExpected STARTSYMBOL
, потому что он смешивается сfunc
определением и думает, что должен быть передан аргумент функции, что имеет смысл. - Если я переопределю грамматику, чтобы зарезервировать символ амперсанда только для функций и добавить круглые скобки
SINGLESTR
, то я смогу разобрать строку с круглыми скобками, но каждая функция, которую я пытаюсь проанализировать, даетExpected LPAR
.
Мое намерение состоит в том, что все, что начинается с a $
, будет проанализировано как SINGLESTR
токен, и тогда я смогу разобрать такие вещи, как &foo($first arg (has) parentheses,,$second arg)
.
Мое решение, на данный момент, заключается в том, что я использую в своих строках слова escape, такие как LEFTPAR и RIGHTPAR, и я написал вспомогательные функции, чтобы преобразовать их в скобки при обработке дерева. Таким образом, $This is a LEFTPARtestRIGHTPAR
создается правильное дерево, и когда я его обрабатываю, это переводится в This is a (test)
.
Чтобы сформулировать общий вопрос: могу ли я определить мою грамматику таким образом, чтобы некоторые символы, которые являются особыми для грамматики, рассматривались как обычные символы в некоторых ситуациях и как особые в любом другом случае?
РЕДАКТИРОВАТЬ 1
На основании комментария jbndlr
я пересмотрел свою грамматику, чтобы создать отдельные режимы на основе начального символа:
grammar = r'''start: instruction
?instruction: simple
| func
SINGLESTR: (LETTER+|DIGIT+|"_"|" ") (LETTER+|DIGIT+|"_"|" "|"("|")")*
FUNCNAME: (LETTER+) (LETTER+|DIGIT+|"_")* // no parentheses allowed in the func name
DB: "!" SINGLESTR (WORDSEP SINGLESTR)*
TEXT: "$" SINGLESTR
MD: "#" SINGLESTR
simple: TEXT|DB|MD
ARGSEP: ",," // argument separator
WORDSEP: "," // word separator
CONDSEP: ";;" // condition separator
STAR: "*"
func: "&" FUNCNAME "(" [simple|func] (ARGSEP simple|func)* ")"
%import common.LETTER
%import common.WORD
%import common.DIGIT
%ignore ARGSEP
%ignore WORDSEP
'''
Это подпадает (несколько) под мой второй контрольный пример. Я могу анализировать все simple
типы строк (токены TEXT, MD или DB, которые могут содержать скобки) и функции, которые пусты; например, &foo()
или &foo(&bar())
правильно разобрать. В тот момент, когда я помещаю аргумент в функцию (независимо от типа), я получаю UnexpectedEOF Error: Expected ampersand, RPAR or ARGSEP
. В качестве доказательства концепции, если я уберу скобки из определения SINGLESTR в новой грамматике выше, то все будет работать как надо, но я вернусь к исходной точке.
STARTSYMBOL
), и вы добавляете разделители и круглые скобки там, где требуется очистить; Я не вижу здесь никакой двусмысленности. Вам все равно придется разделить свойSTARTSYMBOL
список на отдельные элементы, чтобы их можно было различить.