PRESENTACION DE LA ARQUITECTURA GRIEGA (EDAD ANTIGUA)
presentacindslintro-151124180611-lva1-app6891.pdf
1.
2. Qué es un DSL
● 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
● A veces, 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: Expresiones regulares
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;
}
8. Ejemplos de lenguajes que 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 definen un 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 lenguaje es 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
12. DSL Interno
● Expande el 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 en Python
● 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
● El ejemplo más famoso es la definición de
modelos en Django.
● Muy potente, algo loco, no tan peligroso como
el AST
15. DSL Externos
● El lenguaje 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 un lenguaje
● 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 primera capa 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 llamado analizador 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 los tokens 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.
23. ¿Para qué sirve la 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
25. Definición formal de Gramá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 un punto 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 un ejemplo
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
Los nombres 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
29. Una gramática
Program --> Begin line+ End
line --> id num
id --> [A-Za-z][A-Za-z0-9]*
num --> [0-9]+
30. Otra gramática
Program --> Begin lines End
lines → line+
line --> id num
id --> [A-Za-z][A-Za-z0-9]*
num --> [0-9]+
32. Nomenclatura y otras yerbas
● 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)
● Necesitamos definir 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:
● Que el 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.
43. Ventajas
● Muy fácil de 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 detalles de 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, hay formas 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 las parsers 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ón de 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 de parser
● 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 a ver 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 la gramática
Begin
Bob 12
Alice 26
Charles 33+22
Daniel 12-133
End
53. Fácil de reflejar en 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
55. ¡Pero eso es un split!
¡Qué engaño! ¡A la hoguera con él!
56. ¡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']
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]))
59. 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)
61. 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.