web-dev-qa-db-fra.com

Comment écrire un interpréteur de commande / analyseur?

Problème: Exécutez des commandes sous la forme d'une chaîne.

  • exemple de commande:

    /user/files/ list all; équivalent à: /user/files/ ls -la;

  • un autre:

    post tw fb "HOW DO YOU STOP THE TICKLE MONSTER?;"

équivalent à: post -tf "HOW DO YOU STOP THE TICKLE MONSTER?;"

Solution actuelle:

tokenize string(string, array);

switch(first item in array) {
    case "command":
        if ( argument1 > stuff) {
           // do the actual work;
        }
}

Les problèmes que je vois dans cette solution sont les suivants:

  • Pas de vérification des erreurs autres que les IFS imbriquées, d'autre à l'intérieur de chaque cas. Le script devient très grand et difficile à manquer.
  • Les commandes et les réponses sont codées en papier.
  • Aucun moyen de savoir si les indicateurs sont corrects ou manquants.
  • Manque d'intelligence pour suggérer "Vous pourriez vouloir exécuter $ commandement".

Et la dernière chose que je ne peux pas aborder est Synonymes dans différents codages, exemple:

case command:
case command_in_hebrew:
    do stuff;
break;

Le dernier pourrait être trivial, mais bien, ce que je veux voir est la solide fondation de ce type de programme.

Je programmment actuellement cela dans PHP mais peut le faire à Perl.

22
alfa64

Permettez-moi d'admettre franchement, Bâtiment d'analyseur est un travail fastidieux et s'approche de la technologie du compilateur, mais la construction d'une bonne aventure serait une bonne aventure. Et un analyseur vient avec interprète. Donc, vous devez construire les deux.

une introduction rapide à l'analyseur et aux interprètes

Ce n'est pas trop technique. Donc, les experts ne vous inquiètent pas.

Lorsque vous alimentez une entrée dans une borne, le terminal divise l'entrée en plusieurs unités. L'entrée est appelée expression et les multiples unités sont appelées jetons. Ces jetons peuvent être des opérateurs ou des symboles. Donc, si vous entrez 4 + 5 Dans une calculatrice, cette expression se divise en trois jetons 4, +, 5. Le plus est considéré comme un opérateur pendant 4 et 5 symboles. Ceci est transmis à un programme (considérez cela comme un interprète) qui contient la définition des opérateurs. Basé sur la définition (dans notre cas, ajouter), il ajoute les deux symboles et renvoie le résultat au terminal. Tous les compilateurs sont basés sur cette technologie. Le programme qui scindre une expression en plusieurs jetons s'appelle un lexer et le programme qui convertit ces jetons en étiquettes pour un traitement et une exécution ultérieurs sont appelés analyseurs.

LEX et YACC sont les formes canoniques pour créer des lexers et des analyseurs basés sur [~ # ~ # ~] BNF [~ # ~] grammaire sous C et c'est l'option recommandée. La plupart des analyseurs sont un clone de Lex et Yacc.

Étapes dans la construction d'un analyseur/introprète

  1. Classer vos jetons dans des symboles, des opérateurs et des mots-clés (les mots-clés sont des opérateurs)
  2. Construisez votre grammaire à l'aide du formulaire BNF
  3. Écrire des fonctions d'analyse pour vos opérations
  4. Compilez-le d'une course en tant que programme

Donc, dans le cas ci-dessus, vos jetons supplémentaires seraient des chiffres et un signe plus avec définition de quoi faire avec le signe plus dans le LXER

Notes et conseils

  • Choisissez une technique d'analyseur qui évalue de gauche à droite LALR
  • Lisez ce livre de dragon sur compilateurs pour en avoir une idée. Personnellement, je n'ai pas fini le livre
  • Ceci link donnerait une perspective super-rapide en Lex et Yacc sous Python

une approche simple

Si vous avez juste besoin d'un simple mécanisme d'analyse avec des fonctions limitées, tournez votre exigence dans une expression régulière et créez simplement un tas de fonctions. Pour illustrer, assumer un analyseur simple pour les quatre fonctions arithmétiques. Donc, vous seriez d'abord appeler l'opérateur, puis la liste des fonctions (similaire à LISP) dans le style (+ 4 5) ou (add [4,5]) Ensuite, vous pouvez utiliser une simple Regexp pour obtenir la liste des opérateurs et les symboles à utiliser.

Les cas les plus courants pourraient être facilement résolus par cette approche. L'inconvénient est que vous ne pouvez pas avoir de nombreuses expressions imbriquées avec une syntaxe claire et vous ne pouvez pas avoir de fonctions d'ordre plus efficaces.

14
Ubermensch

Premièrement, en ce qui concerne la grammaire, ou comment spécifier des arguments, n'exposez pas le vôtre. Le standard de style GN est déjà très populaire et bien connu.

Deuxièmement, puisque vous utilisez une norme acceptée, ne réinventez pas la roue. Utilisez une bibliothèque existante pour le faire pour vous. Si vous utilisez GNU style arguments, il existe déjà une bibliothèque mature dans votre langue de choix déjà. Par exemple: C # , PHP , c .

Une bonne option d'analyse d'options imprimera même l'aide formatée sur les options disponibles pour vous.

Éditer 12/27

On dirait que vous rendant cela plus compliqué que cela.

Lorsque vous regardez une ligne de commande, c'est vraiment assez simple. C'est juste des options et des arguments à ces options. Il y a très peu de problèmes de complication. L'option peut avoir des alias. Les arguments peuvent être des listes d'arguments.

Un problème avec votre question est que vous n'avez pas vraiment spécifié de règles pour quel type de ligne de commande que vous souhaitez traiter. J'ai suggéré =GNU Standard, et vos exemples se rapprochent de cela (bien que je ne comprenne pas vraiment votre premier exemple avec le chemin comme premier élément?).

Si nous parlons GNU, une option unique ne peut avoir qu'une forme longue et une forme courte (caractère unique) comme alias. Tous les arguments contenant un espace doivent être entourés de citations. Plusieurs options de formulaire courtes peuvent être chaînées. Les options de formulaire courte doivent être poursuivies par un seul tiret, formulaire long de deux tirets. Seul le dernier des options de forme courtes chaînées peut avoir un argument.

Tout très simple. Tout très commun. Également été implémentée dans chaque langue que vous pouvez trouver, probablement cinq fois plus.

Ne l'écrivez pas. Utilisez ce qui est déjà écrit.

À moins que vous n'ayez quelque chose à l'esprit que des arguments de ligne de commande standard, utilisez simplement l'une des nombreuses bibliothèques déjà testées et testées qui le font.

Quelle est la complication?

7
quentin-starin

Avez-vous déjà essayé quelque chose comme http://qntm.org/loco ? Cette approche est beaucoup plus propre que toute ad hoc manuscrite, mais ne nécessitera pas d'outil de génération de code autonome comme citron.

Modifier: Et un astuce générale pour la manipulation des lignes de commande avec syntaxe complexe consiste à combiner les arguments dans une seule chaîne séparée par des espaces, puis à l'analyser correctement comme S'il s'agit d'une expression de la langue spécifique au domaine.

4
SK-logic

Vous n'avez pas donné de spécificités sur votre grammaire, juste quelques exemples. Ce que je peux voir, c'est qu'il y a des cordes, des espaces et un (probablement votre exemple est indifférent dans votre question) double cordée, puis une ";"; à la fin.

On dirait que cela pourrait être similaire à la syntaxe PHP. Si oui, PHP est livré avec un analyseur, vous pouvez réutiliser, puis valider plus concrètement. Enfin, vous devez faire face aux jetons, mais cela ressemble à cela simplement de gauche à droite, donc une itération sur tous les jetons.

Quelques exemples permettant de réutiliser l'analyseur de jeton PHP ( token_get_all ) sont donnés dans les réponses aux questions suivantes:

Les deux exemples contiennent également un analyseur simple, probablement quelque chose comme ceux qui sont adaptés à votre scénario.

1
hakre

Si vos besoins sont simples et que vous avez tous les deux le temps et que vous y êtes intéressé, je vais aller à l'encontre du grain ici et dire que vous dites pas à l'écart de l'écriture de votre propre analyseur. C'est une bonne expérience d'apprentissage, si rien d'autre. Si vous avez des exigences plus complexes - des appels de fonction imbriqués, des tableaux, etc., soyez tout simplement conscient que cela pourrait prendre une bonne partie du temps. L'un des gros positifs de rouler le vôtre est qu'il n'y a pas eu de problème d'intégration de votre système. L'inconvénient est bien sûr que toutes les vis sont votre faute.

Travaillez contre des jetons, cependant, n'utilisez pas de commandes codées dures. Ensuite, ce problème avec des commandes de sondage similaires disparaît.

Tout le monde recommande toujours le livre de dragon, mais j'ai toujours trouvé "compilateurs et interprètes d'écriture" par Ronald Mak pour être une meilleure intro.

1
GrandmasterB

Je suggère d'utiliser un outil, au lieu de mettre en œuvre un compilateur ou un interprète vous-même. L'ironie utilise C # pour exprimer la grammaire de langue cible (la grammaire de votre ligne de commande). La description sur CodePlex dit: "L'ironie est un kit de développement pour la mise en œuvre de langues sur la plate-forme .NET."

Voir la page d'accueil officielle de l'ironie sur CodePlex: Kit de mise en œuvre de la langue d'ironie .

0

Il y a une belle fonctionnalité dans Programmation fonctionnelle Vous pourriez être intéressé à examiner.

C'est ce qu'on appelle la correspondance .

Voici deux liaisons pour un exemple de correspondance de motif dans scala et dans f # .

Je suis d'accord avec vous que l'utilisation de switch structures est un peu fastidieuse et j'ai particulièrement apprécié d'utiliser Patern Associant Durint la mise en œuvre d'un compilateur à Scala.

En particulier, je vous recommanderais de regarder dans le Lambda Calculus exemple du Scala site web.

C'est à mon avis le moyen le plus intelligent de procéder, mais si vous devez coller strictement avec PHP, alors vous êtes coincé avec la "vieille école" switch.

0
SRKX

J'ai des programmes écrits qui fonctionnent comme ça. On était IRC BOT qui a la syntaxe de commande similaire. Il y a un fichier énorme qui est un énoncé de commutateur important. Ça marche - ça marche vite - mais c'est un peu difficile à entretenir.

Une autre option, qui a un plus OOP spin, est d'utiliser des gestionnaires d'événements. Vous créez une matrice-clé-valeur avec les commandes et leurs fonctions dédiées. Lors de la commande d'une commande, vous vérifiez si le La matrice a la clé donnée. Si tel est le cas, appelez la fonction. Ce serait ma recommandation pour le nouveau code.

0
Brigand

Mon conseil serait Google pour une bibliothèque qui résout votre problème.

J'utilise beaucoup de Nodejs dernièrement, et optimiste est ce que j'utilise pour le traitement de ligne de commande. Je vous encourage à rechercher un sur lequel vous pouvez utiliser votre propre langue de choix. Si non..Collez une et open source IT: D Vous pouvez même lire le code source d'Optimist et le porter à votre langue de choix.

0
ming_codes

Vérifiez Apache CLI , c'est que le but entier semble faire exactement ce que vous voulez faire, alors même si vous ne pouvez pas l'utiliser, vous pouvez consulter son architecture et copier cela.

0
Stephen Rudolph

vous avez plusieurs tâches devant vous.

en regardant vos exigences ...

  • Vous devez analyser la commande. C'est une tâche assez facile
  • Vous devez avoir une langue de commande extensible.
  • Vous devez avoir une vérification et des suggestions d'erreur.

La langue de commande extensible indique qu'un DSL est requis. Je suggérerais de ne pas rouler le vôtre mais utiliser Json si vos extensions sont simples. S'ils sont complexes, une syntaxe d'expression S est agréable.

La vérification des erreurs implique que votre système connaît également les commandes possibles. Cela ferait partie du système post-commandement.

Si [~ # ~] I [~ # ~]] Mise en œuvre d'un tel système à partir de zéro, j'utiliserais des Lisp commun avec un lecteur dépouillé. Chaque jeton de commande planerait dans un symbole, qui serait spécifié dans un fichier RC d'expression S. Après la jocalisation, il serait évalué/étendu dans un contexte limité, piéger les erreurs, et tout modèle d'erreur reconnaissable rendrait des suggestions. Après cela, la commande réelle serait expédiée au système d'exploitation.

0
Paul Nathan