Nota: Cuando utilicé "complejo" en el título, quiero decir que la expresión tiene muchos operadores y operandos. No es que la expresión en sí misma sea compleja.
Recientemente he estado trabajando en un compilador simple para ensamblar x86-64. Terminé el front-end principal del compilador, el lexer y el analizador, y ahora puedo generar una representación de árbol de sintaxis abstracta de mi programa. Y dado que mi idioma se escribirá de forma estática, ahora estoy haciendo la siguiente fase: escribir comprobando el código fuente. Sin embargo, me he encontrado con un problema y no he podido resolverlo razonablemente.
Considere el siguiente ejemplo:
El analizador de mi compilador ha leído esta línea de código:
int a = 1 + 2 - 3 * 4 - 5
Y lo convirtió al siguiente AST:
=
/ \
a(int) \
-
/ \
- 5
/ \
+ *
/ \ / \
1 2 3 4
Ahora debe escribir check the AST. comienza por el primer tipo que verifica el =
operador. Primero verifica el lado izquierdo del operador. Ve que la variable a
se declara como un entero. Por lo tanto, ahora debe verificar que la expresión del lado derecho se evalúe como un número entero.
Entiendo cómo se podría hacer esto si la expresión fuera solo un valor único, como 1
o 'a'
. Pero, ¿cómo se haría esto para expresiones con múltiples valores y operandos, una expresión compleja , como la anterior? Para determinar correctamente el valor de la expresión, parece que el verificador de tipo realmente tendría que ejecutar la expresión en sí misma y registrar el resultado. Pero esto obviamente parece anular el propósito de separar las fases de compilación y ejecución.
La única otra forma en que imagino que esto podría hacerse es verificar de forma recursiva la hoja de cada subexpresión en el AST y verificar que todos los tipos de la hoja coincidan con el tipo de operador esperado. Entonces, comenzando con el =
operador, el verificador de tipos escanearía todo el AST del lado izquierdo y verificaría que todas las hojas sean enteras. Luego repetiría esto para cada operador en la subexpresión.
He intentado investigar el tema en mi copia de "El libro del dragón" , pero no parece entrar en muchos detalles, y simplemente reitera lo que ya sé.
¿Cuál es el método habitual que se utiliza cuando un compilador es tipo de comprobación de expresiones con muchos operadores y operandos? ¿Se utiliza alguno de los métodos que mencioné anteriormente? Si no, ¿cuáles son los métodos y cómo funcionarían exactamente?
double a = 7/2
trataría de interpretar el lado derecho como doble, por lo tanto, trataría de interpretar el numerador y el denominador como doble y convertirlos si fuera necesario; como resultado a = 3.5
. El ascendente realizaría la división de enteros y convertiría solo en el último paso (asignación), entonces a = 3.0
.
int a = 1 + 2 - 3 * 4 - 5
, pero aint a = 5 - ((4*3) - (1+2))