Groovy Domain-Specific Languages techniques

Groovy Domain-Specific Languages techniques

Overview of the techniques that can be combined in order to build, integrate, and tool Domain-Specific Languages in Groovy

137d3908243acfc30e126615d59d4e6d?s=128

Guillaume Laforge

September 11, 2013
Tweet

Transcript

  1. © 2013 SpringOne 2GX. All rights reserved. Do not distribute

    without permission. Groovy Domain-Specific Languages Paul King Groovy Core Developer ASERT @paulk_asert Guillaume Laforge Groovy Project Manager SpringSource / VMware @glaforge
  2. Paul King •Groovy Core Committer • Leads ASERT – software,

    training, consultancy company based in Brisbane, Australia • PhD in Computer Science from The University of Queensland • Co-author of Groovy in Action • Follow me: – Twitter: @paulk_asert 2
  3. Guillaume Laforge • Groovy Project Manager at Pivotal • Initiator

    of the Grails framework • Creator of the Gaelyk • Co-author of Groovy in Action • Follow me: • My blog: http://glaforge.appspot.com • Twitter: @glaforge • Google+: http://gplus.to/glaforge 3
  4. © 2013 SpringOne 2GX. All rights reserved. Do not distribute

    without permission. Introduction Definitions Examples Goals Pros & cons
  5. Domain-Specific Languages • In contrast to General Purpose Languages •

    Also known as: fluent / humane interfaces, language oriented programming, little or mini languages, macros, business natural languages... 5 { } 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.
  6. Technical examples <?xml version="1.0"?> <xsl:stylesheetversion="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> <xsl:output method="xml"/> <xsl:template match="*">

    <xsl:element name="{name()}"> <xsl:for-each select="@*"> <xsl:element name="{name()}"> <xsl:value-of select="."/> </xsl:element> </xsl:for-each> <xsl:apply-templates select="*|text()"/> </xsl:element> </xsl:template> </xsl:stylesheet> <?xml version="1.0"?> <GTK-Interface> <widget> <class>GtkWindow</class> <name>HelloWindow</name> <border_width>5</border_width> <Signal> <name>destroy</name> <handler>gtk_main_quit</handler> </Signal> <title>Hello</title> <type>GTK_WINDOW_TOPLEVEL</type> <position>GTK_WIN_POS_NONE</position> <allow_shrink>True</allow_shrink> <allow_grow>True</allow_grow> <auto_shrink>False</auto_shrink> <widget> <class>GtkButton</class> <name>Hello World</name> <can_focus>True</can_focus> <label>Hello World</label> </widget> </widget> </GTK-Interface> # Poll this site first each cycle. poll pop.provider.net proto pop3 user "jsmith" with pass "secret1" is "smith" here user jones with pass "secret2" is "jjones" here with options keep # Poll this site second, unless Lord Voldemort zaps us first. poll billywig.hogwarts.com with proto imap: user harry_potter with pass "floo" is harry_potter here # Poll this site third in the cycle. # Password will be fetched from ~/.netrc poll mailhost.net with proto imap: user esr is esr here "x.z?z{1,3}y" SELECT * FROM TABLE WHERE NAME LIKE '%SMI' ORDER BY NAME Glade Regex XSLT Fetchmail SQL
  7. Antimalaria drug resistance simulation HR skills representation Insurance policy risk

    calculation engine Market data feeds analysis Loan acceptance rules engine Nuclear safety simulations
  8. Goals of DSLs • Use a more expressive language than

    a general-purpose one • Share a common metaphor of understanding between developers 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 separation • Let business rules have their own lifecycle 8
  9. Pros and cons Pros – Domain experts can help, validate,

    modify, and often develop DSL programs – Somewhat self-documenting – Enhance quality, productivity, reliability, maintainability, portability, reusability – Safety; as long as the language constructs are safe, any DSL sentence can be considered safe Cons – Learning cost vs. limited applicability – Cost of designing, implementing & maintaining DSLs as well as tools/ IDEs – Attaining proper scope – Trade-offs between domain specificity and general purpose language constructs – Efficiency cost – Proliferation of similar non-standard DSLs 9
  10. Groovy provides... • A flexible and malleable syntax – scripts,

    native syntax constructs (list, map, ranges), closures, less punctuation... • Compile-time and runtime meta-programming – metaclasses, AST transformations – also operator overloading • The ability to easily integrate into Java / Spring apps – also security and safety 10
  11. © 2013 SpringOne 2GX. All rights reserved. Do not distribute

    without permission. Let’s get started!
  12. None
  13. Your mission: build a DSL for a Mars robot

  14. We need a robot! 14 package  mars class  Robot  {}

  15. It should move... 15 package  mars   class  Robot  {

           void  move()  {} }
  16. ..in a direction! 16 package  mars   class  Robot  {

           void  move(String  dir)  {} }
  17. More explicit direction 17 package  mars   class  Robot  {

           void  move(Direction  dir)  {} } package  mars   enum  Direction  {        left,  right,  forward,  backward }
  18. Now how can we control it? 18 import  static  mars.Direction.*;

    import  mars.Robot; public  class  Command  {        public  static  void  main(String[]  args)  {                Robot  robot  =  new  Robot();                robot.move(left);        } }
  19. Now how can we control it? 18 import  static  mars.Direction.*;

    import  mars.Robot; public  class  Command  {        public  static  void  main(String[]  args)  {                Robot  robot  =  new  Robot();                robot.move(left);        } } Syntactical noise!
  20. Now how can we control it? 18 import  static  mars.Direction.*;

    import  mars.Robot; public  class  Command  {        public  static  void  main(String[]  args)  {                Robot  robot  =  new  Robot();                robot.move(left);        } } Syntactical noise!                                                            —                                  — ——————————————————————        ————————————————————————————————————————                —————                                        —                                    —        ——        — —
  21. Optional semicolons & parentheses / Scripts vs classes 19 import

     static  mars.Direction.* import  mars.Robot                def      robot  =  new  Robot()                robot.move  left
  22. Optional semicolons & parentheses / Scripts vs classes 19 import

     static  mars.Direction.* import  mars.Robot                def      robot  =  new  Robot()                robot.move  left Optional typing
  23. Optional semicolons & parentheses / Scripts vs classes 19 import

     static  mars.Direction.* import  mars.Robot                def      robot  =  new  Robot()                robot.move  left But I don’t want to compile a script for every command! Optional typing
  24. Integration

  25. GroovyShell to the rescue 21

  26. GroovyShell to the rescue 21 def  shell  =  new  GroovyShell()

    shell.evaluate(        new  File("command.groovy") )
  27. GroovyShell to the rescue 21 def  shell  =  new  GroovyShell()

    shell.evaluate(        new  File("command.groovy") ) integration.groovy
  28. GroovyShell to the rescue 21 import  static  mars.Direction.* import  mars.Robot

    def  robot  =  new  Robot() robot.move  left def  shell  =  new  GroovyShell() shell.evaluate(        new  File("command.groovy") ) integration.groovy
  29. GroovyShell to the rescue 21 import  static  mars.Direction.* import  mars.Robot

    def  robot  =  new  Robot() robot.move  left def  shell  =  new  GroovyShell() shell.evaluate(        new  File("command.groovy") ) integration.groovy command.groovy
  30. Integration mechanisms • Different solutions available: – Groovy’s own mechanisms

    • Eval, GroovyScriptEngine, GroovyShell, GroovyClassLoader, CompilationUnit – Java 6: javax.script.* / JSR-223 • Groovy provides a JSR-223 implementation – Spring’s lang namespace • Groovy provides the highest level of flexibility and customization, but JSR-223 is a standard... 22
  31. Integration mechanisms • Different solutions available: – Groovy’s own mechanisms

    • Eval, GroovyScriptEngine, GroovyShell, GroovyClassLoader, CompilationUnit – Java 6: javax.script.* / JSR-223 • Groovy provides a JSR-223 implementation – Spring’s lang namespace • Groovy provides the highest level of flexibility and customization, but JSR-223 is a standard... 22
  32. What’s wrong with our DSL? 23 import  static  mars.Direction.* import

     mars.Robot def  robot  =  new  Robot() robot.move  left
  33. What’s wrong with our DSL? 23 import  static  mars.Direction.* import

     mars.Robot def  robot  =  new  Robot() robot.move  left Can’t we hide those imports?
  34. What’s wrong with our DSL? 23 import  static  mars.Direction.* import

     mars.Robot def  robot  =  new  Robot() robot.move  left Can’t we hide those imports? Can’t we inject the robot?
  35. What’s wrong with our DSL? 23 import  static  mars.Direction.* import

     mars.Robot def  robot  =  new  Robot() robot.move  left Can’t we hide those imports? Can’t we inject the robot? Do we really need to repeat ‘robot’?
  36. I’m sorry Dave, you can’t do that!

  37. I’m sorry Dave, you can’t do that!

  38. What we really want is... 25  move  left  

  39. Let’s inject a robot! • We can pass data in

    / out of scripts through the Binding – basically just a map of variable name keys and their associated values 26
  40. Let’s inject a robot! • We can pass data in

    / out of scripts through the Binding – basically just a map of variable name keys and their associated values 26 def  binding  =  new  Binding([        robot:  new  mars.Robot() ]) def  shell  =  new  GroovyShell(binding) shell.evaluate(        new  File("command.groovy") )
  41. Let’s inject a robot! • We can pass data in

    / out of scripts through the Binding – basically just a map of variable name keys and their associated values 26 def  binding  =  new  Binding([        robot:  new  mars.Robot() ]) def  shell  =  new  GroovyShell(binding) shell.evaluate(        new  File("command.groovy") ) integration.groovy
  42. Better? 27 import  static  mars.Direction.* robot.move  left

  43. Better? 27 import  static  mars.Direction.* robot.move  left Robot import removed

  44. Better? 27 import  static  mars.Direction.* robot.move  left Robot import removed

    Robot injected, no ‘new’ needed
  45. How to inject the direction? • Using the binding... 28

    import  mars.* def  binding  =  new  Binding([        robot:  new  Robot(),        left:          Direction.left,        right:        Direction.right,        backward:  Direction.backward,        forward:    Direction.forward ]) def  shell  =  new  GroovyShell(binding) shell.evaluate(        new  File("command.groovy") )
  46. How to inject the direction? • Using the binding... 28

    import  mars.* def  binding  =  new  Binding([        robot:  new  Robot(),        left:          Direction.left,        right:        Direction.right,        backward:  Direction.backward,        forward:    Direction.forward ]) def  shell  =  new  GroovyShell(binding) shell.evaluate(        new  File("command.groovy") ) Fragile in case of new directions!
  47. How to inject the direction? • Using the binding... 29

    import  mars.*   def  binding  =  new  Binding([        robot:  new  Robot(),        *:  Direction.values()                        .collectEntries  {                                [(it.name()):  it]                        } ]) def  shell  =  new  GroovyShell(binding) shell.evaluate(        new  File("command.groovy") ) Spread map operator
  48. How to inject the direction? • Using string concatenation? •

    Using compiler customizers 30
  49. String concatenation? Bad idea! 31 new  GroovyShell(new  Binding([robot:  new  mars.Robot()]))

           .evaluate("import  static  mars.Direction.*\n"  +                            "robot.move  left")
  50. String concatenation? Bad idea! 31 new  GroovyShell(new  Binding([robot:  new  mars.Robot()]))

           .evaluate("import  static  mars.Direction.*\n"  +                            "robot.move  left") Cheat with string concatenation? Bad!
  51. String concatenation? Bad idea! 31 new  GroovyShell(new  Binding([robot:  new  mars.Robot()]))

           .evaluate("import  static  mars.Direction.*\n"  +                            "robot.move  left")
  52. String concatenation? Bad idea! 31 new  GroovyShell(new  Binding([robot:  new  mars.Robot()]))

           .evaluate("import  static  mars.Direction.*\n"  +                            "robot.move  left") Line #1 becomes Line #2
  53. String concatenation? Bad idea! 31 new  GroovyShell(new  Binding([robot:  new  mars.Robot()]))

           .evaluate("import  static  mars.Direction.*\n"  +                            "robot.move  left")
  54. Compilation customizers • Ability to apply some customization to the

    Groovy compilation process • Three available customizers – ImportCustomizer: add transparent imports – ASTTransformationCustomizer: injects an AST transform – SecureASTCustomizer: restrict the groovy language to an allowed subset • But you can implement your own 32
  55. Imports customizer 33 def  configuration  =  new  CompilerConfiguration()   def

     imports  =  new  ImportCustomizer() imports.addStaticStar(mars.Direction.name) configuration.addCompilationCustomizers(imports)     new  GroovyShell(new  Binding([robot:  new  mars.Robot()]),                                                  configuration)        .evaluate("robot.move  left")            
  56. AST transformation customizer 34 def  configuration  =  new  CompilerConfiguration()  

    def  imports  =  new  ImportCustomizer() imports.addStaticStar(mars.Direction.name) configuration.addCompilationCustomizers(imports,                          new  ASTTransformationCustomizer(Log))   new  GroovyShell(new  Binding([robot:  new  mars.Robot()]),                                                  configuration)        .evaluate("robot.move  left"  +  "\n"                            "log.info  ‘Robot  moved’")                      
  57. AST transformation customizer 34 def  configuration  =  new  CompilerConfiguration()  

    def  imports  =  new  ImportCustomizer() imports.addStaticStar(mars.Direction.name) configuration.addCompilationCustomizers(imports,                          new  ASTTransformationCustomizer(Log))   new  GroovyShell(new  Binding([robot:  new  mars.Robot()]),                                                  configuration)        .evaluate("robot.move  left"  +  "\n"                            "log.info  ‘Robot  moved’")                       @Log injects a logger in scripts and classes
  58. Secure the onboard trajectory calculator

  59. Secure AST customizer • Let’s set up our environment –

    an import customizer to import java.lang.Math.* – prepare a secure AST customizer 36 def  imports  =  new  ImportCustomizer()                                    .addStaticStars('java.lang.Math') def  secure  =  new  SecureASTCustomizer()
  60. Secure AST customizer • Let’s set up our environment –

    an import customizer to import java.lang.Math.* – prepare a secure AST customizer 36 def  imports  =  new  ImportCustomizer()                                    .addStaticStars('java.lang.Math') def  secure  =  new  SecureASTCustomizer() Idea: secure the rocket’s onboard trajectory calculation system by allowing only math expressions to be evaluated by the calculator
  61. Secure AST customizer 37 ... secure.with  { //  disallow  closure

     creation closuresAllowed  =  false   //  disallow  method  definitions methodDefinitionAllowed  =  false     //  empty  white  list  =>  forbid  imports importsWhitelist  =  []   staticImportsWhitelist  =  [] //  only  allow  the  java.lang.Math.*  static  import staticStarImportsWhitelist  =  ['java.lang.Math'] ...
  62. Secure AST customizer 37 ... secure.with  { //  disallow  closure

     creation closuresAllowed  =  false   //  disallow  method  definitions methodDefinitionAllowed  =  false     //  empty  white  list  =>  forbid  imports importsWhitelist  =  []   staticImportsWhitelist  =  [] //  only  allow  the  java.lang.Math.*  static  import staticStarImportsWhitelist  =  ['java.lang.Math'] ... Disallow closures and methods
  63. Secure AST customizer 37 ... secure.with  { //  disallow  closure

     creation closuresAllowed  =  false   //  disallow  method  definitions methodDefinitionAllowed  =  false     //  empty  white  list  =>  forbid  imports importsWhitelist  =  []   staticImportsWhitelist  =  [] //  only  allow  the  java.lang.Math.*  static  import staticStarImportsWhitelist  =  ['java.lang.Math'] ... Disallow closures and methods Black / white list imports
  64. Secure AST customizer 38 ... //  language  tokens  allowed tokensWhitelist

     =  [ PLUS,  MINUS,  MULTIPLY,  DIVIDE,  MOD,  POWER,  PLUS_PLUS,  MINUS_MINUS,   COMPARE_EQUAL,  COMPARE_NOT_EQUAL,  COMPARE_LESS_THAN,  COMPARE_LESS_THAN_EQUAL,   COMPARE_GREATER_THAN,  COMPARE_GREATER_THAN_EQUAL ]   //  types  allowed  to  be  used  (including  primitive  types) constantTypesClassesWhiteList  =  [ Integer,  Float,  Long,  Double,  BigDecimal,   Integer.TYPE,  Long.TYPE,  Float.TYPE,  Double.TYPE ]   //  classes  who  are  allowed  to  be  receivers  of  method  calls receiversClassesWhiteList  =  [   Math,  Integer,  Float,  Double,  Long,  BigDecimal  ] } ...
  65. Secure AST customizer 38 ... //  language  tokens  allowed tokensWhitelist

     =  [ PLUS,  MINUS,  MULTIPLY,  DIVIDE,  MOD,  POWER,  PLUS_PLUS,  MINUS_MINUS,   COMPARE_EQUAL,  COMPARE_NOT_EQUAL,  COMPARE_LESS_THAN,  COMPARE_LESS_THAN_EQUAL,   COMPARE_GREATER_THAN,  COMPARE_GREATER_THAN_EQUAL ]   //  types  allowed  to  be  used  (including  primitive  types) constantTypesClassesWhiteList  =  [ Integer,  Float,  Long,  Double,  BigDecimal,   Integer.TYPE,  Long.TYPE,  Float.TYPE,  Double.TYPE ]   //  classes  who  are  allowed  to  be  receivers  of  method  calls receiversClassesWhiteList  =  [   Math,  Integer,  Float,  Double,  Long,  BigDecimal  ] } ... You can build a subset of the Groovy syntax!
  66. Secure AST customizer 38 ... //  language  tokens  allowed tokensWhitelist

     =  [ PLUS,  MINUS,  MULTIPLY,  DIVIDE,  MOD,  POWER,  PLUS_PLUS,  MINUS_MINUS,   COMPARE_EQUAL,  COMPARE_NOT_EQUAL,  COMPARE_LESS_THAN,  COMPARE_LESS_THAN_EQUAL,   COMPARE_GREATER_THAN,  COMPARE_GREATER_THAN_EQUAL ]   //  types  allowed  to  be  used  (including  primitive  types) constantTypesClassesWhiteList  =  [ Integer,  Float,  Long,  Double,  BigDecimal,   Integer.TYPE,  Long.TYPE,  Float.TYPE,  Double.TYPE ]   //  classes  who  are  allowed  to  be  receivers  of  method  calls receiversClassesWhiteList  =  [   Math,  Integer,  Float,  Double,  Long,  BigDecimal  ] } ... You can build a subset of the Groovy syntax! Black / white list usage of classes
  67. Secure AST customizer • Ready to evaluate our flight equations!

    • But the following would have failed: 39 shell.evaluate  'System.exit(0)' def  config  =  new  CompilerConfiguration() config.addCompilationCustomizers(imports,  secure) def  shell  =  new  GroovyShell(config)   shell.evaluate  'cos  PI/3'
  68. Back to our robot... 40 robot.move  left

  69. Back to our robot... 40 robot.move  left Still need to

    get rid of the robot prefix!
  70. Can we remove it?

  71. Can we remove it? Yes !

  72. How to get rid of the ‘robot’? • Instead of

    calling the move() method on the robot instance, we should be able to call the move() method directly from within the script • Two approaches 42 • Inject a ‘move’ closure in the binding with a method pointer • Use a base script class with a ‘move’ method delegating to the robot
  73. Inject a closure in the binding 43 def  robot  =

     new  mars.Robot() binding  =  new  Binding([        robot:  robot,        *:  Direction.values()                        .collectEntries  {                                [(it.name()):  it]                        },        move:  robot.&move ])
  74. Inject a closure in the binding 43 def  robot  =

     new  mars.Robot() binding  =  new  Binding([        robot:  robot,        *:  Direction.values()                        .collectEntries  {                                [(it.name()):  it]                        },        move:  robot.&move ]) Method pointer (a closure) on robot’s move instance method
  75. Define a base script class 44 abstract  class  RobotBaseScriptClass  extends

     Script  {        void  move(Direction  dir)  {                def  robot  =  this.binding.robot                robot.move  dir        } }
  76. Define a base script class 44 abstract  class  RobotBaseScriptClass  extends

     Script  {        void  move(Direction  dir)  {                def  robot  =  this.binding.robot                robot.move  dir        } } The move() method is now at the script level
  77. Define a base script class 44 abstract  class  RobotBaseScriptClass  extends

     Script  {        void  move(Direction  dir)  {                def  robot  =  this.binding.robot                robot.move  dir        } } The move() method is now at the script level Access the robot through the script’s binding
  78. Configure the base script class 45 def  conf  =  new

     CompilerConfiguration() conf.scriptBaseClass  =  RobotBaseScriptClass
  79. Configure the base script class 45 def  conf  =  new

     CompilerConfiguration() conf.scriptBaseClass  =  RobotBaseScriptClass Scripts evaluated with this configuration will inherit from that class
  80. Ready for lift off!    move  left

  81. Beep, beep... yes but how do you define the speed?

    ...beep...
  82. Oh no!

  83. What we could do now is... 49 move  left,  at:

     3.km/h
  84. What we could do now is... 49 move  left,  at:

     3.km/h Mix of named and normal parameters
  85. What we could do now is... 49 move  left,  at:

     3.km/h How to support this speed notation? Mix of named and normal parameters
  86. Supporting the speed notation • We need to: – define

    units of distance, time and speed • DistanceUnit and Distance • TimeUnit and Duration • Speed – have a nice notation for them by adding properties to numbers – be able to define speed thanks to operator overloading 50
  87. Distance unit enum and distance 51 enum  DistanceUnit  {  

         centimeter  ('cm',        0.01),        meter            (  'm',        1      ),          kilometer    ('km',  1000      )                  String  abbreviation        double  multiplier                DistanceUnit(String  abbr,  double  mult)  {                this.abbreviation  =  abbr                this.multiplier  =  mult          }        String  toString()  {  abbreviation  }   }
  88. Distance unit enum and distance 51 enum  DistanceUnit  {  

         centimeter  ('cm',        0.01),        meter            (  'm',        1      ),          kilometer    ('km',  1000      )                  String  abbreviation        double  multiplier                DistanceUnit(String  abbr,  double  mult)  {                this.abbreviation  =  abbr                this.multiplier  =  mult          }        String  toString()  {  abbreviation  }   } @TupleConstructor   class  Distance  {        double  amount          DistanceUnit  unit        String  toString()  {                  "$amount  $unit"          }   }
  89. Time unit enum and duration 52 enum  TimeUnit  {  

         hour            (    'h',  3600),        minute        ('min',      60),          second        (    's',        1)                  String  abbreviation        double  multiplier                TimeUnit(String  abbr,  double  mult)  {                this.abbreviation  =  abbr                this.multiplier  =  mult          }        String  toString()  {  abbreviation  }   }
  90. Time unit enum and duration 52 enum  TimeUnit  {  

         hour            (    'h',  3600),        minute        ('min',      60),          second        (    's',        1)                  String  abbreviation        double  multiplier                TimeUnit(String  abbr,  double  mult)  {                this.abbreviation  =  abbr                this.multiplier  =  mult          }        String  toString()  {  abbreviation  }   } @TupleConstructor   class  Duration  {        double  amount          TimeUnit  unit        String  toString()  {                  "$amount  $unit"          }   }
  91. Now at (light!) speed 53 @TupleConstructor   class  Speed  {

           Distance  distance        Duration  dur        String  toString()  {                  "$distance/$dur"          }   } speed = distance duration
  92. First, we need the distance notation • We add a

    dynamic property to numbers by adding a getter to them and use the property notation shortcut: 54 2.km 2.getKm() 㱻
  93. Techniques to add properties to numbers • To add dynamic

    methods or properties, there are several approaches at your disposal: – ExpandoMetaClass – custom MetaClass – Categories – Extension modules • Let’s have a look at the ExpandoMetaClass 55
  94. Using ExpandoMetaClass 56 Number.metaClass.getCm  =  {  -­‐>      

       new  Distance(delegate,  Unit.centimeter)   } Number.metaClass.getM  =  {  -­‐>          new  Distance(delegate,  Unit.meter)   } Number.metaClass.getKm  =  {  -­‐>          new  Distance(delegate,  Unit.kilometer)   }
  95. Using ExpandoMetaClass 56 Number.metaClass.getCm  =  {  -­‐>      

       new  Distance(delegate,  Unit.centimeter)   } Number.metaClass.getM  =  {  -­‐>          new  Distance(delegate,  Unit.meter)   } Number.metaClass.getKm  =  {  -­‐>          new  Distance(delegate,  Unit.kilometer)   } Add that to integration.groovy
  96. Using ExpandoMetaClass 56 Number.metaClass.getCm  =  {  -­‐>      

       new  Distance(delegate,  Unit.centimeter)   } Number.metaClass.getM  =  {  -­‐>          new  Distance(delegate,  Unit.meter)   } Number.metaClass.getKm  =  {  -­‐>          new  Distance(delegate,  Unit.kilometer)   } Add that to integration.groovy ‘delegate’ is the current number
  97. Using ExpandoMetaClass 56 Number.metaClass.getCm  =  {  -­‐>      

       new  Distance(delegate,  Unit.centimeter)   } Number.metaClass.getM  =  {  -­‐>          new  Distance(delegate,  Unit.meter)   } Number.metaClass.getKm  =  {  -­‐>          new  Distance(delegate,  Unit.kilometer)   } 40.cm   3.5.m 4.km Add that to integration.groovy ‘delegate’ is the current number Usage in your DSLs
  98. Distance okay, but speed? • For distance, we just added

    a property access after the number, but we now need to divide (‘div’) by the time 57 2.km/h
  99. Distance okay, but speed? • For distance, we just added

    a property access after the number, but we now need to divide (‘div’) by the time 57 2.km/h The div() method on Distance
  100. Distance okay, but speed? • For distance, we just added

    a property access after the number, but we now need to divide (‘div’) by the time 57 2.km/h The div() method on Distance An ‘h’ duration instance in the binding
  101. Inject the ‘h’ hour constant in the binding 58 def

     binding  =  new  Binding([        robot:  new  Robot(),        *:  Direction.values()                        .collectEntries  {                                [(it.name()):  it]                        },        h:  new  Duration(1,  TimeUnit.hour) ])
  102. Inject the ‘h’ hour constant in the binding 58 def

     binding  =  new  Binding([        robot:  new  Robot(),        *:  Direction.values()                        .collectEntries  {                                [(it.name()):  it]                        },        h:  new  Duration(1,  TimeUnit.hour) ]) An ‘h’ duration added to the binding
  103. Operator overloading • Currency amounts – 15.euros + 10.dollars •

    Distance handling – 10.km - 10.m • Workflow, concurrency – taskA | taskB & taskC • Credit an account – account << 10.dollars account += 10.dollars account.credit 10.dollars 59 a  +  b      //  a.plus(b) a  -­‐  b      //  a.minus(b) a  *  b      //  a.multiply(b) a  /  b      //  a.div(b) a  %  b      //  a.modulo(b) a  **  b    //  a.power(b) a  |  b      //  a.or(b) a  &  b      //  a.and(b) a  ^  b      //  a.xor(b) a[b]        //  a.getAt(b) a  <<  b    //  a.leftShift(b) a  >>  b    //  a.rightShift(b) a  >>>  b  //  a.rightShiftUnsigned(b) +a            //  a.unaryPlus() -­‐a            //  a.unaryMinus() ~a            //  a.bitwiseNegate()
  104. Operator overloading • Update the Distance class with a div()

    method following the naming convention for operators 60 class  Distance  {        ...        Speed  div(Duration  t)  {                new  Speed(this,  t)        }        ... }
  105. Operator overloading • Update the Distance class with a div()

    method following the naming convention for operators 60 class  Distance  {        ...        Speed  div(Duration  t)  {                new  Speed(this,  t)        }        ... } Optional return
  106. Equivalence of notation • Those two notations are actually equivalent:

    61 2.km/h 2.getKm().div(h) 㱻
  107. Equivalence of notation • Those two notations are actually equivalent:

    61 2.km/h 2.getKm().div(h) 㱻 This one might be slightly more verbose!
  108. Named parameters usage move  left,  at:  3.km/h 62

  109. Named parameters usage move  left,  at:  3.km/h 62 Normal parameter

  110. Named parameters usage move  left,  at:  3.km/h 62 Named parameter

    Normal parameter
  111. Named parameters usage move  left,  at:  3.km/h Will call: def

     move(Map  m,  Direction  q) 62 Named parameter Normal parameter
  112. Named parameters usage move  left,  at:  3.km/h Will call: def

     move(Map  m,  Direction  q) 62 Named parameter Normal parameter All named parameters go into the map argument
  113. Named parameters usage move  left,  at:  3.km/h Will call: def

     move(Map  m,  Direction  q) 62 Named parameter Normal parameter All named parameters go into the map argument Positional parameters come afterwards
  114. Named parameters usage move  left,  at:  3.km/h 63

  115. Named parameters usage move  left,  at:  3.km/h 63 Can we

    get rid of the comma?
  116. Named parameters usage move  left,  at:  3.km/h 63 Can we

    get rid of the comma? What about the colon too?
  117. Command chains • 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) 64
  118. Command chains  move  left    at  3.km/h   65

  119. Command chains  move  left    at  3.km/h   Alternation of

    method names 65
  120. Command chains  move  left    at  3.km/h   Alternation of

    method names and parameters (even named ones) 65
  121. Command chains  move  left    at  3.km/h   65

  122. Command chains  move  left    at  3.km/h      

         (        ).    (            ) Equivalent to: 65
  123. Look Ma! No parens, no dots!

  124. Command chains //  Java  fluent  API  approach class  Robot  {

           ...        def  move(Direction  dir)  {                this.dir  =  dir                return  this        }        def  at(Speed  speed)  {                this.speed  =  speed                return  this        }        ... } 67
  125. Command chains def  move(Direction  dir)  {        [at:

     {  Speed  speed  -­‐>                ...        }] } 68
  126. Command chains def  move(Direction  dir)  {        [at:

     {  Speed  speed  -­‐>                ...        }] } 68 Nested maps and closures
  127. Command chains def  move(Direction  dir)  {        [at:

     {  Speed  speed  -­‐>                ...        }] } 68 move  left  at  3.km/h Usage in your DSLs Nested maps and closures
  128. Command chains 69

  129. Command chains //  methods  with  multiple  arguments  (commas) 69

  130. Command chains //  methods  with  multiple  arguments  (commas) take  coffee

       with  sugar,  milk    and  liquor 69
  131. Command chains //  methods  with  multiple  arguments  (commas) take  coffee

       with  sugar,  milk    and  liquor //  leverage  named-­‐args  as  punctuation 69
  132. Command chains //  methods  with  multiple  arguments  (commas) take  coffee

       with  sugar,  milk    and  liquor //  leverage  named-­‐args  as  punctuation check  that:  vodka    tastes  good 69
  133. Command chains //  methods  with  multiple  arguments  (commas) take  coffee

       with  sugar,  milk    and  liquor //  leverage  named-­‐args  as  punctuation check  that:  vodka    tastes  good //  closure  parameters  for  new  control  structures 69
  134. Command chains //  methods  with  multiple  arguments  (commas) take  coffee

       with  sugar,  milk    and  liquor //  leverage  named-­‐args  as  punctuation check  that:  vodka    tastes  good //  closure  parameters  for  new  control  structures given  {}    when  {}    then  {} 69
  135. Command chains //  methods  with  multiple  arguments  (commas) take  coffee

       with  sugar,  milk    and  liquor //  leverage  named-­‐args  as  punctuation check  that:  vodka    tastes  good //  closure  parameters  for  new  control  structures given  {}    when  {}    then  {} //  zero-­‐arg  methods  require  parens 69
  136. Command chains //  methods  with  multiple  arguments  (commas) take  coffee

       with  sugar,  milk    and  liquor //  leverage  named-­‐args  as  punctuation check  that:  vodka    tastes  good //  closure  parameters  for  new  control  structures given  {}    when  {}    then  {} //  zero-­‐arg  methods  require  parens select  all    unique()  from  names 69
  137. Command chains //  methods  with  multiple  arguments  (commas) take  coffee

       with  sugar,  milk    and  liquor //  leverage  named-­‐args  as  punctuation check  that:  vodka    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 69
  138. Command chains //  methods  with  multiple  arguments  (commas) take  coffee

       with  sugar,  milk    and  liquor //  leverage  named-­‐args  as  punctuation check  that:  vodka    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 deploy  left    arm 69
  139. Command chains //  methods  with  multiple  arguments  (commas) take  coffee

       with  sugar,  milk    and  liquor //  leverage  named-­‐args  as  punctuation check  that:  vodka    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 deploy  left    arm        (            ).        (                      ).      (            ) 69
  140. Command chains //  methods  with  multiple  arguments  (commas) take  coffee

       with  sugar,  milk    and  liquor //  leverage  named-­‐args  as  punctuation check  that:  vodka    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 deploy  left    arm        (            ).        (                      ).      (            )          (                      ).            (        ) 69
  141. Command chains //  methods  with  multiple  arguments  (commas) take  coffee

       with  sugar,  milk    and  liquor //  leverage  named-­‐args  as  punctuation check  that:  vodka    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 deploy  left    arm        (            ).        (                      ).      (            )          (                      ).            (        )          (    ).        (    ).        (    ) 69
  142. Command chains //  methods  with  multiple  arguments  (commas) take  coffee

       with  sugar,  milk    and  liquor //  leverage  named-­‐args  as  punctuation check  that:  vodka    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 deploy  left    arm        (            ).        (                      ).      (            )          (                      ).            (        )          (    ).        (    ).        (    )            (      ).                .        (          ) 69
  143. Command chains //  methods  with  multiple  arguments  (commas) take  coffee

       with  sugar,  milk    and  liquor //  leverage  named-­‐args  as  punctuation check  that:  vodka    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 deploy  left    arm        (            ).        (                      ).      (            )          (                      ).            (        )          (    ).        (    ).        (    )            (      ).                .        (          )            (        ). 69
  144. Final result 70

  145. Final result 70 move  forward  at  3.km/h

  146. move forward at 3.km/h

  147. move forward at 3.km/h Yes! We did it!

  148. What about security and safety?

  149. © 2013 SpringOne 2GX. All rights reserved. Do not distribute

    without permission. Security and Safety JVM Security Managers SecureASTCustomizer Sandboxing Controlling script execution
  150. Play it safe in a sandbox

  151. Playing it safe... • You have to think carefully about

    what DSL users are allowed to do with your DSL • Forbid things which are not allowed – leverage the JVM’s Security Managers • this might have an impact on performance – use a Secure AST compilation customizer • not so easy to think about all possible cases – avoid long running scripts with *Interrupt transformations 75
  152. Security Managers • Groovy is just a language leaving on

    the JVM, so you have access to the usual Security Managers mechanism – Nothing Groovy specific here – Please check the documentation on Security Managers and how to design policy files 76
  153. SecureASTCustomizer 77 def  secure  =  new  SecureASTCustomizer() secure.with  { //

     disallow  closure  creation      closuresAllowed  =  false   //  disallow  method  definitions      methodDefinitionAllowed  =  false   //  empty  white  list  =>  forbid  certain  imports      importsWhitelist  =  [...]        staticImportsWhitelist  =  [...] //  only  allow  some  static  import      staticStarImportsWhitelist  =  [...] //  language  tokens  allowed        tokensWhitelist  =  [...] //  types  allowed  to  be  used      constantTypesClassesWhiteList  =  [...] //  classes  who  are  allowed  to  be  receivers  of  method  calls      receiversClassesWhiteList  =  [...] } def  config  =  new  CompilerConfiguration() config.addCompilationCustomizers(secure) def  shell  =  new  GroovyShell(config)
  154. Controlling code execution • Your application may run user’s code

    – what if the code runs in infinite loops or for too long? – what if the code consumes too many resources? • 3 new transforms at your rescue – @ThreadInterrupt: adds Thread#isInterrupted checks so your executing thread stops when interrupted – @TimedInterrupt: adds checks in method and closure bodies to verify it’s run longer than expected – @ConditionalInterrupt: adds checks with your own conditional logic to break out from the user code 78
  155. @ThreadInterrupt 79 @ThreadInterrupt import  groovy.transform.ThreadInterrupt     while  (true)  {

           //  Any  extraterestrial  around? }
  156. @ThreadInterrupt 79 @ThreadInterrupt import  groovy.transform.ThreadInterrupt     while  (true)  {

           //  Any  extraterestrial  around? }          if  (Thread.currentThread().isInterrupted())                throw  new  InterruptedException() { }
  157. @TimedInterrupt • InterruptedException thrown when checks indicate code ran longer

    than desired 80 @TimedInterrupt(10) import  groovy.transform.TimedInterrupt     while  (true)  {        move  left        //  circle  forever }
  158. @ConditionalInterrupt • Specify your own conditions to be inserted at

    the start of method and closure bodies – check for available resources, number of times run, etc. • Leverages closure annotation parameters 81 @ConditionalInterrupt({  battery.level  <  0.1  }) import  groovy.transform.ConditionalInterrupt 100.times  {                move  forward  at  10.km/h }
  159. @ConditionalInterrupt • Specify your own conditions to be inserted at

    the start of method and closure bodies – check for available resources, number of times run, etc. • Leverages closure annotation parameters 81 @ConditionalInterrupt({  battery.level  <  0.1  }) import  groovy.transform.ConditionalInterrupt 100.times  {                move  forward  at  10.km/h } Can we avoid typing the conditional interrupt?
  160. @ConditionalInterrupt • Specify your own conditions to be inserted at

    the start of method and closure bodies – check for available resources, number of times run, etc. • Leverages closure annotation parameters 82 100.times  {                move  forward  at  10.km/h } Yes! Using compilation customizers
  161. Using compilation customizers • In our previous examples, the usage

    of the interrupts were explicit, and users had to type them – if they want to deplete the battery of your robot, they won’t use interrupts, so you have to impose interrupts yourself • With compilation customizers you can inject those interrupts thanks to the AST Transformation Customizer 83
  162. What about tooling?

  163. © 2013 SpringOne 2GX. All rights reserved. Do not distribute

    without permission. Tooling Why tooling? DSL descriptors Pointcuts and contributions Packaging DSLDs
  164. Why tooling? • I know what this language means –why

    do I want anything more? • But, tooling can make things even better –syntax checking –content assist –search –inline documentation 86
  165. 88 Let’s use an IDE 88 • I hear Groovy-Eclipse

    is pretty good…
  166. 88 Let’s use an IDE 88 • I hear Groovy-Eclipse

    is pretty good…
  167. 88 Let’s use an IDE 88 • I hear Groovy-Eclipse

    is pretty good… Uh oh!
  168. 88 Let’s use an IDE 88 • I hear Groovy-Eclipse

    is pretty good… Uh oh! Can we do better?
  169. Of course! • Eclipse is extensible – with a plugin

    architecture 88 Eclipse platform Platform runtime WorkBench JFace SWT Workspace Help Team New plugin New tool
  170. I want my DSL supported in Eclipse 89

  171. I want my DSL supported in Eclipse • Let’s create

    a plugin – create a plugin project – extend an extension point – write the code – build the plugin – host on an update site – convince people to install it 89
  172. I want my DSL supported in Eclipse • Let’s create

    a plugin – create a plugin project – extend an extension point – write the code – build the plugin – host on an update site – convince people to install it • Problems – I don’t want to learn Eclipse APIs – I want an easy way for users to install the DSL support – I need a specific plugin version for my specific DSL version 89
  173. I want my DSL supported in Eclipse • Let’s create

    a plugin – create a plugin project – extend an extension point – write the code – build the plugin – host on an update site – convince people to install it • Problems – I don’t want to learn Eclipse APIs – I want an easy way for users to install the DSL support – I need a specific plugin version for my specific DSL version 89 Uh oh!
  174. I want my DSL supported in Eclipse • Let’s create

    a plugin – create a plugin project – extend an extension point – write the code – build the plugin – host on an update site – convince people to install it • Problems – I don’t want to learn Eclipse APIs – I want an easy way for users to install the DSL support – I need a specific plugin version for my specific DSL version 89 Uh oh! Can we do better?
  175. Of course! • Groovy is extensible! – Meta-Object Protocol –

    Metaprogramming – DSLs... 90
  176. DSL Descriptors • Teach the IDE about DSLs through a

    Groovy DSL 91
  177. DSL Descriptors • Teach the IDE about DSLs through a

    Groovy DSL • Benefits – Powerful – Uses Groovy syntax, semantics, and APIs – No knowledge of Eclipse required – Can ship with Groovy libraries 91
  178. DSL Descriptors • Teach the IDE about DSLs through a

    Groovy DSL • Benefits – Powerful – Uses Groovy syntax, semantics, and APIs – No knowledge of Eclipse required – Can ship with Groovy libraries 91 DSL Descriptors (DSLD)
  179. DSL Descriptors • Teach the IDE about DSLs through a

    Groovy DSL • Benefits – Powerful – Uses Groovy syntax, semantics, and APIs – No knowledge of Eclipse required – Can ship with Groovy libraries 91 DSL Descriptors (DSLD) In IntelliJ. called GDSL
  180. Let’s start simple 92 move deploy h left right forward

    backward
  181. Let’s start simple • In English: – When the type

    is this, add the following properties/methods • move, deploy, h, etc from binding • Direction from import customizer 92 move deploy h left right forward backward
  182. Let’s start simple • In English: – When the type

    is this, add the following properties/methods • move, deploy, h, etc from binding • Direction from import customizer • In DSLD: – When the type is this contribute( isThisType() ) {…} – …properties/methods… property name: left, type: 'v11.Direction' … method name: move, type: 'java.util.Map<…>' 92 move deploy h left right forward backward
  183. Let’s start simple • In English: – When the type

    is this, add the following properties/methods • move, deploy, h, etc from binding • Direction from import customizer • In DSLD: – When the type is this contribute( isThisType() ) {…} – …properties/methods… property name: left, type: 'v11.Direction' … method name: move, type: 'java.util.Map<…>' 92 Pointcut move deploy h left right forward backward
  184. Let’s start simple • In English: – When the type

    is this, add the following properties/methods • move, deploy, h, etc from binding • Direction from import customizer • In DSLD: – When the type is this contribute( isThisType() ) {…} – …properties/methods… property name: left, type: 'v11.Direction' … method name: move, type: 'java.util.Map<…>' 92 Pointcut Contribution block move deploy h left right forward backward
  185. Anatomy of a DSLD script •Pointcuts – Where to do

    it – What is the current expression? – Current type? – Enclosing class? •Contribution blocks – What to do – « Add » method – « Add » property – Delegate to another type 93
  186. Anatomy of a DSLD script •Pointcuts – Where to do

    it – What is the current expression? – Current type? – Enclosing class? •Contribution blocks – What to do – « Add » method – « Add » property – Delegate to another type 93 Where
  187. Anatomy of a DSLD script •Pointcuts – Where to do

    it – What is the current expression? – Current type? – Enclosing class? •Contribution blocks – What to do – « Add » method – « Add » property – Delegate to another type 93 Where What
  188. Anatomy of a DSLD script •Pointcuts – Where to do

    it – What is the current expression? – Current type? – Enclosing class? •Contribution blocks – What to do – « Add » method – « Add » property – Delegate to another type 93 Not at runtime... only while editing Where What
  189. Talking about « x » 94 class  Other  {  }

    class  Foo  {        def  method()  {                def  x  =  new  Other()                x.nuthin        } }
  190. Talking about « x » 94 class  Other  {  }

    class  Foo  {        def  method()  {                def  x  =  new  Other()                x.nuthin        } } Current type
  191. Talking about « x » 94 class  Other  {  }

    class  Foo  {        def  method()  {                def  x  =  new  Other()                x.nuthin        } } Current type Enclosing class
  192. Talking about « x » 94 class  Other  {  }

    class  Foo  {        def  method()  {                def  x  =  new  Other()                x.nuthin        } } Current type Enclosing class Enclosing method
  193. Pointcuts 95

  194. Pointcuts 95 currentType()        //  matches  on  current

     declaring  type
  195. Pointcuts 95 currentType()        //  matches  on  current

     declaring  type isScript()              //  matches  on  the  enclosing  script
  196. Pointcuts 95 currentType()        //  matches  on  current

     declaring  type isScript()              //  matches  on  the  enclosing  script currentType("groovy.dsl.Robot")
  197. Pointcuts 95 currentType()        //  matches  on  current

     declaring  type isScript()              //  matches  on  the  enclosing  script currentType("groovy.dsl.Robot") currentType(subType("groovy.dsl.Robot"))
  198. Pointcuts 95 currentType()        //  matches  on  current

     declaring  type isScript()              //  matches  on  the  enclosing  script currentType("groovy.dsl.Robot") currentType(subType("groovy.dsl.Robot")) currentType(method("move"))
  199. Pointcuts 95 currentType()        //  matches  on  current

     declaring  type isScript()              //  matches  on  the  enclosing  script currentType("groovy.dsl.Robot") currentType(subType("groovy.dsl.Robot")) currentType(method("move")) currentType(annotatedBy("groovy.dsl.Robotic"))
  200. Pointcuts 95 currentType()        //  matches  on  current

     declaring  type isScript()              //  matches  on  the  enclosing  script currentType("groovy.dsl.Robot") currentType(subType("groovy.dsl.Robot")) currentType(method("move")) currentType(annotatedBy("groovy.dsl.Robotic")) //  combining  them,  and  using  the  logical  and
  201. Pointcuts 95 currentType()        //  matches  on  current

     declaring  type isScript()              //  matches  on  the  enclosing  script currentType("groovy.dsl.Robot") currentType(subType("groovy.dsl.Robot")) currentType(method("move")) currentType(annotatedBy("groovy.dsl.Robotic")) //  combining  them,  and  using  the  logical  and isScript(        annotatedBy("groovy.dsl.Robotic")
  202. Pointcuts 95 currentType()        //  matches  on  current

     declaring  type isScript()              //  matches  on  the  enclosing  script currentType("groovy.dsl.Robot") currentType(subType("groovy.dsl.Robot")) currentType(method("move")) currentType(annotatedBy("groovy.dsl.Robotic")) //  combining  them,  and  using  the  logical  and isScript(        annotatedBy("groovy.dsl.Robotic") )  &  currentType(method("move"))
  203. What goes in a contribution block? 96

  204. What goes in a contribution block? • property: “adds” a

    property – name: “myName” (REQUIRED) – type: “java.lang.String” – declaringType: ”com.foo.Frumble” – doc: “Some JavaDoc” 96
  205. What goes in a contribution block? • property: “adds” a

    property – name: “myName” (REQUIRED) – type: “java.lang.String” – declaringType: ”com.foo.Frumble” – doc: “Some JavaDoc” • method: “adds” a method – all arguments above, and – params: [ firstName: “java.lang.String”, lastName: “java.lang.String” ] – namedParams, optionalParams 96
  206. What goes in a contribution block? • property: “adds” a

    property – name: “myName” (REQUIRED) – type: “java.lang.String” – declaringType: ”com.foo.Frumble” – doc: “Some JavaDoc” • method: “adds” a method – all arguments above, and – params: [ firstName: “java.lang.String”, lastName: “java.lang.String” ] – namedParams, optionalParams • delegatesTo: “delegates” invocations to another type – type (REQUIRED) 96
  207. What goes in a contribution block? • property: “adds” a

    property – name: “myName” (REQUIRED) – type: “java.lang.String” – declaringType: ”com.foo.Frumble” – doc: “Some JavaDoc” • method: “adds” a method – all arguments above, and – params: [ firstName: “java.lang.String”, lastName: “java.lang.String” ] – namedParams, optionalParams • delegatesTo: “delegates” invocations to another type – type (REQUIRED) 96 contribute(...)  {    property  name:  "myName"    method  name:  "getMyName"    delegatesTo  type:              "some.other.Type" }
  208. Wait... isn’t this Aspect-Oriented Programming? 97

  209. Wait... isn’t this Aspect-Oriented Programming? • Pointcut – Intentionally borrowed

    from AOP 97
  210. Wait... isn’t this Aspect-Oriented Programming? • Pointcut – Intentionally borrowed

    from AOP • AspectJ: pointcuts and advice – operates on Java instructions at runtime 97
  211. Wait... isn’t this Aspect-Oriented Programming? • Pointcut – Intentionally borrowed

    from AOP • AspectJ: pointcuts and advice – operates on Java instructions at runtime • DSLD: pointcuts and contribution blocks – operates on AST in the editor org.codehaus.groovy.ast.expr.* 97
  212. Wait... isn’t this Aspect-Oriented Programming? • Pointcut – Intentionally borrowed

    from AOP • AspectJ: pointcuts and advice – operates on Java instructions at runtime • DSLD: pointcuts and contribution blocks – operates on AST in the editor org.codehaus.groovy.ast.expr.* • Join Point Model – Join points (e.g., instructions, expressions) – Mechanism for quantifying join points (e.g., pointcuts) – Means of affect at a join point (e.g., advice, contribution blocks) 97
  213. How do we ship it? 98 • jar/war file •

    DSLD file: – as source in dsld package • Hint: – Use script folder support in preferences – **/*.dsld to be copied to bin folder as source • Can also use maven or gradle
  214. To summarize: Editing support for DSLs • Getting it out

    there – include a dsld package in your JAR – add the DSLD for your DSL to the package as source – ship it! 99 DSLD contribution blocks pointcuts
  215. To summarize: Editing support for DSLs • Getting it out

    there – include a dsld package in your JAR – add the DSLD for your DSL to the package as source – ship it! 99 DSLD contribution blocks pointcuts Where
  216. To summarize: Editing support for DSLs • Getting it out

    there – include a dsld package in your JAR – add the DSLD for your DSL to the package as source – ship it! 99 DSLD contribution blocks pointcuts Where What
  217. © 2013 SpringOne 2GX. All rights reserved. Do not distribute

    without permission. What have we learnt?
  218. Groovy Power!™ • A flexible and malleable syntax – scripts

    vs classes, optional typing, colons and parens • Groovy offers useful dynamic features for DSLs – operator overloading, ExpandoMetaClass • Can write almost plain natural language sentences – for readable, concise and expressive DSLs • Groovy DSLs are easy to integrate, and can be secured to run safely in your own sandbox • Groovy DSLs can be tooled for improved authoring capabilities 101
  219. Groovy Power!™ • A flexible and malleable syntax – scripts

    vs classes, optional typing, colons and parens • Groovy offers useful dynamic features for DSLs – operator overloading, ExpandoMetaClass • Can write almost plain natural language sentences – for readable, concise and expressive DSLs • Groovy DSLs are easy to integrate, and can be secured to run safely in your own sandbox • Groovy DSLs can be tooled for improved authoring capabilities 101 Groovy is a great fit for DSLs!
  220. And there’s more! • We haven’t dived into... – How

    to implement your own control structures with closures – How to create Groovy « builders » – How to define extension modules – How to hijack the Groovy syntax to develop our own language extensions with AST Transformations – Source preprocessing for custom syntax – How to use the other metaprogramming techniques available – How to improve error reporting with customizers 102
  221. Thank you! 103

  222. Questions & Answers 104 Got questions, really?

  223. Questions & Answers 104 Got questions, really? I might have

    answers!
  224. Image credits • Wikipedia logo: http://www.geekosystem.com/wp-content/uploads/2011/01/wikipedia-logo.png • Chains: http://2.bp.blogspot.com/-GXDVqUYSCa0/TVdBsON4tdI/AAAAAAAAAW4/EgJOUmAxB28/s1600/breaking-chains5_copy9611.jpg •

    Space odissey: http://dearjesus.files.wordpress.com/2010/04/2001_a_space_odyssey_1.jpg • Yes we scan: http://i.huffpost.com/gen/1218045/thumbs/o-YES-WE-SCAN-facebook.jpg • HAL red: http://2.bp.blogspot.com/-yjsyPxUFicY/TcazwAltOaI/AAAAAAAAAho/GVT7wGhnrUM/s1600/2001-a-space-odyssey-HAL.jpg • USSR Space posters: http://www.flickr.com/photos/justinvg • General: http://www.defense.gov/dodcmsshare/newsphoto/2009-02/hires_090217-D-7203C-004.jpg • Rocket: http://astro.vision.free.fr/download/fonds/7/saturn5c.jpg • Star Trek / 747: http://24.media.tumblr.com/tumblr_m35foiJI6a1qzz0iho1_1280.jpg • Man in space: http://www.nasa.gov/images/content/60130main_image_feature_182_jwfull.jpg • Sputnik 2: http://launiusr.files.wordpress.com/2010/06/sputnik2.jpg • Lunakod: http://www.astr.ua.edu/keel/space/lunakhod_moscow.jpg • Sandbox: http://www.turnbacktogod.com/wp-content/uploads/2008/09/sandbox.jpg • Repair: http://www.oneangels.com/wp-content/uploads/2012/03/repair1.jpg • Mars rover: http://wallpapers.free-review.net/wallpapers/49/Mars_rover%2C_Mars_-_03.jpg • Mars rover 2: http://www.universetoday.com/wp-content/uploads/2011/06/551038main_pia14156-43_946-710.jpg • Thumb: http://www.wpclipart.com/sign_language/thumbs_up_large.png.html • Night sky: http://www.aplf-planetariums.info/galeries/ciel_profond/2004-07-01-Voie_Lactee_Scorpion-Jean-Luc_PUGLIESI.jpg • Obama yes we can: http://www.dessinemoiunboulon.net/wp-content/uploads/2009/01/obama-yes-we-can_04-nov-08.jpg • Hook: http://winningware.com/blog/wp-content/uploads/2009/12/FishHookXSmall.jpg • HP 48 GX: http://calculators.torensma.net/files/images/hewlett-packard_hp-48g.jpg • Omer: http://www.irmin.com/wallpaper/TV/Homer%20Simpson%20Oh%20No.jpg • Cadenat: http://acsgsecurite.com/upl/site/cadenat.png • Thanks: http://4.bp.blogspot.com/-hTdT5Ebk5ws/Tu_x2tE4ccI/AAAAAAAAAZc/pxtG8A0w7VE/s1600/thanks-digital-calligraphy-md.png • Buzz Aldrin: http://2.bp.blogspot.com/-rpV5Oy5N78U/TprVli-2ZdI/AAAAAAAABN8/WiHob4rp2b8/s1600/Astronaut.jpg 105