Der harte Weg
Sie möchten einen Parser für rekursiven Abstieg .
Um Vorrang zu haben, müssen Sie rekursiv denken, z. B. mithilfe Ihrer Beispielzeichenfolge.
1+11*5
Um dies manuell zu tun, müssten Sie das lesen 1
, dann das Plus sehen und eine ganz neue rekursive Analyse "Sitzung" starten, beginnend mit 11
... und sicherstellen, dass Sie das 11 * 5
in seinen eigenen Faktor analysieren, um einen Analysebaum mit zu erhalten 1 + (11 * 5)
.
Dies alles fühlt sich so schmerzhaft an, selbst zu versuchen, es zu erklären, insbesondere mit der zusätzlichen Ohnmacht von C. Sehen Sie, nach dem Parsen der 11, wenn das * tatsächlich ein + wäre, müssten Sie den Versuch, einen Begriff zu bilden, abbrechen und stattdessen das analysieren 11
selbst als Faktor. Mein Kopf explodiert bereits. Es ist mit der rekursiven anständigen Strategie möglich, aber es gibt einen besseren Weg ...
Der einfache (richtige) Weg
Wenn Sie ein GPL-Tool wie Bison verwenden, müssen Sie sich wahrscheinlich keine Gedanken über Lizenzprobleme machen, da der von Bison generierte C-Code nicht von der GPL abgedeckt wird (IANAL, aber ich bin mir ziemlich sicher, dass GPL-Tools die GPL nicht erzwingen generierter Code / Binärdateien (zum Beispiel Apple kompiliert Code wie beispielsweise Aperture mit GCC und sie verkaufen ihn, ohne den Code GPL zu müssen).
Laden Sie Bison herunter (oder etwas Ähnliches, ANTLR usw.).
Normalerweise gibt es einen Beispielcode, mit dem Sie Bison einfach ausführen und den gewünschten C-Code erhalten können, der diesen Rechner mit vier Funktionen demonstriert:
http://www.gnu.org/software/bison/manual/html_node/Infix-Calc.html
Schauen Sie sich den generierten Code an und stellen Sie fest, dass dies nicht so einfach ist, wie es sich anhört. Die Vorteile eines Tools wie Bison sind außerdem: 1) Sie lernen etwas (insbesondere, wenn Sie das Drachenbuch lesen und etwas über Grammatiken lernen). 2) Sie vermeiden, dass NIH versucht, das Rad neu zu erfinden. Mit einem echten Parser-Generator-Tool können Sie später skalieren und anderen Personen zeigen, dass Parser die Domäne von Parsing-Tools sind.
Aktualisieren:
Die Leute hier haben viele gute Ratschläge gegeben. Meine einzige Warnung vor dem Überspringen der Parsing-Tools oder der Verwendung des Shunting Yard-Algorithmus oder eines handgerollten rekursiven anständigen Parsers ist, dass kleine Spielzeugsprachen 1 eines Tages zu großen tatsächlichen Sprachen mit Funktionen (sin, cos, log) und Variablen, Bedingungen und für werden können Schleifen.
Flex / Bison kann für einen kleinen, einfachen Interpreter durchaus übertrieben sein, aber ein einmaliger Parser + Evaluator kann später zu Problemen führen, wenn Änderungen vorgenommen oder Funktionen hinzugefügt werden müssen. Ihre Situation wird unterschiedlich sein und Sie müssen Ihr Urteilsvermögen einsetzen. Bestrafe einfach keine anderen Menschen für deine Sünden [2] und baue ein weniger als angemessenes Werkzeug.
Mein Lieblingswerkzeug zum Parsen
Das beste Werkzeug der Welt für diesen Job ist die Parsec- Bibliothek (für rekursive anständige Parser), die mit der Programmiersprache Haskell geliefert wird. Es ähnelt stark BNF oder einem speziellen Tool oder einer domänenspezifischen Sprache zum Parsen (Beispielcode [3]), ist jedoch nur eine reguläre Bibliothek in Haskell, was bedeutet, dass es im selben Erstellungsschritt wie die anderen kompiliert wird Sie können beliebigen Haskell-Code schreiben und diesen in Ihrem Parser aufrufen, und Sie können andere Bibliotheken im selben Code mischen und abgleichen . (Das Einbetten einer Parsing-Sprache wie dieser in eine andere Sprache als Haskell führt übrigens zu einer Menge syntaktischer Cruft. Ich habe dies in C # gemacht und es funktioniert ganz gut, aber es ist nicht so hübsch und prägnant.)
Anmerkungen:
1 Richard Stallman sagt in Warum Sie Tcl nicht verwenden sollten
Die wichtigste Lehre von Emacs ist, dass eine Sprache für Erweiterungen keine bloße "Erweiterungssprache" sein sollte. Es sollte eine echte Programmiersprache sein, die zum Schreiben und Verwalten umfangreicher Programme entwickelt wurde. Weil die Leute das wollen werden!
[2] Ja, ich habe für immer Angst davor, diese "Sprache" zu benutzen.
Beachten Sie auch , dass , wenn ich diesen Eintrag vorgelegt, die Vorschau war richtig, aber SO ist weniger als ausreichend Parser meiner Nähe Anker - Tag auf dem ersten Absatz aß , was beweist , dass Parser sind nicht etwas zu spaßen , denn wenn Sie reguläre Ausdrücke verwenden und eine off - Hacks Sie wird wahrscheinlich etwas subtiles und kleines falsch machen .
[3] Ausschnitt eines Haskell-Parsers mit Parsec: Ein Vierfunktionsrechner, der um Exponenten, Klammern, Leerzeichen für die Multiplikation und Konstanten (wie pi und e) erweitert wurde.
aexpr = expr `chainl1` toOp
expr = optChainl1 term addop (toScalar 0)
term = factor `chainl1` mulop
factor = sexpr `chainr1` powop
sexpr = parens aexpr
<|> scalar
<|> ident
powop = sym "^" >>= return . (B Pow)
<|> sym "^-" >>= return . (\x y -> B Pow x (B Sub (toScalar 0) y))
toOp = sym "->" >>= return . (B To)
mulop = sym "*" >>= return . (B Mul)
<|> sym "/" >>= return . (B Div)
<|> sym "%" >>= return . (B Mod)
<|> return . (B Mul)
addop = sym "+" >>= return . (B Add)
<|> sym "-" >>= return . (B Sub)
scalar = number >>= return . toScalar
ident = literal >>= return . Lit
parens p = do
lparen
result <- p
rparen
return result