Java a Tope Traductores y Compiladores Con Lex Yacc JFlex Cup y .JavaCC

Embed Size (px)

Citation preview

  • 7/22/2019 Java a Tope Traductores y Compiladores Con Lex Yacc JFlex Cup y .JavaCC

    1/319

  • 7/22/2019 Java a Tope Traductores y Compiladores Con Lex Yacc JFlex Cup y .JavaCC

    2/319

    JAVA A TOPE:

    TRADUCTORES YCOMPILADORES CONLEX/YACC,JFLEX/CUP YJAVACC.EDICIN ELECTRNICA

    AUTORES: SERGIO GLVEZ ROJASM IGUEL NGEL MORA MATA

    ILUSTRACINDE PORTADA: JOS M IGUEL GLVEZ ROJAS

    Sun, el logotipo de Sun, Sun M icrosystems y Java son marcas o marcas registradas de Sun Microsystems

    Inc. en los EE.UU. y otros pases. El personaje de Duke es una m arca de Sun M icrosystems Inc.PCLex y PC Yacc son productos de Abraxas Software Inc.JFlex est liberado con licencia GPL.Cup est protegido por las licencias de cdigo abierto, siendo compatible con la licencia GPL.JavaCC est sujeto a la licencia BSD (Be rkeley Software Distribution) de cdigo abierto.

    2005 por Sergio Glvez RojasDEPSITO LEGAL: MA-185-2005ISBN:84-689-1037-6

  • 7/22/2019 Java a Tope Traductores y Compiladores Con Lex Yacc JFlex Cup y .JavaCC

    3/319

    Java a tope:

    CompiladoresTraductores y Compiladores

    con Lex/Yacc, JFlex/cup y JavaCC

    Sergio Glvez RojasDoctor Ingeniero en Informtica

    Miguel ngel Mora MataIngeniero en Informtica

    Dpto. de Lenguajes y Ciencias de la ComputacinE.T.S. de Ingeniera Informtica

    Universidad de Mlaga

  • 7/22/2019 Java a Tope Traductores y Compiladores Con Lex Yacc JFlex Cup y .JavaCC

    4/319

  • 7/22/2019 Java a Tope Traductores y Compiladores Con Lex Yacc JFlex Cup y .JavaCC

    5/319

    Java a tope: Traductores y compiladores con Lex/Yacc, JFlex/Cup y JavaCC

    i

    ndice

    Prlogo . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . vii

    Captulo 1 Introduccin . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 11.1 Visin general . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 11.2 Concepto de traductor. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2

    1.2.1 Tipos de traductores . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2

    1.2.1.1 Traductores del idioma . . . . . . . . . . . . . . . . . . . . . . . . . . . . 31.2.1.2 Compiladores . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 31.2.1.3 Intrpretes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 31.2.1.4 Preprocesadores . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 41.2.1.5 Intrpretes de comandos . . . . . . . . . . . . . . . . . . . . . . . . . . . . 41.2.1.6 Ensambladores y macroensambladores . . . . . . . . . . . . . . . . . 51.2.1.7 Conversores fuente-fuente . . . . . . . . . . . . . . . . . . . . . . . . . . 51.2.1.8 Compilador cruzado . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6

    1.2.2 Conceptos bsicos relacionados con la traduccin . . . . . . . . . . . 61.2.2.1 Compilacin, enlace y carga. . . . . . . . . . . . . . . . . . . . . . . . . 61.2.2.2 Pasadas de compilacin . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8

    1.2.2.3 Compilacin incremental . . . . . . . . . . . . . . . . . . . . . . . . . . . 91.2.2.4 Autocompilador . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 101.2.2.5 Metacompilador . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 101.2.2.6 Descompilador . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 11

    1.3 Estructura de un traductor . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 111.3.1 Construccin sistemtica de compiladores . . . . . . . . . . . . . . . . 121.3.2 La tabla de smbolos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 14

    1.4 Ejemplo de compilacin . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 151.4.1 Preprocesamiento . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 161.4.2 Etapa de anlisis . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 16

    1.4.2.1 Fase de anlisis lexicogrfico . . . . . . . . . . . . . . . . . . . . . . . 171.4.2.2 Fase de anlisis sintctico . . . . . . . . . . . . . . . . . . . . . . . . . . 17

    1.4.2.2.1 Compilacin dirigida por sintaxis . . . . . . . . . . . . . 181.4.2.3 Fase de anlisis semntico . . . . . . . . . . . . . . . . . . . . . . . . . 19

    1.4.3 Etapa de sntesis . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 191.4.3.1 Fase de generacin de cdigo intermedio . . . . . . . . . . . . . . 191.4.3.2 Fase de optimizacin de cdigo . . . . . . . . . . . . . . . . . . . . . 201.4.3.3 Fase de generacin de cdigo mquina . . . . . . . . . . . . . . . 20

  • 7/22/2019 Java a Tope Traductores y Compiladores Con Lex Yacc JFlex Cup y .JavaCC

    6/319

    ndice

    ii

    Captulo 2 Anlisis lexicogrfico . . . . . . . . . . . . . . . . . . . . . . . 232.1 Visin general . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 232.2 Concepto de analizador lxico . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 23

    2.2.1 Funciones del analizador lxico . . . . . . . . . . . . . . . . . . . . . . . . . 242.2.2 Necesidad del analizador lxico . . . . . . . . . . . . . . . . . . . . . . . . . 25

    2.2.2.1 Simplificacin del diseo . . . . . . . . . . . . . . . . . . . . . . . . . . 252.2.2.2 Eficiencia . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 272.2.2.3 Portabilidad . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 272.2.2.4 Patrones complejos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 28

    2.3 Token, patrn y lexema . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 282.3.1 Aproximaciones para construir un analizador lexicogrfico . . . 30

    2.4 El generador de analizadores lexicogrficos: PCLex . . . . . . . . . . . . . . . . 312.4.1 Visin general . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 31

    2.4.2 Creacin de un analizador lxico . . . . . . . . . . . . . . . . . . . . . . . . 322.4.3 El lenguaje Lex . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 33

    2.4.3.1 Premisas de Lex para reconocer lexemas . . . . . . . . . . . . . . 342.4.3.2 Caracteres especiales de Lex . . . . . . . . . . . . . . . . . . . . . . . 342.4.3.3 Caracteres de sensibilidad al contexto . . . . . . . . . . . . . . . . 352.4.3.4 Estado lxicos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 362.4.3.5 rea de definiciones y rea de funciones . . . . . . . . . . . . . . 372.4 .3 .6 Funciones y variab les suminis tradas por PCLex . . . . . . . . 38

    2.5 El generador de analizadores lexicogrficos JFlex . . . . . . . . . . . . . . . . . 422.5.1 Ejemplo preliminar . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 42

    2.5.2 rea de opciones y declaraciones . . . . . . . . . . . . . . . . . . . . . . . 442.5.2.1 Opciones . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 442.5.2.1.1 Opciones de clase . . . . . . . . . . . . . . . . . . . . . . . . . 442.5.2.1.2 Opciones de la funcin de anlisis . . . . . . . . . . . . 442.5.2.1.3 Opciones de fin de fichero . . . . . . . . . . . . . . . . . . 452.5.2.1.4 Opciones de juego de caracteres . . . . . . . . . . . . . . 452.5.2.1.5 Opciones de contadores . . . . . . . . . . . . . . . . . . . . 46

    2.5.2.2 Declaraciones. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 462.5.2.2.1 Declaraciones de estados lxicos. . . . . . . . . . . . . . 462.5.2.2.2 Declaraciones de reglas. . . . . . . . . . . . . . . . . . . . . 46

    2.5.3 rea de reglas. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 462.5.4 Funciones y variables de la clase Yylex . . . . . . . . . . . . . . . . . . 47

    Captulo 3 Anlisis sintctico . . . . . . . . . . . . . . . . . . . . . . . . . . 493.1 Visin general . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 493.2 Concepto de analizador sintctico . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 493.3 Manejo de errores sintcticos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 50

    3.3.1 Ignorar el problema . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 513.3.2 Recuperacin a nivel de frase . . . . . . . . . . . . . . . . . . . . . . . . . . 523.3.3 Reglas de produccin adicionales . . . . . . . . . . . . . . . . . . . . . . . 52

  • 7/22/2019 Java a Tope Traductores y Compiladores Con Lex Yacc JFlex Cup y .JavaCC

    7/319

    Java a tope: Traductores y compiladores con Lex/Yacc, JFlex/Cup y JavaCC

    iii

    3.3.4 Correccin Global . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 523.4 Gramtica utilizada por un analizador sintctico . . . . . . . . . . . . . . . . . . . 52

    3.4.1 Derivaciones . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 53

    3.4.2 rbol sintctico de una sentencia de un lenguaje . . . . . . . . . . . 543.5 Tipos de anlisis sintctico . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 56

    3.5.1 Anlisis descendente con retroceso . . . . . . . . . . . . . . . . . . . . . . 573.5.2 Anlisis descendente con funciones recursivas . . . . . . . . . . . . . 60

    3.5.2.1 Diagramas de sintaxis . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 613.5.2.2 Potencia de los diagramas de sintaxis . . . . . . . . . . . . . . . . . 613.5.2.3 Correspondencia con flujos de ejecucin . . . . . . . . . . . . . . 623.5.2.4 Ejemplo completo . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 633.5.2.5 Conclusiones sobre el anlisis descendente con funciones

    recursivas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 66

    3.5.3 Anlisis descendente de gramticas LL(1) . . . . . . . . . . . . . . . . 673.5.4 Generalidades del anlisis ascendente . . . . . . . . . . . . . . . . . . . . 70

    3.5.4.1 Operaciones en un analizador ascendente . . . . . . . . . . . . . . 713.5.5 Anlisis ascendente con retroceso . . . . . . . . . . . . . . . . . . . . . . . 723.5.6 Anlisis ascendente de gramticas LR(1) . . . . . . . . . . . . . . . . . 74

    3.5.6.1 Consideraciones sobre el anlisis LR (1) . . . . . . . . . . . . . . . 793.5.6.1.1 Recursin a derecha o a izquierda . . . . . . . . . . . . 793.5.6.1.2 Conflictos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 80

    3.5.6.2 Conclusiones sobre el anlisis LR(1) . . . . . . . . . . . . . . . . . 81

    Captulo 4 Gramticas atribuidas . . . . . . . . . . . . . . . . . . . . . . 834.1 Anlisis semntico . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 834.1.1 Atributos y acciones semnticas . . . . . . . . . . . . . . . . . . . . . . . . 834.1.2 Ejecucin de una accin semntica . . . . . . . . . . . . . . . . . . . . . . 86

    4.2 Traduccin dirigida por sintaxis . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 884.2.1 Definicin dirigida por sintaxis . . . . . . . . . . . . . . . . . . . . . . . . . 894 .2 .2 Esquema formal de una defin icin dirigida por sin taxis . . . . . . 89

    4.2.2.1 Atributos sintetizados . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 914.2.2.2 Atributos heredados . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 924.2.2.3 Grafo de dependencias . . . . . . . . . . . . . . . . . . . . . . . . . . . . 93

    4.2.2.4 Orden de evaluacin . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 954.2.2.5 Gramtica L-atribuida . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 964.2.2.6 Gramtica S-atribuida . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 97

    4.2.3 Esquemas de traduccin . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 984.2.4 Anlisis LALR con atributos . . . . . . . . . . . . . . . . . . . . . . . . . . . 99

    4.2.4.1 Conflictos reducir/reducir . . . . . . . . . . . . . . . . . . . . . . . . . 1004.2.4.2 Conflictos desplazar/reducir . . . . . . . . . . . . . . . . . . . . . . . 100

    4.3 El generador de analizadores sintcticos PCYacc . . . . . . . . . . . . . . . . . 1024.3.1 Formato de un programa Yacc . . . . . . . . . . . . . . . . . . . . . . . . . 102

    4.3.1.1 rea de definiciones . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 102

  • 7/22/2019 Java a Tope Traductores y Compiladores Con Lex Yacc JFlex Cup y .JavaCC

    8/319

    ndice

    iv

    4.3.1.2 Creacin del analizador lxico . . . . . . . . . . . . . . . . . . . . . 1034.3.1.3 rea de reglas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1054.3.1.4 rea de funciones . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 106

    4.3.2 Gestin de atributos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1064.3.2.1 Acciones intermedias . . . . . . . . . . . . . . . . . . . . . . . . . . . . 110

    4.3.3 Ambigedad . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1104.3.4 Tratamiento de errores . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1134.3.5 Ejemplo final . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 116

    4.4 El generador de analizadores sintcticos Cup . . . . . . . . . . . . . . . . . . . . 1174.4.1 Diferencias principales . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1174.4.2 Sintaxis completa. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 120

    4.4.2.1 Especificacin del paquete e importaciones. . . . . . . . . . . 1204.4.2.2 Cdigo de usuario. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 120

    4.4.2.3 Listas de smbolos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1214.4.2.4 Asociatividad y precedencia. . . . . . . . . . . . . . . . . . . . . . . 1214.4.2.5 Gramtica. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 122

    4.4.3 Ejecucin de Cup. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1234.4.4 Comunicacin con JFlex . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 124

    Captulo 5 JavaCC . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1275.1 Introduccin . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 127

    5.1.1 Caractersticas generales . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1275.1.2 Ejemplo preliminar. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 128

    5.2 Estructura de un programa en JavaCC . . . . . . . . . . . . . . . . . . . . . . . . . . 1305.2.1 Opciones. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1325.2.2 rea de tokens . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 134

    5.2 .2 .1 Caracteres espec iales para patrones JavaCC . . . . . . . . . . . 1355.2.2.2 Elementos accesibles en una accin lxica . . . . . . . . . . . . 1375.2.2.3 La clase Token y el token manager . . . . . . . . . . . . . . . . . 139

    5.2.3 rea de funciones BNF . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1405.3 Gestin de atributos. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1445.4 Gestin de errores. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 146

    5.4.1 Versiones antiguas de JavaCC . . . . . . . . . . . . . . . . . . . . . . . . . 146

    5.4.2 Versiones modernas de JavaCC . . . . . . . . . . . . . . . . . . . . . . . . 1475.5 Ejemplo final . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1485.6 La utilidad JJTree . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 150

    5.6.1 Caractersticas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1505.6.2 Ejemplo Prctico . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 150

    5.6.2.1 Renombrado de nodos mediante # . . . . . . . . . . . . . . . . . . 1545.6.2.2 Construccin del rbol . . . . . . . . . . . . . . . . . . . . . . . . . . . 1555.6.2.3 Tipos de Nodos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 156

    5.6.3 mbitos de Nodos y Acciones de Usuario . . . . . . . . . . . . . . . 1585.6.4 Manejo de Excepciones . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 160

  • 7/22/2019 Java a Tope Traductores y Compiladores Con Lex Yacc JFlex Cup y .JavaCC

    9/319

    Java a tope: Traductores y compiladores con Lex/Yacc, JFlex/Cup y JavaCC

    v

    5.6.5 Node Scope Hooks . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1605.6.6 El ciclo de vida de un nodo . . . . . . . . . . . . . . . . . . . . . . . . . . . 1635.6.7 Visitor Support . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 164

    5.6.7.1 Parmetros de Ejecucin . . . . . . . . . . . . . . . . . . . . . . . . . . 1645.6.8 Estados en JTree . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 165

    5.6.8.1 Objetos Node . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1655.6.8.2 Pasos de Ejecucin . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 166

    5.6.9 Manual de Referencia de JJDoc . . . . . . . . . . . . . . . . . . . . . . . . 167

    Captulo 6 Tabla de smbolos . . . . . . . . . . . . . . . . . . . . . . . . . 1716.1 Visin general . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1716.2 Informacin sobre los identificadores de usuario . . . . . . . . . . . . . . . . . . 1716.3 Consideraciones sobre la tabla de smbolos . . . . . . . . . . . . . . . . . . . . . . 1736.4 Ejemplo: una calculadora con variables . . . . . . . . . . . . . . . . . . . . . . . . . 175

    6.4.1 Interfaz de la tabla de smbolos . . . . . . . . . . . . . . . . . . . . . . . . 1766.4.2 Solucin con Lex/Yacc . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1766.4.3 Solucin con JFlex/Cup . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1796.4.4 Solucin con JavaCC . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 182

    Captulo 7 Gestin de tipos . . . . . . . . . . . . . . . . . . . . . . . . . . 1877.1 Visin general . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1877.2 Compatibilidad nominal, estructural y funcional . . . . . . . . . . . . . . . . . . 1887.3 Gestin de tipos primitivos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 190

    7.3.1 Gramtica de partida . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1907.3.2 Pasos de construccin . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 192

    7.3.2.1 Propuesta de un ejemplo . . . . . . . . . . . . . . . . . . . . . . . . . . 1927.3.2.2 Definicin de la tabla de smbolos . . . . . . . . . . . . . . . . . . 1937.3.2.3 Asignacin de atributos . . . . . . . . . . . . . . . . . . . . . . . . . . 193

    7.3.2.3.1 Atributos de terminales . . . . . . . . . . . . . . . . . . . . 1947.3.2.3.2 Atributos de no terminales . . . . . . . . . . . . . . . . . 195

    7.3.2.4 Acciones semnticas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1967.3.3 Solucin con Lex/Yacc . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1987.3.4 Solucin con JFlex/Cup . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 203

    7.3.5 Solucin con JavaCC . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2077.4 Gestin de tipos complejos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 210

    7.4.1 Objetivos y propuesta de un ejemplo . . . . . . . . . . . . . . . . . . . . 2117.4.2 Pasos de construccin . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 212

    7.4.2.1 Gramtica de partida . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2127.4.2.2 Gestin de atributos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2147.4.2.3 Implementacin de una pila de tipos . . . . . . . . . . . . . . . . . 2157.4.2.4 Acciones semnticas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 217

    7.4.3 Solucin con Lex/Yacc . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2207.4.4 Solucin con JFlex/Cup . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 224

  • 7/22/2019 Java a Tope Traductores y Compiladores Con Lex Yacc JFlex Cup y .JavaCC

    10/319

    ndice

    vi

    7.4.5 Solucin con JavaCC . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 229

    Captulo 8 Generacin de cdigo . . . . . . . . . . . . . . . . . . . . . . 2358.1 Visin general . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2358.2 Cdigo de tercetos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2378.3 Una calculadora simple compilada . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 238

    8.3.1 Pasos de construccin . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2398.3.1.1 Propuesta de un ejemplo . . . . . . . . . . . . . . . . . . . . . . . . . . 2398.3.1.2 Gramtica de partida . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2398.3.1.3 Consideraciones importantes sobre el traductor . . . . . . . . 2408.3.1.4 Atributos necesarios . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2428.3.1.5 Acciones semnticas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 244

    8.3.2 Solucin con Lex/Yacc . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2448.3.3 Solucin con JFlex/Cup . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2468.3.4 Solucin con JavaCC . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 247

    8.4 Generacin de cdigo en sentencias de control . . . . . . . . . . . . . . . . . . . 2508.4.1 Gramtica de partida . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2508.4.2 Ejemplos preliminares . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2548.4.3 Gestin de condiciones . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 257

    8.4.3.1 Evaluacin de condiciones usando cortocircuito . . . . . . . 2598.4.3.1.1 Condiciones simples . . . . . . . . . . . . . . . . . . . . . . 2598.4.3.1.2 Condiciones compuestas . . . . . . . . . . . . . . . . . . 261

    8.4.4 Gestin de sentencias de control de flujo . . . . . . . . . . . . . . . . . 265

    8.4.4.1 Sentencia IF-THEN-ELSE . . . . . . . . . . . . . . . . . . . . . . . 2668.4.4.2 Sentencia WHILE . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2688.4.4.3 Sentencia REPEAT . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2698.4.4.4 Sentencia CASE . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 272

    8.4.5 Solucin con Lex/Yacc . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2768.4.6 Solucin con JFlex/Cup . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2818.4.7 Solucin con JavaCC . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 286

    Captulo 9 Gestin de memoria en tiempo de ejecucin . . . 2939.1 Organizacin de la mem oria durante la ejecucin . . . . . . . . . . . . . . . . . 293

    9.2 Zona de cdigo . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2949.2.1 Overlays . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 294

    9.3 Zona de datos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2959.3.1 Zona de Datos de Tamao Fijo . . . . . . . . . . . . . . . . . . . . . . . . 2969.3.2 Pila (Stack) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2989.3.3 Montn (heap) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 303

  • 7/22/2019 Java a Tope Traductores y Compiladores Con Lex Yacc JFlex Cup y .JavaCC

    11/319

    Java a tope: Traductores y compiladores con Lex/Yacc, JFlex/Cup y JavaCC

    vii

    Prlogo

    La construccin de un compilador es una de las tareas ms gratas con lasque un informtico puede encontrarse a lo largo de su carreraprofesional. Aunque no resulta sensato pensar que una labor tal pueda

    formar parte de la actividad cotidiana de la mayora de estos profesionales, s es ciertoque, con cierta frecuencia, suele aparecer la necesidad de analizar un fichero de textoque contiene informacin distribuida segn algn patrn reconocible: ficheros XML,ficheros de inicializacin .ini, ficheros con comandos del sistema operativo, los propiosprogramas fuente, e tc.

    Es ms, el concepto de anlisis de un texto puede ser til para ahorrar trabajoen situaciones que estn fuera del mbito profesional informtico, como por ejemplola generacin de ndices analticos en archivos de texto escritos con procesadores queno admiten esta opcin.

    Pero a pesar de la indudable utilidad de los conceptos generales relacionadoscon la teora y prctica de los anlisis de textos, el crear un compilador introduce alinformtico en un nuevo mundo en el que el control sobre el ordenador es absoluto yle lleva al paroxismo de la omnipotencia sobre la mquina. Y es que resultaimprescindible un pleno conocimiento de todos los conceptos intrnsecos al ordenador:

    dispositivos de E/S, cdigo mquina utilizado por el microprocesador, comunicacina bajo nivel entre ste y el resto de sus perifricos, distribucin de la memoria,mecanismos de uso de memoria virtual, etc.

    No obstante, el lec tor no debe asustarse ante el poder que este libro le va aconceder, ni tampoco ante los grandes conocimientos que ex ige el control de ese poder.Un compilador es una programa que da mucho juego en el sentido de que puedeapoyarse sobre otras herramientas ya existentes, lo que facilita enormemente elaprendizaje de su construccin y permite su estudio de forma gradual y pausada.

    En el volumen que el lector tiene en sus manos se ana una gran cantidad deinformacin orientada a la construccin de compiladores, pero en la que se presta

    especial atencin a las herramientas destinadas a facilitar la labor de reconocimientode textos que siguen una determinada gramtica y lxico. Es por ello que los ejemplospropuestos se resuelven desde una perspectiva ambivalen te: de un lado mediante Lexy Yacc, ya que por motivos histricos constituyen el pilar principal de apoyo alreconocimiento lxico y sintctico; y de otro lado mediante JavaCC, lo que nosintroduce tanto en la utilizacin del lenguaje Java como medio para construircompiladores, como en los mecanismos basados en notacin BNF para dichaconstruccin. Con el objetivo de centrar nuestra atencin en el lenguaje Java, losejemplos tambin son resueltos mediante las herramientas FLEX y Cup que constituyenversiones adaptadas de Lex y Yacc para generar cdigo Java orientado a objetos.

  • 7/22/2019 Java a Tope Traductores y Compiladores Con Lex Yacc JFlex Cup y .JavaCC

    12/319

    Prlogo

    viii

    Hemos escogido JavaCC frente a otras opciones como SableCC, CoCo/Java,etc. (vase la pgina http://catalog.compilertools.net/java.html) por dos motivosprinc ipa les. Primero, JavaCC ha sido apoyado por Sun M icrosystems hasta hace bien

    poco (hoy por hoy Sun Microsystems no apoya a ninguna de estas utilidades , ya quese hallan lo suficientemente maduras como para proseguir solas su camino). Y segundo,se trata de una de las herramientas ms potentes basadas en una notacinsustancialmente diferente a la empleada por Lex y Yacc, lo cual enriquecer nuestrapercepcin de este excitante mundo: la construccin de compiladores.

    http://catalog.compilertools.net/java.htmlhttp://catalog.compilertools.net/java.html
  • 7/22/2019 Java a Tope Traductores y Compiladores Con Lex Yacc JFlex Cup y .JavaCC

    13/319

    Java a tope: Traductores y compiladores con Lex/Yacc, JFlex/Cup y JavaCC

    1

    Captulo 1

    Introduccin

    1.1 Visin general

    Uno de los principales mecanismos de com unicacin entre un ordenador y unapersona viene dado por el envo y recepcin de mensajes de tipo textual: el usuarioescribe una orden mediante el teclado, y el ordenador la ejecuta devolviendo comoresultado un mensaje informativo sobre las acciones llevadas a cabo.

    Aunque la evolucin de los ordenadores se encuentra dirigida actualmentehacia el empleo de novedosas y ergonmicas interfaces de usuario (como el ratn, laspanta llas tctiles, las table tas grficas, etc .), podemos decir que casi todas las accionesque el usuario realiza sobre estas interfaces se traducen antes o despus a secuenciasde comandos que son ejecutadas como si hubieran sido introducidas por teclado. Porotro lado, y desde el punto de vista del profesional de la In formtica, el trabajo que sterealiza sobre el ordenador se encuentra plagado de situaciones en las que se produceuna comunicacin textual directa con la mquina: utilizacin de un intrprete decomandos (shell), construccin de ficheros de trabajo por lotes, programacin mediantediversos lenguajes, etc. Incluso los procesadores de texto como WordPerfect y MS

    Word almacenan los documentos escritos por el usuario mediante una codificacintextual estructurada que, cada vez que se abre el documento, es reconocida, recorriday presentada en pantalla.

    Por todo esto, ningn informtico que se precie puede esquivar la indudablenecesidad de conocer los entresijos de la herramienta que utiliza durante su trabajodiario y sobre la que descansa la interaccin hombre-mquina: el traductor.

    Existe una gran cantidad de situaciones en las que puede ser muy til conocercmo funcionan las distintas partes de un compilador, especialmente aqulla que seencarga de trocear los textos fuentes y convertirlos en frases sintcticamente vlidas.

    Por ejemplo, una situacin de aparente complejidad puede presentrsenos si se poseeun documento de MS Word que procede de una fusin con una base de datos y sequiere, a partir de l, obtener la B.D. original. Cmo solucionar el problema? Puesbasndose en que la estructura del documento est formada por bloques que se rep iten;la solucin podra ser:

    Convertir el documento a formato texto puro. Procesar dicho texto con un traductor para eliminar los caracteres superfluos

    y dar como resultado otro texto en el que cada campo de la tabla de la B.D.est entre comillas.

    El texto anterior se importa con cualquier SGBD.

  • 7/22/2019 Java a Tope Traductores y Compiladores Con Lex Yacc JFlex Cup y .JavaCC

    14/319

    Introduccin

    2

    Otras aplicaciones de la construcc in de traductores pueden ser la creacin depreprocesadores para lenguajes que no lo tienen (por ejemplo, para trabajar fcilmente

    con SQL en C, se puede hacer un preprocesador para introducir SQL inmerso), oincluso la conversin del carcter ASCII 10 (LF) en
    de HTML para pasar textoa la web.

    En este captulo, se introduce la construccin de un compilador y se describensus componentes, el entorno en el que estos trabajan y algunas herramientas desoftware que facilitan su construccin.

    1.2 Concepto de traductor.

    Un traductor se define como un programa que traduce o convierte desdeun texto o programa escrito en un lenguaje fuente hasta un texto o programaequivalente escr ito en un lenguaje destino produciendo, si cabe, mensajes de error.Los traductores engloban tanto a los compiladores (en los que el lenguaje destino sueleser cdigo mquina) como a los intrpretes (en los que el lenguaje destino estconstituido por las acciones atmicas que puede ejecutar el intrprete). La figura 1.1muestra el esquema bsico que compone a un compilador/intrprete.

    Es importante destacar la velocidad con la que hoy en da se puede construirun compilador. En la dcada de 1950, se consider a los traductores como programasnotablemente difciles de escribir. El primer compilador de Fortran (FormulaTranslator), por ejemplo, necesit para su implementacin el equivalente a 18 aos detrabajo individual (realmente no se tard tanto puesto que el trabajo se desarroll enequipo). Hasta que la teora de autmatas y lenguajes formales no se aplic a lacreacin de traductores, su desarrollo ha estado plagado de problemas y errores. Sin

    embargo, hoy da un compilador bsico puede ser el proyecto fin de carrera decualquier estudiante universitario de Informtica.

    1.2.1 Tipos de traductores

    Desde los orgenes de la computacin, ha existido un abismo entre la formaen que las personas expresan sus neces idades y la forma en que un ordenador es capazde interpretar instrucciones. Los traductores han intentado salvar este abismo parafacilitarle el trabajo a los humanos, lo que ha llevado a aplicar la teora de autmatasa diferentes campos y reas concretas de la informtica, dando lugar a los distintostipos de traductores que veremos a continuacin.

    Figura 1.1. Esquema preliminar de un traductor

  • 7/22/2019 Java a Tope Traductores y Compiladores Con Lex Yacc JFlex Cup y .JavaCC

    15/319

    Java a tope: Traductores y compiladores con Lex/Yacc, JFlex/Cup y JavaCC

    3

    1.2.1.1 Traductores del idioma

    Traducen de un idioma dado a otro, como por ejemplo del ingls al espaol.

    Este tipo de traductores posee multitud de problemas, a saber:

    Necesidad de inteligencia artificial y problema de las frases hechas. Elproblema de la inteligencia artificial es que tiene mucho de artificial y pocode inteligencia, por lo que en la actualidad resulta casi imposible traducirfrases con un sentido profundo. Como ancdota, durante la guerra fra, en unintento por realizar traducciones automticas del ruso al ingls y viceversa,se puso a prueba un prototipo introduciendo el texto en ingls: El espritu esfuerte pero la carne es dbil cuya traduccin al ruso se pas de nuevo alingls para ver si coincida con el original. Cual fue la sorpresa de losdesarrolladores cuando lo que se obtuvo fue : El vino est bueno pero la carne

    est podrida (en ingls spirit significa tanto espritu como alcohol ). Otrosejemplos difciles de traducir lo constituyen las frases hechas como: Pie l degallina, por si las moscas, molar mazo, etc.

    Difcil formalizacin en la especificacin del significado de las palabras. Cambio del sentido de las palabras segn el contexto. Ej: por decir aquello,

    se llev una galleta. En general, los resultados ms satisfactorios en la traduccin del lenguaje

    natural se han producido sobre subconjuntos restringidos del lenguaje. Y anms, sobre subconjuntos en los que hay m uy poco margen de ambigedad enla interpretacin de los textos: discursos jurdicos, docum entacin tcnica, etc.

    1.2.1.2 Compiladores

    Es aquel traductor que tiene como entrada una sentencia en lenguaje formaly como salida tiene un fichero e jecutable, es decir, realiza una traduccin de un cdigode alto nivel a cdigo mquina (tambin se entiende por compilador aquel programaque proporciona un fichero objeto en lugar del ejecutable final).

    1.2.1.3 Intrpretes

    Es como un compilador, solo que la salida es una ejecucin. El programa deentrada se reconoce y ejecuta a la vez. No se produce un resultado fsico (cdigo

    mquina) sino lgico (una ejecucin). Hay lenguajes que slo pueden ser interpretados,como p.ej. SNOBOL (StriNg Oriented SimBOlyc Language), LISP (LISt Processing),algunas versiones de BASIC (Beginners All-purpose Symbolic Instruction C ode), etc.

    Su principal ventaja es que permiten una fcil depuracin. Entre losinconvenientes podemos citar, en primer lugar, la lentitud de ejecucin , ya que alejecutar a la vez que se traduce no puede aplicarse un alto grado de optimizacin; porejemplo, si el programa entra en un bucle y la optimizacin no est muy afinada, lasmismas instrucciones se interpretarn y ejecutarn una y otra vez, enlenteciendo laejecucin del programa. Otro inconveniente es que durante la ejecucin, el intrpretedebe residir en memoria, por lo que consumen ms recursos.

  • 7/22/2019 Java a Tope Traductores y Compiladores Con Lex Yacc JFlex Cup y .JavaCC

    16/319

    Introduccin

    4

    Figura 1.2. Esquema de traduccin/ejecucin de un programa interpretado

    Adems de que la traduccin optimiza el programa acercndolo a la mquina,los lenguajes interpretados tienen la caracterstica de que permiten construir programasque se pueden modificar a s mismos.

    Algunos lenguajes intentan aunar las ventajas de los compiladores y de losintrpretes y evitar sus desventajas; son los lenguajes pseudointerpretados. En estos,el programa fuente pasa por un pseudocompilador que genera un pseudoejecutable.Para ejecutar este pseudoejecutable se le hace pasar por un motor de ejecucin que lointerpreta de manera relativamente eficiente. Esto tiene la ventaja de la portabilidad,ya que el pseudoejecutable es independiente de la mquina en que vaya a ejecutarse,y basta con que en dicha mquina se disponga del motor de ejecucin apropiado parapoder interpreta r cualquier pseudoejecutable. El ejemplo actua l ms conocido loconstituye el lenguaje Java; tambin son pseudointerpretadas algunas versiones dePascal y de COBOL (COmmon Bussiness Oriented Language). La figura 1.2 muestralos pasos a seguir en estos lenguajes para obtener una ejecucin.

    1.2.1.4 Preprocesadores

    Permiten modificar el programa fuente antes de la verdadera compilacin.Hacen uso de macroinstrucciones y directivas de compilacin. Por ejemplo, en lenguajeC, el preprocesador sustituye la directiva #include Uno.c por el cdigo completo quecontiene el fichero Uno.c, de manera que cuando el compilador comienza suejecucin se encuentra con el cdigo ya insertado en el programa fuente (la figura 1.3ilustra esta situacin). Algunas otras directivas de preprocesamiento permiten compilartrozos de cdigos opcionales (lenguajes C y Clipper): #fi, #ifdef, #define, #ifndef, etc.Los preprocesadores suelen actuar de manera transparente para el programador,pudiendo incluso considerarse que son una fase pre liminar del compilador.

    1.2.1.5 Intrpretes de comandos

    Un intrprete de comandos traduce sentencias simples a invocaciones aprogramas de una biblioteca. Se utilizan especialmente en los sistemas operativos (lashellde Unix es un intrprete de comandos). Los programas invocados pueden residiren el kernel(ncleo) del sistema o estar almacenados en algn dispositivo externocomo rutinas ejecutables que se traen a memoria bajo demanda.

    Por ejemplo, si bajo MS-DOS se teclea el comando copy se ejecutar lafuncin de copia de ficheros del sistema operativo, que se encuentra residente enmemoria.

  • 7/22/2019 Java a Tope Traductores y Compiladores Con Lex Yacc JFlex Cup y .JavaCC

    17/319

    Java a tope: Traductores y compiladores con Lex/Yacc, JFlex/Cup y JavaCC

    5

    1.2.1.6 Ensambladores y macroensambladores

    Son los pioneros de los compiladores, ya que en los albores de la informtica,los programas se escriban directamente en cdigo mquina, y el primer paso hacia loslenguajes de alto nivel lo constituyen los ensambladores. En lenguaje ensamblador se

    establece una relacin biunvoca entre cada instruccin y una palabra mnemotcnica,de manera que el usuario escribe los programas haciendo uso de los mnemotcnicos,y el ensamblador se encarga de traducirlo a cdigo mquina puro. De esta manera, losensambladores suelen producir directamente cdigo ejecutable en lugar de producirficheros objeto.

    Un ensamblador es un compilador sencillo, en el que el lenguaje fuente tieneuna estructura tan sencilla que permite la traduccin de cada sentencia fuente a unanica instruccin en cdigo mquina. Al lenguaje que admite este compilador tambinse le llama lenguaje ensamblador. En definitiva, existe una correspondencia uno a unoentre las instrucciones ensamblador y las instrucciones mquina. Ej:

    Instruccin ensamblador: LD HL, #0100Cdigo mquina generado: 65h.00h.01h

    Por otro lado, existen ensambladores avanzados que permiten definirmacroinstrucciones que se pueden traducir a varias instrucciones mquina. A estosprogramas se les llama macroensambladores, y suponen el siguiente paso hacia loslenguajes de alto nivel. Desde un punto de vista formal, un macroensamblador puedeentenderse como un ensamblador con un preprocesador previo.

    1.2.1.7 Conversores fuente-fuente

    Permiten traducir desde un lenguaje de alto nivel a otro lenguaje de alto nivel,

    Figura 1.3. Funcionamiento de la directiva de preprocesamiento #include en lenguaje C

  • 7/22/2019 Java a Tope Traductores y Compiladores Con Lex Yacc JFlex Cup y .JavaCC

    18/319

    Introduccin

    6

    Figura 1.4. Entrada y salida de un compilador real

    con lo que se consigue una mayor portabilidad en los programas de alto nivel.

    Por ejemplo, si un ordenador slo dispone de un compilador de Pascal, y

    queremos ejecutar un programa escrito para otra mquina en COBOL, pues unconversor de COBOL a Pascal solucionar el problema. No obstante el programafuente resultado puede requerir retoques manuales debido a diversos motivos:

    En situaciones en que el lenguaje destino carece de importantes caractersticasque el lenguaje origen s tiene. Por ejemplo un conversor de Java a C,necesitara modificaciones ya que C no tiene recolector de basura.

    En situaciones en que la traduccin no es inteligente y los programas destinoson altamente ineficientes.

    1.2.1.8 Compilador cruzado

    Es un compilador que genera cdigo para ser ejecutado en otra mquina. Seutilizan en la fase de desarrollo de nuevos ordenadores. De esta manera es posible,p.ej. , construir el sistema operativo de un nuevo ordenador recurriendo a un lenguajede alto nivel, e incluso antes de que dicho nuevo ordenador disponga siquiera de uncompilador.

    Ntese tambin que, para facilitar el desarrollo de software de un nuevoordenador, uno de los primeros programas que se deben desarrollar para ste es,precisamente, un compilador de algn lenguaje de alto n ivel.

    1.2.2 Conceptos bsicos relacionados con la traduccin

    Vamos a estudiar a continuacin diversa terminologa relacionada con elproceso de compilacin y de construccin de compiladores.

    1.2.2.1 Compilacin, enlace y carga.

    Estas son las tres fases bsicas que hay que seguir para que un ordenadorejecute la interpretacin de un texto escrito mediante la utilizacin de un lenguaje dealto nivel. Aunque este libro se centrar exclusivamente en la primera fase, vamos a veren este punto algunas cuestiones relativas al proceso completo.

    Por regla general, el compilador no produce directamente un fichero

    ejecutable, sino que el cdigo generado se estructura en mdulos que se almacenan enun fichero objeto. Los ficheros objeto poseen informacin relativa tanto al cdigomquina como a una tabla de smbolos que almacena la estructura de las variables ytipos utilizados por el programa fuente. La figura 1.4 muestra el resultado real queproduce un compilador.

  • 7/22/2019 Java a Tope Traductores y Compiladores Con Lex Yacc JFlex Cup y .JavaCC

    19/319

    Java a tope: Traductores y compiladores con Lex/Yacc, JFlex/Cup y JavaCC

    7

    Pero, por qu no se genera directamente un fichero ejecutable?Sencillamente, para permitir la compilacin separada, de manera que variosprogramadores puedan desarro llar simultneamente las partes de un programa ms

    grande y, lo que es ms importante, puedan compilarlos independientemente y realizaruna depuracin en paralelo. Una vez que cada una de estas partes ha generado sucorrespondiente fichero objeto, estos deben fus ionarse para generar un solo ejecutable.

    Como se ha comentado, un fichero objeto posee una estructura de mdulostambin llamados registros. Estos registros tienen longitudes diferentes dependiendode su tipo y cometido. Ciertos tipos de estos registros almacenan cdigo mquina, otrosposeen informacin sobre las variables globales, y otros incluyen informacin sobre losobjetos externos (p. ej, variables que se supone que estn declaradas en otro ficheros.El lenguaje C permite explcitamente esta situacin mediante el modificador extern).

    Durante la fase de enlace, el enlazador o linker resuelve las referenciascruzadas, (as se llama a la utilizacin de objetos externos), que pueden estardeclarados en otros ficheros objeto, o en libreras (ficheros con extensin lib o dll),engloba en un nico bloque los distintos registros que almacenan cdigo mquina,estructura el bloque de memoria destinado a almacenar las variables en tiempo deejecucin y genera el ejecutable final incorporando algunas rutinas adicionalesprocedentes de libreras, como por ejemplo las que implementan funciones matemticaso de e/s bsicas. La figura 1.5 ilustra este mecanismo de funcionamiento.

    De esta manera, el bloque de cdigo mquina contenido en el ficheroejecutable es un cdigo reubicable, es decir, un cdigo que en su momento se podrejecutar en diferentes posiciones de memoria, segn la situacin de la misma en elmomento de la ejecucin. Segn el modelo de estructuracin de la memoria delmicroprocesador, este cdigo se estructura de diferentes formas. Lo ms usual es queel fichero ejecutable est dividido en segmentos: de cdigo, de datos, de pila de datos,etc.

    Figura 1.5. Funcionamiento de un enlazador

  • 7/22/2019 Java a Tope Traductores y Compiladores Con Lex Yacc JFlex Cup y .JavaCC

    20/319

    Introduccin

    8

    Cuando el enlazador construye el fichero ejecutable, asume que cadasegmento va a ser colocado en la direccin 0 de la memoria. Como el programa va aestar dividido en segmentos, las direcciones a que hacen referencia las instrucciones

    dentro de cada segmento (instrucciones de cambio de control de flujo, de acceso adatos, etc.), no se tratan como absolutas, sino que son d irecciones relativas a partir dela direccin base en que sea colocado cada segmento en el momento de la ejecucin.El cargador carga el fichero .exe, coloca sus diferentes segmentos en memoria (dondeel sistema operativo le diga que hay m emoria libre para ello) y asigna los registros basea sus posiciones correctas, de manera que las direcciones relativas funcionencorrectamente. La figura 1.6 ilustra el trabajo que realiza un cargador de programas.

    Cada vez que una instruccin mquina hace referencia a una direccin dememoria (partiendo de la direccin 0), el microprocesador se encarga automticamentede sumar a dicha direccin la direccin absoluta de inicio de su segmento. Por ejemplopara acceder a la variable x almacenada en la direccin 1Fh, que se encuentra en elsegmento de datos ubicado en la direccin 8A34h, la instruccin mquina harreferencia a 1Fh, pero el microprocesador la traducir por 8A34h+1Fh dando lugar aun acceso a la direccin 8A53h: dir absoluta del segmento en memoria + dir relativade x en el segmento = dir absoluta de x en memoria.

    1.2.2.2 Pasadas de compilacin

    Es el nmero de veces que un compilador debe leer el programa fuente paragenerar el cdigo. Hay algunas situaciones en las que, para realizar la compilacin, noes suficiente con leer el fichero fuente una sola vez . Por ejemplo, en situaciones en lasque existe recursin indirecta (una funcin A llama a otra B y la B llama a la A).Cuando se lee el cuerpo de A, no se sabe si B est declarada m s adelante o se le haolvidado al programador; o si lo est, si los parmetros reales coinciden en nmero ytipo con los formales o no. Es ms, aunque todo estuviera correcto, an no se hagenerado el cdigo para B, luego no es posible generar el cdigo mquina

    Figura 1.6. Labor realizada por el cargador. El cargador suele ser parte del sistemaoperativo

  • 7/22/2019 Java a Tope Traductores y Compiladores Con Lex Yacc JFlex Cup y .JavaCC

    21/319

    Java a tope: Traductores y compiladores con Lex/Yacc, JFlex/Cup y JavaCC

    9

    correspondiente a la invocacin de B puesto que no se sabe su direccin de comienzoen el segmento de cdigo. Por todo esto, en una pasada posterior hay que controlar loserrores y rellenar los datos que faltan.

    Diferentes compiladores y lenguajes solucionan este problema de manerasdistintas. Una solucin consiste en hacer dos o ms pasadas de compilacin, pero elloconsume dem asiado tiempo puesto que las operaciones de e/s son, hoy por hoy, uno delos puntos fundamentales en la falta de eficiencia de los programas (incluidos loscompiladores). Otra solucin pasa por hacer una sola pasada de compilacin ymodificar el lenguaje obligando a hacer las declaraciones de funciones recursivasindirectas antes de su definicin. De esta manera, lenguajes como Pascal o Modula-2utilizan la palabra reservada FORWARD para ello: FORW ARD precede la declaracinde B, a continuacin se define A y por ltimo se define B. Lenguajes como C tambinpermiten el no tener que declarar una funcin si sta carece de parmetros y devuelveun entero.

    Otros lenguajes dan por implcito el FORWARD, y si an no se hanencontrado aqullo a que se hace referencia, continan, esperando que el linkerresuelva el problema, o emita el mensaje de error.

    Actualmente, cuando un lenguaje necesita hacer varias pasadas decompilacin, suele colocar en memoria una representacin abstracta del fichero fuente,de manera que las pasadas de compilacin se realizan sobre dicha representacin enlugar de sobre el fichero de entrada, lo que soluciona el problema de la ineficienciadebido a operaciones de e/s.

    1.2.2.3 Compilacin incremental

    Cuando se desarrolla un programa fuente, ste se recompila varias veces hastaobtener una versin definitiva libre de errores. Pues bien, en una compilacinincremental slo se recompilan las modificaciones realizadas desde la ltimacompilacin. Lo ideal es que slo se recompilen aquellas partes que contenan loserrores o que, en general, hayan sido modificadas, y que el cdigo generado se reinsertecon cuidado en el fichero objeto generado en la ltima compilacin. Sin embargo estoes muy difcil de conseguir y no suele ahorrar tiempo de compilacin ms que en casosmuy concretos.

    La compilacin incremental se puede llevar a cabo con distintos grados deafinacin. Por ejemplo, si se olvida un ; en una sentencia, se podra generar un ficheroobjeto transitorio parcial. Si se corrige el error y se aade e l ; que falta y se recompila,un compilador incremental puede funcionar a varios niveles

    A nivel de carcter: se recompila el ; y se inserta en el fichero objeto lasentencia que faltaba.

    A nivel de sentencia: si el ; faltaba en la lnea 100 slo se compila la lnea100 y se actualiza el fichero objeto.

    A nivel de bloque: si el ; faltaba en un procedimiento o bloque slo secompila ese bloque y se actualiza el fichero objeto.

  • 7/22/2019 Java a Tope Traductores y Compiladores Con Lex Yacc JFlex Cup y .JavaCC

    22/319

    Introduccin

    10

    A nivel de fichero fuente: si la aplicacin completa consta de 15 ficherosfuente, y solo se modifica 1( al que le faltaba el ;), slo se compila aqul alque se le ha aadido el ;, se genera por completo su fichero objeto y luego

    se enlazan todos juntos para obtener el ejecutable.

    Lo ideal es que se hiciese eficientemente a nivel de sentencia, pero lo normales encontrarlo a nivel de fichero. La mayora de los compiladores actuales realizan unacompilacin incremental a este nivel.

    Cuando un compilador no admite compilacin incremental, suelen suministraruna herramienta externa (como RMAKE en caso de Clipper, MAKE en algunasversiones de C) en la que el programador indica las dependencias entre ficheros, demanera que si se recompila uno, se recompilan todos los que dependen de aqul. Estasherramientas suelen estar diseadas con un propsito ms general y tambin permiten

    enlaces condicionales.

    1.2.2.4 Autocompilador

    Es un compilador escrito en el mismo lenguaje que compila (o parecido).Normalmente, cuando se extiende entre muchas mquinas diferentes el uso de uncompilador, y ste se desea mejorar, el nuevo compilador se escribe utilizando ellenguaje del antiguo, de manera que pueda ser compilado por todas esas mquinasdiferentes, y d como resultado un compilador ms potente de ese mismo lenguaje.

    1.2.2.5 Metacompilador

    Este es uno de los conceptos ms importantes con los que vamos a trabajar.Un metacompilador es un compilador de compiladores. Se trata de un programa queacepta como entrada la descripcin de un lenguaje y produce el compilador de dicholenguaje. Hoy por hoy no existen metacompiladores completos, pero s parciales en losque se acepta como entrada una gramtica de un lenguaje y se genera un autmata quereconoce cualquier sentencia del lenguaje . A este autmata podemos aadirle cdigopara completar el resto del compilador. Ejemplos de metacompiladores son: Lex,YACC, FLex, Bison, JavaCC, JLex, Cup, PCCTS, MEDISE, etc.

    Los metacompiladores se suelen dividir entre los que pueden trabajar congramticas de contexto libre y los que trabajan con gramticas regulares. Los primeros

    se dedican a reconocer la sintaxis del lenguaje y los segundos trocean los ficherosfuente y lo dividen en palabras.

    PCLex es un metacompilador que genera la parte del compilador destinada areconocer las palabras reservadas. PCYACC es otro metacompilador que genera laparte del compilador que informa sobre si una sentencia del lenguaje es vlida o no.JavaCC es un metacompilador que ana el reconocimiento de palabras reservadas y laaceptacin o rechazo de sentencias de un lenguaje. PCLex y PCYACC generan cdigoC y admiten descripciones de un lenguaje mediante gramticas formales, mientras queJavaCC produce cdigo Java y admite descripciones de lenguajes expresadas ennotacin BNF (Backus-Naur Form ). Las diferencias entre ellas se irn estudiando en

  • 7/22/2019 Java a Tope Traductores y Compiladores Con Lex Yacc JFlex Cup y .JavaCC

    23/319

    Java a tope: Traductores y compiladores con Lex/Yacc, JFlex/Cup y JavaCC

    11

    temas posteriores.

    1.2.2.6 Descompilador

    Un descompilador realiza una labor de traduccin inversa, esto es, pasa de uncdigo mquina (programa de salida) al equivalente escrito en el lenguaje que lo gener(programa fuente). Cada descompilador trabaja con un lenguaje de alto nivel concreto.

    La descompilacin suele ser una labor casi imposible, porque al cdigomquina generado casi siempre se le aplica una optimizacin en una fase posterior, demanera que un mismo cdigo mquina ha podido ser generado a partir de varioscdigos fuente. Por esto, slo existen descompiladores de aquellos lenguajes en los queexiste una relacin biyectiva entre el cdigo destino y el cdigo fuente, como sucedecon los desensambladores, en los que a cada instruccin mquina le corresponde una

    y slo una instruccin ensamblador.Los descompiladores se utilizan especialmente cuando el cdigo mquina ha

    sido generado con opciones de depuracin, y contiene informacin adicional de ayudaal descubrimiento de errores (puntos de ruptura, seguimiento de trazas, opciones devisualizacin de variables, etc.).

    Tambin se emplean cuando el compilador no genera cdigo mquina puro,sino pseudocdigo para ser ejecutado a travs de un pseudointrprete. En estos casossuele existir una relacin biyectiva entre las instrucciones del pseudocdigo y lasconstrucciones sintcticas del lenguaje fuente, lo que permite reconstruir un programade alto nivel a partir del de un bloque de pseudocdigo.

    1.3 Estructura de un traductor

    Un traductor divide su labor en dos etapas : una que analiza la entrada y generaestructuras intermedias y otra que sintetiza la salida a partir de dichas estructuras. Portanto, el esquema de un traductor pasa de ser el de la figura 1.1, a ser el de la figura 1.7.

    Bsicamente los objetivos de la etapa de anlisis son: a) controlar lacorreccin del programa fuente, y b) generar las estructuras necesarias para comenzarla etapa de sntesis.

    Para llevar esto a cabo, la etapa de anlisis consta de las siguientes fases:

    O Anlisis lexicogrfico. Divide el programa fuente en los componentes bsicosdel lenguaje a compilar. Cada componente bsico es una subsecuencia de

    Figura 1.7. Esquema por etapas de un traductor

  • 7/22/2019 Java a Tope Traductores y Compiladores Con Lex Yacc JFlex Cup y .JavaCC

    24/319

    Introduccin

    12

    caracteres del programa fuente, y pertenece a una categora gramatical:nmeros , identificadores de usuario (variables, constantes, tipos, nombres deprocedimientos, ... ), palabras reservadas, s ignos de puntuacin, etc .

    O Anlisis sintctico. Comprueba que la estructura de los componentes bsicossea correcta segn las reglas gramaticales del lenguaje que se compila.

    O Anlisis semntico. Comprueba que el programa fuente respeta las directricesdel lenguaje que se compila (todo lo relacionado con el significado): chequeode tipos, rangos de valores, existencia de variables, etc.

    Cualquiera de estas tres fases puede emitir mensajes de error derivados defallos cometidos por el programador en la redaccin de los textos fuente. Mientras mserrores controle un compilador, menos problemas dar un programa en tiempo deejecucin. Por ejemplo, el lenguaje C no controla los lmites de un array, lo queprovoca que en tiempo de ejecucin puedan producirse comportamientos del programade difcil explicacin.

    La etapa de sntesis construye el programa objeto deseado (equivalentesemnticamente al fuente) a partir de las estructuras generadas por la etapa de anlisis.Para ello se compone de tres fases fundamentales:

    O Generacin de cdigo intermedio. Genera un cdigo independiente de lamquina muy parecido al ensamblador. No se genera cdigo mquinadirectamente porque as es ms fcil hacer pseudocompiladores y adems sefacilita la optimizacin de cdigo independientemente del microprocesador.

    O Generacin del cdigo mquina. Crea un bloque de cdigo mquinaejecutable, as como los bloques necesarios destinados a contener los datos.

    O Fase de optimizacin. La optimizacin puede realizarse sobre el cdigointermedio (de forma independiente de las caractersticas concretas delmicroprocesador), sobre el cdigo mquina, o sobre ambos. Y puede ser unaaislada de las dos anteriores, o estar integrada con ellas.

    1.3.1 Construccin sistemtica de compiladores

    Con frecuencia, las fases anteriores se agrupan en una etapa inicial (front-

    end) y una etapa final (back- end). La etapa inicial comprende aquellas fases, o partesde fases, que dependen exclusivamente del lenguaje fuente y que son independientesde la mquina para la cual se va a generar el cdigo. En la etapa inicial se integran losanlisis lxicos y sintcticos, el anlisis semntico y la generacin de cdigointermedio. La etapa inicial tambin puede hacer cierta optimizacin de cdigo eincluye adems, el manejo de errores correspondiente a cada una de esas fases.

    La etapa final incluye aquellas fases del compilador que dependen de lamquina destino y que, en general, no dependen del lenguaje fuente sino slo dellenguaje intermedio. En esta etapa, se encuentran aspectos de la fase de generacin de

  • 7/22/2019 Java a Tope Traductores y Compiladores Con Lex Yacc JFlex Cup y .JavaCC

    25/319

    Java a tope: Traductores y compiladores con Lex/Yacc, JFlex/Cup y JavaCC

    13

    Figura 1.8. Construccin de tres compiladores de C reutilizando unfront-end

    Figura 1.9. Creacin de tres compiladores (Pascal, C y COBOL) para una misma mquina Intel

    cdigo, adems de su optimizacin, junto con el manejo de errores necesario y elacceso a las estructuras intermedias que haga falta.

    Se ha convertido en una prctica comn el tomar la etapa inicial de uncompilador y rehacer su etapa final asociada para producir un compilador para elmismo lenguaje fuente en una mquina distinta. Tambin resulta tentador crearcompiladores para varios lenguajes distintos y generar el mismo lenguaje intermedio

    para, por ltimo, usar una etapa final comn para todos ellos, y obtener as varioscompiladores para una mquina. Para ilustrar esta prctica y su inversa, la figura 1.8

  • 7/22/2019 Java a Tope Traductores y Compiladores Con Lex Yacc JFlex Cup y .JavaCC

    26/319

    Introduccin

    14

    Figura 1.10. La combinacin de cada front-endcon un back-endda lugar a un compiladordistinto: tres de Pascal, tres de C y tres de COBOL. El esfuerzo se ha reducidoconsiderablemente.

    Muestra una situacin en la que se quiere crear un compilador del lenguaje C para tresmquinas diferentes: Dec-Alpha (Unix), Motorola (Mac OS) e Intel (MS-DOS). Cadabloque de lneas punteadas agrupa un front-end con un back-end dando lugar a un

    compilador completo.

    De manera inversa se podran construir tres compiladores de Pascal, C yCOBOL para una misma mquina, p.ej. Intel, como se ilustra en la figura 1.9.

    Por ltimo, la creacin de compiladores de Pascal, C y COBOL para lasmquinas Dec-Alpha, Motorola e Intel, pasara por la combinacin de los mtodosanteriores, tal como ilustra la figura 1.10.

    1.3.2 La tabla de smbolos

    Una funcin esencial de un compilador es registrar los identificadores deusuario (nombres de variables, de funciones, de tipos, etc.) utilizados en el programafuente y reunir informacin sobre los distintos atributos de cada identificador. Estosatributos pueden proporcionar informacin sobre la memoria asignada a unidentificador, la direccin de memoria en que se almacenar en tiempo de ejecucin,su tipo, su mbito (la parte del programa donde es visible), etc.

    Pues bien, la tabla de smbolos es una estructura de datos que poseeinformacin sobre los identificadores definidos por el usuario, ya sean constantes,variables, tipos u otros. Dado que puede contener informacin de diversa ndole, debehacerse de forma que su estructura no sea uniforme, esto es, no se guarda la mismainformacin sobre una variable del programa que sobre un tipo definido por el usuario.Hace funciones de diccionario de datos y su estructura puede ser una tabla hash, unrbol binario de bsqueda, etc., con tal de que las operaciones de acceso sean lo

  • 7/22/2019 Java a Tope Traductores y Compiladores Con Lex Yacc JFlex Cup y .JavaCC

    27/319

    Java a tope: Traductores y compiladores con Lex/Yacc, JFlex/Cup y JavaCC

    15

    bastante eficientes.

    Tanto la etapa de an lisis como la de sntesis accede a esta estructura, por loque se halla muy acoplada al resto de fases del compilador. Por ello conviene dotar ala tabla de smbolos de una interfaz lo suficientemente genrica como para permitir elcambio de las estructuras internas de almacenamiento sin que estas fases deban serretocadas. Esto es as porque suele ser usual hacer un primer prototipo de uncompilador con una tabla de smbolos fcil de construir (y por tanto, ineficiente), y

    cuando el compilador ya ha sido finalizado, entonces se procede a sustituir la tabla desmbolos por otra ms eficiente en funcin de las necesidades que hayan ido surgiendoa lo largo de la etapa de desarrollo anterior. Siguiendo este criterio, el esquema generaldefinitivo de un traductor se detalla en la figura 1.11. La figura 1.12 ilustra el esquemapor fases, donde cada etapa ha sido sustituida por las fases que la componen y se hahecho mencin explcita del preprocesador..

    1.4 Ejemplo de compilacin

    Vamos a estudiar a continuacin las diferentes tareas que lleva a cabo cadafase de un compilador hasta llegar a generar el cdigo asociado a una sentencia de C.

    La sentencia con la que se va a trabajar es la siguiente:#define PORCENTAJE 8comision = fijo + valor * PORCENTAJE;

    Para no complicar demasiado el ejemplo, asumiremos que las variablesreferenciadas han sido previamente declaradas de tipo int, e inicializadas a los valoresdeseados.

    Comenzaremos con el preprocesamiento hasta llegar, finalmente, a la fase degeneracin de cdigo mquina en un microprocesador cualquiera.

    Figura 1.11. Esquema por etapas definitivo de un traductor

  • 7/22/2019 Java a Tope Traductores y Compiladores Con Lex Yacc JFlex Cup y .JavaCC

    28/319

    Introduccin

    16

    1.4.1 Preprocesamiento

    Como ya hemos visto, el cdigo fuente de una aplicacin se puede dividir enmdulos almacenados en archivos distintos. La tarea de reunir el programa fuente amenudo se confa a un programa distinto, llamado preprocesador. El preprocesador

    tambin puede expandir abreviaturas, llamadas macros, a proposiciones del lenguajefuente. En nuestro ejemplo, la constante PORCENTAJE se sustituye por su valor,dando lugar al texto:

    comision = fijo + valor * 8;que pasa a ser la fuente que entrar al compilador.

    1.4.2 Etapa de anlisis

    En esta etapa se controla que e l texto fuente sea correcto en todos los sentidos,y se generan las estructuras necesarias para la generacin de cdigo.

    Figura 1.12. Esquema completo de un compilador por fases conpreprocesador.

  • 7/22/2019 Java a Tope Traductores y Compiladores Con Lex Yacc JFlex Cup y .JavaCC

    29/319

    Java a tope: Traductores y compiladores con Lex/Yacc, JFlex/Cup y JavaCC

    17

    1.4.2.1 Fase de anlisis lexicogrfico

    En esta fase, la cadena de caracteres que constituye el programa fuente se lee

    de izquierda a derecha y se agrupa en componentes lxicos, que son secuencias decaracteres que tienen un significado atmico; adems el analizador lxico trabaja conla tabla de smbolos introduciendo en sta los nombres de las variables.

    En nuestro ejemplo los caracteres de la proposicin de asignacincomision= fijo + valor * 8 ;

    se agruparan en los componentes lxicos siguientes:1.- El identificadorcomision.2.- El smbolo de asignacin =.3.- El identificadorfijo.4.- El signo de suma +.

    5.- El identificadorvalor.6.- El signo de multiplicacin *.7.- El nmero 8.8.- El smbolo de fin de sentencia ;.

    La figura 1.13 ilustra cmo cada componente lxico se traduce a su categoragramatical, y se le asocia alguna informacin, que puede ser un puntero a la tabla desmbolos donde se describe el identificador, o incluso un valor directo, como ocurre enel caso del literal 8. As, cada componente se convierte en un par (categora, atributo),y se actualiza la tabla de smbolos. Esta secuencia de pares se le pasa a la siguiente fasede anlisis.

    Ntese como los espacios en blanco que separan los caracte res de estoscomponentes lxicos normalmente se eliminan durante el anlisis lxico, siempre ycuando la definicin del lenguaje a compilar as lo aconseje, como ocurre en C. Lomismo pasa con los tabuladores innecesarios y con los retornos de carro. Loscomentarios, ya estn anidados o no, tambin son eliminados.

    1.4.2.2 Fase de anlisis sintctico

    Trabaja con una gramtica de contexto libre y genera el rbol sintctico que

    Figura 1.13. Transformacin realizada por el analizador lexicogrfico

  • 7/22/2019 Java a Tope Traductores y Compiladores Con Lex Yacc JFlex Cup y .JavaCC

    30/319

    Introduccin

    18

    reconoce su sentencia de entrada. En nuestro caso las categoras gramaticales delanlisis lxico son los terminales de la gramtica. Para el ejemplo que nos ocupapodemos partir de la gramtica:

    S expr expr

    | expr| expr|

    de manera que el anlisis sintctico intenta generar un rbol sintctico que encaje conla sentencia de entrada. Para nuestro ejemplo, dicho rbol sintctico existe y es el dela figura 1.14. El rbol puede representarse tal y como aparece en esta figura, o bieninvertido.

    1.4.2.2.1 Compilacin dirigida por sintaxis

    Se ha representado una situacin ideal en la que la fase lexicogrfica acta por

    separado y, slo una vez que ha acabado, le suministra la sintctica su resultado desalida. Aunque proseguiremos en nuestro ejemplo con esta clara distincin entre fases,es importante destacar que el analizador sintctico tiene el control en todo momento,y el lxico por trozos, a peticin del sintctico. En otras palabras, el sintctico vaconstruyendo su rbol poco a poco (de izquierda a derecha), y cada vez que necesitaun nuevo componente lxico para continuar dicha construccin, se lo solicita allexicogrfico; ste lee nuevos caracteres del fichero de entrada hasta conformar unnuevo componente y, una vez obtenido, se lo suministra al sintctico, quien continala construccin del rbol hasta que vuelve a necesitar otro componente, momento enque se reinicia el proceso. Este mecanismo finaliza cuando se ha obtenido el rbol y ya

    Figura 1.14. rbol sintctico de la sentencia de entrada.Aunque slo se han representado las categorasgramaticales, recurdese que cada una lleva o puedellevar asociado un atributo.

  • 7/22/2019 Java a Tope Traductores y Compiladores Con Lex Yacc JFlex Cup y .JavaCC

    31/319

    Java a tope: Traductores y compiladores con Lex/Yacc, JFlex/Cup y JavaCC

    19

    no hay ms componentes en el fichero de entrada, o bien cuando es imposible construirel rbol.

    Esto es tan slo el principio de lo que se denomina compilacin dirigida porsintaxis (ver figura 1.15): es aqul mecanismo de compilacin en el que el control lolleva el analizador sintctico, y todas las dems fases estn sometidas a l.

    1.4.2.3 Fase de anlisis semntico

    Esta fase revisa el rbol sintctico junto con los atributos y la tabla desmbolos para tratar de encontrar errores semnticos. Para todo esto se analizan losoperadores y operandos de expresiones y proposiciones. Finalmente rene lainformacin necesaria sobre los tipos de datos para la fase posterior de generacin de

    cdigo.El componente ms importante del anlisis semntico es la verificacin de

    tipos. Aqu, el compilador verifica si los operandos de cada operador son compatiblessegn la especificacin del lenguaje fuente. Si suponemos que nuestro lenguaje solotrabaja con nmeros rea les, la salida de esta fase sera su mismo rbol, excepto porqueel atributo de , que era el entero 8 a la entrada, ahora pasara a ser el real 8,0.Adems se ha debido controlar que las variables implicadas en la sentencia, a saber,comision, fijo y valorson compatibles con el tipo numrico de la constante 8,0.

    1.4.3 Etapa de sntesis

    En la etapa anterior se ha controlado que el programa de entrada es correcto.Por tanto, el compilador ya se encuentra en disposicin de generar el cdigo mquinaequivalente semnticamente al programa fuente. Para ello se parte de las estructurasgeneradas en dicha etapa anterior: rbol sintctico y tabla de smbolos.

    1.4.3.1 Fase de generacin de cdigo intermedio

    Despus de la etapa de anlisis, se suele generar una representacin intermediaexplcita del programa fuente. Dicha representacin intermedia se puede considerarcomo un programa para una mquina abstracta.

    Figura 1.15. Anlisis dirigido por sintaxis

  • 7/22/2019 Java a Tope Traductores y Compiladores Con Lex Yacc JFlex Cup y .JavaCC

    32/319

    Introduccin

    20

    Cualquier representacin intermedia debe tener dos propiedades importantes;debe ser fcil de generar y fcil de traducir al cdigo mquina destino. As, unarepresentacin intermedia puede tener diversas formas. En el presente ejemplo se

    trabajar con una forma intermedia llamada cdigo de tres direcciones, que es muyparecida a un lenguaje ensamblador para un microprocesador que carece de regis trosy slo es capaz de trabajar con direcciones de memoria y literales. En el cdigo de tresdirecciones cada instruccin tiene como mximo tres operandos. Siguiendo el ejemplopropuesto, se generara el siguiente cdigo de tres direcciones:

    t1 = 8.0t2 = valor * t1t3 = fijo + t2comision = t3

    De este ejemplo se pueden destacar varias propiedades del cdigo intermedioescogido:

    Cada instruccin de tres direcciones tiene a lo sumo un operador, adems dela asignacin.

    El compilador debe generar un nombre temporal para guardar los valoresintermedios calculados por cada instruccin: t1, t2 y t3.

    Algunas instrucciones tienen menos de tres operandos, como la primera y laltima instrucciones del ejemplo.

    1.4.3.2 Fase de optimizacin de cdigo

    Esta fase trata de mejorar el cdigo intermedio, de modo que en la siguiente

    fase resulte un cdigo de mquina ms rpido de ejecutar. Algunas optimizaciones sontriviales. En nuestro ejemplo hay una forma mejor de realizar el clculo de la comisin,y pasa por realizar sustituciones triviales en la segunda y cuarta instrucciones,obtenindose:

    t2 = valor * 8.0comision= fijo + t2

    El compilador puede deducir que todas las apariciones de la variable t1pueden sustituirse por la constan te 8,0, ya que a t1 se le asigna un valor que ya nocambia, de modo que la primera instruccin se puede eliminar. Algo parecido sucedecon la variable t3, que se utiliza slo una vez, para transmitir su valor a comision en

    una asignacin directa, luego resulta seguro sustituircomisionport3 , a raz de lo cualse elimina otra de las lneas del cdigo intermedio.

    1.4.3.3 Fase de generacin de cdigo mquina

    La fase final de un compilador es la generacin de cdigo objeto, que por logeneral consiste en cdigo mquina reubicable o cdigo ensamblador. Cada una de lasvariables usadas por el programa se traduce a una direccin de memoria (esto tambinse ha podido hacer en la fase de generacin de cdigo intermedio). Despus, cada unade las instrucciones intermedias se traduce a una secuencia de instrucciones de mquinaque ejecuta la misma tarea. Un aspecto decisivo es la asignacin de variables a

  • 7/22/2019 Java a Tope Traductores y Compiladores Con Lex Yacc JFlex Cup y .JavaCC

    33/319

    Java a tope: Traductores y compiladores con Lex/Yacc, JFlex/Cup y JavaCC

    21

    registros.

    Siguiendo el mismo ejemplo, y utilizando los registros R1 y R2 de un

    microprocesador hipottico, la traduccin del cdigo optimizado podra ser:MOVE [1Ah], R1MULT #8.0, R1MOVE [15h], R2

    ADD R1, R2MOVE R2, [10h]

    El primer y segundo operandos de cada instruccin especifican una fuente yun destino, respectivamente. Es te cdigo traslada el contendido de la direccin [1Ah]al registro R1, despus lo multiplica por la constante real 8.0. La tercera instruccinpasa el contenido de la direccin [15h] al regis tro R2. La cuarta ins truccin le suma el

    valor previamente calculado en el registro R1. Por ltimo el valor del registro R2 sepasa a la direccin [10h]. Como el lector puede suponer, la variable comision sealmacena en la direccin [10h], fijo en [15h] y valoren [1Ah].

  • 7/22/2019 Java a Tope Traductores y Compiladores Con Lex Yacc JFlex Cup y .JavaCC

    34/319

    Introduccin

    22

  • 7/22/2019 Java a Tope Traductores y Compiladores Con Lex Yacc JFlex Cup y .JavaCC

    35/319

    Java a tope: Traductores y compiladores con Lex/Yacc, JFlex/Cup y JavaCC

    23

    Captulo 2

    Anlisis lexicogrfico

    2.1 Visin general

    Este captulo estudia la primera fase de un compilador, es decir su anlisislexicogrfico, tambin denominado abreviadamente anlisis lxico. Las tcnicasutilizadas para construir analizadores lxicos tambin se pueden aplicar a otras reascomo, por ejemplo, a lenguajes de consulta y sistemas de recuperac in de informacin.

    En cada aplicacin, el problema de fondo es la especificacin y diseo de programasque ejecuten las acciones activadas por palabras que siguen ciertos patrones dentro delas cadenas a reconocer. Como la programacin dirigida por patrones est ampliamenteextendida y resulta de indudable utilidad, existen numerosos metalenguajes quepermiten establecer pares de la form a patrn-accin, de manera que la accin se ejecutacada vez que el sistema se encuentra una serie de caracteres cuya estructura coincidecon la del patrn. En concreto, vamos a estudiar Lex con el objetivo de especificar losanalizadores lxicos. En este lenguaje, los patrones se especifican por medio deexpresiones regulares, y el metacompilador de Lex genera un reconocedor de lasexpresiones regulares mediante un autmata finito (determinista evidentemente)

    eficiente.Por otro lado, una herramienta software que automatiza la construccin de

    analizadores lxicos permite que personas con diferentes conocimientos utilicen laconcordancia de patrones en sus propias reas de aplicacin, ya que, a la hora de laverdad, no es necesario tener profundos conocimientos de informtica para aplicardichas herramientas.

    2.2 Concepto de analizador lxico

    Se encarga de buscar los componentes lxicos o palabras que componen elprograma fuente, segn unas reglas o patrones.

    La entrada del analizador lxico podemos definirla como una secuencia decaracteres, que pueda hallarse codificada segn cualquier estndar: ASCII (Am ericanStandard Code for Information Interchange), EBCDIC (Extended Binary CodedDecimal Interchange C ode), Unicode, etc. El analizador lxico divide esta secuenciaen palabras con significado propio y despus las convierte a una secuencia determinales desde el punto de vista del analizador sintctico. Dicha secuencia es el puntode partida para que el analizador sintctico construya el rbol sintctico que reconocela/s sentencia/s de entrada, tal y como puede verse en la figura 2.1.

  • 7/22/2019 Java a Tope Traductores y Compiladores Con Lex Yacc JFlex Cup y .JavaCC

    36/319

    Anlisis lexicogrfico

    24

    Figura 2.1. Entradas y salidas de las dos primeras fases de la etapa de anlisis.

    La frase Secuencia de Terminales hace referencia a la gramtica del sintctico;pero tambin es posible considerar que dicha secuencia es de no terminales siusamos el punto de vista del lexicogrfico.

    El analizador lxico reconoce las palabras en funcin de una gramtica regularde manera que el alfabeto G de dicha gramtica son los distintos caracteres del juegode caracteres del ordenador sobre el que se trabaja (que forman el conjunto de smbolosterminales), mientras que sus no terminales son las categoras lxicas en que se integranlas distintas secuencias de caracteres. Cada no terminal o categora lxica de lagramtica regular del anlisis lxico es considerado como un terminal de la gramticade contexto libre con la que trabaja el analizador sintctico, de manera que la salida dealto nivel (no terminales) de la fase lxica supone la entrada de bajo nivel (terminales)de la fase sintctica. En el caso de Lex, por ejemplo, la gramtica regular se expresamediante expresiones regulares.

    2.2.1 Funciones del analizador lxico

    El analizador lxico es la primera fase de un compilador. Su principal funcinconsiste en leer los caracteres de entrada y elaborar como salida una secuencia decomponentes lxicos que utiliza el analizador sintctico para hacer el anlisis. Esta

    Figura 2.2. La fase de anlisis lxico se halla bajo el control del anlisissintctico. Normalmente se implementa como una funcin de ste

  • 7/22/2019 Java a Tope Traductores y Compiladores Con Lex Yacc JFlex Cup y .JavaCC

    37/319

    Java a tope: Traductores y compiladores con Lex/Yacc, JFlex/Cup y JavaCC

    25

    interaccin suele aplicarse convirtiendo al analizador lxico en una subrutina ocorrutina del analizador sintctico. Recibida la orden Dam e el siguiente componentelxicodel analizador sintctico, el lxico lee los caracteres de entrada hasta que pueda

    identificar el siguiente componente lxico, el cual devuelve al sintctico segn elformato convenido (ver figura 2.2).

    Adems de esta funcin principal, el analizador lxico tambin realiza otrasde gran importancia, a saber:

    Eliminar los comentarios del programa. Eliminar espacios en blanco, tabuladores, retorno de carro, etc, y en general,

    todo aquello que carezca de significado segn la sintaxis del lenguaje. Reconocer los identificadores de usuario, nmeros, palabras reservadas del

    lenguaje, etc., y tratarlos correctamente con respecto a la tabla de smbolos(solo en los casos en que este analizador deba tratar con dicha estructura).

    Llevar la cuenta del nmero de lnea por la que va leyendo, por si se producealgn error, dar informacin acerca de dnde se ha producido.

    Avisar de errores lxicos. Por ejemplo, si el carcter @ no pertenece allenguaje, se debe emitir un error.

    Tambin puede hacer funciones de preprocesador.

    2.2.2 Necesidad del analizador lxico

    Un buen profesional debe ser capaz de cuestionar y plantearse todas lasdecisiones de diseo que se tomen, y un asunto importante es el porqu se separa elanlisis lxico del sintctico si, al fin y al cabo, el control lo va a llevar el segundo. Enotras palabras, por qu no se delega todo el procesamiento del programa fuente slo enel anlisis sintctico, cosa perfectamente posible (aunque no plausible como veremosa continuacin), ya que el sintctico trabaja con gramticas de contexto libre y stasengloban a la regulares. A continuacin estudiaremos algunas razones de estaseparacin.

    2.2.2.1 Simplificacin del diseo

    Un diseo sencillo es quizs la ventaja ms importante. Separar el anlisislxico del anlisis sintctico a menudo permite simplificar una, otra o ambas fases.Normalmente aadir un analizador lxico permite simplificar notab lemente elanalizador sintctico. An ms, la simplificacin obtenida se hace especialmentepatente cuando es necesario realizar modificaciones o extensiones al lenguajeinicialmente ideado; en otras palabras, se facilita el mantenimiento del compilador amedida que el lenguaje evoluciona.

    La figura 2.3 ilustra una situacin en la que, mediante los patronescorrespondientes, el analizador lxico reconoce nmeros enteros y operadoresaritmticos. A la hora de construir una primera versin del analizador sintctico,

  • 7/22/2019 Java a Tope Traductores y Compiladores Con Lex Yacc JFlex Cup y .JavaCC

    38/319

    Anlisis lexicogrfico

    26

    Figura 2.3. Pasos en la construccin progresiva de un compilador

    podemos asumir que dos expresiones pueden ir conectadas con cualquiera de dichosoperadores, por lo que se puede optar por agruparlos todos bajo la categora lxicaOPARIT (Operadores ARITm ticos). En una fase posterior, puede resultar necesariodisgregar dicha categora en tantas otras como operadores semnticamente diferenteshaya: OPARIT desaparece y aparecen MAS, MENOS, MULT y DIV . Unamodificacin tal resulta trivial si se han separado adecuadamente ambos analizadores,ya que consiste en sustituir el patrn agrupado (-|+|*|/) por los patronesdisgregados -, +, * y /.

    Si el sintctico tuviera la gramtica del paso 1, el lexicogrfico sera:el patrn ( 0 | 1 | 2 | ... | 9) retorna la categora NUM+

    el patrn (+ | - | * | /) retorna la categora OPARIT

    En cambio, si el sintctico adopta el paso 2, el lexicogrfico sera:el patrn ( 0 | 1 | 2 | ... | 9) retorna la categora NUM+

    el patrn + retorna la categora MASel patrn - retorna la categora MENOSel patrn * retorna la categora MULT

    el patrn / retorna la categora DIV

    Tambin se puede pensar en eliminar el lexicogrfico, incorporando sugramtica en la del sintctico, de manera que ste vera incrementado su nmero dereglas con las siguientes:

    NUM 6 0| 1| 2| 3...

  • 7/22/2019 Java a Tope Traductores y Compiladores Con Lex Yacc JFlex Cup y .JavaCC

    39/319

    Java a tope: Traductores y compiladores con Lex/Yacc, JFlex/Cup y JavaCC

    27

    | NUM NUMsin embargo, los autmatas destinados a reconocer los componentes lxicos y al rbolsintctico son radicalmente diferentes, tanto en su concepcin como en su

    implementacin lo que, de nuevo, nos lleva a establecer una divisin entre estosanlisis.

    A modo de conclusin, se puede decir que es muy recomendable trabajar condos gramticas, una que se encarga del anlisis lxico y otra que se encarga del anlisissintctico. Dnde se pone el lmite entre lo que reconoce una y otra gramtica?, quse considera un componente bsico?, cuntas categoras gramaticales se establecen?Si se crean muchas categoras gramaticales se estar complicando la gramtica delsintctico, como ocurre p.ej. en el paso 2. En general, deben seguirse un par de reglasbsicas para mantener la comple jidad en unos niveles admisibles. La primera es que lainformacin asoc iada a cada categora lxica debe ser la necesaria y suficiente, lo quequedar ms claro en captulos posteriores, cuando se conozca el concepto de atributo .La segunda es que, por regla general, las gramticas que se planteen (regular y decontexto libre) no deben verse forzadas, en el sentido de que los distintos conceptosque componen el lenguaje de programacin a compilar deben formar parte de una o deotra de forma natural; p.ej., el reconocimiento que deba hacerse carcter a carcter (sinque stos tengan un significado semntico por s solos) debe formar parte del anlisislxico.

    2.2.2.2 Eficiencia

    La divisin entre anlisis lxico y sintctico tambin mejora la eficiencia del

    compilador. Un analizador lxico independiente permite construir un procesadorespecializado y potencialmente ms eficiente para las funciones explicadas en elepgrafe 2.2.1. Gran parte del tiempo de compilacin se invierte en leer el programafuente y dividirlo en componentes lxicos. Con tcnicas especializadas de manejo debufferspara la lec tura de caracteres de entrada y procesamiento de patrones se puedemejorar significativamente el rendimiento de un compilador.

    2.2.2.3 Portabilidad

    Se mejora la portabilidad del compilador, ya que las peculiaridades delalfabeto de partida, del juego de caracteres base y otras anomalas propias de los

    dispositivos de entrada pueden limitarse al analizador lxico. La representacin desmbolos especiales o no estndares, como 8 en Pascal, se pueden aislar en elanalizador lxico.

    Por otro lado, algunos lenguajes, como APL (A Program Language) sebenefician sobremanera del tratamiento de los caracteres que forman el programa deentrada mediante un analizador aislado. El Diccionario de Informtica de la OxfordUniversity Press define este lenguaje de la siguiente forma:

    ... Su caracterstica principal es que proporciona un conjunto m uy grandede operadores importantes para tratar las rdenes multidimensionales junto con la

  • 7/22/2019 Java a Tope Traductores y Compiladores Con Lex Yacc JFlex Cup y .JavaCC

    40/319

    Anlisis lexicogrfico

    28

    capacidad del usuario de definir sus propios operadores. Los operadores incorporados

    se encuentran representados, principalmente, por caracteres solos que utilizan un

    conjunto de caracteres especiales. De este modo, los programas APL son m uy concisos

    y, con frecuencia, impene trables.

    2.2.2.4 Patrones complejos

    Otra razn por la que se separan los dos anlisis es para que el analizadorlxico se centre en el reconocimiento de componentes bsicos complejos. Por ejemploen Fortran, existe el siguiente par de proposiciones muy similares sintcticamente, perode significado bien distinto:

    DO5I = 2.5 (Asignacin de l va lor 2.5 a la variable DO5I)DO 5 I = 2, 5 (Bucle que se repite para I = 2, 3, 4 y 5)

    En este lenguaje los espacios en blancos no son significativos fuera de loscomentarios y de un cierto tipo de cadenas (para ahorrar espacio de almacenamiento,en una poca de la Informtica en la que ste era un bien escaso), de modo quesupngase que todos los espacios en blanco eliminables se suprimen antes de comenzarel anlisis lxico. En tal caso, las proposiciones anteriores apareceran ante elanalizador lxico como:

    DO5I=2.5DO5I=2,5

    El analizador lxico no sabe si DO es una palabra reservada o es el prefijo delnombre de una variable hasta que se lee la coma. Ha sido necesario examinar la cadena

    de entrada mucho ms all de la propia palabra a reconocer haciendo lo que sedenomina lookahead (o prebsqueda). La complejidad de este procesamiento hacerecomendable aislarlo en una fase independiente del anlisis sintctico.

    En cualquier caso, en lenguajes como Fortran primero se dise el lenguajey luego el compilador, lo que conllev problemas como el que se acaba de plantear.Hoy da los lenguajes se disean teniendo en mente las herramientas de que se disponepara la construccin de su compilador y se evitan este tipo de situaciones.

    2.3 Token, patrn y lexema

    Desde un punto de vista muy general, podemos abstraer el programa que

    implementa un anlisis lxicogrfico mediante una estructura como:1 1(Expresin regular) {accin a ejecutar}

    2 2(Expresin regular) {accin a ejecutar}

    3 3(Expresin regular) {accin a ejecutar}... ...

    n n(Expresin regular) {accin a ejecutar}donde cada accin a ejecutar es un fragmento de programa que describe cul ha de serla accin del analizador lxico cuando la secuencia de entrada coincida con laexpresin regular. Normalmen