Aller au contenu

Un interpréteur LISP


(gile)

Messages recommandés

Salut,

 

En me penchant de plus près sur l'utilisation des expressions régulières (ou rationnelles), j'ai pensé qu'il pourrait être amusant de les utiliser dans l'élaboration d'un petit programme qui pourrait évaluer une chaîne de caractères représentant une expression LISP.

 

J'ai fait un premier prototype en F#, visible et testable sur Try F#. Ce prototype ne fonctionne qu'avec les opérateurs +, -, * et / pour des nombres entiers (la rigueur de F# concernant le typage rend complexe l'utilisation de différents types de nombres.

Try F# permet d'écrire et d'évaluer du code F# "à la volée". Il suffit de sélectionner le code et de clique sur "run" pour voir le résultat de l'évaluation dans la fenêtre de sortie (Output window).

 

J'ai ensuite voulu traduire ça dans un langage moins confidentiel (C#) et un peu plus souple pour gérer les nombres entiers et réels à la manière du LISP.

L'interface est sommaire (Console) mais ce n'était pas l'objectif.

L'intérêt à l'usage est plus que limité, néanmoins je trouve ça intéressant d'un point de vue didactique.

 

using System;
using System.Linq;
using System.Text.RegularExpressions;

namespace ConsoleLISP
{
   class Program
   {
       // // @"(?>\((?<depth>)\s*[\+\-\*/]\s+((\-)?\d+\s*)+\)(?<-depth>))(?(depth)(?!))"
       // retourne l'appel de fonction le plus imbriqué
       static Regex regex = new Regex(@"
           (?>             # début de la recherche non rétroactive
           \(              # parenthèse ouvrante
           (?<depth>)      # incrémente le nombre de parenthèses
           \s*             # zéro ou plusieurs espaces
           [\+\-\*/]       # l'opérateur : caractère '+', '-', '*' ou '/'
           (\s+            # un ou plusieurs espaces
           (\-)?\d+        # suivi de un ou plusieurs chiffres éventuellement précédé du signe '-'
           (.\d*)?         # éventuellement suivi d'un point et zéro ou plusieurs chiffres
           )+              # le tout une ou plusieurs fois
           \s*             # zéro ou plusieurs espaces
           \)              # parenthèse fermante
           (?<-depth>)     # décrémente le nombre de parenthèses
           )               # fin de la recherche
           (?(depth)(?!))  # vérifie l’appariement des parenthèses
           ", RegexOptions.IgnorePatternWhitespace);

       static string Apply(string function, string[] args)
       {
           if (args.Any(s => s.Contains('.')))
           {
               var values = args.Select(s => double.Parse(s));
               switch (function)
               {
                   case "+": return values.Aggregate((a, B) => a + B).ToString();
                   case "-": return (values.Count() == 1 ? -values.First() : values.Aggregate((a, B) => a - B)).ToString();
                   case "*": return values.Aggregate((a, B) => a * B).ToString();
                   case "/": return values.Aggregate((a, B) => a / B).ToString();
                   default: throw new Exception("Opérateur non valide.");
               }
           }
           else
           {
               var values = args.Select(s => int.Parse(s));
               switch (function)
               {
                   case "+": return values.Aggregate((a, B) => a + B).ToString();
                   case "-": return (values.Count() == 1 ? -values.First() : values.Aggregate((a, B) => a - B)).ToString();
                   case "*": return values.Aggregate((a, B) => a * B).ToString();
                   case "/": return values.Aggregate((a, B) => a / B).ToString();
                   default: throw new Exception("Opérateur non valide.");
               }
           }
       }

       static string Eval(string input)
       {
           var match = regex.Match(input);
           if (!match.Success)
               throw new Exception("\nExpression LISP non valide.");

           string expression = match.Value;
           string function = Regex.Match(expression, @"[\+\-\*/]").Value;
           var args = Regex.Split(Regex.Replace(expression, @"[\(\)\+\-\*/]", "").Trim(), @"\s+");
           if (expression == input)
               return Apply(function, args);
           else
               return Eval(Regex.Replace(input, Regex.Escape(expression), Apply(function, args)));
       }

       static void Main()
       {
           while (true)
           {
               Console.WriteLine("\nEntrez une expression arithmétique LISP (ou Entrée pour quitter) :");
               string input = Console.ReadLine();
               if (input == "")
                   return;
               try
               {
                   Console.WriteLine(Eval(input));
               }
               catch (Exception ex)
               {
                   Console.WriteLine(ex.Message);
               }
           }
       }
   }
}

 

Pour tester :

  • Télécharger ConsoleLISP.zip
  • Débloquer ConsoleLISP.zip
  • Extraire et lancer ConsoleLISP.exe

Gilles Chanteau - gileCAD -
Développements sur mesure pour AutoCAD
ADSK_Expert_Elite_Icon_S_Color_Blk_125.png

Lien vers le commentaire
Partager sur d’autres sites

Créer un compte ou se connecter pour commenter

Vous devez être membre afin de pouvoir déposer un commentaire

Créer un compte

Créez un compte sur notre communauté. C’est facile !

Créer un nouveau compte

Se connecter

Vous avez déjà un compte ? Connectez-vous ici.

Connectez-vous maintenant
×
×
  • Créer...

Information importante

Nous avons placé des cookies sur votre appareil pour aider à améliorer ce site. Vous pouvez choisir d’ajuster vos paramètres de cookie, sinon nous supposerons que vous êtes d’accord pour continuer. Politique de confidentialité