À la dure
Vous voulez un analyseur de descente récursif .
Pour obtenir la priorité, vous devez penser de manière récursive, par exemple en utilisant votre exemple de chaîne,
1+11*5
pour ce faire manuellement, vous devrez lire le 1
, puis voir le plus et démarrer une toute nouvelle "session" d'analyse récursive commençant par 11
... et assurez-vous d'analyser le 11 * 5
dans son propre facteur, ce qui donne un arbre d'analyse avec 1 + (11 * 5)
.
Tout cela semble si douloureux même d'essayer d'expliquer, en particulier avec l'impuissance supplémentaire de C.Voyez, après avoir analysé le 11, si le * était en fait un + à la place, vous devriez abandonner la tentative de création d'un terme et analyser à la place le 11
lui-même en tant que facteur. Ma tête explose déjà. C'est possible avec la stratégie décente récursive, mais il y a une meilleure façon ...
La manière la plus simple (bonne)
Si vous utilisez un outil GPL comme Bison, vous n'avez probablement pas à vous soucier des problèmes de licence car le code C généré par bison n'est pas couvert par la GPL (IANAL mais je suis presque sûr que les outils GPL ne forcent pas la GPL sur code généré / binaires; par exemple, Apple compile du code comme, par exemple, Aperture avec GCC et ils le vendent sans avoir à GPL ledit code).
Téléchargez Bison (ou quelque chose d'équivalent, ANTLR, etc.).
Il existe généralement un exemple de code sur lequel vous pouvez simplement exécuter bison et obtenir le code C souhaité qui illustre cette calculatrice à quatre fonctions:
http://www.gnu.org/software/bison/manual/html_node/Infix-Calc.html
Regardez le code généré et voyez que ce n'est pas aussi simple qu'il y paraît. En outre, les avantages d'utiliser un outil comme Bison sont 1) vous apprenez quelque chose (surtout si vous lisez le livre Dragon et apprenez à connaître les grammaires), 2) vous évitez que les NIH tentent de réinventer la roue. Avec un véritable outil de générateur d'analyseurs, vous avez en fait l'espoir de passer à l'échelle plus tard, en montrant aux autres personnes que vous savez que les analyseurs sont le domaine des outils d'analyse.
Mettre à jour:
Les gens ici ont offert de nombreux conseils judicieux. Mon seul avertissement contre le fait de sauter les outils d'analyse ou simplement d'utiliser l'algorithme Shunting Yard ou un analyseur décent récursif roulé à la main est que les petits langages jouets 1 peuvent un jour se transformer en grands langages réels avec des fonctions (sin, cos, log) et des variables, des conditions et pour boucles.
Flex / Bison peut très bien être exagéré pour un petit interpréteur simple, mais un analyseur + évaluateur unique peut causer des problèmes en aval lorsque des modifications doivent être apportées ou des fonctionnalités doivent être ajoutées. Votre situation variera et vous devrez utiliser votre jugement; ne punissez pas les autres pour vos péchés [2] et construisez un outil moins qu'adéquat.
Mon outil d'analyse préféré
Le meilleur outil au monde pour ce travail est la bibliothèque Parsec (pour les parseurs récursifs décents) qui est fournie avec le langage de programmation Haskell. Cela ressemble beaucoup à BNF , ou à un outil spécialisé ou à un langage spécifique à un domaine pour l'analyse (exemple de code [3]), mais il s'agit en fait d'une bibliothèque ordinaire dans Haskell, ce qui signifie qu'il se compile dans la même étape de construction que le reste de votre code Haskell, et vous pouvez écrire du code Haskell arbitraire et l'appeler dans votre analyseur, et vous pouvez mélanger et faire correspondre d'autres bibliothèques dans le même code . (Incorporer un langage d'analyse comme celui-ci dans un langage autre que Haskell entraîne des tas de cruauté syntaxique, au fait. J'ai fait cela en C # et cela fonctionne assez bien mais ce n'est pas si joli et succinct.)
Remarques:
1 Richard Stallman dit, dans Pourquoi vous ne devriez pas utiliser Tcl
La principale leçon d'Emacs est qu'un langage pour extensions ne doit pas être un simple "langage d'extension". Ce devrait être un véritable langage de programmation, conçu pour écrire et maintenir des programmes importants. Parce que les gens voudront faire ça!
[2] Oui, je suis à jamais marqué par l'utilisation de ce «langage».
Notez également que lorsque j'ai soumis cette entrée, l'aperçu était correct, mais l'analyseur moins qu'adéquat de SO a mangé ma balise d'ancrage proche sur le premier paragraphe , prouvant que les analyseurs ne sont pas quelque chose à prendre à la légère, car si vous utilisez des expressions régulières et un piratage, vous obtiendra probablement quelque chose de subtil et petit mal .
[3] Extrait d'un analyseur Haskell utilisant Parsec: une calculatrice à quatre fonctions étendue avec des exposants, des parenthèses, des espaces pour la multiplication et des constantes (comme pi et e).
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