Las ideas son el plato fuerte del día.
Primeramente, resumiendo tu proyecto: estás diseñando un compilador interpretado al estilo de java, generas código intermedio que equivaldría a los bytecodes pero en este caso para un lenguaje ensamblador. Dicho esto, ideas y anotaciones que se me ocurren:
-Puedes tomar como referencia
MIPS o DLX, son lenguajes ensamblador muy completos, legibles sintácticamente hablando y programados de una manera muy eficiente; además tienes mucha documentación por internet.
-Cuando tengas avanzado el lenguaje, lo normal es que programes pequeñas aplicaciones para comprobar que todo funciona correctamente; ahora si, también debes saber que esa fase se utiliza para
averiguar si hay algún dato o valor que se use repetidamente en las aplicaciones en general y así declararlo como una constante en un registro. El más común y que habremos visto todos en el 90% de lenguajes ensamblador es el famoso
Registro Cero que siempre tiene el valor "0" porque resulta sobradamente útil para hacer comparaciones en bucles, copiar registros, etc.
-Quizá de las cosas más pesadas que te vas a encontrar es tratar en todas las instrucciones los espacios/tabulador y saltos de línea. Por ejemplo, has comentado que las funciones se definen del siguiente modo:
%nombredefuncion,parametro1,param...%
Instrucciones
%end nombredefuncion%
¿Y si entre la coma y el parámetro dejo un espacio?, ¿y si dejo 10 espacios?.
Esto me recuerda: ¿Al final has utilizado las expresiones regulares para el analizador léxico-sintáctico como te comenté en el otro post?, si es así no vas a tener problemas para reconocer comentarios, saltos de línea sin ningún valor para el programa, espacios seguidos, etc.
-El anterior punto es bastante importante porque cuando generes el bytecodes tienes que procurar que sea un fichero pensado
únicamente para su comprensión por parte del PC (como no usa un compilador sino un compilador interpretado realmente hablamos de la máquina virtual); es decir,
los comentarios, espacios, saltos de línea, y demás carácteres que solo ayudan a la legibilidad del programador deben ser eliminados al crear el fichero intermedio (bytecode u opcode), así harás el programa más ligero y rápido cuando llegue el momento en que la máquina virtual lo interprete y ejecute las instrucciones.
-¿Las instrucciones son monoformato o multiformato?.
-Has escrito algo muy importante pero un poco implícito en el mensaje, por si acaso no le has dado vueltas te lo comento para que lo tengas en cuenta: Debes organizar el lenguaje de manera que:
1- Tenga el menor número posible de instrucciones.
2- Las instrucciones realicen operaciones básicas. A partir de instrucciones aritmetico-lógicas elementales, el programador construirá otras más complejas.Estos dos puntos harán que la ejecución de los programas creados con tu lenguaje sean 100 veces más eficientes. Como se podrán incluir librerías o API's, para mantener la eficiencia que conseguimos en el apartado anterior te recomiendo que:
1- La inlusión de estas librerías sea totalmente opcional.
2- Su especificación e
implementación sea libre y cualquiera pueda ver cómo está diseñada; esto permite que un programador de tu lenguaje pueda comprobar si la manera en que está implementada la librería merece la pena incluirla o no (lleva demasiadas funciones (o tareas en ensamblador) y prefiere crear él mismo las que necesita);
parece una tontería desde el punto de vista de un lenguaje de alto nivel pero estamos hablando de un lenguaje ensamblador.-No me ha quedado claro si vas a incluir una instrucción para copiar un registro a otro, pero resultaría una operación sobrecargada, es decir, puedes hacer lo mismo con otras más básicas:
r0; <- Registro Cero
addr r2, r1, r0; <- Hemos copiado el valor de r1 en el r2
-Para intercambiar registros se puede usar un tercero que sirva de auxiliar o la pila, tampoco merece la pena crear una operación solo para eso.
Un proyecto genial, espero que vayas comentando según avances. Si se me ocurre algo más lo posteo; plantea cualquier duda que tengas.
Un saludo.
EI: juntando mensajes.
Nueva anotación:
Si aún así te da por incluir operaciones complejas existe un método para mantener la eficiencia: "pre-traductor" (no precompilador). Es un proceso que se ejecuta antes del compilador y que se dedica a traducir las operaciones complejas a otras más elementales. Por ejemplo, el programador diseña el siguiente código:
addr r2, r1, r3
sub r2, r4, r5
operacion_compleja
addr r2, r1, r3
Antes de que se ejecute el compilador un proceso debe traducir la operación a su equivalente en instrucciones básicas:
addr r2, r1, r3
sub r2, r4, r5
addr r0, r1, r4 //operacion_elemental 1
setr r0, 8 //operacion_elemental 2
... //operacion elemental x
addr r2, r1, r3
Seguro que te suena esto y resulta que se trata de la extensión que pones en la consola de comandos cuando quieres ejecutar un compilador de ensamblador para que aplique ténicas como: desenrrollado de bucles, intercambio de iteraciones, arrays combinados, etc. aunque lo que te he comentado mucho más sencillo que cualquiera de estas.
Ya nos dirás qué camino has seguido en tu proyecto.
Un saludo.