M.C. Juan Carlos Olivares Rojas

Slides:



Advertisements
Presentaciones similares
ÁRBOLES DE SINTAXIS ÁRBOL grafo dirigido acíclico.
Advertisements

Análisis Sintáctico Capítulo 4.
INSTITUTO TECNOLÓGICO DE MINATITLÁN
Filminas Segunda semana
Compiladores e intérpretes Análisis Sintáctico II
Compiladores e intérpretes
Gramáticas Libres de Contexto
Teoría de Autómatas y Compiladores
¿Cómo hacer para que una máquina comprenda el LN?
Analizadores Sintácticos Descendentes Predictivos
Técnico en programación de Software
Unidad 3. Análisis Sintáctico
Traducción dirigida por la Sintaxis
ANALISIS SINTACTICO DESCENDENTE
Teoría de lenguajes y compiladores
CAPITULO 2 La Representación del Conocimiento
Tema 4. Gramáticas y Análisis Sintáctico PARSER
ANALISIS SINTACTICO El análisis gramatical es la tarea de determinar la sintaxis, o estructura, de un programa. Por esta razón también se le conoce como.
M.C. Juan Carlos Olivares Rojas
Teoría de lenguajes y compiladores
Analizador Sintáctico Descendente
Al término de la clase, el alumno reconoce las ventajas de usar JAVASCRIPT para un proyecto web.
Tema 2 Lenguajes Formales.
Programación de sistemas
UNIVERSIDAD DE MANAGUA I CUATRIMESTRE INTRODUCCION A LA PROGRAMACION
M.C. Meliza Contreras González
Análisis sintáctico LR: SLR (LR simple)
Procesadores del Lenguaje
M.C. Juan Carlos Olivares Rojas
Teoría de lenguajes y compiladores
FUNDAMENTOS DE PROGRAMACION
ANALISIS SINTACTICO Parte I
Clasificación de Gramáticas y Manejo de Errores
ESTRUCTURA DE DATOS ESD-243
Teoría de lenguajes y compiladores
Teoría de lenguajes y compiladores
Todo traductor esta basado en una gramática para el lenguaje fuente. Todo traductor esta basado en una gramática para el lenguaje fuente. Una gramática.
Introducción al Análisis Sintáctico
Teoría de lenguajes y compiladores Analizadores lexicográficos
Compiladores e intérpretes Análisis Sintáctico III
ETAPAS PARA SOLUCIONAR UN PROBLEMA
Programación de Sistemas
Compiladores e intérpretes
Tema 1. Introducción y Conceptos Básicos
Análisis Léxico Área Software de Base.
Unidad 1: FUNDAMENTOS DE COMPUTACIÓN Y PSEUDOLENGUAJE
Elementos básicos del lenguaje
Programación de Sistemas FEI – 2008
Unidad 1. Introducción a los Compiladores.
Términos algoritmo diseñar algoritmo implementar algoritmo
ELEMENTOS DE COMPUTACIÓN Profesor: Guillermo Figueroa
UNIVERSIDAD LATINA (UNILA)
1 Sebastián Argüello A60490 semana 3 Autómatas y compiladores CI-1322.
Teoría de lenguajes y compiladores
CARACTERÍSTICAS Es un lenguaje de programación estructurado de propósito general. Está estrechamente asociado al sistema operativo UNIX, ya que el propio.
Teoría de lenguajes y compiladores
I.- ESTUDIO DE LOS LENGUAJES DE PROGRAMACIÓN.
Elaboración de algoritmos usando lógica de programación
Teoría de lenguajes y compiladores
El proceso de compilación
Presente un cuestionario con los aspectos mas importantes sobre los
Metodología de la programación
IV. GRAMÁTICAS DISTRIBUIDAS Y TABLAS DE SÍMBOLOS
Programación de Sistemas
REPÚBLICA BOLIVARIANA DE VENEZUELA MINISTERIO DEL PODER POPULAR PARA LA EDUCACION SUPERIOR UNIVERSIDAD VALLE DEL MOMBOY CARVAJAL EDO. TRUJILLO ENERO 2014.
República Bolivariana de Venezuela Ministerio del Poder Popular para la Educación Superior Universidad Valle del Momboy Carvajal, Trujillo Alumnas Luzmila.
INFORMÁTICA II TEMA: DISEÑA Y ELABORA ALGORITMOS DOCENTE: MARÍA DOLORES GARCÍA PONCE CAMPUS TONALÁ JALISCO.
Tema 8: Programación estructurada Introducción La importancia del conocimiento de programación es mucho mayor de lo que se piensa. Aprender a programar.
Sintaxis y Semántica. S.Takahashi Fases en el proceso de análisis de lenguajes Lexer Parser caracteres tokensrespuesta.
ALGORITMOS Y PROGRAMAS. OBJETIVOS  Resolver problemas mediante la especificación algorítmica.  Proporcionar los procedimientos y técnicas para el desarrollo.
Transcripción de la presentación:

M.C. Juan Carlos Olivares Rojas Análisis Sintáctico M.C. Juan Carlos Olivares Rojas

Agenda Introducción a las Gramáticas libres de contexto y árboles de derivación. Diagramas de sintaxis. Precedencia de operadores. Analizador sintáctico. Analizador descendente (LL). Analizador ascendente(LR, LALR).

Agenda Administración de tablas de símbolos Manejo de errores sintácticos y su recuperación Generadores de código para analizadores sintácticos: Yacc, Bison

Introducción a las GICs y Árboles de Derivación Todo lenguaje posee una serie de reglas para describir los programas fuentes (sintaxis). Un analizador sintáctico implementa estas reglas haciendo uso de GICs (Gramáticas Independientes del Contexto).

Gramáticas Son un formalismo matemático que permite decidir si una cadena pertenece a un lenguaje dado. Se define como la cuarteta G= (N, Σ, S, P), en donde N es el conjunto de símbolos terminales, Σ es conjunto de símbolos terminales, S es el símbolo inicial (S pertenece a N) y P es un cojuntode reglas de producción.

Gramáticas Los símbolos no terminales (N) son aquellos que pueden seguir derivando en otros; mientras que los terminales el proceso finaliza allí. Las reglas de producción siguen el formato: αβdonde α y βpertenecen a N y Σen cualquier forma.

Reglas de Producción Son las reglas que permiten decidir si la cadena pertenece a un lenguaje y la estructura que lleva: SA | aB Bε AaA | bC Cε S Genera cadenas del lenguaje a*b u a

Tipos de gramáticas Las gramáticas más sencillas son las gramáticas regulares, debido a que no presentan anomalías de ningún tipo. Desafortunadamente este tipo de gramáticas no permiten expresar todos los lenguajes posibles y en especial los humanos por lo que se necesitan otros tipos de gramáticas. Las más utilizadas en informáticas son las libres del contexto.

Gramáticas Regulares Son las que se forman a través de Autómatas Finitos Deterministas y Expresiones regulares. No presentan ambiguedades. Sus reglas de producción son del tipo: αβ donde α pertenece a N y β pertenece a (Σ)* N?

GICs Son aquellas G cuya reglas de producción son de la forma: αβ, en donde αpertenece a N y βpertenece (N u Σ)* Las ventajas de uso de GICs son: Proporcionan una estructura sintáctica precisa y fácil de comprender. Proporciona al lenguaje fuente una estructura adecuada para la generación del código. Por medio de las GICs es fácil construir analizadores sintácticos.

GICs Hay que revisar que la gramática no sea inherentemente ambigua para poder eliminar esa ambigüedad o rediseñar la gramática sin anomalías. Algunas formas de eliminar esa ambigüedad es utilizando técnicas como algoritmos CYK y las formas normales de Chomsky(FNCh) y Greibach(FNG).

Ejemplo de GICs Expresiones válidas en lenguajes C: expr  (expr) | - expr | expr op expr | VAR | NUM Error sintáctico: cuando la secuencia de componentes léxicos no puede ser generada por la gramática del lenguaje fuente.

Ejemplos de GICs Declaración de variables en C: Decl TIPO listavar PYC listavarvar | var COMA listavar varID | ASTER var dimensionCI ENTERO CD | CI ENTERO CD dimension

Gramática de un if prop if expr then prop | if expr then prop else prop | ε exprtermino oprel termino | termino termino id | num oprel  < |>|=|<=|<>|>=| idletra (letra | digito)*

Gramática de un if num digito+ (.digito+)? (E(+|-)? digito+)? Eb delim+ Delim blanco | tab | linenueva

Jerarquía de Chomsky Las otras dos gramáticas en las cuales clasificó Chomsky (GR tipo 3, GIC tipo 2) son las gramáticas sensible al contexto (tipo 1, donde |α| < |β|, donde α y β pertenecen a (Σ u N)* salvo ε) y las gramáticas del tipo 0 o sin restricciones, las cuales sus reglas de producción pueden ser de cualquier tipo.

Árboles de Derivación Es la representación gráfica de la derivación de una cadena. Se crea utilizando el símbolo inicial como la raíz, los símbolos N representan nodos del árbol y los símbolos Σ las hojas del árbol. A través de los árboles de derivación se puede verificar la sintaxis de un lenguaje así como comprobar el significado de las palabras.

Árboles de Derivación Si para la misma cadena existen dos o más árboles de derivación la gramática es ambigua.

BNF La Forma Backus-Naur es una meta-sintaxis; es decir, una sintaxis para representar sintaxis. Es un estándar para representar lenguajes. Los paréntesis triangulares < y > sirven para indicar los símbolos no terminales. La barra vertical | para representar Ó

BNF La doble flecha  indica las derivaciones ::= indica las producciones [] indican elementos opcionales {} indican términos repetitivos

BNF <postal-address> ::= <name-part> <street-address> <zip-part> <name-part> ::= <personal-part> <last-name> <opt-jr-part> <EOL> | <personal-part> <name-part> <personal-part> ::= <first-name> | <initial> "."

BNF <street-address> ::= <house-num> <street-name> <opt-apt-num> <EOL> <zip-part> ::= <town-name> "," <state-code> <ZIP-code> <EOL> <opt-jr-part> ::= "Sr." | "Jr." | <roman-numeral> | ""

EBNF Extended Backus Naur Form es una metasintaxis ampliamente utilizada que mejora a su antecesor BNF. Ha cambiado la forma de realizar la especificación de las reglas de producción de la gramática. La motivación para usar EBNF radica que con BNF los elementos repetitivos necesitan de más reglas de producción para trabajar.

EBNF Las reglas de producción pueden contener espacios. Los símbolos terminales se representan con comillas dobles (“”) cuando representan un símbolo del alfabeto y comillas simples (‘’) para representar cadenas El operador de producción ahora es el símbolo de igual (=)

EBNF Se recomienda escribir los símbolos no terminales en minúsculas. Cada regla de producción termina con el símbolo de punto y com (;). El operador | indica una alternativa de regla de producción.

EBNF digito sin cero = “1” | “2” | “3” | “4” | “5” | “6” | “7” | “8” | “9”; digito = “0” | digito sin cero Las comas (,) sirven para separar tanto terminales como no terminales de las reglas de producción. Las llaves ({}) indican elementos repetitivos (operador estrella: *)

EBNF natural = digito sin cero, {digito}; Los corchetes ([]) se manejan para elementos auxiliares. entero = “0” | [“-”], natural Entre símbolos de interrogación (?) se pueden poner símbolos especiales.

EBNF Un espacio en blanco se define como: espacio = ? US-ASCII character 32 ?; Se pueden poner comentarios con los símbolos (* comentario *) Los paréntesis “(” y “)” se utilizan para agrupar símbolos. El símbolo “-” sirve para expresar excepciones.

EBNF Se utiliza “*” para indicar repeticion, por ejemplo regla = “A”; repetición = 3 * aa, “B”; Si se deriva la regla de producción repetición la cadena generada sería: AAAB

EBNF Se pueden anidar operadores como *, {} y [] para lograr cualquier tipo de repetición. Tanto BNF como EBNF pueden determinar cualquier tipo de gramática, sencillamente EBNF permite simplificar y tener menos ambigüedad en la metasintaxis.

Diagramas de Sintaxis Es otra forma (al igual que los árboles de derivación) de especificar gramáticas de cualquier tipo en especial de tipo 2. La característica de este esquema es que permite ver las derivaciones al instante de que ocurren. Es una forma visual de representar la gramática de un lenguaje.

Diagramas de Sintaxis

Precedencia de Operadores La precedencia de operadores es de vital importancia en el proceso de análisis sintáctico ya que nos representará la forma en que debe construirse el árbol de derivación. En aritmética existen prioridades, por ejemplo: * y / tienen preferencia sobre + y -. () indican la máxima prioridad.

Precedencia de Operadores La instrucción a = b + c / 2 en la mayoría de los lenguajes no se evalúa de la forma a = (b + c) /2, sino de la forma a = b + (c/2) La forma de evaluación depende de cómo se construyan los operadores, ya sea en infijo, postfijo o prefijo. Las operaciones se realizan de abajo hacia arriba.

Precedencia de Operadores Podría pensarse que esta parte corresponde a la parte semántica dado que el resultado de las operaciones depende del orden. Al hablar de orden se hace referencia al aspecto sintáctico y gramatical. Las reglas gramaticales de mayor prioridad deben de definirse primero. ¿Cómo se diferencia el operador “-” binario del unario?

Tarea UML es el lenguaje de modelado unificado. Tiene la característica principal de ser visual y extensible. Al ser un lenguaje formal debe de contener una estructura bien definida, en este caso vocabulario (alfabeto) bien definido y reglas gramaticales para definir el orden o sintaxis de los elementos así como una semántica o significado propio.

Tarea Realizar un trabajo de investigación que contenga la definición de una gramática visual y sus características. Además, se debe de investigar de un diagrama UML en particular (versión 2.0 o superior) los elementos de léxico, sintaxis y semántica. Se deberá preseleccionar (orden en que lleguen los correos) el tipo de diagrama.

Tarea La tarea se entregará el martes 3 de noviembre. La tarea es individual. Se recomienda consultar libros de UML sólo se deberá tener cuidado de encontrar como la sintaxis y semántica del documento (se puede utilizar una herramienta de diagramación pero quizás no cumpla con el estándar).

Analizador Sintáctico Un analizador sintáctico (Parser) es un programa que reconoce si una o varias cadenas de caracteres forman parte de un determinado lenguaje. Los lenguajes habitualmente reconocidos por los analizadores sintácticos son los lenguajes libres de contexto.

Analizador Sintáctico Los analizadores sintácticos fueron extensivamente estudiados durante la década de 1970, detectándose numerosos patrones de funcionamiento en ellos, cosa que permitió la creación de programas generadores de analizadores sintácticos a partir de una especificación de la sintaxis del lenguaje, tales como YACC, GNU bison y javacc.

Análisis Sintáctico Es el proceso de determinar si una cadena dada puede ser generada por una gramática. Los analizadores sintácticos de lenguajes de programación suele hacerse de izquierda a derecha, viendo un componente léxico a la vez.

Análisis Sintáctico

Análisis Sintáctico Los analizadores pueden clasificarse dependiendo de la forma en como se construyen los nodos del árbol de derivación sintáctico: ascendentes y descendentes. El análisis sintáctico impone una estructura jerárquica. Sea la expresion: id1 := id2 + id3 * 60

Análisis Sintáctico Dicha expresión queda expresada en un árbol sintáctico de la siguiente forma: := id1 + id2 * id3 60

Tipos de Analizadores Sintácticos LL (left toleft) leen la cadena de izquierda a derecha y derivan por la izquierda LR (lefttoright) SaA AaBbC Bb Cc

Analizador Descendente Existen diferentes métodos de análisis sintáctico. La mayoría caen en una de dos categorías: ascendentes y descendentes. Los ascendentes construyen el árbol desde las hojas hacia la raíz. Los descendentes lo hacen en modo inverso. Un analizador ampliamente utilizado es el llamado de análisis predictivo descendente recursivo que es muy sencillo.

Analizador Descendente Derivación izquierda: SAaaBbCaabbCaabbc(1234) SaAaaBbaaBbcaabbc(3421) LL(k) traductores “top-down” Un análisis anticipado de k caracteres (predictivo de caracteres)

Analizador Descendente SaS | cA AbA | cB | ε BcB |a | ε Construir la cadena acbb: SaS o ScA; al anticipar el primer símbolo.

Analizador Descendente

Administración de la Tabla de Símbolos La tabla de símbolos se crea durante la fase de análisis léxico a través de los componentes léxicos, pero en el proceso de análisis sintáctico sufren algunas modificaciones. Generalmente se agregan valores de tipo y significado para el análisis sintáctico.

Manejo de errores sintácticos y su recuperación Si los traductores tuvieran que procesar programas correctas el proceso de implantación se simplificaría mucho. ¿Cómo debe de responder un compilador de pascal a un código Fortran? Ningún método de recuperación de errores resuelve todos los problemas

Tipos de Errores Léxicos: como escribir mal un identificador, palabra clave u operador. Sintácticos: como una expresión aritmética con paréntesis no equilibrados. Semánticos: como un operador aplicado a un operando incompatible. Lógicos: como una llamada infinitamente recursiva.

Tipos de Errores La mayoría de los errores se centra en la fase de análisis sintáctico. El manejador de errores debe: Informar la presencia de errores con claridad y exactitud. Brindar una posible solución al problema.

Administrador de Errores Recuperar de cada error con la suficiente rapidez como para detectar errores posibles. No debe retrasar de manera significativa el procesamiento de programas correctos. Debe indicar la línea del error y algún mensaje informativo

Estrategia de Recuperación de Errores Modo Pánico Nivel de Frase Producciones de error Corrección global

Recuperación en Modo Pánico Es el más sencillo de implantar. El analizador sintáctico desecha componentes léxicos hasta encontrar un carácter de sincronización. Estos caracteres son el punto y como (;) entre otros. En C/Java es muy común este tipo de errores.

Recuperación en Modo Pánico int a.b,c; struct c { …. } main() { int a;

Recuperación a nivel de frase Esta técnica utiliza una corrección de caracteres adyacentes, ya sea por inserción, eliminación o intercambio. Esta técnica permite sustituir “,” por “;”, etc. Son traductores que corrigen errores. Desafortunadamente para muchos casos no aplican por lo que no se utilizan demasiados.

Producción de Errores Se pueden generar gramáticas para generar producciones de error y así de esta forma seguir con el proceso. La dificultad radica en el sentido de encontrar esas reglas gramaticales para generar error. En algunos casos sería inclusiva más extensa que la gramática del propio lenguaje. for(i<3, a<10; i++)

Corrección Global Idealmente, sería recomendable que un traductor hiciera el mínimo de cambios para procesar una entrada inválida. Este algoritmo genera menores costos globales para realizar cambios. El problema radica en que el implementar estas estrategias son muy costosas en tiempo y espacio.

Generadores de Analizadores Sintácticos: YACC & Bison YACC (YET ANOTHER COMPILER- COMPILER): es una herramienta que nos permite validar lenguajes a través de la especificación de reglas gramaticales. Esta fuertemente relacionado con Lex para la identificación de caracteres extraños.

YACC La forma de trabajar con yacc es la siguiente: Analizador.y (#include“lex.yy.c”)  bison  analizador.c (y.tab.c)  gcc analizador $gcc analizador.c –o analizador –lfl Se necesita modificar el valor de retorno en el analizador léxico para trabajar de manera conjunta.

Estructura de un programa en YACC/BISON %{ Declaraciones globales C }% Declaraciones bison %% Gramáticas Nombre:prod1|prod2|…|prodn; Código auxiliar C

Consejos Todo lexema debe ser un entero: #define VAR 200 (256) return (VAR); Gramática vacía: prod1| prod2| ; Es sumamente sensible a los separadores.

Consejos Yacc no es una herramienta que se caracterice por optimizar gramáticas. Por lo tanto, se pueden presentar algunos problemas de ambigüedad: Reduce/Reduceambigüedad infinita Shift/Reduce A continuación se muestran algunos ejemplos de Yacc.

Analizador.lex %{ #include“ytab.h” }% sp [\n\r\t] if [i][f] %% {if} {return(IF);} “(” {return(PI); } . return(ERROR);

ytab.h /*Se deben colocar todos los tokens*/ #define IF 1 #define

Analizador.y %{ #include“lex.yy.c” }% %token IF PI PD LLI LLD %token ID NUM OPREL OPLOG %% programa: linea programa | ; Linea: iflinea | ; if: ifPI condicionPD LLI campo LLD ;

Analizador.y .: {printf(“Error sintáctico”);} %% main(intargc, char*argv[]) { FILE *f = fopen(argv[1], “r”); yyin= f; while(yyparse()); fclose(f); }

Compilación con Yacc $flexanalizador.lex $bisonanalizador.y $gccanalizador.c –o analizador –lfl yytextcomponente léxico yyinflujo de entrada yylinenolínea de error

Manejo de errores con Yacc %% yyerror() { printf(“Error sintáctico en %dlinea”, yylineno); } En general, el manejo de errores se realiza de forma trivial. De lo contrario tendría que ser más específico.

CUP Es el generador de analizadores sintácticos más populares para Java. Al igual que con lex, los archivos de JLEX deben de modificarse para poder trabajar en conjunto. Se deben devolver valores de la clase sym, además de incluir la biblioteca de cup. A continuación se muestra un ejemplo de un analizador sintáctico de expresiones.

CUP import java_cup.runtime.SymbolFactory; %% %cup %class Scanner %{ public Scanner(java.io.InputStream r, SymbolFactory sf){ this(r); this.sf=sf; }

CUP private SymbolFactory sf; %} %eofval{ return sf.newSymbol("EOF",sym.EOF); %eofval} %% ";" { return sf.newSymbol("Semicolon",sym.SEMI); } "+" { return sf.newSymbol("Plus",sym.PLUS); } "*" { return

CUP sf.newSymbol("Times",sym.TIMES); } "(" { return sf.newSymbol("Left Bracket",sym.LPAREN); } ")" { return sf.newSymbol("Right Bracket",sym.RPAREN); } [0-9]+ { return sf.newSymbol("Integral Number",sym.NUMBER, new Integer(yytext())); } [ \t\r\n\f] { /* ignore white space. */ } . { System.err.println("Illegal character: "+yytext()); }

CUP En cup se definen las gramáticas en una sintaxis muy similar a BNF. El archivo define algunas secciones para código auxiliar y definición de terminales y no terminales. Para compilar se debe de incluir en el path la ruta de la biblioteca de cup. El archivo generado por cup se denomina parser.java y sym.java que se compilan con el lexer.java

CUP import java_cup.runtime.*; parser code {: public static void main(String args[]) throws Exception { SymbolFactory sf = new DefaultSymbolFactory(); if (args.length==0) new parser(new Scanner(System.in,sf),sf).parse(); else new parser(new Scanner(new java.io.FileInputStream(args[0]),sf),sf).parse();

CUP } :} terminal SEMI, PLUS, TIMES, LPAREN, RPAREN; terminal Integer NUMBER; non terminal expr_list, expr_part; non terminal Integer expr; precedence left PLUS; precedence left TIMES;

CUP expr_list ::= expr_list expr_part | expr_part; expr_part ::= expr:e {: System.out.println(" = "+e+";"); :} SEMI; expr ::= NUMBER:n {: RESULT=n; :} | expr:l PLUS expr:r {: RESULT=new Integer(l.intValue() + r.intValue()); :} | expr:l TIMES expr:r {: RESULT=new Integer(l.intValue()

CUP * r.intValue()); :} | LPAREN expr:e RPAREN {: RESULT=e; :} ;

Examen Escrito: 70% Próxima unidad se juntará análisis sintáctico y semántico del ensamblador de Java. Etiquetas para unidad 5: aconst_null, aload, bipush, breakpoint, cmpgd, cmpldconst, iadd, iaload, iand, iastore, iconst, idiv, if_acmpeq, if_acmpne, if_icmpeq, if_icmpge, if_icmpgt, if_icmple,

Examen if_icmplt, if_icmpne, ifeq, ifge, ifgt, ifle, iflt, ifne, ifnonnull, ifnull, iinc, iload, iload_<n>, imul, ineg, ishl, ishr, istore, istore_<n>, isub, iushr, ixor, jsr, jsr_w.

Pendientes Práctica 9: viernes programa para reconocer if en yacc/cup.

Referencias Aho, Sethi, Ullman. Compiladores Principios, técnicas y herramientas Ed. Addison Wesley. Beck,. Software de Sistemas, Introducción a la programación de Sistemas Ed. Addison-Wesley Iberoamericana. Kenneth C. Louden. Construcción de compiladores Principios y práctica. Ed. Thomson.

Referencias EBNF, Wikipedia la Enciclopedia Libre, http://www.wikipedia.org/ Recuperado en octubre de 2007.

¿Preguntas?