I. Introduction

Le framework .NET expose des classes permettant d'accéder au compilateur C#. Au cours de cet article, nous verrons comment utiliser ces classes afin de créer notre propre compilateur C#.

II. Interface graphique

Nous allons commencer par réaliser l'interface graphique de notre application en WPF. Ouvrez Visual Studio et sélectionnez Fichier - Nouveau projet, puis choisissez Application WPF et appelez le « Compilateur » :

Image non disponible

Nous allons ensuite ajouter une TextBox dans notre fenêtre principale. Celle-ci nous servira à écrire le code qui devra être compilé. Puis nous ajouterons un TabControl contenant deux TabItem. Ces deux TabItem serviront à afficher le résultat de la compilation et la liste des erreurs relatives au code. Nous ajouterons également un Button afin de générer le code et l'exécuter si la génération s'est déroulée avec succès. Votre fenêtre devrait normalement (ou à peu de choses près) ressembler à ceci :

Image non disponible

A présent, nous pouvons passer au chapitre suivant ;)

III. Implémentation du code

III-A. Présentation de la classe CSharpCodeProvider

Comme je vous le disais en introduction, le framework .NET contient des classes permettant d'accéder aux instances du générateur de code et donc de pouvoir créer son propre compilateur. Dans le cadre de cet article, nous allons nous intéresser de plus près à la classe CSharpCodeProvider.

Cette classe est une classe dérivée de la classe de base CodeDomProvider et fournit l'accès au compilateur de code C#.

L'équivalent existe pour les langages VB et JScript.

III-B. Compilation du code

En premier lieu, nous allons ajouter au code behind, les instructions using suivantes :

 
Sélectionnez
  1. using System.CodeDom.Compiler; 
  2. using Microsoft.CSharp; 

Nous allons ensuite créer l'événement Click du Button Build en double-cliquant sur celui-ci depuis le fichier .xaml puis implémenter sa logique. Tout d'abord, nous allons effacer le contenu des TextBox des onglets :

 
Sélectionnez
  1. // Cleaning txtItemOutput and txtItemErrors 
  2. txtItemOutput.Text = ""; 
  3. txtItemErrors.Text = ""; 

On va ensuite créer une instance de CodeDomProvider pour le langage C# :

 
Sélectionnez
  1. // Create instance of CodeDomProvider 
  2. CodeDomProvider provider = CSharpCodeProvider.CreateProvider("CSharp"); 

La prochaine étape sera de préciser que nous souhaitons générer un fichier exécutable et spécifier son nom. Pour y parvenir, nous devons utiliser la classe CompilerParameters représentant la liste des paramètres d'un compilateur :

 
Sélectionnez
  1. // Name of .exe file 
  2. string Output = "MyProgram.exe"; 
  3.  
  4. CompilerParameters parameters = new CompilerParameters(); 
  5.  
  6. // Specify we want an .exe file instead of a .dll 
  7. parameters.GenerateExecutable = true; 
  8. // Name of output assembly 
  9. parameters.OutputAssembly = Output; 

On passe maintenant à la partie « Compilation » en utilisant la méthode CompileAssemblyFromSource de l'objet provider. Cette méthode permet de compiler un assembly et renvoie un objet de type CompilerResults contenant le résultat de la compilation. Elle prend deux objets en paramètres. Tout d'abord un objet de type CompileParameters que nous avons créé précédemment puis un tableau de string contenant les sources à compiler.

 
Sélectionnez
  1. CompilerResults results = provider.CompileAssemblyFromSource(parameters, txtCodeSource.Text); 

La dernière étape consiste à vérifier si l'objet results contient des erreurs de compilation et le cas échéant, à l'indiquer dans l'onglet Output et afficher le détail dans l'onglet Errors :

 
Sélectionnez
  1. if (results.Errors.Count > 0) 
  2. { 
  3.     txtItemOutput.Text = results.Errors.Count + " error(s). See Errors Menu."; 
  4.  
  5.     foreach (CompilerError CompErr in results.Errors) 
  6.     { 
  7.         txtItemErrors.Text = txtItemErrors.Text + 
  8.         "Line number " + CompErr.Line + 
  9.         ", Error Number: " + CompErr.ErrorNumber + 
  10.         ", '" + CompErr.ErrorText + ";" + 
  11.         Environment.NewLine + Environment.NewLine; 
  12.     } 
  13. } 

III-C. Exécution du code

Afin de pouvoir exécuter votre code, une nouvelle instruction using doit être ajouté à votre fichier .xaml :

 
Sélectionnez
  1. using System.Diagnostics; 

Cette instruction nous permet d'accéder à la classe Process, Nous allons ajouter une condition else gérant le cas où la compilation s'est déroulée sans erreurs :

 
Sélectionnez
  1. else 
  2. { 
  3.     //Successful Compile 
  4.     txtItemOutput.Text = "Compile successful!"; 
  5.     Process.Start(Output); 
  6. } 

C'est aussi simple que ça :)

Voici le programme complet :

 
Sélectionnez
  1. private void Build_Click(object sender, RoutedEventArgs e) 
  2. { 
  3.     // Cleaning txtItemOutput and txtItemErrors 
  4.     txtItemOutput.Text = ""; 
  5.     txtItemErrors.Text = ""; 
  6.  
  7.     // Create instance of CodeDomProvider 
  8.     CodeDomProvider provider = CSharpCodeProvider.CreateProvider("CSharp"); 
  9.  
  10.     // Name of .exe file 
  11.     string Output = "MyProgram.exe"; 
  12.  
  13.     CompilerParameters parameters = new CompilerParameters(); 
  14.  
  15.     // Specify we want an .exe file instead of a .dll 
  16.     parameters.GenerateExecutable = true; 
  17.  
  18.     // Name of output assembly 
  19.     parameters.OutputAssembly = Output; 
  20.  
  21.     CompilerResults results = provider.CompileAssemblyFromSource(parameters, txtCodeSource.Text); 
  22.  
  23.     if (results.Errors.Count > 0) 
  24.     { 
  25.         txtItemOutput.Text = results.Errors.Count + " error(s). See Errors Menu."; 
  26.  
  27.         foreach (CompilerError CompErr in results.Errors) 
  28.         { 
  29.             txtItemErrors.Text = txtItemErrors.Text + 
  30.             "Line number " + CompErr.Line + 
  31.             ", Error Number: " + CompErr.ErrorNumber + 
  32.             ", '" + CompErr.ErrorText + ";" + 
  33.  
  34.             Environment.NewLine + Environment.NewLine; 
  35.         } 
  36.     } 
  37.     else 
  38.     { 
  39.         //Successful Compile 
  40.         txtItemOutput.Text = "Compile successful!"; 
  41.         Process.Start(Output); 
  42.     } 
  43. } 

Allez ! On se fait le classique Hello World ? :)

Exécutez votre programme et copier coller le code suivant dans la TextBox source :

 
Sélectionnez
  1. using System; 
  2.  
  3.  
  4.  
  5. namespace HelloWorld 
  6. { 
  7.     class HelloWorld 
  8.     { 
  9.         static void Main(string[] args) 
  10.         { 
  11.             Console.WriteLine("Hello World!"); 
  12.             Console.ReadLine(); 
  13.         } 
  14.     } 
  15. } 

Normalement, vous devriez obtenir ceci :

Image non disponible
Image non disponible

La compilation et l'exécution se sont bien déroulées et en vérifiant dans le dossier bin/Debug de votre projet, un fichier .exe a bien été généré. Vous pouvez également modifier le code source de façon à générer une erreur et voir comment se comporte le programme.

IV. Conclusion

Dans cet article, nous avons vu qu'il n'était pas bien compliqué de réaliser un compilateur C#. Il ne vous reste plus qu'à l'améliorer à votre sauce en ajoutant d'autres fonctionnalités telles que la coloration syntaxique, la gestion des tabulations, etc..

V. Remerciements

Je tiens à remercier xxx et xxx pour la relecture attentive de cet article ainsi que les corrections apportées.