280 likes | 424 Views
A Little Language for Surveys: Constructing an Internal DSL in Ruby. H. Conrad Cunningham Computer and Information Science University of Mississippi. Domain-Specific Language (DSL).
E N D
A Little Language for Surveys: Constructing an Internal DSL in Ruby H. Conrad Cunningham Computer and Information ScienceUniversity of Mississippi
Domain-Specific Language (DSL) • “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” – van Deursen, Klint, and Visser • “little language” – J. Bentley
Typical DSL Characteristics • Not general-purpose • Small (at least initially) • Declarative • Semantically expressive of domain • Targeted at domain specialists, not necessarily programmers
External or Internal External: different from main programming language • make, yacc, pic, XML-based config files Internal (embedded): constrained use of the main programming language • use of Lisp macros, Haskell algebraic types, metaprogramming in Ruby, etc.
Defining Internal DSLs in Ruby (1 of 2) Flexible syntax enables convenient DSL statements • optional parentheses on method calls • variable number of arguments question “Male or female?” # has optional second arg
Defining Internal DSLs in Ruby (2 of 2) Blocks (closures) provide DSL structuring and delayed execution • anonymous function definitions • passed as arguments question “Male or female?” do response “Male” response “Female” do @fem = true end end
Implementing Internal DSLs in Ruby (1 of 2) Reflexive metaprogramming: writing programs that manipulate themselves as data • obj.instance_eval(str) executes string str as Ruby code in context of object obj • execute DSL statements dynamically • mod.class_eval(str) executes string str as Ruby code in context of module mod • declare new methods and classes dynamically
Implementing Internal DSLs in Ruby (2 of 2) • obj.method_missing(sym,*args) invoked when undefined method sym called with arguments args • take remedial action • obj.send(sym,*args) calls method sym on object obj with arguments args • dynamically “send a message”
Little Language for Surveys Domain: Specification and presentation of simple, multiple-choice surveys Tasks: • Analyze domain for language design • Design Ruby internal DSL • Implement DSL
Commonality/Variability Analysis • Determine domain boundaries (scope) • Define specialized terms and concepts (terminology) • Identify unchanging features among domain members (commonalities) • Identify features that may change among domain members (variabilities)
Scope • Support definition of simple, multiple-choice surveys • specification of survey • presentation of survey and collection of responses • Exclude tabulation of aggregate results for now
Terms and Commonalities • Survey has a title • Survey has a sequence of questions • Question has a sequence of responses • Use of conditional question depends upon previous responses • Response to silent question determined from previous responses • Survey execution presents appropriate questions to respondent and collects choices of responses
Variabilities • Texts for title, questions, and responses • Number and order of questions within survey • Number of responses expected for question • Number and order of responses in question • Condition under which question included • Method for answering silent question • Source of survey specification • Method for displaying questions and collecting responses during execution
DSL Design Strategy • Adopt declarative approach – structure explicit but processing implicit • Use terminology and commonalities to suggest language statements • Represent variabilities as values that survey programmers supply
Survey DSL Example (1 of 3) # C1 survey has title (V1) title “ACMSE Survey” # C2 survey has seq of questions (V2) question “Are you an author?” do # (V3) # C3 question has seq of responses (V4) response "Yes" do # execute if chosen @author = true end response “No” do # execute if chosen @author = false end end
Survey DSL Example (2 of 3) # C4 conditional question (V5) question "What type of paper?" do condition { @author } # when execute? response "Regular" do @p = :rg end response "Student" do @p = :st end response "Work-in-progress" do @p = :wp end # V5/V6 block on response & action sets # state for conditions & silent choices action {@t = 25; @t = 15 if @p == :wp} end
Survey DSL Example (3 of 3) # C5 silent question calculates choice (V6) result "How long is presentation?“ do condition { @author } alternative "25" do # when choose? @p == :rp || @p == :sp end alternative “15” do # when choose? @p == :wp end end
Two-Pass Implementation • Parse DSL and build abstract syntax tree (AST) • Execute survey by traversing AST
First Pass: DSL Parsing • Use instance_eval to execute DSL input as Ruby code (V7) • Let Ruby interpreter do most of parsing • Add methods for each DSL statement – title, question, action, etc. • Check specialized syntax and build AST as 3-level tree (survey, question, response) • Defer conditions and actions by storing unevaluated blocks
Second Pass: DSL Interpretation • Traverse AST to display questions and collect responses • Execute deferred blocks needed for conditions and actions • Use missing_method and class_eval to create reader/writer methods for variables in blocks
Architecture First pass – use Object Scoping and Context Variable patterns for safety and flexibility, Deferred Evaluation for actions Second pass – use Visitor pattern for flexibility (C6, V8)
Summary • Illustrated how commonality/variability analysis can be adapted for DSL design • Demonstrated how Ruby facilities can be used for internal DSL development • Explored how design patterns can help lead to safe and flexible DSL processors
Future Work • More systematic techniques to explore domain and discover needed constructs • Improved runtime error handling tied to DSL input • Better facilities for user extension • Investigation and comparison of other languages – Groovy, Scala, Haskell
Acknowledgements • Members of Fall 2006 graduate class on Ruby and Software Development • Suggestions on the paper by several current and former students