Estoy tratando de crear una gramática para analizar algunas fórmulas similares a Excel que he ideado, donde un carácter especial al comienzo de una cadena significa una fuente diferente. Por ejemplo, $
puede significar una cadena, por lo que " $This is text
" se trataría como una entrada de cadena en el programa y &
puede significar una función, por lo que &foo()
se puede tratar como una llamada a la función interna foo
.
El problema al que me enfrento es cómo construir la gramática correctamente. Por ejemplo, esta es una versión simplificada como 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')
Así pues, con esta gramática, cosas como: $This is a string
, &foo()
, &foo(#arg1)
, &foo($arg1,,#arg2)
y &foo(!w1,w2,w3,,!w4,w5,w6)
están todos analizados como se esperaba. Pero si quisiera agregar más flexibilidad a mi simple
terminal, entonces necesito comenzar a jugar con la SINGLESTR
definición del token que no es conveniente.
Que he probado
La parte que no puedo superar es que si quiero tener una cadena que incluya paréntesis (que son literales de func
), entonces no puedo manejarlos en mi situación actual.
- Si agrego los paréntesis
SINGLESTR
, obtengoExpected STARTSYMBOL
, porque se está mezclando con lafunc
definición y cree que se debe pasar un argumento de función, lo cual tiene sentido. - Si redefino la gramática para reservar el símbolo de ampersand solo para las funciones y agrego los paréntesis
SINGLESTR
, entonces puedo analizar una cadena con paréntesis, pero cada función que estoy tratando de analizar daExpected LPAR
.
Mi intención es que cualquier cosa que comience con a $
se analice como una SINGLESTR
ficha y luego pueda analizar cosas como &foo($first arg (has) parentheses,,$second arg)
.
Mi solución, por ahora, es que estoy usando palabras de 'escape' como LEFTPAR y RIGHTPAR en mis cadenas y he escrito funciones de ayuda para cambiarlas entre paréntesis cuando proceso el árbol. Entonces, $This is a LEFTPARtestRIGHTPAR
produce el árbol correcto y cuando lo proceso, esto se traduce a This is a (test)
.
Para formular una pregunta general: ¿Puedo definir mi gramática de tal manera que algunos caracteres que son especiales para la gramática sean tratados como caracteres normales en algunas situaciones y como especiales en cualquier otro caso?
EDITAR 1
Basado en un comentario de jbndlr
Revisé mi gramática para crear modos individuales basados en el símbolo de inicio:
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
'''
Esto cae (algo) en mi segundo caso de prueba. Puedo analizar todos los simple
tipos de cadenas (tokens TEXT, MD o DB que pueden contener paréntesis) y funciones que están vacías; por ejemplo, &foo()
o &foo(&bar())
analizar correctamente. En el momento en que pongo un argumento dentro de una función (no importa qué tipo), obtengo un UnexpectedEOF Error: Expected ampersand, RPAR or ARGSEP
. Como prueba de concepto, si elimino los paréntesis de la definición de SINGLESTR en la nueva gramática anterior, entonces todo funciona como debería, pero vuelvo al punto de partida.
STARTSYMBOL
) y agregas separadores y paréntesis cuando es necesario que sean claros; No veo ninguna ambigüedad aquí. Aún tendría que dividir suSTARTSYMBOL
lista en elementos individuales para poder distinguirlos.