Cow 1.0 for delphi7 : un générateur de code pour grammaires formelles intégré à l'ide

Description

Avant toute chose: ce code est destiné aux utilisateurs de Delphi7 (désolé pour les autres). J'ai essayé de le rendre compatible avec d'autres versions, mais ça me parait vraiment trop compliqué et fastidieux, donc pour l'instant le projet restera dans cet état-là.

Pour installer la bête: installer (dans l'ordre)
- OTAExpress_D7.dpk : un package qui introduit plein de composants utiles pour intégrer des gadgets dans l'IDE de Delphi7
- PreProcessors_D7.dpk : un package qui permet de définir de nouveaux pré-processeurs de code-source. Voir plus bas.
- COW_D7.dpk : le package qui génère les grammaires formelles.
- SmartRes_D7.dpk : un package pour inclure facilement des ensembles massifs de fichiers dans les resources d'un exécutable. Ce dernier n'est pas tout à fait fini, mais il a été nécessaire à la génération des autres packages.

La philosophie de ce que j'appelle les pré-processeurs est qu'on définit des fichiers pré-pascal (extension *.ppas). Lors de la compilation dans l'IDE, les fichiers *.ppas sont lus par les différents pré-processeurs installés dans Delphi et servent à générer de "vrais" fichiers Pascal *.pas de même nom. Voir par exemple mon source:
http://www.delphifr.com/code.aspx?ID=38929
Le fichier Pascal.ppas contient la directive suivante:
{#COW -L "Pascal.lexer" -STREAMDATA}
Le pré-processeur COW, en la lisant, détermine qu'il doit aller chercher dans le fichier Pascal.lexer la définition formelle d'un lexer, et inclure le code-source d'une classe qui implémente ce lexer dans le fichier Pascal.pas.

Lorsqu'un fichier *.ppas (ou un fichier *.pas associé à un fichier *.ppas) est ouvert dans Delphi, un petit onglet apparait en bas de la fenêtre d'édition qui permet de passer de l'une à l'autre des 2 versions (voir la capture). Lorsqu'un fichier est "pré-processé", des commentaires éventuels et/ou des erreurs de compilation sont affichées en bas, comme lorsqu'on compile un projet Delphi! ;-)

De plus, les fichiers *.lexer (lexers formels) *.parser (parsers formels) et *.smartres (définition des resources) ont leur propre mise en valeur de la syntaxe, là encore intégrée à l'éditeur de Delphi! (voir capture) ;-O

Il y a aussi un menu "Pre-processors" rajoutté dans l'IDE en haut, qui permet de naviguer dans les fichiers ouverts, les fermer, en rajoutter au projet etc... (voir capture)

Il y a aussi un "designer de code" qui permet de gérer facilement les directives de pré-compilation des pré-compilateurs sans avoir à tapper unes seule ligne de code. (voir capture) Il suffit d'appuyer sur F12 pour se faire afficher ce designer, comme pour les composants

Il y a aussi des fichiers d'aide intégrés (appuyer sur F1 depuis un fichier *.ppas) mais pour l'instant ils ne sont pas encore tout à fait complets. :-(

Il y a pas mal d'options configurables, par exemple on peut faire que les pré-processeurs se déclenchent automatiquement à chaque compilation, ou au contraire manuellement. On peut choisir où le code généré est placé (début/fin de la partie implémentation, etc...). On peut aussi choisir d'utiliser ou non la mise en cache des données générées dans des fichiers *.pcu (l'équivalent des *.dcu de Delphi) pour que ça aille plus vite pour compiler lorsque le source n'a pas été modifié...

Quelque mots au sujet de COW:
D'abord, les grammaires reconnues sont LR(1). J'ai voulu faire un parser qui soit le plus "net" et "idéal" possible. Par là, j'entends que la syntaxe est faite de façon à être la plus lisible possible. De plus, contrairement au code habituellement généré par les générateurs de Parser (yacc ou autres, c'est à dire des algorithmes paresseux), l'arbre syntaxique est calculé EN ENTIER avant l'évaluation. C'est à dire que de cette façon, il est possible de la parcourir plusieurs fois et dans n'importe quel ordre, sans que cela ne pose de problèmes de performance ou autre.

Merci de vous reporter à l'aide intégrée dans les packages pour plus d'information (ne pas essayer de lire les fichiers *.html directement, les liens ont été changés pour fonctionner avec le protocole res://)

Source / Exemple :


La majeure partie de mon code est auto-générée. C'est à dire que c'est COW qui a engendré le code-source de COW... Je l'ai écrit moi-même de A à Z, sans passer par un autre générateur de parser, ce qui explique qu'il ne ressemble pas aux autres par sa syntaxe et la façon de construire l'arbre syntaxique.

La table de l'automate généré est raisonablement optimisée avant la génération du code avec les techniques usuelles, même si elle peut parfois être vraiment énorme. Cette taille est toutefois nécessaire pour une rapidité maximale.

De base, sans l'utilisation de la directive -STREAMDATA, le code généré par COW ne nécessite que l'unité COW_RunTime. Avec cette directive (nécessaire quand le code commence à faire plus de 50000 lignes!) il faudra en plus rajoutter les unités Classes et COW_Utils.

Je n'ai pas encore implémenté les règles de priorité dans les conflits, mais en théorie on peut s'en passer en rajouttant des noeuds à la grammaire.

Le token "Error" est supporté

Conclusion :


IMPORTANT: il est tout à fait déconseillé d'essayer d'appliquer les pré-processeurs à leur propre code ;-)
En effet, si on compile un pré-processeur alors qu'il est installé, il risque d'être actif au moment où Delphi décharge/recharge le module en mémoire, et ça fait planter l'IDE.

Codes Sources

A voir également

Vous n'êtes pas encore membre ?

inscrivez-vous, c'est gratuit et ça prend moins d'une minute !

Les membres obtiennent plus de réponses que les utilisateurs anonymes.

Le fait d'être membre vous permet d'avoir un suivi détaillé de vos demandes et codes sources.

Le fait d'être membre vous permet d'avoir des options supplémentaires.