1 Parsing Un parser podría ser definido como un programa que analiza una porción de texto para determinar su estructura lógica: la fase de parsing en un.

Slides:



Advertisements
Presentaciones similares
ANALIZADOR SEMANTICO DIANA CAROLINA CASTAÑO YANCI VIVIANA CASTRO PEDRO LUIS FLÓREZ SANTIAGO GUTIÉRREZ A.
Advertisements

Complejidad Computacional
Compiladores e intérpretes Análisis Sintáctico II
Compiladores e intérpretes
Compiladores e intérpretes Análisis Sintáctico III
UML DCU -DS Alvaro Garrido V..
Curso de java básico (scjp)
Gramáticas Libres de Contexto
Interpretando objetos
DIAGRAMAS DE FLUJO Y PSEUDOCÓDIGO
1 Tipos inductivos y Pattern Matching Introducción a tipos inductivos y pattern matching Abstracciones con pattern matching. Traducción a cálculo lambda.
Variaciones sobre un evaluador Objetivo: Mostrar cómo un estilo de programación monádica puede capturar similitudes entre distintas variaciones de un elemental.
1 Agregando primitivas 2 Introducción Ahora presentaremos cómo agregar operaciones primitivas a la máquina G. Entenderemos como operaciones primitivas.
Programación monádica
Combinadores SK.
Tipo de Dato Abstracto Tipos de datos:
Procesadores de Lenguajes I
Taller matemático (Cálculo)
Tratamiento de listas en Java
Programación 1 Introducción
ALGORÍTMICA Dpto. Ingeniería de Sistemas y Automática
ANALISIS SINTACTICO DESCENDENTE
Teoría de lenguajes y compiladores
METODOLOGIA DE LA PROGRAMACION
Manipulación de caracteres e hileras de texto
La estructura básica de los programas en java, algo a tener en cuenta y que debemos recordar siempre es el archivo debe llevar el nombre de la clase con.

Métodos Algoritmos y Desarrollo de Programas I. Cuando utilizar métodos  Los métodos se utilizan para romper un problema en pedazos de forma que este.
Modificadores.
1 Procedimientos Es un conjunto de sentencias incluidas entre las declaraciones Sub o Function y End Sub/Function VB utiliza varios tipos de procedimientos:
POO Java Módulo 3 Elementos de programas Identificadores
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.
T A D : Módulos Los módulos proporcionan la única manera de construir tipos abstractos de datos (TAD) en Haskell.
Teoría de lenguajes y compiladores
TRAMPAS EN EL DISEÑO DE LENGUAJES CON RELACIÓN A LOS NOMBRES - REGLAS DE ALCANCE - COMPILACIÓN POR SEPARADO CONTROL DE FLUJO - EVALUACIÓN DE EXPRESIONES.
Clases y objetos La unidad fundamental de programación OO son las clases. Conjunto de métodos y semántica Qué se va a hacer POO Clase: que define la implementación.
Al término de la clase, el alumno reconoce las ventajas de usar JAVASCRIPT para un proyecto web.
ALGORITMOS es un conjunto preescrito de instrucciones o reglas bien definidas, ordenadas y finitas que permite realizar una actividad mediante pasos.
Semana 5 Subprogramas..
Formateador y Analizador de textos
ANALISIS SINTACTICO Parte I
Sintaxis.
Estructura de Datos y Algoritmos
TIPOS DE DATOS ABSTRACTOS

Clasificación de Gramáticas y Manejo de Errores
Agenda Clase 16 Motivación e Historia de la Programación Funcional y la Programación Lógica. Concepto y Características de la Programación Funcional. Ventajas.
Material de apoyo Unidad 4 Estructura de datos
Teoría de lenguajes y compiladores
Introducción al Análisis Sintáctico
Semantica.
Programación de Sistemas
Compiladores e intérpretes
Tema 1. Introducción y Conceptos Básicos
1 Compilación, pereza y expresiones let(rec) 2 Compilando un programa Describiremos un compilador para la máquina minimal usando un conjunto de esquemas.
Programación de Sistemas FEI – 2008
COLEGIO DE BACHILLERES PLANTEL 13 XOCHIMILCO-TEPEPAN MATERIA:TIC EQUIPO:21 PRESENTACION: BASE DE DATOS ALUMNAS: Adán Millán Sánchez.
UNIVERSIDAD LATINA (UNILA)
LE, EI, Profesor Ramón Castro Liceaga UNIVERSIDAD LATINA (UNILA) TRADUCTORES Y ANALIZADOR LEXICOGRÁFICO.
Teoría de lenguajes y compiladores
PRINCIPIOS DE PROGRAMACIÓN
6. Clases y Métodos..  6.1 Structs  6.2 Funciones  6.3 Namespaces  6.4 Clases.
árbol de problemas y objetivos
IV. GRAMÁTICAS DISTRIBUIDAS Y TABLAS DE SÍMBOLOS
Programación de Sistemas
 Las funciones son un conjunto de instrucciones que realizan una tarea específica. En general toman unos valores de entrada, llamados parámetros y proporcionan.
Prof. Jonathan Silva Ingeniería Civil – Informática I Ingeniería Civil Informática I Clase 3 – Diseño de Programas.
Programación en Java Introducción a Java. Reseña histórica Surge en 1991 por Sun Microsystems Desarrollado para electrodomésticos Se buscaba un código.
1 Clase 6: control (1ª parte) iic1102 – introducción a la programación.
Programación en Java Introducción a Java. Reseña histórica Surge en 1991 por Sun Microsystems Desarrollado para electrodomésticos Se buscaba un código.
Transcripción de la presentación:

1 Parsing Un parser podría ser definido como un programa que analiza una porción de texto para determinar su estructura lógica: la fase de parsing en un compilador toma el texto de un programa y produce un arbol sintáctico que representa la estructura del programa. Diferentes métodos para construir parsers, aquí presentaremos una metodología que ha sido ampliamente aceptada para ser usada en un contexto de programación funcional-perezosa. Parsers son modelados como funciones. Parsers complejos son obtenidos a partir de otros más simples haciendo un amplio uso de alto orden.

2 Parsing (2) Definiremos funciones de alto orden para representar secuencia, alternancia y repetición. Así, el código de un parser se asemejará notablementa a la notación BNF de la gramática que reconocen. Parsers en este estilo son muy fáciles de construir, simples de entender y modificar. Esta metodología se conoce con el nombre de combinator parsing. Características: Se pueden reconocer gramáticas ambiguas Backtracking Acciones semánticas

3 El tipo Parser Pensemos un parser como una función del siguiente tipo type Parser = String -> Tree Problema: no provee una forma natural para secuenciar parsers. En un parser para expresiones aritméticas uno quisiera buscar primero un número, luego un operador y luego otro número. Cada uno de estos procesos consume parte de la entrada. Una buena idea sería entonces refinar el tipo Parser de forma que el string de entrada no consumido sea retornado como parte del resultado : type Parser = String -> (Tree, String)

4 El tipo Parser (2) Otro aspecto a considerar: puede pasar que un parser falle tratando de reconocer un string de entrada, esto no es en principio un error. En una expresión aritmética podríamos querer buscar un símbolo de operación o un paréntesis que abre. En general, un parser puede encontrar diferentes formas en que una porción inicial de la entrada puede ser estructurada como un Tree. Falla, entonces, corresponde al caso particular en que no existen resultados de parsing. Refinamos entonces el tipo Parser de tal forma de poder retornar una lista de pares: type Parser = String -> [(Tree, String)]

5 El tipo Parser (3) El último refinamiento que efectuaremos está motivado por el hecho de que diferentes parsers pueden tanto operar sobre distintos tipos de entrada así como retornar diferentes clases de arboles sintácticos. Entonces abstraemos en los tipos Char y Tree, para definir a un parser como una función del siguiente tipo: type Parser a b = [a] -> [(b, [a])] Por ejemplo, un parser para expresiones aritméticas podría ser un objeto de tipo Parser Char Exp.

6 Parsers primitivos Estos son los bloques básicos de construcción de combinadores de parsing. El primero de ellos corresponde al símbolo de la notación BNF el que denota el string vacío. El parser succeed siempre es exitoso, y no consume ninguna porción del string de entrada: succeed :: b Parser a b succeed v inp = [( v, inp)] Ya que el resultado de succeed no depende de su entrada, el tipo de estos valores debe ser predeterminado, y es incluído como un parámetro extra.

7 Parsers primitivos (2) Mientras que succeed nunca falla, el parser fail siempre lo hace, independientemente de cual sea la entrada: fail :: Parser a b fail inp = [] La siguiente función nos permitirá construir parsers que reconocen un único símbolo. En vez de enumerar los símbolos aceptables, se provee un predicado que determina si el símbolo pertenece a este conjunto: satisfy :: (a bool) Parser a a satisfy p [] = fail [] satisfy p (x:xs) | p x = succeed x xs | otherwise = fail xs

8 Parsers primitivos (3) Usando satisfy podemos definir, como dicho anteriormente, parsers que aceptan un símbolo específico: literal :: a Parser a a literal x = satisfy (= x) Por ejemplo, si aplicamos el parser ( literal 3 ) a el string 345, esto nos da como resultado la lista [(3, 45)].

9 Combinadores En notación BNF gramáticas complejas son definidas a partir de otras de menor complejidad usando, por ejemplo, |, con el cual se denota alternancia y juxtaposición para denotar secuenciación. Definiremos funciones de alto orden que se corresponderán con los operadores arriba mencionados. Ya que estas funciones combinan parsers para construir otros parsers las llamaremos combinadores de parsing.

10 Combinadores (2) El combinador ` alt` corresponde a alternancia en BNF: infixr 4 `alt` (alt) :: Parser a b Parser a b Parser a b p1 `alt` p2 = \inp -> p1 inp ++ p2 inp El parser (p1 `alt` p2) reconoce todo lo que reconoce p1 o lo que reconoce p2 (inclusivamente). Se puede verificar fácilmente que: (fail `alt` p) = (p `alt` fail) = p (p `alt` q) `alt` r = p `alt`(q `alt` r) (heredada de ++)

11 Combinadores (3) El combinador `seq` correponde a secuencia en BNF: infixr 6 `seq` (seq) :: Parser a b Parser a c Parser a (b,c) p1 `seq` p2 = \ inp -> [((v1,v2), out2) | (v1,out1) p1 inp; (v2,out2) p2 out1 ] El parser (p1 `seq` p2) reconoce todo lo que reconocen p1 y p2 en secuencia. Ya que el primer parser puede terminar con varios resultados, cada uno con con su correpondiente string de entrada no consumido, el segundo parser debe ser aplicado a cada uno de estos resultados. Dos resultados son producidos, uno por cada parser. Por ejemplo, si aplicamos literala `seq` literal b a la entrada abcd esto da como resultado [((a, b), cd)].

12 Manipulación de valores Una porción del resultado de un parser es un valor. El combinador using nos permite manipular valores, donde la creación de un árbol sintáctico es una de las aplicaciones más comunes: (using) :: Parser a b (b c) Parser a c p `using` q = \inp -> [(f v, out) |(v,out) p inp] El parser (p `using` f) se comporta como el parser p salvo que la función f es aplicada a cada uno de los valores de su resultado. Aunque `using` no tiene contraparte en notación BNF tiene mucho en común con el operador {...} de Yacc.

13 Ignorando valores y Repetición Dos distintas versiones de ` seq ` que son muy útiles son ` getl ` and `getr`, las que ignoran uno de los valores retornados por los sub- parsers: infixr 8 `getl`, `getr` (getl) :: Parser a b Parser a c Parser a b p1 `getl` p2 = p1 `seq` p2 `using` fst (getr) :: Parser a b Parser a c Parser a c p1 `getl` p2 = p1 `seq` p2 `using` snd El combinador many toma como argumento un parser y lo aplica repetidamente hasta que falla: many :: Parser a b Parser a [b] many p = p `seq` many p `using` (\(x,xs) -> x : xs) `alt` succeed [] some p = p `seq` many p `using` cons

14 Combinadores de parsing monádicos Un combinador que ha revelado ser muy útil y que no hemos presentado es `into`, el que permite no sólo secuenciar dos parsers sino que además puede hacer uso del resultado del primer parser en la definición del subsecuente parser: infixr 6 `into` (into) :: Parser a b ( b Parser a c) Parser a c p1 `into` p2 = \ inp -> [(v2, out2 | (v1,out1) p1 inp ; (v2,out2) p2 v1 out1] p1 `seq` p2 = p1 `into` \v -> p2 `using` \w -> (v,w) p `using` f = p `into` \v -> succeed f Pero...

15 Combinadores... (2) Un combinador más interesante de definir es `ap`, el que se puede entender como aplicación, pero de parsers: infixr 6 `ap` (ap) :: Parser a (b c) Parser a b Parser a c p1 `ap` p2 = p1 into \f -> p2 into \x -> succeed (f x) Nota: ` into ` y succeed se pueden entender como >>= y return Ahora es posible, entonces, definir a many como un combinador monádico: many p = succeed (:) `ap` p `ap` many p `alt` succeed [] infixr 6 `ap` (chk) :: Parser a b Parser a c Parser a b p1 `chk` p2 = p1 `into` \v -> p2 \ _ -> succeed v

16 Una versión de la mónada Parser newtype Parser a b = MkP ([a] [ (b, [a]) ]) apply :: Parser a b [a] [ (b, [a]) ] apply (MkP f) inp = f inp applyParser :: Parser a b [a] a applyParser = fst. head. apply La función applyParser retorna el primer componente del primer elemento en una lista de árboles sintácticos. instance Monad (Parser a) where return x = MkP f where f = \inp -> [(x,inp)] p >>= q = MkP f where f = \inp -> [(v2, out2 | (v1,out1) apply p inp ; (v2,out2) apply (q v1) out1]

17 Combinando parsers con do Habiendo declarado ( Parser a ) como una mónada, ahora podemos combinar parsers usando notación do : item :: Parser a a item = MkP f where f [] = [] f (x:xs) = [(x,xs)] fail :: Parser a b fail = MkP f where f inp = [] satisfy :: (a Bool) Parser a a satisfy p = do c item if p c then return c else fail digit :: Parser Char Int digit = do d satisfy isDigit return (ord d - ord 0) }