Creando un lenguaje de programación | Blog #1

Investigando un poco por mi cuenta sobre cómo crear un interprete, ya que si se tiene esa parte, después es fácil tener el lenguaje interpretado corriendo.
Mientras que uno compilado, una vez que tenes el interprete, generas código ensamblador, para que el ensamblador genere el código máquina (los .o), y el “linker” genere el ejecutable a partir de los .o (el código máquina), que le diga al sistema cómo tiene que cargar el código y datos a memoria, para hacerle el “jump” (jmp en assembly), a la función o rutina _start. Con lo qué se puede ver porque los ejecutables son dependientes de la plataforma, ya que tienen formas diferentes de cargar el código y datos a memoria, aunque las instrucciones de código máquina sean las mismas (mismo procesador).

Con lo que, experimentando con crear el autómata que de funcionamiento al lenguaje, primero cree una función que me diga si un carácter es una letra.

bool __isLetter(char l)
{
	for(char i='a'; i<='z'; i++)
		if(l==i) return true;
	for(char i='A'; i<='Z'; i++)
		if(l==i) return true;
	for(char i='0'; i<='9'; i++)
		if(l==i) return true;
	return false;
}

A través de tres bucles consecutivos simples.
Luego, una función que me de una palabra con esa función, que la devuelve cuando encuentra un carácter que desconoce, junto con el tamaño de la misma.

char* __follow(const char *const buffer,int *n)
{
	int i=0;
	while(__isLetter(buffer[i])) i++;
	char *follow = (char*)malloc(i);
	i=0;
	while(__isLetter(buffer[i])){
		follow[i] = buffer[i];
		follow[i+1] = 0x00;
		i++;
	}
	*n=i;
	return follow;
}

Hasta aquí la parte organizada, el resto del código que use para probar ambas funciones dan pena. Pero iré comentando los avances con la estructuración del código.

¿Que opinan?

2 Me gusta

Tengo competencia?, bueno yo tengo un intérprete llamado lima ya esto me presiona a terminarlo y llegar antes de que el motor germond

Si el intérprete tiene más popularidad me va a dar algo

Por el momento el mío se llaman Hispa (Hispanidad).
Y avanza lento, solo tiene la base del autómata.
Cuando termine con el analizador léxico, empieza recién su API o STD.
Aunque se vean unos “bool”, está en C.

1 me gusta

Ya ya casi termino mi std y también el intérprete pero el mio interpreta las instrucciones línea por linea

El mío no, por cómo funciona mi entendimiento de la máquina, decidí que lo mejor es byte por byte.
Aún así sé cuando termina una linea y empieza otra, ya que hay un byte que es 0x0A, o 10, que es el salto de linea…

Que impresionante, espera Noooo mi populariiidaaaa nooo se le va a ir al caño la popularidad e Lima :sob:, es mejor que el mio, bueno, ojalá resiva muchos likes no como el mio

El tuyo va más avanzado, además, está serie estoy explicando cómo lo voy armando, en los bloques de código centrales.
Cuando tenga armado algo decente, veo de publicar el código fuente, para que cualquiera lo estudie, si quiere…

1 me gusta

ya erick no te peles

1 me gusta

no le hagas caso xd, se esucha muy interesante hispa.
sigue asi y vas hacer como terry devis

yo:
bueno es que lima tiene no tiene popularidad y pues que le vaya mejor a algo que es casi lo mismo para mi es muy , xd
ni yo se que hago mal que me pasan esas cosas

Hola, antes de todo, espero que te vaya bien en tu proyecto

Solo quería comentarte que puedes evitar los bucles en la función __isLetter() con unas comparaciones simples:

bool __isLetter(char l) {
    return (l >= '0' && l <='9') || (l >= 'A' && l <='Z') ||  (l >= 'a' && l <='z');
}

Los bucles pueden afectar mucho al rendimiento de tu programa. Un char por dentro no deja de ser un valor numero, por lo que puedes aprovechar y realizar comparaciones simples.

Esto es un detalle sin importancia, en __follow(), puedes asigna el 0x00 después de terminar el bucles, de esta manera:

while(__isLetter(buffer[i])) {
    follow[i] = buffer[i];
    i++;
}
follow[i] = '\0';

Y por ultimo, puedes simplificar la función __follow() evitando asignación de memoria, ya que estas creando asignaciones que pueden ser pequeñas. Esto es un ejemplo con la función llamas a strncpy(). Ejemplo:

uint32_t  __follow(const char *buffer)
{
    uint32_t i=0;
    while(__isLetter(buffer[i])) {
        i++;
        if (i > 255) return 0; // Esto es simplemente para evitar palabras demasiado largas, y que acabes con un bucle infinito. Puedes elegir el valor que desees.
    }
    return i;
}

int main(...) {
    char buffer[1024];
    const char* test = "hello";
    uint32_t len = __follow(test);
    strncpy(buffer, test, len);
}

Hay mil y un maneras de hacer las cosas, es la gracia de la programación. Esto son recomendaciones, puedes usarlas, mejorarlas o descartarlas.

Saludos y muchos ánimos.

Muy buenas recomendaciones, sin los bucles aumenta considerablemente el rendimiento, y ya que está es una función “base” o “hoja”, es una de esas que se ejecutan gran número de veces. Ya que me da las palabras, para luego hacer las comparaciones en el lenguaje.
Es interesante tener un limitante para evitar bucles infinitos, o casi (es lectura de un archivo).

La función __isLetter y __follow son para la parte:
Cuando entra el programa, el interprete esta en el estado 0, hay entra __isLetter, si es, sigue hasta juntar una palabra, hay __follow, si da negativo, es otro carácter, y hará lo que tenga que hacer, si no es ningún carácter especificado, da error y cierra el interprete.

Con lo qué, parte en estado 0, si es letra, pasa a estado 1 para juntar una palabra, y pasar a estado 2 para ver qué palabra, sino a estado de error. Si no es una letra, pasa a estado 3, revisa si es un carácter existente, sino, a estado de error.

Esa sería la lógica del interprete.
Cuando arme algo decente, lo subo a github, y ahí acepto colaboradores con derecho a editar el código (si se gana mi confianza para el proyecto, con colaboraciones como esta, gracias), si esta alguien interesado.

Por el momento, una prueba del funcionamiento del interprete, aún no tengo claro, bien, que sintaxis darle…

marco@marco:/d/Escritorio/Git/hispa$ cat hola.txt 
print ("HolaMundo")
print ("QueTal")
print ("Probando")
print ("HolaMundoProbando1234567890")
marco@marco:/d/Escritorio/Git/hispa$ ./g hola.txt 
HolaMundo
QueTal
Probando
HolaMundoProbando1234567890

Por último, está función es la que permite convertir un archivo de texto a una cadena:

char* __read(const char *file)
{
	FILE *f = fopen(file,"rb");
	fseek(f,0,SEEK_END);
	long s = ftell(f);
	fseek(f,0,SEEK_SET);
	char *buffer = (char*)malloc((int)s);
	fread(buffer,(int)s,1,f);
	fclose(f);
	return buffer;
}

Junto con el código fuente del pre prototipo:

#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <string.h>

char* __read(const char *file)
{
	FILE *f = fopen(file,"rb");
	fseek(f,0,SEEK_END);
	long s = ftell(f);
	fseek(f,0,SEEK_SET);
	char *buffer = (char*)malloc((int)s);
	fread(buffer,(int)s,1,f);
	fclose(f);
	return buffer;
}

bool __isLetter(char l) {
    return (l >= '0' && l <='9') || (l >= 'A' && l <='Z') ||  (l >= 'a' && l <='z');
}

char* __follow(const char *const buffer,int *n)
{
	int i=0;
	while(__isLetter(buffer[i])) i++;
	char *follow = (char*)malloc(i);
	i=0;
	while(__isLetter(buffer[i])){
		follow[i] = buffer[i];
		follow[i+1] = 0x00;
		i++;
	}
	*n=i;
	return follow;
}

bool __print(const char*const _string,int *n)
{
	int i=0;
	char const* string = _string;
	if(*string=='('){
		string++;
		if(*string==0x22){
			string++;
			i=0;
			puts(__follow(string,&i));
			string+=i;
			if(*string==0x22) { *n=i; return true; }
			else return false;
		}
	}else if(*string==' '){
		string++;
		__print(string,n);
	}else return false;
	return true;
}

bool ___interpeter(const char*const buffer)
{
	int i=0;
	const char* copy = buffer;
	// read first char on file
	if(!__isLetter(copy[0])){
		// Token reserved for system
		if(copy[0]=='#'){
		}else if(copy[0]=='/'){
		}else{
			puts("Character unknow!");
			return false;
		}
	}
	// read first follow on file
	char *follow = __follow(copy,&i);
	copy+=i;
	// API std-hispa
	int n=0;
	if(!strcmp(follow,"print")){
		__print(copy,&n);
		copy+=n+5;
	}else if(!strcmp(follow,"")){
	}else if(!strcmp(follow,"")){
	}else{
		puts("Token unknow!");
		return false;
	}
	if(copy[0]==0x0A) {
		if(copy[1]=='\0') return true;
		___interpeter(copy+1);
	}
	return true;
}

bool __interpeter(const char*const buffer)
{
	int i=0;
	const char * copy = buffer;
	char *follow = __follow(copy,&i);
	copy+=i;
	if(!strcmp(follow,"print")){
		if(copy[0]==' '||copy[0]=='('){
			if(copy[1]==0x22){
				free(follow);
				copy+=2;
				follow = __follow(copy,&i);
				puts(follow);
				free(follow);
				copy+=i;
				if(copy[0]==0x22){
					if(copy[1]==0x0A)
						copy+=2;
						__interpeter(copy);
				}
			}
		}else return false;
	}else return false;
	return true;
}

int main(int argc,char**argv)
{
	char *buffer = __read(argv[1]);
	___interpeter(buffer);
	free(buffer);
	return 0;
}