Estou tentando criar uma gramática para analisar algumas fórmulas semelhantes ao Excel que eu criei, onde um caractere especial no início de uma seqüência de caracteres significa uma fonte diferente. Por exemplo, $
pode significar uma string, portanto " $This is text
" seria tratada como uma entrada de string no programa e &
pode significar uma função, &foo()
podendo ser tratada como uma chamada para a função interna foo
.
O problema que estou enfrentando é como construir a gramática corretamente. Por exemplo, esta é uma versão simplificada como um 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')
Assim, com esta gramática, coisas como: $This is a string
, &foo()
, &foo(#arg1)
, &foo($arg1,,#arg2)
e &foo(!w1,w2,w3,,!w4,w5,w6)
são analisados como esperado. Mas se eu quiser adicionar mais flexibilidade ao meu simple
terminal, preciso começar a mexer na SINGLESTR
definição do token que não é conveniente.
O que eu tentei
A parte que eu não consigo superar é que, se eu quero ter uma string incluindo parênteses (que são literais de func
), não posso lidar com eles na minha situação atual.
- Se eu adicionar os parênteses
SINGLESTR
, entendoExpected STARTSYMBOL
, porque ele está se confundindo com afunc
definição e pensa que um argumento de função deve ser passado, o que faz sentido. - Se eu redefinir a gramática para reservar o símbolo "comercial" apenas para funções e adicionar parênteses
SINGLESTR
, posso analisar uma string com parênteses, mas todas as funções que estou tentando analisar fornecemExpected LPAR
.
Minha intenção é que qualquer coisa que comece com a $
seja analisada como um SINGLESTR
token e então eu poderia analisar coisas como &foo($first arg (has) parentheses,,$second arg)
.
Minha solução, por enquanto, é que estou usando palavras de 'escape' como LEFTPAR e RIGHTPAR em minhas strings e escrevi funções auxiliares para transformá-las em parênteses quando processo a árvore. Então, $This is a LEFTPARtestRIGHTPAR
produz a árvore correta e quando eu a processo, isso é traduzido para This is a (test)
.
Para formular uma pergunta geral: Posso definir minha gramática de forma que alguns caracteres especiais para a gramática sejam tratados como caracteres normais em algumas situações e como especiais em qualquer outro caso?
EDIT 1
Com base em um comentário jbndlr
, revisei minha gramática para criar modos individuais com base no símbolo de início:
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
'''
Isso cai (um pouco) no meu segundo caso de teste. Eu posso analisar todos os simple
tipos de strings (tokens TEXT, MD ou DB que podem conter parênteses) e funções vazias; por exemplo, &foo()
ou &foo(&bar())
analise corretamente. No momento em que coloco um argumento em uma função (não importa qual tipo), recebo um UnexpectedEOF Error: Expected ampersand, RPAR or ARGSEP
. Como prova de conceito, se eu remover os parênteses da definição de SINGLESTR na nova gramática acima, tudo funcionará como deveria, mas estou de volta à estaca zero.
STARTSYMBOL
) e adiciona separadores e parênteses quando necessário; Não vejo nenhuma ambiguidade aqui. Você ainda teria que dividir suaSTARTSYMBOL
lista em itens individuais para ser distinguível.