Я намагаюся створити граматику для розбору деяких формул, схожих на 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список на окремі елементи, щоб їх можна було відрізняти.