Skip to content

Tutoría 4 — Simulacro de Examen: Acciones Semánticas y Gramáticas de Atributos

Resumen Ejecutivo

Tutoría/taller centrada en la parte semántica del examen (la más difícil), continuando el simulacro de tutorías anteriores. Se resuelven en directo, paso a paso, acciones semánticas sobre gramáticas de atributos: contar elementos, propagar valores y hacer comprobaciones semánticas con condiciones anidadas. Mensaje pedagógico recurrente: el objetivo no es programar sino conceptualizar y diseñar; se puede responder en pseudocódigo.

Reparto de nota del examen: léxico 3 pts, sintáctico 3,5 pts, semántico 3,5 pts. ⚠️ EXAMEN

(La transcripción tiene tramos con silencios largos —la profesora escribía en pantalla—; los apuntes recogen el contenido reconstruido.)


Conceptos Clave

  • Atributo sintetizado: se calcula a partir de los hijos y sube por el árbol (de derecha a izquierda/padre). Es lo natural en analizadores LR ascendentes. ⚠️ EXAMEN
  • Atributo heredado: se pasa a un hermano o hijo (p.ej. el tipo en T id). Más difícil en ascendente: requiere marcadores. ⚠️ EXAMEN
  • Regla de redenominación: producción con un solo símbolo a cada lado (L → E, R → T); solo pasa todos los atributos del hijo al padre, sin añadir nada. ⚠️ EXAMEN
  • Notación de atributos: $$ = padre; $1,$2,$3 = símbolos de la parte derecha (en CUP: RESULT y etiquetas con nombre). ⚠️ EXAMEN
  • Conversión de valor en E → num: convertir el lexema a entero (Integer.parseInt) o usar el valor ya tipado que pasa el léxico. ⚠️ EXAMEN
  • E → id: no tiene valor numérico → inicializar el atributo valor = 0. ⚠️ EXAMEN
  • Comprobaciones anidadas: varias condiciones semánticas se anidan con IF ... ELSE; el OK solo se da si se cumplen todas. ⚠️ EXAMEN

Desarrollo del Temario

1. Filosofía: pseudocódigo y diseño, no programación

"No os estáis formando para ser programadores; lo vuestro es conceptualizar, diseñar y pensar el algoritmo para que luego otro pique el código."

  • Se valora explicar cada línea y justificar las decisiones.
  • Se acepta padre.numR = hijo1.numR igual que $$.numR = $1.numR: lo importante es que se vea qué información pasa del hijo al padre.
  • Los nombres de los atributos importan: representan semánticamente qué se quiere hacer (valor, numEles, numR…).
  • Mejor no mezclar Java y pseudocódigo; elegir uno.

2. Ejercicio 1 — Contar elementos y valores (R → R + T)

Para una producción R → R + T con atributos: - numR = nº de elementos de la expresión. - valor / numY = otro contador (p.ej. nº de símbolos Y).

Acciones semánticas:

R → R + T   {  $$.numR = $1.numR + $3.numR + 1     // +1 por el signo '+'
               $$.numY = $1.numY + $3.numY  }

Por qué el +1: la producción tiene tres partes (R, +, T); el + cuenta como un elemento más. Al aplicar la recursividad, la R izquierda ya trae acumulado lo sumado en pasos anteriores.

Casos base:

T → x       {  $$.numR = 1     // un elemento que aporta a R
               $$.numY = 0  }
T → y       {  $$.numR = 1
               $$.numY = 1  }   // cuenta una Y
R → T       {  $$ = $1  }       // redenominación: pasa todos los atributos

3. Ejercicio 1 — Hojas E → num y E → id

E → num     {  $$.valor = Integer.parseInt($1.lexema)   // o $1.valor si el léxico ya lo tipó
               $$.numL  = 1  }
E → id      {  $$.valor = 0      // un identificador NO tiene valor numérico → inicializar
               $$.numL  = 1  }

Dos opciones para num según el léxico: - El léxico pasa solo el lexema (string de dígitos) → hay que convertir a entero. - El léxico ya pasa el valor tipado (porque la expresión regular reconoció el número) → se propaga directamente.

4. Ejercicio 1 — Redenominación L → E y comprobación final

L → E       {  $$.numL  = $1.numL
               $$.valor = $1.valor  }     // equivale a  $$ = $1  (o sin acción → paso automático en CUP)

Comprobación semántica (anidada) en S → L , R (o similar): el número de elementos de L debe coincidir con el de R y la suma de valores cumplir una condición:

S → L , R   {
    IF ($1.numL == $3.numR) THEN
        IF ($1.valor >= $3.numY) THEN
            $$ = OK
        ELSE
            error("el valor de num no es mayor o igual que el nº de elementos Y")
    ELSE
        error("el nº de elementos de L no coincide con el de R")
}

⚠️ Clave: las condiciones se anidan, no se ponen independientes. Si se pusieran sueltas y la primera da OK, se "olvidaría" comprobar la segunda. El OK solo se emite cuando ambas se cumplen.

5. Sintetizados vs. heredados

  • Todo lo que se ha subido (numR, valor, numL) son atributos sintetizados (van hacia arriba). En la raíz no se "heredan": se hace una comprobación de condición.
  • Atributo heredado (ejemplo T id, una declaración): T lleva el tipo (int, char…) que ha subido; se lo pasa al id (su hermano a la derecha). Eso es herencia (de padres o hermanos).
  • Puede extenderse a una lista de identificadores: el tipo se reparte a todos.
  • En analizadores ascendentes la herencia es más difícil (las reducciones van hacia arriba) → requiere marcadores; por eso se usan menos ejemplos con atributos heredados.

6. Ejercicio 2 (propuesto) — Restricciones de longitud

Gramática recursiva por la izquierda: - P genera muchas A precedidas de una B (P → P a | b). - Q genera muchas C precedidas de una D (Q → Q c | d).

Restricciones a comprobar: 1. Si P contiene más de dos símbolos A, entonces Q debe contener al menos un símbolo. 2. longitud(P) ≤ longitud(Q).

Atributos: numA (cuenta A en P), num (cuenta C/D en Q), longP, longQ. Se cuentan los símbolos por separado con atributos distintos y se acumulan por la recursividad; al llegar arriba se comparan.

  • longP = numA + 1 (la única B); longQ = numC + 1 (la única D).
  • Hay formas más eficientes (un solo atributo long) y otras más detalladas (un atributo por símbolo y comparaciones al final).

Recordatorio de gramáticas: en la recursividad por la izquierda P → P a, cada aplicación añade una a; la recursividad para cuando se aplica el caso base P → b. Sin caso base, el lenguaje no genera cadenas.


Preguntas de Autoevaluación

  1. ¿Cómo se reparte la nota del examen entre léxico, sintáctico y semántico?
  2. En R → R + T, ¿por qué la acción del contador suma +1? ¿Qué hace la R izquierda en la recursividad?
  3. En E → num, ¿cuándo hay que convertir el lexema a entero y cuándo basta con propagar el valor?
  4. ¿Por qué en E → id se inicializa valor = 0?
  5. ¿Qué es una regla de redenominación y qué acción semántica lleva? ¿Se puede omitir en CUP?
  6. ¿Por qué las comprobaciones semánticas deben anidarse con IF/ELSE en vez de ponerse independientes?
  7. Diferencia entre atributo sintetizado y heredado. Pon un ejemplo de herencia con T id.
  8. ¿Por qué los atributos heredados son más difíciles en un analizador LR ascendente?
  9. ¿Qué significan $$, $1, $2, $3 y cómo se traducen a CUP?
  10. Para el ejercicio P/Q, ¿qué atributos definirías y cómo calcularías longP y longQ?