Habría mucho por comentar, pero tampoco quiero hacer un mensaje demasiado largo, así que he obviado cosas, respondiendo simplemente a algunas de tus dudas...
1 - Preprocesamiento (directivas de preprocesado):El preprocesador, actúa en la fase primera de la compilación, de hecho empieza en el analizador léxico.
En VB6 hay algunas directivas de preprocesameinto, seguramente las hayas visto en algún código:
Supongamos que tienes que acceder a la hora del sistema y para ello debes invocar una librería, la cual dependerá del sistema operativo destino, en éste caso, la directiva tiene su fundamento, en que compilará la parte perteneciente al sistema operativo vigente a la hora de solicitar la compilación.
Tú decides si vas a compilar para Linux, o para Microsoft (por ejemplo), cambiando el valor de la contante antes de compilar...
#const Os as string = "Microsoft"
public Function GetLocalTime as string
dim hora as string
#If Os = "Microsoft" then
hora = ...
#Elseif Os = "Linux" then
hora = ...
#end if
GetLocalTime = hora
End function
No sólo eso, también por ejemplo para incluir macros...
Supongamos que facilitas la posibilidad de tener 'snippets' (pequeños trozos de código, para tareas frecuentes, pero demasiado largas para escribirlas cada vez).
Una forma sencilla de preprocesamiento, sería porejemplo crear directivas de snippets.
Así esto:
#snippet = "C:\Mi lenguaje\snippets\copypaste.snippet"
Podría incluir ahí código de forma automática, durante la compilación, yendo a la ruta concreta, leyendo el fichero y pegando el contenido íntegro en donde aparece esa directiva.
Otra forma podría ser funciones que no tienes en librerías, sino que se adjuntan con el ejecutable, así por ejemplo podría tener una función en forma de directiva de esta forma:
#funcion = Ordenar (elarray)
que básicamente podría ser una función (o sub) llamada ordenar, que recibe como parámetro un array...
Y lo que hará el compilador será remplazar esa línea por el código de la función con el identificador correcto del parámetro (que tiene la línea) a la hora de compilar.
Y esto como alternativa a invocar una función ordenar en una librería externa. Uno sabrá qué razones serían precisas para elegir dicha forma... (que a veces son precisas).
Fíjate que en este último caso, no se indica ruta, el formato lo decides tú, podría constar en un fichero, pero quizás si sean pocas (funciones), decidas cargarlas en memoria todas y por tanto luego basta el nombre para localizarla en una colección en vez de en un fichero.
Las directivas, en definitiva facilitan escribir el código, y se le informa a la fase de compilación que debe hacer algo especial distinto al resto del código con el contenido de la directiva.
2 - Continuadores de línea:Concuerdo contigo, que los continuadores de línea, son desagradables, sin embargo es preciso incluirlos si respondes afirmativamente a esta cuestión:
Nunca hay que olvidar de vista, si al hacer un compilador lo haces exclusivamente para tí, o para que también sea usado por otros. En tu caso es lo segundo?. Si es esto segundo, debes hacerlo porque habrá mucha gente que lo heche en falta (aunque otros lo odien), si es lo primero, puedes excluirlo.
De todos modos, para un ejercicio de publicación (como es los vídeos que vas a realizar) puedes omitirlo, y ya lo dejarás pendiente para otra ocasión. En general un compilador es siempre la mejora de una versión previa...
En BV6, por ejemplo, el continuador de línea es " _" esto es un espacio seguido de un guión bajo, esto no solo es para las expresiones (líneas de código9, es así incluso para los comentarios (cosa que el 99'995 ignora).
Así esto es válido en vb6:
' Aquí empieza un comentario multilínea _
_
esto también sigue la línea de comentarios _
continuador de línea espacio y _
_
y aquí acaba, a que parece extraño ver una línea de comentario que no empieza por un apóstrofo?
OJO: los continuadores de línea, no deben dejar líneas en blanco, sin su correspondiente 'token':
continuadorLinea = " _" CRLF
Una línea en blanco, siempre se debe interpretar como espacios, como comentario, excepto que esté entre comillas, porque entonces forma parte del valor de dicha cadena de texto.
Es decir esto otro:
"valor de los días d ela semana
Lunes
Martes
...
Domingo"
No es continuador de línea es el valor de una cadena de texto, que contiene caracteres CRLF, igual que podría contener cualquier otro carácter...
3 - Operadores:Los operadores limítalo a los propios del procesador (incluyendo shifts), el resto considero conveniente dejarlo como funciones internas añadidas al lenguaje (quizás en una clase Math):
Porciento(Valor, Porcentaje)
Logaritmo(Base, valor)
Random(Maximo, Minimo)
Coseno(angulo)
...etc...
Más preciso aún... (ya que tratas de hacer un compilador orientado a objetos), es que cada función pueda ser nativa del tipo de datos específico que es.
No tengas apuros en crear operadores sinónimos:
éste: ">=", lo mismo que éste otro: "=>"
y éste: "><" puede ser lo mismo que "<>" y lo mismo que "!="
...esto de cara al analizador léxico, luego internamente será solo uno cuando se convierta a código intermedio.
4 - Tabla de atributos en tu lenguaje para todos los caracteres ASCII: Es adecuado, que antes de nada, crees un array con todos los caracteres ASCII, y que a cada uno le des valores conforme al tipo de carácter que es, así, por ejemplo "A", es un carácter alfabético, es un caracter mayúsculas y también es un figito en la base hexadecimal. Esto facilita, que debes hacer después con cada carácter, si evaluas un identificador, "A" es un carácter válido, y si evaluas un número "A" puede o no ser un dígito válido en función de si se trata o no de un número en base hexadecimal.
5 - Especificación del lenguaje: Por último y más importante, lo primero a la hora de crear un compilador es crear la especificación del lenguaje. Es una tarea que puede llevar meses, ahora bien si la basas en otro lenguaje ya conocido, al que sólo cambias determinadas cosas (como creo que es el caso), se puede omitir casi todo, y tan solo especificar esa lista de cambios... por ejemplo como señalabas para utilizar ciertos caracteres diferentes como remplazo de los operadores que incorpora VB6, por ejemplo...
la especifciación de un lenguaje empieza por cosas simples como lo que es un dígito, un número, y para ello lo mejor es el BNF.
digito = "0"|"1"|"2"|"3"|"4"|"5"|"6"|"7"|"8"|"9"
digitos = digito [digitos]
numero = ["+"|"-"] digitos
numeroFlotante = numero ["." numero] *** podría tambén admitirse la notación científica de E.
digitoHexadecimal = digito|"A"|"B"|"C"|"D"|"E"|"F"|"a"|"b"|"c"|"d"|"e"|"f"
digitosHexadecimal = digitoHexadecimal [digitosHexadecimal]
numeroHexadecimal = "&Hx" digitosHexadecimal *** esto es, ahora sabrás que "A" es un dígito válido, porque hay un 'prefijo' para indicar la base numerica hexadecimal, en ausencia debe considerarse decimal.
caracterMayuscula = A-Z, a-z *** es otra forma de indicar "A"|"B"|...."Y"|"Z"
caracterMinuscula = a-z
caracterAlfabetico = caracterMayuscula|caracterMinuscula
caracterAlfanumerico = caracterAlfabetico|digito
caracteresAlfanumericos = caracterAlfanumerico [caracteresAlfanumericos]
identificador = caracterAlfabetico caracteresAlfanumericos *** esto es debe empezar obligatoriamente por una letra y ser lo largo que sea.
Entonces puedes llegar a la declaración de variables así:
byte = identificador "byte" ["=" numero]
El analizador kléxico, verifica que se escribe bien, el sintáctico, que todo está en orden, que no tiene nada extraño, ni le falta algo, el semántico, verifica que si se inicializa un valor esté en el rango del byte (0-255), y si no se inicializa expresamente con un valor le asigna el valor 0 (el valor por defecto)
Y así vas especificando todo el lenguaje, pero como mínimo si te basas en otro, especificas aquello que cambie, o aquello donde necesites claridad para en adelante no equivocarte...
Por ejemplo puede definir un bucle 'For... next' en español, así:
hasta = “Hasta” exprNumerica [“Salta” exprNumerica] comentFinLinea
itera = “Itera” identificador “Desde” exprNumerica
itera1 = itera hasta
itera2 = itera comentFinLinea
declaracionItera = (itera1|itera2)
repite1 = “Repetir” comentFinLinea
repite2 = “Repetir” hasta
*** básicamente señala que el cuerpo interno del bucle puede contener expresiones y también la sentencia "SalirDeAqui" (del bucle), en cualquier parte.... expresiones debe definirse en otra parte, tal que sea la suma de expresiones admisiblea estar dentor del cuerpo de un bucle (incluso otro bucle, pero no una función, por ejemplo)
exprSalirDeAqui = ["SalirDeAqui"] expresiones ["SalirDeAqui"] [exprSalirDeAqui]
bloqueItera = (itera1 exprSalirDeAqui repite1|itera2 exprSalirDeAqui repite2)
Y conviene adjuntar ejemplos de lo que se admite y comentar si algo en espacila no se admite.
Estos ejemplos serían validos con la especificación recién dada:
Itera k Desde 5 Hasta 40
SalirDeAqui *** esta producción no se ha descrito...
Mostrar k *** esta producción no se ha descrito...
SalirDeAqui *** es el típico 'exit for', un goto a la dirección siguiente a 'repetir (un jump incondicional a una dirección)
Repetir
Itera cuenta Desde 40 Hasta 5 Salta -2
Mostrar cuenta
Repetir
Itera k Desde 5
Mostrar k
Repetir Hasta 40 Salta 5
Itera k Desde 40
Mostrar k
Si ((k and X) = j) luego SalirDeAqui
Repetir Hasta 5 Salta -8
En BNF, cada 'variable' a la izquierda, se llama producción, y es una equivalencia de todo lo que está a la parte derecha del "=". Hay cosas que nunca están en la parte izquierda, se llaman símbolos terminales, y no se describen porque están al punto sumplificados que son 'autoentendibles', típicamente son los caracteres, o alguna secuencia especial de ellos, basta ponerlo entre comillas o en negrita (yo prefiero ambas cosas, porque al portarlo a otro sitio que no admite negritas, desaparece el formato, si queda entre comillas, todavía es claro...
...y bueno, al final me he extendido, más de lo que pretendía...