MINI INTERPRETE 001

From Movaxes

(Difference between revisions)
(Obtener())
(Obtener())
Line 22: Line 22:
l)''', cualquier espacio, tabulador o '''(@)''' es omitido.
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 indicar el final.
+
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()===
===Mostrar()===

Revision as of 16:27, 11 March 2007

Contents

Version 0.001

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!

Personal tools