Я намагаюся створити граматику для розбору деяких формул, схожих на 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
визначенням, і він вважає, що слід передати аргумент функції, що має сенс. - Якщо я перезначую граматику, щоб зарезервувати символ ampersand лише для функцій і додати в дужки
SINGLESTR
, тоді я можу розібрати рядок із дужками, але кожна функція, яку я намагаюся розібрати, даєExpected LPAR
.
Мій намір полягає в тому, щоб все, що починається з $
символу, було би розібране як SINGLESTR
маркер, і тоді я міг би розібрати такі речі &foo($first arg (has) parentheses,,$second arg)
.
Наразі моє рішення полягає в тому, що я використовую в своїх рядках слова "втечі", такі як 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
список на окремі елементи, щоб їх можна було відрізняти.