Comment puis-je écrire un analyseur (descendance récursive?) En C #? Pour l'instant, je veux juste un analyseur syntaxique simple qui analyse les expressions arithmétiques (et lit les variables?). Bien que plus tard, j’ai l’intention d’écrire un analyseur syntaxique XML et HTML (à des fins d’apprentissage). Je le fais en raison de la grande variété d'éléments dans lesquels les analyseurs syntaxiques sont utiles: développement Web, interpréteurs de langages de programmation, outils Inhouse, moteurs de jeu, éditeurs de cartes et de tuiles, etc. en implémenter un en C #? C # est-il le bon langage pour les analyseurs syntaxiques (j’ai déjà écrit un analyseur arithmétique simple en C++ efficace; la compilation JIT sera-t-elle aussi efficace?). Toutes les ressources utiles et des articles. Et le meilleur de tous, des exemples de code (ou des liens vers des exemples de code).
Remarque: par curiosité, quelqu'un qui a répondu à cette question a-t-il déjà implémenté un analyseur en C #?
J'ai implémenté plusieurs analyseurs en C # - écrits à la main et générés par des outils.
Un très bon didacticiel d'introduction à l'analyse en général est Construisons un compilateur } -, il montre comment créer un analyseur récursif de descente; et les concepts sont facilement traduits de son langage (je pense que c'était Pascal) en C # pour tout développeur compétent. Cela vous apprendra comment fonctionne un analyseur de descente récursif, mais il est totalement irréaliste d’écrire manuellement un analyseur de langage de programmation complet.
Vous devriez chercher des outils pour générer le code pour vous - si vous êtes déterminé à écrire un analyseur analyseur de descente récursif classique ( TinyPG , Coco/R , Ironie ). N'oubliez pas qu'il existe maintenant d'autres moyens d'écrire des analyseurs syntaxiques, qui fonctionnent généralement mieux et qui ont des définitions plus simples (par exemple, analyse TDOP ou analyse monadique ).
À propos de savoir si C # est prêt pour la tâche - C # possède certaines des meilleures bibliothèques de texte disponibles. Beaucoup d'analyseurs aujourd'hui (dans d'autres langues) ont une quantité obscène de code à traiter avec Unicode, etc. Je ne commenterai pas trop le code JITted car il peut devenir assez religieux - mais vous devriez vous en contenter. IronJS est un bon exemple d'analyseur/d'exécution sur le CLR (même s'il est écrit en F #) et que ses performances sont un peu moins bonnes que Google V8.
Côté Remarque: Les analyseurs de balisage sont des animaux complètement différents des analyseurs syntaxiques - ils sont, dans la plupart des cas, écrits à la main - et au niveau scanner/analyseur très simple; ce n'est généralement pas une descente récursive - et en particulier dans le cas de XML, il est préférable d'écrire un analyseur de descente récursif (pour éviter les débordements de pile et parce qu'un analyseur "plat" peut être utilisé en mode SAX/Push).
Sprache est un framework à la fois puissant et léger pour l’écriture d’analyseurs en .NET. Il existe également un package Sprache NuGet . Pour vous donner une idée du framework, voici l'un des samples qui peuvent analyser une expression arithmétique simple dans un arbre d'expression .NET. Assez incroyable je dirais.
using System;
using System.Linq.Expressions;
using Sprache;
namespace LinqyCalculator
{
static class ExpressionParser
{
public static Expression<Func<decimal>> ParseExpression(string text)
{
return Lambda.Parse(text);
}
static Parser<ExpressionType> Operator(string op, ExpressionType opType)
{
return Parse.String(op).Token().Return(opType);
}
static readonly Parser<ExpressionType> Add = Operator("+", ExpressionType.AddChecked);
static readonly Parser<ExpressionType> Subtract = Operator("-", ExpressionType.SubtractChecked);
static readonly Parser<ExpressionType> Multiply = Operator("*", ExpressionType.MultiplyChecked);
static readonly Parser<ExpressionType> Divide = Operator("/", ExpressionType.Divide);
static readonly Parser<Expression> Constant =
(from d in Parse.Decimal.Token()
select (Expression)Expression.Constant(decimal.Parse(d))).Named("number");
static readonly Parser<Expression> Factor =
((from lparen in Parse.Char('(')
from expr in Parse.Ref(() => Expr)
from rparen in Parse.Char(')')
select expr).Named("expression")
.XOr(Constant)).Token();
static readonly Parser<Expression> Term = Parse.ChainOperator(Multiply.Or(Divide), Factor, Expression.MakeBinary);
static readonly Parser<Expression> Expr = Parse.ChainOperator(Add.Or(Subtract), Term, Expression.MakeBinary);
static readonly Parser<Expression<Func<decimal>>> Lambda =
Expr.End().Select(body => Expression.Lambda<Func<decimal>>(body));
}
}
C # est presque un langage fonctionnel décent, il n’est donc pas si difficile d’implémenter quelque chose comme Parsec. Voici un exemple de procédure: http://jparsec.codehaus.org/NParsec+Tutorial
Il est également possible de mettre en œuvre un Packrat basé sur un combinateur, de manière très similaire, mais en conservant cette fois un état d’analyse globale quelque part au lieu d’effectuer des opérations purement fonctionnelles. Dans mon implémentation (très basique et ad hoc), elle était assez rapide, mais bien sûr, un générateur de code comme this doit fonctionner mieux.
Je sais que je suis un peu en retard, mais je viens de publier une bibliothèque de générateur d'analyseur syntaxique/grammaire/AST appelée Ve Parser. vous pouvez le trouver sur http://veparser.codeplex.com ou l'ajouter à votre projet en tapant 'Install-Package veparser' dans la console Package Manager. Cette bibliothèque est une sorte d’analyseur de descente récursive qui se veut simple d’utilisation et souple. Comme sa source est disponible, vous pouvez apprendre de ses codes source. J'espère que ça aide.
À mon avis, il existe un meilleur moyen d'implémenter des analyseurs syntaxiques que les méthodes traditionnelles: il en résulte un code plus simple et plus facile à comprendre, et il est notamment plus facile d'étendre le langage que vous analysez en insérant simplement une nouvelle classe dans un très objet. manière orientée. Un article d'une série plus importante que j'ai écrite est consacré à cette méthode d'analyse. Le code source complet est inclus pour un analyseur syntaxique C # 2.0: http://www.codeproject.com/Articles/492466/Object-Oriented- Analyser-rompre-avec-tradition-pa
Eh bien ... par où commencer avec celui-ci ....
Tout d’abord, écrire un analyseur, eh bien, c’est une déclaration très large, en particulier avec la question que vous posez.
Votre déclaration liminaire disait que vous vouliez un "analyseur" arithmatique simple. Eh bien, techniquement, ce n'est pas un analyseur syntaxique, c'est un analyseur lexical, similaire à celui que vous pouvez utiliser pour créer un nouveau langage. ( http://en.wikipedia.org/wiki/analyse_expressive ) Je comprends toutefois exactement d'où peut provenir la confusion qui existe entre eux. Il est important de noter que l'analyse lexicale est aussi ce que vous voudrez comprendre si vous voulez aussi écrire des analyseurs syntaxiques de langage/script. Il ne s'agit en aucun cas d'une analyse syntaxique, car vous interprétez les instructions au lieu de les utiliser.
Retour à la question de l'analyse syntaxique ....
C’est ce que vous ferez si vous prenez une structure de fichier définie de manière rigide pour en extraire des informations.
En général, vous n'avez pas vraiment besoin d'écrire un analyseur syntaxique pour XML/HTML, car il en existe déjà une tonne, et plus encore si votre XML d'analyse produit par le runtime .NET, vous n'avez même pas besoin de le faire. parser, il vous suffit de "sérialiser" et "désérialiser".
Cependant, dans l’intérêt de l’apprentissage, l’analyse XML (ou tout ce qui ressemble au HTML) est très simple dans la plupart des cas.
si nous commençons avec le XML suivant:
<movies>
<movie id="1">
<name>Tron</name>
</movie>
<movie id="2">
<name>Tron Legacy</name>
</movie>
<movies>
nous pouvons charger les données dans un XElement comme suit:
XElement myXML = XElement.Load("mymovies.xml");
vous pouvez ensuite accéder à l'élément racine 'movies' à l'aide de 'myXML.Root'
MOre intéressant cependant, vous pouvez facilement utiliser Linq pour obtenir les tags imbriqués:
var myElements = from p in myXML.Root.Elements("movie")
select p;
Vous donnera une variété de XElements contenant chacun un '...' que vous pouvez obtenir en utilisant quelque chose comme:
foreach(var v in myElements)
{
Console.WriteLine(string.Format("ID {0} = {1}",(int)v.Attributes["id"],(string)v.Element("movie"));
}
Pour tout ce qui est autre que XML, comme les structures de données, je crains fort que vous deviez commencer à apprendre l'art des expressions régulières, un outil tel que "Regular Expression Coach" vous aidera immédiatement ( http://weitz.de/regex-coach/ ) ou l’un des outils similaires les plus récents.
Vous devrez également vous familiariser avec les objets d’expression régulière .NET ( http://www.codeproject.com/KB/dotnet/regextutorial.aspx ), ce qui devrait vous donner un bon départ.
Une fois que vous savez comment fonctionne votre fichier reg-ex, il vous suffit dans la plupart des cas de lire dans les fichiers, ligne par ligne, et de les interpréter selon la méthode avec laquelle vous vous sentez à l'aise.
Une bonne source gratuite de formats de fichiers pour presque tout ce que vous pouvez imaginer est disponible sur ( http://www.wotsit.org/ )
Pour l’information, j’ai implémenté le générateur d’analyseur en C # simplement parce que je n’en trouvais aucun qui fonctionnait correctement ou similaire à YACC (voir: http://sourceforge.net/projects/naivelangtools/ ).
Cependant, après quelques expériences avec ANTLR, j’ai décidé d’utiliser LALR au lieu de LL. Je sais que LL est théoriquement plus facile à implémenter (générateur ou analyseur), mais je ne peux tout simplement pas vivre avec une pile d'expressions simplement pour exprimer les priorités des opérateurs (comme *
va avant +
dans "2 + 5 * 3"). Dans LL, vous dites que mult_expr est intégré à add_expr, ce qui ne me semble pas naturel.