MINI INTERPRETE 001
From Movaxes
Contents |
Version 0.001
NOTA
Estoy creando la versión 0.002 que utiliza pilas en lugar de la recursividad tan difícil de entender de esta versión (al menos a simple vista), el código se reduce mucho y es más fácil de entender, solo debes ver la sección de la wiki de estructuras de datos (pilas). Estará listo en unos días.
Explicación
El interprete funciona de esta manera, primero el usuario ingresa una línea de código y presiona ENTER para indicar que termino de ingresar, suponiendo que el usuario ingresó:
> A = 200 + B * 2
El interprete empieza por leer la línea de código caracter por caracter y la guarda en un buffer sin espacios, sin tabuladores y omitiendo cualquier signo de arroba que pueda haber en el código. Para indicar el fin del buffer el interprete pone el signo de arroba (@) al final del código. El buffer con el código queda así:
A=200+B*2
Luego el interprete revisa que el código ingresado sea válido, para eso mira en una lista de caracteres válidos.
Para terminar el interprete ejecuta el código dependiendo de si es una asignación, un input para una variable, un output, un comentario, etc.
Obtener()
Obtiene una cadena del teclado hasta encontrar un (\ l), cualquier espacio, tabulador o (@) es omitido.
La cadena es copiada a un arreglo con un límite de 100 caracteres, luego del final de la cadena se pone el signo de (@) para indicarlo.
Mostrar()
Muestra la cadena obtenida por obtener() y el total de caracteres. Muy útil si se quiere mostrar al usuario su error cuando se revisa el código.
Revisar()
Revisa que los caracteres de la cadena sean válidos, revisa si la línea es un comentario, y que si es un INPUT que tenga la sintaxis correcta.
Ejecutar()
Decide qué es lo que se va a hacer dependiendo del código que el usuario ingresó.
Código del Interprete
mini.c:
/* * MINI INTERPRETE v0.001 * POR: movaxes 2007 * Licencia: GPL */ #include <stdio.h> #include <stdlib.h> #include <ctype.h> #include <string.h> #ifdef linux #define cls() system("clear") #else #define cls() system("cls") #endif #define COMM '$' //comentario #define PARI '(' //parentesis izquierdo #define PARD ')' //parentesis derecho #define ASIG '=' //operador de asignacion+ #define DIV '/' //operador de division #define MUL '*' //operador de multiplicacion #define RES '-' //operador de resta #define SUM '+' //operador de suma #define OUT '!' //output #define IN '?' //input #define SNL '_' //salto de linea en cadena #define FINP '.' //indica el final del programa #define FINBUF '@' //indica el final de la expresion #define ESP ' ' //espacio #define PROMPT "> " //prompt #define TAB '\\t' //tab #define NL '\ ' //newline struct variables //variables disponibles { int A, B, C, D, E; } vars; char buffer[100]; //buffer char fin = 0; //terminamos el programa char permiso = 0; //permiso para ejecutar el buffer? int total = 0; //total de caracteres del buffer usados int temp_var; //guarda el valor de la variable a la que se esta asignando un valor int posicion; //la posicion en la que estamos en el buffer void error(char *str); //muestra un error char comparar(char c, char x); //compara dos caracteres void obtener(void); //obtiene la expresion actual void mostrar(void); //muestra la expresion obtenida char es_var(char c); //vamos a asignar? char es_out(char c); //vamos a imprimir en pantalla? char es_in(char c); //vamos a pedir ingreso desde el teclado? char es_comentario(char c); //es un comentario? char es_salto(char c); //es salto de linea en cadena? char es_cadena(char c); //es una cadena? char es_parentesis(char pos); //es parentesis? char mas_menos(char c); //es +,-? char por_entre(char c); //es /,*? void revisar(void); //revisamos que la expresion sea correcta void ejecutar(int pos); //ejecutamos int obtener_var(char c); //regresa el valor de una variable void poner_var(char c, int n); //pone el valor de una variable int multi_divi(int pos); //es multiplicacion o division int subexpresion(int pos); //subexpresion int expresion(int pos); //expresion char asignar(char c, int pos);//asignamos el valor de la expresion a la variable int out_cadena(int inicio, char final);//pone cadena en pantalla void output(int pos); //output en pantalla void input(int pos); //pedimos al usuario el ingreso de un valor (input) int main (void) { cls(); if(!buffer) { error("no se pudo crear buffer..."); return 0; } printf("MINI INTERPRETE v0.001\ "); while(!fin) { printf(PROMPT); obtener(); if(!fin) { //mostrar(); //Si se quiere ver la expresion (para depurar) revisar(); if(permiso) ejecutar(0); } } return 0; } void error(char *str) { printf("\\aERROR: %s\ ",str); } char es_var(char c) { switch(c) { case 'A': case 'B': case 'C': case 'D': case 'E': return 1; default : return 0; } } char comparar(char c, char x) { return(c==x); } char es_out(char c){ return(c==OUT); } char es_in(char c){ return(c==IN); } char es_comentario(char c){ return(c==COMM); } char es_cadena(char c){ return(c=='\''||c=='\\"'); } char es_salto(char c){ return(c==SNL); } char es_parentesis(char c){ return(c=='('); } char mas_menos(char c){ return(c=='+' || c=='-'); } char por_entre(char c){ return(c=='*' || c=='/'); } void obtener(void) //Obtenemos una expresion del usuario { char c; int i = 0; char comentario = 0; while((c=getchar())!=NL) //Mientras no lleguemos al final de la linea { if(c==FINP && !comentario) //Si encontramos un FINP y no es un comentario { fin = 1; //el programa termina return; } else if(c!=ESP && c!=TAB && c!=FINBUF) //Copiamos todo menos espacios, tab y el signo de FINBUF @ { if(es_cadena(c)) //Si es una cadena la copiamos literalmente { char comilla = c; do { buffer[i] = c; c = getchar(); i++; } while(!comparar(comilla,c)); //Revisamos si la cadena termino buffer[i] = c; i++; } else if(es_comentario(c)) //Si es un comentario lo copiamos, seria mejor si no (pendiente) { comentario = 1; buffer[i] = c; i++; } else //cualquier otro caracter lo copiamos { buffer[i] = c; i++; } } } total = i; //el total de caracteres del buffer buffer[i]=FINBUF; //indicamos en el arreglo el fin de la expresion } void mostrar(void) //Muestra el buffer { int i = 0; printf(": "); if(buffer[0]!=COMM) //Si no es un comentario { while(buffer[i]!=FINBUF) { printf("%c",buffer[i]); i++; } printf(" (%i)\ ",total); } else printf("comentario\ "); //Si es un comentario } void revisar(void) //Revisa que la expresion sea valida { int i = 0; char temp = buffer[i]; while(temp!=FINBUF) //Mientras no lleguemos al final de la expresion { if(temp==COMM) //Si es un comentario { permiso = 0; //no lo ejecutamos return; } else if(temp==IN) //Si es un input { ++i; temp = buffer[i]; if(es_var(temp)) //Revisamos que despues de IN este el nombre de una variable { permiso = 1; return; } else { permiso = 0; error("se esperaba una variable"); return; } } else if(es_cadena(temp)) //Revisamos si es una cadena { char comilla = temp; do { ++i; temp = buffer[i]; } while(!comparar(comilla,temp)); //Revisamos si la cadena termino } else if(!strchr("ABCDE0123456789+-*/()_=?!=&",temp)) //Vemos si es un caracter valido { permiso = 0; error("token no reconocida"); return; } i++; temp = buffer[i]; } permiso = 1; } void ejecutar(int pos) //Ejecuta la expresion { char c = buffer[pos]; if(es_var(c)) asignar(c,pos+1); //asignacion else if(es_out(c)) output(pos+1); //output else if(es_in(c)) input(pos+1); //input else if(es_comentario(c)); //comentario } int obtener_var(char c) { switch(c) { case 'A': return vars.A; case 'B': return vars.B; case 'C': return vars.C; case 'D': return vars.D; case 'E': return vars.E; } return 0; } void poner_var(char c, int n) { switch(c) { case 'A': vars.A = n; break; case 'B': vars.B = n; break; case 'C': vars.C = n; break; case 'D': vars.D = n; break; case 'E': vars.E = n; break; } } int subexpresion(int pos) { int num = 0; int i = pos; char actual = buffer[i]; if(actual==PARI) //Estamos entre parentesis? { num = expresion(i+1); //Calculamos toda la expresion entre parentesis i = posicion; actual = buffer[i]; if(actual!=PARD) //Si no encontramos el cierre de parentesis { if(actual!=FINBUF) { error("se esperaba \')\'"); posicion = i; return temp_var; } error("se esperaba \')\'"); posicion = i; return temp_var; } else //Encontramos el cierre de parentesis { i++; posicion = i; return num; } } else if(es_var(actual)) //Es una variable? { num = obtener_var(actual); i++; posicion = i; return num; } else if(isdigit(actual)) //Es un numero? { char c = actual; while(isdigit(c)) { i++; num = 10*num+c-'0'; c = buffer[i]; } posicion = i; return num; } else if(actual==FINBUF) //Llegamos al final del buffer? { error("expresion invalida"); } posicion = i; return temp_var; } int multi_divi(int pos) //Calcula multiplicaciones y divisiones { int num = subexpresion(pos); int i = posicion; char actual = buffer[i]; if(actual!=FINBUF) while(por_entre(actual)) //Mientras encontremos multiplicacion o division { switch(actual) { case MUL: num*=subexpresion(i+1); break; //Multiplicar case DIV: num/=subexpresion(i+1); break; //Dividir } i = posicion; actual = buffer[i]; } return num; } int expresion(int pos) //Calcula el valor de una expresion { int num = 0; int i = pos; char actual = buffer[i]; char continuar = 0; if(isdigit(actual)) //es un numeto? { while(isdigit(actual)) //Mientras encontremos digitos { i++; num = 10*num+actual-'0'; actual = buffer[i]; } continuar = 1; } else if(es_var(actual)) //Es variable { i++; num = obtener_var(actual); actual = buffer[i]; continuar = 1; } else if(actual==PARI) //Es un parentesis? { actual = buffer[i-1]; if(strchr("+-*/=",actual)) num = subexpresion(i); //Obtener subexpresion else { error("expresion incorrecta, se esperaba: +-*/="); return temp_var; } i = posicion; actual = buffer[i]; continuar = 1; } if(continuar) //Podemos continuar? { if(actual==FINBUF) return num; while(mas_menos(actual) || por_entre(actual) || es_parentesis(actual)) //Operar { switch(actual) { case SUM: num+=multi_divi(i+1); break; case RES: num-=multi_divi(i+1); break; case MUL: num*=multi_divi(i+1); break; case DIV: num/=multi_divi(i+1); break; } i = posicion; actual = buffer[i]; } posicion = i; return num; } return num; } char asignar(char c,int pos) { if(buffer[pos]==ASIG) //Si es asignacion { switch(c) { case 'A': temp_var = vars.A; vars.A = expresion(pos+1); break; case 'B': temp_var = vars.B; vars.B = expresion(pos+1); break; case 'C': temp_var = vars.C; vars.C = expresion(pos+1); break; case 'D': temp_var = vars.D; vars.D = expresion(pos+1); break; case 'E': temp_var = vars.E; vars.E = expresion(pos+1); break; } return 1; } else error("se esperaba \'=\' para asignacion"); return 0; } int out_cadena(int inicio, char comilla) //Pone una cadena en pantalla { int i = inicio+1; int j = 1; char c; do { c = buffer[i]; if(c!=comilla && c!=NL) printf("%c",c); //Mientras no encontremos la comilla y no sea \ j++; i++; } while(c!=comilla && c!=FINBUF); if(es_cadena(c)) return j; //La cadena es valida else return 0; //Cadena invalida } void output(int pos) //Pone en pantalla { char c; int i = pos; while(i<=(total-1)) { c = buffer[i]; if(c!=FINBUF) //Mientras no termine el buffer { if(es_var(c)) //Es variable { printf("%i",obtener_var(c)); //mostrarla i++; } else if(es_cadena(c)) //Es una cadena { int caracteres = out_cadena(i,c); if(caracteres==0) { printf("\ "); error("cadena invalida, posiblemente falto terminarla"); return; } i += caracteres; } else if(es_salto(c)) //si encuentra un SNL en la expresion { printf("\ "); i++; } else { error("expresion invalida en output"); return; } } else return; } printf("\ "); } void input(int pos) //Asigna un valor a una variable dependiendo del input del usuario { int num = 0; char c; char v = buffer[pos]; if(es_var(v)) //revisamos que es una variable { printf(":"); while(!isdigit(c=getchar()) && c!=NL); while(isdigit(c)) //obtenemos numero { num = 10*num+c-'0'; c = getchar(); } poner_var(v,num); //Ponemos el valor a la variable while(c!=NL) c = getchar(); //todo lo demas es ignorado } }
Compilar
Para compilarlo en GCC:
gcc -Wall -O mini.c -o mini
Tutorial del uso del interprete
Asignacion
En esta versión del interprete se dispone de solo 5 variables para guardar datos numericos, las variables disponibles son A,B,C,D y E.
Para asignar una variable se escribe el nombre de la variable seguida por el caracter = que indica asignación.
Ejemplo:
A=200
Operaciones
Hasta ahora el interprete reconoce + (sumas) - (restas) * (multiplicaciones) / (divisiones). También pueden utilizarse los parentesis.
Ejemplo:
A=200+(2*2+(2+3))
Output en pantalla
Para mostrar datos en pantalla se utiliza el signo de admiración """!""" seguido por una cadena, una variable o el indicador de salto de linea ("""_""").
Ejemplo de cadena:
!"Hola Mundo"
Ejemplo de variable:
!"A es igual a "A
Esto pondrá en pantalla (suponiendo que A contiene el valor 100):
A es igual a 100
Ejemplo, muestra el contenido de todas las variables:
!A_B_C_D_E
Esto mostrará el valor de cada variable, el simbolo de salto de linea (newline) que se usa es: """_""".
Input del teclado
Para obtener datos del teclado se utiliza el caracter ? y el nombre de la variable a la que se le asignará el valor ingresado.
Ejemplo:
!"Ingrese un numero:" ?A
Pide el ingreso de un numero para asignar a la variable A.
Terminar el programa y salir del interprete
Para terminar el programa usamos el punto .
Notas
Este es un interprete sencillo, aún falta que reciba un archivo de texto en la linea de comandos para ejecutarlo, faltan el condicional IF, y los loops FOR, WHILE...
Hasta la próxima!