Sto cercando di creare una grammatica per analizzare alcune formule simili a Excel che ho ideato, in cui un carattere speciale all'inizio di una stringa indica una fonte diversa. Ad esempio, $può indicare una stringa, pertanto " $This is text" verrebbe trattato come un input di stringa nel programma e &può indicare una funzione, quindi &foo()può essere trattato come una chiamata alla funzione interna foo.
Il problema che sto affrontando è come costruire correttamente la grammatica. Ad esempio, questa è una versione semplificata come 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')
Così, con questa grammatica, cose come: $This is a string, &foo(), &foo(#arg1), &foo($arg1,,#arg2)e &foo(!w1,w2,w3,,!w4,w5,w6)sono tutti analizzati come previsto. Ma se vorrei aggiungere maggiore flessibilità al mio simpleterminale, allora devo iniziare a giocherellare con la SINGLESTRdefinizione di token che non è conveniente.
Cosa ho provato
La parte che non posso superare è che se voglio avere una stringa tra parentesi (che sono letterali func), non posso gestirle nella mia situazione attuale.
- Se aggiungo le parentesi
SINGLESTR, ottengoExpected STARTSYMBOL, perché si confonde con lafuncdefinizione e pensa che dovrebbe essere passato un argomento di funzione, il che ha senso. - Se ridefinisco la grammatica per riservare il simbolo e commerciale solo per le funzioni e aggiungo le parentesi
SINGLESTR, allora posso analizzare una stringa con parentesi, ma ogni funzione che sto cercando di analizzare dàExpected LPAR.
Il mio intento è che qualsiasi cosa che inizia con a $verrebbe analizzata come SINGLESTRtoken e quindi potrei analizzare cose del genere &foo($first arg (has) parentheses,,$second arg).
La mia soluzione, per ora, è che sto usando parole "escape" come LEFTPAR e RIGHTPAR nelle mie stringhe e ho scritto funzioni di aiuto per cambiarle tra parentesi quando elaboro l'albero. Quindi, $This is a LEFTPARtestRIGHTPARproduce l'albero corretto e quando lo elaboro, questo viene tradotto in This is a (test).
Per formulare una domanda generale: posso definire la mia grammatica in modo tale che alcuni caratteri speciali della grammatica vengano trattati come caratteri normali in alcune situazioni e speciali in ogni altro caso?
MODIFICA 1
Sulla base di un commento di jbndlrho rivisto la mia grammatica per creare singole modalità basate sul simbolo iniziale:
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
'''
Questo rientra (in qualche modo) nel mio secondo caso di test. Posso analizzare tutti i simpletipi di stringhe (token TEXT, MD o DB che possono contenere parentesi) e funzioni vuote; per esempio, &foo()o &foo(&bar())analizzare correttamente. Nel momento in cui inserisco un argomento in una funzione (non importa quale tipo), ottengo un UnexpectedEOF Error: Expected ampersand, RPAR or ARGSEP. Come prova del concetto, se rimuovo le parentesi dalla definizione di SINGLESTR nella nuova grammatica sopra, allora tutto funziona come dovrebbe, ma sono tornato al punto di partenza.
STARTSYMBOL) e aggiungi separatori e parentesi dove è necessario essere chiari; Non vedo alcuna ambiguità qui. Dovresti comunque dividere la tuaSTARTSYMBOLlista in singoli elementi per essere distinguibili.