Lección 3: Análisis Semántico 1. 2. 3. 4. 5. Expresiones y Asignación Estructuras de Control Procesamiento de Declaraciones Procedimientos y Funciones Perspectiva Lecturas: Cooper, capítulo 4 Scott, capítulo 4 Aho, capítulo 8 Fischer, capítulos 10, 11 , 12 , 13, 14 Holub, capítulo 6 Bennett, capítulo 4, 10 12048 - J. Neira – Universidad de Zaragoza 1 1. Expresiones y Asignación ¿ 5+2*4 ? ¿ 5-2-4 ? ¿ a = b = c (en C)? ¿ a ** b ** c (en ADA)? ¿ a < b < c (en C)? ¿ a > 0 and b > 0 (en PASCAL)? 12048 - J. Neira – Universidad de Zaragoza 2 Operadores • Precedencia: prioridad de los operadores; determina qué operadores obtienen sus operandos primero + 5 + 5 * 6 / 10 • Se altera con paréntesis ((5 + 5) * 6) / 10 / or Men encia ed prec 10 * 5 / 6 + equivalente a: 5 + ((5 * 6 )/ 10) * M prec ayor ede ncia 10 5 Mayor precedencia ADA: 5 6 logical_operator ::= relational_operator ::= binary_adding_operator ::= unary_adding_operator ::= multiplying_operator ::= highest_precedence_operator ::= 12048 - J. Neira – Universidad de Zaragoza 5 and | or | xor = | /= | < | <= | > | >= + | - | & + | * | / | mod | rem ** | abs | not 3 Operadores • Asociatividad: determina el orden de evaluación de operadores de igual precedencia • De izquierda a derecha: x + 3 - y + 5 (x + 3) - y + 5 ((x + 3) - y) + 5 (((x + 3) - y) + 5) • De derecha a izquierda x = y += z -= 4 x = y += (z -= 4) x = (y += (z -= 4)) (x = y += (z -= 4)) = x += y -= + 5 - 3 12048 - J. Neira – Universidad de Zaragoza 4 • Algunos NO son asociativos: ADA: y + x z A**B**C (A**B)**C A**(B**C) 4 Operadores Operator Name = i && i = f() && g(); f() g() • En algunos casos NO está definido 1 1 0 , 0 1 ++ 1 [] 1 1 i 1 i x 1 12048 - J. Neira – Universidad de Zaragoza func2(++i, x[i]); ¿? Mayor precedencia • Orden de evaluación: determina qué operandos se evalúan primero x x ! ! x c c c 5 x C: Associativity Operators Primary Unary left to right right to left Multiplicative left to right * / Additive left to right + - Bitwise Shift left to right << Relational left to right < () ++ [ ] . -> -- + % >> > Equality left to right == Bitwise AND left to right & Bitwise Exclusive OR left to right ^ Bitwise Inclusive OR left to right | Logical AND left to right && Logical OR left to right || Conditional right to left ? : Assignment right to left = Comma left to right , <= >= != += -= *= /= <<= >>= int x = 11; int y = 6; > 9 && y != 3 int z = 1; == 5 || y != 3 char c = 'k'; • Ejercicio: char d = 'y'; x > 14 (x > 9 && y != 23) <= 1 && y == 6 || z < 4 >= 'a' && c <= 'z' >= 'A' || c <= 'Z' != d && c != '\n' && y != 8 || 0 >= y >= z 5 ( http://libre.act-europe.fr/Software_Matters 12048 - J. Neira – Universidad de Zaragoza 6 ¿es correcto este programa? /* Si *y es cero, x > 0, asignar a *k el mayor entero. * Si *y es cero, x <= 0, no cambiar *k. * Si *y no es cero, asignar a *k x dividido por *y e incrementar *y en 1. */ #include <limits.h> void check_divide (int *k, int x, int *y) if (*y = 0) if (x > 0) *k = INT_MAX; else *k = x / *y; *y++; } 12048 - J. Neira – Universidad de Zaragoza { 7 = .vs. == • La elección de “=“ para la asignación, y de “==“ para la igualdad fue muy desacertada. • No todos los compiladores advierten del uso de ‘=‘ en condiciones. • Es difícil, C++ fomenta su uso: while (*s1++ = *s2++); • Precedencia del ++ mal definida: *y++ equivale a *(y++) 12048 - J. Neira – Universidad de Zaragoza 8 La versión correcta sería: #include <limits.h> void check_divide (int *k, int x, int *y) if (*y == 0) { if ( x > 0) *k = INT_MAX; } else { *k = x / *y; (*y)++; } } 12048 - J. Neira – Universidad de Zaragoza { 9 Precedencia y asociatividad if (x & mask == 0) • NO equivale a: if ((x & mask) == 0) if (x & (mask == 0) ) if (x < y < z) • NO equivale a: if ((x < y) && (y < z)) if (((x < y) && (1 < z)) || (0 < z)) • hi << 4 + low? • x == y == z? 12048 - J. Neira – Universidad de Zaragoza 10 Legibilidad int x; int y; int z; … x = y---z--; 12048 - J. Neira – Universidad de Zaragoza 11 Quiere decir: x : integer; y : integer; z : integer; x := y - z; y := y - 1; z := z - 1; 12048 - J. Neira – Universidad de Zaragoza 12 Comentario acerca de x = y---z--; • Un programador cuidadoso escribiria: x = y-- - z--; • Sigue siendo preferible: x = y - z; y--; z--; • Es más fácil de entender 12048 - J. Neira – Universidad de Zaragoza 13 ) http://libre.act-europe.fr/Software_Matters 12048 - J. Neira – Universidad de Zaragoza 14 Comprobación de expresiones con YACC expr ... ... ... ... ... ; | | | | | | expr '+' expr expr tAND expr expr tMAY expr '(' expr ')‘ tIDENTIFICADOR tCONSTENTERA • Se utilizan atributos sintetizados • El atributo será el tipo de la expresión resultado resultado entero entero resultado resultado booleano booleano + > -51 24 -51 24 12048 - J. Neira – Universidad de Zaragoza • La producción se valida a partir de los atributos del RHS ($n) • El atributo del LHS ($$) se genera a partir de los atributos del RHS 15 Comprobación de expresiones con YACC • Constantes e identificadores: %{ typedef enum { DESCONOCIDO, ENTERO, BOOLEANO, CHAR, CADENA } TIPO_VARIABLE; %} %union { TIPO_VARIABLE tipo; registro de atributos SIMBOLO *simbolo; } %type <tipo> expresion declaración de %type <simbolo> tIDENTIFICADOR atributo por símbolo %% expresion : ... | tCONSTENTERA { $$ = ENTERO; } | tIDENTIFICADOR { atributo LHS $$ = DESCONOCIDO; sintetizado if ($1 == NULL) to error_semantico(”identificador desconocido.”); u b i r at HS ... 16 12048R- J. Neira – Universidad de Zaragoza Comprobación de expresiones con YACC expresion : expresion ’+’ expresion { $$ = ENTERO; if (($1 != ENTERO) && ($1 != DESCONOCIDO)) error_semantico(”Op 1 debe ser entero."); else if (($3 != ENTERO) && ($3 != DESCONOCIDO)) error_semantico(”Op 2 debe ser entero."); } | expresion tAND expresion ... | expresion ‘=‘ expresion { $$ = BOOLEANO; if (($1 != DESCONOCIDO) && ($3 != DESCONOCIDO) && ($1 != $3)) error_semantico(”Ops deben ser iguales."); ... } ; 12048 - J. Neira – Universidad de Zaragoza 17 YACC: comprobación de expresiones • ¡Es ambigua! 2*3+4 2*3+4 (2*3)+4 2*(3+4) 2*(3+4) (2*3)+4 shift tCONSTENTERA 2 reduce tCONSTENTERA -> expr expr shift * expr * shift tCONSTENTERA expr * 3 reduce tCONSTENTERA -> expr expr * expr ó ó reduce expr * expr -> expr expr shift + expr * expr + • No se ha especificado la precedencia ni asociatividad de los operadores 12048 - J. Neira – Universidad de Zaragoza 18 Precedencia: solución implícita • Se re-escribe la gramática para definirla implícitamente expresion : expresion ’+’ expresion | expresion ’-’ expresion | mulexp ; mulexp : mulexp ’*’ mulexp | mulexp ’/’ mulexp | primaria ; primaria : ’-’ primaria | ’(’ expresion ’)’ | tCONSTENTERA ; mayor precedencia menor 12048 - J. Neira – Universidad de Zaragoza precedencia “alteración” de la precedencia 19 Asociatividad: solución implícita • Se re-escribe la gramática para definirla implícitamente expresion : expresion ’+’ mulexp | expresion ’-’ mulexp | mulexp ; mulexp : | | ; primaria primaria ’*’ mulexp primaria ’/’ mulexp primaria : ’-’ primaria | ’(’ expresion ’)’ | tCONSTENTERA ; de derecha a izquierda (¡normalmente es al revés!) 12048 - J. Neira – Universidad de Zaragoza de izquierda a derecha 20 Precedencia y Asociatividad: solución explícita • YACC permite definir ambas cosas explícitamente • %left indica la asociatividad de los operadores (también existe %right). • El orden de las declaraciones de%left ’+’ ’-’ Precedencia: termina la precedencia de los %left ’*’ ’/’ de menor operadores. a mayor ... • A cada producción se le asocia la %nonassoc UMINUS precedencia del token más a la %% derecha. expr : expr ’+’ expr • Los conflictos shift/reduce se ... resuelven utilizando la prece| expr tAND expr dencia. ... | ’(’ expr ’)’ expr ’+’ expr ’*’ | ’-’ expr %prec UMINUS | tCONSTENTERA ¿preced(’*’) > preced(’+’)? ... •si, shift ’*’ •no, reduce expr ’+’ expr -> expr ; • Parser pequeño y eficiente. 12048 - J. Neira – Universidad de Zaragoza 21 Precedencia 2*3+4 2*3+4 • Si el * tuviese mayor prece- • Si el + tuviese mayor predencia que el +: cedencia que el *: Reading (2) Shifting (tCONSTENTERA) Reducing tCONSTENTERA -> Reading ('*') Shifting ('*') Reading (3) Shifting (tCONSTENTERA) Reducing tCONSTENTERA -> Reducing expr '*' expr -> Reading ('+') Shifting ('+') Reading (4) Shifting (tCONSTENTERA) Reducing tCONSTENTERA -> Reducing expr '+' expr -> expr expr expr expr expr 10 10 12048 - J. Neira – Universidad de Zaragoza Reading (2) Shifting (tCONSTENTERA) Reducing tCONSTENTERA -> Reading ('*') Shifting ('*‘) Reading (3) Shifting (tCONSTENTERA) Reducing tCONSTENTERA -> Reading ('+') Shifting ('+') Reading (4) Shifting (tCONSTENTERA) Reducing tCONSTENTERA -> Reducing expr '+' expr -> Reducing expr '*' expr -> expr expr expr expr expr 14 14 22 Asociatividad 2-3+4 2-3+4 • Asociatividad por la izquierda: • Asociatividad por la derecha: Reading (2) Shifting (tCONSTENTERA) Reducing tCONSTENTERA -> Reading ('-') Shifting ('-') Reading (3) Shifting (tCONSTENTERA) Reducing tCONSTENTERA -> Reducing expr '-' expr -> Reading ('+') Shifting ('+') Reading (4) Shifting (tCONSTENTERA) Reducing tCONSTENTERA -> Reducing expr '+' expr -> Reading (2) Shifting (tCONSTENTERA) Reducing tCONSTENTERA -> Reading ('-') Shifting ('-') Reading (3) Shifting (tCONSTENTERA) Reducing tCONSTENTERA -> Reading ('+') Shifting ('+‘) Reading a (4) Shifting (tCONSTENTERA) Reducing tCONSTENTERA -> Reducing expr '+' expr -> Reducing expr '-' expr -> expr expr expr expr expr 33 12048 - J. Neira – Universidad de Zaragoza expr expr expr expr expr -5 -5 23 Asignación i := j + 42; %union{ struct { char *nombre; SIMBOLO *simbolo; } identificador; } %type <identificador> tIDENTIFICADOR asignacion: tIDENTIFICADOR lex debe buscarlo en la tabla { if ($1.simbolo == NULL) error_semantico ("id desconocido"); else if (($1.simbolo->tipo != VARIABLE) && ($1.simbolo->tipo != PARAMETRO)) error_semantico ("id debe ser variable o parametro."); } tOPAS expresion { if (($1 != DESCONOCIDO) && ($4 != DESCONOCIDO)&& ¿NULL? ($1.simbolo->variable != $4)) ¿$3? error_semantico ("tipos incompatibles"); } ; 12048 - J. Neira – Universidad de Zaragoza 24 Mejor... • $$ se refiere al atributo sinteti• Esto permite transmitir información entre acciones zado, sólo en la última acción de la producción. • Acciones intermedias: $$ atributo asociado con la acción. asignacion: %union{ tIDENTIFICADOR ... { int ok; $<ok>$ = FALSE; ... if ... } else $<ok>$ = TRUE; } tOPAS expresion { if ($<ok>2 && ($1 != DESCONOCIDO) && ($4 != DESCONOCIDO)&& ($1.simbolo->variable != $4)) error_semantico ("tipos incompatibles"); $$ ... } ; 12048 - J. Neira – Universidad de Zaragoza 25 Asignación a vectores • Los atributos también pueden utilizarse para contar elementos. %union{ int cantidad; } v := [10,-5,4,i,j+1]; %type <cantidad> lista_expresiones entero v[10]; ... asignacion_vector: tIDENTIFICADOR {...} tOPAS ’[’ lista_expresiones ’]’ {... if ($1.simbolo->dimension < $5) error_semantico ("cantidad incorrecta"); } ; lista_expresiones : expresion {$$ = 1;} | lista_expresiones ’,’ expresion {$$ = $1 + 1;} 12048 - J. Neira – Universidad de Zaragoza 26 2. Estructuras de control • Sentencias: las acciones efectúan las comprobaciones • No se sintetiza ningún atributo seleccion: tSI expresion { if (($2 != BOOLEANO) && ($2 != DESCONOCIDO)) error_semantico( "seleccion: condicion invalida."); } tENT lista_instrucciones resto_seleccion tFSI ; mientras_que: tMQ expresion ... lista_instrucciones tFMQ ; 12048 - J. Neira – Universidad de Zaragoza 27 Estructuras de Control • Instrucciones tipo break y continue %{ int bucle = 0; %} a := 10; %% ... continuar : { if (!bucle) error_semantico( "Solo dentro de bucles."); } ; mientras_que : tMQ expresion { bucle = TRUE; } lista_instrucciones { bucle = FALSE; } tFMQ ¿Problemas? ¿Problemas? 28 ; Zaragoza J. Neira – Universidad de mq a > 0 ... si a = 0 continuar fsi ... fmq 12048 - Estructuras de Control ¡Puede haber bucles anidados! a := 10; mq a > 0 ... mq b > 0 ... fmq si a = 0 continuar fsi ... fmq Mejor contar los bucles: %{ int bucle = 0; %} %% ... continuar : { if (bucle > 0) ; else error_semantico ( "Solo dentro de bucles."); } ; mientras_que: tMQ expresion {++bucle;} lista_instrucciones {--bucle;} tFMQ ; 12048 - J. Neira – Universidad de Zaragoza 29 3. Declaración de Variables • Declaraciones tipo C: El tipo está definido al procesar los identificadores. %union{ int tipo; int i, j, k; } float x, y, z, w; %type <tipo> tipo_variables declaracion : tipo_variables identificadores ’;’ ; tipo_variables : tENTERO { $$ = ENTERO; } | tCARACTER { $$ = CHAR; } | tBOOLEANO { $$ = BOOLEANO; } ; identificadores : tIDENTIFICADOR { if ($1.simbolo == NULL) introducir_variable (tabsim, $1.nombre, ¿tipo?, ¿nivel?...); else error_semantico ("id duplicado."); } | identificadores ‘,’ tIDENTIFICADOR { ... } 12048 - J. Neira – Universidad de Zaragoza 30 Atributos heredados declaracion : tipo_variables identificadores ’;’ ; tipo_variables : tENTERO { $$ = ENTERO; } | tCARACTER { $$ = CHAR; } | tBOOLEANO { $$ = BOOLEANO; } ; identificadores : tIDENTIFICADOR { if ($1.simbolo == NULL) introducir_variable (tabsim, $1.nombre, $<tipo>0, ¿nivel?, ...); else error_semantico ("id duplicado."); Atributo Atributodel delsímbolo símboloalmacealmace} nado en la pila antes en la pila antesde de | identificadores nado ‘,’ tIDENTIFICADOR identificadores $0, $-1,... identificadores { ... } leer : tLEER ’(’ identificadores ’)’ OJO: ; Ahora Ahoraantes antesde deidentificadores identificadores aparece aparecetambién también‘(‘. ‘(‘. 12048 - J. Neira – Universidad de Zaragoza 31 Bloques {% int nivel; %} ... void abrir_bloque () { nivel++; ... } void cerrar_bloque () { ... nivel--; } declaracion_accion: cabecera_accion ';' declaracion_variables declaracion_acciones bloque_sentencias { ... cerrar_bloque(); } ; cabecera_accion: tACCION tIDENTIFICADOR { ... El bloque se abre después de procesar abrir_bloque(); el identificador y antes de los parámetros } parametros_formales identificadores ; : tIDENTIFICADOR { if (($1.simbolo == NULL) || ($1.simbolo->nivel != nivel)) introducir_variable (tabsim, $1.nombre, $<tipo>0, nivel, ...); else error_semantico ("id duplicado."); } 12048 - J. Neira – Universidad de Zaragoza 32 Declaración de Variables • Declaraciones tipo ADA: El tipo NO está definido al procesar los %union{ identificadores. LISTA lista; I, J, K : Integer; } %type <lista> identificadores X, Y, Z, W : Float; declaracion : identificadores ’:’ tipo_variables ’;’ G1 G1 { declaracion_variables ($1, $3); } ... ; tipo_variables : ... ; identificadores : tIDENTIFICADOR { $$ = crear_lista ($1); } Es necesario Es necesario usar usarlistas listasde deids. ids. | identificadores ’,’ tIDENTIFICADOR { $$ = anadir ($1, $3); } 12048 - J. Neira – Universidad de Zaragoza 33 Alternativa • Modificación de la gramática • ¡Cambia el orden de inserción! %union{ int tipo; 1 2 3 G1 G1 } %type <tipo> resto_declaracion declaracion : tIDENTIFICADOR resto_declaracion G2 {... G2 } ; i, j, k : Integer; 3 2 1 G2 G2 resto_declaracion : ',' tIDENTIFICADOR resto_declaracion { $$ = $3;... } Evita | ':' tipo_variables Evitaelelmanejo manejode de listas { listasde denombres. nombres. $$ = $2; } 12048 - J. Neira – Universidad de Zaragoza 34 ¿es relevante el orden en la declaración? • En Pascal, el orden NO es relevante var var • En C, ADA ¡el orden si puede ser relevante! i, j, k : integer; x, y, z, w : real; int a = pop(), b = pop(); ... push(a-b); w, y, z, x : real; k, i, j : integer; int b = pop(), a = pop(); ... push(a-b); • NO hay información semántica en el orden de declaración. 12048 - J. Neira – Universidad de Zaragoza • SI hay información semántica en el orden de declaración (la secuencialidad de los inicializadores). 35 Ventajas sintácticas • C: char* i, j, k; • ADA: i, j, k : access character; 12048 - J. Neira – Universidad de Zaragoza 36 4. Procedimientos y Funciones Declaraciones: Almacenar la declaración en la tabla (incluyendo los parámetros). procedure q (i : integer; var t : boolean); var j : integer; f : boolean; procedure r (var j : integer); var i : integer; begin q (i, t, 2); r (j+1) end begin q (j); r (’a’) end Invocaciones: Verificar que la cantidad, tipo y clase de los argumentos sea correcta. 12048 - J. Neira – Universidad de Zaragoza 37 Declaraciones %union {.... struct {SIMBOLO *simbolo; char *nombre;} LISTA *lista; .....} accion p( %type <ident> tIDENTIFICADOR val entero %type <lista> parametros_formales ref booleano %% val caracter accion : tACCION tIDENTIFICADOR { $<simbolo>$ = NULL; if (($2.simbolo == NULL) || ($2.simbolo->nivel != nivel)) $<simbolo>$ = introducir_accion (tabsim, $2.nombre, .... } parametros_formales { if ($<simbolo>3 != NULL) $<simbolo>3->parametros = $4; } ';' declaracion_variables declaracion_acciones bloque_instrucciones 12048 - J. Neira – Universidad de Zaragoza ident; i, j; k, l, m; n, o) ...); 38 Declaraciones • Es necesario almacenar el símbolo correspondiente a cada parámetro, su dirección asignada, y el orden en que ha sido declarado (necesario para verificar invocaciones). %type <lista> parametros lista_parametros %% parametros_formales : '(' lista_parametros ')' { $$ = $2; } | { crear_vacia (&($$)); } ; lista_parametros : parametros { $$ = $1; Acción por defecto } | lista_parametros ';' parametros { concatenar (&($1), $3); $$ = $1; } ; 12048 - J. Neira – Universidad de Zaragoza 39 Invocaciones • Cada argumento debe compararse por orden de aparición con la correspondiente declaración del parámetro. – Los tipos deben coincidir – Los argumentos a parámetros por referencia sólo pueden ser variables. invocacion_accion : tIDENTIFICADOR | tIDENTIFICADOR ’(’ argumentos ’)’ ; argumentos : expresion { LISTA pars; SIMBOLO *p; pars = (*($<identificador.simbolo>-1)).parametros; if (longitud_lista (pars) < 1) error ("Accion no tiene parametros."); p = observar (pars, 1); if ((*p).tipo != $1.tipo) ..... /*verificaciones */ $$ = 1; } | argumentos ',' expresion ....... /* algo parecido para $1 + 1 */ 12048 - J. Neira – Universidad de Zaragoza 40 5. Perspectiva • Sobrecarga de Operadores function ”/” (i, j : integer) return rational is ... Put(i/j); • Interpretaciones posibles de ‘/’: integer / integer -> integer / integer -> integer rational • Acción semántica: expresion : expresion ’/’ expresion { if (($1.tipo == ENTERO) && ($3.tipo == ENTERO)) ??????????????????? } ; ¡hay ¡haydos dos posibilidades! posibilidades! 12048 - J. Neira – Universidad de Zaragoza 41 5. Perspectiva • Paso de parámetros: el orden posicional y parámetros por valor/referencia es UNO de los posibles mecanismos de paso de parámetros. Orden Ordennominal nominal procedure Quadriatic (A, B, C : in float ...... ... Quadratic(B=>M, C=>N, A=>L, .....); orden ordendiferente diferente Valores Valorespor pordefecto defecto procedure Quadriatic (A : in float := 0.... ... Quadratic (B=>M, C=>N, .....); AAno noaparece aparece 12048 - El Ellenguaje lenguajese seenriquece enriquecepero perola la complejidad del compilador aumenta. complejidad del compilador aumenta. J. Neira – Universidad de Zaragoza 42 Ejercicio • Asignación múltiple entero i; booleano b; caracter c; i, b, c := caraent(c), i > 0, entacar(caraent(c) + 1); • Verificar que la cantidad y los tipos son correctos. asignacion_multiple : identificadores tOPAS expresiones ; identificadores : tIDENTIFICADOR | identificadores ’,’ tIDENTIFICADOR ; expresiones : expresion | expresiones ’,’ expresion ; 12048 - J. Neira – Universidad de Zaragoza 43 Ejercicio • Inicializadores entero i = 2, j = i + 4, k; booleano b = FALSE, d = i > j; • Verificar que el tipo del inicializador coincida con el de la variable. declaracion : tipo_variables declaradores ’;’ ; tipo_variables : tENTERO | tCARACTER | tBOOLEANO ; declaradores : declarador | declaradores ’,’ declarador ; declarador : tIDENTIFICADOR | tIDENTIFICADOR ’=’ expresion ; 12048 - J. Neira – Universidad de Zaragoza 44 Inicializadores %union{ int tipo; } %type <tipo> tipo_variables declaracion : tipo_variables declaradores ; tipo_variables : tENTERO { $$ = ENTERO; } | tCARACTER { $$ = CHAR; } | tBOOLEANO { $$ = BOOLEANO;} ; declaradores : declarador | declaradores ',' { $<tipo>$ = $<tipo>0; } declarador ; declarador : tIDENTIFICADOR | tIDENTIFICADOR '=‘ expresion {if ($3 != $<tipo>0) error_semantico(”...”); } ; 12048 - J. Neira – Universidad de Zaragoza 45