Successfully reported this slideshow.
We use your LinkedIn profile and activity data to personalize ads and to show you more relevant ads. You can change your ad preferences anytime.

Turtle Graphics in Groovy

1,023 views

Published on

Using DSL techniques to recreate the children's learning tool

Published in: Technology
  • Be the first to comment

  • Be the first to like this

Turtle Graphics in Groovy

  1. 1. <Insert Picture Here> Turtle Graphics in Groovy Jim Driscoll
  2. 2. 2 My Background • ADFm – Part of Oracle's ADF Framework – Provides data binding and database access – Provides ways to customize data views via Groovy – Groovy uses have basic DSL customizations • Wanted to Learn JavaFX (after last year's GR8 Conf) • Have 5 year old daughter • Napili – named after the Maui bay
  3. 3. 3 Logo • An early programming language (1960's) • Popular in the 80's as a teaching tool – Which is how I first heard about it • A dialect of Lisp (but much more approachable) • Currently (per Wikipedia) 197 different dialects • Most famous for it's use of Turtle Graphics
  4. 4. 4 Turtle Graphics • Vector based graphics using a relative cursor – The “Turtle” – Represented in early work as a triangle • Turtles have three attributes – A location – An orientation – A “pen” (which has on/off state, color, width, etc)
  5. 5. 5 Turtle Commands • home • left / right (takes int arg, degrees) • forward / back (takes int arg, #pixels) • penup / pendown • pencolor (takes Color arg) • show / hide • speed (takes int arg)
  6. 6. 6 Language Style • Commands without parens – left 90, home, hide, etc • Chained Commands – left 90 forward 100 hide (as one line) • Use with an explicit turtle, or with an implicit turtle • Use Groovy language constructs – Define variables – Define methods – Closures
  7. 7. 7 Language Decisions – DSL vs GPL • DSL (Domain Specific Language) – A limited language, designed to be useful for a specific task – HTML, SQL, stuff you can build with Groovy Builders • GPL (General Purpose Language) – A general language, designed to be useful for any task – Java, Groovy, Assembler, etc • I went the GPLish route, full Groovy + DSL extensions – But there's not necessarily a one right answer
  8. 8. 8 Demo
  9. 9. 9 First, Create a Framework • Created a Turtle class, with supporting classes – Methods like #home(), #hide(), #forward(int), etc. • Allows drawing on a canvas via a turtle • Used JavaFX, including animations – This isn't a JavaFX talk... • Once that's all working, add the control language
  10. 10. 10 Executing Programs In Memory • Object GroovyShell#evaluate(String) – Can be slow (10ms) • Cacheing Scripts – Script GroovyShell#parse(String) ← slow part (10ms) – Object Script#run() ← fast part (<1ms) GroovyShell shell = new GroovyShell(); Script script = shell.parse("println 'hello world'"); script.run();
  11. 11. 11 Bindings – the Nouns • A Binding is a way to pass values in/out of a script – Used as unqualified variables (“println name”) – Declared varaibles (def) go into a separate space • Groovy provides a default Binding – But you can provide one of your own • Use it to provide – Constant values – Default values – Error checked values test = 1 println test def test = 0 println test binding.test
  12. 12. 12 Bindings in Napili • Little used – But could be used much more • Just two variables, turtle and out • out – Special variable to redirect println output • turtle – Used for direct calls on the turtle • turtle.penup(), turtle.forward(100) will now work
  13. 13. 13 Initialize Bindings in Napili static class BasicBinding extends Binding { Turtle turtle public BasicBinding() { super(); turtle = new Turtle() }
  14. 14. 14 Getting Binding Values public Object getVariable(String name) { if (name == 'out') { return NapiliOutput.getPrintWriter(); } if (name == 'turtle') { return turtle; } return super.getVariable(name) }
  15. 15. 15 Setting Binding Values public void setVariable(String name, Object value) { if ("turtle".equals(name)) { NapiliOutput.println('Unable to set "turtle" to value' + value) return; } super.setVariable(name, value); }
  16. 16. 16 Using the Binding GroovyShell shell = new GroovyShell(config) Script script = shell.parse(scriptStr) script.setBinding(new BasicBinding()) script.run()
  17. 17. 17 Adding Imports • Can add imports and static imports to scripts • Allows easy usage of existing classes • Added via CompilerConfiguration – An optional parameter to GroovyShell • Static imports can let you define variables – import static java.lang.Math.PI lets “println PI” work
  18. 18. 18 Adding Imports in Napili CompilerConfiguration config = new CompilerConfiguration() ImportCustomizer ic = new ImportCustomizer(); ic.addImports('javafx.scene.paint.Color') config.addCompilationCustomizers(ic) GroovyShell shell = new GroovyShell(config) • Adds an automatic import for JavaFX's Color so... • turtle.pencolor(Color.Purple) now works • If we instead do a static import, we could just say Purple – ImportCustomizer#addStarImports(String)
  19. 19. 19 ScriptBaseClass – the Verbs • Dynamically assign a new base class • Allows you to declare top level functions • Inserted as part of the CompilerConfiguration • Has access to Binding variables – Can be used for implicit parameter passing
  20. 20. 20 ScriptBaseClass – example abstract class BaseScript extends Script { def forward(int i) { turtle.forward(i) } def right(int i) { turtle.right(i) } }
  21. 21. 21 InvokeMethod • Allows you to dynamically route method calls – Lets you use a context object – GroovyInterceptable interface • Just add a #invokeMethod(String,args) function def invokeMethod(String name, args) { println name } def test() {println “hey”} hello() test()
  22. 22. 22 MethodMissing • Allows you to specify new behavior if a method isn't found • Just add a #methodMissing(String,args) function • Combined with a ScriptBaseClass...
  23. 23. 23 ScriptBaseClass in Napili abstract class TurtleDelegateBaseScript extends Script { // if we can't find the method, look for it on the turtle binding object def methodMissing(String name, args) { binding.turtle."$name"(* args) } } • Abstract class • Effectively reroutes all calls through Turtle
  24. 24. 24 ScriptBaseClass in Napili CompilerConfiguration config = new CompilerConfiguration() config.setScriptBaseClass("fullpath.TurtleDelegateBaseScript") GroovyShell shell = new GroovyShell(config) • Now, “forward(100)” works • Because of optional parens, “forward 100” works too • But “home()” is required, “home” won't work...
  25. 25. 25 No arg Methods • But what about “home” or “penup”? • They're seen as Binding variables • So, use the Binding to execute them: public Object getVariable(String name) { ... // treat all no-arg methods on Turtle as binding variables with side effects if (turtle.metaClass.respondsTo(turtle,name)) { return turtle.metaClass.invokeMethod(turtle, name) } return super.getVariable(name) }
  26. 26. 26 No arg Methods as Properties • Use #propertyMissing(String) on Turtle • Allows turtle.hide instead of turtle.hide() def propertyMissing(String name) { // treat all no-arg methods as properties if (this.metaClass.respondsTo(this, name)) { return this.metaClass.invokeMethod(this, name) } throw new MissingPropertyException("No property on Turtle named '$name'") }
  27. 27. 27 Timeout protection • What about while (true) {}? • groovy.transform.TimedInterrupt CompilerConfiguration config = new CompilerConfiguration() config.setScriptBaseClass("org.netdance.napili.language.TurtleDelegateBaseScript") ImportCustomizer ic = new ImportCustomizer(); ic.addImports('javafx.scene.paint.Color') def ti = new ASTTransformationCustomizer([value: Napili.TIMEOUT],TimedInterrupt) config.addCompilationCustomizers(ic, ti) GroovyShell shell = new GroovyShell(config)
  28. 28. 28 Recap • Create a Turtle object • Make it available to scripts via the Binding • Use ScriptBaseClass to route all method calls through it • ImportCustomizer to allow using JavaFX Color • TimedInterrupt to stop runaway processes
  29. 29. 29 Method Chaining • You may have noticed.... – forward 100 back 100 hide • It's harder than it looks – first second third fourth == first(second).third(fourth) – first second third fourth fifth == first(second).third(fourth).fifth – GroovyConsole is great for understanding the parse rules • No arg methods break that flow – forward 100 hide home ← doesn't work
  30. 30. 30 Removing Language Features • SecureASTCustomizer – Static Analysis – def s = Shell; s.exit(0) – (See my talk this afternoon for more on security) • Can be used to remove language features
  31. 31. 31 Removing Increment Operator (++) def config = new CompilerConfiguration(); def customizer = new SecureASTCustomizer(); def tokens = [] tokens.add(Types.PLUS_PLUS); customizer.setTokensBlacklist(tokens); config.addCompilationCustomizers(customizer); def shell = new GroovyShell(config);
  32. 32. 32 Removing For Loops def config = new CompilerConfiguration(); def customizer = new SecureASTCustomizer(); def statements = [] statements.add(ForStatement.class); customizer.setStatementsBlacklist(statements); config.addCompilationCustomizers(customizer); def shell = new GroovyShell(config);
  33. 33. 33 Q&A
  34. 34. 34 Resources • Napili Code – https://github.com/netdance/Napili • My Blog – https://jamesgdriscoll.wordpress.com/ • Books – Groovy in Action, 2nd ed (still in early access, but worth getting) – Groovy for Domain Specific Languages – DSLs in Action (multiple language examples)
  35. 35. 35 The preceding is intended to outline our general product direction. It is intended for information purposes only, and may not be incorporated into any contract. It is not a commitment to deliver any material, code, or functionality, and should not be relied upon in making purchasing decisions. The development, release, and timing of any features or functionality described for Oracle’s products remains at the sole discretion of Oracle.
  36. 36. 36

×