Introducción a DSL (Lenguajes Específicos de Dominios) con Python
Un DSL (lenguaje específico de dominio) se utiliza para resolver problemas específicos de manera más sencilla que un lenguaje de propósito general. Ejemplos de DSL incluyen SQL para bases de datos, expresiones regulares para buscar patrones en texto y CSS para la apariencia de elementos HTML. La creación de un DSL puede ser interna, expandiendo un lenguaje existente, o externa, donde se desarrolla un lenguaje completamente nuevo con su propia gramática y parser.
Introducción a DSL (Lenguajes Específicos de Dominios) con Python
2.
Qué es unDSL
● Lenguaje EspecÍfico de Dominio (Domain
Specific Language):
– Dominio: El problema que queremos resolver
– Específico: Solo para ese dominio
– Lenguaje: Para ser escrito y leído por personas
● Los problemas se pueden resolver con un
lenguaje de propósito general, pero...
3.
Especificar soluciones
● Aveces, puede ser una buena opción crear un
lenguaje específico para ese problema (o
sea, un DSL)
● Ese lenguaje permite expresar la solución de
una forma más sencilla
4.
Ejemplo 1: SQL
Dominio:Extraer información de un sistema de
base de datos relacional
SELECT *
FROM VENTAS
WHERE precio <= 0.99;
5.
Ejemplo 2: Expresionesregulares
Dominio: Buscar y extraer patrones de texto
dentro de otros textos.
[A-Za-z_][A-Za-z0-9_]*
6.
Ejemplo 3: CSS
Dominio:Especificar la apariencia gráfica de
elementos HTML (No es un lenguaje de
programación, y sus usuarios no son
necesariamente desarrolladores)
div.titulo {
color: #BB2266;
font-size: 24pt;
}
Ejemplos de lenguajesque no son
DSL (IMHO)
● R (Estadísticas)
– Restringido a un dominio (estadísticas), pero en
realidad es un lenguaje turing-completo
● XSLT
– Restringido a un dominio (Transformar contenidos
XML), pero no pensado para ser leído o escrito por
humanos (cuerdos)
9.
Características que definenun DSL
● No están pensado para ser de uso general
(rara vez un DSL es turing-completo)
● Hacen únicamente lo que tienen que hacer,
nunca se salen del dominio
● Pensados para ser leídos y escritos por
personas (aunque se puedan generar
automáticamente)
10.
Ventajas
● El lenguajees más legible y más corto
● No necesita ser Turing-completo
● Puede ser usado por personal no técnico
● Flexible y adaptable
● Es código: Podemos transformarlo, almacenarlo,
distribuirlo, representarlo, etc...
● Fomenta la colaboración entre personas de
distintas especialidades
● Control de versiones
DSL Interno
● Expandeel interprete de un lenguaje de propósito
general. Python puede hacer cosas así con
Macros, metaprogramación, el módulo ast, etc...
– Fácil, pero te puede condicionar mucho
– Muy usado en lenguajes como scala, groovy, lisp...
– Parser ya incluido
– Ventajas: toda la potencia del lenguaje base
– Desventajas: toda la potencia del lenguaje base
13.
Usar AST enPython
● Nos permite modificar el árbol AST que
implementa el propio Python para interpretarse.
● Muy potente, muy loco, muy peligroso, muy
divertido. Elige una.
14.
Usar Metaclases
● Elejemplo más famoso es la definición de
modelos en Django.
● Muy potente, algo loco, no tan peligroso como
el AST
15.
DSL Externos
● Ellenguaje es totalmente independiente.
– Tenemos que escribir nuestro propio lenguaje
– Mola
– Pero puede dar miedito
– No es tan terrible como parece
16.
Características DSL externo
●Tienes que crear tu propio parser y tu propia
Gramática (más sobre estas palabrejas más
adelante)
● Libertad absoluta
● La sintaxis del lenguaje solo está condicionada
por el dominio (y tu talento)
17.
Estructura de unlenguaje
● Distintos autores usan diferentes nombres,
pero básicamente consta de 4 fases:
– Scanner
– Analizador Léxico, Tokenizer o Lexer
– Analizador Sintáctico o Parser*
– Intérprete o generación de código
● Cada capa o fase usa los servicios de la
anterior y pasa sus resultados a la siguiente
* El turrón está aquí / The Nougat is here
18.
Scanner
● La primeracapa y la más sencilla.
Simplemente lee la entrada carácter a carácter.
● A veces puede ser un poco más complicada.
por ejemplo, puede que necesite leer
caracteres por delante del actual, para poder
tomar decisiones
– Por ejemplo, para discriminar entre a++ y a+1
19.
Lexer/Tokenizer
● También llamadoanalizador léxico
● Usando los servicios del scanner, lee una secuencia de
bytes y devuelve una serie de unidades léxicas o
Tokens (Si fuera un lenguaje humano, palabras y
signos)
● Es razonablemente sencillo de implementar. Una
técnica muy usada es usar expresiones regulares.
● Los tokens devueltos están etiquetados para saber de
que tipo son (Si fuera un lenguaje humano, la palabra y
su función, p.e. verbo).
20.
Parser
● Organiza lostokens entregados por el lexer de
forma que sean procesables. Esto es,
normalmente, en forma de árbol.
● Para ello utiliza una Gramática del lenguaje.
● Existen varios algoritmos, cada uno con sus
ventajas e inconvenientes.
21.
Generación/Ejecución de código
●La última fase. En el caso de un lenguaje de
programación general, se genera o ejecuta
código, bytecodes o código ejecutable.
● Normalmente en un DSL se crea o controla un
módelo creado por nosotros.
● En cualquier caso, es la fase final. Lo que
hagamos con el árbol es cosa nuestra.
¿Para qué sirvela gramática?
● El parser usa la gramática para:
– Detectar si la entrada es correcta, es decir, si
pertenece al lenguaje.
– Organizar los tokens de entrada en forma de Árbol
de sintáxis o Árbol de Sintáxis Abstracta o AST.
– Se le clasifica de abstracto porque no representa
fielmente todos los detalles de la sintaxis.
– Por ejemplo, los caracteres ; que diferencia los
líneas en lenguajes como C, se suelen eliminar del
AST
Definición formal deGramática
● Una gramática es la definición matemática de
un lenguaje
● La gramática nos dice si un texto pertenece al
lenguaje o no
● Para ello, establece unas reglas de producción;
si usando esas reglas se puede llegar a
obtener un texto, entonces ese texto pertenece
al lenguaje
26.
Gramáticas
● Desde unpunto de visto formal, una gramática
es un conjunto de reglas llamadas
producciones
● De esas reglas, una debe ser la inicial
● Las reglas tienen la forma:
<parte derecha> → <parte izquierda>
● la → se suele representar de muchas maneras,
pero viene a significar “se define como”
27.
Mejor con unejemplo
Supongamos un lenguaje muy, muy sencillo:
Begin
Bob 12
Alice 26
End
Entre Begin y End, una lista de varios nombres,
junto con un número.
28.
Unas aclaraciones previas
Losnombres están formados por una sola
palabra; “Bob” es válido, “Robert Drake” no.
La primera letra del nombre tiene que ser
alfabética, pero luego se aceptan números:
“labo3” es válido pero “3ell” no.
Los números son enteros, no se aceptan
decimales, “123” es válido, “3.14” no.
Al menos tiene que venir una línea de datos
Nomenclatura y otrasyerbas
● Cualquier símbolo que aparezca a la izquierda
de una flecha es un símbolo No Terminal.
● Cualquier símbolo 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 símbolos
especiales
33.
Símbolos especiales
● |Representa alternativas, a|b significa que
se puede generar a o b
● * Significa repetición de cero o más
● + Significa repetición de uno o más
● ? Opcional (uno o cero)
● [a-z] Rango de caracteres
[0-9] se podría representar como 0|1|2|3|4|5|6|8|9
34.
Parser Recursivo Descendente
●Hay varios tipos de parsers
● Uno de los más simples es el Parser Recursivo
Descendente
● Es tan simple que se puede escribir a mano
● Vamos a hacerlo
35.
Parser LL(1)
● Necesitamosdefinir una función para cada
símbolo no terminal de la gramática
● El parser consiste en una serie de llamadas
recursivas
● En nuestro caso, debemos escribir una función
para cada uno de los símbolos:
– program
– line
– id
– num
36.
Además, necesitamos:
● Queel tokenizer haya hecho su trabajo, y nos
haya entregado una lista o cola de tokens (Lo
simularemos)
● Una función que nos permita examinar el
siguiente token a procesar
● Una función auxiliar, consume, que retira un
elemento de la lista de tokens si es el que
esperamos. Si no lo fuera, da un error.
Ventajas
● Muy fácilde escribir: se siguen unas reglas
para cada tipo de símbolo y regla de la
gramática.
● Fácil de entender
● Si cambia la gramática, se cambia el programa
● La primera vez es divertido
44.
Inconvenientes
● Los detallesde la gramática están “perdidos”
en el código; no contamos con una
representación explícita de la gramática
● Si la gramática es complicada, es tedioso
● Algunas gramáticas no se puede parsear así.
Las reglas recursivas, de tipo
– expr → expr (+|-|*|+) expr
hacen que el parser entre un un bucle sin fin
● La décima vez ya no es tan divertido...
45.
Soluciones
● Afortunadamente, hayformas de generar un parser
automáticamente a partir de la gramática.
● El parser que hemos hecho es de tipo LL. La segunda L
indica que hacemos un análisis 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.
46.
Características de lasparsers LR
● El análisis se hace de forma ascendente, por lo
que pueden usarse reglas gramaticales recursivas
● La gramática pueda ser más sencilla (Pero aun
hay ciertos problemas, por ejemplo con
gramáticas ambiguas)
● En general, más poderosos que los LL
● Existe formas automáticas de generarlos, igual
que los LR
47.
Técnicas de generaciónde parser
● No tenemos tiempo de ver más de las técnicas
de creación 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
48.
Algunos generadores deparser
● Lex y Yaccç; Lex se encarga del tokenizer y
Yacc del parser. Generan código 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 código en Python,
entre otros lenguajes. Genera parsers LL(K).
● Hay muchísimos más...
49.
Centrándonos en Python
●También hay muchos, algunos de los más
destacados son:
– Plex
– Grako
– PLY
– Pyparsing
– Muchos más ...
http://nedbatchelder.com/text/python-parsers.html
50.
Pyparsing
● Vamos aver como resuelve Pyparsing el
lenguaje anterior
● La idea es componer, a base de elementos
más pequeños, el parser
● La estructura refleja la gramática (pero al
revés, la regla inicial es la última, para evitar
referencias futuras)
51.
Pyparsing
from pyparsing import(Literal, Word, nums, alphas, Optional,
OneOrMore, Group)
num = Word(nums)
id = Word(alphas)
line = id + num
lines = OneOrMore(line)
begin = Literal('Begin')
end = Literal('End')
program = begin + lines + end
52.
Cambios en lagramática
Begin
Bob 12
Alice 26
Charles 33+22
Daniel 12-133
End
53.
Fácil de reflejaren el código
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
¡Pero eso esun split!
¡Qué engaño! ¡A la hoguera con él!
56.
¡Tranquilidad!
● Nosotros podemosincluir 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']
57.
Añadamos 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]))
Ventajas de Pyparsing
●Sencillo (pero potente, como Python)
● 100% pure python
● La gramática está reflejada en el código, pero
es legible
● Se puede testear por partes
● Es relativamente lento comparado con otras
opciones, pero no suele ser problema para un
DSL
62.
Un ejemplo mayor
●Grafel: Un lenguaje para hacer animaciones
sencillas, pensado para no programadores
● Desarrollado en una semana
● Las animaciones de esta presentación están
hechas en él
● Usa pyparsing para el lenguaje, PyGame para
realizar las animaciones.