Problema: ejecutar comandos en forma de cadena.
ejemplo de comando:
/user/files/ list all;
equivalente a:/user/files/ ls -la;
otro:
post tw fb "HOW DO YOU STOP THE TICKLE MONSTER?;"
equivalente a:
post -tf "HOW DO YOU STOP THE TICKLE MONSTER?;"
Solución actual:
tokenize string(string, array);
switch(first item in array) {
case "command":
if ( argument1 > stuff) {
// do the actual work;
}
}
Los problemas que veo en esta solución son:
- No hay errores de comprobación que no sean anidados ifs-else dentro de cada caso. El guión se vuelve muy grande y difícil de mantener.
- Los comandos y las respuestas están codificados.
- No hay forma de saber si los indicadores son parámetros correctos o faltantes.
- Falta de inteligencia para sugerir "es posible que desee ejecutar $ command".
Y lo último que no puedo abordar es sinónimos en diferentes codificaciones, por ejemplo:
case command:
case command_in_hebrew:
do stuff;
break;
El último podría ser trivial, pero bueno, lo que quiero ver son los fundamentos sólidos de este tipo de programa.
Actualmente estoy programando esto en PHP, pero podría hacerlo en PERL.
php
algorithms
perl
parsing
command-line
alfa64
fuente
fuente
Respuestas:
Permítanme admitir francamente, que el analizador de construcción es un trabajo tedioso y se acerca a la tecnología de compilación, pero construir uno resultaría ser una buena aventura. Y un analizador viene con intérprete. Entonces tienes que construir ambos.
Una introducción rápida a analizadores e intérpretes
Esto no es demasiado técnico. Entonces los expertos no se preocupan por mí.
Cuando introduce alguna entrada en un terminal, el terminal divide la entrada en varias unidades. La entrada se llama expresión y las múltiples unidades se llaman tokens. Estos tokens pueden ser operadores o símbolos. Entonces, si ingresa 4 + 5 en una calculadora, esta expresión se divide en tres tokens 4, +, 5. El plus se considera un operador mientras que 4 y 5 símbolos. Esto se pasa a un programa (considérelo como un intérprete) que contiene la definición para los operadores. Según la definición (en nuestro caso, add), agrega los dos símbolos y devuelve el resultado al terminal. Todos los compiladores se basan en esta tecnología. El programa que divide una expresión en múltiples tokens se llama lexer y el programa que convierte estos tokens en etiquetas para su posterior procesamiento y ejecución se llama analizador.
Lex y Yacc son las formas canónicas para construir lexers y analizadores basados en la gramática BNF bajo C y es la opción recomendada. La mayoría de los analizadores son un clon de Lex y Yacc.
Pasos para construir un analizador / intérprete
Entonces, en el caso anterior, sus tokens de suma serían cualquier dígito y un signo más con la definición de qué hacer con el signo más en el lexer
Notas y consejos
Un enfoque simple
Si solo necesita un mecanismo de análisis simple con funciones limitadas, convierta su requisito en una Expresión regular y simplemente cree un montón de funciones. Para ilustrar, suponga un analizador simple para las cuatro funciones aritméticas. Por lo tanto, primero llamaría al operador y luego a la lista de funciones (similar a lisp) en el estilo
(+ 4 5)
o(add [4,5])
luego podría usar un RegExp simple para obtener la lista de operadores y los símbolos que se utilizarán.Los casos más comunes podrían resolverse fácilmente con este enfoque. La desventaja es que no puede tener muchas expresiones anidadas con una sintaxis clara y no puede tener funciones fáciles de orden superior.
fuente
Primero, cuando se trata de gramática, o cómo especificar argumentos, no inventes el tuyo. El estándar de estilo GNU ya es muy popular y conocido.
En segundo lugar, dado que está utilizando un estándar aceptado, no reinvente la rueda. Use una biblioteca existente para hacerlo por usted. Si usa argumentos de estilo GNU, es casi seguro que ya haya una biblioteca madura en su idioma de elección. Por ejemplo: c # , php , c .
Una buena biblioteca de análisis de opciones incluso imprimirá ayuda formateada sobre las opciones disponibles para usted.
EDITAR 27/12
Parece que estás haciendo que esto sea más complicado de lo que es.
Cuando miras una línea de comando, es realmente bastante simple. Son solo opciones y argumentos para esas opciones. Hay muy pocos problemas complicados. La opción puede tener alias. Los argumentos pueden ser listas de argumentos.
Un problema con su pregunta es que realmente no ha especificado ninguna regla para qué tipo de línea de comando le gustaría tratar. He sugerido el estándar GNU, y sus ejemplos se acercan a eso (¿aunque realmente no entiendo su primer ejemplo con la ruta como primer elemento?).
Si estamos hablando de GNU, cualquier opción puede tener solo una forma larga y una forma corta (carácter único) como alias. Cualquier argumento que contenga un espacio debe estar entre comillas. Se pueden encadenar múltiples opciones de forma corta. Las opciones de forma corta se deben proceder con un solo guión, la forma larga con dos guiones. Solo las últimas opciones de forma corta encadenadas pueden tener un argumento.
Todo muy sencillo. Todo muy comun. También se ha implementado en todos los idiomas que puede encontrar, probablemente cinco veces.
No lo escribas Usa lo que ya está escrito.
A menos que tenga algo en mente que no sean argumentos de línea de comandos estándar, simplemente use una de las MUCHAS bibliotecas ya probadas que lo hacen.
¿Cuál es la complicación?
fuente
¿Ya has probado algo como http://qntm.org/loco ? Este enfoque es mucho más limpio que cualquier ad hoc escrito a mano, pero no requerirá una herramienta de generación de código independiente como Lemon.
EDITAR: Y un truco general para manejar líneas de comando con sintaxis compleja es combinar los argumentos nuevamente en una sola cadena separada por espacios en blanco y luego analizarlos correctamente como si fuera una expresión de algún lenguaje específico de dominio.
fuente
No ha dado muchos detalles sobre su gramática, solo algunos ejemplos. Lo que puedo ver es que hay algunas cadenas, espacios en blanco y una (probablemente, su ejemplo es indiferente en su pregunta) una cadena doble entre comillas y luego un ";" al final.
Parece que esto podría ser similar a la sintaxis de PHP. Si es así, PHP viene con un analizador, puede reutilizarlo y luego validarlo más concretamente. Finalmente, debe lidiar con los tokens, pero parece que esto es simplemente de izquierda a derecha, por lo que en realidad solo es una iteración sobre todos los tokens.
En
token_get_all
las respuestas a las siguientes preguntas se dan algunos ejemplos para reutilizar el analizador de tokens PHP ( ):Ambos ejemplos también contienen un analizador simple, probablemente algo así se ajuste a su escenario.
fuente
Si sus necesidades son simples, y ambos tienen el tiempo y están interesados en ello, iré a contracorriente aquí y les diré que no tengan miedo de escribir su propio analizador. Es una buena experiencia de aprendizaje, por lo menos. Si tiene requisitos más complejos (llamadas a funciones anidadas, matrices, etc.), tenga en cuenta que hacerlo podría llevar bastante tiempo. Uno de los grandes aspectos positivos de rodar la suya es que no habrá problemas de integración con su sistema. La desventaja es, por supuesto, que todos los errores son tu culpa.
Sin embargo, trabaje contra tokens, no use comandos codificados. Entonces ese problema con comandos de sonido similares desaparece.
Todo el mundo siempre recomienda el libro del dragón, pero siempre he encontrado que "Escribir compiladores e intérpretes" de Ronald Mak es una mejor introducción.
fuente
He escrito programas que funcionan así. Uno era un bot IRC que tiene una sintaxis de comando similar. Hay un archivo enorme que es una gran declaración de cambio. Funciona, funciona rápido, pero es algo difícil de mantener.
Otra opción, que tiene un giro más OOP, es utilizar controladores de eventos. Crea una matriz de valores-clave con comandos y sus funciones dedicadas. Cuando se da un comando, verifica si la matriz tiene la clave dada. Si es así, llame a la función. Esta sería mi recomendación para el nuevo código.
fuente
I think my implementation is very crude and faulty
abut as i stated, if you want other people to use, you need to add error checking and stuff
... Dinos exactamente lo que está crudo sobre él y lo que es defectuoso, que ayudaría a obtener mejores respuestas.Sugiero usar una herramienta, en lugar de implementar un compilador o un intérprete. Irony usa C # para expresar la gramática del idioma de destino (la gramática de su línea de comando). La descripción en CodePlex dice: "Irony es un kit de desarrollo para implementar lenguajes en la plataforma .NET".
Vea la página oficial de Irony en CodePlex: Irony - .NET Language Implementation Kit .
fuente
Mi consejo sería google para una biblioteca que resuelva su problema.
He estado usando NodeJS mucho últimamente, y Optimist es lo que uso para el procesamiento de línea de comandos. Te animo a buscar uno que puedas usar para tu propio idioma de elección. Si no ... escriba uno y abra el código fuente: D Incluso puede leer el código fuente de Optimist y transferirlo al idioma de su elección.
fuente
¿Por qué no simplificas un poco tus requisitos?
No use un analizador completo, es demasiado complejo e incluso innecesario para su caso.
Haga un bucle, escriba un mensaje que represente su "solicitud", puede ser la ruta actual que es.
Espere una cadena, "analice" la cadena y haga algo dependiendo del contenido de la cadena.
La cadena podría "analizar" como si esperara una línea, en la que los espacios son los separadores ("tokenizer"), y el resto de los caracteres están agrupados.
Ejemplo.
El programa genera (y permanece en la misma línea): / user / files / El usuario escribe (en la misma línea) enumera todos;
Su programa generará una lista, colección o matriz como
o si ";" se considera un separador como espacios
Su programa podría comenzar esperando una sola instrucción, sin "canalizaciones" de estilo unix, ni redirección de estilo de ventana.
Su programa podría hacer un diccionario de instrucciones, cada instrucción puede tener una lista de parámetros.
El patrón de diseño del comando se aplica a su caso:
http://en.wikipedia.org/wiki/Command_pattern
Este es un pseudocódigo "simple c", no se ha probado ni terminado, solo una idea de cómo se podría hacer.
También podría hacerlo más orientado a objetos y, en el lenguaje de programación, le guste.
Ejemplo:
No mencionaste tu lenguaje de programación. También puede mencionar cualquier lenguaje de programación, pero preferiblemente "XYZ".
fuente
Tienes varias tareas por delante.
mirando sus requisitos ...
El lenguaje de comando extensible indica que se requiere un DSL. Sugeriría no rodar el tuyo sino usar JSON si tus extensiones son simples. Si son complejos, una sintaxis de expresión s es buena.
La comprobación de errores implica que su sistema también conoce los posibles comandos. Eso sería parte del sistema posterior al comando.
Si yo estaba ejecutando un sistema de este tipo a partir de cero, me gustaría utilizar Common Lisp con un lector reducidos al mínimo. Cada token de comando se correlacionaría con un símbolo, que se especificaría en un archivo RC de expresión s. Después de la tokenización, se evaluaría / expandiría en un contexto limitado, atrapando los errores, y cualquier patrón de error reconocible devolvería sugerencias. Después de eso, el comando real se enviaría al sistema operativo.
fuente
Hay una buena característica en la programación funcional que puede interesarle considerar.
Se llama coincidencia de patrones .
Aquí hay dos enlaces para algún ejemplo de coincidencia de patrones en Scala y en F # .
Estoy de acuerdo con usted en que el uso de
switch
estructuras es un poco tedioso, y disfruté especialmente el uso de la correspondencia patern durante la implementación de un compilador en Scala.En particular, le recomendaría que consulte el ejemplo de cálculo lambda del sitio web de Scala.
Esa es, en mi opinión, la forma más inteligente de proceder, pero si tiene que seguir estrictamente con PHP, entonces está atrapado en la "vieja escuela"
switch
.fuente
Echa un vistazo a la CLI de Apache , parece que todo su propósito es hacer exactamente lo que quieres hacer, por lo que incluso si no puedes usarlo, puedes revisar su arquitectura y copiarlo.
fuente