Problema: Execute comandos na forma de uma sequência.
exemplo de comando:
/user/files/ list all;
equivalente a:/user/files/ ls -la;
outro:
post tw fb "HOW DO YOU STOP THE TICKLE MONSTER?;"
equivalente a:
post -tf "HOW DO YOU STOP THE TICKLE MONSTER?;"
Solução atual:
tokenize string(string, array);
switch(first item in array) {
case "command":
if ( argument1 > stuff) {
// do the actual work;
}
}
Os problemas que vejo nesta solução são:
- Nenhum erro de verificação além de ifs aninhado em cada caso. O script se torna muito grande e difícil de manter.
- Comandos e respostas são codificados.
- Não há como saber se os sinalizadores estão corretos ou faltando parâmetros.
- Falta de inteligência para sugerir "você pode querer executar o comando $".
E a última coisa que não consigo abordar são sinônimos em diferentes codificações, por exemplo:
case command:
case command_in_hebrew:
do stuff;
break;
O último pode ser trivial, mas bem, o que eu quero ver são os sólidos fundos desse tipo de programa.
Atualmente, estou programando isso em PHP, mas pode fazê-lo em PERL.
php
algorithms
perl
parsing
command-line
alfa64
fonte
fonte
Respostas:
Permitam-me admitir francamente que a construção de analisador é um trabalho tedioso e chega perto da tecnologia do compilador, mas a construção de um acabaria sendo uma boa aventura. E um analisador vem com intérprete. Então você tem que construir os dois.
Uma rápida introdução ao analisador e intérpretes
Isso não é muito técnico. Para que os especialistas não se preocupem comigo.
Quando você alimenta alguma entrada em um terminal, o terminal divide a entrada em várias unidades. A entrada é chamada expressão e as várias unidades são chamadas tokens. Esses tokens podem ser operadores ou símbolos. Portanto, se você digitar 4 + 5 em uma calculadora, essa expressão será dividida em três tokens 4, +, 5. O sinal de mais é considerado um operador com 4 e 5 símbolos. Isso é passado para um programa (considere isso como um intérprete) que contém a definição para os operadores. Com base na definição (no nosso caso, add), ele adiciona os dois símbolos e retorna o resultado ao terminal. Todos os compiladores são baseados nessa tecnologia. O programa que divide uma expressão em vários tokens é chamado de lexer e o programa que converte esses tokens em tags para processamento e execução adicionais é chamado de analisador.
Lex e Yacc são as formas canônicas para a construção de lexers e analisadores baseados na gramática BNF em C e é a opção recomendada. A maioria dos analisadores é um clone de Lex e Yacc.
Etapas na construção de um analisador / intérprete
Portanto, no caso acima, seus tokens de adição teriam qualquer dígito e um sinal de mais com a definição do que fazer com o sinal de mais no lexer
Notas e dicas
Uma abordagem simples
Se você precisar apenas de um mecanismo de análise simples com funções limitadas, transforme seu requisito em uma Expressão Regular e crie um monte de funções. Para ilustrar, assuma um analisador simples para as quatro funções aritméticas. Então você chamaria primeiro o operador e depois a lista de funções (semelhante ao lisp) no estilo
(+ 4 5)
ou(add [4,5])
então poderia usar um RegExp simples para obter a lista de operadores e os símbolos a serem operados.Os casos mais comuns poderiam ser facilmente resolvidos por essa abordagem. A desvantagem é que você não pode ter muitas expressões aninhadas com uma sintaxe clara e não pode ter funções fáceis de ordem superior.
fonte
Primeiro, quando se trata de gramática ou como especificar argumentos, não invente a sua. O padrão no estilo GNU já é muito popular e conhecido.
Segundo, como você está usando um padrão aceito, não reinvente a roda. Use uma biblioteca existente para fazer isso por você. Se você usa argumentos no estilo GNU, quase certamente já existe uma biblioteca madura no seu idioma preferido. Por exemplo: c # , php , c .
Uma boa opção para analisar a biblioteca imprimirá até ajuda formatada nas opções disponíveis.
EDIT 12/27
Parece que você está tornando isso mais complicado do que é.
Quando você olha para uma linha de comando, é realmente bastante simples. São apenas opções e argumentos para essas opções. Existem muito poucos problemas complicadores. A opção pode ter aliases. Argumentos podem ser listas de argumentos.
Um problema com sua pergunta é que você realmente não especificou nenhuma regra para qual tipo de linha de comando você gostaria de lidar. Sugeri o padrão GNU, e seus exemplos se aproximam disso (embora eu realmente não entenda seu primeiro exemplo com o caminho como o primeiro item?).
Se estamos falando do GNU, qualquer opção única pode ter apenas uma forma longa e uma forma curta (caractere único) como alias. Qualquer argumento que contenha um espaço deve estar entre aspas. Várias opções de formato curto podem ser encadeadas. As opções de formato curto devem ser processadas por um único traço, o formato longo por dois traços. Somente a última das opções de forma abreviada encadeada pode ter um argumento.
Tudo muito direto. Tudo muito comum. Também foi implementado em todos os idiomas que você pode encontrar, provavelmente cinco vezes mais.
Não escreva. Use o que já está escrito.
A menos que você tenha algo em mente além dos argumentos padrão da linha de comando, use uma das MUITAS bibliotecas testadas já existentes que fazem isso.
Qual a complicação?
fonte
Você já tentou algo como http://qntm.org/loco ? Essa abordagem é muito mais limpa do que qualquer ad hoc manuscrita, mas não exige uma ferramenta de geração de código independente como o Lemon.
EDIT: E um truque geral para lidar com linhas de comando com sintaxe complexa é combinar os argumentos novamente em uma única sequência separada por espaços em branco e analisá-los corretamente como se fosse uma expressão de alguma linguagem específica de domínio.
fonte
Você não deu muitos detalhes sobre sua gramática, apenas alguns exemplos. O que posso ver é que existem algumas strings, espaços em branco e uma (provavelmente, seu exemplo é indiferente na sua pergunta) com aspas duplas e depois uma ";" no fim.
Parece que isso pode ser semelhante à sintaxe do PHP. Nesse caso, o PHP vem com um analisador, você pode reutilizar e validar mais concretamente. Finalmente, você precisa lidar com os tokens, mas parece que isso é simplesmente da esquerda para a direita; portanto, apenas uma iteração sobre todos os tokens.
Alguns exemplos para reutilizar o analisador de token do PHP (
token_get_all
) são fornecidos nas respostas às seguintes perguntas:Ambos os exemplos também contêm um analisador simples, provavelmente algo como esse é adequado ao seu cenário.
fonte
Se suas necessidades são simples, e vocês dois têm tempo e estão interessados nisso, eu vou contra a corrente aqui e digo: não coíbe de escrever seu próprio analisador. É uma boa experiência de aprendizado, se nada mais. Se você tiver requisitos mais complexos - chamadas de funções aninhadas, matrizes etc. - esteja ciente de que isso pode levar um bom tempo. Um dos grandes pontos positivos de criar o seu próprio é que não haverá um problema de integração com seu sistema. A desvantagem é, obviamente, que todos os erros são sua culpa.
O trabalho contra tokens, no entanto, não usa comandos codificados. Então esse problema com comandos de som semelhantes desaparece.
Todo mundo sempre recomenda o livro do dragão, mas eu sempre achei "Compiladores e Intérpretes de Escrita", de Ronald Mak, uma introdução melhor.
fonte
Eu escrevi programas que funcionam assim. Um deles era um bot de IRC que possui sintaxe de comando semelhante. Há um arquivo enorme que é uma grande declaração de opção. Funciona - funciona rápido - mas é um pouco difícil de manter.
Outra opção, que tem mais rotação de OOP, é usar manipuladores de eventos. Você cria uma matriz de valores-chave com comandos e suas funções dedicadas. Quando um comando é dado, você verifica se a matriz possui a chave fornecida. Caso isso aconteça, chame a função Essa seria minha recomendação para um novo código.
fonte
I think my implementation is very crude and faulty
dabut as i stated, if you want other people to use, you need to add error checking and stuff
... Conte-nos exatamente o que é bruto sobre isso eo que é deficiente, ele iria ajudá-lo a obter melhores respostas.Sugiro usar uma ferramenta, em vez de implementar um compilador ou intérprete. O Irony usa C # para expressar a gramática do idioma de destino (a gramática da sua linha de comando). A descrição no CodePlex diz: "O Irony é um kit de desenvolvimento para implementar linguagens na plataforma .NET."
Consulte a página oficial da Irony no CodePlex: Irony - .NET Language Implementation Kit .
fonte
Meu conselho seria o google para uma biblioteca que resolve seu problema.
Ultimamente tenho usado o NodeJS, e o Optimist é o que eu uso para o processamento da linha de comando. Convido você a procurar um que possa usar para o seu próprio idioma de escolha. Caso contrário, escreva um e abra o código-fonte: D Você pode até ler o código-fonte do Optimist e portá-lo para o idioma de sua escolha.
fonte
Por que você não simplifica um pouco seus requisitos?
Não use um analisador completo, é muito complexo e até desnecessário para o seu caso.
Faça um loop, escreva uma mensagem que represente seu prompt, pode ser o caminho atual.
Aguarde uma sequência, "analise" a sequência e faça algo dependendo do seu conteúdo.
A string pode "analisar" como esperar uma linha, na qual os espaços são os separadores ("tokenizer") e o restante dos caracteres são agrupados.
Exemplo.
O programa gera (e permanece na mesma linha): / user / files / O usuário grava (na mesma linha) lista tudo;
Seu programa irá gerar uma lista, coleção ou matriz como
ou se ";" é considerado um separador como espaços
Seu programa pode começar esperando uma única instrução, sem "pipes" no estilo unix, nem redirecionamento no estilo windowze.
Seu programa pode criar um dicionário de instruções, cada instrução, pode ter uma lista de parâmetros.
O padrão de design do comando se aplica ao seu caso:
http://en.wikipedia.org/wiki/Command_pattern
Esse pseudocódigo "simples c" não foi testado ou finalizado, apenas uma idéia de como poderia ser feito.
Você também pode torná-lo mais orientado a objetos e, na linguagem de programação, desejar.
Exemplo:
Você não mencionou sua linguagem de programação. Você também pode mencionar qualquer linguagem de programação, mas preferencialmente "XYZ".
fonte
você tem várias tarefas pela frente.
olhando para suas necessidades ...
A linguagem de comando extensível indica que uma DSL é necessária. Eu sugeriria não criar o seu próprio, mas usar JSON se suas extensões forem simples. Se eles são complexos, uma sintaxe de expressão s é boa.
A verificação de erros implica que o seu sistema também conheça os possíveis comandos. Isso faria parte do sistema pós-comando.
Se eu estivesse implementando esse sistema a partir do zero, usaria o Common Lisp com um leitor simplificado. Cada token de comando seria mapeado para um símbolo, que seria especificado em um arquivo RC de expressão s. Após a tokenização, ela seria avaliada / expandida em um contexto limitado, capturando os erros e quaisquer padrões de erro reconhecíveis retornariam sugestões. Depois disso, o comando real seria despachado para o sistema operacional.
fonte
Há um bom recurso na programação funcional que você pode estar interessado em examinar.
É chamado de correspondência de padrões .
Aqui estão dois links para alguns exemplos de correspondência de padrões no Scala e no F # .
Concordo com você que o uso de
switch
estruturas é um pouco tedioso, e eu particularmente gostei de usar a correspondência de padrões durante a implementação de um compilador no Scala.Em particular, eu recomendo que você analise o exemplo de cálculo lambda do site da Scala.
Essa é, na minha opinião, a maneira mais inteligente de proceder, mas se você tiver que se ater estritamente ao PHP, ficará com a "velha escola"
switch
.fonte
Confira a Apache CLI , todo o seu objetivo parece estar fazendo exatamente o que você deseja fazer; portanto, mesmo que você não possa usá-lo, verifique sua arquitetura e copie-a.
fonte