LABORATORIO DE PROCESADORES DE LENGUAJE – Curso 2007-2008 Práctica 2: Analizador léxico/sintáctico/semántico con Flex y Bison Se trata de realizar, mediante el generador de analizadores léxicos FLEX y sintácticos BISON, un analizador léxico, sintáctico y semántico que reconozca un sencillo lenguaje de programación, que llamaremos MiniModula, cuyas especificaciones léxicas y sintácticas se detallan a continuación. Además se deberá incluir la construcción de la tabla de símbolos (TS) y realización de comprobaciones semánticas. Especificación a nivel léxico Consideraremos como comentario todo aquello que vaya entre los símbolos (* y *). Los identificadores, id, constarán de una letra seguida opcionalmente de cualquier número de letras, dígitos y símbolos de subrayado ‘_’. Existen constantes numéricas de tipo entero, num_integer, un dígito seguido de uno o más dígitos, o de tipo real, num_real, un dígito o más dígitos seguidos de un punto más un dígito o más dígitos. Los símbolos especiales son: ( > . : .. ) [ ] ; , + - * / Las palabras clave son: integer real char boolean if while do read write begin end function return var array of record and or not true false = == then module < else type Especificación a nivel sintáctico La gramática que utilizaremos representa un sencillo lenguaje de programación que incluye como tipos simples de datos el tipo char, integer y real. Como tipos compuestos permite la definición de vectores y estructuras. Como sentencias incluye las sentencias condicionales (if_then_else), sentencias repetitivas (while_do), asignaciones, llamadas y retorno de funciones y E/S de datos. También permite trabajar con expresiones aritméticas (+,-,*,/), relacionales (<,==, >) y lógicas (and, or, not). Al final de la práctica se incluye ejemplos de código fuente que reconoce esta gramática. Generación de la Tabla de Símbolos y de la Tabla de Tipos A la hora de generar la tabla de símbolos se debe tener en cuenta que es necesario almacenar tanto variables globales como locales, para ello tendremos que diferenciar las variables mediante el alcance que cada una de ellas tiene. A la hora de implementar la tabla de símbolos deberá utilizar un campo para el alcance de los símbolos siendo, por ejemplo, su valor cero para las variables globales e ir incrementándose para las variables locales dependiendo de la función en la que están declaradas. La tabla debe contener información de si se trata de una variable o de una función, su nombre, el ámbito y tipo de dato. Si se trata de una función debe almacenar el tipo del valor de retorno, número y tipo de argumentos. Cuando se definan un nuevo tipo de dato se almacenarán dentro una tabla de tipos donde guardaremos información de cada tipo de dato definido. Si es una estructura se guardará su nombre, tipo de dato de los campos y su nombre. Cuando se defina un tipo de dato vector, se almacenará el nombre, tamaño y tipo base. Ejemplos de definiciones de tipos: type objeto = record color : char; peso: integer; end; type vector = array [1..100] of integer; 1 La gramática que define el lenguaje es: Program → module id ; Declarations begin StatementList end id . Declarations → type id = TypeDenoter ; TypeList Declarations | var id IdList : TypeDenoter ; VarList Declarations | function id ( Parameter ParameterList ) : SimpleType ; Declarations begin StatementList end id ; Declarations | ε TypeList → id = TypeDenoter ; TypeList | ε VarList → id IdList : TypeDenoter ; VarList | ε IdList → , id IdList | ε Parameter → TypeDenoter id | ε ParameterList → , Parameter ParameterList | ε SimpleType → char | integer |real | boolean TypeDenoter → SimpleType | id | CompositeType CompositeType → array [ num_integer .. num_integer ] of SimpleType | record id IdList : SimpleType ; FieldList end FieldList → id IdList : SimpleType ; FieldList | ε StatementList → Statement ; StatementList | ε Statement → AssignStm |IfStm | WhileStm | FunctionStm | ReturnStm | WriteStm | ReadStm AssignStm → LeftValue = Expression LeftValue → id | id [ Expression ] | id . id IfStm → if Expression then StatementList ElseStm end ElseStm → else StatementList | ε WhileStm → while Expression do StatementList end FunctionStm → id ( Arg ArgList ) ReturnStm → return Expression WriteStm → write Expression ReadStm → read LeftValue Arg → Num | - Num | id | - id | ε Num → num_integer | num_real ArgList → , Arg ArgList | ε Expression → LeftValue Expression * Expression | Expression | < Expression | ( Expression ) | true | false | Num | FunctionStm | Expression | Expression / Expression | Expression Expression + Expression | - Expression | Expression or Expression and Expression | not Expression | Expression | Expression > Expression | Expression == Expression Nota: Se podrán incluir modificaciones a la gramática siempre que se genere el mismo lenguaje. 2 Importante: Habrá que tener en cuenta la precedencia entre operadores y el menos unario en las expresiones que trabajan con dichos operadores. Por otra parte, deberemos indicar la fila y columna de los posibles errores, tanto léxicos como sintácticos. Los operadores =, <, >, == no son asociativos. La precedencia de los operadores es, de menor a mayor, la siguiente: < > == + - or * / and menos unario not = Comprobaciones semánticas Se deberán realizar todas las siguientes comprobaciones semánticas básicas: • Comprobaciones de tipos en asignaciones, en paso de parámetros a funciones y en expresiones aritmético-lógicas. Todos los operandos en una expresión deben de ser del mismo tipo y los operadores deben estar definidos para ese tipo de variables. Asumiremos que, para variables de tipo entero y real, tenemos definidos los operadores aritméticos y relacionales. Para variables booleanas sólo están definidos los operadores lógicos. Para variables tipo carácter sólo están definidos los operadores relacionales. • Comprobaciones relacionadas con la tabla de símbolos: No insertar un identificador de una función o variable dos veces con el mismo nombre y con el mismo ámbito. En las llamadas a funciones el número y tipo de los de argumentos y valor de retorno debe coincidir con la definición de la función. Dentro de las funciones cuando se usen variables deben estar previamente almacenadas en la tabla de símbolos y con el ámbito adecuado. • El índice de un tipo array debe ser entero y no debe salirse de rango. En la definición del rango de un array se debe tener en cuenta que el límite inferior, que no tiene porqué ser cero, debe ser menor que el superior. • Sólo se pueden asignar variables de tipo array entre sí en el caso de que tengan el mismo tamaño y sean del mismo tipo. Se pueden asignar variables de tipo estructura entre sí, siempre que los campos tengan el mismo nombre, tamaño y tipo de dato. No se pueden hacer operaciones relacionales o artiméticológicas sobre tipos compuestos. • Los identificadores al principio y al final del programa principal y de las funciones deben coincidir. • Siempre que se usen variables de tipo estructura se deberá comprobar que el identificador del campo de la estructura coincide con el valor en la definición de dicho tipo. • No se pueden definir variables de tipos, que no sean simples o compuestos, que no hayan sido previamente definidos por el programador. Nota: Opcionalmente se podrán incluir otro tipo de comprobaciones. Fecha de entrega: Todos los grupos, el día 3 de abril de 2007 en pizarra.uv.es 3 Ejemplo estadistica.mod module Estadistica ; (* Este programa calcula e imprime el valor medio de un vector de 100 enteros leido desde el teclado *) type vector = array [1..100] of integer; var datos : vector; n:integer; media:real; function fnc_media(vector x, integer n): real; var temp:real; i:integer; begin (* calculo de la media*) temp = 0; i=1; while (i< (n+1)) do temp=temp+x[i]; i=i+1; end; return (temp/n); end fnc_media ; begin n=1; while n<101 do read datos[n]; n=n+1; end; media=fnc_media(datos,n); write media; end Estadistica . Ejemplo objetos.mod module Objetos ; type objeto = record color : char; peso: integer; end; var x,y : objeto; begin read x.color; read x.peso; read y.color; read y.peso; if (y.peso==x.peso) and (y.color== x.color) then write 0; else write 1; end; end Objetos . 4