Compylateur
Posté le 08/05/2020 14:00
Bonjour à tous !
Il y a quelques temps j'ai fait un 'compilateur' qui permet d'exécuter un algorithme en langage naturel en le 'traduisant' en Python. Le code est atroce et repose sur un remplacement entre les commandes en langage naturel et les commandes en Python (à coup de dictionnaires et de tests conditionnels
)… J'aimerais faire de ce projet un 'vrai' compilateur
(on reste sur du Python
). Et j'ai quelques questions :
- La phase d'analyse lexicale repose pour l'instant sur une recherche et un replacement, avec un dictionnaire qui contient en clés les commandes en langage naturel, et en items, les commandes correspondantes en Python… Je me doute que ce n'est pas pertinent…
En fait l'analyse lexicale est mélangée à la phase d'analyse syntaxique.
- Comment faire pour basculer du langage naturel au Python ? Faut-il forcément passer par un hard code, ou est-ce que d'autre technique plus esthétiques existent ?
- L'analyse syntaxique est un bête
replace basé sur un dico… Du coup ça revient à la question précédente : comment éviter le hard code ?
- La phase sémantique… Je ne suis pas sûr d'avoir bien compris toutes les subtilités…
Dans mon cas, après le remplacement bête et méchant, la syntaxe Python n'est pas bonne, du coup je passe à travers différents tests conditionnels pour avoir un 'vrai' script fonctionnel… Encore une fois le hard code à coup de
if me plaît moyen…
- En derniers je refait un passage sur mon code généré et j'ajoute les alinéas. Est-ce que je devrais les gérer plus tôt (je pense à la phase d'analyse syntaxique… mais le placement des alinéas dépend du contexte du code, et sur une ligne donnée je vois pas trop comment faire…
Merci d'avance !
Citer : Posté le 09/06/2020 14:21 | #
Ah mais ça c'est faux !
Citer : Posté le 09/06/2020 14:25 | #
Ah ? Pourtant je fait bien : a / (b/c) ?
Citer : Posté le 09/06/2020 14:31 | #
Ah mais non !
1.0
Citer : Posté le 09/06/2020 14:34 | #
Donc la priorité c'est (a/b) / c
Il faut que je fasse un while j'y retourne ! o/
Ajouté le 09/06/2020 à 14:37 :
Du coup je décompose en multipliant par les inverses : a/b/c devient a * 1/b * 1/c
--- Tokens ---
('VAR', 'a')
('DIVI', '/')
('VAR', 'b')
('DIVI', '/')
('VAR', 'c')
--- AST ---
Operation : *
Variable : a
Operation : 1/
Variable : b
Operation : 1/
Variable : c
Citer : Posté le 09/06/2020 14:40 | #
Pour ton information, si tu as un symbole binaire (disons •) et que tu poses a•b•...•z = ((a•b)•...z), alors le symbole est dit associatif de gauche-à-droite.
À l'inverse, si tu poses que a•b•...•z = a•(b•(...z)), alors le symbole est dit associatif de droite-à-gauche.
Par exemple, l'exposant est associatif de droite-à-gauche, donc a**b**c = a**(b**c). Tous les autres opérateurs binaires usuels sont associatifs de gauche-à-droite, ce qui est le plus courant. Dans un parser comme celui que tu écris, qu'on appelle "à descente récursive", il faut faire des boucles pour manipuler les opérateurs qui sont associatifs de gauche-à-droite.
Ajouté le 09/06/2020 à 14:40 :
Oui parfait ! C'est le bon résultat !
Citer : Posté le 09/06/2020 14:44 | #
Okay ! Merci !!
Je pense m'arrêter là pour aujourd'hui prochaine étapes les comparaisons / conditions ? ou il reste encore de l'arithmétique ?
Citer : Posté le 09/06/2020 15:59 | #
À part le plus unaire qui est anecdotique, il manque surtout les appels de fonctions ! Qui sont un atome, donc sans trop spoiler je te laisse réfléchir à comment les ajouter.
Sinon oui, les conditions, ça se passera quasiment pareil. Je préfère parler d'"expressions booléennes" parce que tu vas voir qu'il y aussi des opérateurs binaires (et/ou), qu'ils ont des associativités (gauche-à-droite), un opérateur unaire (non), des parenthèses...
Citer : Posté le 09/06/2020 16:02 | #
J'ai pas trop compris le coup des appels de fonctions…
Okay pour "expression bouléennes" !
Citer : Posté le 09/06/2020 17:44 | #
Les appels de fonctions c'est les trucs comme f(x) !
Citer : Posté le 09/06/2020 17:50 | #
Ah oui ! Je les avaient oubliées x)
J'ai le droit de passer par une règle en plus ?
function_call -> VAR LPAR atome RPAR
Citer : Posté le 09/06/2020 17:57 | #
Tu as toujours le droit de rajouter des règles dans une grammaire !
Nooon ! Tu es tombé dans le piège facile !
Réfléchis bien à ce que tu as le droit de mettre entre les parenthèses.
Citer : Posté le 09/06/2020 17:59 | #
Ah ? x)
Bon ben faut réfléchir alors ?
Je peut mettre un nombre ou une variable, mais on peut bien imaginer mettre une expression arithmétique… ?
Citer : Posté le 09/06/2020 18:44 | #
Bien sûr qu'on peut imaginer une expression arithmétique ! Pourquoi limiter la forme de ce que tu peux mettre dans l'argument d'une fonction ?
C'est la spécialité du Basic Casio ça, tu as le droit d'écrire List 1 ou List N (avec N=1) mais tu ne peux pas écrire List (N+1). C'est chiant au possible !
Quand tu conçois ta grammaire, demande-toi toujours : quelle est la construction la plus générale que je peux autoriser ici ?
Citer : Posté le 09/06/2020 19:50 | #
Justement ! Ici je pense que la construction la plus générale possible c'est atome
Citer : Posté le 09/06/2020 19:57 | #
Faux ! atome c'est tout petit. Comme je l'ai mentionné plusieurs fois, la construction la plus générale pour les expressions arithmétique c'est somme.
Pendant que j'y suis, tu devrais vraiment avoir un symbole arith_expr -> somme pour éviter de te poser la question à chaque fois et utiliser arith_expr à tous les endroits où tu peux mettre de l'arithmétique.
Citer : Posté le 10/06/2020 13:41 | #
Ah oui ok ! Je comprends
Pour le coup de expr c'est juste une fonction : def expr(self): return self.sum() ?
Citer : Posté le 10/06/2020 13:47 | #
Du coup oui, il suffit de faire ça.
Prends bien le temps de comprendre pourquoi somme est le plus général ici. Regarde comment dériver 1*a depuis atome ou depuis somme, par exemple.
Citer : Posté le 10/06/2020 13:59 | #
somme est plus général parce que j'ai cette hiérarchie : somme -> produit -> exposant -> atome (si j'ai compris ce que je programme ? xD )
Citer : Posté le 10/06/2020 14:05 | #
Voilà c'est ça ! Une somme peut facilement devenir n'importe quel autre symbole arithmétique, donc c'est forcément la plus générale. atome c'est pas aussi général parce que si tu veux écrire 1*a tu es obligé de mettre des parenthèses !
Citer : Posté le 10/06/2020 14:07 | #
Du coup il me reste à implémenter la règle : function -> VAR LPAR expr RPAR !
Citer : Posté le 10/06/2020 14:16 | #
Et... n'oublie pas que les fonctions peuvent avoir plusieurs arguments !