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 simple
terminale, allora devo iniziare a giocherellare con la SINGLESTR
definizione 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 lafunc
definizione 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 SINGLESTR
token 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 LEFTPARtestRIGHTPAR
produce 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 jbndlr
ho 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 simple
tipi 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 tuaSTARTSYMBOL
lista in singoli elementi per essere distinguibili.