introducción a dsl (lenguajes específicos de dominios) con python

Download Introducción a DSL (Lenguajes Específicos de Dominios) con Python

If you can't read please download the document

Upload: juan-rodriguez

Post on 07-Apr-2017

822 views

Category:

Engineering


2 download

TRANSCRIPT

Juan Ignacio Rodrguez de [email protected] @jileon

Qu es un DSL

Lenguaje Especfico de Dominio (Domain Specific Language):Dominio: El problema que queremos resolver

Especfico: Solo para ese dominio

Lenguaje: Para ser escrito y ledo por personas

Los problemas se pueden resolver con un lenguaje de propsito general, pero...

Especificar soluciones

A veces, puede ser una buena opcin crear un lenguaje especfico para ese problema (o sea, un DSL)

Ese lenguaje permite expresar la solucin de una forma ms sencilla

Ejemplo 1: SQL

Dominio: Extraer informacin de un sistema de base de datos relacional

SELECT * FROM VENTAS WHERE precio Begin line+ End

line --> id num

id --> [A-Za-z][A-Za-z0-9]*

num --> [0-9]+

Otra gramtica

La primera regla es la regla inicial:

Program --> Begin lines End

lines line+

line --> id num

id --> [A-Za-z][A-Za-z0-9]*

num --> [0-9]+

Syntax Chart

Nomenclatura y otras yerbas

Cualquier smbolo que aparezca a la izquierda de una flecha es un smbolo No Terminal.

Cualquier smbolo que no aparezca en la parte izquierda de ninguna regla es un Terminal o Literal

En la parte de la derecha de la regla pueden aparecer terminales, no terminales y smbolos especiales

Smbolos especiales

|Representa alternativas, a|b significa que se puede generar a o b

*Significa repeticin de cero o ms

+ Significa repeticin de uno o ms

?Opcional (uno o cero)

[a-z] Rango de caracteres [0-9] se podra representar como 0|1|2|3|4|5|6|8|9

Parser Recursivo Descendente

Hay varios tipos de parsers

Uno de los ms simples es el Parser Recursivo Descendente

Es tan simple que se puede escribir a mano

Vamos a hacerlo

Parser LL(1)

Necesitamos definir una funcin para cada smbolo no terminal de la gramtica

El parser consiste en una serie de llamadas recursivas

En nuestro caso, debemos escribir una funcin para cada uno de los smbolos:program

line

id

num

Adems, necesitamos:

Que el tokenizer haya hecho su trabajo, y nos haya entregado una lista o cola de tokens (Lo simularemos)

Una funcin que nos permita examinar el siguiente token a procesar

Una funcin auxiliar, consume, que retira un elemento de la lista de tokens si es el que esperamos. Si no lo fuera, da un error.

Lista de Tokens

Token = namedtuple('Token', ['kind', 'value'])

buffer = [ Token('BEGIN', 'Begin'), Token('ID', 'Bob'), Token('NUM', 12), Token('ID', 'Alice'), Token('NUM', 26), Token('END', 'End'), ]

pos = 0

Funcin auxiliar reduce

def consume(token): global buffer, pos tk = buffer[pos] if tk.kind != token: raise ParserError(...) pos += 1 return True

Funcin program

def program(): begin() r = line() while r: r = line() end() if not eof(): raise ParserError(...) return True

Funcin begin

def begin(): return consume('BEGIN')

Funcin line

def line(): global buffer, pos tk = buffer[pos] if tk.kind == 'ID': pos += 1 tk = buffer[pos] if tk.kind == 'NUM': pos += 1 else: raise ParserError(...) return True return False

Funcin end

def end(): return consume('END')

Ventajas

Muy fcil de escribir: se siguen unas reglas para cada tipo de smbolo y regla de la gramtica.

Fcil de entender

Si cambia la gramtica, se cambia el programa

La primera vez es divertido

Inconvenientes

Los detalles de la gramtica estn perdidos en el cdigo; no contamos con una representacin explcita de la gramtica

Si la gramtica es complicada, es tedioso

Algunas gramticas no se puede parsear as. Las reglas recursivas, de tipo expr expr (+|-|*|+) expr

hacen que el parser entre un un bucle sin finLa dcima vez ya no es tan divertido...

Soluciones

Afortunadamente, hay formas de generar un parser automticamente a partir de la gramtica.

El parser que hemos hecho es de tipo LL. La segunda L indica que hacemos un anlisis descendente, es decir, empezamos en la regla inicial y bajamos

Otro tipo de parsers son los LR o ascendentes, en este caso, comenzamos desde la parte derecha de las reglas y vamos subiendo hasta la regla inicial.

Caractersticas de las parsers LR

El anlisis se hace de forma ascendente, por lo que pueden usarse reglas gramaticales recursivas

La gramtica pueda ser ms sencilla (Pero aun hay ciertos problemas, por ejemplo con gramticas ambiguas)

En general, ms poderosos que los LL

Existe formas automticas de generarlos, igual que los LR

Tcnicas de generacin de parser

No tenemos tiempo de ver ms de las tcnicas de creacin de parsers, pero como se pueden imaginar, es un campo amplio y complejo.

Lo bueno es que no nos hace falta. Podemos usar un generador

Algunos generadores de parser

Lex y Yacc; Lex se encarga del tokenizer y Yacc del parser. Generan cdigo C y el parser resultante es LR

Flex y Bison son similares, pero generan C++

ANTL-R es una herramienta escrita en Java pero que puede general cdigo en Python, entre otros lenguajes. Genera parsers LL(K).

Hay muchsimos ms...

Centrndonos en Python

Tambin hay muchos, algunos de los ms destacados son:Plex

Grako

PLY

Pyparsing

Muchos ms ...

http://nedbatchelder.com/text/python-parsers.html

Pyparsing

Vamos a ver como resuelve Pyparsing el lenguaje anterior

La idea es componer, a base de elementos ms pequeos, el parser

La estructura refleja la gramtica (pero al revs, la regla inicial es la ltima, para evitar referencias futuras)

Pyparsing

from pyparsing import (Literal, Word, nums, alphas, Optional, OneOrMore, Group)

num = Word(nums)id = Word(alphas)line = id + numlines = OneOrMore(line)begin = Literal('Begin')end = Literal('End')program = begin + lines + end

Cambios en la gramtica

Begin Bob 12 Alice 26 Charles 33+22 Daniel 12-133End

Fcil de reflejar en el cdigo

num = Word(nums)op = Literal('+') | Literal('-')id = Word(alphas)line = Group(id + num + Optional(op + num))lines = OneOrMore(line)begin = Literal('Begin')end = Literal('End')program = begin + lines + end

Ejecutemos el parser

L asali

['Begin', 'Bob', '12', 'Alice', '26', 'End']

Pero eso es un split!

Qu engao! A la hoguera con l!

Tranquilidad!

Nosotros podemos incluir las acciones que queramos, asociadas a los elementos que constituyen las reglas.

Como no hay acciones, el resultado es una simple lista de tokens.

['Begin', 'Bob', '12', 'Alice', '26', 'End']

Aadamos acciones

begin = Suppress(Literal('Begin'))end = Suppress(Literal('End'))num = Word(nums)op = Literal('+') | Literal('-')id = Word(alphas)line = Group(id + num + Optional(op + num))lines = OneOrMore(line)program = begin + lines + end

num.setParseAction(lambda p: int(p[0]))

Esto est mejor

Ejecutemos ejemplo_pyparsing_2.py:

['Begin', ['Bob', 12], ['Alice', 26], 'End']

Las acciones nos permiten construir nuestro modelo (o un AST)

def parse_line(s, loc, toks): items = toks[0] if len(items) == 4: op = items[2] delta = items[3] if op == '+': v = items[1] + delta elif op == '-': v = items[1] - delta return (items[0], v) else: return tuple(items)

line.setParseAction(parse_line)

Mucho mejor

Ejecutemos ejemplo_pyparsing_3.py ejemplo3.txt:

[('Bob', 12), ('Alice', 26), ('Charles', 55), ('Daniel', 29)]

Begin Bob 12 Alice 26 Charles 33+22 Daniel 72-43End

Ventajas de Pyparsing

Sencillo (pero potente, como Python)

100% pure python

La gramtica est reflejada en el cdigo, pero es legible

Se puede testear por partes

Es relativamente lento comparado con otras opciones, pero no suele ser problema para un DSL

Un ejemplo mayor

Grafel: Un lenguaje para hacer animaciones sencillas, pensado para no programadores

Desarrollado en una semana

Las animaciones de esta presentacin estn hechas en l

Usa pyparsing para el lenguaje, PyGame para realizar las animaciones.

Hola, mundo grafel

Cast:

a = Box pos 110x160

Actions:

25-75 a Swing 1090x600

Ejemplo de uso de pyparsing

attrs = ZeroOrMore(attr)Identifier = Word(alphas, alphanums)role = oneOf( 'Square Rect RoundRect Star Dice Label' ' Text Circle Box Triangle Bitmap Path' )castline = ( Identifier('name') + Suppress('=') + role('role') + Group(attrs)('params') )

Actores

Square

Box

Rect

Circle

Path

Polygon

Triangle

Star

RoundRect

Dice

Text

Label

Bitmap

Acciones

Blink

Colorize

Enter

FadeIn

FadeOut

Exit

Background

Move

Fall

Land

Swing

EaseIn

EaseOut

Timer

Libros

Esta es la biblia de como construir tu propio intrprete o compilador

No lo recomiendo para un DSL

Es demasiado complejo. Es EL LIBRO si vas a implementar un lenguaje completo, pero un DSL es mucho ms sencillo.

Recomendaciones

Domain-Specific LanguagesMartin Fowler, Rebbeca ParsonsAddison Wesley

Language Implementation PatternsTerence ParrPragmatic Bookshelf

Gracias por su tiempo

Repositorio de Grafel:http://bitbucket.org/euribates/grafel

Juan Ignacio Rodrguez de Len@jileon en twitter