Ich habe eine Anwendung, die es Benutzern ermöglichen soll, ähnliche Ausdrücke wie Excel zu schreiben:
(H1 + (D1 / C3)) * I8
und komplexere Dinge wie
If (H1 = 'Wahr', D3 * .2, D3 * .5)
Ich kann nur so viel mit regulären Ausdrücken machen. Irgendwelche Vorschläge bezüglich der richtigen Herangehensweise und all der Ressourcen, von denen ich lernen kann, wären sehr willkommen.
Danke!
Einige andere Fragen, Sie finden Hinweise in:
Viel Glück!
Angesichts einer ähnlichen Situation - der Notwendigkeit, mit kurzen Ein-Zeilen-Ausdrücken umzugehen - schrieb ich einen Parser. Die Ausdrücke waren boolesche Logik der Form
%Vor%und so weiter. Im Englischen könnte man sagen, dass Atome durch AND und OR verbunden sind, und jedes Atom hat drei Elemente - ein linksseitiges Attribut, einen Operator und einen Wert. Weil es so sucint war, glaube ich, dass das Parsen einfacher war. Die Menge der möglichen Attribute ist bekannt und begrenzt (zB: Name, Größe, Zeit). Die Operatoren variieren je nach Attribut: verschiedene Attribute nehmen unterschiedliche Gruppen von Operatoren an. Und der Bereich und das Format der möglichen Werte variieren ebenfalls je nach Attribut.
Um zu analysieren, teile ich die Zeichenfolge in Whitespace mit String.Split (). Später erkannte ich, dass ich vor Split () die Eingabe-Zeichenfolge normalisieren musste - Leerzeichen vor und nach Parens einfügen. Ich habe das mit einem regex.Replace () getan.
Die Ausgabe der Teilung ist ein Array von Token. Dann erfolgt das Parsen in einer großen for-Schleife mit einem Schalter auf dem Attributwert auf der linken Seite. Mit jeder Runde der Schleife wurde ich in eine Gruppe von Spielmarken schlürfen lassen. Wenn das erste Token ein open-paren war, dann war die Gruppe nur ein Token lang: der paren selbst. Für Token, die bekannte Namen waren - meine Attributwerte -, musste der Parser in einer Gruppe von 3 Token schlürfen, jeweils eine für den Namen, den Operator und den Wert. Wenn zu irgendeinem Zeitpunkt nicht genügend Token vorhanden sind, löst der Parser eine Ausnahme aus. Basierend auf dem Tokenstrom würde sich der Parserstatus ändern. Eine Konjunktion (UND, ODER, XOR) bedeutete, das vorherige Atom auf einen Stapel zu schieben, und wenn das nächste Atom fertig war, würde ich das vorherige Atom aufknallen und diese beiden Atome zu einem zusammengesetzten Atom verbinden. Und so weiter. Die Zustandsverwaltung erfolgte am Ende jeder Schleife des Parsers.
%Vor%Das ist vereinfacht, nur ein bisschen. Aber die Idee ist, dass jede Fallaussage ziemlich einfach ist. Es ist einfach, eine atomare Einheit des Ausdrucks zu analysieren. Der schwierige Teil bestand darin, sie alle angemessen zusammenzufügen.
Dieser Trick wurde im Abschnitt "Housekeeping" am Ende jeder Slurp-Schleife unter Verwendung des Statusstacks und des Atomstacks durchgeführt. Je nach Parser-Status können verschiedene Dinge passieren. Wie gesagt, in jeder case-Anweisung könnte sich der Parser-Status ändern, wobei der vorherige Zustand auf einen Stapel geschoben wird. Wenn dann am Ende der switch-Anweisung der Zustand besagt, dass ich gerade mit der Analyse eines Atoms fertig war und eine ausstehende Konjunktion vorlag, würde ich das gerade analysierte Atom in den CompoundAtom verschieben. Der Code sieht so aus:
%Vor%Das eine andere bisschen Magie war das EnumUtil.Parse. Dadurch kann ich Dinge wie "& lt;" in einen enum Wert. Angenommen, Sie definieren Ihre Enums wie folgt:
%Vor%Normalerweise sucht Enum.Parse nach dem symbolischen Namen des Aufzählungswerts und & lt; ist kein gültiger symbolischer Name. EnumUtil.Parse () sucht nach dem Ding in der Beschreibung. Der Code sieht so aus:
%Vor%Ich habe das EnumUtil.Parse () Ding von woanders bekommen. Vielleicht hier?
Ein kleiner rekursiver Parser ist dafür perfekt. Sie müssen wahrscheinlich nicht einmal einen Syntaxbaum erstellen - Sie können die Auswertung während der Analyse durchführen.
%Vor%Wenn Sie nun einfach ParseTop aufrufen, berechnet es den Wert eines Ausdrucks für Sie.
Der Grund für das PARSE_HIGHER-Makro besteht darin, das Hinzufügen von Operatoren auf mittleren Prioritätsstufen zu erleichtern.
Die if-Anweisung ist etwas komplizierter. Jede Parse-Routine benötigt ein zusätzliches "enable" -Argument und führt daher keine Berechnung durch, es sei denn, sie ist aktiviert. Dann parsen Sie das Wort "if", parsen den Testausdruck und parsen dann die beiden Ergebnisausdrücke, wobei der inaktive deaktiviert ist.
Ich habe ein Gegenbeispiel dafür, wie nicht dafür geeignet ist: Will o 'the Wisp (da dies mein eigener Code ist, fühle ich mich sicher, ihn zu kritisieren).
Was ist gut über den Code?
Was ist schlecht über den Code?
Besuche ANTLR . Sie definieren eine Sprachsyntax, testen sie mit einem GUI-Tool und generieren Quellcode in verschiedenen Sprachen. Open Source.
Ich würde das Buch Kleine Sprachen empfehlen. Es führt Sie durch viele Compiler-Grundlagen, die zum ordnungsgemäßen Ausführen dieser Aufgabe benötigt werden.
Sie haben die Tatsache hervorgehoben, dass reguläre Ausdrücke nur dann funktionieren, wenn Sie strenge Beschränkungen für Ihre Sprache haben. Wie andere bereits gesagt haben, wird ein Recursive Descent Parser den Zweck erfüllen.
Die nächste Wahl wäre, ob man einen Parser Generator wie ANTLR , oder um einen von Grund auf neu zu schreiben.
Ich empfehle CoreCalc / FunCalc zu arbeiten: Ссылка
Ich habe ihre Grammatik für COCO \ R Parser Generator in der Produktion verwendet und es funktioniert sehr schnell.
Alles, was Sie tun müssen, ist: 1. Holen Sie sich Excel-Grammatik von Corecalc 2. Führen Sie coco.exe darauf aus (generiert Parser für Excel-ähnliche Ausdrücke) 3. Übersetzen Sie den Ausdrucksbaum in die umgekehrte polnische Schreibweise 4. einfaches calc
Tags und Links .net language-design