Going to Mars with Groovy Domain-Specific Languages

43,154 views
38,469 views

Published on

Practical example of implementing and evolving a simple DSL in Groovy, to drive a robot rover on the planet Mars.

Published in: Technology
8 Comments
82 Likes
Statistics
Notes
  • This is real take it serious, who will believe that a herb can cure herpes, i navel believe that this will work i have spend a lot when getting drugs from the hospital to keep me healthy, what i was waiting for is death because i was broke, one day i hard about this great man who is well know of HIV and cancer cure, i decided to email him, unknowingly to me that this will be the end of the herpes in my body, he prepare the herb for me, and give me instruction on how to take it, at the end of the one month, he told me to go to the hospital for a check up, and i went, surprisingly after the test the doctor confirm me negative, i thought it was a joke, i went to other hospital was also negative, then i took my friend who was also herpes positive to the Dr Agumagu, after the treatment she was also confirm negative . He also have the herb to cure cancer. please i want every one with this virus to be free, that is why am dropping his email address, agumaguspelltemple@outlook.com or agumaguspelltemple@gmail.com do email him he is a great man. the government is also interested in this DR, thank you for saving my life, and I promise I will always testify for your good work.call his number +233200116937.
       Reply 
    Are you sure you want to  Yes  No
    Your message goes here
  • Future of Banking, Trend and Fintech forecast 2016-2100. http://www.slideshare.net/ishmelev/bank-future
       Reply 
    Are you sure you want to  Yes  No
    Your message goes here
  • hey Guillaume why we need importcustomizer???
       Reply 
    Are you sure you want to  Yes  No
    Your message goes here
  • Great !
       Reply 
    Are you sure you want to  Yes  No
    Your message goes here
  • Impressive!
       Reply 
    Are you sure you want to  Yes  No
    Your message goes here
No Downloads
Views
Total views
43,154
On SlideShare
0
From Embeds
0
Number of Embeds
2,649
Actions
Shares
0
Downloads
494
Comments
8
Likes
82
Embeds 0
No embeds

No notes for slide

Going to Mars with Groovy Domain-Specific Languages

  1. GroovyDomainSpecific Guillaume Laforge Groovy Project Manager SpringSource / VMwareLanguages @glaforge 1
  2. ГИЙОМЛАФОРЖ
  3. Guillaume Laforge• Groovy Project Manager at VMware •Initiator of the Grails framework •Creator of the Gaelyk and Caelyf toolkits• Co-author of Groovy in Action• Follow me on... •My blog: http://glaforge.appspot.com •Twitter: @glaforge •Google+: http://gplus.to/glaforge 3
  4. IntroductionDefinition, Examples, Goals, Pros & Cons 4
  5. 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 • Also known as: fluent / human interfaces, language oriented programming, little or mini languages, macros, business natural languages... 5
  6. Technical examplesGlade 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> <Signal> <xsl:element name="{name()}"> Regex <xsl:for-each select="@*"> <name>destroy</name> <handler>gtk_main_quit</handler> <xsl:element name="{name()}"> <xsl:value-of select="."/> "x.z?z{1,3}y" </Signal> <title>Hello</title> </xsl:element> <type>GTK_WINDOW_TOPLEVEL</type> </xsl:for-each> <position>GTK_WIN_POS_NONE</position> <xsl:apply-templates select="*|text()"/> <allow_shrink>True</allow_shrink> </xsl:element> <allow_grow>True</allow_grow> </xsl:template> <auto_shrink>False</auto_shrink> </xsl:stylesheet> <widget> <class>GtkButton</class> <name>Hello World</name> <can_focus>True</can_focus> fetchmail <label>Hello World</label> # Poll this site first each cycle. </widget> poll pop.provider.net proto pop3</widget> user "jsmith" with pass "secret1" is "smith" here</GTK-Interface> user jones with pass "secret2" is "jjones" here with options keep SQL # 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 SELECT * FROM TABLE # Poll this site third in the cycle. WHERE NAME LIKE %SMI # Password will be fetched from ~/.netrc ORDER BY NAME poll mailhost.net with proto imap: user esr is esr here
  7. Antimalaria drug Insurance policy risk HR skills representationresistance simulation calculation engine Nuclear safety simulationsMarket data feeds analysis Loan acceptance rules engine
  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 Cons • Domain experts can help, validate, • Learning cost vs. limited applicability modify, and often develop DSL programs • Cost of designing, implementing & maintaining DSLs as well as tools/IDEs • Somewhat self-documenting • Attaining proper scope • Enhance quality, productivity, • Trade-offs between domain specificity reliability, maintainability, portability, and general purpose language reusability constructs • Safety; as long as the language • Efficiency cost constructs are safe, any DSL sentence can be considered safe • 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. Let’s get started! 11
  12. Your mission: build a DSL for a Mars robot
  13. We need a robot! class!Robot!{} 14
  14. It should move... class!Robot!{ !!!!void)move()!{} } 15
  15. ..in a direction! class!Robot!{ !!!!void)move(String!dir)!{} } 16
  16. More explicit direction class!Robot!{ !!!!void)move(Direction!dir)!{} } enum!Direction!{ !!!!left,!right,!forward,!backward } 17
  17. Now how can we control it?import)static)mars.Direction.*;import)mars.Robot;public)class)Command!{))))public)static)void)main(String[]!args)!{!!!!!!!!Robot!robot!=)new!Robot();!!!!!!!!robot.move(left);!!!!}} 18
  18. Now how can we control it?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! 18
  19. Now how can we control it?))))))))))))))))))))))))))))))—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! 18
  20. Scripts vs ClassesOptional semicolons & parensimport)static)mars.Direction.*import)mars.Robot!!!!!!!!def))!robot!=)new!Robot()!!!!!!!!robot.move!left 19
  21. Scripts vs ClassesOptional semicolons & parensimport)static)mars.Direction.*import)mars.Robot Optional typing!!!!!!!!def))!robot!=)new!Robot()!!!!!!!!robot.move!left 19
  22. Scripts vs ClassesOptional semicolons & parensimport)static)mars.Direction.*import)mars.Robot Optional typing!!!!!!!!def))!robot!=)new!Robot()!!!!!!!!robot.move!left But I don’t want to compile a script for every command! 19
  23. Integration
  24. GroovyShell to the rescue 21
  25. GroovyShell to the rescue def$shell%=$new%GroovyShell() shell.evaluate( %%%%new%File("command.groovy") ) 21
  26. GroovyShell to the rescue def$shell%=$new%GroovyShell() shell.evaluate( integration.groovy %%%%new%File("command.groovy") ) 21
  27. GroovyShell to the rescue def$shell%=$new%GroovyShell() shell.evaluate( integration.groovy %%%%new%File("command.groovy") )import$static$mars.Direction.*import$mars.Robotdef$robot%=$new%Robot()robot.move%left 21
  28. GroovyShell to the rescue def$shell%=$new%GroovyShell() shell.evaluate( integration.groovy %%%%new%File("command.groovy") )import$static$mars.Direction.*import$mars.Robot command.groovydef$robot%=$new%Robot()robot.move%left 21
  29. Integration mechanisms• Different solutions available: •Groovy’s own mechanisms • GroovyScriptEngine, Eval, GroovyClassLoader, GroovyShell •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
  30. Integration mechanisms• Different solutions available: •Groovy’s own mechanisms • GroovyScriptEngine, Eval, GroovyClassLoader, GroovyShell •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. What’s wrong with our DSL? import)static)mars.Direction.* import)mars.Robot def)robot!=)new!Robot() robot.move!left 23
  32. What’s wrong with our DSL? Can’t we hide those imports? import)static)mars.Direction.* import)mars.Robot def)robot!=)new!Robot() robot.move!left 23
  33. What’s wrong with our DSL? Can’t we hide those imports? import)static)mars.Direction.* import)mars.Robot def)robot!=)new!Robot() robot.move!left Can’t we inject the robot? 23
  34. What’s wrong with our DSL? Can’t we hide those imports? import)static)mars.Direction.* import)mars.Robot def)robot!=)new!Robot() robot.move!left Can’t we inject Do we really need to the robot? repeat ‘robot’? 23
  35. I’m sorry Dave,you can’t do that!
  36. I’m sorry Dave,you can’t do that!
  37. What we really want is... !move!left! 25
  38. Let’s inject a robot!• We can pass data in / out of scripts through the Binding •it’s basically just like a map of variable name keys and their associated values 26
  39. Let’s inject a robot!• We can pass data in / out of scripts through the Binding •it’s basically just like a map of variable name keys and their associated values def)binding!=)new!Binding([ !!!!robot:!new!Robot() ]) def)shell!=)new!GroovyShell(binding) shell.evaluate( !!!!new!File("command.groovy") ) 26
  40. Let’s inject a robot!• We can pass data in / out of scripts through the Binding •it’s basically just like a map of variable name keys and their associated values integration.groovy def)binding!=)new!Binding([ !!!!robot:!new!Robot() ]) def)shell!=)new!GroovyShell(binding) shell.evaluate( !!!!new!File("command.groovy") ) 26
  41. Better? import)static)mars.Direction.* robot.move!left 27
  42. Better?Robot import removed import)static)mars.Direction.* robot.move!left 27
  43. Better?Robot import removed import)static)mars.Direction.* robot.move!left Robot injected, no ‘new’ needed 27
  44. How to inject the direction?• Using the binding... 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") ) 28
  45. How to inject the direction? Fragile in case of new directions!• Using the binding... 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") ) 28
  46. How to inject the direction?• Using the binding... def)binding!=)new!Binding([ !!!!robot:!new!Robot(), !!!!*:!Direction.values() !!!!!!!!!!!!.collectEntries!{ !!!!!!!!!!!!!!!![(it.name()):!it] !!!!!!!!!!!!} ]) def)shell!=)new!GroovyShell(binding) shell.evaluate( !!!!new!File("command.groovy") ) 29
  47. How to inject the direction?• Using compiler customizers...• Let’s have a look at them 30
  48. 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 31
  49. Imports customizer new!GroovyShell(new!Binding([robot:!new)Robot()])) !!!!.evaluate("import!static!Direction.*n"!+ !!!!!!!!!!!!!!"robot.move!left") 32
  50. Imports customizer Cheat with string concatenation? Bad! new!GroovyShell(new!Binding([robot:!new)Robot()])) !!!!.evaluate("import!static!Direction.*n"!+ !!!!!!!!!!!!!!"robot.move!left") 32
  51. Imports customizer new!GroovyShell(new!Binding([robot:!new)Robot()])) !!!!.evaluate("import!static!Direction.*n"!+ !!!!!!!!!!!!!!"robot.move!left") 32
  52. Imports customizer def!configuration!=!new!CompilerConfiguration() ! def!imports!=!new!ImportCustomizer() imports.addStaticStar(mars.Direction.name) configuration.addCompilationCustomizers(imports)! ! new!GroovyShell(new!Binding([robot:!new)Robot()]),!!!!!!! ! !!!!!!!!!!!!!!!!configuration) !!!!.evaluate("robot.move!left")!!!!!! 33
  53. AST transformation customizer 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)Robot()]),!!!!!!! ! !!!!!!!!!!!!!!!!configuration) !!!!.evaluate("robot.move!left"!+!"n" !!!!!!!!!!!!!!"log.info!‘Robot!moved’")!!!!!!!!!!! 34
  54. AST transformation customizer 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)Robot()]),!!!!!!! ! !!!!!!!!!!!!!!!!configuration) !!!!.evaluate("robot.move!left"!+!"n" !!!!!!!!!!!!!!"log.info!‘Robot!moved’")!!!!!!!!!!!@Log injects a logger in scripts and classes 34
  55. Secure the onboardtrajectory calculator
  56. Secure AST customizer Idea: Secure the rocket’s onboard trajectory calculation system by allowing only math expressions to be evaluated by the calculator• Let’s setup our environment •an import customizer to import java.lang.Math.* •prepare a secure AST customizerdef%imports%=%new%ImportCustomizer().addStaticStars(java.lang.Math)def%secure%=%new%SecureASTCustomizer() 36
  57. Secure AST customizer...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]... 37
  58. Secure AST customizer Disallow closures... and methodssecure.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]... 37
  59. Secure AST customizer Disallow closures... and methodssecure.with!{ //!disallow!closure!creation closuresAllowed!=!false! //!disallow!method!definitions Black / white list methodDefinitionAllowed!=!false! imports! //!empty!white!list!=>!forbid!imports importsWhitelist!=![]! staticImportsWhitelist!=![] //!only!allow!the!java.lang.Math.*!static!import staticStarImportsWhitelist!=![java.lang.Math]... 37
  60. Secure AST customizer... //%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%]}... 38
  61. Secure AST customizer You can build a subset of the Groovy syntax!... //%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%]}... 38
  62. Secure AST customizer You can build a subset of the Groovy syntax!... //%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) Black / white list constantTypesClassesWhiteList%=%[ usage of classes 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%]}... 38
  63. Secure AST customizer• Ready to evaluate our flight equations! def%config%=%new%CompilerConfiguration() config.addCompilationCustomizers(imports,%secure) def%shell%=%new%GroovyShell(config) % shell.evaluate%cos%PI/3• But the following would have failed: shell.evaluate%System.exit(0) 39
  64. Back to our robot... robot.move)left 40
  65. Back to our robot... Still need to get rid of the robot prefix! robot.move)left 40
  66. Can we remove it?
  67. Can we remove it? да !
  68. How to remove 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: • Inject a ‘move’ closure in the • Use a base script class with a ‘move’ method delegating to the binding with a method pointer robot 42
  69. Inject a closure in the binding def$robot%=%new%Robot() binding%=$new%Binding([ %%%%robot:%robot, %%%%*:%Direction.values() %%%%%%%%%%%%.collectEntries%{ %%%%%%%%%%%%%%%%[(it.name()):%it] %%%%%%%%%%%%}, %%%%move:%robot.&move ]) 43
  70. Inject a closure in the binding def$robot%=%new%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 43
  71. Define a base script class abstract)class!RobotBaseScriptClass!!! !!!!!!!extends!Script!{ !!!!void!move(Direction!dir)!{ !!!!!!!!def)robot!=!this.binding.robot !!!!!!!!robot.move!dir !!!!} } 44
  72. Define a base script class abstract)class!RobotBaseScriptClass!!! !!!!!!!extends!Script!{ !!!!void!move(Direction!dir)!{ !!!!!!!!def)robot!=!this.binding.robot !!!!!!!!robot.move!dir !!!!} }The move() method isnow at the script level 44
  73. Define a base script class abstract)class!RobotBaseScriptClass!!! !!!!!!!extends!Script!{ !!!!void!move(Direction!dir)!{ !!!!!!!!def)robot!=!this.binding.robot !!!!!!!!robot.move!dir !!!!} }The move() method is Access the robot throughnow at the script level the script’s binding 44
  74. Configure the base script classdef)conf)=)new)CompilerConfiguration()conf.scriptBaseClass)=)RobotBaseScriptClass 45
  75. Configure the base script classdef)conf)=)new)CompilerConfiguration()conf.scriptBaseClass)=)RobotBaseScriptClass Scripts evaluated with this configuration will inherit from that class 45
  76. Ready for lift off! %%move%left
  77. Beep, beep...yes but how do you define the speed? ...beep...
  78. Oh no!
  79. What we could do now is... move!left,!at:!3.km/h 49
  80. What we could do now is... Mix of named and normal parameters move!left,!at:!3.km/h 49
  81. What we could do now is... Mix of named and normal parameters move!left,!at:!3.km/h How to add a km property to numbers? 49
  82. Adding properties to numbers• We need to: • define units, distance and speed • have a nice notation for them • that’s where we add properties to numbers! 50
  83. Unit enum and Distance classenum$DistanceUnit%{%%%%centimeter%(cm,%%%%0.01),%%%%meter%%%%%%(%m,%%%%1),%%%%%kilometer%%(km,%1000)%%%%%%%%%String%abbreviation%%%%double%multiplier%%%%%%%%Unit(String%abbr,%double%mult)%{%%%%%%%%this.abbreviation%=%abbr%%%%%%%%this.multiplier%=%mult%%%%%}%%%%String%toString()%{%abbreviation%}%} 51
  84. Unit enum and Distance class @TupleConstructor%enum$DistanceUnit%{ class%Distance%{%%%%centimeter%(cm,%%%%0.01), %%%%double%amount%%%%%meter%%%%%%(%m,%%%%1),% %%%%DistanceUnit%unit%%%%kilometer%%(km,%1000)%%%%% %%%%String%toString()%{%%%%%String%abbreviation %%%%%%%%"$amount%$unit"%%%%%double%multiplier %%%%}%%%%% }%%%%Unit(String%abbr,%double%mult)%{%%%%%%%%this.abbreviation%=%abbr%%%%%%%%this.multiplier%=%mult%%%%%}%%%%String%toString()%{%abbreviation%}%} 51
  85. Different techniques• To add dynamic methods or properties, there are several approaches at your disposal: • ExpandoMetaClass • custom MetaClass • Categories• Let’s have a look at the ExpandoMetaClass 52
  86. Using ExpandoMetaClass Number.metaClass.getCm%=%{%f>% %%%%new%Distance(delegate,%Unit.centimeter)% } Number.metaClass.getM%=%{%f>% %%%%new%Distance(delegate,%Unit.meter)% } Number.metaClass.getKm%=%{%f>% %%%%new%Distance(delegate,%Unit.kilometer)% } 53
  87. Using ExpandoMetaClass Add that tointegration.groovy Number.metaClass.getCm%=%{%f>% %%%%new%Distance(delegate,%Unit.centimeter)% } Number.metaClass.getM%=%{%f>% %%%%new%Distance(delegate,%Unit.meter)% } Number.metaClass.getKm%=%{%f>% %%%%new%Distance(delegate,%Unit.kilometer)% } 53
  88. Using ExpandoMetaClass Add that tointegration.groovy Number.metaClass.getCm%=%{%f>% %%%%new%Distance(delegate,%Unit.centimeter)% } Number.metaClass.getM%=%{%f>% %%%%new%Distance(delegate,%Unit.meter)% } Number.metaClass.getKm%=%{%f>% %%%%new%Distance(delegate,%Unit.kilometer)% } ‘delegate’ is the current number 53
  89. Using ExpandoMetaClass Add that tointegration.groovy Usage in your DSLs Number.metaClass.getCm%=%{%f>% %%%%new%Distance(delegate,%Unit.centimeter)% } 40.cm! Number.metaClass.getM%=%{%f>% %%%%new%Distance(delegate,%Unit.meter)% 3.5.m } 4.km Number.metaClass.getKm%=%{%f>% %%%%new%Distance(delegate,%Unit.kilometer)% } ‘delegate’ is the current number 53
  90. 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 2.km/h 54
  91. 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 The div() method on Distance 2.km/h 54
  92. 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 The div() method on Distance 2.km/h An h duration instance in the binding 54
  93. First, let’s look at timeenum$TimeUnit%{%%%%hour%%%%%%(h,%%%3600),%%%%minute%%%%(min,%%%60),%%%%%kilometer%(s,%%%%%%1)%%%%%%%%%String%abbreviation%%%%double%multiplier%%%%%%%%TimeUnit(String%abbr,%double%mult)%{%%%%%%%%this.abbreviation%=%abbr%%%%%%%%this.multiplier%=%mult%%%%%}%%%%String%toString()%{%abbreviation%}%} 55
  94. First, let’s look at time @TupleConstructor% class%Duration%{enum$TimeUnit%{ %%%%double%amount%%%%%hour%%%%%%(h,%%%3600), %%%%TimeUnit%unit%%%%minute%%%%(min,%%%60),%%%%%kilometer%(s,%%%%%%1)% %%%%String%toString()%{%%%%% %%%%%%%%"$amount%$unit"%%%%%String%abbreviation %%%%}%%%%%double%multiplier }%%%%%%%%TimeUnit(String%abbr,%double%mult)%{%%%%%%%%this.abbreviation%=%abbr%%%%%%%%this.multiplier%=%mult%%%%%}%%%%String%toString()%{%abbreviation%}%} 55
  95. Inject the ‘h’ hour constant in the binding def)binding!=)new!Binding([ !!!!robot:!new!Robot(), !!!!*:!Direction.values() !!!!!!!!!!!!.collectEntries!{ !!!!!!!!!!!!!!!![(it.name()):!it] !!!!!!!!!!!!}, !!!!h:!new!Duration(1,!TimeUnit.hour) ]) 56
  96. Now at (light!) speed @TupleConstructor) class)Speed){ ))))Distance)distance ))))Duration)duration ))))String)toString()){) ))))))))"$distance/$duration") ))))}) } 57
  97. Operator overloading a%+%b%%//%a.plus(b) a%f%b%%//%a.minus(b) • Currency amounts a%*%b%%//%a.multiply(b) • 15.euros + 10.dollars a%/%b%%//%a.div(b) a%%%b%%//%a.modulo(b) • Distance handling a%**%b%//%a.power(b) • 10.km - 10.m a%|%b%%//%a.or(b) a%&%b%%//%a.and(b) • Workflow, concurrency a%^%b%%//%a.xor(b) • taskA | taskB & taskC a[b]%%%//%a.getAt(b) a%<<%b%//%a.leftShift(b) • Credit an account a%>>%b%//%a.rightShift(b) +a%%%%%//%a.unaryPlus() • account << 10.dollars account += 10.dollars fa%%%%%//%a.unaryMinus() account.credit 10.dollars ~a%%%%%//%a.bitwiseNegate() 58
  98. Operator overloading• Update the Distance class with a div() method following the naming convetion for operators class%Distance%{ %%%%... %%%%Speed%div(Duration%t)%{ %%%%%%%%new%Speed(this,%t) %%%%} %%%%... } 59
  99. Operator overloading• Update the Distance class with a div() method following the naming convetion for operators class%Distance%{ %%%%... %%%%Speed%div(Duration%t)%{ %%%%%%%%new%Speed(this,%t) %%%%} %%%%... } Optional return 59
  100. Equivalence of notation• Those two notations are actually equivalent: 2.km/h 2.getKm().div(h) 60
  101. Equivalence of notation• Those two notations are actually equivalent: 2.km/h This one might be slightly more verbose! 2.getKm().div(h) 60
  102. Named parameters usage move!left,!at:!3.km/h 61
  103. Named parameters usage move!left,!at:!3.km/h Normal parameter 61
  104. Named parameters usage move!left,!at:!3.km/h Normal Named parameter parameter 61
  105. Named parameters usage move!left,!at:!3.km/h Normal Named parameter parameter Will call: def!take(Map!m,!Direction!q) 61
  106. Named parameters usage move!left,!at:!3.km/h Normal Named parameter parameter Will call: def!take(Map!m,!Direction!q) All named parameters go into the map argument 61
  107. Named parameters usage move!left,!at:!3.km/h Normal Named parameter parameter Will call: def!take(Map!m,!Direction!q) All named parameters go Positional parameters into the map argument come afterwards 61
  108. Named parameters usage move!left,!at:!3.km/h 62
  109. Named parameters usage move!left,!at:!3.km/h Can we get rid of the comma? 62
  110. Named parameters usage move!left,!at:!3.km/h Can we get rid of What about the the comma? colon too? 62
  111. Command chains• A grammar improvement in Groovy 1.8 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) 63
  112. Command chains %move%left%%at%3.km/h% 64
  113. Command chains Alternation of method names %move%left%%at%3.km/h% 64
  114. Command chains Alternation of method names %move%left%%at%3.km/h% and parameters (even named ones) 64
  115. Command chains %move%left%%at%3.km/h% 64
  116. Command chains Equivalent to: %%%%%(%%%%).%%(%%%%%%) %move%left%%at%3.km/h% 64
  117. Look Ma!No par ens, no dots!
  118. 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 %%%%} %%%%... } 66
  119. Command chains def)move(Direction)dir)){ ))))[at:){)Speed)speed)F> ))))))))))))... ))))))))}] ))))}] } 67
  120. Command chains def)move(Direction)dir)){ ))))[at:){)Speed)speed)F> ))))))))))))... Nested maps ))))))))}] and closures ))))}] } 67
  121. Command chains def)move(Direction)dir)){ ))))[at:){)Speed)speed)F> ))))))))))))... Nested maps ))))))))}] and closures ))))}] } Usage in your DSLs move%left%at%3.km/h 67
  122. Command chains 68
  123. Command chains //%methods%with%multiple%arguments%(commas) 68
  124. Command chains //%methods%with%multiple%arguments%(commas) take%coffee%%with%sugar,%milk%%and%liquor 68
  125. Command chains //%methods%with%multiple%arguments%(commas) take%coffee%%with%sugar,%milk%%and%liquor //%leverage%namedfargs%as%punctuation 68
  126. Command chains //%methods%with%multiple%arguments%(commas) take%coffee%%with%sugar,%milk%%and%liquor //%leverage%namedfargs%as%punctuation check%that:%vodka%%tastes%good 68
  127. Command chains //%methods%with%multiple%arguments%(commas) take%coffee%%with%sugar,%milk%%and%liquor //%leverage%namedfargs%as%punctuation check%that:%vodka%%tastes%good //%closure%parameters%for%new%control%structures 68
  128. Command chains //%methods%with%multiple%arguments%(commas) take%coffee%%with%sugar,%milk%%and%liquor //%leverage%namedfargs%as%punctuation check%that:%vodka%%tastes%good //%closure%parameters%for%new%control%structures given%{}%%when%{}%%then%{} 68
  129. Command chains //%methods%with%multiple%arguments%(commas) take%coffee%%with%sugar,%milk%%and%liquor //%leverage%namedfargs%as%punctuation check%that:%vodka%%tastes%good //%closure%parameters%for%new%control%structures given%{}%%when%{}%%then%{} //%zerofarg%methods%require%parens 68
  130. Command chains //%methods%with%multiple%arguments%(commas) take%coffee%%with%sugar,%milk%%and%liquor //%leverage%namedfargs%as%punctuation check%that:%vodka%%tastes%good //%closure%parameters%for%new%control%structures given%{}%%when%{}%%then%{} //%zerofarg%methods%require%parens select%all%%unique()%from%names 68
  131. Command chains //%methods%with%multiple%arguments%(commas) take%coffee%%with%sugar,%milk%%and%liquor //%leverage%namedfargs%as%punctuation check%that:%vodka%%tastes%good //%closure%parameters%for%new%control%structures given%{}%%when%{}%%then%{} //%zerofarg%methods%require%parens select%all%%unique()%from%names //%possible%with%an%odd%number%of%terms 68
  132. Command chains //%methods%with%multiple%arguments%(commas) take%coffee%%with%sugar,%milk%%and%liquor //%leverage%namedfargs%as%punctuation check%that:%vodka%%tastes%good //%closure%parameters%for%new%control%structures given%{}%%when%{}%%then%{} //%zerofarg%methods%require%parens select%all%%unique()%from%names //%possible%with%an%odd%number%of%terms deploy%left%%arm 68
  133. Command chains //%methods%with%multiple%arguments%(commas) take%coffee%%with%sugar,%milk%%and%liquor %%%%(%%%%%%).%%%%(%%%%%%%%%%%).%%%(%%%%%%) //%leverage%namedfargs%as%punctuation check%that:%vodka%%tastes%good //%closure%parameters%for%new%control%structures given%{}%%when%{}%%then%{} //%zerofarg%methods%require%parens select%all%%unique()%from%names //%possible%with%an%odd%number%of%terms deploy%left%%arm 68
  134. Command chains //%methods%with%multiple%arguments%(commas) take%coffee%%with%sugar,%milk%%and%liquor %%%%(%%%%%%).%%%%(%%%%%%%%%%%).%%%(%%%%%%) //%leverage%namedfargs%as%punctuation check%that:%vodka%%tastes%good %%%%%(%%%%%%%%%%%).%%%%%%(%%%%) //%closure%parameters%for%new%control%structures given%{}%%when%{}%%then%{} //%zerofarg%methods%require%parens select%all%%unique()%from%names //%possible%with%an%odd%number%of%terms deploy%left%%arm 68
  135. Command chains //%methods%with%multiple%arguments%(commas) take%coffee%%with%sugar,%milk%%and%liquor %%%%(%%%%%%).%%%%(%%%%%%%%%%%).%%%(%%%%%%) //%leverage%namedfargs%as%punctuation check%that:%vodka%%tastes%good %%%%%(%%%%%%%%%%%).%%%%%%(%%%%) //%closure%parameters%for%new%control%structures given%{}%%when%{}%%then%{} %%%%%(%%).%%%%(%%).%%%%(%%) //%zerofarg%methods%require%parens select%all%%unique()%from%names //%possible%with%an%odd%number%of%terms deploy%left%%arm 68
  136. Command chains //%methods%with%multiple%arguments%(commas) take%coffee%%with%sugar,%milk%%and%liquor %%%%(%%%%%%).%%%%(%%%%%%%%%%%).%%%(%%%%%%) //%leverage%namedfargs%as%punctuation check%that:%vodka%%tastes%good %%%%%(%%%%%%%%%%%).%%%%%%(%%%%) //%closure%parameters%for%new%control%structures given%{}%%when%{}%%then%{} %%%%%(%%).%%%%(%%).%%%%(%%) //%zerofarg%methods%require%parens select%all%%unique()%from%names %%%%%%(%%%).%%%%%%%%.%%%%(%%%%%) //%possible%with%an%odd%number%of%terms deploy%left%%arm 68
  137. Command chains //%methods%with%multiple%arguments%(commas) take%coffee%%with%sugar,%milk%%and%liquor %%%%(%%%%%%).%%%%(%%%%%%%%%%%).%%%(%%%%%%) //%leverage%namedfargs%as%punctuation check%that:%vodka%%tastes%good %%%%%(%%%%%%%%%%%).%%%%%%(%%%%) //%closure%parameters%for%new%control%structures given%{}%%when%{}%%then%{} %%%%%(%%).%%%%(%%).%%%%(%%) //%zerofarg%methods%require%parens select%all%%unique()%from%names %%%%%%(%%%).%%%%%%%%.%%%%(%%%%%) //%possible%with%an%odd%number%of%terms deploy%left%%arm %%%%%%(%%%%). 68
  138. Final result 69
  139. Final result move!forward!at!3.km/h 69
  140. move forward at 3.km/h
  141. move forward at 3.km/h Yes! We did it!
  142. What about security and safety?
  143. Security and safety JVM Security Managers SecureASTCustomizer Sandboxing Controlling scripts execution 72
  144. Play it safe in a sandbox
  145. 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 74
  146. 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 75
  147. SecureASTCustomizer 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) 76
  148. 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 77
  149. @ThreadInterrupt @ThreadInterrupt import%groovy.transform.ThreadInterrupt% % while%(true)%{ %%%%//%Any%extraterestrial%around? } 78
  150. @ThreadInterrupt @ThreadInterrupt import%groovy.transform.ThreadInterrupt% % while%(true)%{{ %%%%if%(Thread.currentThread.isInterrupted()) %%%%%%%%throw$new%InterruptedException() } %%%%//%Any%extraterestrial%around? } 78
  151. @TimedInterrupt @TimedInterrupt(10) import!groovy.transform.TimedInterrupt! ! while!(true)!{ !!!!move!left !!!!//!circle!forever }• InterruptedException thrown when checks indicate code ran longer than desired 79
  152. @ConditionalInterrupt• Specify your own condition to be inserted at the start of method and closure bodies • check for available resources, number of times run, etc.• Leverages closure annotation parameters from Groovy 1.8 @ConditionalInterrupt({$battery.level$<$O.1$}) import%groovy.transform.ConditionalInterrupt 100.times%{%%%% %%%%move%forward%at%10.km/h } 80
  153. Using compilation customizers• In our previous three 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 ASTTransformationCustomizer 81
  154. What have we learnt? 82
  155. 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 83
  156. Groovy Power!™ Groovy is a great fit for DSLs!• 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 83
  157. And there’s more!• We haven’t dived into... •How to implement your own control structures with the help of closures •How to create Groovy « builders » •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 dynamic metaprogramming techniques available •How to improve error reporting with customizers •IDE support with DSL descriptors (GDSL and DSLD) 84
  158. Thank you! ge e Lafor lopment Gui llaum ovy Deve Head of Gro om @ gmail.c laforge rge Email: g @glafo o/glaforg e Twitter : http://gplus.t spot.com : G oogle+ //glaforge.app p: B log: htt 85
  159. Questions & AnswersGot questions, really? 86
  160. Questions & Answers I might have answers!Got questions, really? 86
  161. Image credits• Pills: http://www.we-ew.com/wp-content/uploads/2011/01/plan-b-pills.jpg• Chains: http://2.bp.blogspot.com/-GXDVqUYSCa0/TVdBsON4tdI/AAAAAAAAAW4/EgJOUmAxB28/s1600/breaking-chains5_copy9611.jpg• Russian flag: http://good-wallpapers.com/pictures/4794/Russian%20Flag.jpg• Space odissey: http://dearjesus.files.wordpress.com/2010/04/2001_a_space_odyssey_1.jpg• HAL red: http://2.bp.blogspot.com/-yjsyPxUFicY/TcazwAltOaI/AAAAAAAAAho/GVT7wGhnrUM/s1600/2001-a-space-odyssey-HAL.jpg• Back in time: http://4.bp.blogspot.com/-Pt44Dk9J2EM/TrQx9YNmVcI/AAAAAAAAAk4/ivWw9Lja05k/s1600/clocky.jpg• USSR Space posters: http://www.flickr.com/photos/justinvg• Russian General: http://rickrozoff.files.wordpress.com/2012/02/general-nikolai-makarov.jpg• Rocket: http://blog.al.com/breaking/2009/03/soyuz_launch.jpg• Progress capsule: http://www.spacedaily.com/images/progress-ocean-desk-1024.jpg• Buran: http://www.buran.fr/mriya-antonov/Photos//050-Exhibition%20au%20Bourget%20avec%20Bourane-Airshow%20with%20Buran%20at%20Le%20Bourget-1134884.jpg• Man in space: http://vintagespace.files.wordpress.com/2010/11/voskhod-2_leonov.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 87

×