310 likes | 439 Views
Domain Specific Languages. Based on http://martinfowler.com/dslwip. [A-Za-z][A-Za-z0-9_]*. PROGRAMS=ex1_connect ex2_select all: $(PROGRAMS) clean: rm -f $(PROGRAMS) *.o include $(ORACLE_HOME)/rdbms/lib/env_rdbms.mk OPTIMIZE=$(OPTIMIZEG) .SUFFIXES: .o .cc .cc.o:
E N D
Domain Specific Languages Based on http://martinfowler.com/dslwip
PROGRAMS=ex1_connect ex2_select all: $(PROGRAMS) clean: rm -f $(PROGRAMS) *.o include $(ORACLE_HOME)/rdbms/lib/env_rdbms.mk OPTIMIZE=$(OPTIMIZEG) .SUFFIXES: .o .cc .cc.o: $(CCC) $(CFLAGSCC) -c $< ex1_connect: ex1_connect.o $(LINKCC) ex1_connect.o $(SHARED_OCCILIBS) ex2_select: ex2_select.o $(LINKCC) ex2_select.o $(SHARED_OCCILIBS)
<?xml version="1.0" encoding="ISO-8859-1"?> <web-app> <display-name>IMDB</display-name> <description>Internet Movie Database</description> <context-param> <param-name>db-url</param-name> <param-value>www.imdb.com/datacenter</param-value> </context-param> <servlet> <servlet-name>search</servlet-name> <servlet-class>my.package.SearchServlet</servlet-class> </servlet> <servlet-mapping> <servlet-name>search</servlet-name> <url-pattern>/search.html</url-pattern> </servlet-mapping> </web-app>
<menubar> <item name="File"> <item name="Open" mnemonic="O">commands.Open</item> <item name="Save" mnemonic="S">commands.Save</item> <item name="Save As" mnemonic="A">commands.SaveAs</item> </item> <item name="Edit"> <item name="Cut" mnemonic="T">commands.Cut</item> <item name="Copy" mnemonic="C">commands.Copy</item> <item name="Paste" mnemonic="P">commands.Paste</item> </item> </menubar>
last.file.name=c:\\files\\input.dat x=105 y=99 width=527 height=401
DSL • A language designed for solving a specific class of problems • “Do one thing well” • Less powerful than a general-purpose language (GPL) • Usually not Turing complete • If it’s not simpler to use than a GPL – no reason to use it
Motivation for DSLs • Initially: Customizability • Currently: Productivity
DSL: Customizability • Allow users to customize a program • Without recompiling it • Imagine: Unix’s make without the DSL • Make will be a library • User will write a program that uses the library • Will use API for defining the dependencies, actions, etc. • Compile & Run • In the good old days compilation was an issue
DSL: Productivity • In its domain a DSL is more productive than a general purpose language • Because it does one thing well • Example: String matching • Very easy with regular expressions • Much harder with manually written Java code • The direct result: DSLs are all over the place • Even if no user-customizability is needed
DSL Oriented Development • Development that focuses on building and using DSLs to develop a system • Various DSLs float around – each one solves a particular problem within the broader context • One cannot build a whole program with one DSL • Don’t overdo it – implementing a DSL takes effort
External DSL A DSL represented in a separate language to the host programming language
External DSL Highlights • Syntax • Custom-made • (Or) Follows a common format CSV, XML, etc. • Stages • Parsing and tree Construction • Semantic Model construction • Execution • Not all stages must exist • Developer may choose to do execution while parsing
[{ name: "File", shortcut:'F', children: [ { name:"Open", shortcut:'o', action:"p1.Main$FileOpen"}, { name:"Exit", shortcut:'x', action:"p1.Main$FileExit"} ] }, { name: "Edit", shortcut:'E', children: [ { name:"Cut", shortcut:'t', action:"p1.Main$EditCut"}, { name:"Paste", shortcut:'p', action:"p1.Main$EditPaste"} ] }]
Theory of Compilation & DSLs • Compilation courses often focus on advanced parsing issues • In practice, simple recursive descent parsers are fine • Even lexical scanners are not always needed • => No need to use parser generators • If you do want to use them: modern tools (Antlr, Javacc) are much simpler than Lex/Yacc • Bottom line: don’t panic
Internal DSL A DSL expressed within the syntax of a general purpose language. A stylized use of the language for a domain specific purpose.
Menu Definition API public class Menu { public Menu add(Menu m) { // add m as a child return this; } } public Menu item(String name){ ... } public Menu item(String name, char mnemonic) } ... } public Menu item(String name, char accelerator) } ... } public Menu item(String name, char mnemonic, char accelerator) } ... } public Menu item(String name, char mnemonic, ActionListener l) { ... } public Menu menubar(){ return new Menu(); }
Defining a Menu menubar() .add( item("File", 'f') .add(item("Open", 'o', new FileOpen())) .add(item("Exit", 'o', new FileExit()))) .add( item("Edit", 'e') .add(item("Cut", 'c', new EditCut())) .add(item("Paste", 'p', new EditPaste())));
Difficulties of the Proposed API • Order of parameters • Too many parenthesis • Must create an item() method for each subset of desired menu properties
A Better API public interface MenuBuilder { public MenuBuilder child(String name); public MenuBuilder shortcut(char c); public MenuBuilder action(ActionListener al); public MenuBuilder up(); // ... and a few more }
Defining a Menu (Again) new MenuBuilder() .child("File").shortcut('f') .child("Open") .shortcut('o').action(new FileOpen()) .up() .child("Exit") .shortcut('x').action(new FileExit()) .up() .up() .child("Edit").shortcut('e') .child("Cut") .shortcut('c').action(new EditCut()) .up() .child("Paste") .shortcut('p').action(new EditPaste()) .up() .up()
Internal DSL Highlights • Syntax • Based on the API • Heavy use of fluent interfaces • Stages • Semantic Model construction • Execution • Not all stages must exist • API may do the actual execution when called
Comparison • Pros: External DSL • Full control over syntax, semantics • User customizability • Pros: Internal DSL • Integration with the host language • IDE support
Rake File • Rake: A build system for the Ruby world • Similar to Java’s Ant, Unix’s Make • As expected uses a DSL for building program • Here’s a sample Rake file • Q: is this an internal or external DSL?
require 'rake/clean' PROG = "foo" LIBNAME = PROG LIBFILE = "lib#{LIBNAME}.a" SRC = FileList['**/*.c'] OBJDIR = 'obj' OBJ = SRC.collect { |fn| File.join(OBJDIR, File.basename(fn).ext('o')) } CLEAN.include(OBJ, OBJDIR, LIBFILE) CLOBBER.include(PROG) task :default => [:build, :run] # continued on next slide
# continued from previous slide task :build => [PROG] task :run => [PROG] do sh "./#{PROG}" end file PROG => [LIBFILE] do sh "cc -o #{PROG} -L . -l#{LIBNAME}" end file LIBFILE => OBJ do sh "ar cr #{LIBFILE} #{OBJ}" sh "ranlib #{LIBFILE}" end
DSL & Dynamic Languages • Dynamic languages are flexible • Syntax • Offer new constructs • Redefining semantics of existing constructs • Thus, they can support a wide spectrum of internal DSL syntaxes • The best of both worlds • Integration with the host language • IDE support • User customizability • (Almost) Flexible syntax
Language Workbench • A design approach • For a heavily used DSL • Multiple formats • Multiple editors • Minimal duplication of the DSL logic
The Trio • Abstract representation • Editors + Bi-directional translators • Generator(s) • Change of focus • From the DSL syntax to the abstract representation
Summary • DSL is likely to be more productive than a GPL • wrt to its domain • Developer can reduce development time • If they have a good set of DSLs • DSL oriented development • User can customize the program w/o recompiling • A DSL for the user may ends up being used by the programmer