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 19/06/2020 13:00 | #
Ah ok !
Donc pour les opérateurs logiques j'ai :
and -> not AND not
not -> comp_ega | NOT comp_ega
c'est ça ?
Pour la méthode general_comp j'ai ce code :
elmnt_1 = self.expr()
comp = self.expect("SUP", "SUP_EGA", "INF", "INF_EGA")
elmnt_2 = self.expr()
return Node("Comparison", comp.type, elmnt_1, elmnt_2)
Edit : Finalement je me demande si c'est pas plus pratique d'avoir comme règle :
general_comp -> expr | expr (SUP | SUP_EGA | INF | INF_EGA) expr ? Comme ça, je peux simplifier la règle comp_ega en
comp_ega -> general_comp | general_comp (EGA | DIF) general_comp ?
Citer : Posté le 19/06/2020 13:27 | #
Euh... non tu oublies un truc pour les opérateurs logiques ! Essaie d'écrire x == 2 OR y ≥ 3, tu verras tout de suite le problème.
general_comp -> expr | expr (SUP | SUP_EGA | INF | INF_EGA) expr ? Comme ça, je peux simplifier la règle comp_ega en
comp_ega -> general_comp | general_comp (EGA | DIF) general_comp ?
Tu peux mais oublies pas qu'il y a des questions de typage, tu peux pas écrire x+1 == (y ≤ 3).
Citer : Posté le 19/06/2020 13:35 | #
Pour les opérateurs logiques, j'y reviendrais
Ajouté le 19/06/2020 à 13:37 :
Du coup je vais rester sur la première règle, mais j'ai du mal à voir comment intégrer les trois règles pour comp_ega ?
Citer : Posté le 19/06/2020 13:39 | #
Ah effectivement ma version n'est pas prête pour passer à un parser LL. Ta version est bien, tu devrais l'utiliser : il faudra juste envisager de typer après l'analyse syntaxique ou à l'exécution.
Citer : Posté le 19/06/2020 13:57 | #
C'est quoi 'typer' ?
Ajouté le 19/06/2020 à 13:59 :
Sinon j'ai ce code :
elmnt_1 = self.general_comp()
if self.token_ahead.type not in ("EGA", "DIF"): return elmnt_1
comp = self.expect()
elmnt_2 = self.general_comp()
return Node("Comparison", comp_type, elmnt_1, elmnt_2)
def general_comp(self):
elmnt_1 = self.expr()
if self.token_ahead.type not in ("SUP", "SUP_EGA", "INF", "INF_EGA"): return elmnt_1
comp = self.expect()
elmnt_2 = self.expr()
return Node("Comparison", comp.type, elmnt_1, elmnt_2)
comparison est comp_ega
Citer : Posté le 19/06/2020 14:14 | #
Typer c'est attribuer des types aux expressions... parce que tu peux pas comparer un entier et un booléen pour l'égalité même si tu peux tout à fait écrire n == b.
Citer : Posté le 19/06/2020 14:18 | #
Ah ok
J'ai fait quelques tests sur les comparaisons, ça marche bien je pense attaquer les conditions…
Du coup il faut que je gère l'absence d'opérateur logique sur les and et not… ? Ce qui ferait une grammaire comme ça :
and -> not | not AND not
not -> comparison | NOT comparison
Autant or et and en pseudo-code je vois comment l'écrire, autant not… ?
Ajouté le 19/06/2020 à 14:28 :
Du coup j'ai ces méthodes :
elmnt_1 = self.cond_and()
if self.token_ahead.type != "OR": return elmnt_1
self.expect()
elmnt_2 = self.cond_and()
return Node("Condition", "OR", elmnt_1, elmnt_2)
def cond_and(self):
elmnt_1 = self.cond_not()
if self.token_ahead.type != "AND": return elmnt_1
self.expect()
elmnt_2 = self.cond_not()
return Node("Condition", "AND", elmnt_1, elmnt_2)
def cond_not(self):
if self.token_ahead.type != "NOT": return self.comparison()
self.expect()
elmnt_1 = self.comparison()
return Node("Conditon", "NOT", elmnt_1)
Citer : Posté le 19/06/2020 14:46 | #
Voilà c'est ça ! Une expression n'est pas obligée de contenir un opérateur de chaque niveau de priorité, il faut pouvoir sauter des étages. Tu devrais aussi avoir or -> and | and OR and et ensuite bool_expr -> or exactement comme pour somme et expr.
Le code a l'air bien
Citer : Posté le 19/06/2020 14:51 | #
Okay, merci !
J'ai fait or -> and | and OR and. J'ai des résultats qui ont l'air pas mal :
--- Tokens ---
('VAR', 'a')
('EGA', 'est égal à')
('VAR', 'b')
('AND', 'et que')
('VAR', 'a')
('EGA', 'est égal à')
('VAR', 'c')
--- AST ---
Condition : AND
Comparison : EGA
Variable : a
Variable : b
Comparison : EGA
Variable : a
Variable : c
Citer : Posté le 19/06/2020 15:21 | #
Sympa !
Citer : Posté le 19/06/2020 15:23 | #
Il me reste un problème à la con : je ne détecte pas le not dans le lexer parce que je ne sais pas comment il est noté en pesudo-code xD
Ajouté le 19/06/2020 à 17:27 :
À la limite est-ce qu'on peut ne pas mettre le not ?
Ajouté le 19/06/2020 à 17:42 :
J'ai une question : le symbole = est utilisé actuellement pour les comparaisons d'égalité. Est-ce que c'est envisageable d'avoir un token EQUAL qui pourrait servir aux assignements et aux comparaisons ? (la différence étant faite au niveau de l'analyse sémantique… ? )
Citer : Posté le 19/06/2020 21:33 | #
Ta grammaire ne sera pas forcément ambiguë même si tu utilise = pour les deux usages, il te suffit d'interdire les expressions volantes (le genre qui va dans Ans en Basic Casio).
Citer : Posté le 21/06/2020 08:11 | #
Pour moi la différence entre "=" pour l'assignement et "=" pour la comparaison devrait se résoudre dans l'AST. Le parseur sort un noeud "equal" et l'AST détermine que si ce noeud constitue une expression à part entière (en vérifiant le parent du noeud) alors c'est un assignement, sinon c'est une comparaison.
Ecrivez vos programmes basic sur PC avec BIDE
Citer : Posté le 21/06/2020 17:35 | #
Hmm... je veux pas dire "non", mais personne ne fait ce genre de trucs, c'est du hack. Une grammaire bien écrite permet de lever l'ambiguité tout de suite. Si tu attends d'avoir ton AST pour lever l'ambiguité tu seras encore obligé de restructurer l'AST pour créer un noeud d'assignement après, autrement dit tu continues de parser après avoir finir de parser.
Citer : Posté le 22/06/2020 09:10 | #
Je suis pas sûr d'avoir compris ce que c'est… ?
Au niveau du lexer, je ne sais pas comment gérer les tokens comme Affecter à var la valeur 15 la variable est au milieu… je pensais renvoyer une liste de tokens comme ça :
('ASSI', 'Affecter à # la valeur')
('NUM', 15)
mais je ne vois pas comment le coder… ? x)
Et sinon au niveau de la grammaire, c'est quoi la prochaine étape ?
Citer : Posté le 22/06/2020 15:08 | #
Ah, les expressions volantes c'est... par exemple ça, en C :
{
int x = 2;
/* Expression volante, ne sert à rien */
x+2;
return 0;
}
Si tu autorises ce genre de trucs alors il y a une ambiguité quand tu écris x=4 entre assignment et expression booléenne volante. Si tu les interdis alors y'a pas de problème et c'est pas ambigu.
En toute rigueur tu devrais avoir un regex :
de laquelle tu émets soit deux tokens comme tu avais imaginé, soit un seul token ASSIGN(<la variable>). C'est là que ton lexer montre un peu ses limites.
Au niveau de la grammaire, la prochaine étape c'est des statements... et des tests unitaires !
Citer : Posté le 22/06/2020 15:33 | #
Ah ok… Je pense que je commence à comprendre… x) Mais comment je les autorise ou pas… ?
Je ne sais comment faire des regex… ? C'est possible de refaire le lexer ?
Pour les statement, je vais y aller doucement, c'est du chinois pour l'instant… xD
Citer : Posté le 22/06/2020 15:35 | #
Tu les autorises si dans ta grammaire il y a une règle qui permet de construire un statement à partir d'une expressions toute seule... et tu les interdis s'il n'y en a pas ! C'est aussi simple que ça.
C'est possible de refaire le lexer, après ça dépend si tu veux absolument le faire toi-même ou si tu es prêt à le générer. Tu peux faire un lexer à regex potable à la main (assez technique) mais ce sera jamais aussi propre et performant que le super-automate que te donnent les générateurs.
Les statements tu sais c'est les assignments, les if/else, les boucles, les return, tout ça.
Citer : Posté le 22/06/2020 15:40 | #
Ben refaire le lexer m'enchante pas… mais si je le repense pas comment je peux gérer les affectations Affecter à … la valeur ?
Ah ok je savais que ça s'appelle comme ça !
Citer : Posté le 22/06/2020 15:43 | #
Bah le plus simple c'est de faire un token pour "Affecter à" et un pour "la valeur" et ensuite tu te fais une règle de grammaire :
Citer : Posté le 22/06/2020 16:08 | #
C'pas bête !
Ajouté le 24/06/2020 à 13:48 :
Du coup tu me conseille de commencer par quel statement ?