Problème: exécutez les 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:
- Aucune vérification d'erreur autre que des ifs imbriqués sinon dans chaque cas. Le script devient très gros et difficile à maintenir.
- Les commandes et les réponses sont codées en dur.
- Aucun moyen de savoir si les drapeaux sont des paramètres corrects ou manquants.
- Manque d'intelligence pour suggérer "vous pourriez vouloir exécuter $ command".
Et la dernière chose que je ne peux pas aborder est les synonymes dans différents encodages, par exemple:
case command:
case command_in_hebrew:
do stuff;
break;
Le dernier peut être trivial, mais bon, ce que je veux voir, ce sont les solides fondations de ce type de programme.
Je programme actuellement cela en PHP mais je pourrais le faire en PERL.
php
algorithms
perl
parsing
command-line
alfa64
la source
la source
Réponses:
Permettez-moi d'admettre franchement, la construction de l'analyseur est un travail fastidieux et se rapproche de la technologie du compilateur, mais la construction de celui-ci se révélerait être une bonne aventure. Et un analyseur vient avec un interprète. Vous devez donc construire les deux.
Une introduction rapide à l'analyseur et aux interprètes
Ce n'est pas trop technique. Les experts ne s'inquiètent donc pas de moi.
Lorsque vous introduisez une entrée dans un terminal, le terminal divise l'entrée en plusieurs unités. L'entrée est appelée expression et les unités multiples 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 est divisée en trois jetons 4, +, 5. Le plus est considéré comme un opérateur avec 4 et 5 symboles. Ceci est transmis à un programme (considérez-le comme un interpréteur) qui contient la définition des opérateurs. Sur la base de la définition (dans notre cas, ajoutez), il ajoute les deux symboles et renvoie le résultat au terminal. Tous les compilateurs sont basés sur cette technologie. Le programme qui divise une expression en plusieurs jetons est appelé un lexer et le programme qui convertit ces jetons en balises pour un traitement et une exécution ultérieurs est appelé analyseur.
Lex et Yacc sont les formes canoniques pour la construction de lexers et d'analyseurs basés sur la grammaire BNF sous C et c'est l'option recommandée. La plupart des analyseurs sont un clone de Lex et Yacc.
Étapes de la construction d'un analyseur / intrépète
Donc, dans le cas ci-dessus, vos jetons d'addition seraient des chiffres et un signe plus avec une définition de ce qu'il faut faire avec le signe plus dans le lexer
Notes et astuces
Une approche simple
Si vous avez juste besoin d'un mécanisme d'analyse simple avec des fonctions limitées, transformez votre exigence en une expression régulière et créez simplement un tas de fonctions. Pour illustrer, supposons un analyseur simple pour les quatre fonctions arithmétiques. Vous devez donc appeler l'opérateur en premier puis la liste des fonctions (similaire à lisp) dans le style
(+ 4 5)
ou(add [4,5])
alors vous pouvez utiliser un simple RegExp pour obtenir la liste des opérateurs et les symboles à utiliser.La plupart des cas courants pourraient être facilement résolus par cette approche. L'inconvénient est que vous ne pouvez pas avoir beaucoup d'expressions imbriquées avec une syntaxe claire et que vous ne pouvez pas avoir de fonctions faciles d'ordre supérieur.
la source
Tout d'abord, quand il s'agit de grammaire, ou comment spécifier des arguments, n'inventez pas les vôtres. La norme de style GNU est déjà très populaire et bien connue.
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 des arguments de style GNU, il existe presque certainement déjà une bibliothèque mature dans la langue de votre choix. Par exemple: c # , php , c .
Une bonne bibliothèque d'analyse des options imprimera même une aide formatée sur les options disponibles pour vous.
EDIT 12/27
Il semble que ce soit plus compliqué que ça.
Lorsque vous regardez une ligne de commande, c'est vraiment très simple. Ce ne sont que des options et des arguments pour ces options. Il y a très peu de problèmes compliqués. 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 le type de ligne de commande que vous souhaitez traiter. J'ai suggéré la norme GNU, et vos exemples s'en rapprochent (même si je ne comprends pas vraiment votre premier exemple avec le chemin comme premier élément?).
Si nous parlons de GNU, une seule option ne peut avoir que des formes longues et courtes (caractère unique) comme alias. Tout argument contenant un espace doit être entouré de guillemets. Plusieurs options de formulaire court peuvent être chaînées. Les options de forme courte doivent être précédées d'un seul tiret, la forme longue de deux tirets. Seule la dernière des options de forme courte chaînées peut avoir un argument.
Tout est très simple. Tout cela est très courant. Également implémenté dans toutes les langues que vous pouvez trouver, probablement cinq fois.
Ne l'écris pas. Utilisez ce qui est déjà écrit.
À moins que vous n'ayez à l'esprit autre chose que les arguments de ligne de commande standard, utilisez simplement l'une des NOMBREUSES bibliothèques déjà testées qui le font.
Quelle est la complication?
la source
Avez-vous déjà essayé quelque chose comme http://qntm.org/loco ? Cette approche est beaucoup plus propre que tout autre ad hoc manuscrit, mais ne nécessitera pas d'outil de génération de code autonome comme Lemon.
EDIT: Et une astuce générale pour gérer les lignes de commande avec une syntaxe complexe consiste à combiner les arguments en une seule chaîne séparée par des espaces, puis à les analyser correctement comme s'il s'agissait d'une expression d'un langage spécifique au domaine.
la source
Vous n'avez pas donné beaucoup de détails sur votre grammaire, juste quelques exemples. Ce que je peux voir, c'est qu'il y a des chaînes, des espaces et une (probablement, votre exemple est indifférent dans votre question) une chaîne entre guillemets puis un ";" à la fin.
Il semble que cela puisse être similaire à la syntaxe PHP. Si c'est le cas, PHP est livré avec un analyseur, vous pouvez réutiliser puis valider plus concrètement. Enfin, vous devez gérer les jetons, mais il semble que ce soit simplement de gauche à droite, donc en fait juste une itération sur tous les jetons.
Quelques exemples pour 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 convient à votre scénario.
la source
Si vos besoins sont simples et que vous avez tous les deux le temps et que cela vous intéresse, je vais aller à contre-courant et dire: n'hésitez pas à écrire votre propre analyseur. C'est une bonne expérience d'apprentissage, si rien d'autre. Si vous avez des exigences plus complexes - appels de fonctions imbriquées, tableaux, etc. - sachez que cela pourrait prendre beaucoup de temps. L'un des grands avantages de rouler le vôtre est qu'il n'y aura pas de problème d'intégration avec votre système. L'inconvénient est, bien sûr, que tous les échecs sont de votre faute.
Travaillez contre les jetons, cependant, n'utilisez pas de commandes codées en dur. Ensuite, ce problème avec des commandes de sondage similaires disparaît.
Tout le monde recommande toujours le livre du dragon, mais j'ai toujours trouvé que "Writing Compilers and Interpreters" de Ronald Mak était une meilleure introduction.
la source
J'ai écrit des programmes qui fonctionnent comme ça. L'un était un bot IRC qui a une syntaxe de commande similaire. Il y a un énorme fichier qui est une grosse déclaration de commutateur. Ça marche - ça marche vite - mais c'est un peu difficile à entretenir.
Une autre option, qui a une rotation plus POO, consiste à utiliser des gestionnaires d'événements. Vous créez un tableau de valeurs-clés avec des commandes et leurs fonctions dédiées. Lorsqu'une commande est donnée, vous vérifiez si le tableau a la clé donnée. Si c'est le cas, appelez la fonction. Ce serait ma recommandation pour un nouveau code.
la source
I think my implementation is very crude and faulty
labut as i stated, if you want other people to use, you need to add error checking and stuff
... Dites - nous exactement ce qui est brut à ce sujet et ce qui est défectueux, il vous aider à obtenir de meilleures réponses.Je suggère d'utiliser un outil, au lieu d'implémenter vous-même un compilateur ou un interprète. Irony utilise C # pour exprimer la grammaire de la langue cible (la grammaire de votre ligne de commande). La description sur CodePlex dit: "Irony est un kit de développement pour implémenter des langages sur la plate-forme .NET."
Voir la page d'accueil officielle d'Irony sur CodePlex: Irony - Kit de mise en œuvre du langage .NET .
la source
Mon conseil serait google pour une bibliothèque qui résout votre problème.
J'utilise beaucoup NodeJS ces derniers temps, et Optimist est ce que j'utilise pour le traitement en ligne de commande. Je vous encourage à rechercher celui que vous pouvez utiliser pour votre propre langue de choix. Sinon ... écrivez-en un et ouvrez-le: D Vous pouvez même lire le code source d'Optimist et le porter dans la langue de votre choix.
la source
Pourquoi ne simplifiez-vous pas un peu vos exigences?
N'utilisez pas un analyseur complet, c'est trop complexe et même inutile pour votre cas.
Faites une boucle, écrivez un message qui représente votre "invite", peut être le chemin actuel que vous êtes.
Attendez une chaîne, "analysez" la chaîne et faites quelque chose en fonction du contenu de la chaîne.
La chaîne pourrait "analyser" comme attendre une ligne, dans laquelle les espaces sont les séparateurs ("tokenizer"), et le reste des caractères sont regroupés.
Exemple.
Le programme génère (et reste sur la même ligne): / user / files / L'utilisateur écrit (sur la même ligne) liste tout;
Votre programme va générer une liste, une collection ou un tableau comme
ou si ";" est considéré comme un séparateur comme des espaces
Votre programme pourrait commencer par attendre une seule instruction, sans "tuyaux" de style Unix, ni redirection de style windowze.
Votre programme pourrait créer un dictionnaire d'instructions, chaque instruction peut avoir une liste de paramètres.
Le modèle de conception de commande s'applique à votre cas:
http://en.wikipedia.org/wiki/Command_pattern
Ce pseudocode "plain c" n'est ni testé ni terminé, juste une idée de la façon de le faire.
Vous pouvez également le rendre plus orienté objet, et dans le langage de programmation, vous aimez.
Exemple:
Vous n'avez pas mentionné votre langage de programmation. Vous pouvez également mentionner n'importe quel langage de programmation, mais de préférence "XYZ".
la source
vous avez plusieurs tâches devant vous.
en regardant vos besoins ...
Le langage de commande extensible indique qu'un DSL est requis. Je suggérerais de ne pas rouler le vôtre mais d'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 j'exécutait un tel système à partir de zéro, j'utiliser Common Lisp avec un lecteur-dépouillé. Chaque jeton de commande correspondrait à un symbole, qui serait spécifié dans un fichier RC d'expression s. Après la tokenisation, il serait évalué / développé dans un contexte limité, interceptant les erreurs, et tout modèle d'erreur reconnaissable renverrait des suggestions. Après cela, la commande réelle serait envoyée au système d'exploitation.
la source
Il y a une fonctionnalité intéressante dans la programmation fonctionnelle qui pourrait vous intéresser.
Cela s'appelle la correspondance de motifs .
Voici deux liens pour un exemple de correspondance de motifs en Scala et en F # .
Je suis d'accord avec vous que l'utilisation des
switch
structures est un peu fastidieuse, et j'ai particulièrement apprécié l'utilisation de la correspondance de patern pendant la mise en œuvre d'un compilateur dans Scala.En particulier, je vous recommande de regarder l' exemple du calcul lambda du site Web de Scala.
C'est, à mon avis, la façon la plus intelligente de procéder, mais si vous devez vous en tenir strictement à PHP, alors vous êtes coincé avec la "vieille école"
switch
.la source
Consultez Apache CLI , son objectif semble faire exactement ce que vous voulez faire, donc même si vous ne pouvez pas l'utiliser, vous pouvez vérifier son architecture et le copier.
la source