Comment écrire un Analyseur syntaxique en C#? [fermé]

Comment puis-je écrire un analyseur (descente récursive? en C#? Pour l'instant, je veux juste un simple analyseur qui analyse les expressions arithmétiques (et lit les variables?). Bien que plus tard, j'ai l'intention d'écrire un analyseur xml et html (à des fins d'apprentissage). Je le fais en raison de la large gamme de choses dans lesquelles les analyseurs sont utiles: développement Web, interprètes de langage de programmation, outils internes, moteurs de jeu, éditeurs de cartes et de tuiles, etc. Alors, quelle est la théorie de base de l'écriture d'analyseurs et comment puis-je implémenter un en C#? C# est-il le bon langage pour les analyseurs (j'ai écrit un simple analyseur arithmétique en C++ et c'était efficace. Est-ce que la compilation JIT se révélera tout aussi bonne?). Toutes les ressources utiles et des articles. Et le meilleur de tous, des exemples de code (ou des liens vers des exemples de code).

Note: par curiosité, quelqu'un a-t-il déjà implémenté un analyseur en C#?

58
demandé sur Kirill Kobelev 2011-09-11 13:24:20

7 réponses

J'ai implémenté plusieurs analyseurs en C#-écrit à la main et généré par l'outil.

Un très bon tutoriel d'introduction sur l'analyse en général est construisons un compilateur - Il montre comment construire un analyseur de descente récursif; et les concepts sont facilement traduits de sa langue (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 complètement impraticable d'écrire un analyseur de langage de programmation complet en main.

Vous devriez examiner certains des outils pour générer le code pour vous - si vous êtes déterminé à écrire un classique descente récursive de l'analyseur (TinyPG, Coco/R, Ironie). Gardez à l'esprit qu'il existe d'autres façons d'écrire des analyseurs maintenant, qui fonctionnent généralement mieux - et ont des définitions plus faciles (par exemple TDOP parsing ou monadic Parsing).

Sur le sujet de savoir si C# est en place pour la tâche-C# a quelques-unes des meilleures bibliothèques de texte y. Beaucoup d'analyseurs aujourd'hui (dans d'autres langues) ont une quantité obscène de code pour traiter Unicode etc. Je ne commenterai pas trop sur le code JITted car il peut devenir très religieux-mais vous devriez être très bien. IronJS est un bon exemple d'analyseur/runtime sur le CLR (même s'il est écrit en F#) et ses performances sont juste à côté de Google V8.

Note latérale: Les analyseurs de balisage sont des bêtes complètement différentes par rapport aux analyseurs de langage - ils sont, dans la majorité des cas, écrits à la main - et au niveau du scanner/analyseur très simple; ils ne sont généralement pas de descente récursive - et surtout dans le cas de XML, il est préférable de ne pas é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).

78
répondu Jonathan Dickinson 2018-03-25 14:49:32

Sprache {[3] } est un framework puissant mais léger pour écrire des analyseurs dans. NET. il y a aussi un paquet Sprache NuGet . Pour vous donner une idée du framework, voici l'un des samples qui peuvent analyser une expression arithmétique simple dans une arborescence d'expressions. net. Assez étonnant, 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));
    }
}
16
répondu Martin Liversage 2017-11-23 20:46:58

C # est presque un langage fonctionnel décent, donc ce n'est pas si grave d'implémenter quelque chose comme Parsec. Voici l'un des exemples de la façon de le faire: http://jparsec.codehaus.org/NParsec+Tutoriel

Il est également possible d'implémenter un combinator-based Packrat , d'une manière très similaire, mais cette fois en gardant un État d'analyse globale quelque part au lieu de faire un truc fonctionnel pur. Dans ma mise en œuvre (très basique et ad hoc), c'était raisonnablement rapide, mais de bien sûr, un générateur de code comme CE doit fonctionner mieux.

3
répondu SK-logic 2011-09-11 18:55:58

À mon avis, il existe une meilleure façon d'implémenter des analyseurs que les méthodes traditionnelles qui se traduisent par un code plus simple et plus facile à comprendre, et surtout facilite l'extension de la langue que vous analysez en branchant simplement une nouvelle classe d'une manière très orientée objet. Un article d'une plus grande série que j'ai écrit se concentre sur cette méthode d'analyse, et le code source complet est inclus pour un C # 2.0 analyseur: http://www.codeproject.com/Articles/492466/Object-Oriented-Parsing-Breaking-With-Tradition-Pa

1
répondu Ken Beckett 2013-08-17 16:11:11

Eh bien... par où commencer avec celui-ci....

Tout d'abord, écrire un analyseur, Eh bien c'est une déclaration très large surtout avec la question que vous posez.

Votre déclaration d'ouverture était que vous vouliez un simple "analyseur" arithmétique, techniquement ce n'est pas un analyseur, c'est un analyseur lexical, similaire à ce que vous pouvez utiliser pour créer un nouveau langage. ( http://en.wikipedia.org/wiki/Lexical_analysis ) je comprends cependant exactement où la confusion d'entre eux étant le la même chose peut venir de la. Il est important de noter que L'analyse lexicale est aussi ce que vous voudrez comprendre si vous allez aussi écrire des analyseurs de langage/script, ce n'est strictement pas l'analyse parce que vous interprétez les instructions plutôt que de les utiliser.

Retour à la question d'analyse....

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 vraiment pas besoin d'écrire un analyseur pour XML / HTML, car il y en a déjà une tonne, et plus encore si votre XML d'analyse est produit par L'exécution. net, alors vous n'avez même pas besoin d'analyser, il vous suffit de "sérialiser" et de "désérialiser".

Dans l'intérêt de l'apprentissage, l'analyse XML (ou quelque chose de similaire comme html) est très simple dans la plupart des cas.

Si nous commençons par 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 alors obtenir à l'élément racine'movies' utilise ' myXML.Racine '

Plus intéressant cependant, vous pouvez utiliser Linq facilement pour obtenir les balises imbriquées:

    var myElements = from p in myXML.Root.Elements("movie")
                     select p;

Vous donnera une var 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 autre chose que XML comme les structures de données, alors j'ai peur que vous deviez commencer à apprendre l'art des expressions régulières, un outil comme "Regular Expression Coach" vous aidera imensly ( 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 ) devrait vous donner une bonne longueur d'avance.

Une fois que vous savez comment fonctionne votre truc reg-ex, dans la plupart des cas, c'est un cas simple de lire dans les fichiers une ligne à la fois et de les comprendre en utilisant quelle méthode vous vous sentez à l'aise avec.

Une bonne source gratuite de formats de fichiers pour presque tout ce que vous pouvez imaginer peut être trouvé à ( http://www.wotsit.org/ )

0
répondu shawty 2011-09-11 10:02:52

Pour mémoire, j'ai implémenté un générateur d'analyseur en C # juste parce que je n'en trouvais pas fonctionnant correctement ou similaire à YACC (voir: http://sourceforge.net/projects/naivelangtools/).

Cependant, après une certaine expérience avec ANTLR, j'ai décidé d'aller avec LALR au lieu de LL. Je sais que théoriquement LL est plus facile à implémenter (générateur ou analyseur) mais je ne peux tout simplement pas vivre avec une pile d'expressions juste 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é dans add_expr ce qui ne me semble pas naturel.

0
répondu greenoldman 2013-10-20 19:54:52