Ich versuche, eine Grammatik zu erstellen, um einige von mir entwickelte Excel-ähnliche Formeln zu analysieren, wobei ein Sonderzeichen am Anfang einer Zeichenfolge eine andere Quelle kennzeichnet. $
Kann beispielsweise eine Zeichenfolge kennzeichnen, sodass " $This is text
" als Zeichenfolgeneingabe im Programm behandelt wird und &
eine Funktion kennzeichnen kann, sodass &foo()
dies als Aufruf der internen Funktion behandelt werden kann foo
.
Das Problem, mit dem ich konfrontiert bin, ist, wie man die Grammatik richtig konstruiert. Beispiel: Dies ist eine vereinfachte Version als 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')
Also, mit dieser Grammatik, Dinge wie: $This is a string
, &foo()
, &foo(#arg1)
, &foo($arg1,,#arg2)
und &foo(!w1,w2,w3,,!w4,w5,w6)
sind alle wie erwartet analysiert. Aber wenn ich meinem simple
Terminal mehr Flexibilität hinzufügen möchte , muss ich anfangen, mit der SINGLESTR
Token-Definition herumzuspielen, was nicht bequem ist.
Was habe ich versucht?
Der Teil, an dem ich nicht vorbeikommen kann, ist, dass ich func
in meiner aktuellen Situation nicht damit umgehen kann , wenn ich eine Zeichenfolge mit Klammern (die Literale sind ) haben möchte .
- Wenn ich die Klammern hinzufüge
SINGLESTR
, bekomme ichExpected STARTSYMBOL
, weil es mit derfunc
Definition verwechselt wird und es denkt, dass ein Funktionsargument übergeben werden sollte, was Sinn macht. - Wenn ich die Grammatik neu definiere, um das kaufmännische Und-Symbol nur für Funktionen zu reservieren und die Klammern hinzuzufügen
SINGLESTR
, kann ich eine Zeichenfolge mit Klammern analysieren, aber jede Funktion, die ich zu analysieren versuche, gibtExpected LPAR
.
Meine Absicht ist, dass alles, was mit a beginnt $
, als SINGLESTR
Token analysiert wird und ich dann Dinge wie analysieren kann &foo($first arg (has) parentheses,,$second arg)
.
Meine Lösung besteht derzeit darin, dass ich in meinen Zeichenfolgen "Escape" -Wörter wie LEFTPAR und RIGHTPAR verwende und Hilfsfunktionen geschrieben habe, um diese bei der Verarbeitung des Baums in Klammern zu setzen. So $This is a LEFTPARtestRIGHTPAR
produziert die richtigen Baum , und wenn ich es verarbeiten, dann wird diese übersetzt This is a (test)
.
Um eine allgemeine Frage zu formulieren: Kann ich meine Grammatik so definieren, dass einige Zeichen, die für die Grammatik spezifisch sind, in bestimmten Situationen als normale Zeichen und in anderen Fällen als speziell behandelt werden?
BEARBEITEN 1
Basierend auf einem Kommentar von habe jbndlr
ich meine Grammatik überarbeitet, um individuelle Modi basierend auf dem Startsymbol zu erstellen:
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
'''
Dies fällt (etwas) unter meinen zweiten Testfall. Ich kann alle simple
Arten von Zeichenfolgen (TEXT-, MD- oder DB-Token, die Klammern enthalten können) und Funktionen analysieren , die leer sind. zum Beispiel &foo()
oder &foo(&bar())
richtig analysieren. In dem Moment, in dem ich ein Argument in eine Funktion einfüge (egal welcher Typ), erhalte ich eine UnexpectedEOF Error: Expected ampersand, RPAR or ARGSEP
. Wenn ich als Proof of Concept die Klammern aus der Definition von SINGLESTR in der neuen Grammatik oben entferne, funktioniert alles so, wie es sollte, aber ich bin wieder auf dem ersten Platz.
quelle
STARTSYMBOL
), und Sie fügen Trennzeichen und Klammern hinzu, wenn dies erforderlich ist, um klar zu sein. Ich sehe hier keine Mehrdeutigkeit. SieSTARTSYMBOL
müssten Ihre Liste immer noch in einzelne Elemente aufteilen , um unterscheidbar zu sein.Antworten:
Ausgabe:
Ich hoffe, es ist das, wonach Sie gesucht haben.
Das waren ein paar verrückte Tage. Ich versuchte es mit Lerche und scheiterte. Ich habe es auch versucht
persimonious
undpyparsing
. Alle diese verschiedenen Parser hatten alle das gleiche Problem mit dem 'Argument'-Token, das die richtige Klammer verbrauchte, die Teil der Funktion war, und scheiterten schließlich, weil die Klammern der Funktion nicht geschlossen wurden.Der Trick bestand darin, herauszufinden, wie Sie eine richtige Klammer definieren, die "nicht besonders" ist. Siehe den regulären Ausdruck für
MIDTEXTRPAR
im obigen Code. Ich habe es als eine rechte Klammer definiert, auf die keine Argumenttrennung oder kein Ende der Zeichenfolge folgt. Ich habe das getan, indem ich die reguläre Ausdruckserweiterung verwendet habe,(?!...)
die nur übereinstimmt, wenn sie nicht gefolgt wird,...
aber keine Zeichen verbraucht. Glücklicherweise erlaubt es sogar das passende Ende der Zeichenfolge innerhalb dieser speziellen Erweiterung für reguläre Ausdrücke.BEARBEITEN:
Die oben erwähnte Methode funktioniert nur, wenn Sie kein Argument haben, das mit a) endet, da der reguläre Ausdruck MIDTEXTRPAR dies dann nicht abfängt und denkt, dass dies das Ende der Funktion ist, obwohl mehr Argumente zu verarbeiten sind. Es kann auch Unklarheiten wie ... asdf) ,, ... geben, es kann ein Ende einer Funktionsdeklaration innerhalb eines Arguments oder ein 'textähnliches') innerhalb eines Arguments sein und die Funktionsdeklaration geht weiter.
Dieses Problem hängt mit der Tatsache zusammen, dass das, was Sie in Ihrer Frage beschreiben, keine kontextfreie Grammatik ( https://en.wikipedia.org/wiki/Context-free_grammar ) ist, für die Parser wie Lerche existieren. Stattdessen handelt es sich um eine kontextsensitive Grammatik ( https://en.wikipedia.org/wiki/Context-sensitive_grammar ).
Der Grund dafür, dass es sich um eine kontextsensitive Grammatik handelt, besteht darin, dass der Parser sich daran erinnern muss, dass er in einer Funktion verschachtelt ist und wie viele Verschachtelungsebenen vorhanden sind, und dass dieser Speicher in irgendeiner Weise in der Syntax der Grammatik verfügbar ist.
EDIT2:
Schauen Sie sich auch den folgenden Parser an, der kontextsensitiv ist und das Problem zu lösen scheint, jedoch eine exponentielle zeitliche Komplexität in der Anzahl der verschachtelten Funktionen aufweist, da er versucht, alle möglichen Funktionsbarrieren zu analysieren, bis eine funktionierende gefunden wird. Ich glaube, es muss eine exponentielle Komplexität haben, da es nicht kontextfrei ist.
quelle
&
.Das Problem ist, dass Funktionsargumente in Klammern stehen, wobei eines der Argumente Klammern enthalten kann.
Eine der möglichen Lösungen besteht darin, die Rücktaste \ vor (oder) zu verwenden, wenn sie Teil von String ist
Ähnliche Lösung, die von C verwendet wird, um doppelte Anführungszeichen (") als Teil der Zeichenfolgenkonstante einzuschließen, wobei die Zeichenfolgenkonstante in doppelte Anführungszeichen eingeschlossen ist.
Ausgabe ist
quelle