SlideShare une entreprise Scribd logo
1  sur  154
Groovy.DSLs(from: beginner, to: expert)

Paul King
Core Groovy Committer
ASERT Director


Guillaume Laforge
Groovy Project Manager
SpringSource, a division of VMware

© 2011 SpringOne 2GX 2011. All rights reserved. Do not distribute without permission.
Groovy.DSLs(from: beginner, to: expert)

Paul King                                                                      2 011
Core Groovy Committer                                                            E dition
ASERT Director


Guillaume Laforge
Groovy Project Manager
SpringSource, a division of VMware

© 2011 SpringOne 2GX 2011. All rights reserved. Do not distribute without permission.
Guillaume Laforge

• Groovy Project Manager at VMware
    • Initiator of the Grails framework
    • Creator of the Gaelyk toolkit
• Co-author of Groovy in Action
• Speaking worldwide w/ a French accent


• Follow me on...
    • My blog: http://glaforge.appspot.com
    • Twitter: @glaforge
    • Google+: http://gplus.to/glaforge


2        @glaforge — @paulk_asert
Guillaume Laforge

• Groovy Project Manager at VMware
    • Initiator of the Grails framework
    • Creator of the Gaelyk toolkit
• Co-author of Groovy in Action
• Speaking worldwide w/ a French accent


• Follow me on...
    • My blog: http://glaforge.appspot.com
    • Twitter: @glaforge
    • Google+: http://gplus.to/glaforge


2        @glaforge — @paulk_asert
Paul King

• Core Groovy Committer
• Co-author of Groovy in Action
• Leads ASERT, a Brisbane, Australia, company providing
  software development, training & mentoring support
• International speaker


• Follow me on
    • Website: http://www.asert.com.au
    • Twitter: @paulk_asert




3       @glaforge — @paulk_asert
Domain-Specific Languages

• Wikipedia definition
    – A Domain-Specific Language is a programming language or
      executable specification language that offers, through
      appropriate notations and abstractions, expressive power
      focused on, and usually restricted to, a particular problem
      domain.


• In contrast to General Purprose Languages
• Somewhere between declarative data and GPLs


• Also known as: fluent / human interfaces, language oriented
  programming, little or mini languages, macros, business
4        @glaforge — @paulk_asert
Goals of Domain-Specific Languages

• Use a more expressive language
  than a general-purpose one

• Share a common metaphore of understanding
  between develoeprs and subject matter experts

• Have domain experts help with the design
  of the business logic of an application

• Avoid cluttering business code with too much boilerplate
  technical code thanks to a clean seperation


5     @glaforge — @paulk_asert
Antimalaria drug Insurance policy risk   HR skills
resistance       calculation engine      representation
simulation




                                    Nuclear safety
                                    simulations




Market data feeds analysis    Loan acceptance rules engine
Technical examples
  Glade                                                  XSLT
  <?xml version="1.0"?>                                   <?xml version="1.0"?>
  <GTK-Interface>                                         <xsl:stylesheetversion="1.0"
  <widget>                                                    xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <class>GtkWindow</class>                              <xsl:output method="xml"/>
    <name>HelloWindow</name>                                <xsl:template match="*">
    <border_width>5</border_width>                            <xsl:element name="{name()}">
    <Signal>                                                    <xsl:for-each select="@*">                   Regex
                                                                  <xsl:element name="{name()}">
                                                                                                                        "x.z?z{1,3}y"
      <name>destroy</name>
      <handler>gtk_main_quit</handler>                              <xsl:value-of select="."/>
    </Signal>                                                     </xsl:element>
    <title>Hello</title>                                        </xsl:for-each>
    <type>GTK_WINDOW_TOPLEVEL</type>                            <xsl:apply-templates select="*|text()"/>
    <position>GTK_WIN_POS_NONE</position>                     </xsl:element>
    <allow_shrink>True</allow_shrink>                       </xsl:template>
    <allow_grow>True</allow_grow>                         </xsl:stylesheet>
    <auto_shrink>False</auto_shrink>
    <widget>
      <class>GtkButton</class>                                                  fetchmail
      <name>Hello World</name>
      <can_focus>True</can_focus>                                               # Poll this site first each cycle.
      <label>Hello World</label>                                                poll pop.provider.net proto pop3
    </widget>                                                                     user "jsmith" with pass "secret1" is "smith" here
  </widget>                                                                       user jones with pass "secret2" is "jjones" here with options
  </GTK-Interface>                                                              keep

                                 SQL                                            # Poll this site second, unless Lord Voldemort zaps us first.
                                                                                poll billywig.hogwarts.com with proto imap:
                                 SELECT * FROM TABLE                              user harry_potter with pass "floo" is harry_potter here
                                 WHERE NAME LIKE '%SMI'                         # Poll this site third in the cycle.
                                 ORDER BY NAME                                  # Password will be fetched from ~/.netrc
                                                                                poll mailhost.net with proto imap:
                                                                                  user esr is esr here

  Troff
  cat thesis.ms | chem | tbl | refer | grap | pic | eqn | groff -Tps > thesis.ps                                           8

   Source: Applying minilanguages: http://www.faqs.org/docs/artu/ch08s02.html


          @glaforge — @paulk_asert
Pros and cons of DSLs

• Pros                                 – Safety; as long as the
    – Domain experts can help,           language constructs are safe,
      validate, modify, and often        any DSL sentence can be
      develop DSL programs               considered safe
    – Somewhat self-documenting
    – Enhance quality,                • Cons
      productivity, reliability,       – Learning cost vs. limited
      maintainability, portability,      applicability
      reusability                      – Cost of designing,
                                         implementing & maintaining
                                         DSLs as well as tools/IDEs
                                       – Attaining proper scope

8        @glaforge — @paulk_asert
• The flexible nature of the language
          – method call and syntax conventions
          – native syntax constructs
          – adding properties to numbers
          – command chain expressions
          – operator overloading
          – hierarchical structures with builders
          – AST transformations

9   © 2011 SpringOne 2GX 2011. All rights reserved. Do not distribute without permission.
Scripts vs classes
 • Hide all the boilerplate technical code
     – an end-user doesn’t need to know about classes




10       @glaforge — @paulk_asert
Scripts vs classes
 • Hide all the boilerplate technical code
     – an end-user doesn’t need to know about classes


       public
class
Rule
{
       



public
static
void
main(String[]
args)
{
       







System.out.println("Hello");
       



}
       }




10       @glaforge — @paulk_asert
Scripts vs classes
 • Hide all the boilerplate technical code
     – an end-user doesn’t need to know about classes


       public
class
Rule
{
       



public
static
void
main(String[]
args)
{
       







System.out.println("Hello");
       



}
       }




                            println
"Hello"




10       @glaforge — @paulk_asert
Optional typing
     • No need to bother with types or even generics
       – unless you want to!
       – but strongly typed if you so desire




11         @glaforge — @paulk_asert
Optional typing
     • No need to bother with types or even generics
       – unless you want to!
       – but strongly typed if you so desire

//
given
this
API
method
public
Rate<LoanType,
Duration,
BigDecimal>[]
lookupTable()
{
...
}




11         @glaforge — @paulk_asert
Optional typing
     • No need to bother with types or even generics
       – unless you want to!
       – but strongly typed if you so desire

//
given
this
API
method
public
Rate<LoanType,
Duration,
BigDecimal>[]
lookupTable()
{
...
}



 //
verbose
Java
notation
 Rate<LoanType,
Duration,
BigDecimal>[]
table
=
lookupTable();




11         @glaforge — @paulk_asert
Optional typing
     • No need to bother with types or even generics
       – unless you want to!
       – but strongly typed if you so desire

//
given
this
API
method
public
Rate<LoanType,
Duration,
BigDecimal>[]
lookupTable()
{
...
}



 //
verbose
Java
notation
 Rate<LoanType,
Duration,
BigDecimal>[]
table
=
lookupTable();



                                         //
leaner
Groovy
variant
                                         def
table
=
lookupTable()



11         @glaforge — @paulk_asert
Native syntax constructs




12   @glaforge — @paulk_asert
Native syntax constructs


     //
Lists




12     @glaforge — @paulk_asert
Native syntax constructs


     //
Lists
     def
days
=
[Monday,
Tuesday,
Wednesday]




12     @glaforge — @paulk_asert
Native syntax constructs


     //
Lists
     def
days
=
[Monday,
Tuesday,
Wednesday]

     //
Maps




12     @glaforge — @paulk_asert
Native syntax constructs


     //
Lists
     def
days
=
[Monday,
Tuesday,
Wednesday]

     //
Maps
     def
states
=
[CA:
'California',
TX:
'Texas']




12     @glaforge — @paulk_asert
Native syntax constructs


     //
Lists
     def
days
=
[Monday,
Tuesday,
Wednesday]

     //
Maps
     def
states
=
[CA:
'California',
TX:
'Texas']

     //
Ranges
(you
can
create
your
own)




12     @glaforge — @paulk_asert
Native syntax constructs


     //
Lists
     def
days
=
[Monday,
Tuesday,
Wednesday]

     //
Maps
     def
states
=
[CA:
'California',
TX:
'Texas']

     //
Ranges
(you
can
create
your
own)
     def
bizDays
=
Monday..Friday




12     @glaforge — @paulk_asert
Native syntax constructs


     //
Lists
     def
days
=
[Monday,
Tuesday,
Wednesday]

     //
Maps
     def
states
=
[CA:
'California',
TX:
'Texas']

     //
Ranges
(you
can
create
your
own)
     def
bizDays
=
Monday..Friday
     def
allowedAge
=
18..65




12     @glaforge — @paulk_asert
Optional parens & semis

• Make statements and expressions
  look more like natural languages




13    @glaforge — @paulk_asert
Optional parens & semis

• Make statements and expressions
  look more like natural languages


            move(left);




13    @glaforge — @paulk_asert
Optional parens & semis

• Make statements and expressions
  look more like natural languages


            move(left);



           move
left


13    @glaforge — @paulk_asert
Adding properties to numbers

• Several approaches to adding properties to numbers
     – through a category
                   class
PillsCategory
{
                   



static
getPills(Number
n)
{
n
}
                   }

                   use(PillsCategory)
{
                   



2.pills









//
2.getPills()
                   }




     – through ExpandoMetaClass
                 Number.metaClass.getPills
{
‐>
delegate
}

                 2.pills
















//
2.getPills()


14        @glaforge — @paulk_asert
Named arguments

• In Groovy you can mix named and unnamed arguments for
  method parameters
     – named params are actually put in a map parameter
     – plus optional parens & semis

          

take
2.pills,

          


of:
chloroquinine,

          after:
6.hours

          //
Calls
a
method
signature
like:
          def
take(Map
m,
MedicineQuantity
mq)


15        @glaforge — @paulk_asert
Command chain expressions




16   @glaforge — @paulk_asert
Command chain expressions


• A grammar improvement allowing you to
  drop dots & parens when chaining method calls
     – an extended version of top-level statements like println




16        @glaforge — @paulk_asert
Command chain expressions


• A grammar improvement allowing you to
  drop dots & parens when chaining method calls
     – an extended version of top-level statements like println




16        @glaforge — @paulk_asert
Command chain expressions


• A grammar improvement allowing you to
  drop dots & parens when chaining method calls
     – an extended version of top-level statements like println


• Less dots, less parens allow you to
     – write more readable business rules
     – in almost plain English sentences
       • (or any language, of course)




16        @glaforge — @paulk_asert
Command chain expressions


• A grammar improvement allowing you to
  drop dots & parens when chaining method calls
     – an extended version of top-level statements like println


• Less dots, less parens allow you to
     – write more readable business rules
     – in almost plain English sentences
       • (or any language, of course)




16        @glaforge — @paulk_asert
Command chain expressions


• A grammar improvement allowing you to
  drop dots & parens when chaining method calls
     – an extended version of top-level statements like println


• Less dots, less parens allow you to
     – write more readable business rules
     – in almost plain English sentences
       • (or any language, of course)


• Let’s have a look at some examples
16        @glaforge — @paulk_asert
Command chain expressions




           
turn
left

then
right





17   @glaforge — @paulk_asert
Command chain expressions
            Alternation of
            method names




           
turn
left

then
right





17   @glaforge — @paulk_asert
Command chain expressions
            Alternation of
            method names




           
turn
left

then
right



                                  and parameters
                                (even named ones)




17   @glaforge — @paulk_asert
Command chain expressions




           
turn
left

then
right





17   @glaforge — @paulk_asert
Command chain expressions




            Equivalent to:


           




(



).



(




)
           
turn
left

then
right





17   @glaforge — @paulk_asert
LookM  a!
N op are ns,
no do ts!
Command chain expressions



        Before... we used to do...



     take
2.pills,
of:
chloroquinine,
after:
6.hours




19      @glaforge — @paulk_asert
Command chain expressions



        Before... we used to do...



     take
2.pills,
of:
chloroquinine,
after:
6.hours


        Normal argument




19      @glaforge — @paulk_asert
Command chain expressions



        Before... we used to do...



     take
2.pills,
of:
chloroquinine,
after:
6.hours


        Normal argument              Named arguments




19      @glaforge — @paulk_asert
Command chain expressions



         Before... we used to do...



     take
2.pills,
of:
chloroquinine,
after:
6.hours


         Normal argument              Named arguments


      Woud call:
      def
take(Map
m,
Quantity
q)


19       @glaforge — @paulk_asert
Command chain expressions



         Now, even less punctuation!



     take
2.pills

of

chloroquinine

after

6.hours




20      @glaforge — @paulk_asert
Command chain expressions



         Now, even less punctuation!



     



(






).


(












).





(






)
     take
2.pills

of

chloroquinine

after

6.hours




20      @glaforge — @paulk_asert
Command chain expressions

         //
environment
initialization
         Integer.metaClass.getPills
{
‐>
delegate
}
         Integer.metaClass.getHours
{
‐>
delegate
}
         

         //
variable
injection
         def
chloroquinine
=
/*...*/
         





     {                                                 }
         //
implementing
the
DSL
logic
         def
take(n)
{
         



[of:
{
drug
‐>
         







[after:
{
time
‐>
/*...*/
}]
         



}]
         }

         //
‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐
         take
2.pills
of
chloroquinine
after
6.hours

21   @glaforge — @paulk_asert
Command chain expressions




     take
2.pills

of

chloroquinine

after

6.hours




22      @glaforge — @paulk_asert
Command chain expressions




     take
2.pills

of

chloroquinine

after

6.hours


                               ... some dots remain ...




22      @glaforge — @paulk_asert
Command chain expressions



             Yes, we can... get rid of them :-)



     take
2

pills
of

chloroquinine

after

6
hours




23      @glaforge — @paulk_asert
Command chain expressions



             Yes, we can... get rid of them :-)



     



(
).




(

).













(




).
(




)
     take
2

pills
of

chloroquinine

after

6
hours




23      @glaforge — @paulk_asert
Command chain expressions

         //
variable
injection
         def
(of,
after,
hours)
=
/*...*/
         

         //
implementing
the
DSL
logic




     {                                                 }
         def
take(n)
{
         



[pills:
{
of
‐>
         







[chloroquinine:
{
after
‐>
         











['6':
{
time
‐>
}]
         







}]
         



}]
         }

         //
‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐
         take
2
pills
of
chloroquinine
after
6
hours


24   @glaforge — @paulk_asert
Command chain expressions




    @glaforge — @paulk_asert   25
Command chain expressions

 //
methods
with
multiple
arguments
(commas)




    @glaforge — @paulk_asert                   25
Command chain expressions

 //
methods
with
multiple
arguments
(commas)
 take
coffee

with
sugar,
milk

and
liquor




    @glaforge — @paulk_asert                   25
Command chain expressions

 //
methods
with
multiple
arguments
(commas)
 take
coffee

with
sugar,
milk

and
liquor

 //
leverage
named‐args
as
punctuation




    @glaforge — @paulk_asert                   25
Command chain expressions

 //
methods
with
multiple
arguments
(commas)
 take
coffee

with
sugar,
milk

and
liquor

 //
leverage
named‐args
as
punctuation
 check
that:
margarita

tastes
good




    @glaforge — @paulk_asert                   25
Command chain expressions

 //
methods
with
multiple
arguments
(commas)
 take
coffee

with
sugar,
milk

and
liquor

 //
leverage
named‐args
as
punctuation
 check
that:
margarita

tastes
good

 //
closure
parameters
for
new
control
structures




    @glaforge — @paulk_asert                        25
Command chain expressions

 //
methods
with
multiple
arguments
(commas)
 take
coffee

with
sugar,
milk

and
liquor

 //
leverage
named‐args
as
punctuation
 check
that:
margarita

tastes
good

 //
closure
parameters
for
new
control
structures
 given
{}

when
{}

then
{}




    @glaforge — @paulk_asert                        25
Command chain expressions

 //
methods
with
multiple
arguments
(commas)
 take
coffee

with
sugar,
milk

and
liquor

 //
leverage
named‐args
as
punctuation
 check
that:
margarita

tastes
good

 //
closure
parameters
for
new
control
structures
 given
{}

when
{}

then
{}

 //
zero‐arg
methods
require
parens




    @glaforge — @paulk_asert                        25
Command chain expressions

 //
methods
with
multiple
arguments
(commas)
 take
coffee

with
sugar,
milk

and
liquor

 //
leverage
named‐args
as
punctuation
 check
that:
margarita

tastes
good

 //
closure
parameters
for
new
control
structures
 given
{}

when
{}

then
{}

 //
zero‐arg
methods
require
parens
 select
all

unique()
from
names




    @glaforge — @paulk_asert                        25
Command chain expressions

 //
methods
with
multiple
arguments
(commas)
 take
coffee

with
sugar,
milk

and
liquor

 //
leverage
named‐args
as
punctuation
 check
that:
margarita

tastes
good

 //
closure
parameters
for
new
control
structures
 given
{}

when
{}

then
{}

 //
zero‐arg
methods
require
parens
 select
all

unique()
from
names

 //
possible
with
an
odd
number
of
terms


    @glaforge — @paulk_asert                        25
Command chain expressions

 //
methods
with
multiple
arguments
(commas)
 take
coffee

with
sugar,
milk

and
liquor

 //
leverage
named‐args
as
punctuation
 check
that:
margarita

tastes
good

 //
closure
parameters
for
new
control
structures
 given
{}

when
{}

then
{}

 //
zero‐arg
methods
require
parens
 select
all

unique()
from
names

 //
possible
with
an
odd
number
of
terms
 take
3

cookies

    @glaforge — @paulk_asert                        25
Command chain expressions

 //
methods
with
multiple
arguments
(commas)
 take
coffee

with
sugar,
milk

and
liquor
 



(





).



(










).


(





)

 //
leverage
named‐args
as
punctuation
 check
that:
margarita

tastes
good

 //
closure
parameters
for
new
control
structures
 given
{}

when
{}

then
{}

 //
zero‐arg
methods
require
parens
 select
all

unique()
from
names

 //
possible
with
an
odd
number
of
terms
 take
3

cookies

    @glaforge — @paulk_asert                        25
Command chain expressions

 //
methods
with
multiple
arguments
(commas)
 take
coffee

with
sugar,
milk

and
liquor
 



(





).



(










).


(





)

 //
leverage
named‐args
as
punctuation
 check
that:
margarita

tastes
good
 




(














).





(



)

 //
closure
parameters
for
new
control
structures
 given
{}

when
{}

then
{}

 //
zero‐arg
methods
require
parens
 select
all

unique()
from
names

 //
possible
with
an
odd
number
of
terms
 take
3

cookies

    @glaforge — @paulk_asert                        25
Command chain expressions

 //
methods
with
multiple
arguments
(commas)
 take
coffee

with
sugar,
milk

and
liquor
 



(





).



(










).


(





)

 //
leverage
named‐args
as
punctuation
 check
that:
margarita

tastes
good
 




(














).





(



)

 //
closure
parameters
for
new
control
structures
 given
{}

when
{}

then
{}
 




(

).



(

).



(

)

 //
zero‐arg
methods
require
parens
 select
all

unique()
from
names

 //
possible
with
an
odd
number
of
terms
 take
3

cookies

    @glaforge — @paulk_asert                        25
Command chain expressions

 //
methods
with
multiple
arguments
(commas)
 take
coffee

with
sugar,
milk

and
liquor
 



(





).



(










).


(





)

 //
leverage
named‐args
as
punctuation
 check
that:
margarita

tastes
good
 




(














).





(



)

 //
closure
parameters
for
new
control
structures
 given
{}

when
{}

then
{}
 




(

).



(

).



(

)

 //
zero‐arg
methods
require
parens
 select
all

unique()
from
names
 





(


).







.



(




)

 //
possible
with
an
odd
number
of
terms
 take
3

cookies

    @glaforge — @paulk_asert                        25
Command chain expressions

 //
methods
with
multiple
arguments
(commas)
 take
coffee

with
sugar,
milk

and
liquor
 



(





).



(










).


(





)

 //
leverage
named‐args
as
punctuation
 check
that:
margarita

tastes
good
 




(














).





(



)

 //
closure
parameters
for
new
control
structures
 given
{}

when
{}

then
{}
 




(

).



(

).



(

)

 //
zero‐arg
methods
require
parens
 select
all

unique()
from
names
 





(


).







.



(




)

 //
possible
with
an
odd
number
of
terms
 take
3

cookies
 



(
).

    @glaforge — @paulk_asert                        25
Operator overloading

a
+
b

//
a.plus(b)
a
‐
b

//
a.minus(b)
                                 • Currency amounts
a
*
b

//
a.multiply(b)            – 15.euros + 10.dollars
a
/
b

//
a.divide(b)
a
%
b

//
a.modulo(b)            • Distance handling
a
**
b
//
a.power(b)
a
|
b

//
a.or(b)                  – 10.kilometers - 10.meters
a
&
b

//
a.and(b)
a
^
b

//
a.xor(b)               • Workflow, concurrency
a[b]


//
a.getAt(b)               – taskA | taskB & taskC
a
<<
b
//
a.leftShift(b)
a
>>
b
//
a.rightShift(b)
+a




//
a.unaryPlus()          • Credit an account
‐a




//
a.unaryMinus()           – account << 10.dollars
~a




//
a.bitwiseNegate()          account += 10.dollars


26    @glaforge — @paulk_asert
Builders

• For hierachical structures, extend:
     – BuilderSupport
     – FactoryBuilderSupport
       • nice for extending builders with new nodes and attributes
                                      import
groovy.xml.*
                                      

• Roll your own..                     def
builder
=
new
MarkupBuilder()
                                      builder.html
{
     – with invokeMethod()            



head
{
title
"Chicago!"
}
       or methodMissing()             



body
{
                                      







div(id:
"main")
{
     – with get/setProperty()         











p
"Groovy
rocks!"
                                      







}
      or propertyMissing()            



}
                                      }


27        @glaforge — @paulk_asert
AST Transformations


• Hook into the compiler process to do compile-time
  metaprogramming by modifying the Abstract Syntax Tree




                            Transformation



28    @glaforge — @paulk_asert
@InheritConstructors

• Classes like Exception are painful when extended, as all
  the base constructors should be replicated




 class
CustomException
extends
Exception
{
 



CustomException()























{
super()






}
 



CustomException(String
msg)













{
super(msg)



}
 



CustomException(String
msg,
Throwable
t)
{
super(msg,
t)
}
 



CustomException(Throwable
t)












{
super(t)





}
 }




29     @glaforge — http://glaforge.appspot.com   —   http://gplus.to/glaforge
@InheritConstructors

• Classes like Exception are painful when extended, as all
  the base constructors should be replicated

 import
groovy.transform.*

 @InheritConstructors
 class
CustomException
extends
Exception
{
 



CustomException()























{
super()






}
 



CustomException(String
msg)













{
super(msg)



}
 



CustomException(String
msg,
Throwable
t)
{
super(msg,
t)
}
 



CustomException(Throwable
t)












{
super(t)





}
 }




29     @glaforge — http://glaforge.appspot.com   —   http://gplus.to/glaforge
@InheritConstructor... under the covers

//
...
ClassNode
sNode
=
cNode.getSuperClass();











for
(ConstructorNode
cn
:
sNode.getDeclaredConstructors())
{




Parameter[]
params
=
cn.getParameters();




if
(cn.isPrivate())
continue;




Parameter[]
newParams
=
new
Parameter[params.length];




List<Expression>
args
=
copyParamsToArgs(params,
newParams);




if
(isClashing(cNode,
newParams))
continue;




BlockStatement
body
=
new
BlockStatement();




ConstructorCallExpression
callArgs
=








new
ConstructorCallExpression(ClassNode.SUPER,







































new
ArgumentListExpression(args));




body.addStatement(new
ExpressionStatement(callArgs));




cNode.addConstructor(cn.getModifiers(),
newParams,


























cn.getExceptions(),
body);
}
//
...




30      @glaforge — http://glaforge.appspot.com   —   http://gplus.to/glaforge
Advanced example with Spock


def
"length
of
Spock's
&
his
friends'
names"()
{




expect:








name.size()
==
length




where:








name




|
length








"Spock"

|
5








"Kirk"


|
4








"Scotty"
|
6
}




31   @glaforge — http://glaforge.appspot.com   —   http://gplus.to/glaforge
• A week in the life of DSL inc.

           – find the examples on GitHub:


                https://github.com/paulk-asert/
                DSLsFromBeginnerToExpert




32   © 2011 SpringOne 2GX 2011. All rights reserved. Do not distribute without permission.
MONDAY




33   @glaforge — @paulk_asert
Currency DSL…




                From:



customer@acme.org
                To:





Guillaume
                Subject:
Project
Request

                Dear
Guillaume,

                We
would
like
a
DSL
for
capturing
currency

                expressions.
Just
US
currency
to
start
with.

                Thanks,
Happy
Customer




34   @glaforge — @paulk_asert
…Currency DSL...

     enum
Coin
{
     



penny(1),
nickel(5),
dime(10),
quarter(25)
     



Coin(int
value)
{
this.value
=
value
}
     



int
value
     }

     import
static
Coin.*
     

     assert
2
*
quarter.value
+
     






1
*
nickel.value
+
     






2
*
penny.value
==
57



35     @glaforge — @paulk_asert
…Currency DSL…



              From:



customer@acme.org
              To:





Guillaume
              Subject:
Project
Request

              Dear
Guillaume,

              That
works
fairly
well
but
can
we
get
rid

              of
the
‘.value’
part
of
those
expressions.

              They
are
confusing
our
users.

              Thanks,
Happy
Customer


36   @glaforge — @paulk_asert
...Currency DSL...
//
Category
class
CoinMath
{




static
multiply(Integer
self,
Coin
c)
{








self
*
c.value




}
}


use
(CoinMath)
{




assert
2
*
quarter
+











1
*
nickel
+











2
*
penny
==
57
}




37     @glaforge — @paulk_asert
...Currency DSL...
//
Category
class
CoinMath
{




static
multiply(Integer
self,
Coin
c)
{








self
*
c.value




}
}


use
(CoinMath)
{




assert
2
*
quarter
+











1
*
nickel
+         //
EMC
equivalent











2
*
penny
==
57      Integer.metaClass.multiply
=
{
}                               



Coin
c
‐>
delegate
*
c.value
                                }

                                  assert
2
*
quarter
+

                                  






1
*
nickel
+

                                  






2
*
penny
==
57


37     @glaforge — @paulk_asert
…Currency DSL…



              From:



customer@acme.org
              To:





Guillaume
              Subject:
Project
Request

              Dear
Guillaume,

              Much
better
but
our
users
are
sometimes

              using
plural
as
well
as
singular

              expressions.
Is
there
anything
that
you

              can
do
about
that?

              Thanks,
Happy
Customer


38   @glaforge — @paulk_asert
... Currency DSL
class
CoinValues
{




static
get(Integer
self,
String
name)
{








self
*
Coin."${singular(name)}".value




}




static
singular(String
val)
{








val.endsWith('ies')
?
val[0..‐4]
+
'y'
:
val.endsWith('s')
?
val[0..‐2]
:
val




}
}


use
(CoinValues)
{




assert
2.quarters
+
1.nickel
+
2.pennies
==
57
}




39       @glaforge — @paulk_asert
... Currency DSL
class
CoinValues
{




static
get(Integer
self,
String
name)
{








self
*
Coin."${singular(name)}".value




}




static
singular(String
val)
{








val.endsWith('ies')
?
val[0..‐4]
+
'y'
:
val.endsWith('s')
?
val[0..‐2]
:
val




}
}


use
(CoinValues)
{




assert
2.quarters
+
1.nickel
+
2.pennies
==
57
}


           //
EMC
equivalent
           Integer.metaClass.getProperty
=
{
String
name
‐>
           



def
mp
=
Integer.metaClass.getMetaProperty(name)
           



if
(mp)
return
mp.getProperty(delegate)
           



def
singular
=
name.endsWith('ies')
?
name[0..‐4]
+
'y'
:
           


















name.endsWith('s')
?
name[0..‐2]
:
name
           



delegate
*
Coin."$singular".value
           }
           

           assert
2.quarters
+
1.nickel
+
2.pennies
==
57


39       @glaforge — @paulk_asert
TUESDAY




http://www.google.com/imgres?imgurl=http://i733.photobucket.com/albums/ww336/snake1953/Day_of_Week/Tue/HappyTuesda
    40          @glaforge — @paulk_asert
%3D1%26hl%3Den%26client%3Dfirefox-a%26sa%3DN%26rls%3Dorg.mozilla:en-US:official%26biw%3D1488%26bih%3D852%
Business logic DSL

            From:



customer@acme.org
            To:





Paul
King
            Subject:
Project
Request

            Dear
Paul,

            Could
you
please
create
us
a
small
DSL
for
capturing
            our
business
rules.
We
are
thinking
of
something
like:

            



price
=
3
            



quantity
=
10
            



total
=
price
*
quantity

            Perhaps
also
an
ability
to
check
values:

            



assert
total
==
30

            Thanks,
Happy
Customer

            P.S.
Will
send
a
couple
of
more
details
in
a
follow‐up
email
but
            please
consider
the
requirements
as
being
pretty
much
locked
down.

41   @glaforge — @paulk_asert
Business logic DSL

• Options
     – Embedded Groovy
     – Regex parser
     – Antlr parser
     – Split / StringTokenizer
     – Parser combinators




42        @glaforge — @paulk_asert
Business logic DSL


           From:



customer@acme.org
           To:





Paul
King
           Subject:
Project
Request
           Date:



Mid
morning

           Dear
Paul,

           We
were
thinking
a
bit
more
about
it,
and
it
would
be
good
to
have
           just
a
few
more
features.
Hopefully,
they
won’t
have
much
impact
           on
your
existing
design
and
can
be
added
very
quickly.
It
isn’t
           much,
just
the
ability
to
have
IF,
THEN,
ELSE
like
structures,
oh
           yeah
and
the
ability
to
have
loops,
and
store
stuff
in
files
and
           get
stuff
from
databases
and
web
services
if
we
need.

           Thanks,
Happy
Customer

           P.S.
It
would
be
great
if
you
can
be
finished
by
this
afternoon.
           We
have
a
customer
who
would
like
this
feature
RSN.
Thanks.


43   @glaforge — @paulk_asert
Business logic DSL


           def
shell
=
new
GroovyShell()
           shell.evaluate('''
           



price
=
3
           



quantity
=
10
           



total
=
price
*
quantity
           



assert
total
==
30
           ''')




44   @glaforge — @paulk_asert
Business logic DSL


            From:



customer@acme.org
            To:





Paul
King
            Subject:
Project
Request
            Date:



This
afternoon

            Dear
Paul,

            We
were
really
happy
with
the
DSL
engine
you
provided
us

            with
but
people
started
importing
all
sorts
of
classes.
Can

            you
stop
them
from
doing
that?
Maybe
just
java.lang.Math

            only.
Also
we
decided
we
don’t
like
“while”
loops
anymore.

            Leave
you
to
it.

            Thanks,
Happy
Customer

            P.S.
Have
a
beer
and
crab
dinner
for
me
at
the
conference.

            Bye.

45   @glaforge — @paulk_asert
Business logic DSL
        class
CustomerShell
{
        



Object
evaluate(String
text)
{
        







try
{
        











def
loader
=
new
CustomerClassLoader()
        











def
clazz
=
loader.parseClass(text)
        











def
script
=
clazz.newInstance()
        











return
script.run()
        







}
catch
(...)
{
...
}
        



}
        }
        

        class
CustomerClassLoader
extends
GroovyClassLoader
{
        



def
createCompilationUnit(CompilerConfiguration
config,
CodeSource
codeSource)
{
        







CompilationUnit
cu
=
super.createCompilationUnit(config,
codeSource)
        







cu.addPhaseOperation(new
CustomerFilteringNodeOperation(),
Phases.SEMANTIC_ANALYSIS)
        







return
cu
        



}
        }
        

        private
class
CustomerFilteringNodeOperation
extends
PrimaryClassNodeOperation
{
        



//
...
        



private
static
final
allowedStaticImports
=
[Math].asImmutable()
        



void
visitStaticMethodCallExpression(StaticMethodCallExpression
smce)
{
        







if
(!allowedStaticImports.contains(smce.ownerType.getTypeClass()))
{
        











throw
new
SecurityException("Static
method
call
expressions
forbidden
in
acme
shell.")
        







}
        



}
        



void
visitWhileLoop(WhileStatement
whileStatement)
{
        







throw
new
SecurityException("While
statements
forbidden
in
acme
shell.")
        


}
        


//
...
        }

   46          @glaforge — @paulk_asert
Please also see the ArithmeticShell which is included under examples in the Groovy distribution
Business logic DSL



           def
shell
=
new
CustomerShell()
           shell.evaluate('''
           



price
=
3
           



quantity
=
10
           



total
=
price
*
quantity
           



assert
total
==
30
           ''')




47   @glaforge — @paulk_asert
WEDNESDAY




   48          @glaforge — @paulk_asert
http://www.crystalscomments.com/1/days/wednesday/animals/006.jpg
Stock Exchange Order DSL


                      From:



customer@finance‐broker.org
                      To:





Guillaume
                      Subject:
Project
Request
                      Date:



Wednesday
morning

                      Dear
Guillaume,

                      For
our
order
processing
system
we
have
a
need
to
capture

                      client
orders
using
a
DSL.
An
order
includes
the
name
of

                      the
security
to
be
transacted
(buy
or
sell)
as
well
as

                      quantity
and
unit
price
details
to
specify
any
constraint

                      that
the
counterparty
would
like
to
impose
on
the
price

                      of
transaction.

                      Thanks,
Happy
Customer
Example inspired
from «DSLs in Action»:
http://www.manning.com/ghosh/

 49        @glaforge — @paulk_asert
//
‐‐‐‐‐
Implementation
of
the
Fluent
API
‐‐‐‐‐
… Stock Exchange Order DSL…
enum
Action
{
Buy,
Sell
}

                                                         println
new
Order()
class
Order
{                                             



.sell(150,
"IBM")




def
security




def
quantity,
limitPrice                              



.limitPrice(300)




boolean
allOrNone




def
valueCalculation
                                                          



.allOrNone(true)




Action
action                                         



.valueAs
{
qty,
unitPrice
‐>






def
buy(Integer
quantity,
String
security)
{
                                                          








qty
*
unitPrice
‐
100
}








this.quantity
=
quantity                          









this.security
=
security








this.action
=
Action.Buy
                                                          println
new
Order()








return
this                                       



.buy(200,
"GOOG")




}




def
sell(Integer
quantity,
String
security)
{         



.limitPrice(200)








this.quantity
=
quantity                          



.allOrNone(true)








this.security
=
security








this.action
=
Action.Sell                         



.valueAs{
qty,
unitPrice
‐>








return
this                                       







qty
*
unitPrice
‐
500
}




}




def
limitPrice(Integer
limit)
{








this.limitPrice
=
limit;
return
this




}                                                Sell 150 shares of IBM at valuation of 44900




def
allOrNone(boolean
allOrNone)
{








this.allOrNone
=
allOrNone;
return
this      Buy 200 shares of GOOG at valuation of 39500




}




def
valueAs(Closure
valueCalculation)
{








this.valueCalculation
=
valueCalculation;
return
this




}




String
toString()
{








"$action
$quantity
shares
of
$security
at
valuation
of
${valueCalculation(quantity,
limitPrice)}"




}
}


50         @glaforge — @paulk_asert
Stock Exchange Order DSL



             From:



customer@finance‐broker.org
             To:





Guillaume
             Subject:
Project
Request
             Date:



Wednesday
morning

             Dear
Guillaume,

             That
version
was
great
but
can
you
make
the
             DSL
a
little
more
fluent.
The
brokers
aren’t
             programmers
after
all!

             Thanks,
Happy
Customer

51   @glaforge — @paulk_asert
Stock Exchange Order DSL
     class
Order
{
     



def
security,
quantity,
limitPrice,
allOrNone,
value,
bs
     

     



def
buy(securityQuantity,
closure)
{
     







bs
=
'Bought'
     







buySell(securityQuantity,
closure)
     



}
     




     



def
sell(securityQuantity,
closure)
{
     







bs
=
'Sold'
     







buySell(securityQuantity,
closure)
     



}
     

     



private
buySell(securityQuantity,
closure)
{
     







//
multiple
assignment
     







(security,
quantity)
=
[securityQuantity.security,
securityQuantity.quantity]
     







//
better
clone
the
closure
to
avoid
multi‐threading
access
issues
     







def
c
=
closure.clone()
     







//
delegate
the
method
calls
inside
the
closure
to
our
methodMissing
     







c.delegationStrategy
=
Closure.DELEGATE_ONLY
     







c.delegate
=
this
     







def
valuation
=
c()
     







println
"$bs
$quantity
$security.name
at
valuation
of
$valuation"
     



}
     

     



//
methods
inside
the
closure
will
assign
the
Order
properties
     



def
methodMissing(String
name,
args)
{
this."$name"
=
args[0]
}


52        @glaforge — @paulk_asert
Stock Exchange Order DSL
 



def
getTo()
{
this
}                                newOrder.to.buy(100.shares.of(IBM))
{
 

                                                      



limitPrice

300
 



def
valueAs(closure)
{                              



allOrNone


true
 







value
=
closure(quantity,
limitPrice)           



valueAs




{
qty,
unitPrice
‐>
 



}
                                                         







qty
*
unitPrice
‐
200
}
 }
 
                                                       }
 class
Security
{                                        

 



String
name                                         newOrder.to.sell
200.shares.of(GOOG),
{
 }                                                       



limitPrice

200
 

 class
Quantity
{
                                                         



allOrNone


false
 



Security
security                                   



valueAs




{
qty,
unitPrice
‐>
 



Integer
quantity                                    







qty
*
unitPrice
‐
500
}
 }                                                       }
 

 Integer.metaClass.getShares
=
{
‐>
delegate
}
 Integer.metaClass.of
=
{
new
Quantity(security:
it,
quantity:
delegate)
}
 

 class
CustomBinding
extends
Binding
{
 



def
getVariable(String
symbol)
{
 







//
create
a
new
order
each
time
 







//
for
when
you
pass
several
orders
 







if
(symbol
==
"newOrder")
new
Order()
 







//
otherwise,
it's
an
instrument
 







//
trick
to
avoid
using
strings:
use
IBM
instead
of
'IBM'
 







else
new
Security(name:
symbol)

 



}
 }
 

 //
use
the
script
binding
for
retrieving
IBM,
etc.
 binding
=
new
CustomBinding()
53        @glaforge — @paulk_asert
Stock Exchange Order DSL


                  From:



customer@finance‐broker.org
                  To:





Guillaume
                  Subject:
Project
Request
                  Date:



Wednesday
lunch
time

                  Dear
Guillaume,

                  That
version
was
great
but
can
we
simplify
the
                  constraint
to
remove
the
parameters,
i.e.:
change

                  



{
qty,
unitPrice
‐>
qty
*
unitPrice
‐
200
}

                  to
this:

                  



{
qty
*
unitPrice
‐
200
}

                  Thanks,
Happy
Customer

54   @glaforge — @paulk_asert
Stock Exchange Order DSL
                                                newOrder.to.buy(100.shares.of(IBM))
{
class
Order
{
                                                  



limitPrice

300
...
                                                  



allOrNone


true




def
allOrNone,
valueCalculation,
bs
                                                  



valueAs




{
qty
*
unitPrice
‐
200
}
...




private
buySell(securityQuantity,
closure)
{ }
...                                               









valueCalculation
=
c()                    newOrder.to.sell
200.shares.of(GOOG),
{








//
debug
print
resulting
order            



limitPrice

200








println
toString()                        



allOrNone


false








return
this                               



valueAs




{
qty
*
unitPrice
‐
500
}




}                                             }
...




def
valueAs(Closure
wrapee)
{




//
in
order
to
be
able
to
define
closures
like
{
qty
*
unitPrice
}
without
having




//
to
explicitly
pass
the
parameters
to
the
closure
we
can
wrap
the
closure
inside




//
another
one
and
that
closure
sets
a
delegate
to
the
qty
and
unitPrice
variables








def
wrapped
=
{
qty,
unitPrice
‐>












def
cloned
=
wrapee.clone()












cloned.resolveStrategy
=
Closure.DELEGATE_ONLY












cloned.delegate
=
[qty:
qty,
unitPrice:
unitPrice]












cloned()








}








return
wrapped




}
...
}


55        @glaforge — @paulk_asert
Stock Exchange Order DSL



             From:



customer@finance‐broker.org
             To:





Guillaume
             Subject:
Project
Request
             Date:



Wednesday
afternoon

             Dear
Guillaume,

             Fantastic!
This
is
getting
better
all
the

             time!
But
can
we
get
rid
most
of
the
‘.’
and

             bracket
symbols!

             Thanks,
Happy
Customer

56   @glaforge — @paulk_asert
Stock Exchange Order DSL
     



//
Characteristics
of
the
order:
"of
GOOG
{...}"
     



def
of(SecurityAndCharacteristics
secAndCharact)
{
     







security
=
secAndCharact.security
     







def
c
=
secAndCharact.characteristics.clone()
     







c.delegationStrategy
=
Closure.DELEGATE_ONLY
     







c.delegate
=
this
     







c()
     







//
debug
print
of
the
resulting
order
     







println
toString()
     







return
this
     



}
     

     




//
Valuation
closure:
"of
{
qty,
unitPrice
‐>
...
}"
     




def
of(Closure
valueCalculation)
{
     








//
in
order
to
be
able
to
define
closures
like
{
qty
*
unitPrice
}
     








//
without
having
to
explicitly
pass
the
parameters
to
the
closure
     








//
we
can
wrap
the
closure
inside
another
one
     








//
and
that
closure
sets
a
delegate
to
the
qty
and
unitPrice
variables
     








def
wrapped
=
{
qty,
unitPrice
‐>
     












def
cloned
=
valueCalculation.clone()
     












cloned.resolveStrategy
=
Closure.DELEGATE_ONLY
     












cloned.delegate
=
[qty:
qty,
unitPrice:
unitPrice]
     












cloned()
     








}
     








return
wrapped
     



}
     }



57      @glaforge — @paulk_asert
          http://groovyconsole.appspot.com/script/226001 by glaforge
Stock Exchange Order DSL
        class
Security
{
        



String
name
        }
        

        class
SecurityAndCharacteristics
{
        



Security
security
        



Closure
characteristics
        }
        

        class
CustomBinding
extends
Binding
{
        



def
getVariable(String
word)
{
        







//
return
System.out
when
the
script
requests
to
write
to
'out'
        







if
(word
==
"out")
System.out
        







//
don't
throw
an
exception
and
return
null
        







//
when
a
silent
sentence
word
is
used,
        







//
like
"to"
and
"the"
in
our
DSL
        







null
        



}
        }
        

        //
Script
helper
method
for
"GOOG
{}",
"VMW
{}",
etc.
        def
methodMissing(String
name,
args)
{
        



new
SecurityAndCharacteristics(
        







security:
new
Security(name:
name),
        







characteristics:
args[0]
        



)
        }
        

        //
Script
helper
method
to
make
"order
to"
silent
        //
by
just
creating
our
current
order
        def
order(to)
{
new
Order()
}
        

        //
use
the
script
binding
for
silent
sentence
words
like
"to",
"the"
        binding
=
new
CustomBinding()
        //
syntax
for
200.shares
        Integer.metaClass.getShares
=
{
‐>
delegate
}
58   @glaforge — @paulk_asert
Stock Exchange Order DSL


     order
to
buy
200.shares
of
GOOG
{
     



limitPrice






500
     



allOrNone







false
     



at
the
value
of

{
qty
*
unitPrice
‐
100
}
     }
     

     order
to
sell
150.shares
of
VMW
{
     



limitPrice






80
     



allOrNone







true
     



at
the
value
of

{
qty
*
unitPrice
}
     }




59     http://groovyconsole.appspot.com/script/226001
      @glaforge — @paulk_asert                          by glaforge
Stock Exchange Order DSL




            From:



customer@finance‐broker.org
            To:





Guillaume
            Subject:
Project
Request
            Date:



Wednesday
evening

            Dear
Guillaume,

            Brilliant!
Even
our
CEO
could
write
orders!

            Thanks,
Happy
Customer


60   @glaforge — @paulk_asert
THURSDAY




   61             @glaforge — @paulk_asert
http://t1.gstatic.com/images?q=tbn:_VBloEP8YC-6IM:http://msp248.photobucket.com/albums/gg171/ingrid2002/TextPL252028
Einstein’s Riddle DSL …




                    From:



customer@acme.org
                    To:





Paul
King
                    Subject:
Project
Request
                    Date:



Early
morning

                    Dear
Paul,

                    We
would
like
a
DSL
for
capturing
our
business

                    rules.
Our
business
rules
are
captured
in
the
form

                    of
logic
clauses.
They
are
very
much
like
those

                    found
in
Einstein’s
riddle.

                    Thanks,
Happy
Customer


62   @glaforge — @paulk_asert
Einstein’s Riddle

• Wikipedia: The zebra puzzle is a well-known logic puzzle

     – It is often called Einstein's Puzzle or Einstein's Riddle because it
       is said to have been invented by Albert Einstein as a boy, with
       the claim that Einstein said “only 2 percent of the world's
       population can solve it.”


     – The puzzle is also sometimes attributed to Lewis Carroll.
       However, there is no known evidence for Einstein's or Carroll's
       authorship; and the original puzzle cited below mentions brands
       of cigarette, such as Kools, that did not exist during Carroll's
       lifetime or Einstein's boyhood

63        @glaforge — @paulk_asert
Einstein’s Riddle

• Some premises:
     – The British person lives in the red house
     – The Swede keeps dogs as pets
     – The Dane drinks tea
     – The green house is on the left of the white house
     – The green homeowner drinks coffee
     – The man who smokes Pall Mall keeps birds
     – The owner of the yellow house smokes Dunhill
     – The man living in the center house drinks milk
     – The Norwegian lives in the first house
     – The man who smokes Blend lives next to the one who keeps cats
     – The man who keeps the horse lives next to the man who smokes Dunhill
     – The man who smokes Bluemaster drinks beer
     – The German smokes Prince
     – The Norwegian lives next to the blue house


64          @glaforge — @paulk_asert
Einstein’s Riddle

• Some premises:
     – The British person lives in the red house             • And a question:
     – The Swede keeps dogs as pets                          – Who owns the fish?
     – The Dane drinks tea
     – The green house is on the left of the white house
     – The green homeowner drinks coffee
     – The man who smokes Pall Mall keeps birds
     – The owner of the yellow house smokes Dunhill
     – The man living in the center house drinks milk
     – The Norwegian lives in the first house
     – The man who smokes Blend lives next to the one who keeps cats
     – The man who keeps the horse lives next to the man who smokes Dunhill
     – The man who smokes Bluemaster drinks beer
     – The German smokes Prince
     – The Norwegian lives next to the blue house


64          @glaforge — @paulk_asert
Einstein’s Riddle : Prolog
%
from
http://www.baptiste‐wicht.com/2010/09/solve‐einsteins‐riddle‐using‐prolog

%
Preliminary
definitions
persons(0,
[])
:‐
!.
persons(N,
[(_Men,_Color,_Drink,_Smoke,_Animal)|T])
:‐
N1
is
N‐1,
persons(N1,T).
person(1,
[H|_],
H)
:‐
!.
person(N,
[_|T],
R)
:‐
N1
is
N‐1,
person(N1,
T,
R).

%
The
Brit
lives
in
a
red
house
hint1([(brit,red,_,
_,
_)|_]).
hint1([_|T])
:‐
hint1(T).

%
The
Swede
keeps
dogs
as
pets
hint2([(swede,_,_,_,dog)|_]).
hint2([_|T])
:‐
hint2(T).

%
The
Dane
drinks
tea
hint3([(dane,_,tea,_,_)|_]).
hint3([_|T])
:‐
hint3(T).

%
The
Green
house
is
on
the
left
of
the
White
house
hint4([(_,green,_,_,_),(_,white,_,_,_)|_]).
hint4([_|T])
:‐
hint4(T).

%
The
owner
of
the
Green
house
drinks
coffee.
hint5([(_,green,coffee,_,_)|_]).
hint5([_|T])
:‐
hint5(T).
...



65        @glaforge — @paulk_asert
Einstein’s Riddle DSL



           From:



customer@acme.org
           To:





Paul
King
           Subject:
Project
Request
           Date:



Early
morning

           Dear
Paul,

           Thanks
for
your
Prolog
solution
but
we
don’t
have
           Prolog
installed.
Do
you
have
a
version
that
runs
           on
the
JVM?

           Thanks,
Happy
Customer


66   @glaforge — @paulk_asert
Einstein’s Riddle : Polyglot


@GrabResolver('https://oss.sonatype.org/content/repositories/snapshots/')
@Grab('org.prolog4j:prolog4j‐api:0.2.1‐SNAPSHOT')
@Grab('org.prolog4j:prolog4j‐tuprolog:0.2.1‐SNAPSHOT')
import
org.prolog4j.*

def
p
=
ProverFactory.prover
p.addTheory(new
File('/GroovyExamples/tuProlog/src/einstein.pl').text)
def
sol
=
p.solve("solution(Persons).")
println
sol.get('Persons')




67      @glaforge — @paulk_asert
Einstein’s Riddle DSL …



          From:



customer@acme.org
          To:





Paul
King
          Subject:
Project
Request
          Date:



Early
morning

          Dear
Paul,

          Thanks
for
that
version
but
in
terms
of

          maintaining
the
rules,
we
would
like
a
more

          fluent
expression
of
the
rules
rather
than

          Prolog?
Any
thoughts?

          Thanks,
Happy
Customer

68   @glaforge — @paulk_asert
Einstein’s Riddle : Polyglot w/ DSL
           //
define
some
domain
classes
and
objects
           enum
Pet
{
dog,
cat,
bird,
fish,
horse
}
           enum
Color
{
green,
white,
red,
blue,
yellow
}
           enum
Smoke
{
dunhill,
blends,
pallmall,
prince,
bluemaster
}
           enum
Drink
{
water,
tea,
milk,
coffee,
beer
}
           enum
Nationality
{
Norwegian,
Dane,
Brit,
German,
Swede
}
           

           dogs
=
dog;
birds
=
bird;
cats
=
cat;
horses
=
horse
           a
=
owner
=
house
=
the
=
abode
=
person
=
man
=
is
=
to
=
           



side
=
next
=
who
=
different
=
'ignored'


     //
some
preliminary
definitions
     p
=
ProverFactory.prover
     hintNum
=
1
     

     p.addTheory('''
     



persons(0,
[])
:‐
!.
     



persons(N,
[(_Men,_Color,_Drink,_Smoke,_Animal)|T])
:‐
N1
is
N‐1,
persons(N1,T).
     



person(1,
[H|_],
H)
:‐
!.
     



person(N,
[_|T],
R)
:‐
N1
is
N‐1,
person(N1,
T,
R).
     ''')



69          @glaforge — @paulk_asert
Einstein’s Riddle : Polyglot w/ DSL…
     //
define
some
helper
methods
(our
interface
to
prolog)

     def
addPairHint(Map
m)
{
     



def
from
=
m.from?.toString()?.toLowerCase()
     



p.addTheory("""
     



hint$hintNum([(${from
?:
'_'},${m.color
?:
'_'},${m.drink
?:
'_'},
     







${m.smoke
?:
'_'},${m.pet
?:
'_'})|_]).
     



hint$hintNum([_|T])
:‐
hint$hintNum(T).
     



""")
     



hintNum++
     }
     

     def
addPositionHint(Map
m,
int
pos)
{
     



def
from
=
m.from?.toString()?.toLowerCase()
     



p.addTheory("""
     







hint$hintNum(Persons)
:‐
person($pos,
Persons,
(${from
?:
'_'},
     







${m.color
?:
'_'},$



{m.drink
?:
'_'},${m.smoke
?:
'_'},${m.pet
?:
'_'})).
     



""")
     



hintNum++
     }
     

     def
addToLeftHint(Map
left,
Map
right)
{
     



p.addTheory("""
     







hint$hintNum([(_,$left.color,_,_,_),(_,$right.color,_,_,_)|_]).
     







hint$hintNum([_|T])
:‐
hint$hintNum(T).
     



""")
     



hintNum++
     }
     ...
70           @glaforge — @paulk_asert
Einstein’s Riddle : Polyglot w/ DSL
     //
now
implement
DSL
in
terms
of
helper
methods
     def
the(Nationality
n)
{
     



def
ctx
=
[from:n]
     



[
     







drinks:
{
d
‐>
addPairHint(ctx
+
[drink:d])
},
     







smokes:
{
s
‐>
addPairHint(ctx
+
[smoke:s])
},
     







keeps:
{
p
‐>
addPairHint(ctx
+
[pet:p])
},
     







rears:
{
p
‐>
addPairHint(ctx
+
[pet:p])
},
     







owns:
{
_the
‐>
[first:
{
house
‐>
addPositionHint(ctx,
1)
}]
},
     







has:
{
_a
‐>
     











[pet:
{
a
‐>
addPairHint(ctx
+
[pet:a])
}]
+
     















Color.values().collectEntries
{
c
‐>
     



















[c.toString(),
{
_dummy
‐>
addPairHint(ctx
+
[color:c])
}
]
     















}
     







},
     







lives:
{
_next
‐>
[to:
{
_the
‐>
     











Color.values().collectEntries{
c
‐>
     















[c.toString(),
{
_dummy
‐>
addNeighbourHint(ctx,
[color:c])
}
]
     











}
     







}]}
     



]
     }
     ...



71         @glaforge — @paulk_asert
Einstein’s Riddle : Polyglot w/ DSL

//
now
define
the
DSL
the
man
from
the
centre
house
drinks
milk
the
Norwegian
owns
the
first
house
the
Dane
drinks
tea
the
German
smokes
prince
the
Swede
keeps
dogs
//
alternate
ending:
has
a
pet
dog
the
Brit
has
a
red
house

//
alternate
ending:
red
abode
the
owner
of
the
green
house
drinks
coffee
the
owner
of
the
yellow
house
smokes
dunhill
the
person
known
to
smoke
pallmall
rears
birds
//
alternate
end:
keeps
birds
the
man
known
to
smoke
bluemaster
drinks
beer
the
green
house
is
on
the
left
side
of
the
white
house
the
man
known
to
smoke
blends
lives
next
to
the
one
who
keeps
cats
the
man
known
to
keep
horses
lives
next
to
the
man
who
smokes
dunhill
the
man
known
to
smoke
blends
lives
next
to
the
one
who
drinks
water
the
Norwegian
lives
next
to
the
blue
house




72      @glaforge — @paulk_asert
Einstein’s Riddle DSL


                    From:



customer@acme.org
                    To:





Paul
King
                    Subject:
Project
Request
                    Date:



Early
morning

                    Dear
Paul,

                    That’s
great.
We
even
get
some
code

                    completion
When
using
an
IDE.
Is
there

                    anything
more
we
can
do
to
get
more

                    completion?

                    Thanks,
Happy
Customer

73   @glaforge — @paulk_asert
Einstein’s Riddle : Polyglot w/ DSL
          //
now
implement
DSL
in
terms
of
helper
methods
          def
the(Nationality
n)
{
          



def
ctx
=
[from:n]
          



[
          







drinks:
{
d
‐>
addPairHint(ctx
+
[drink:d])
},
          







smokes:
{
s
‐>
addPairHint(ctx
+
[smoke:s])
},
          







keeps:
{
p
‐>
addPairHint(ctx
+
[pet:p])
},
          







...
          



]
          }
          ...


                                the
German
smokes
prince
                                the(German).smokes(prince)
n
=
German
ctx
=
[from:
German]
[drinks:
…,

smokes:
{
s
‐>
addPairHint([from:
German,
smoke:
s])
},

keeps:
…,

…                                 addPairHint([from:
German,
smoke:

]                                  prince])

  74     @glaforge — @paulk_asert
Einstein’s Riddle : Polyglot w/ DSL

• Some parts of our DSL are automatically statically inferred,
  e.g. typing ‘bl’ and then asking for completion yields:




• But other parts are not known, e.g. the word ‘house’ in the
  fragment below:

                        ‘house’ is key for a Map and could be any value



75     @glaforge — @paulk_asert
Einstein’s Riddle : Polyglot w/ DSL
     def
the(Color
c1)
{[
     



house:
{
_is
‐>
[on:
{
_the
‐>
[left:
{
_side
‐>
[of:
{
__the
‐>
     







Color.values().collectEntries{
c2
‐>
[c2.toString(),
{
_dummy
‐>
     











addToLeftHint([color:c1],
[color:c2])
     







}]}
     



}]}]}]}
     ]}




      class
HousePlaceHolder
{
      



def
c1,
script
      



def
house(_is)
{
      







[on:
{
_the
‐>
[left:
{
_side
‐>
[of:
{
__the
‐>
      











Color.values().collectEntries
{
c2
‐>
      















[c2.toString(),
{
_dummy
‐>
script.addToLeftHint(
      



















[color:
c1],
[color:
c2]
)}]}
      







}]}]}]
      



}
      }
      def
the(Color
c1)
{
new
HousePlaceHolder(c1:c1,
script:this)
}




76   @glaforge — @paulk_asert
                      ‘house’   is now understood
Einstein’s Riddle DSL


           From:



customer@acme.org
           To:





Paul
King
           Subject:
Project
Request
           Date:



Early
morning

           Dear
Paul,

           That’s
fantastic!
But
we
have
just
started
           standardizing
on
Choco
as
our
logic
solving
           engine.
I
guess
we
need
to
start
from
scratch.
           Let
me
know
what
you
think.

           Thanks,
Happy
Customer


77   @glaforge — @paulk_asert
Einstein’s Riddle : Choco w/ DSL
     @GrabResolver('http://www.emn.fr/z‐info/choco‐solver/mvn/repository/')
     @Grab('choco:choco:2.1.1‐SNAPSHOT')
     import
static
choco.Choco.*
     import
choco.kernel.model.variables.integer.*
     

     def
m
=
new
choco.cp.model.CPModel()
     m.metaClass.plus
=
{
m.addConstraint(it);
m
}
     def
s
=
new
choco.cp.solver.CPSolver()
     choco.Choco.metaClass.static.eq
=
{
c,
v
‐>
delegate.eq(c,
v.ordinal())
}
     def
makeEnumVar(st,
arr)
{

     



choco.Choco.makeIntVar(st,
0,
arr.size()‐1,
choco.Options.V_ENUM)
}
     pets
=
new
IntegerVariable[num]
     colors
=
new
IntegerVariable[num]
     smokes
=
new
IntegerVariable[num]
     drinks
=
new
IntegerVariable[num]
     nations
=
new
IntegerVariable[num]
     

     (0..<num).each
{
i
‐>
     





pets[i]
=
makeEnumVar("pet$i",


pets)
     



colors[i]
=
makeEnumVar("color$i",
colors)
     



smokes[i]
=
makeEnumVar("smoke$i",
smokes)
     



drinks[i]
=
makeEnumVar("drink$i",
drinks)
     


nations[i]
=
makeEnumVar("nation$i",

nations)
     }
     ...

78       @glaforge — @paulk_asert
Einstein’s Riddle : Choco w/ DSL
     //
define
DSL
(simplistic
non‐refactored
version)
     def
neighbours(var1,
val1,
var2,
val2)
{
     



and(
     







ifOnlyIf(eq(var1[0],
val1),
eq(var2[1],
val2)),
     







implies(eq(var1[1],
val1),
or(eq(var2[0],
val2),
eq(var2[2],
val2))),
     







implies(eq(var1[2],
val1),
or(eq(var2[1],
val2),
eq(var2[3],
val2))),
     







implies(eq(var1[3],
val1),
or(eq(var2[2],
val2),
eq(var2[4],
val2))),
     







ifOnlyIf(eq(var1[4],
val1),
eq(var2[3],
val2))
     



)
     }
     iff
=
{
e1,
c1,
e2,
c2
‐>
and(*(0..<num).collect{
     



ifOnlyIf(eq(e1[it],
c1),
eq(e2[it],
c2))
     })
}
     ...
     //
define
the
DSL
in
terms
of
DSL
implementation
     def
the(Nationality
n)
{
     def
ctx
=
[nations,
n]
     [
     



drinks:
iff.curry(*ctx,
drinks),
     



smokes:
iff.curry(*ctx,
smokes),
     



keeps:
iff.curry(*ctx,
pets),
     



rears:
iff.curry(*ctx,
pets),
     



owns:
{
_the
‐>
[first:
{
house
‐>
eq(nations[first],
n)}]
},
     ...


79         @glaforge — @paulk_asert
Einstein’s Riddle : Choco w/ DSL
     //
define
rules
     m
+=
all
pets
are
different
     m
+=
all
colors
are
different
     m
+=
all
smokes
are
different
     m
+=
all
drinks
are
different
     m
+=
all
nations
are
different
     m
+=
the
man
from
the
centre
house
drinks
milk
     m
+=
the
Norwegian
owns
the
first
house
     m
+=
the
Dane
drinks
tea
     m
+=
the
German
smokes
prince
     m
+=
the
Swede
keeps
dogs
//
alternate
ending:
has
a
pet
dog
     m
+=
the
Brit
has
a
red
house

//
alternate
ending:
red
abode
     m
+=
the
owner
of
the
green
house
drinks
coffee
     m
+=
the
owner
of
the
yellow
house
smokes
dunhill
     m
+=
the
person
known
to
smoke
pallmall
rears
birds
//
alt
end:
keeps
birds
     m
+=
the
man
known
to
smoke
bluemaster
drinks
beer
     m
+=
the
green
house
is
on
the
left
side
of
the
white
house
     m
+=
the
man
known
to
smoke
blends
lives
next
to
the
one
who
keeps
cats
     m
+=
the
man
known
to
keep
horses
lives
next
to
the
man
who
smokes
dunhill
     m
+=
the
man
known
to
smoke
blends
lives
next
to
the
one
who
drinks
water
     m
+=
the
Norwegian
lives
next
to
the
blue
house
     ...


80         @glaforge — @paulk_asert
Einstein’s Riddle : Choco w/ DSL
     def
pretty(s,
c,
arr,
i)
{

     



c.values().find{
it.ordinal()
==
s.getVar(arr[i])?.value
}

     }
     

     //
invoke
logic
solver
     s.read(m)
     def
more
=
s.solve()
     while
(more)
{
     



for
(i
in
0..<num)
{
     







print


'The
'












+
pretty(s,
Nationality,
nations,
i)
     







print


'
has
a
pet
'





+
pretty(s,
Pet,
pets,
i)
     







print


'
smokes
'








+
pretty(s,
Smoke,
smokes,
i)
     







print


'
drinks
'








+
pretty(s,
Drink,
drinks,
i)
     







println
'
and
lives
in
a
'
+
pretty(s,
Color,
colors,
i)
+
'
house'
     



}
     



more
=
s.nextSolution()
     }

     Solving
Einstein's
Riddle:
     The
Norwegian
has
a
pet
cat
smokes
dunhill
drinks
water
and
lives
in
a
yellow
house
     The
Dane
has
a
pet
horse
smokes
blends
drinks
tea
and
lives
in
a
blue
house
     The
Brit
has
a
pet
bird
smokes
pallmall
drinks
milk
and
lives
in
a
red
house
     The
German
has
a
pet
fish
smokes
prince
drinks
coffee
and
lives
in
a
green
house
     The
Swede
has
a
pet
dog
smokes
bluemaster
drinks
beer
and
lives
in
a
white
house

81          @glaforge — @paulk_asert
FRIDAY




   82             @glaforge — @paulk_asert
http://t3.gstatic.com/images?q=tbn:mgkj1lXpQ2-uWM:http://i298.photobucket.com/albums/mm269/bearyjuicylicious/comments
Nasa Robot


                    From:



customer@acme.org
                    To:





Guillaume
Laforge
                    Subject:
Project
Request
                    Date:



Early
morning

                    Dear
Guillaume,

                    We’ve
got
a
contract
with
NASA
to
send
a

                    rover
on
Mars.
We’d
need
a
DSL
to
lead

                    the
robot
on
the
rocky
soil.
Can
you

                    help?

                    Thanks,
Happy
Customer

83   @glaforge — @paulk_asert
Nasa Robot
     import
static
Direction.*

     enum
Direction
{
     



left,
right,
forward,
backward
     }

     class
Robot
{
     



void
move(Direction
dir)
{
     







println
"robot
moved
$dir"
     



}
     }
                                   Nothing special here.
     def
robot
=
new
Robot()
                                   But can we make it prettier?
     robot.move
left


84      @glaforge — @paulk_asert
Nasa Robot
package
projectmars.domain

import
static
Direction.*
enum
Direction
{
left,
right,
forward,
backward
}

class
Robot
{




void
move(Direction
dir)
{          Some simplistic   GroovyShell








println
"robot
moved
$dir"




}
}                package
projectmars.integration

                  def
shell
=
new
GroovyShell(this.class.classLoader)
                  shell.evaluate
'''
                  



import
projectmars.domain.Robot
                  



import
static
projectmars.domain.Direction.*
                  




                  



def
robot
=
new
Robot()
                  




                  



robot.move
left
                  '''

85     @glaforge — @paulk_asert
…Nasa Robot…
     package
projectmars.domain

     import
static
Direction.*
     enum
Direction
{
left,
right,
forward,
backward
}

     class
Robot
{
     



void
move(Direction
dir)
{
     







println
"robot
moved
$dir"
     



}
     }                                          Inject robot into binding
     def
binding
=
new
Binding(robot:
new
Robot())
     def
shell
=
new
GroovyShell(this.class.classLoader,
binding)

     shell.evaluate
'''
     



import
static
projectmars.domain.Direction.*
     



robot.move
left
     '''


86         @glaforge — @paulk_asert
Nasa Robot
     package
projectmars.domain

     import
static
Direction.*
     enum
Direction
{
left,
right,
forward,
backward
}

     class
Robot
{
     



void
move(Direction
dir)
{
     







println
"robot
moved
$dir"
     



}
     }
     /*                                     Inject directions into binding
     def
binding
=
new
Binding(
     



robot:
new
Robot(),
     



left:
Direction.left,
right:
Direction.right,
     



forward:
Direction.forward,
backward:
Direction.backward)
     */
     def
binding
=
new
Binding(
     



robot:
new
Robot(),
     



*:Direction.values().collectEntries
{
[(it.name()):
it]
})

     def
shell
=
new
GroovyShell(this.class.classLoader,
binding)
     shell.evaluate
'''
     



robot.move
left
     '''
87         @glaforge — @paulk_asert
…Nasa Robot…
package
projectmars.domain

import
org.codehaus.groovy.control.CompilerConfiguration
import
org.codehaus.groovy.control.customizers.*

import
static
Direction.*
enum
Direction
{
left,
right,
forward,
backward
}

class
Robot
{




void
move(Direction
dir)
{
println
"robot
moved
$dir"
}
}

def
binding
=
new
Binding(robot:
new
Robot())
                                                           Inject import
def
importCustomizer
=
new
ImportCustomizer()
importCustomizer.addStaticStars
'projectmars.domain.Direction'

def
config
=
new
CompilerConfiguration()
config.addCompilationCustomizers
importCustomizer

def
shell
=
new
GroovyShell(this.class.classLoader,
binding,
config)
shell.evaluate
'''




robot.move
left
'''
88     @glaforge — @paulk_asert
package
projectmars.domain
…Nasa Robot…
import
org.codehaus.groovy.control.CompilerConfiguration
     import
org.codehaus.groovy.control.customizers.*

     import
static
Direction.*
     enum
Direction
{
left,
right,
forward,
backward
}

     class
Robot
{
     



void
move(Direction
dir)
{
println
"robot
moved
$dir"
}
     }

     def
binding
=
new
Binding(robot:
new
Robot())
     def
importCustomizer
=
new
ImportCustomizer()
     importCustomizer.addStaticStars
'projectmars.domain.Direction'

     def
config
=
new
CompilerConfiguration()
     config.addCompilationCustomizers
importCustomizer

     def
shell
=
new
GroovyShell(this.class.classLoader,
binding,
config)
     shell.evaluate
'''
     



move
left
     '''
+
'''
     



def
move(dir)
{
     







robot.move
dir                   Can we make ‘robot’ implicit?
     



}
     '''
89          @glaforge — @paulk_asert
package
projectmars.domain
Nasa Robot
import
org.codehaus.groovy.control.CompilerConfiguration
 import
org.codehaus.groovy.control.customizers.*

 import
static
Direction.*
 enum
Direction
{
left,
right,
forward,
backward
}

 class
Robot
{
 



void
move(Direction
dir)
{
println
"robot
moved
$dir"
}
 }

 def
binding
=
new
Binding(robot:
new
Robot())

 def
importCustomizer
=
new
ImportCustomizer()
 importCustomizer.addStaticStars
Direction.class.name

 def
config
=
new
CompilerConfiguration()                      Better ‘robot’ implicit
 config.addCompilationCustomizers
importCustomizer
 config.scriptBaseClass
=
RobotBaseScriptClass.class.name

 def
shell
=
new
GroovyShell(this.class.classLoader,
binding,
config)
 shell.evaluate
'''
 



move
left
 '''

 abstract
class
RobotBaseScriptClass
extends
Script
{
 



void
move(Direction
dir)
{
 







this.binding.robot.move
dir
 



}
 }
90        @glaforge — @paulk_asert
package
projectmars.domain
Nasa Robot
import
org.codehaus.groovy.control.CompilerConfiguration
import
org.codehaus.groovy.control.customizers.*

import
static
Direction.*
enum
Direction
{
left,
right,
forward,
backward
}

class
Robot
{




void
move(Direction
dir)
{
println
"robot
moved
$dir"
}
}
                                                    Better ‘robot’ implicit
def
robot
=
new
Robot()
def
binding
=
new
Binding(robot:
robot,
move:
robot.&move)

def
importCustomizer
=
new
ImportCustomizer()
importCustomizer.addStaticStars
Direction.class.name

def
config
=
new
CompilerConfiguration()
config.addCompilationCustomizers
importCustomizer

def
shell
=
new
GroovyShell(this.class.classLoader,
binding,
config)
shell.evaluate
'''




move
left
'''
91     @glaforge — @paulk_asert
…
Nasa Robot
 shell.evaluate
'''
 



mOvE
lefT                Case insensitive?
 '''

 abstract
class
RobotBaseScriptClass
extends
Script
{
 



@Delegate
@Lazy
Robot
robot
=
this.binding.robot
 




 



def
invokeMethod(String
name,
args)
{
 







robot."${name.toLowerCase()}"(*args)
 



}
 }

   class
CustomBinding
extends
Binding
{
   



private
Map
variables
   




   



CustomBinding(Map
vars)
{
   







this.variables
=
[
   











*:vars,

   











*:Direction.values().collectEntries
{
[(it.name()):
it]
}
   







]
   



}
   




   



def
getVariable(String
name)
{
   







variables[name.toLowerCase()]
   



}
92 }     @glaforge — @paulk_asert
package
projectmars.domain
Nasa Robot
 import
org.codehaus.groovy.control.CompilerConfiguration
     import
org.codehaus.groovy.control.customizers.*

     import
groovy.transform.TupleConstructor

     import
static
Direction.*
     import
static
Duration.*

     enum
Direction
{
     



left,
right,
forward,
backward
     }

     enum
Unit
{
     



centimeter
('cm',
0.01),
                                                            Going further?
     



meter





(
'm',



1),
     



kilometer

('km',
1000)





     



String
abbreviation
     



double
multiplier
     




     



Unit(String
abbr,
double
mult)
{
     







this.abbreviation
=
abbr
     







this.multiplier
=
mult
     



}
     




     



String
toString()
{
abbreviation
}
     }
     …
93          @glaforge — @paulk_asert
…
   …Nasa Robot…
enum
Duration
{
hour
}

@TupleConstructor                          …
class
Distance
{                           class
DistanceCategory
{




double
amount                          



static
Distance
getCentimeters(Number
num)
{




Unit
unit                              







new
Distance(num,
Unit.centimeter)




                                       



}




Speed
div(Duration
dur)
{              



static
Distance
getMeters(Number
num)
{








new
Speed(amount,
unit)            







new
Distance(num,
Unit.meter)




}                                      



}




                                       



static
Distance
getKilometers(Number
num)
{




String
toString()
{
"$amount$unit"
}   







new
Distance(num,
Unit.kilometer)
}                                          



}
                                           



static
Distance
getCm(Number
num)
{
getCentimeters(num)
}
@TupleConstructor                          



static
Distance
getM(Number
num)

{
getMeters(num)
}
class
Speed
{                              



static
Distance
getKm(Number
num)
{
getKilometers(num)
}




double
amount                          }




Unit
unit




                                      class
Robot
{




String
toString()
{
"$amount
$unit/h"
}
                                          



void
move(Direction
dir)
{
}                                         







println
"robot
moved
$dir"
…                                         



}
                                           



void
move(Direction
dir,
Distance
d)
{
                                           







println
"robot
moved
$dir
by
$d"
                                           



}
                                           



void
move(Map
m,
Direction
dir)
{
                                           







println
"robot
moved
$dir
by
$m.by
at
${m.at
?:
'1
km
                                           



}
                                           }
                                           …


   94        @glaforge — @paulk_asert
…Nasa Robot…
 …
 def
binding
=
new
Binding(robot:
new
Robot(),
h:
Duration.hour)

 def
importCustomizer
=
new
ImportCustomizer()
 importCustomizer.addStaticStars
Direction.class.name

 def
config
=
new
CompilerConfiguration()
 config.addCompilationCustomizers
importCustomizer
 config.scriptBaseClass
=
RobotBaseScriptClass.class.name

 def
shell
=
new
GroovyShell(this.class.classLoader,
binding,
config)
 use(DistanceCategory)
{
 shell.evaluate
'''
 



move
left
 



move
right,
3.meters
 



move
right,
by:
3.meters
 



move
right,
by:
3.meters,
at:
5.km/h
 '''
 }

 abstract
class
RobotBaseScriptClass
extends
Script
{
 



@Delegate
@Lazy
Robot
robot
=
this.binding.robot
 }
95      @glaforge — @paulk_asert
Nasa Robot
                                 Going even further?

     shell.evaluate
'''
     



move
left
     



move
right,
3.meters
     

     



move
right,
by:
3.meters
     



move
right,
by:
3.meters,
at:
5.km/h
     

     



move
right
by
3.meters
at
5.km/h


     

     



deploy
left
arm
     '''


96    @glaforge — @paulk_asert
Domain-Specific Language Descriptors

• DSLs are nice, but what about support in my IDE?


• DSLD to the rescue
     – Domain-Specific Language Descriptors
     – for Eclipse STS (but GDLS concent available in IntelliJ IDEA too)


• Idea: a DSL for describing DSLs, in order to provide
     – code-completion
     – code navigation
     – documentation hovers


97        @glaforge — @paulk_asert
Domain-Specific Language Descriptors




98   @glaforge — @paulk_asert
Domain-Specific Language Descriptors




 currentType(
subType(
Number
)
).accept
{
 



[m:
"meter",
yd:
"yard",
cm:
"centimeter",
mi:
"mile",
km:
"kilometer"].each
{
 







property
name:it.key,
type:"Distance",
 











doc:
"""A
<code>${it.value}</code>
from
<a
href="$url">$url</a>"""
 



}
 }




98       @glaforge — @paulk_asert
Groovy DSLs, from Beginner to Expert - Guillaume Laforge and Paul King - SpringOne2GX 2011
Groovy DSLs, from Beginner to Expert - Guillaume Laforge and Paul King - SpringOne2GX 2011
Groovy DSLs, from Beginner to Expert - Guillaume Laforge and Paul King - SpringOne2GX 2011
Groovy DSLs, from Beginner to Expert - Guillaume Laforge and Paul King - SpringOne2GX 2011
Groovy DSLs, from Beginner to Expert - Guillaume Laforge and Paul King - SpringOne2GX 2011

Contenu connexe

Tendances

Rapid Development with Ruby/JRuby and Rails
Rapid Development with Ruby/JRuby and RailsRapid Development with Ruby/JRuby and Rails
Rapid Development with Ruby/JRuby and Railselliando dias
 
Functional Programming with Groovy
Functional Programming with GroovyFunctional Programming with Groovy
Functional Programming with GroovyArturo Herrero
 
Atlassian Groovy Plugins
Atlassian Groovy PluginsAtlassian Groovy Plugins
Atlassian Groovy PluginsPaul King
 
Groovy for Java Developers
Groovy for Java DevelopersGroovy for Java Developers
Groovy for Java DevelopersAndres Almiray
 
Polyglot Programming in the JVM
Polyglot Programming in the JVMPolyglot Programming in the JVM
Polyglot Programming in the JVMAndres Almiray
 
Groovy for java developers
Groovy for java developersGroovy for java developers
Groovy for java developersPuneet Behl
 
groovy transforms
groovy transformsgroovy transforms
groovy transformsPaul King
 
Building DSLs with Xtext - Eclipse Modeling Day 2009
Building DSLs with Xtext - Eclipse Modeling Day 2009Building DSLs with Xtext - Eclipse Modeling Day 2009
Building DSLs with Xtext - Eclipse Modeling Day 2009Heiko Behrens
 
Groovy Update - JavaPolis 2007
Groovy Update - JavaPolis 2007Groovy Update - JavaPolis 2007
Groovy Update - JavaPolis 2007Guillaume Laforge
 
Designing with Groovy Traits - Gr8Conf India
Designing with Groovy Traits - Gr8Conf IndiaDesigning with Groovy Traits - Gr8Conf India
Designing with Groovy Traits - Gr8Conf IndiaNaresha K
 
Introduction To Groovy
Introduction To GroovyIntroduction To Groovy
Introduction To Groovymanishkp84
 
Quick Intro To JRuby
Quick Intro To JRubyQuick Intro To JRuby
Quick Intro To JRubyFrederic Jean
 
Embedding Languages Without Breaking Tools
Embedding Languages Without Breaking ToolsEmbedding Languages Without Breaking Tools
Embedding Languages Without Breaking ToolsLukas Renggli
 

Tendances (20)

Rapid Development with Ruby/JRuby and Rails
Rapid Development with Ruby/JRuby and RailsRapid Development with Ruby/JRuby and Rails
Rapid Development with Ruby/JRuby and Rails
 
Groovy 2.0 webinar
Groovy 2.0 webinarGroovy 2.0 webinar
Groovy 2.0 webinar
 
Functional Programming with Groovy
Functional Programming with GroovyFunctional Programming with Groovy
Functional Programming with Groovy
 
Polyglot JVM
Polyglot JVMPolyglot JVM
Polyglot JVM
 
Atlassian Groovy Plugins
Atlassian Groovy PluginsAtlassian Groovy Plugins
Atlassian Groovy Plugins
 
Groovy Programming Language
Groovy Programming LanguageGroovy Programming Language
Groovy Programming Language
 
Groovy for Java Developers
Groovy for Java DevelopersGroovy for Java Developers
Groovy for Java Developers
 
Polyglot Programming in the JVM
Polyglot Programming in the JVMPolyglot Programming in the JVM
Polyglot Programming in the JVM
 
Groovy for java developers
Groovy for java developersGroovy for java developers
Groovy for java developers
 
groovy transforms
groovy transformsgroovy transforms
groovy transforms
 
Building DSLs with Xtext - Eclipse Modeling Day 2009
Building DSLs with Xtext - Eclipse Modeling Day 2009Building DSLs with Xtext - Eclipse Modeling Day 2009
Building DSLs with Xtext - Eclipse Modeling Day 2009
 
On Web Browsers
On Web BrowsersOn Web Browsers
On Web Browsers
 
Groovy Update - JavaPolis 2007
Groovy Update - JavaPolis 2007Groovy Update - JavaPolis 2007
Groovy Update - JavaPolis 2007
 
Designing with Groovy Traits - Gr8Conf India
Designing with Groovy Traits - Gr8Conf IndiaDesigning with Groovy Traits - Gr8Conf India
Designing with Groovy Traits - Gr8Conf India
 
Introduction To Groovy
Introduction To GroovyIntroduction To Groovy
Introduction To Groovy
 
Quick Intro To JRuby
Quick Intro To JRubyQuick Intro To JRuby
Quick Intro To JRuby
 
Embedding Languages Without Breaking Tools
Embedding Languages Without Breaking ToolsEmbedding Languages Without Breaking Tools
Embedding Languages Without Breaking Tools
 
Java Full Throttle
Java Full ThrottleJava Full Throttle
Java Full Throttle
 
Groovy & Grails
Groovy & GrailsGroovy & Grails
Groovy & Grails
 
Intro to J Ruby
Intro to J RubyIntro to J Ruby
Intro to J Ruby
 

En vedette

Going to Mars with Groovy Domain-Specific Languages
Going to Mars with Groovy Domain-Specific LanguagesGoing to Mars with Groovy Domain-Specific Languages
Going to Mars with Groovy Domain-Specific LanguagesGuillaume Laforge
 
Groovy workshop à Mix-IT 2013
Groovy workshop à Mix-IT 2013Groovy workshop à Mix-IT 2013
Groovy workshop à Mix-IT 2013Guillaume Laforge
 
Buenos Aires Drools Expert Presentation
Buenos Aires Drools Expert PresentationBuenos Aires Drools Expert Presentation
Buenos Aires Drools Expert PresentationMark Proctor
 
UberFire Quick Intro and Overview (early beta Aug 2013)
UberFire Quick Intro and Overview (early beta Aug 2013)UberFire Quick Intro and Overview (early beta Aug 2013)
UberFire Quick Intro and Overview (early beta Aug 2013)Mark Proctor
 
JBoss Drools - Open-Source Business Logic Platform
JBoss Drools - Open-Source Business Logic PlatformJBoss Drools - Open-Source Business Logic Platform
JBoss Drools - Open-Source Business Logic Platformelliando dias
 
16 nutrición en los animales i
16 nutrición en los animales i16 nutrición en los animales i
16 nutrición en los animales iTony R. Diaz
 
110801 anuncio post it emociones 2012
110801 anuncio post it emociones 2012110801 anuncio post it emociones 2012
110801 anuncio post it emociones 2012Villanuevadiz
 
Personal Brand Assessment
Personal Brand Assessment Personal Brand Assessment
Personal Brand Assessment Patrick Sitkins
 
groovy and concurrency
groovy and concurrencygroovy and concurrency
groovy and concurrencyPaul King
 
An introduction to sales 3.0
An introduction to sales 3.0An introduction to sales 3.0
An introduction to sales 3.0CPIconsulting
 
Flor essence desintoxicante
Flor essence desintoxicanteFlor essence desintoxicante
Flor essence desintoxicanteNeuromon 21
 
JBoss Drools - Pure Java Rule Engine
JBoss Drools - Pure Java Rule EngineJBoss Drools - Pure Java Rule Engine
JBoss Drools - Pure Java Rule EngineAnil Allewar
 
groovy DSLs from beginner to expert
groovy DSLs from beginner to expertgroovy DSLs from beginner to expert
groovy DSLs from beginner to expertPaul King
 

En vedette (20)

Going to Mars with Groovy Domain-Specific Languages
Going to Mars with Groovy Domain-Specific LanguagesGoing to Mars with Groovy Domain-Specific Languages
Going to Mars with Groovy Domain-Specific Languages
 
Groovy workshop à Mix-IT 2013
Groovy workshop à Mix-IT 2013Groovy workshop à Mix-IT 2013
Groovy workshop à Mix-IT 2013
 
Buenos Aires Drools Expert Presentation
Buenos Aires Drools Expert PresentationBuenos Aires Drools Expert Presentation
Buenos Aires Drools Expert Presentation
 
UberFire Quick Intro and Overview (early beta Aug 2013)
UberFire Quick Intro and Overview (early beta Aug 2013)UberFire Quick Intro and Overview (early beta Aug 2013)
UberFire Quick Intro and Overview (early beta Aug 2013)
 
JBoss Drools - Open-Source Business Logic Platform
JBoss Drools - Open-Source Business Logic PlatformJBoss Drools - Open-Source Business Logic Platform
JBoss Drools - Open-Source Business Logic Platform
 
GroovyDSLs
GroovyDSLsGroovyDSLs
GroovyDSLs
 
Polygons
PolygonsPolygons
Polygons
 
16 nutrición en los animales i
16 nutrición en los animales i16 nutrición en los animales i
16 nutrición en los animales i
 
110801 anuncio post it emociones 2012
110801 anuncio post it emociones 2012110801 anuncio post it emociones 2012
110801 anuncio post it emociones 2012
 
Metodologia ii
Metodologia iiMetodologia ii
Metodologia ii
 
Prensa 10 enero_09-05-31
Prensa 10 enero_09-05-31Prensa 10 enero_09-05-31
Prensa 10 enero_09-05-31
 
¿Por qué se necesitan Odontólogos Preparados en nuestro país?
¿Por qué se necesitan Odontólogos Preparados en nuestro país?¿Por qué se necesitan Odontólogos Preparados en nuestro país?
¿Por qué se necesitan Odontólogos Preparados en nuestro país?
 
Personal Brand Assessment
Personal Brand Assessment Personal Brand Assessment
Personal Brand Assessment
 
groovy and concurrency
groovy and concurrencygroovy and concurrency
groovy and concurrency
 
An introduction to sales 3.0
An introduction to sales 3.0An introduction to sales 3.0
An introduction to sales 3.0
 
Flor essence desintoxicante
Flor essence desintoxicanteFlor essence desintoxicante
Flor essence desintoxicante
 
Drools
DroolsDrools
Drools
 
JBoss Drools - Pure Java Rule Engine
JBoss Drools - Pure Java Rule EngineJBoss Drools - Pure Java Rule Engine
JBoss Drools - Pure Java Rule Engine
 
groovy DSLs from beginner to expert
groovy DSLs from beginner to expertgroovy DSLs from beginner to expert
groovy DSLs from beginner to expert
 
Pdf psicofundacion[1]
Pdf psicofundacion[1]Pdf psicofundacion[1]
Pdf psicofundacion[1]
 

Similaire à Groovy DSLs, from Beginner to Expert - Guillaume Laforge and Paul King - SpringOne2GX 2011

"Xapi-lang For declarative code generation" By James Nelson
"Xapi-lang For declarative code generation" By James Nelson"Xapi-lang For declarative code generation" By James Nelson
"Xapi-lang For declarative code generation" By James NelsonGWTcon
 
Apache Groovy's Metaprogramming Options and You
Apache Groovy's Metaprogramming Options and YouApache Groovy's Metaprogramming Options and You
Apache Groovy's Metaprogramming Options and YouAndres Almiray
 
Javascript done right - Open Web Camp III
Javascript done right - Open Web Camp IIIJavascript done right - Open Web Camp III
Javascript done right - Open Web Camp IIIDirk Ginader
 
Building maintainable javascript applications
Building maintainable javascript applicationsBuilding maintainable javascript applications
Building maintainable javascript applicationsequisodie
 
Java - A broad introduction
Java - A broad introductionJava - A broad introduction
Java - A broad introductionBirol Efe
 
Gaelyk - JFokus 2011 - Guillaume Laforge
Gaelyk - JFokus 2011 - Guillaume LaforgeGaelyk - JFokus 2011 - Guillaume Laforge
Gaelyk - JFokus 2011 - Guillaume LaforgeGuillaume Laforge
 
Creating Better Builds with Gradle
Creating Better Builds with GradleCreating Better Builds with Gradle
Creating Better Builds with GradleAndres Almiray
 
Automating Your Workflow with Gulp.js - php[world] 2016
Automating Your Workflow with Gulp.js - php[world] 2016Automating Your Workflow with Gulp.js - php[world] 2016
Automating Your Workflow with Gulp.js - php[world] 2016Colin O'Dell
 
PuppetDB: Sneaking Clojure into Operations
PuppetDB: Sneaking Clojure into OperationsPuppetDB: Sneaking Clojure into Operations
PuppetDB: Sneaking Clojure into Operationsgrim_radical
 
IPCSE12: Hands on FLOW3
IPCSE12: Hands on FLOW3IPCSE12: Hands on FLOW3
IPCSE12: Hands on FLOW3Robert Lemke
 
Getting Into FLOW3 (DPC12)
Getting Into FLOW3 (DPC12)Getting Into FLOW3 (DPC12)
Getting Into FLOW3 (DPC12)Robert Lemke
 
Ed presents JSF 2.2 and WebSocket to Gameduell.
Ed presents JSF 2.2 and WebSocket to Gameduell.Ed presents JSF 2.2 and WebSocket to Gameduell.
Ed presents JSF 2.2 and WebSocket to Gameduell.Edward Burns
 
Php on the Web and Desktop
Php on the Web and DesktopPhp on the Web and Desktop
Php on the Web and DesktopElizabeth Smith
 
Ajax Tags Advanced
Ajax Tags AdvancedAjax Tags Advanced
Ajax Tags AdvancedAkramWaseem
 
Oscon Java Testing on the Fast Lane
Oscon Java Testing on the Fast LaneOscon Java Testing on the Fast Lane
Oscon Java Testing on the Fast LaneAndres Almiray
 

Similaire à Groovy DSLs, from Beginner to Expert - Guillaume Laforge and Paul King - SpringOne2GX 2011 (20)

"Xapi-lang For declarative code generation" By James Nelson
"Xapi-lang For declarative code generation" By James Nelson"Xapi-lang For declarative code generation" By James Nelson
"Xapi-lang For declarative code generation" By James Nelson
 
Inside DocBlox
Inside DocBloxInside DocBlox
Inside DocBlox
 
Apache Groovy's Metaprogramming Options and You
Apache Groovy's Metaprogramming Options and YouApache Groovy's Metaprogramming Options and You
Apache Groovy's Metaprogramming Options and You
 
Javascript done right - Open Web Camp III
Javascript done right - Open Web Camp IIIJavascript done right - Open Web Camp III
Javascript done right - Open Web Camp III
 
Building maintainable javascript applications
Building maintainable javascript applicationsBuilding maintainable javascript applications
Building maintainable javascript applications
 
Java - A broad introduction
Java - A broad introductionJava - A broad introduction
Java - A broad introduction
 
Gaelyk - JFokus 2011 - Guillaume Laforge
Gaelyk - JFokus 2011 - Guillaume LaforgeGaelyk - JFokus 2011 - Guillaume Laforge
Gaelyk - JFokus 2011 - Guillaume Laforge
 
Creating Better Builds with Gradle
Creating Better Builds with GradleCreating Better Builds with Gradle
Creating Better Builds with Gradle
 
Automating Your Workflow with Gulp.js - php[world] 2016
Automating Your Workflow with Gulp.js - php[world] 2016Automating Your Workflow with Gulp.js - php[world] 2016
Automating Your Workflow with Gulp.js - php[world] 2016
 
PuppetDB: Sneaking Clojure into Operations
PuppetDB: Sneaking Clojure into OperationsPuppetDB: Sneaking Clojure into Operations
PuppetDB: Sneaking Clojure into Operations
 
IPCSE12: Hands on FLOW3
IPCSE12: Hands on FLOW3IPCSE12: Hands on FLOW3
IPCSE12: Hands on FLOW3
 
Getting Into FLOW3 (DPC12)
Getting Into FLOW3 (DPC12)Getting Into FLOW3 (DPC12)
Getting Into FLOW3 (DPC12)
 
Intro to JavaScript
Intro to JavaScriptIntro to JavaScript
Intro to JavaScript
 
Ed presents JSF 2.2 and WebSocket to Gameduell.
Ed presents JSF 2.2 and WebSocket to Gameduell.Ed presents JSF 2.2 and WebSocket to Gameduell.
Ed presents JSF 2.2 and WebSocket to Gameduell.
 
Php on the Web and Desktop
Php on the Web and DesktopPhp on the Web and Desktop
Php on the Web and Desktop
 
Ajax Tags Advanced
Ajax Tags AdvancedAjax Tags Advanced
Ajax Tags Advanced
 
Oscon Java Testing on the Fast Lane
Oscon Java Testing on the Fast LaneOscon Java Testing on the Fast Lane
Oscon Java Testing on the Fast Lane
 
Linked Process
Linked ProcessLinked Process
Linked Process
 
JavaScript-Core
JavaScript-CoreJavaScript-Core
JavaScript-Core
 
JavaScript-Core
JavaScript-CoreJavaScript-Core
JavaScript-Core
 

Plus de Guillaume Laforge

Les nouveautés de Groovy 2 -- Mix-IT 2013
Les nouveautés de Groovy 2 -- Mix-IT 2013Les nouveautés de Groovy 2 -- Mix-IT 2013
Les nouveautés de Groovy 2 -- Mix-IT 2013Guillaume Laforge
 
Groovy 2.0 update at Devoxx 2012
Groovy 2.0 update at Devoxx 2012Groovy 2.0 update at Devoxx 2012
Groovy 2.0 update at Devoxx 2012Guillaume Laforge
 
Groovy update at SpringOne2GX 2012
Groovy update at SpringOne2GX 2012Groovy update at SpringOne2GX 2012
Groovy update at SpringOne2GX 2012Guillaume Laforge
 
Groovy 1.8 et 2.0 au BreizhC@mp 2012
Groovy 1.8 et 2.0 au BreizhC@mp 2012Groovy 1.8 et 2.0 au BreizhC@mp 2012
Groovy 1.8 et 2.0 au BreizhC@mp 2012Guillaume Laforge
 
Groovy 1.8 and 2.0 at GR8Conf Europe 2012
Groovy 1.8 and 2.0 at GR8Conf Europe 2012Groovy 1.8 and 2.0 at GR8Conf Europe 2012
Groovy 1.8 and 2.0 at GR8Conf Europe 2012Guillaume Laforge
 
Groovy 2.0 update - Cloud Foundry Open Tour Moscow - Guillaume Laforge
Groovy 2.0 update - Cloud Foundry Open Tour Moscow - Guillaume LaforgeGroovy 2.0 update - Cloud Foundry Open Tour Moscow - Guillaume Laforge
Groovy 2.0 update - Cloud Foundry Open Tour Moscow - Guillaume LaforgeGuillaume Laforge
 
Groovy 2.0 - Devoxx France 2012
Groovy 2.0 - Devoxx France 2012Groovy 2.0 - Devoxx France 2012
Groovy 2.0 - Devoxx France 2012Guillaume Laforge
 
Groovy Update, new in 1.8 and beyond - Guillaume Laforge - Devoxx 2011
Groovy Update, new in 1.8 and beyond - Guillaume Laforge - Devoxx 2011Groovy Update, new in 1.8 and beyond - Guillaume Laforge - Devoxx 2011
Groovy Update, new in 1.8 and beyond - Guillaume Laforge - Devoxx 2011Guillaume Laforge
 
GPars et PrettyTime - Paris JUG 2011 - Guillaume Laforge
GPars et PrettyTime - Paris JUG 2011 - Guillaume LaforgeGPars et PrettyTime - Paris JUG 2011 - Guillaume Laforge
GPars et PrettyTime - Paris JUG 2011 - Guillaume LaforgeGuillaume Laforge
 
Groovy Update - Guillaume Laforge - Greach 2011
Groovy Update - Guillaume Laforge - Greach 2011Groovy Update - Guillaume Laforge - Greach 2011
Groovy Update - Guillaume Laforge - Greach 2011Guillaume Laforge
 
Gaelyk update - Guillaume Laforge - SpringOne2GX 2011
Gaelyk update - Guillaume Laforge - SpringOne2GX 2011Gaelyk update - Guillaume Laforge - SpringOne2GX 2011
Gaelyk update - Guillaume Laforge - SpringOne2GX 2011Guillaume Laforge
 
Groovy Update, what's new in Groovy 1.8 and beyond - Guillaume Laforge - Spri...
Groovy Update, what's new in Groovy 1.8 and beyond - Guillaume Laforge - Spri...Groovy Update, what's new in Groovy 1.8 and beyond - Guillaume Laforge - Spri...
Groovy Update, what's new in Groovy 1.8 and beyond - Guillaume Laforge - Spri...Guillaume Laforge
 
Cloud foundry intro with groovy
Cloud foundry intro with groovyCloud foundry intro with groovy
Cloud foundry intro with groovyGuillaume Laforge
 
Groovy update - S2GForum London 2011 - Guillaume Laforge
Groovy update - S2GForum London 2011 - Guillaume LaforgeGroovy update - S2GForum London 2011 - Guillaume Laforge
Groovy update - S2GForum London 2011 - Guillaume LaforgeGuillaume Laforge
 
Groovy DSLs - S2GForum London 2011 - Guillaume Laforge
Groovy DSLs - S2GForum London 2011 - Guillaume LaforgeGroovy DSLs - S2GForum London 2011 - Guillaume Laforge
Groovy DSLs - S2GForum London 2011 - Guillaume LaforgeGuillaume Laforge
 
Gaelyk - Guillaume Laforge - GR8Conf Europe 2011
Gaelyk - Guillaume Laforge - GR8Conf Europe 2011Gaelyk - Guillaume Laforge - GR8Conf Europe 2011
Gaelyk - Guillaume Laforge - GR8Conf Europe 2011Guillaume Laforge
 
Groovy 1.8 update - Guillaume Laforge - GR8Conf Europe 2011
Groovy 1.8 update - Guillaume Laforge - GR8Conf Europe 2011Groovy 1.8 update - Guillaume Laforge - GR8Conf Europe 2011
Groovy 1.8 update - Guillaume Laforge - GR8Conf Europe 2011Guillaume Laforge
 

Plus de Guillaume Laforge (20)

Les nouveautés de Groovy 2 -- Mix-IT 2013
Les nouveautés de Groovy 2 -- Mix-IT 2013Les nouveautés de Groovy 2 -- Mix-IT 2013
Les nouveautés de Groovy 2 -- Mix-IT 2013
 
Groovy 2 and beyond
Groovy 2 and beyondGroovy 2 and beyond
Groovy 2 and beyond
 
Groovy 2.0 update at Devoxx 2012
Groovy 2.0 update at Devoxx 2012Groovy 2.0 update at Devoxx 2012
Groovy 2.0 update at Devoxx 2012
 
Groovy update at SpringOne2GX 2012
Groovy update at SpringOne2GX 2012Groovy update at SpringOne2GX 2012
Groovy update at SpringOne2GX 2012
 
JavaOne 2012 Groovy update
JavaOne 2012 Groovy updateJavaOne 2012 Groovy update
JavaOne 2012 Groovy update
 
Groovy 1.8 et 2.0 au BreizhC@mp 2012
Groovy 1.8 et 2.0 au BreizhC@mp 2012Groovy 1.8 et 2.0 au BreizhC@mp 2012
Groovy 1.8 et 2.0 au BreizhC@mp 2012
 
Groovy 1.8 and 2.0 at GR8Conf Europe 2012
Groovy 1.8 and 2.0 at GR8Conf Europe 2012Groovy 1.8 and 2.0 at GR8Conf Europe 2012
Groovy 1.8 and 2.0 at GR8Conf Europe 2012
 
Groovy 2.0 update - Cloud Foundry Open Tour Moscow - Guillaume Laforge
Groovy 2.0 update - Cloud Foundry Open Tour Moscow - Guillaume LaforgeGroovy 2.0 update - Cloud Foundry Open Tour Moscow - Guillaume Laforge
Groovy 2.0 update - Cloud Foundry Open Tour Moscow - Guillaume Laforge
 
Groovy 2.0 - Devoxx France 2012
Groovy 2.0 - Devoxx France 2012Groovy 2.0 - Devoxx France 2012
Groovy 2.0 - Devoxx France 2012
 
Whats new in Groovy 2.0?
Whats new in Groovy 2.0?Whats new in Groovy 2.0?
Whats new in Groovy 2.0?
 
Groovy Update, new in 1.8 and beyond - Guillaume Laforge - Devoxx 2011
Groovy Update, new in 1.8 and beyond - Guillaume Laforge - Devoxx 2011Groovy Update, new in 1.8 and beyond - Guillaume Laforge - Devoxx 2011
Groovy Update, new in 1.8 and beyond - Guillaume Laforge - Devoxx 2011
 
GPars et PrettyTime - Paris JUG 2011 - Guillaume Laforge
GPars et PrettyTime - Paris JUG 2011 - Guillaume LaforgeGPars et PrettyTime - Paris JUG 2011 - Guillaume Laforge
GPars et PrettyTime - Paris JUG 2011 - Guillaume Laforge
 
Groovy Update - Guillaume Laforge - Greach 2011
Groovy Update - Guillaume Laforge - Greach 2011Groovy Update - Guillaume Laforge - Greach 2011
Groovy Update - Guillaume Laforge - Greach 2011
 
Gaelyk update - Guillaume Laforge - SpringOne2GX 2011
Gaelyk update - Guillaume Laforge - SpringOne2GX 2011Gaelyk update - Guillaume Laforge - SpringOne2GX 2011
Gaelyk update - Guillaume Laforge - SpringOne2GX 2011
 
Groovy Update, what's new in Groovy 1.8 and beyond - Guillaume Laforge - Spri...
Groovy Update, what's new in Groovy 1.8 and beyond - Guillaume Laforge - Spri...Groovy Update, what's new in Groovy 1.8 and beyond - Guillaume Laforge - Spri...
Groovy Update, what's new in Groovy 1.8 and beyond - Guillaume Laforge - Spri...
 
Cloud foundry intro with groovy
Cloud foundry intro with groovyCloud foundry intro with groovy
Cloud foundry intro with groovy
 
Groovy update - S2GForum London 2011 - Guillaume Laforge
Groovy update - S2GForum London 2011 - Guillaume LaforgeGroovy update - S2GForum London 2011 - Guillaume Laforge
Groovy update - S2GForum London 2011 - Guillaume Laforge
 
Groovy DSLs - S2GForum London 2011 - Guillaume Laforge
Groovy DSLs - S2GForum London 2011 - Guillaume LaforgeGroovy DSLs - S2GForum London 2011 - Guillaume Laforge
Groovy DSLs - S2GForum London 2011 - Guillaume Laforge
 
Gaelyk - Guillaume Laforge - GR8Conf Europe 2011
Gaelyk - Guillaume Laforge - GR8Conf Europe 2011Gaelyk - Guillaume Laforge - GR8Conf Europe 2011
Gaelyk - Guillaume Laforge - GR8Conf Europe 2011
 
Groovy 1.8 update - Guillaume Laforge - GR8Conf Europe 2011
Groovy 1.8 update - Guillaume Laforge - GR8Conf Europe 2011Groovy 1.8 update - Guillaume Laforge - GR8Conf Europe 2011
Groovy 1.8 update - Guillaume Laforge - GR8Conf Europe 2011
 

Dernier

Potential of AI (Generative AI) in Business: Learnings and Insights
Potential of AI (Generative AI) in Business: Learnings and InsightsPotential of AI (Generative AI) in Business: Learnings and Insights
Potential of AI (Generative AI) in Business: Learnings and InsightsRavi Sanghani
 
Emixa Mendix Meetup 11 April 2024 about Mendix Native development
Emixa Mendix Meetup 11 April 2024 about Mendix Native developmentEmixa Mendix Meetup 11 April 2024 about Mendix Native development
Emixa Mendix Meetup 11 April 2024 about Mendix Native developmentPim van der Noll
 
Why device, WIFI, and ISP insights are crucial to supporting remote Microsoft...
Why device, WIFI, and ISP insights are crucial to supporting remote Microsoft...Why device, WIFI, and ISP insights are crucial to supporting remote Microsoft...
Why device, WIFI, and ISP insights are crucial to supporting remote Microsoft...panagenda
 
(How to Program) Paul Deitel, Harvey Deitel-Java How to Program, Early Object...
(How to Program) Paul Deitel, Harvey Deitel-Java How to Program, Early Object...(How to Program) Paul Deitel, Harvey Deitel-Java How to Program, Early Object...
(How to Program) Paul Deitel, Harvey Deitel-Java How to Program, Early Object...AliaaTarek5
 
The Future Roadmap for the Composable Data Stack - Wes McKinney - Data Counci...
The Future Roadmap for the Composable Data Stack - Wes McKinney - Data Counci...The Future Roadmap for the Composable Data Stack - Wes McKinney - Data Counci...
The Future Roadmap for the Composable Data Stack - Wes McKinney - Data Counci...Wes McKinney
 
Data governance with Unity Catalog Presentation
Data governance with Unity Catalog PresentationData governance with Unity Catalog Presentation
Data governance with Unity Catalog PresentationKnoldus Inc.
 
The Fit for Passkeys for Employee and Consumer Sign-ins: FIDO Paris Seminar.pptx
The Fit for Passkeys for Employee and Consumer Sign-ins: FIDO Paris Seminar.pptxThe Fit for Passkeys for Employee and Consumer Sign-ins: FIDO Paris Seminar.pptx
The Fit for Passkeys for Employee and Consumer Sign-ins: FIDO Paris Seminar.pptxLoriGlavin3
 
2024 April Patch Tuesday
2024 April Patch Tuesday2024 April Patch Tuesday
2024 April Patch TuesdayIvanti
 
Sample pptx for embedding into website for demo
Sample pptx for embedding into website for demoSample pptx for embedding into website for demo
Sample pptx for embedding into website for demoHarshalMandlekar2
 
Arizona Broadband Policy Past, Present, and Future Presentation 3/25/24
Arizona Broadband Policy Past, Present, and Future Presentation 3/25/24Arizona Broadband Policy Past, Present, and Future Presentation 3/25/24
Arizona Broadband Policy Past, Present, and Future Presentation 3/25/24Mark Goldstein
 
TeamStation AI System Report LATAM IT Salaries 2024
TeamStation AI System Report LATAM IT Salaries 2024TeamStation AI System Report LATAM IT Salaries 2024
TeamStation AI System Report LATAM IT Salaries 2024Lonnie McRorey
 
Modern Roaming for Notes and Nomad – Cheaper Faster Better Stronger
Modern Roaming for Notes and Nomad – Cheaper Faster Better StrongerModern Roaming for Notes and Nomad – Cheaper Faster Better Stronger
Modern Roaming for Notes and Nomad – Cheaper Faster Better Strongerpanagenda
 
Decarbonising Buildings: Making a net-zero built environment a reality
Decarbonising Buildings: Making a net-zero built environment a realityDecarbonising Buildings: Making a net-zero built environment a reality
Decarbonising Buildings: Making a net-zero built environment a realityIES VE
 
Connecting the Dots for Information Discovery.pdf
Connecting the Dots for Information Discovery.pdfConnecting the Dots for Information Discovery.pdf
Connecting the Dots for Information Discovery.pdfNeo4j
 
New from BookNet Canada for 2024: Loan Stars - Tech Forum 2024
New from BookNet Canada for 2024: Loan Stars - Tech Forum 2024New from BookNet Canada for 2024: Loan Stars - Tech Forum 2024
New from BookNet Canada for 2024: Loan Stars - Tech Forum 2024BookNet Canada
 
So einfach geht modernes Roaming fuer Notes und Nomad.pdf
So einfach geht modernes Roaming fuer Notes und Nomad.pdfSo einfach geht modernes Roaming fuer Notes und Nomad.pdf
So einfach geht modernes Roaming fuer Notes und Nomad.pdfpanagenda
 
Digital Identity is Under Attack: FIDO Paris Seminar.pptx
Digital Identity is Under Attack: FIDO Paris Seminar.pptxDigital Identity is Under Attack: FIDO Paris Seminar.pptx
Digital Identity is Under Attack: FIDO Paris Seminar.pptxLoriGlavin3
 
Enhancing User Experience - Exploring the Latest Features of Tallyman Axis Lo...
Enhancing User Experience - Exploring the Latest Features of Tallyman Axis Lo...Enhancing User Experience - Exploring the Latest Features of Tallyman Axis Lo...
Enhancing User Experience - Exploring the Latest Features of Tallyman Axis Lo...Scott Andery
 
Generative AI for Technical Writer or Information Developers
Generative AI for Technical Writer or Information DevelopersGenerative AI for Technical Writer or Information Developers
Generative AI for Technical Writer or Information DevelopersRaghuram Pandurangan
 
The State of Passkeys with FIDO Alliance.pptx
The State of Passkeys with FIDO Alliance.pptxThe State of Passkeys with FIDO Alliance.pptx
The State of Passkeys with FIDO Alliance.pptxLoriGlavin3
 

Dernier (20)

Potential of AI (Generative AI) in Business: Learnings and Insights
Potential of AI (Generative AI) in Business: Learnings and InsightsPotential of AI (Generative AI) in Business: Learnings and Insights
Potential of AI (Generative AI) in Business: Learnings and Insights
 
Emixa Mendix Meetup 11 April 2024 about Mendix Native development
Emixa Mendix Meetup 11 April 2024 about Mendix Native developmentEmixa Mendix Meetup 11 April 2024 about Mendix Native development
Emixa Mendix Meetup 11 April 2024 about Mendix Native development
 
Why device, WIFI, and ISP insights are crucial to supporting remote Microsoft...
Why device, WIFI, and ISP insights are crucial to supporting remote Microsoft...Why device, WIFI, and ISP insights are crucial to supporting remote Microsoft...
Why device, WIFI, and ISP insights are crucial to supporting remote Microsoft...
 
(How to Program) Paul Deitel, Harvey Deitel-Java How to Program, Early Object...
(How to Program) Paul Deitel, Harvey Deitel-Java How to Program, Early Object...(How to Program) Paul Deitel, Harvey Deitel-Java How to Program, Early Object...
(How to Program) Paul Deitel, Harvey Deitel-Java How to Program, Early Object...
 
The Future Roadmap for the Composable Data Stack - Wes McKinney - Data Counci...
The Future Roadmap for the Composable Data Stack - Wes McKinney - Data Counci...The Future Roadmap for the Composable Data Stack - Wes McKinney - Data Counci...
The Future Roadmap for the Composable Data Stack - Wes McKinney - Data Counci...
 
Data governance with Unity Catalog Presentation
Data governance with Unity Catalog PresentationData governance with Unity Catalog Presentation
Data governance with Unity Catalog Presentation
 
The Fit for Passkeys for Employee and Consumer Sign-ins: FIDO Paris Seminar.pptx
The Fit for Passkeys for Employee and Consumer Sign-ins: FIDO Paris Seminar.pptxThe Fit for Passkeys for Employee and Consumer Sign-ins: FIDO Paris Seminar.pptx
The Fit for Passkeys for Employee and Consumer Sign-ins: FIDO Paris Seminar.pptx
 
2024 April Patch Tuesday
2024 April Patch Tuesday2024 April Patch Tuesday
2024 April Patch Tuesday
 
Sample pptx for embedding into website for demo
Sample pptx for embedding into website for demoSample pptx for embedding into website for demo
Sample pptx for embedding into website for demo
 
Arizona Broadband Policy Past, Present, and Future Presentation 3/25/24
Arizona Broadband Policy Past, Present, and Future Presentation 3/25/24Arizona Broadband Policy Past, Present, and Future Presentation 3/25/24
Arizona Broadband Policy Past, Present, and Future Presentation 3/25/24
 
TeamStation AI System Report LATAM IT Salaries 2024
TeamStation AI System Report LATAM IT Salaries 2024TeamStation AI System Report LATAM IT Salaries 2024
TeamStation AI System Report LATAM IT Salaries 2024
 
Modern Roaming for Notes and Nomad – Cheaper Faster Better Stronger
Modern Roaming for Notes and Nomad – Cheaper Faster Better StrongerModern Roaming for Notes and Nomad – Cheaper Faster Better Stronger
Modern Roaming for Notes and Nomad – Cheaper Faster Better Stronger
 
Decarbonising Buildings: Making a net-zero built environment a reality
Decarbonising Buildings: Making a net-zero built environment a realityDecarbonising Buildings: Making a net-zero built environment a reality
Decarbonising Buildings: Making a net-zero built environment a reality
 
Connecting the Dots for Information Discovery.pdf
Connecting the Dots for Information Discovery.pdfConnecting the Dots for Information Discovery.pdf
Connecting the Dots for Information Discovery.pdf
 
New from BookNet Canada for 2024: Loan Stars - Tech Forum 2024
New from BookNet Canada for 2024: Loan Stars - Tech Forum 2024New from BookNet Canada for 2024: Loan Stars - Tech Forum 2024
New from BookNet Canada for 2024: Loan Stars - Tech Forum 2024
 
So einfach geht modernes Roaming fuer Notes und Nomad.pdf
So einfach geht modernes Roaming fuer Notes und Nomad.pdfSo einfach geht modernes Roaming fuer Notes und Nomad.pdf
So einfach geht modernes Roaming fuer Notes und Nomad.pdf
 
Digital Identity is Under Attack: FIDO Paris Seminar.pptx
Digital Identity is Under Attack: FIDO Paris Seminar.pptxDigital Identity is Under Attack: FIDO Paris Seminar.pptx
Digital Identity is Under Attack: FIDO Paris Seminar.pptx
 
Enhancing User Experience - Exploring the Latest Features of Tallyman Axis Lo...
Enhancing User Experience - Exploring the Latest Features of Tallyman Axis Lo...Enhancing User Experience - Exploring the Latest Features of Tallyman Axis Lo...
Enhancing User Experience - Exploring the Latest Features of Tallyman Axis Lo...
 
Generative AI for Technical Writer or Information Developers
Generative AI for Technical Writer or Information DevelopersGenerative AI for Technical Writer or Information Developers
Generative AI for Technical Writer or Information Developers
 
The State of Passkeys with FIDO Alliance.pptx
The State of Passkeys with FIDO Alliance.pptxThe State of Passkeys with FIDO Alliance.pptx
The State of Passkeys with FIDO Alliance.pptx
 

Groovy DSLs, from Beginner to Expert - Guillaume Laforge and Paul King - SpringOne2GX 2011

  • 1. Groovy.DSLs(from: beginner, to: expert) Paul King Core Groovy Committer ASERT Director Guillaume Laforge Groovy Project Manager SpringSource, a division of VMware © 2011 SpringOne 2GX 2011. All rights reserved. Do not distribute without permission.
  • 2. Groovy.DSLs(from: beginner, to: expert) Paul King 2 011 Core Groovy Committer E dition ASERT Director Guillaume Laforge Groovy Project Manager SpringSource, a division of VMware © 2011 SpringOne 2GX 2011. All rights reserved. Do not distribute without permission.
  • 3. Guillaume Laforge • Groovy Project Manager at VMware • Initiator of the Grails framework • Creator of the Gaelyk toolkit • Co-author of Groovy in Action • Speaking worldwide w/ a French accent • Follow me on... • My blog: http://glaforge.appspot.com • Twitter: @glaforge • Google+: http://gplus.to/glaforge 2 @glaforge — @paulk_asert
  • 4. Guillaume Laforge • Groovy Project Manager at VMware • Initiator of the Grails framework • Creator of the Gaelyk toolkit • Co-author of Groovy in Action • Speaking worldwide w/ a French accent • Follow me on... • My blog: http://glaforge.appspot.com • Twitter: @glaforge • Google+: http://gplus.to/glaforge 2 @glaforge — @paulk_asert
  • 5. Paul King • Core Groovy Committer • Co-author of Groovy in Action • Leads ASERT, a Brisbane, Australia, company providing software development, training & mentoring support • International speaker • Follow me on • Website: http://www.asert.com.au • Twitter: @paulk_asert 3 @glaforge — @paulk_asert
  • 6. Domain-Specific Languages • Wikipedia definition – A Domain-Specific Language is a programming language or executable specification language that offers, through appropriate notations and abstractions, expressive power focused on, and usually restricted to, a particular problem domain. • In contrast to General Purprose Languages • Somewhere between declarative data and GPLs • Also known as: fluent / human interfaces, language oriented programming, little or mini languages, macros, business 4 @glaforge — @paulk_asert
  • 7. Goals of Domain-Specific Languages • Use a more expressive language than a general-purpose one • Share a common metaphore of understanding between develoeprs and subject matter experts • Have domain experts help with the design of the business logic of an application • Avoid cluttering business code with too much boilerplate technical code thanks to a clean seperation 5 @glaforge — @paulk_asert
  • 8. Antimalaria drug Insurance policy risk HR skills resistance calculation engine representation simulation Nuclear safety simulations Market data feeds analysis Loan acceptance rules engine
  • 9. Technical examples Glade XSLT <?xml version="1.0"?> <?xml version="1.0"?> <GTK-Interface> <xsl:stylesheetversion="1.0" <widget> xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> <class>GtkWindow</class> <xsl:output method="xml"/> <name>HelloWindow</name> <xsl:template match="*"> <border_width>5</border_width> <xsl:element name="{name()}"> <Signal> <xsl:for-each select="@*"> Regex <xsl:element name="{name()}"> "x.z?z{1,3}y" <name>destroy</name> <handler>gtk_main_quit</handler> <xsl:value-of select="."/> </Signal> </xsl:element> <title>Hello</title> </xsl:for-each> <type>GTK_WINDOW_TOPLEVEL</type> <xsl:apply-templates select="*|text()"/> <position>GTK_WIN_POS_NONE</position> </xsl:element> <allow_shrink>True</allow_shrink> </xsl:template> <allow_grow>True</allow_grow> </xsl:stylesheet> <auto_shrink>False</auto_shrink> <widget> <class>GtkButton</class> fetchmail <name>Hello World</name> <can_focus>True</can_focus> # Poll this site first each cycle. <label>Hello World</label> poll pop.provider.net proto pop3 </widget> user "jsmith" with pass "secret1" is "smith" here </widget> user jones with pass "secret2" is "jjones" here with options </GTK-Interface> keep SQL # Poll this site second, unless Lord Voldemort zaps us first. poll billywig.hogwarts.com with proto imap: SELECT * FROM TABLE user harry_potter with pass "floo" is harry_potter here WHERE NAME LIKE '%SMI' # Poll this site third in the cycle. ORDER BY NAME # Password will be fetched from ~/.netrc poll mailhost.net with proto imap: user esr is esr here Troff cat thesis.ms | chem | tbl | refer | grap | pic | eqn | groff -Tps > thesis.ps 8 Source: Applying minilanguages: http://www.faqs.org/docs/artu/ch08s02.html @glaforge — @paulk_asert
  • 10. Pros and cons of DSLs • Pros – Safety; as long as the – Domain experts can help, language constructs are safe, validate, modify, and often any DSL sentence can be develop DSL programs considered safe – Somewhat self-documenting – Enhance quality, • Cons productivity, reliability, – Learning cost vs. limited maintainability, portability, applicability reusability – Cost of designing, implementing & maintaining DSLs as well as tools/IDEs – Attaining proper scope 8 @glaforge — @paulk_asert
  • 11. • The flexible nature of the language – method call and syntax conventions – native syntax constructs – adding properties to numbers – command chain expressions – operator overloading – hierarchical structures with builders – AST transformations 9 © 2011 SpringOne 2GX 2011. All rights reserved. Do not distribute without permission.
  • 12. Scripts vs classes • Hide all the boilerplate technical code – an end-user doesn’t need to know about classes 10 @glaforge — @paulk_asert
  • 13. Scripts vs classes • Hide all the boilerplate technical code – an end-user doesn’t need to know about classes public
class
Rule
{ 



public
static
void
main(String[]
args)
{ 







System.out.println("Hello"); 



} } 10 @glaforge — @paulk_asert
  • 14. Scripts vs classes • Hide all the boilerplate technical code – an end-user doesn’t need to know about classes public
class
Rule
{ 



public
static
void
main(String[]
args)
{ 







System.out.println("Hello"); 



} } println
"Hello" 10 @glaforge — @paulk_asert
  • 15. Optional typing • No need to bother with types or even generics – unless you want to! – but strongly typed if you so desire 11 @glaforge — @paulk_asert
  • 16. Optional typing • No need to bother with types or even generics – unless you want to! – but strongly typed if you so desire //
given
this
API
method public
Rate<LoanType,
Duration,
BigDecimal>[]
lookupTable()
{
...
} 11 @glaforge — @paulk_asert
  • 17. Optional typing • No need to bother with types or even generics – unless you want to! – but strongly typed if you so desire //
given
this
API
method public
Rate<LoanType,
Duration,
BigDecimal>[]
lookupTable()
{
...
} //
verbose
Java
notation Rate<LoanType,
Duration,
BigDecimal>[]
table
=
lookupTable(); 11 @glaforge — @paulk_asert
  • 18. Optional typing • No need to bother with types or even generics – unless you want to! – but strongly typed if you so desire //
given
this
API
method public
Rate<LoanType,
Duration,
BigDecimal>[]
lookupTable()
{
...
} //
verbose
Java
notation Rate<LoanType,
Duration,
BigDecimal>[]
table
=
lookupTable(); //
leaner
Groovy
variant def
table
=
lookupTable() 11 @glaforge — @paulk_asert
  • 19. Native syntax constructs 12 @glaforge — @paulk_asert
  • 20. Native syntax constructs //
Lists 12 @glaforge — @paulk_asert
  • 21. Native syntax constructs //
Lists def
days
=
[Monday,
Tuesday,
Wednesday] 12 @glaforge — @paulk_asert
  • 22. Native syntax constructs //
Lists def
days
=
[Monday,
Tuesday,
Wednesday] //
Maps 12 @glaforge — @paulk_asert
  • 23. Native syntax constructs //
Lists def
days
=
[Monday,
Tuesday,
Wednesday] //
Maps def
states
=
[CA:
'California',
TX:
'Texas'] 12 @glaforge — @paulk_asert
  • 24. Native syntax constructs //
Lists def
days
=
[Monday,
Tuesday,
Wednesday] //
Maps def
states
=
[CA:
'California',
TX:
'Texas'] //
Ranges
(you
can
create
your
own) 12 @glaforge — @paulk_asert
  • 25. Native syntax constructs //
Lists def
days
=
[Monday,
Tuesday,
Wednesday] //
Maps def
states
=
[CA:
'California',
TX:
'Texas'] //
Ranges
(you
can
create
your
own) def
bizDays
=
Monday..Friday 12 @glaforge — @paulk_asert
  • 26. Native syntax constructs //
Lists def
days
=
[Monday,
Tuesday,
Wednesday] //
Maps def
states
=
[CA:
'California',
TX:
'Texas'] //
Ranges
(you
can
create
your
own) def
bizDays
=
Monday..Friday def
allowedAge
=
18..65 12 @glaforge — @paulk_asert
  • 27. Optional parens & semis • Make statements and expressions look more like natural languages 13 @glaforge — @paulk_asert
  • 28. Optional parens & semis • Make statements and expressions look more like natural languages move(left); 13 @glaforge — @paulk_asert
  • 29. Optional parens & semis • Make statements and expressions look more like natural languages move(left); move
left 13 @glaforge — @paulk_asert
  • 30. Adding properties to numbers • Several approaches to adding properties to numbers – through a category class
PillsCategory
{ 



static
getPills(Number
n)
{
n
} } use(PillsCategory)
{ 



2.pills









//
2.getPills() } – through ExpandoMetaClass Number.metaClass.getPills
{
‐>
delegate
} 2.pills
















//
2.getPills() 14 @glaforge — @paulk_asert
  • 31. Named arguments • In Groovy you can mix named and unnamed arguments for method parameters – named params are actually put in a map parameter – plus optional parens & semis 

take
2.pills,
 


of:
chloroquinine,
 after:
6.hours //
Calls
a
method
signature
like: def
take(Map
m,
MedicineQuantity
mq) 15 @glaforge — @paulk_asert
  • 32. Command chain expressions 16 @glaforge — @paulk_asert
  • 33. Command chain expressions • A grammar improvement allowing you to drop dots & parens when chaining method calls – an extended version of top-level statements like println 16 @glaforge — @paulk_asert
  • 34. Command chain expressions • A grammar improvement allowing you to drop dots & parens when chaining method calls – an extended version of top-level statements like println 16 @glaforge — @paulk_asert
  • 35. Command chain expressions • A grammar improvement allowing you to drop dots & parens when chaining method calls – an extended version of top-level statements like println • Less dots, less parens allow you to – write more readable business rules – in almost plain English sentences • (or any language, of course) 16 @glaforge — @paulk_asert
  • 36. Command chain expressions • A grammar improvement allowing you to drop dots & parens when chaining method calls – an extended version of top-level statements like println • Less dots, less parens allow you to – write more readable business rules – in almost plain English sentences • (or any language, of course) 16 @glaforge — @paulk_asert
  • 37. Command chain expressions • A grammar improvement allowing you to drop dots & parens when chaining method calls – an extended version of top-level statements like println • Less dots, less parens allow you to – write more readable business rules – in almost plain English sentences • (or any language, of course) • Let’s have a look at some examples 16 @glaforge — @paulk_asert
  • 38. Command chain expressions 
turn
left

then
right
 17 @glaforge — @paulk_asert
  • 39. Command chain expressions Alternation of method names 
turn
left

then
right
 17 @glaforge — @paulk_asert
  • 40. Command chain expressions Alternation of method names 
turn
left

then
right
 and parameters (even named ones) 17 @glaforge — @paulk_asert
  • 41. Command chain expressions 
turn
left

then
right
 17 @glaforge — @paulk_asert
  • 42. Command chain expressions Equivalent to: 




(



).



(




) 
turn
left

then
right
 17 @glaforge — @paulk_asert
  • 43. LookM a! N op are ns, no do ts!
  • 44. Command chain expressions Before... we used to do... take
2.pills,
of:
chloroquinine,
after:
6.hours 19 @glaforge — @paulk_asert
  • 45. Command chain expressions Before... we used to do... take
2.pills,
of:
chloroquinine,
after:
6.hours Normal argument 19 @glaforge — @paulk_asert
  • 46. Command chain expressions Before... we used to do... take
2.pills,
of:
chloroquinine,
after:
6.hours Normal argument Named arguments 19 @glaforge — @paulk_asert
  • 47. Command chain expressions Before... we used to do... take
2.pills,
of:
chloroquinine,
after:
6.hours Normal argument Named arguments Woud call: def
take(Map
m,
Quantity
q) 19 @glaforge — @paulk_asert
  • 48. Command chain expressions Now, even less punctuation! take
2.pills

of

chloroquinine

after

6.hours 20 @glaforge — @paulk_asert
  • 49. Command chain expressions Now, even less punctuation! 



(






).


(












).





(






) take
2.pills

of

chloroquinine

after

6.hours 20 @glaforge — @paulk_asert
  • 50. Command chain expressions //
environment
initialization Integer.metaClass.getPills
{
‐>
delegate
} Integer.metaClass.getHours
{
‐>
delegate
} 
 //
variable
injection def
chloroquinine
=
/*...*/ 
 { } //
implementing
the
DSL
logic def
take(n)
{ 



[of:
{
drug
‐> 







[after:
{
time
‐>
/*...*/
}] 



}] } //
‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐ take
2.pills
of
chloroquinine
after
6.hours 21 @glaforge — @paulk_asert
  • 51. Command chain expressions take
2.pills

of

chloroquinine

after

6.hours 22 @glaforge — @paulk_asert
  • 52. Command chain expressions take
2.pills

of

chloroquinine

after

6.hours ... some dots remain ... 22 @glaforge — @paulk_asert
  • 53. Command chain expressions Yes, we can... get rid of them :-) take
2

pills
of

chloroquinine

after

6
hours 23 @glaforge — @paulk_asert
  • 54. Command chain expressions Yes, we can... get rid of them :-) 



(
).




(

).













(




).
(




) take
2

pills
of

chloroquinine

after

6
hours 23 @glaforge — @paulk_asert
  • 55. Command chain expressions //
variable
injection def
(of,
after,
hours)
=
/*...*/ 
 //
implementing
the
DSL
logic { } def
take(n)
{ 



[pills:
{
of
‐> 







[chloroquinine:
{
after
‐> 











['6':
{
time
‐>
}] 







}] 



}] } //
‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐ take
2
pills
of
chloroquinine
after
6
hours 24 @glaforge — @paulk_asert
  • 56. Command chain expressions @glaforge — @paulk_asert 25
  • 57. Command chain expressions //
methods
with
multiple
arguments
(commas) @glaforge — @paulk_asert 25
  • 58. Command chain expressions //
methods
with
multiple
arguments
(commas) take
coffee

with
sugar,
milk

and
liquor @glaforge — @paulk_asert 25
  • 59. Command chain expressions //
methods
with
multiple
arguments
(commas) take
coffee

with
sugar,
milk

and
liquor //
leverage
named‐args
as
punctuation @glaforge — @paulk_asert 25
  • 60. Command chain expressions //
methods
with
multiple
arguments
(commas) take
coffee

with
sugar,
milk

and
liquor //
leverage
named‐args
as
punctuation check
that:
margarita

tastes
good @glaforge — @paulk_asert 25
  • 61. Command chain expressions //
methods
with
multiple
arguments
(commas) take
coffee

with
sugar,
milk

and
liquor //
leverage
named‐args
as
punctuation check
that:
margarita

tastes
good //
closure
parameters
for
new
control
structures @glaforge — @paulk_asert 25
  • 62. Command chain expressions //
methods
with
multiple
arguments
(commas) take
coffee

with
sugar,
milk

and
liquor //
leverage
named‐args
as
punctuation check
that:
margarita

tastes
good //
closure
parameters
for
new
control
structures given
{}

when
{}

then
{} @glaforge — @paulk_asert 25
  • 63. Command chain expressions //
methods
with
multiple
arguments
(commas) take
coffee

with
sugar,
milk

and
liquor //
leverage
named‐args
as
punctuation check
that:
margarita

tastes
good //
closure
parameters
for
new
control
structures given
{}

when
{}

then
{} //
zero‐arg
methods
require
parens @glaforge — @paulk_asert 25
  • 64. Command chain expressions //
methods
with
multiple
arguments
(commas) take
coffee

with
sugar,
milk

and
liquor //
leverage
named‐args
as
punctuation check
that:
margarita

tastes
good //
closure
parameters
for
new
control
structures given
{}

when
{}

then
{} //
zero‐arg
methods
require
parens select
all

unique()
from
names @glaforge — @paulk_asert 25
  • 65. Command chain expressions //
methods
with
multiple
arguments
(commas) take
coffee

with
sugar,
milk

and
liquor //
leverage
named‐args
as
punctuation check
that:
margarita

tastes
good //
closure
parameters
for
new
control
structures given
{}

when
{}

then
{} //
zero‐arg
methods
require
parens select
all

unique()
from
names //
possible
with
an
odd
number
of
terms @glaforge — @paulk_asert 25
  • 66. Command chain expressions //
methods
with
multiple
arguments
(commas) take
coffee

with
sugar,
milk

and
liquor //
leverage
named‐args
as
punctuation check
that:
margarita

tastes
good //
closure
parameters
for
new
control
structures given
{}

when
{}

then
{} //
zero‐arg
methods
require
parens select
all

unique()
from
names //
possible
with
an
odd
number
of
terms take
3

cookies @glaforge — @paulk_asert 25
  • 67. Command chain expressions //
methods
with
multiple
arguments
(commas) take
coffee

with
sugar,
milk

and
liquor 



(





).



(










).


(





) //
leverage
named‐args
as
punctuation check
that:
margarita

tastes
good //
closure
parameters
for
new
control
structures given
{}

when
{}

then
{} //
zero‐arg
methods
require
parens select
all

unique()
from
names //
possible
with
an
odd
number
of
terms take
3

cookies @glaforge — @paulk_asert 25
  • 68. Command chain expressions //
methods
with
multiple
arguments
(commas) take
coffee

with
sugar,
milk

and
liquor 



(





).



(










).


(





) //
leverage
named‐args
as
punctuation check
that:
margarita

tastes
good 




(














).





(



) //
closure
parameters
for
new
control
structures given
{}

when
{}

then
{} //
zero‐arg
methods
require
parens select
all

unique()
from
names //
possible
with
an
odd
number
of
terms take
3

cookies @glaforge — @paulk_asert 25
  • 69. Command chain expressions //
methods
with
multiple
arguments
(commas) take
coffee

with
sugar,
milk

and
liquor 



(





).



(










).


(





) //
leverage
named‐args
as
punctuation check
that:
margarita

tastes
good 




(














).





(



) //
closure
parameters
for
new
control
structures given
{}

when
{}

then
{} 




(

).



(

).



(

) //
zero‐arg
methods
require
parens select
all

unique()
from
names //
possible
with
an
odd
number
of
terms take
3

cookies @glaforge — @paulk_asert 25
  • 70. Command chain expressions //
methods
with
multiple
arguments
(commas) take
coffee

with
sugar,
milk

and
liquor 



(





).



(










).


(





) //
leverage
named‐args
as
punctuation check
that:
margarita

tastes
good 




(














).





(



) //
closure
parameters
for
new
control
structures given
{}

when
{}

then
{} 




(

).



(

).



(

) //
zero‐arg
methods
require
parens select
all

unique()
from
names 





(


).







.



(




) //
possible
with
an
odd
number
of
terms take
3

cookies @glaforge — @paulk_asert 25
  • 71. Command chain expressions //
methods
with
multiple
arguments
(commas) take
coffee

with
sugar,
milk

and
liquor 



(





).



(










).


(





) //
leverage
named‐args
as
punctuation check
that:
margarita

tastes
good 




(














).





(



) //
closure
parameters
for
new
control
structures given
{}

when
{}

then
{} 




(

).



(

).



(

) //
zero‐arg
methods
require
parens select
all

unique()
from
names 





(


).







.



(




) //
possible
with
an
odd
number
of
terms take
3

cookies 



(
). @glaforge — @paulk_asert 25
  • 72. Operator overloading a
+
b

//
a.plus(b) a
‐
b

//
a.minus(b) • Currency amounts a
*
b

//
a.multiply(b) – 15.euros + 10.dollars a
/
b

//
a.divide(b) a
%
b

//
a.modulo(b) • Distance handling a
**
b
//
a.power(b) a
|
b

//
a.or(b) – 10.kilometers - 10.meters a
&
b

//
a.and(b) a
^
b

//
a.xor(b) • Workflow, concurrency a[b]


//
a.getAt(b) – taskA | taskB & taskC a
<<
b
//
a.leftShift(b) a
>>
b
//
a.rightShift(b) +a




//
a.unaryPlus() • Credit an account ‐a




//
a.unaryMinus() – account << 10.dollars ~a




//
a.bitwiseNegate() account += 10.dollars 26 @glaforge — @paulk_asert
  • 73. Builders • For hierachical structures, extend: – BuilderSupport – FactoryBuilderSupport • nice for extending builders with new nodes and attributes import
groovy.xml.* 
 • Roll your own.. def
builder
=
new
MarkupBuilder() builder.html
{ – with invokeMethod() 



head
{
title
"Chicago!"
} or methodMissing() 



body
{ 







div(id:
"main")
{ – with get/setProperty() 











p
"Groovy
rocks!" 







} or propertyMissing() 



} } 27 @glaforge — @paulk_asert
  • 74. AST Transformations • Hook into the compiler process to do compile-time metaprogramming by modifying the Abstract Syntax Tree Transformation 28 @glaforge — @paulk_asert
  • 75. @InheritConstructors • Classes like Exception are painful when extended, as all the base constructors should be replicated class
CustomException
extends
Exception
{ 



CustomException()























{
super()






} 



CustomException(String
msg)













{
super(msg)



} 



CustomException(String
msg,
Throwable
t)
{
super(msg,
t)
} 



CustomException(Throwable
t)












{
super(t)





} } 29 @glaforge — http://glaforge.appspot.com — http://gplus.to/glaforge
  • 76. @InheritConstructors • Classes like Exception are painful when extended, as all the base constructors should be replicated import
groovy.transform.* @InheritConstructors class
CustomException
extends
Exception
{ 



CustomException()























{
super()






} 



CustomException(String
msg)













{
super(msg)



} 



CustomException(String
msg,
Throwable
t)
{
super(msg,
t)
} 



CustomException(Throwable
t)












{
super(t)





} } 29 @glaforge — http://glaforge.appspot.com — http://gplus.to/glaforge
  • 77. @InheritConstructor... under the covers //
... ClassNode
sNode
=
cNode.getSuperClass();










 for
(ConstructorNode
cn
:
sNode.getDeclaredConstructors())
{ 



Parameter[]
params
=
cn.getParameters(); 



if
(cn.isPrivate())
continue; 



Parameter[]
newParams
=
new
Parameter[params.length]; 



List<Expression>
args
=
copyParamsToArgs(params,
newParams); 



if
(isClashing(cNode,
newParams))
continue; 



BlockStatement
body
=
new
BlockStatement(); 



ConstructorCallExpression
callArgs
= 







new
ConstructorCallExpression(ClassNode.SUPER,
 





































new
ArgumentListExpression(args)); 



body.addStatement(new
ExpressionStatement(callArgs)); 



cNode.addConstructor(cn.getModifiers(),
newParams,
 
























cn.getExceptions(),
body); } //
... 30 @glaforge — http://glaforge.appspot.com — http://gplus.to/glaforge
  • 78. Advanced example with Spock def
"length
of
Spock's
&
his
friends'
names"()
{ 



expect: 







name.size()
==
length 



where: 







name




|
length 







"Spock"

|
5 







"Kirk"


|
4 







"Scotty"
|
6 } 31 @glaforge — http://glaforge.appspot.com — http://gplus.to/glaforge
  • 79. • A week in the life of DSL inc. – find the examples on GitHub: https://github.com/paulk-asert/ DSLsFromBeginnerToExpert 32 © 2011 SpringOne 2GX 2011. All rights reserved. Do not distribute without permission.
  • 80. MONDAY 33 @glaforge — @paulk_asert
  • 81. Currency DSL… From:



customer@acme.org To:





Guillaume Subject:
Project
Request Dear
Guillaume, We
would
like
a
DSL
for
capturing
currency
 expressions.
Just
US
currency
to
start
with. Thanks,
Happy
Customer 34 @glaforge — @paulk_asert
  • 82. …Currency DSL... enum
Coin
{ 



penny(1),
nickel(5),
dime(10),
quarter(25) 



Coin(int
value)
{
this.value
=
value
} 



int
value } import
static
Coin.* 
 assert
2
*
quarter.value
+ 






1
*
nickel.value
+ 






2
*
penny.value
==
57 35 @glaforge — @paulk_asert
  • 83. …Currency DSL… From:



customer@acme.org To:





Guillaume Subject:
Project
Request Dear
Guillaume, That
works
fairly
well
but
can
we
get
rid
 of
the
‘.value’
part
of
those
expressions.
 They
are
confusing
our
users. Thanks,
Happy
Customer 36 @glaforge — @paulk_asert
  • 85. ...Currency DSL... //
Category class
CoinMath
{ 



static
multiply(Integer
self,
Coin
c)
{ 







self
*
c.value 



} } 
 use
(CoinMath)
{ 



assert
2
*
quarter
+ 










1
*
nickel
+ //
EMC
equivalent 










2
*
penny
==
57 Integer.metaClass.multiply
=
{ } 



Coin
c
‐>
delegate
*
c.value } assert
2
*
quarter
+
 






1
*
nickel
+
 






2
*
penny
==
57 37 @glaforge — @paulk_asert
  • 86. …Currency DSL… From:



customer@acme.org To:





Guillaume Subject:
Project
Request Dear
Guillaume, Much
better
but
our
users
are
sometimes
 using
plural
as
well
as
singular
 expressions.
Is
there
anything
that
you
 can
do
about
that? Thanks,
Happy
Customer 38 @glaforge — @paulk_asert
  • 88. ... Currency DSL class
CoinValues
{ 



static
get(Integer
self,
String
name)
{ 







self
*
Coin."${singular(name)}".value 



} 



static
singular(String
val)
{ 







val.endsWith('ies')
?
val[0..‐4]
+
'y'
:
val.endsWith('s')
?
val[0..‐2]
:
val 



} } 
 use
(CoinValues)
{ 



assert
2.quarters
+
1.nickel
+
2.pennies
==
57 } //
EMC
equivalent Integer.metaClass.getProperty
=
{
String
name
‐> 



def
mp
=
Integer.metaClass.getMetaProperty(name) 



if
(mp)
return
mp.getProperty(delegate) 



def
singular
=
name.endsWith('ies')
?
name[0..‐4]
+
'y'
: 


















name.endsWith('s')
?
name[0..‐2]
:
name 



delegate
*
Coin."$singular".value } 
 assert
2.quarters
+
1.nickel
+
2.pennies
==
57 39 @glaforge — @paulk_asert
  • 89. TUESDAY http://www.google.com/imgres?imgurl=http://i733.photobucket.com/albums/ww336/snake1953/Day_of_Week/Tue/HappyTuesda 40 @glaforge — @paulk_asert %3D1%26hl%3Den%26client%3Dfirefox-a%26sa%3DN%26rls%3Dorg.mozilla:en-US:official%26biw%3D1488%26bih%3D852%
  • 90. Business logic DSL From:



customer@acme.org To:





Paul
King Subject:
Project
Request Dear
Paul, Could
you
please
create
us
a
small
DSL
for
capturing our
business
rules.
We
are
thinking
of
something
like: 



price
=
3 



quantity
=
10 



total
=
price
*
quantity Perhaps
also
an
ability
to
check
values: 



assert
total
==
30 Thanks,
Happy
Customer P.S.
Will
send
a
couple
of
more
details
in
a
follow‐up
email
but please
consider
the
requirements
as
being
pretty
much
locked
down. 41 @glaforge — @paulk_asert
  • 91. Business logic DSL • Options – Embedded Groovy – Regex parser – Antlr parser – Split / StringTokenizer – Parser combinators 42 @glaforge — @paulk_asert
  • 92. Business logic DSL From:



customer@acme.org To:





Paul
King Subject:
Project
Request Date:



Mid
morning Dear
Paul, We
were
thinking
a
bit
more
about
it,
and
it
would
be
good
to
have just
a
few
more
features.
Hopefully,
they
won’t
have
much
impact on
your
existing
design
and
can
be
added
very
quickly.
It
isn’t much,
just
the
ability
to
have
IF,
THEN,
ELSE
like
structures,
oh yeah
and
the
ability
to
have
loops,
and
store
stuff
in
files
and get
stuff
from
databases
and
web
services
if
we
need. Thanks,
Happy
Customer P.S.
It
would
be
great
if
you
can
be
finished
by
this
afternoon. We
have
a
customer
who
would
like
this
feature
RSN.
Thanks. 43 @glaforge — @paulk_asert
  • 93. Business logic DSL def
shell
=
new
GroovyShell() shell.evaluate(''' 



price
=
3 



quantity
=
10 



total
=
price
*
quantity 



assert
total
==
30 ''') 44 @glaforge — @paulk_asert
  • 94. Business logic DSL From:



customer@acme.org To:





Paul
King Subject:
Project
Request Date:



This
afternoon Dear
Paul, We
were
really
happy
with
the
DSL
engine
you
provided
us
 with
but
people
started
importing
all
sorts
of
classes.
Can
 you
stop
them
from
doing
that?
Maybe
just
java.lang.Math
 only.
Also
we
decided
we
don’t
like
“while”
loops
anymore.
 Leave
you
to
it. Thanks,
Happy
Customer P.S.
Have
a
beer
and
crab
dinner
for
me
at
the
conference.
 Bye. 45 @glaforge — @paulk_asert
  • 95. Business logic DSL class
CustomerShell
{ 



Object
evaluate(String
text)
{ 







try
{ 











def
loader
=
new
CustomerClassLoader() 











def
clazz
=
loader.parseClass(text) 











def
script
=
clazz.newInstance() 











return
script.run() 







}
catch
(...)
{
...
} 



} } 
 class
CustomerClassLoader
extends
GroovyClassLoader
{ 



def
createCompilationUnit(CompilerConfiguration
config,
CodeSource
codeSource)
{ 







CompilationUnit
cu
=
super.createCompilationUnit(config,
codeSource) 







cu.addPhaseOperation(new
CustomerFilteringNodeOperation(),
Phases.SEMANTIC_ANALYSIS) 







return
cu 



} } 
 private
class
CustomerFilteringNodeOperation
extends
PrimaryClassNodeOperation
{ 



//
... 



private
static
final
allowedStaticImports
=
[Math].asImmutable() 



void
visitStaticMethodCallExpression(StaticMethodCallExpression
smce)
{ 







if
(!allowedStaticImports.contains(smce.ownerType.getTypeClass()))
{ 











throw
new
SecurityException("Static
method
call
expressions
forbidden
in
acme
shell.") 







} 



} 



void
visitWhileLoop(WhileStatement
whileStatement)
{ 







throw
new
SecurityException("While
statements
forbidden
in
acme
shell.") 


} 


//
... } 46 @glaforge — @paulk_asert Please also see the ArithmeticShell which is included under examples in the Groovy distribution
  • 96. Business logic DSL def
shell
=
new
CustomerShell() shell.evaluate(''' 



price
=
3 



quantity
=
10 



total
=
price
*
quantity 



assert
total
==
30 ''') 47 @glaforge — @paulk_asert
  • 97. WEDNESDAY 48 @glaforge — @paulk_asert http://www.crystalscomments.com/1/days/wednesday/animals/006.jpg
  • 98. Stock Exchange Order DSL From:



customer@finance‐broker.org To:





Guillaume Subject:
Project
Request Date:



Wednesday
morning Dear
Guillaume, For
our
order
processing
system
we
have
a
need
to
capture
 client
orders
using
a
DSL.
An
order
includes
the
name
of
 the
security
to
be
transacted
(buy
or
sell)
as
well
as
 quantity
and
unit
price
details
to
specify
any
constraint
 that
the
counterparty
would
like
to
impose
on
the
price
 of
transaction. Thanks,
Happy
Customer Example inspired from «DSLs in Action»: http://www.manning.com/ghosh/ 49 @glaforge — @paulk_asert
  • 99. //
‐‐‐‐‐
Implementation
of
the
Fluent
API
‐‐‐‐‐ … Stock Exchange Order DSL… enum
Action
{
Buy,
Sell
} 
 println
new
Order() class
Order
{ 



.sell(150,
"IBM") 



def
security 



def
quantity,
limitPrice 



.limitPrice(300) 



boolean
allOrNone 



def
valueCalculation 



.allOrNone(true) 



Action
action 



.valueAs
{
qty,
unitPrice
‐> 
 



def
buy(Integer
quantity,
String
security)
{ 








qty
*
unitPrice
‐
100
} 







this.quantity
=
quantity 
 







this.security
=
security 







this.action
=
Action.Buy println
new
Order() 







return
this 



.buy(200,
"GOOG") 



} 



def
sell(Integer
quantity,
String
security)
{ 



.limitPrice(200) 







this.quantity
=
quantity 



.allOrNone(true) 







this.security
=
security 







this.action
=
Action.Sell 



.valueAs{
qty,
unitPrice
‐> 







return
this 







qty
*
unitPrice
‐
500
} 



} 



def
limitPrice(Integer
limit)
{ 







this.limitPrice
=
limit;
return
this 



} Sell 150 shares of IBM at valuation of 44900 



def
allOrNone(boolean
allOrNone)
{ 







this.allOrNone
=
allOrNone;
return
this Buy 200 shares of GOOG at valuation of 39500 



} 



def
valueAs(Closure
valueCalculation)
{ 







this.valueCalculation
=
valueCalculation;
return
this 



} 



String
toString()
{ 







"$action
$quantity
shares
of
$security
at
valuation
of
${valueCalculation(quantity,
limitPrice)}" 



} } 50 @glaforge — @paulk_asert
  • 100. Stock Exchange Order DSL From:



customer@finance‐broker.org To:





Guillaume Subject:
Project
Request Date:



Wednesday
morning Dear
Guillaume, That
version
was
great
but
can
you
make
the DSL
a
little
more
fluent.
The
brokers
aren’t programmers
after
all! Thanks,
Happy
Customer 51 @glaforge — @paulk_asert
  • 101. Stock Exchange Order DSL class
Order
{ 



def
security,
quantity,
limitPrice,
allOrNone,
value,
bs 
 



def
buy(securityQuantity,
closure)
{ 







bs
=
'Bought' 







buySell(securityQuantity,
closure) 



} 



 



def
sell(securityQuantity,
closure)
{ 







bs
=
'Sold' 







buySell(securityQuantity,
closure) 



} 
 



private
buySell(securityQuantity,
closure)
{ 







//
multiple
assignment 







(security,
quantity)
=
[securityQuantity.security,
securityQuantity.quantity] 







//
better
clone
the
closure
to
avoid
multi‐threading
access
issues 







def
c
=
closure.clone() 







//
delegate
the
method
calls
inside
the
closure
to
our
methodMissing 







c.delegationStrategy
=
Closure.DELEGATE_ONLY 







c.delegate
=
this 







def
valuation
=
c() 







println
"$bs
$quantity
$security.name
at
valuation
of
$valuation" 



} 
 



//
methods
inside
the
closure
will
assign
the
Order
properties 



def
methodMissing(String
name,
args)
{
this."$name"
=
args[0]
} 52 @glaforge — @paulk_asert
  • 102. Stock Exchange Order DSL 



def
getTo()
{
this
} newOrder.to.buy(100.shares.of(IBM))
{ 

 



limitPrice

300 



def
valueAs(closure)
{ 



allOrNone


true 







value
=
closure(quantity,
limitPrice) 



valueAs




{
qty,
unitPrice
‐> 



} 







qty
*
unitPrice
‐
200
} } 
 } class
Security
{ 
 



String
name newOrder.to.sell
200.shares.of(GOOG),
{ } 



limitPrice

200 
 class
Quantity
{ 



allOrNone


false 



Security
security 



valueAs




{
qty,
unitPrice
‐> 



Integer
quantity 







qty
*
unitPrice
‐
500
} } } 
 Integer.metaClass.getShares
=
{
‐>
delegate
} Integer.metaClass.of
=
{
new
Quantity(security:
it,
quantity:
delegate)
} 
 class
CustomBinding
extends
Binding
{ 



def
getVariable(String
symbol)
{ 







//
create
a
new
order
each
time 







//
for
when
you
pass
several
orders 







if
(symbol
==
"newOrder")
new
Order() 







//
otherwise,
it's
an
instrument 







//
trick
to
avoid
using
strings:
use
IBM
instead
of
'IBM' 







else
new
Security(name:
symbol)
 



} } 
 //
use
the
script
binding
for
retrieving
IBM,
etc. binding
=
new
CustomBinding() 53 @glaforge — @paulk_asert
  • 103. Stock Exchange Order DSL From:



customer@finance‐broker.org To:





Guillaume Subject:
Project
Request Date:



Wednesday
lunch
time Dear
Guillaume, That
version
was
great
but
can
we
simplify
the constraint
to
remove
the
parameters,
i.e.:
change 



{
qty,
unitPrice
‐>
qty
*
unitPrice
‐
200
} to
this: 



{
qty
*
unitPrice
‐
200
} Thanks,
Happy
Customer 54 @glaforge — @paulk_asert
  • 104. Stock Exchange Order DSL newOrder.to.buy(100.shares.of(IBM))
{ class
Order
{ 



limitPrice

300 ... 



allOrNone


true 



def
allOrNone,
valueCalculation,
bs 



valueAs




{
qty
*
unitPrice
‐
200
} ... 



private
buySell(securityQuantity,
closure)
{ } ... 
 







valueCalculation
=
c() newOrder.to.sell
200.shares.of(GOOG),
{ 







//
debug
print
resulting
order 



limitPrice

200 







println
toString() 



allOrNone


false 







return
this 



valueAs




{
qty
*
unitPrice
‐
500
} 



} } ... 



def
valueAs(Closure
wrapee)
{ 



//
in
order
to
be
able
to
define
closures
like
{
qty
*
unitPrice
}
without
having 



//
to
explicitly
pass
the
parameters
to
the
closure
we
can
wrap
the
closure
inside 



//
another
one
and
that
closure
sets
a
delegate
to
the
qty
and
unitPrice
variables 







def
wrapped
=
{
qty,
unitPrice
‐> 











def
cloned
=
wrapee.clone() 











cloned.resolveStrategy
=
Closure.DELEGATE_ONLY 











cloned.delegate
=
[qty:
qty,
unitPrice:
unitPrice] 











cloned() 







} 







return
wrapped 



} ... } 55 @glaforge — @paulk_asert
  • 105. Stock Exchange Order DSL From:



customer@finance‐broker.org To:





Guillaume Subject:
Project
Request Date:



Wednesday
afternoon Dear
Guillaume, Fantastic!
This
is
getting
better
all
the
 time!
But
can
we
get
rid
most
of
the
‘.’
and
 bracket
symbols! Thanks,
Happy
Customer 56 @glaforge — @paulk_asert
  • 106. Stock Exchange Order DSL 



//
Characteristics
of
the
order:
"of
GOOG
{...}" 



def
of(SecurityAndCharacteristics
secAndCharact)
{ 







security
=
secAndCharact.security 







def
c
=
secAndCharact.characteristics.clone() 







c.delegationStrategy
=
Closure.DELEGATE_ONLY 







c.delegate
=
this 







c() 







//
debug
print
of
the
resulting
order 







println
toString() 







return
this 



} 
 




//
Valuation
closure:
"of
{
qty,
unitPrice
‐>
...
}" 




def
of(Closure
valueCalculation)
{ 








//
in
order
to
be
able
to
define
closures
like
{
qty
*
unitPrice
} 








//
without
having
to
explicitly
pass
the
parameters
to
the
closure 








//
we
can
wrap
the
closure
inside
another
one 








//
and
that
closure
sets
a
delegate
to
the
qty
and
unitPrice
variables 








def
wrapped
=
{
qty,
unitPrice
‐> 












def
cloned
=
valueCalculation.clone() 












cloned.resolveStrategy
=
Closure.DELEGATE_ONLY 












cloned.delegate
=
[qty:
qty,
unitPrice:
unitPrice] 












cloned() 








} 








return
wrapped 



} }
 57 @glaforge — @paulk_asert http://groovyconsole.appspot.com/script/226001 by glaforge
  • 107. Stock Exchange Order DSL class
Security
{ 



String
name } 
 class
SecurityAndCharacteristics
{ 



Security
security 



Closure
characteristics } 
 class
CustomBinding
extends
Binding
{ 



def
getVariable(String
word)
{ 







//
return
System.out
when
the
script
requests
to
write
to
'out' 







if
(word
==
"out")
System.out 







//
don't
throw
an
exception
and
return
null 







//
when
a
silent
sentence
word
is
used, 







//
like
"to"
and
"the"
in
our
DSL 







null 



} } 
 //
Script
helper
method
for
"GOOG
{}",
"VMW
{}",
etc. def
methodMissing(String
name,
args)
{ 



new
SecurityAndCharacteristics( 







security:
new
Security(name:
name), 







characteristics:
args[0] 



) } 
 //
Script
helper
method
to
make
"order
to"
silent //
by
just
creating
our
current
order def
order(to)
{
new
Order()
} 
 //
use
the
script
binding
for
silent
sentence
words
like
"to",
"the" binding
=
new
CustomBinding() //
syntax
for
200.shares Integer.metaClass.getShares
=
{
‐>
delegate
} 58 @glaforge — @paulk_asert
  • 108. Stock Exchange Order DSL order
to
buy
200.shares
of
GOOG
{ 



limitPrice






500 



allOrNone







false 



at
the
value
of

{
qty
*
unitPrice
‐
100
} } 
 order
to
sell
150.shares
of
VMW
{ 



limitPrice






80 



allOrNone







true 



at
the
value
of

{
qty
*
unitPrice
} } 59 http://groovyconsole.appspot.com/script/226001 @glaforge — @paulk_asert by glaforge
  • 109. Stock Exchange Order DSL From:



customer@finance‐broker.org To:





Guillaume Subject:
Project
Request Date:



Wednesday
evening Dear
Guillaume, Brilliant!
Even
our
CEO
could
write
orders! Thanks,
Happy
Customer 60 @glaforge — @paulk_asert
  • 110. THURSDAY 61 @glaforge — @paulk_asert http://t1.gstatic.com/images?q=tbn:_VBloEP8YC-6IM:http://msp248.photobucket.com/albums/gg171/ingrid2002/TextPL252028
  • 111. Einstein’s Riddle DSL … From:



customer@acme.org To:





Paul
King Subject:
Project
Request Date:



Early
morning Dear
Paul, We
would
like
a
DSL
for
capturing
our
business
 rules.
Our
business
rules
are
captured
in
the
form
 of
logic
clauses.
They
are
very
much
like
those
 found
in
Einstein’s
riddle. Thanks,
Happy
Customer 62 @glaforge — @paulk_asert
  • 112. Einstein’s Riddle • Wikipedia: The zebra puzzle is a well-known logic puzzle – It is often called Einstein's Puzzle or Einstein's Riddle because it is said to have been invented by Albert Einstein as a boy, with the claim that Einstein said “only 2 percent of the world's population can solve it.” – The puzzle is also sometimes attributed to Lewis Carroll. However, there is no known evidence for Einstein's or Carroll's authorship; and the original puzzle cited below mentions brands of cigarette, such as Kools, that did not exist during Carroll's lifetime or Einstein's boyhood 63 @glaforge — @paulk_asert
  • 113. Einstein’s Riddle • Some premises: – The British person lives in the red house – The Swede keeps dogs as pets – The Dane drinks tea – The green house is on the left of the white house – The green homeowner drinks coffee – The man who smokes Pall Mall keeps birds – The owner of the yellow house smokes Dunhill – The man living in the center house drinks milk – The Norwegian lives in the first house – The man who smokes Blend lives next to the one who keeps cats – The man who keeps the horse lives next to the man who smokes Dunhill – The man who smokes Bluemaster drinks beer – The German smokes Prince – The Norwegian lives next to the blue house 64 @glaforge — @paulk_asert
  • 114. Einstein’s Riddle • Some premises: – The British person lives in the red house • And a question: – The Swede keeps dogs as pets – Who owns the fish? – The Dane drinks tea – The green house is on the left of the white house – The green homeowner drinks coffee – The man who smokes Pall Mall keeps birds – The owner of the yellow house smokes Dunhill – The man living in the center house drinks milk – The Norwegian lives in the first house – The man who smokes Blend lives next to the one who keeps cats – The man who keeps the horse lives next to the man who smokes Dunhill – The man who smokes Bluemaster drinks beer – The German smokes Prince – The Norwegian lives next to the blue house 64 @glaforge — @paulk_asert
  • 115. Einstein’s Riddle : Prolog %
from
http://www.baptiste‐wicht.com/2010/09/solve‐einsteins‐riddle‐using‐prolog %
Preliminary
definitions persons(0,
[])
:‐
!. persons(N,
[(_Men,_Color,_Drink,_Smoke,_Animal)|T])
:‐
N1
is
N‐1,
persons(N1,T). person(1,
[H|_],
H)
:‐
!. person(N,
[_|T],
R)
:‐
N1
is
N‐1,
person(N1,
T,
R). %
The
Brit
lives
in
a
red
house hint1([(brit,red,_,
_,
_)|_]). hint1([_|T])
:‐
hint1(T). %
The
Swede
keeps
dogs
as
pets hint2([(swede,_,_,_,dog)|_]). hint2([_|T])
:‐
hint2(T). %
The
Dane
drinks
tea hint3([(dane,_,tea,_,_)|_]). hint3([_|T])
:‐
hint3(T). %
The
Green
house
is
on
the
left
of
the
White
house
hint4([(_,green,_,_,_),(_,white,_,_,_)|_]). hint4([_|T])
:‐
hint4(T). %
The
owner
of
the
Green
house
drinks
coffee. hint5([(_,green,coffee,_,_)|_]). hint5([_|T])
:‐
hint5(T). ... 65 @glaforge — @paulk_asert
  • 116. Einstein’s Riddle DSL From:



customer@acme.org To:





Paul
King Subject:
Project
Request Date:



Early
morning Dear
Paul, Thanks
for
your
Prolog
solution
but
we
don’t
have Prolog
installed.
Do
you
have
a
version
that
runs on
the
JVM? Thanks,
Happy
Customer 66 @glaforge — @paulk_asert
  • 117. Einstein’s Riddle : Polyglot @GrabResolver('https://oss.sonatype.org/content/repositories/snapshots/') @Grab('org.prolog4j:prolog4j‐api:0.2.1‐SNAPSHOT') @Grab('org.prolog4j:prolog4j‐tuprolog:0.2.1‐SNAPSHOT') import
org.prolog4j.* def
p
=
ProverFactory.prover p.addTheory(new
File('/GroovyExamples/tuProlog/src/einstein.pl').text) def
sol
=
p.solve("solution(Persons).") println
sol.get('Persons') 67 @glaforge — @paulk_asert
  • 118. Einstein’s Riddle DSL … From:



customer@acme.org To:





Paul
King Subject:
Project
Request Date:



Early
morning Dear
Paul, Thanks
for
that
version
but
in
terms
of
 maintaining
the
rules,
we
would
like
a
more
 fluent
expression
of
the
rules
rather
than
 Prolog?
Any
thoughts? Thanks,
Happy
Customer 68 @glaforge — @paulk_asert
  • 119. Einstein’s Riddle : Polyglot w/ DSL //
define
some
domain
classes
and
objects enum
Pet
{
dog,
cat,
bird,
fish,
horse
} enum
Color
{
green,
white,
red,
blue,
yellow
} enum
Smoke
{
dunhill,
blends,
pallmall,
prince,
bluemaster
} enum
Drink
{
water,
tea,
milk,
coffee,
beer
} enum
Nationality
{
Norwegian,
Dane,
Brit,
German,
Swede
} 
 dogs
=
dog;
birds
=
bird;
cats
=
cat;
horses
=
horse a
=
owner
=
house
=
the
=
abode
=
person
=
man
=
is
=
to
= 



side
=
next
=
who
=
different
=
'ignored' //
some
preliminary
definitions p
=
ProverFactory.prover hintNum
=
1 
 p.addTheory(''' 



persons(0,
[])
:‐
!. 



persons(N,
[(_Men,_Color,_Drink,_Smoke,_Animal)|T])
:‐
N1
is
N‐1,
persons(N1,T). 



person(1,
[H|_],
H)
:‐
!. 



person(N,
[_|T],
R)
:‐
N1
is
N‐1,
person(N1,
T,
R). ''') 69 @glaforge — @paulk_asert
  • 120. Einstein’s Riddle : Polyglot w/ DSL… //
define
some
helper
methods
(our
interface
to
prolog)
 def
addPairHint(Map
m)
{ 



def
from
=
m.from?.toString()?.toLowerCase() 



p.addTheory(""" 



hint$hintNum([(${from
?:
'_'},${m.color
?:
'_'},${m.drink
?:
'_'}, 







${m.smoke
?:
'_'},${m.pet
?:
'_'})|_]). 



hint$hintNum([_|T])
:‐
hint$hintNum(T). 



""") 



hintNum++ } 
 def
addPositionHint(Map
m,
int
pos)
{ 



def
from
=
m.from?.toString()?.toLowerCase() 



p.addTheory(""" 







hint$hintNum(Persons)
:‐
person($pos,
Persons,
(${from
?:
'_'}, 







${m.color
?:
'_'},$



{m.drink
?:
'_'},${m.smoke
?:
'_'},${m.pet
?:
'_'})). 



""") 



hintNum++ } 
 def
addToLeftHint(Map
left,
Map
right)
{ 



p.addTheory(""" 







hint$hintNum([(_,$left.color,_,_,_),(_,$right.color,_,_,_)|_]). 







hint$hintNum([_|T])
:‐
hint$hintNum(T). 



""") 



hintNum++ } ... 70 @glaforge — @paulk_asert
  • 121. Einstein’s Riddle : Polyglot w/ DSL //
now
implement
DSL
in
terms
of
helper
methods def
the(Nationality
n)
{ 



def
ctx
=
[from:n] 



[ 







drinks:
{
d
‐>
addPairHint(ctx
+
[drink:d])
}, 







smokes:
{
s
‐>
addPairHint(ctx
+
[smoke:s])
}, 







keeps:
{
p
‐>
addPairHint(ctx
+
[pet:p])
}, 







rears:
{
p
‐>
addPairHint(ctx
+
[pet:p])
}, 







owns:
{
_the
‐>
[first:
{
house
‐>
addPositionHint(ctx,
1)
}]
}, 







has:
{
_a
‐> 











[pet:
{
a
‐>
addPairHint(ctx
+
[pet:a])
}]
+ 















Color.values().collectEntries
{
c
‐> 



















[c.toString(),
{
_dummy
‐>
addPairHint(ctx
+
[color:c])
}
] 















} 







}, 







lives:
{
_next
‐>
[to:
{
_the
‐> 











Color.values().collectEntries{
c
‐> 















[c.toString(),
{
_dummy
‐>
addNeighbourHint(ctx,
[color:c])
}
] 











} 







}]} 



] } ... 71 @glaforge — @paulk_asert
  • 122. Einstein’s Riddle : Polyglot w/ DSL //
now
define
the
DSL the
man
from
the
centre
house
drinks
milk the
Norwegian
owns
the
first
house the
Dane
drinks
tea the
German
smokes
prince the
Swede
keeps
dogs
//
alternate
ending:
has
a
pet
dog the
Brit
has
a
red
house

//
alternate
ending:
red
abode the
owner
of
the
green
house
drinks
coffee the
owner
of
the
yellow
house
smokes
dunhill the
person
known
to
smoke
pallmall
rears
birds
//
alternate
end:
keeps
birds the
man
known
to
smoke
bluemaster
drinks
beer the
green
house
is
on
the
left
side
of
the
white
house the
man
known
to
smoke
blends
lives
next
to
the
one
who
keeps
cats the
man
known
to
keep
horses
lives
next
to
the
man
who
smokes
dunhill the
man
known
to
smoke
blends
lives
next
to
the
one
who
drinks
water the
Norwegian
lives
next
to
the
blue
house 72 @glaforge — @paulk_asert
  • 123. Einstein’s Riddle DSL From:



customer@acme.org To:





Paul
King Subject:
Project
Request Date:



Early
morning Dear
Paul, That’s
great.
We
even
get
some
code
 completion
When
using
an
IDE.
Is
there
 anything
more
we
can
do
to
get
more
 completion? Thanks,
Happy
Customer 73 @glaforge — @paulk_asert
  • 124. Einstein’s Riddle : Polyglot w/ DSL //
now
implement
DSL
in
terms
of
helper
methods def
the(Nationality
n)
{ 



def
ctx
=
[from:n] 



[ 







drinks:
{
d
‐>
addPairHint(ctx
+
[drink:d])
}, 







smokes:
{
s
‐>
addPairHint(ctx
+
[smoke:s])
}, 







keeps:
{
p
‐>
addPairHint(ctx
+
[pet:p])
}, 







... 



] } ... the
German
smokes
prince the(German).smokes(prince) n
=
German ctx
=
[from:
German] [drinks:
…, 
smokes:
{
s
‐>
addPairHint([from:
German,
smoke:
s])
}, 
keeps:
…, 
… addPairHint([from:
German,
smoke:
 ] prince]) 74 @glaforge — @paulk_asert
  • 125. Einstein’s Riddle : Polyglot w/ DSL • Some parts of our DSL are automatically statically inferred, e.g. typing ‘bl’ and then asking for completion yields: • But other parts are not known, e.g. the word ‘house’ in the fragment below: ‘house’ is key for a Map and could be any value 75 @glaforge — @paulk_asert
  • 126. Einstein’s Riddle : Polyglot w/ DSL def
the(Color
c1)
{[ 



house:
{
_is
‐>
[on:
{
_the
‐>
[left:
{
_side
‐>
[of:
{
__the
‐> 







Color.values().collectEntries{
c2
‐>
[c2.toString(),
{
_dummy
‐> 











addToLeftHint([color:c1],
[color:c2]) 







}]} 



}]}]}]} ]} class
HousePlaceHolder
{ 



def
c1,
script 



def
house(_is)
{ 







[on:
{
_the
‐>
[left:
{
_side
‐>
[of:
{
__the
‐> 











Color.values().collectEntries
{
c2
‐> 















[c2.toString(),
{
_dummy
‐>
script.addToLeftHint( 



















[color:
c1],
[color:
c2]
)}]} 







}]}]}] 



} } def
the(Color
c1)
{
new
HousePlaceHolder(c1:c1,
script:this)
} 76 @glaforge — @paulk_asert ‘house’ is now understood
  • 127. Einstein’s Riddle DSL From:



customer@acme.org To:





Paul
King Subject:
Project
Request Date:



Early
morning Dear
Paul, That’s
fantastic!
But
we
have
just
started standardizing
on
Choco
as
our
logic
solving engine.
I
guess
we
need
to
start
from
scratch. Let
me
know
what
you
think. Thanks,
Happy
Customer 77 @glaforge — @paulk_asert
  • 128. Einstein’s Riddle : Choco w/ DSL @GrabResolver('http://www.emn.fr/z‐info/choco‐solver/mvn/repository/') @Grab('choco:choco:2.1.1‐SNAPSHOT') import
static
choco.Choco.* import
choco.kernel.model.variables.integer.* 
 def
m
=
new
choco.cp.model.CPModel() m.metaClass.plus
=
{
m.addConstraint(it);
m
} def
s
=
new
choco.cp.solver.CPSolver() choco.Choco.metaClass.static.eq
=
{
c,
v
‐>
delegate.eq(c,
v.ordinal())
} def
makeEnumVar(st,
arr)
{
 



choco.Choco.makeIntVar(st,
0,
arr.size()‐1,
choco.Options.V_ENUM)
} pets
=
new
IntegerVariable[num] colors
=
new
IntegerVariable[num] smokes
=
new
IntegerVariable[num] drinks
=
new
IntegerVariable[num] nations
=
new
IntegerVariable[num] 
 (0..<num).each
{
i
‐> 





pets[i]
=
makeEnumVar("pet$i",


pets) 



colors[i]
=
makeEnumVar("color$i",
colors) 



smokes[i]
=
makeEnumVar("smoke$i",
smokes) 



drinks[i]
=
makeEnumVar("drink$i",
drinks) 


nations[i]
=
makeEnumVar("nation$i",

nations) } ... 78 @glaforge — @paulk_asert
  • 129. Einstein’s Riddle : Choco w/ DSL //
define
DSL
(simplistic
non‐refactored
version) def
neighbours(var1,
val1,
var2,
val2)
{ 



and( 







ifOnlyIf(eq(var1[0],
val1),
eq(var2[1],
val2)), 







implies(eq(var1[1],
val1),
or(eq(var2[0],
val2),
eq(var2[2],
val2))), 







implies(eq(var1[2],
val1),
or(eq(var2[1],
val2),
eq(var2[3],
val2))), 







implies(eq(var1[3],
val1),
or(eq(var2[2],
val2),
eq(var2[4],
val2))), 







ifOnlyIf(eq(var1[4],
val1),
eq(var2[3],
val2)) 



) } iff
=
{
e1,
c1,
e2,
c2
‐>
and(*(0..<num).collect{ 



ifOnlyIf(eq(e1[it],
c1),
eq(e2[it],
c2)) })
} ... //
define
the
DSL
in
terms
of
DSL
implementation def
the(Nationality
n)
{ def
ctx
=
[nations,
n] [ 



drinks:
iff.curry(*ctx,
drinks), 



smokes:
iff.curry(*ctx,
smokes), 



keeps:
iff.curry(*ctx,
pets), 



rears:
iff.curry(*ctx,
pets), 



owns:
{
_the
‐>
[first:
{
house
‐>
eq(nations[first],
n)}]
}, ... 79 @glaforge — @paulk_asert
  • 130. Einstein’s Riddle : Choco w/ DSL //
define
rules m
+=
all
pets
are
different m
+=
all
colors
are
different m
+=
all
smokes
are
different m
+=
all
drinks
are
different m
+=
all
nations
are
different m
+=
the
man
from
the
centre
house
drinks
milk m
+=
the
Norwegian
owns
the
first
house m
+=
the
Dane
drinks
tea m
+=
the
German
smokes
prince m
+=
the
Swede
keeps
dogs
//
alternate
ending:
has
a
pet
dog m
+=
the
Brit
has
a
red
house

//
alternate
ending:
red
abode m
+=
the
owner
of
the
green
house
drinks
coffee m
+=
the
owner
of
the
yellow
house
smokes
dunhill m
+=
the
person
known
to
smoke
pallmall
rears
birds
//
alt
end:
keeps
birds m
+=
the
man
known
to
smoke
bluemaster
drinks
beer m
+=
the
green
house
is
on
the
left
side
of
the
white
house m
+=
the
man
known
to
smoke
blends
lives
next
to
the
one
who
keeps
cats m
+=
the
man
known
to
keep
horses
lives
next
to
the
man
who
smokes
dunhill m
+=
the
man
known
to
smoke
blends
lives
next
to
the
one
who
drinks
water m
+=
the
Norwegian
lives
next
to
the
blue
house ... 80 @glaforge — @paulk_asert
  • 131. Einstein’s Riddle : Choco w/ DSL def
pretty(s,
c,
arr,
i)
{
 



c.values().find{
it.ordinal()
==
s.getVar(arr[i])?.value
}
 } 
 //
invoke
logic
solver s.read(m) def
more
=
s.solve() while
(more)
{ 



for
(i
in
0..<num)
{ 







print


'The
'












+
pretty(s,
Nationality,
nations,
i) 







print


'
has
a
pet
'





+
pretty(s,
Pet,
pets,
i) 







print


'
smokes
'








+
pretty(s,
Smoke,
smokes,
i) 







print


'
drinks
'








+
pretty(s,
Drink,
drinks,
i) 







println
'
and
lives
in
a
'
+
pretty(s,
Color,
colors,
i)
+
'
house' 



} 



more
=
s.nextSolution() } Solving
Einstein's
Riddle: The
Norwegian
has
a
pet
cat
smokes
dunhill
drinks
water
and
lives
in
a
yellow
house The
Dane
has
a
pet
horse
smokes
blends
drinks
tea
and
lives
in
a
blue
house The
Brit
has
a
pet
bird
smokes
pallmall
drinks
milk
and
lives
in
a
red
house The
German
has
a
pet
fish
smokes
prince
drinks
coffee
and
lives
in
a
green
house The
Swede
has
a
pet
dog
smokes
bluemaster
drinks
beer
and
lives
in
a
white
house 81 @glaforge — @paulk_asert
  • 132. FRIDAY 82 @glaforge — @paulk_asert http://t3.gstatic.com/images?q=tbn:mgkj1lXpQ2-uWM:http://i298.photobucket.com/albums/mm269/bearyjuicylicious/comments
  • 133. Nasa Robot From:



customer@acme.org To:





Guillaume
Laforge Subject:
Project
Request Date:



Early
morning Dear
Guillaume, We’ve
got
a
contract
with
NASA
to
send
a
 rover
on
Mars.
We’d
need
a
DSL
to
lead
 the
robot
on
the
rocky
soil.
Can
you
 help? Thanks,
Happy
Customer 83 @glaforge — @paulk_asert
  • 134. Nasa Robot import
static
Direction.* enum
Direction
{ 



left,
right,
forward,
backward } class
Robot
{ 



void
move(Direction
dir)
{ 







println
"robot
moved
$dir" 



} } Nothing special here. def
robot
=
new
Robot() But can we make it prettier? robot.move
left 84 @glaforge — @paulk_asert
  • 135. Nasa Robot package
projectmars.domain import
static
Direction.* enum
Direction
{
left,
right,
forward,
backward
} class
Robot
{ 



void
move(Direction
dir)
{ Some simplistic GroovyShell 







println
"robot
moved
$dir" 



} } package
projectmars.integration def
shell
=
new
GroovyShell(this.class.classLoader) shell.evaluate
''' 



import
projectmars.domain.Robot 



import
static
projectmars.domain.Direction.* 



 



def
robot
=
new
Robot() 



 



robot.move
left ''' 85 @glaforge — @paulk_asert
  • 136. …Nasa Robot… package
projectmars.domain import
static
Direction.* enum
Direction
{
left,
right,
forward,
backward
} class
Robot
{ 



void
move(Direction
dir)
{ 







println
"robot
moved
$dir" 



} } Inject robot into binding def
binding
=
new
Binding(robot:
new
Robot()) def
shell
=
new
GroovyShell(this.class.classLoader,
binding) shell.evaluate
''' 



import
static
projectmars.domain.Direction.* 



robot.move
left ''' 86 @glaforge — @paulk_asert
  • 137. Nasa Robot package
projectmars.domain import
static
Direction.* enum
Direction
{
left,
right,
forward,
backward
} class
Robot
{ 



void
move(Direction
dir)
{ 







println
"robot
moved
$dir" 



} } /* Inject directions into binding def
binding
=
new
Binding( 



robot:
new
Robot(), 



left:
Direction.left,
right:
Direction.right, 



forward:
Direction.forward,
backward:
Direction.backward) */ def
binding
=
new
Binding( 



robot:
new
Robot(), 



*:Direction.values().collectEntries
{
[(it.name()):
it]
}) def
shell
=
new
GroovyShell(this.class.classLoader,
binding) shell.evaluate
''' 



robot.move
left ''' 87 @glaforge — @paulk_asert
  • 138. …Nasa Robot… package
projectmars.domain import
org.codehaus.groovy.control.CompilerConfiguration import
org.codehaus.groovy.control.customizers.* import
static
Direction.* enum
Direction
{
left,
right,
forward,
backward
} class
Robot
{ 



void
move(Direction
dir)
{
println
"robot
moved
$dir"
} } def
binding
=
new
Binding(robot:
new
Robot()) Inject import def
importCustomizer
=
new
ImportCustomizer() importCustomizer.addStaticStars
'projectmars.domain.Direction' def
config
=
new
CompilerConfiguration() config.addCompilationCustomizers
importCustomizer def
shell
=
new
GroovyShell(this.class.classLoader,
binding,
config) shell.evaluate
''' 



robot.move
left ''' 88 @glaforge — @paulk_asert
  • 139. package
projectmars.domain …Nasa Robot… import
org.codehaus.groovy.control.CompilerConfiguration import
org.codehaus.groovy.control.customizers.* import
static
Direction.* enum
Direction
{
left,
right,
forward,
backward
} class
Robot
{ 



void
move(Direction
dir)
{
println
"robot
moved
$dir"
} } def
binding
=
new
Binding(robot:
new
Robot()) def
importCustomizer
=
new
ImportCustomizer() importCustomizer.addStaticStars
'projectmars.domain.Direction' def
config
=
new
CompilerConfiguration() config.addCompilationCustomizers
importCustomizer def
shell
=
new
GroovyShell(this.class.classLoader,
binding,
config) shell.evaluate
''' 



move
left '''
+
''' 



def
move(dir)
{ 







robot.move
dir Can we make ‘robot’ implicit? 



} ''' 89 @glaforge — @paulk_asert
  • 140. package
projectmars.domain Nasa Robot import
org.codehaus.groovy.control.CompilerConfiguration import
org.codehaus.groovy.control.customizers.* import
static
Direction.* enum
Direction
{
left,
right,
forward,
backward
} class
Robot
{ 



void
move(Direction
dir)
{
println
"robot
moved
$dir"
} } def
binding
=
new
Binding(robot:
new
Robot()) def
importCustomizer
=
new
ImportCustomizer() importCustomizer.addStaticStars
Direction.class.name def
config
=
new
CompilerConfiguration() Better ‘robot’ implicit config.addCompilationCustomizers
importCustomizer config.scriptBaseClass
=
RobotBaseScriptClass.class.name def
shell
=
new
GroovyShell(this.class.classLoader,
binding,
config) shell.evaluate
''' 



move
left ''' abstract
class
RobotBaseScriptClass
extends
Script
{ 



void
move(Direction
dir)
{ 







this.binding.robot.move
dir 



} } 90 @glaforge — @paulk_asert
  • 141. package
projectmars.domain Nasa Robot import
org.codehaus.groovy.control.CompilerConfiguration import
org.codehaus.groovy.control.customizers.* import
static
Direction.* enum
Direction
{
left,
right,
forward,
backward
} class
Robot
{ 



void
move(Direction
dir)
{
println
"robot
moved
$dir"
} } Better ‘robot’ implicit def
robot
=
new
Robot() def
binding
=
new
Binding(robot:
robot,
move:
robot.&move) def
importCustomizer
=
new
ImportCustomizer() importCustomizer.addStaticStars
Direction.class.name def
config
=
new
CompilerConfiguration() config.addCompilationCustomizers
importCustomizer def
shell
=
new
GroovyShell(this.class.classLoader,
binding,
config) shell.evaluate
''' 



move
left ''' 91 @glaforge — @paulk_asert
  • 142. … Nasa Robot shell.evaluate
''' 



mOvE
lefT Case insensitive? ''' abstract
class
RobotBaseScriptClass
extends
Script
{ 



@Delegate
@Lazy
Robot
robot
=
this.binding.robot 



 



def
invokeMethod(String
name,
args)
{ 







robot."${name.toLowerCase()}"(*args) 



} } class
CustomBinding
extends
Binding
{ 



private
Map
variables 



 



CustomBinding(Map
vars)
{ 







this.variables
=
[ 











*:vars,
 











*:Direction.values().collectEntries
{
[(it.name()):
it]
} 







] 



} 



 



def
getVariable(String
name)
{ 







variables[name.toLowerCase()] 



} 92 } @glaforge — @paulk_asert
  • 143. package
projectmars.domain Nasa Robot import
org.codehaus.groovy.control.CompilerConfiguration import
org.codehaus.groovy.control.customizers.* import
groovy.transform.TupleConstructor import
static
Direction.* import
static
Duration.* enum
Direction
{ 



left,
right,
forward,
backward } enum
Unit
{ 



centimeter
('cm',
0.01), Going further? 



meter





(
'm',



1), 



kilometer

('km',
1000)



 



String
abbreviation 



double
multiplier 



 



Unit(String
abbr,
double
mult)
{ 







this.abbreviation
=
abbr 







this.multiplier
=
mult 



} 



 



String
toString()
{
abbreviation
} } … 93 @glaforge — @paulk_asert
  • 144. …Nasa Robot… enum
Duration
{
hour
} @TupleConstructor … class
Distance
{ class
DistanceCategory
{ 



double
amount 



static
Distance
getCentimeters(Number
num)
{ 



Unit
unit 







new
Distance(num,
Unit.centimeter) 



 



} 



Speed
div(Duration
dur)
{ 



static
Distance
getMeters(Number
num)
{ 







new
Speed(amount,
unit) 







new
Distance(num,
Unit.meter) 



} 



} 



 



static
Distance
getKilometers(Number
num)
{ 



String
toString()
{
"$amount$unit"
} 







new
Distance(num,
Unit.kilometer) } 



} 



static
Distance
getCm(Number
num)
{
getCentimeters(num)
} @TupleConstructor 



static
Distance
getM(Number
num)

{
getMeters(num)
} class
Speed
{ 



static
Distance
getKm(Number
num)
{
getKilometers(num)
} 



double
amount } 



Unit
unit 



 class
Robot
{ 



String
toString()
{
"$amount
$unit/h"
} 



void
move(Direction
dir)
{ } 







println
"robot
moved
$dir" … 



} 



void
move(Direction
dir,
Distance
d)
{ 







println
"robot
moved
$dir
by
$d" 



} 



void
move(Map
m,
Direction
dir)
{ 







println
"robot
moved
$dir
by
$m.by
at
${m.at
?:
'1
km 



} } … 94 @glaforge — @paulk_asert
  • 145. …Nasa Robot… … def
binding
=
new
Binding(robot:
new
Robot(),
h:
Duration.hour) def
importCustomizer
=
new
ImportCustomizer() importCustomizer.addStaticStars
Direction.class.name def
config
=
new
CompilerConfiguration() config.addCompilationCustomizers
importCustomizer config.scriptBaseClass
=
RobotBaseScriptClass.class.name def
shell
=
new
GroovyShell(this.class.classLoader,
binding,
config) use(DistanceCategory)
{ shell.evaluate
''' 



move
left 



move
right,
3.meters 



move
right,
by:
3.meters 



move
right,
by:
3.meters,
at:
5.km/h ''' } abstract
class
RobotBaseScriptClass
extends
Script
{ 



@Delegate
@Lazy
Robot
robot
=
this.binding.robot } 95 @glaforge — @paulk_asert
  • 146. Nasa Robot Going even further? shell.evaluate
''' 



move
left 



move
right,
3.meters 
 



move
right,
by:
3.meters 



move
right,
by:
3.meters,
at:
5.km/h 
 



move
right
by
3.meters
at
5.km/h

 
 



deploy
left
arm ''' 96 @glaforge — @paulk_asert
  • 147. Domain-Specific Language Descriptors • DSLs are nice, but what about support in my IDE? • DSLD to the rescue – Domain-Specific Language Descriptors – for Eclipse STS (but GDLS concent available in IntelliJ IDEA too) • Idea: a DSL for describing DSLs, in order to provide – code-completion – code navigation – documentation hovers 97 @glaforge — @paulk_asert
  • 148. Domain-Specific Language Descriptors 98 @glaforge — @paulk_asert
  • 149. Domain-Specific Language Descriptors currentType(
subType(
Number
)
).accept
{ 



[m:
"meter",
yd:
"yard",
cm:
"centimeter",
mi:
"mile",
km:
"kilometer"].each
{ 







property
name:it.key,
type:"Distance", 











doc:
"""A
<code>${it.value}</code>
from
<a
href="$url">$url</a>""" 



} } 98 @glaforge — @paulk_asert

Notes de l'éditeur

  1. \n
  2. \n
  3. \n
  4. \n
  5. \n
  6. \n
  7. \n
  8. \n
  9. \n
  10. \n
  11. \n
  12. \n
  13. \n
  14. \n
  15. \n
  16. \n
  17. \n
  18. \n
  19. \n
  20. \n
  21. \n
  22. \n
  23. \n
  24. \n
  25. \n
  26. \n
  27. \n
  28. \n
  29. \n
  30. \n
  31. \n
  32. \n
  33. \n
  34. \n
  35. \n
  36. \n
  37. \n
  38. \n
  39. \n
  40. \n
  41. \n
  42. \n
  43. \n
  44. \n
  45. \n
  46. \n
  47. \n
  48. \n
  49. \n
  50. \n
  51. \n
  52. \n
  53. \n
  54. \n
  55. \n
  56. \n
  57. \n
  58. \n
  59. \n
  60. \n
  61. \n
  62. \n
  63. \n
  64. \n
  65. \n
  66. \n
  67. \n
  68. \n
  69. \n
  70. \n
  71. \n
  72. \n
  73. \n
  74. \n
  75. \n
  76. \n
  77. \n
  78. \n
  79. \n
  80. \n
  81. \n
  82. \n
  83. \n
  84. \n
  85. \n
  86. \n
  87. \n
  88. \n
  89. \n
  90. \n
  91. \n
  92. \n
  93. \n
  94. \n
  95. \n
  96. \n
  97. \n
  98. \n
  99. \n
  100. \n
  101. \n
  102. \n
  103. \n
  104. \n
  105. \n
  106. \n
  107. \n
  108. \n
  109. \n
  110. \n
  111. \n
  112. \n
  113. \n
  114. \n
  115. \n
  116. \n
  117. \n
  118. \n
  119. \n
  120. \n
  121. \n
  122. \n
  123. \n
  124. \n
  125. \n
  126. \n
  127. \n
  128. \n
  129. \n
  130. \n
  131. \n
  132. \n
  133. \n
  134. \n
  135. \n
  136. \n
  137. \n
  138. \n
  139. \n
  140. \n
  141. \n
  142. \n
  143. \n
  144. \n
  145. \n
  146. \n
  147. \n
  148. \n
  149. \n
  150. \n
  151. \n
  152. \n
  153. \n
  154. \n
  155. \n
  156. \n