Groovy e Domain Specific Languages

24
GROOVY E DSL BY TIZIANO LATTISI

Transcript of Groovy e Domain Specific Languages

Page 1: Groovy e Domain Specific Languages

GROOVY E DSLBY TIZIANO LATTISI

Page 2: Groovy e Domain Specific Languages

INDICE

• cos’è Groovy?

• caratteristiche interessanti (a mio giudizio), in ordine arbitrariamente sparso

• cos’è un DSL?

• caratteristiche che rendono Groovy adatto a costruire un DSL

• un esempio semplice (lo facciamo al volo)

• un esempio meno semplice (non lo facciamo al volo)

nota: in parallelo vedremo esempi

Page 3: Groovy e Domain Specific Languages

COS’È GROOVY (PARTE 1)?

• linguaggio per la JVM alternativo a Java

• ispirato a: Ruby, Python, Smalltalk

• consente compilazione dinamica, ma può anche generare bytecode

• tipizzazione forte dinamica

• closure

Page 4: Groovy e Domain Specific Languages

COMMAND-LINE GROOVY HELLO GROOVY

• $ groovy -e “println ‘Hello Groovy’”!

• $ echo “println ‘Hello Groovy’” > hello.groovy $ groovy hello.groovy!

• $ groovy -Dmsg=Groovy -e “println ‘Hello ‘ + System.getProperty(‘msg’)”

Page 5: Groovy e Domain Specific Languages

COMMAND-LINE GROOVY SWITCH -N -P -I

• $ printf “1\n2” > data.txt $ groovy -n -e “println line.toLong()*2” data.txt 2 4!

• $ groovy -i.bak —n -p -e “line.toLong()*2” data.txt $ cat data.txt 2 4 $ cat data.txt.bak 1 2

Page 6: Groovy e Domain Specific Languages

COMMAND-LINE GROOVY SWITCH -L

• $ groovy -l 1234 -e “if(line==‘DATE’){println new Date()}”Groovy is listening on port 1234 $ telnet localhost 1234 Connect to localhost. Escape character is ‘^]’. DATE Mon Mar 17 21:38:49 CET 2014!

• $ groovy -l 80 SimpleWebServer.groovy Esempio nei sorgenti Groovy: serve i file di una cartella come webserver

Page 7: Groovy e Domain Specific Languages

COS’È GROOVY (PARTE 2)?• è possibile usare direttamente l’API Java

• accetta (salvo alcune eccezioni) anche sintassi Java (es. {1,2,3,4} non è array, ma [1,2,3,4] è un ArrayList)

• import automatico (di convenienza) di alcune classi Java (es. java.io, java.lang, java.net, java.util…)

• println -> System.out.println

• parentesi opzionali nella chiamata a metodi

• notazione breve per getter e setter (o.field -> o.getField())

• ; opzionale (salvo alcune eccezioni)

• return opzionale (viene restituita l’ultima valutazione)

• this usata in contesti statici punta alla classe

Page 8: Groovy e Domain Specific Languages

COS’È GROOVY (PARTE 3)?• GString interpolation: “Hello ${name}”

• Lazily interpolation (eval in conversione a String): “Nr. ${-> i}”

• == -> equals, equals -> is: non più “a != null && a.equals()” !!

• in: è un operatore basato su contains(): 3 in [1,2,3,4]

• sintassi nativa per alcune strutture dati: [1,2,3] list, [TN:’Trento’, BZ:’Bolzano] map, 1..10 range

• contesti booleani: if( myString!=null && myString.length>0 ){} -> if(myString){}

• safe-dereferencing: email?.destinatario?.indirizzo

• costrutto and-or (Elvis operator): def result = name != null ? name : “Unknow”

Page 9: Groovy e Domain Specific Languages

FOR E FOR EACH

• for (int i=0; i<n; i++) { … }!

• for (i in 0..n-1) { … }!

• for (i in 0..<n) { … }!

• n.times { … }

• (1..5).each { println “nr. $it” }!

• 1..5 instanceof List!

• { println “nr. $it” }(3) nr. 3

Page 10: Groovy e Domain Specific Languages

GROOVY BEANS

class Book { String title String description } def b1 = new Book() b1.setTitle(“Anna Karenina”) b1.description = “A very long book…”!

println b2.getDescription() println b2.title def b2 = new Book(title:”Anna Karenina”) println b2.title

Page 11: Groovy e Domain Specific Languages

ALTRO SUI GROOVY BEANS• Annotation based AST transformation (groovy.transform.*):!

• @Immutable (read only bean)!

• @ToString(includeNames=true, excludes=‘description,year’)!

• @EqualsAndHashCode!

• @Canonical (@ToString + @EqualsAndHashCode)!

• @TupleConstructor -> new Book(“Anna Karenina”, “Very long book…”)!

• @AutoClone(style=AutoCloneStyle.COPY_CONSTRUCTOR) def book1 = new Book(title:”Anna Karenina”) def book2 = book1.clone() assert book1.title == book2.title!

• AutoCloneStyle.SERIALIZABLE se implementa Serializable

Page 12: Groovy e Domain Specific Languages

ALTRE COSE BELLE

• methodMissing e propertyMissing per gestire accessi a proprietà o metodi mancanti

• supporto nativo per markup XML, Json (Slurper e Builder)

• conversioni bean in xml e json

• ExpandoMetaClass

• built-in memoize

Page 13: Groovy e Domain Specific Languages

DSL DOMAIN SPECIFIC LANGUAGE

• Linguaggio di programmazione dedicato ad uno specifico dominio (contrapposto a “general-purpose”)

• statistica (R e S)

• programmazione matriciale (Mata)

• Logo

• SQL

• …

Page 14: Groovy e Domain Specific Languages

DEFINIRE UN DSL IN GROOVY

Combinazione di tre punti chiave:

• fluent API

• embedded shell

• “specificità” del linguaggio

Page 15: Groovy e Domain Specific Languages

FLUENT API

È un implementazione di un API “method chaining”, ovvero che permette chiamate di metodi “a catena”.

Es. JavaFX

Scene scene = SceneBuilder.create().width(516).height(387) ! .root(! ! GroupBuilder.create().children(! ! ! ImageViewBuilder.create().image(new Image(“..”)),! ! ! [omissam]! ! ).build()).build();

Page 16: Groovy e Domain Specific Languages

EMBEDING GROOVY IN JAVA// Semplice esecuzione di codice Groovy!

GroovyShell shell = new GroovyShell(); String groovyCode = “println ‘Hello ’ + ‘Groovy’”; String out = shell.evaluate(groovyCode);

// Esecuzione codice Groovy con variabile embeddata!

Book myBook = new Book(); myBook.setTitle(“Anna Karenina”);!

Binding binding = new Binding(); binding.setVariable(“book”, myBook); String groovyCode = “println ‘Reading ‘ + book.title”; GroovyShell shell = new GroovyShell(binding); String out = shell.evaluate(groovyCode);

Page 17: Groovy e Domain Specific Languages

SPECIFICITÀ DI GROOVY

• le chiamate di metodi possono omettere le parentesi

• scriptBaseClass: la classe script base che rappresenta il contesto di esecuzione dello script

• ImportCustomizer: importazione diretta nello script di classi e package (anche *)

• SecureASTCustomizer: gestione della sicurezza (es. liste bianche/nere)

• ASTTransformationCustomizer: per aggiungere automaticamente annotazioni di trasformazione ai metodi

Page 18: Groovy e Domain Specific Languages

ESEMPIO SEMPLICEVoglio creare un DSL in grado di eseguire:

compute 4 plus 3 plus 2 minus 1 print total!

• Una classe che implementa i metodi di linguaggio (compute, plus, minus, etc) come API fluent

• Una classe astratta come base dello script groovy (si occuperà di proxare i metodi sullo script)

• Un enum per le costanti (es. total)

• GroovyShell con:

• binding della classe linguaggio

• classe base astratta (vedi sopra)

• importazione custom delle costanti (vedi sopra)

Vediamo in pratica come procedere! (sorry, no slides here…)

Page 19: Groovy e Domain Specific Languages

LA CLASSE DI LINGUAGGIOclass Language { Integer tot; def compute(Integer a){ tot=a this } def plus(Integer a){ tot += a this } def minus(Integer a){ tot -= a this } def print(Consts c){ tot } }

Page 20: Groovy e Domain Specific Languages

LA SCRIPTBASE E LA COSTANTEabstract class AbstractScriptBaseClass extends Script{ def compute(Integer a){ this.lang.compute(a) } def plus(Integer a){ this.lang.plus(a) } def minus(Integer a){ this.lang.minus(a) } def print(String t){ this.lang.print(t) } }

public enum Consts { total }

Page 21: Groovy e Domain Specific Languages

TESTIAMO L’API

void testAPI() { Language lang = new Language() String total = "total" // test API con sintassi tradizionale Integer tot1 = lang.compute(4).plus(3).plus(2).minus(1).print(total) // test API omettendo le parentesi Integer tot2 = lang.compute 4 plus 3 plus 2 minus 1 print total assert tot1 == 8 assert tot2 == 8 }

Page 22: Groovy e Domain Specific Languages

TESTIAMO IL DSLvoid testShell() { // il codice scritto nel DSL def code = "compute 4 plus 3 plus 2 minus 1 print total" Language lang = new Language() Binding binding = new Binding(); binding.setVariable("lang", lang) CompilerConfiguration conf = new CompilerConfiguration() conf.scriptBaseClass = AbstractScriptBaseClass.class.name ImportCustomizer imports = new ImportCustomizer() imports.addStaticStars(Consts.name) conf.addCompilationCustomizers(imports) GroovyShell shell = new GroovyShell(binding, conf) Integer tot = (Integer) shell.evaluate(code) assert tot == 8 }

Page 23: Groovy e Domain Specific Languages

ESEMPIO MENO SEMPLICEUn DSL per definire e risolvere problemi di geometria piana euclidea(https://github.com/tizianolattisi/peg)

create triangle name "ABC" extend "AC" to "D" with measure:"BC" extend "BC" to "E" with measure:"AC" create segment name "ED" extend "DE" to "H" extend "AB" to "H" apply "10.8" on "ad", "bc" //angoli opposti apply "10.3" on "CED", "ABC" apply "10.6" on "ABC", "cba", "CED", "edc" create segment name "BD" apply "10.10" on "BCD", "BC", "CD"

Page 24: Groovy e Domain Specific Languages

–Tiziano Lattisi

“Grazie a tutti!”.