building dsls with the spoofax language workbench

149
Building DSLs with The Spoofax Language Workbench Eelco Visser Delft University of Technology http://eelcovisser.org Joint work with Lennart Kats, Karl-Trygve Kalleberg, Maartje de Jonge, and many others

Upload: eelco-visser

Post on 05-Dec-2014

1.908 views

Category:

Technology


7 download

DESCRIPTION

Slides for my keynote talk at Code Generation 2010

TRANSCRIPT

Page 1: Building DSLs with the Spoofax Language Workbench

Building DSLs with The Spoofax Language Workbench

Eelco VisserDelft University of Technology

http://eelcovisser.org

Joint work with Lennart Kats, Karl-Trygve Kalleberg, Maartje de Jonge, and many others

Page 2: Building DSLs with the Spoofax Language Workbench

The Meta-Languages Wars

Internal DSLs vs External DSLs

Textual vs Graphical

Partial vs Complete Code Generation

Project-Specific vs Multi-Project DSLs

Code Generation vs Interpretation

Dynamically vs Statically Typed

Page 3: Building DSLs with the Spoofax Language Workbench

The Meta-Languages Wars

Internal DSLs vs External DSLs

Textual vs Graphical

Partial vs Complete Code Generation

Project-Specific vs Multi-Project DSLs

Code Generation vs Interpretation

Dynamically vs Statically Typed

Page 5: Building DSLs with the Spoofax Language Workbench

1,190,303 publications

http://researchr.org

Page 6: Building DSLs with the Spoofax Language Workbench

correct & extend

publication records

Page 7: Building DSLs with the Spoofax Language Workbench

authorprofiles

Page 8: Building DSLs with the Spoofax Language Workbench

bibliographies

tagging

reputation system

access control rules

user groups

conference calendar

community engineering

etc.

Page 9: Building DSLs with the Spoofax Language Workbench

18,000 lines of WebDSL code

138 (generated) tables in mysql

Page 10: Building DSLs with the Spoofax Language Workbench

Concerns in web applications

- Data model

- User interface templates

- Actions

- Access control rules

- Data validation

WebDSL

- Linguistic integration

- Cross concern, static consistency checking

Page 11: Building DSLs with the Spoofax Language Workbench

WebLib: a model of researchr

Page 12: Building DSLs with the Spoofax Language Workbench

Data Model

entity Publication { key :: String (id) title :: String (name,searchable) authors -> List<Author> journal :: String (searchable) volume :: Int issue :: Int year :: Int (searchable) month :: String (searchable) firstpage :: Int lastpage :: Int cites -> Set<Publication> citedBy -> Set<Publication> (inverse=Publication.cites) }

entity Author { key :: String (id) name :: String publications -> Set<Publication> (inverse=Publication.authors) created :: DateTime modified :: DateTime}

Page 13: Building DSLs with the Spoofax Language Workbench

User Interface

Page 14: Building DSLs with the Spoofax Language Workbench

define page search() { var query : String action show() { var pubs : List<Publication> := searchPublication(query); if(pubs.length > 0) { replace(results, results(query,pubs)); } else { replace(results, noresults()); } } main{ section{ header{"Search"} form{ input(query)[onkeyup:=show()] } placeholder results{} } }}

Full Text Search

entity Publication { title :: String (name,searchable) journal :: String (searchable) year :: Int (searchable) month :: String (searchable) ... }

Page 15: Building DSLs with the Spoofax Language Workbench

define page root() { main{ section{ header{"Welcome to WebLib: Your Digital Library"} aboutWeblib() recent() tips() } }}

define aboutWeblib() { par{ "WebLib is a digital library ..." }}

Page & Template Definitions

Page 16: Building DSLs with the Spoofax Language Workbench

XML Embeddingdefine no-span main() { <div class="page-wrap"> top() <div class="body"> messages() elements() </div> <div class="clear"></div> </div> footer()}

Page 17: Building DSLs with the Spoofax Language Workbench

Page Navigationdefine tips() { section{ header{"What Can You Do Here?"} list{ listitem{ navigate(newauthor()){ "Add an author" } } listitem{ navigate(newpublication()){ "Add a publication" } } listitem{ navigate(search()){ "Search" } } } }}

define page search() { ... }

Page 18: Building DSLs with the Spoofax Language Workbench

HQL Embedding

extend entity Person { publications -> List<Publication> := select p from Publication as p, Author as a where (a.person = ~this) and (p = a.publication)}

Page 19: Building DSLs with the Spoofax Language Workbench

Forms & Data Bindingdefine page editpublication(pub : Publication) { main{ section{ header{"Edit Publication '" output(pub.title) "'"} form{ group{ label("Key:" ){ input(pub.key) } label("Title:" ){ input(pub.title) } label("Authors:"){ input(pub.authors) } ... } submit action{ pub.modified := now(); return publication(pub); } { "Save" } } } }}

Page 21: Building DSLs with the Spoofax Language Workbench

Access Control Rules

define showReview(review : Review) { action delete() { ... } section{ ... navigate(editReview(review)){"[Edit]"} submitlink delete() {"[X]"} }}

Page 22: Building DSLs with the Spoofax Language Workbench

Access Control Rules

access control rules

principal is User with credentials username, password

predicate isReviewer(review : Review) { review.reviewer == securityContext.principal } rule template showReview(review : Review, pubLink : Bool) { review.public || isReviewer(review) rule action delete() { loggedIn() && isReviewer(review) } } rule page editReview(review : Review) { loggedIn() && isReviewer(review) }

define showReview(review : Review) { action delete() { ... } section{ ... navigate(editReview(review)){"[Edit]"} submitlink delete() {"[X]"} }}

Page 24: Building DSLs with the Spoofax Language Workbench

Data Validation: Form Input Validation

label("Acronym (without year)"){ input(acronym){

validate(!isEmptyString(acronym), "Provide acronym")

validate(!(/[\ ]/.find(acronym)), "Acronym should not contain spaces")

validate(!(/[0-9][0-9][0-9][0-9]/.find(acronym)), "Acronym should not contain year")

}}

Page 25: Building DSLs with the Spoofax Language Workbench

Data Validation: Data Invariants

entity Publication { key :: String (id, index(25),

validate(isUniquePublication(this), "that key is already in use"),

validate(isValidKeyForURL(key), "Key should consist of letters, digits, : and -")) ... }

Page 26: Building DSLs with the Spoofax Language Workbench

Data Validation: Assertions

extend entity Person { invitation -> Invitation (inverse=Invitation.invitee) function invite(email : Email, invitation : WikiText) : Invitation {

validate(findUser(email) == null && findInvitationByEmail(email).length == 0, "That email address is already in use.");

... }}

Page 27: Building DSLs with the Spoofax Language Workbench

Concerns in web applications

- Data model

- User interface templates

- Actions

- Access control rules

- Data validation

WebDSL

- Linguistic integration

- Cross concern, static consistency checking

Page 28: Building DSLs with the Spoofax Language Workbench

IDEs for DSLs

Page 29: Building DSLs with the Spoofax Language Workbench

Model

Code

AST

core

Errorsparse check

desugar

generate

Traditional Compiler Architecture

Page 30: Building DSLs with the Spoofax Language Workbench

Compiler Ingredients

Syntax definition

★ concrete syntax

★ abstract syntax

Static semantics

★ error checking

★ name resolution

★ type analysis

Model-to-model transformation

★ express constructs in core language

Code generation

★ translate core language models to implementation

Page 31: Building DSLs with the Spoofax Language Workbench

IDEs Required

Page 32: Building DSLs with the Spoofax Language Workbench

Editor Services for Modern IDEs

Syntactic Editor Services

★ syntax checking

★ bracket matching

★ syntax highlighting

★ code folding

★ outline view

Semantic Editor Services

★ error checking

★ reference resolving

★ hover help

★ code completion

★ refactoring

Page 33: Building DSLs with the Spoofax Language Workbench

Editor

Code

AST

core

IntegratedDevelopmentEnvironment

feedback

parse

Page 34: Building DSLs with the Spoofax Language Workbench

Architectural Requirements for IDEs

Editor services are based on AST

★ services analyze structure, not text of source

Error recovery

★ continue services in presence of syntactic errors

Incremental processing

★ effort of applying analysis, transformation, generation should be proportional to change in source

Separate compilation (analysis, transformation)

★ keep track of dependencies

Page 35: Building DSLs with the Spoofax Language Workbench

Holy Grail of Software Language Definition

Automatically derive efficient, scalable, incremental compiler + usable IDE from high-level, declarative language

definition

Page 36: Building DSLs with the Spoofax Language Workbench

The Spoofax Language Workbench

Page 37: Building DSLs with the Spoofax Language Workbench

The Spoofax/IMP Language Workbench

Syntax definition: SDF

★ declarative, modular syntax definition

Transformation, analysis, generation: Stratego

★ rewrite rules, strategies, dynamic rules

Editor services

★ domain-specific languages for configuration

Based on IMP framework by IBM Research

Page 38: Building DSLs with the Spoofax Language Workbench

Syntax Definition

Page 39: Building DSLs with the Spoofax Language Workbench

Syntax Definition

Parse Table Signature Pretty-Print Table

TransformParse Pretty-Print

entity User { name :: String pw :: Secret}def output(u : User) {

@Entityclass User { String _user; public User getUser() { return _user; }syntax definition is basis of language definition

Page 40: Building DSLs with the Spoofax Language Workbench

Lexical & Context-Free Syntax

Page 41: Building DSLs with the Spoofax Language Workbench

composed from

Structure of Sentences

Page 42: Building DSLs with the Spoofax Language Workbench

Context-Free Grammar

grammar production

symbol

DefinitionProperty*

{Stat “;”}*“entity”

regular expressionsort

Page 43: Building DSLs with the Spoofax Language Workbench

Lexical Syntax

character class

characters in lexical syntax not separated by layout

regular expression

Page 44: Building DSLs with the Spoofax Language Workbench

Syntax of Data Models

Page 45: Building DSLs with the Spoofax Language Workbench

RecognizingWell-Formed

Sentences

Page 46: Building DSLs with the Spoofax Language Workbench

Lexical Syntax: Identifiers & Literals

follow restriction: symbol may not be followed by character from this class: BlogEntry is one ID

reserved words

Page 47: Building DSLs with the Spoofax Language Workbench

Lexical Syntax: Strings

complement

escape

escape escape

Page 48: Building DSLs with the Spoofax Language Workbench

Lexical Syntax: Whitespace

LAYOUT? is inserted between symbols in CF productions

normalize

Page 49: Building DSLs with the Spoofax Language Workbench

Modular Syntax Definition

Page 50: Building DSLs with the Spoofax Language Workbench

Parsing & Abstract Syntax

building structured representation of sentence

Page 51: Building DSLs with the Spoofax Language Workbench

Linking Concrete & Abstract Syntax

Page 52: Building DSLs with the Spoofax Language Workbench

Terms to Represent Abstract Syntax

Entity(“Blog”, [Property(“name”,SimpleType(“String”)), Property(“author”, SimpleType(“User”))])

Page 53: Building DSLs with the Spoofax Language Workbench

Parsing in Spoofax

Page 54: Building DSLs with the Spoofax Language Workbench

Signatures

defining the structure of abstract syntax terms

Page 55: Building DSLs with the Spoofax Language Workbench

Syntax Definition

Parse Table Signature Pretty-Print Table

TransformParse Pretty-Print

entity User { name :: String pw :: Secret}def output(u : User) {

@Entityclass User { String _user; public User getUser() { return _user; }syntax definition is basis of language definition

Page 56: Building DSLs with the Spoofax Language Workbench

Signatures

declare arguments and types of constructorssignature is generated from grammar

Page 57: Building DSLs with the Spoofax Language Workbench

Disambiguation

Page 58: Building DSLs with the Spoofax Language Workbench

Arithmetic Expressions

Plus(Times(Var(“x”),Int(2)),Var(“z”))note: this style produces good abstract syntax

Page 59: Building DSLs with the Spoofax Language Workbench
Page 60: Building DSLs with the Spoofax Language Workbench

Associativity

Page 61: Building DSLs with the Spoofax Language Workbench

Priority

Page 62: Building DSLs with the Spoofax Language Workbench

Parsing after Disambiguation

Page 63: Building DSLs with the Spoofax Language Workbench

Brackets

Page 64: Building DSLs with the Spoofax Language Workbench

Language Composition

Page 65: Building DSLs with the Spoofax Language Workbench

define page index() { list{ for(p : Post in select p from Post as p order by (p.title)) { listitem{ output(p) } } }}

Embedding HQL

ForElem("p", SimpleType("Post"), QueryRule( SelectFrom( Some(Select(None(), [AliasedExpression(Path(["p"]), None())])) , FromClause([FromRangeJoin(FromClass(Path(["Post"]), Some(AsAlias(Alias("p"))), ) , Some(OrderByClause([OrderElement(Paren([Path(["p", "title"])]), None())])) ), [CallElems("listitem", [CallArgs("output", [Var("p")])])])

module expressionsimports Commonimports HQLexports context-free syntax SelectStatement -> Exp

Page 66: Building DSLs with the Spoofax Language Workbench

Presentational

Editor Services

Page 67: Building DSLs with the Spoofax Language Workbench

Editor Services

Syntax highlighting

★ token coloring

Code folding

★ folding & unfolding code fragments

Outline view

★ table of contents

Syntax properties

★ bracket matching, indentation

Syntax completion

★ match on syntactic triggers & replace with template

Page 68: Building DSLs with the Spoofax Language Workbench

module nwl.main

imports nwl-Builders nwl-Colorer nwl-Folding nwl-Outliner nwl-References nwl-Syntax nwl-Completions

language General properties name : nwl id : nwl extends : Root description : "Spoofax/IMP-generated editor for the nwl language" url : http://strategoxt.org extensions : nwl table : include/nwl.tbl start symbols : Start

Editor Services Composition

Page 69: Building DSLs with the Spoofax Language Workbench

Syntax Definition

Default Syntax Highlighting

Default Code Folding

DefaultOutline View

Editor

Custom Syntax Highlighting

CustomCode Folding

CustomOutline View+ + +

Page 70: Building DSLs with the Spoofax Language Workbench

module nwl-Foldingimports nwl-Folding.generatedfolding Element.CallElems Element.Call Element.ForElem Element.ForAllElem

module nwl-Folding.generatedfolding Default folding Start.Module Definition.Entity Property.Property Definition.TemplateDef Element.CallArgs PageRef.PageRef Element.Action

Code Folding

Page 71: Building DSLs with the Spoofax Language Workbench

Outline View

module nwl-Outliner.generatedoutliner Default outliner Start.Module Definition.Entity Property.Property Definition.TemplateDef Element.CallArgs Exp.MethodCall Element.CallElems Element.Call Element.ForElem Element.ForAllElem PageRef.PageRef Element.Action Statement.For Statement.While Statement.If Element.Submit Element.XmlElem

Page 72: Building DSLs with the Spoofax Language Workbench

module nwl-Colorer.generated colorer Default, token-based highlighting keyword : 127 0 85 bold identifier : default string : blue number : darkgreen var : 255 0 100 italic operator : 0 0 128 layout : 100 100 0 italic colorer System colors darkred = 128 0 0 red = 255 0 0 darkgreen = 0 128 0 green = 0 255 0 darkblue = 0 0 128 blue = 0 0 255 cyan = 0 255 255 magenta = 255 0 255 yellow = 255 255 0 white = 255 255 255 black = 0 0 0 gray = 128 128 128 grey = gray orange = 255 165 0 pink = 255 105 180 brown = 139 69 19 default = _

Default Syntax Highlighting

Page 73: Building DSLs with the Spoofax Language Workbench

Custom Syntax Highlighting

module nwl-Colorerimports nwl-Colorer.generatedcolorer Element : darkgreen

Page 74: Building DSLs with the Spoofax Language Workbench

Syntax Properties

module nwl-Syntax.generatedlanguage Syntax properties (static defaults) // Comment constructs: line comment : "//" block comment : "/*" * "*/" // Fences (used for matching, // inserting, indenting brackets): fences : [ ] ( ) { } // Automatic indent hints // (indent after these tokens): indent after : "=" ":" // Regular expression for identifiers: identifier lexical : "[A-Za-z0-9_]+"

Page 75: Building DSLs with the Spoofax Language Workbench

Parse Error Recovery

Page 76: Building DSLs with the Spoofax Language Workbench

Parse Error Recovery

also for composite languages

Page 77: Building DSLs with the Spoofax Language Workbench

BuildersSemantic Editor Services

Page 78: Building DSLs with the Spoofax Language Workbench

Builders: Analysis & Transformation Services

module nwl-Buildersbuilders provider: include/nwl.ctree

observer: editor-analyze

builder : "Generate Java code" = generate-java (openeditor) (realtime)

builder : "Show ATerm (selection)" = generate-aterm (openeditor) (realtime) (meta) builder : "Normalize (selection)" = show-normalized (openeditor) (realtime) (meta)

builder : "Normalize Pretty (selection)" = show-normalized-pp (openeditor) (realtime) (meta)

Page 79: Building DSLs with the Spoofax Language Workbench

Term Rewriting

Page 80: Building DSLs with the Spoofax Language Workbench

Term Rewriting

Term rewrite rules

★ transform term to term

★ pattern matching

★ variable binding

★ substitution

Rewriting strategy

★ algorithm for applying rewrite rules

Page 81: Building DSLs with the Spoofax Language Workbench

Term Rewrite Rule

desugar : Property(x, t) -> Property(x, t, [])

label/name

left-hand side pattern

right-hand side patternvariable

Page 82: Building DSLs with the Spoofax Language Workbench

Rewrite Strategy

desugar-all = innermost(desugar)

strategy definitiongeneric strategy

strategy instantiation

innermost(s) apply transformation s exhaustively to all sub-terms of subject term in bottom-up order

Page 83: Building DSLs with the Spoofax Language Workbench

Constant Folding

y := a + (3 * 5 + 2);

Assign( Var("y"), BinOp(Var("a"), "+", IntLit("17")))

Assign( Var("y"), Plus( Var("a") , Plus(Times(IntLit("3"), IntLit("5")), IntLit("2")) ))

y := (a + 17);

desugar + eval

parse

pretty-print

Page 84: Building DSLs with the Spoofax Language Workbench

Constant Folding Rules

strategies

eval-all = innermost(desugar + eval)

rules eval : BinOp(IntLit(x), "+", IntLit(y)) -> IntLit(<addS>(x, y)) eval : BinOp(IntLit(x), "-", IntLit(y)) -> IntLit(<subtS>(x, y)) eval : BinOp(IntLit(x), "*", IntLit(y)) -> IntLit(<mulS>(x, y)) eval : BinOp(IntLit(x), "/", IntLit(y)) -> IntLit(<divS>(x, y))

Page 85: Building DSLs with the Spoofax Language Workbench

Conditional Rewrite Rules

eval : BinOp(IntLit(x), "+", IntLit(y)) -> IntLit(<addS>(x, y))

eval : BinOp(IntLit(x), "+", IntLit(y)) -> IntLit(z) where z := <addS>(x, y)condition

match apply transformation

bound in condition

Page 86: Building DSLs with the Spoofax Language Workbench

Code Generation by Model Transformation

Page 87: Building DSLs with the Spoofax Language Workbench

elem-to-java-servlet : tc@elem|[ x(e)[passign*]{elem*} ]| -> <wrap-input-render-java-code> bstm* |[ String x_temp = ident+"~inputident"+uniqueid; ~*<render-error-messages-with-error-template(|java:expr|[ x_temp ]|, <ErrorTemplateInput>)> bstm*|[ bstm_call* ]| ]| where <is-input-template> tc with inputident := <get-inputnumber> tc ; x_temp := <newname> "temp" ; bstm_call* := <control-flow-tcall-helper(|"render",expr|[ x_temp ]|)> tc

Rewriting with Concrete Object Syntax

Stratego + embedded Java

Page 88: Building DSLs with the Spoofax Language Workbench

Consistency Checking

Page 89: Building DSLs with the Spoofax Language Workbench

Consistency Checking

Syntax definition

★ what are well-formed sentences?

Static analysis

★ not all ‘well-formedness’ properties are context-free

★ consistency of compositions

★ consistency of expressions wrt declarations

Error reporting

★ indicate errors in editor

★ use sensible error message

Page 90: Building DSLs with the Spoofax Language Workbench
Page 91: Building DSLs with the Spoofax Language Workbench
Page 92: Building DSLs with the Spoofax Language Workbench

Consistency Checking: Ingredients

Editor Interface

★ collecting and displaying errors, warnings

Error checking

★ checking static constraints and reporting errors

Type analysis

★ computing types of expressions

Name resolution

★ disambiguation of names

Reference resolving

★ linking identifiers to declarations

Page 93: Building DSLs with the Spoofax Language Workbench

Consistency Checking: Generic Approach

Rename

★ make identifiers unique

Map

★ map identifiers to declarations

Project

★ compute properties of declarations, expressions

Check

★ check constraints

Page 94: Building DSLs with the Spoofax Language Workbench

Editor Interface

module nwl-Builders

imports nwl-Builders.generated

builders

provider: include/nwl.ctree

observer: editor-analyze

module static-analysis

imports include/nwlimports entitiesimports utils

rules // static analysis

editor-analyze: (ast, path, fullpath) -> (errors, warnings, notes) with ...

editor/nwl-Builders.esvtrans/static-analysis.str

Page 95: Building DSLs with the Spoofax Language Workbench

Editor Interface

editor-analyze: (ast, path, fullpath) -> (errors, warnings, notes) with errors := <collect-all(check, conc)> ast; warnings := <collect-all(constraint-warning, conc)> ast; notes := <collect-all(constraint-note, conc)> ast

Page 96: Building DSLs with the Spoofax Language Workbench

Error Checking Rules

check : context -> (target, error) where assumption where require(constraint)

require(s) = not(s)

– Context: identifying points in the code to check– Assumptions: only report an error if certain assumptions hold (validating the context and avoiding spurious errors)– Constraints: checking for constraints at the context– Formulating an error message– Attribution of the error to a particular character range in the source text (usually, only part of the context

Page 97: Building DSLs with the Spoofax Language Workbench

Error Checking: Binary Operators

check : e@BinOp(e1, op, e2) -> (e, $[operator [op] not defined for [<pp>t1] and [<pp>t2]]) where t1 := <type-of> e1 where t2 := <type-of> e2 where require(<type-of> e)

Page 98: Building DSLs with the Spoofax Language Workbench

Pretty-Printing with String Interpolation

pp : Entity(x, prop*) -> $[entity [x] { [<map(pp)>prop*] }] pp : Property(x,t) -> $[[x] : [<pp>t] ] pp : SimpleType(x) -> x pp : SetType(t) -> $[Set<[<pp> t]>] pp : [] -> $[] pp : [t] -> <pp>t pp : [t1,t2|ts] -> $[[<pp>t1],[<pp>[t2|ts]]]

Page 99: Building DSLs with the Spoofax Language Workbench

Type Analysis

type-of : e -> t

compute type of expression

Page 100: Building DSLs with the Spoofax Language Workbench

Type Analysis: Literals

type-of : StringLit(x) -> SimpleType("String") type-of : IntLit(x) -> SimpleType("Int")

Page 101: Building DSLs with the Spoofax Language Workbench

Type Analysis: Binary Operators

type-of : BinOp(e1, op, e2) -> t where t := <function-type>(op, [<type-of>e1, <type-of>e2])

function-type : ("+", [SimpleType("String"), SimpleType("String")]) -> SimpleType("String") function-type : ("+", [SimpleType("Int"), SimpleType("Int")]) -> SimpleType("Int")

function-type : ("-", [SimpleType("Int"), SimpleType("Int")]) -> SimpleType("Int")

Page 102: Building DSLs with the Spoofax Language Workbench

Type Analysis: What is Type of Variable?

define page root(x : Int) { action exptest() { for(y : Int in {1,2,x}) { x := x + y; } } } type-of :

Var(x) -> t where t := ???

Assign( Var("x"), BinOp(Var("x"), "+", Var("y")))

type of variable not part of variable use

Page 103: Building DSLs with the Spoofax Language Workbench

Variables: Map

declare-all = alltd(declare)

declare : Param(x, t) -> Param(x, t) with rules( TypeOf : x -> t )

type-of : Var(x) -> t where t := <TypeOf> x

Page 104: Building DSLs with the Spoofax Language Workbench

Scope

define page root(x : Int) { action exptest() { for(x : Int in {1,2,x}) { print(x); } }}

multiple occurrences of same identifier corresponding to different declarations

Page 105: Building DSLs with the Spoofax Language Workbench

Variables: Map + Renamerename-all = alltd(rename)

rename : Param(x, t) -> Param(y, t) with y := <rename-var>(x, t)

rename-var : (x, t) -> y with y := x{<new>}; rules( TypeOf : y -> t RenameId : x -> y )

rename : Var(x) -> Var(y) where y := <RenameId> x type-of : Var(x) -> t where t := <TypeOf> x

unique annotation

rename occurrences

map variable to type

Page 106: Building DSLs with the Spoofax Language Workbench

Term Annotations

t{t1,...,tn}

add additional information to term without affecting signature

Page 107: Building DSLs with the Spoofax Language Workbench

Variables: Check

check : e@Var(x) -> (e, $[Variable '[x]' not declared]) where require(<type-of>e)

Page 108: Building DSLs with the Spoofax Language Workbench

Variable Binding Constructs

rename : For(x, t, e1, stat1*) -> For(y, t, e2, stat2*) with e2 := <rename-all> e1 with {| RenameId : y := <rename-var>(x, t) ; stat2* := <rename-all> stat1* |}

For defines local variable x in body stat1*’

Page 109: Building DSLs with the Spoofax Language Workbench

Editor Interface with Analysis

editor-analyze: (ast, path, fullpath) -> (errors, warnings, notes) with ast2 := <analyze> ast; errors := <collect-all(check, conc)> ast2; warnings := <collect-all(constraint-warning, conc)> ast2; notes := <collect-all(constraint-note, conc)> ast2

analyze = rename-all

Page 110: Building DSLs with the Spoofax Language Workbench

Rename, Map, Project, Check

Rename

★ make local variables unique

Map

★ variables to their type

Project

★ compute type of expressions

Check

★ check constraints using types

Page 111: Building DSLs with the Spoofax Language Workbench

Reference Resolution

Page 112: Building DSLs with the Spoofax Language Workbench

Reference Resolution

Aiding program navigation

★ Hover-click on identifier to jump to declaration

Reuse name resolution infrastructure

Page 113: Building DSLs with the Spoofax Language Workbench

editor-resolve: (source, position, ast, path, fullpath) -> target where target := <compute-target> source

Reference Resolution

module nwl-References

imports nwl-References.generated

references

reference _ : editor-resolve

Page 114: Building DSLs with the Spoofax Language Workbench

editor-resolve: (SimpleType(type), position, ast, path, fullpath) -> target where Entity(target,_) := <declaration-of> type editor-resolve: (ref@PageRef(x,e*), position, ast, path, fullpath) -> target where TemplateDef(_,target,_,_) := <declaration-of> ref

From Use to Declaration

Page 115: Building DSLs with the Spoofax Language Workbench

Experience

Page 116: Building DSLs with the Spoofax Language Workbench

Experience

Bootstrapped

★ SDF, Stratego, ATerm, PP

★ Editor service language

Spoofax

★ WebDSL

★ Mobl: mobile web applications

★ Acoda: migration of data models

★ Aster: attribute grammars

★ PIL: platform independent language

★ Student languages

SDF+Stratego

★ Java, AspectJ, XML, PHP, SQL, Jimple, Octave, Dot, ...

Page 117: Building DSLs with the Spoofax Language Workbench

Future Work (in progress)

Higher-level definition of scope & type system

★ cover scope systems of real languages

Refactoring

★ layout preservation, generic refactoring strategies

Systematic language design

★ how do we decide that a language design is good?

Interaction design

★ higher-level specifications of interaction

Combining textual and visual

Textual IDE in the browser

Page 119: Building DSLs with the Spoofax Language Workbench

Context-sensitiveTransformation

Page 120: Building DSLs with the Spoofax Language Workbench

Template Inliningdefine page blog(b : Blog) { header{output(b.name)} list{ for(p : Post in b.posts) { listitem{ outputPost(p) } } }}define outputPost(pst : Post) { navigate post(pst) { output(pst.name) }}define list() { <ul> elements </ul> }define listitem() { <li> elements </li> }define header() { <h1> elements </h1> }

define page blog ( b : Blog ) { <h1> output(b.name) </h1> <ul> for ( p : Post in b.posts ) { <li> navigate post(p) { output(p.name) } </li> } </ul>}

Page 121: Building DSLs with the Spoofax Language Workbench

Context-sensitive Transformation

Local-to-local

★ replace term by another term

Local-to-global

★ local term influence terms in other parts of model

Global-to-local

★ global information influences transformation of local terms

Page 122: Building DSLs with the Spoofax Language Workbench

define page blog(b : Blog) { header{output(b.name)} list{ for(p : Post in b.posts) { listitem{ outputPost(p) } } }}define outputPost(pst : Post) { navigate post(pst) { output(pst.name) }}define list() { <ul> elements </ul> }define listitem() { <li> elements </li> }define header() { <h1> elements </h1> }

define page blog ( b : Blog ) { <h1> output(b.name) </h1> <ul> for ( p : Post in b.posts ) { <li> navigate post(p) { output(p.name) } </li> } </ul>}

Inlining is Global-to-Local

Page 123: Building DSLs with the Spoofax Language Workbench

outputPost(p) -> navigate post(p) { output(p.name) }where define outputPost(pst : Post) { navigate post(pst) { output(pst.name) } }

Inlining as Rewrite Problem

(this is not a valid Stratego rewrite rule)

Page 124: Building DSLs with the Spoofax Language Workbench

Rewrite Rules are Context-free

desugar : Property(x, t) -> Property(x, t, [])

Page 125: Building DSLs with the Spoofax Language Workbench

Simplification: Parameterless Templates

define page blog(b : Blog) { ... footer()}define footer() { navigate url(http://webdsl.org) { “WebDSL” }}

define page blog ( b : Blog ) { ... container() { navigate url(http://webdsl.org) { “WebDSL” } }}

Page 126: Building DSLs with the Spoofax Language Workbench

Rewrite in Context

Inline : [def@TemplateDef(f,[],elem1*) | def1*] -> [def | def2*] where def2* := <alltd((Call(f) -> Call("container", elem1*)))> def1*

bound in contextlocal traversal

container needed to replace single call with multiple elements

Page 127: Building DSLs with the Spoofax Language Workbench

Intermezzo:Rewriting Strategies

Page 128: Building DSLs with the Spoofax Language Workbench

transformation

is

partial* function from terms to terms

* transformation may fail to apply to a term

Page 129: Building DSLs with the Spoofax Language Workbench

Defining Transformations: Rules & Strategies

Rewrite rules are basic transformations

★ transform term matching lhs to instantiation of rhs

★ evaluate condition (where clause)

Strategies combine rules into complex transformations

★ select rules to apply

★ select algorithm to apply rules

Strategy combinators

★ composition of custom transformations

Page 130: Building DSLs with the Spoofax Language Workbench

Strategy Combinators

id★ Identity

fail★ failure

s1 ; s2★ sequential composition

s1 <+ s2★ choice

Page 131: Building DSLs with the Spoofax Language Workbench

Variables

{x, y : s}★ term variable scope

?t★ match term pattern t

!t★ build term pattern t

t1 := t2★ match term t2 to term (pattern) t1

<s> t★ apply strategy s to term t

Page 132: Building DSLs with the Spoofax Language Workbench

Rewrite Rules

l : t1 -> t2 where s★ named, scoped rewrite rule

★ all variables in t1, t2, s are in scope of the rule

(t1 -> t2 where s)★ unscoped rewrite rule

★ variables are in scope of enclosing scope

Page 133: Building DSLs with the Spoofax Language Workbench

Rewrite in Context

Inline : [def@TemplateDef(f,[],elem1*) | def1*] -> [def | def2*] where def2* := <alltd((Call(f) -> Call("container", elem1*)))> def1*

bound in context

Page 134: Building DSLs with the Spoofax Language Workbench

[ TemplateDef( "footer" , [] , [Navigate(PageRef("webdsl", []), [String("WebDSL")])] ), TemplateDef( "blog" , [Param("b", SimpleType("Blog"))] , [Call("footer")] )]

Inline : [def@TemplateDef(f,[],elem*) | def1*] -> [def | def2*] where def2* := <alltd((Call(f) -> Call("container", elem*)))> def1*

f elem*

def1*Call(f)

Page 135: Building DSLs with the Spoofax Language Workbench

Strategy Definitions

f(x,y|a,b) = s★ x, y are strategy parameters

★ a, b are term parameters

★ s uses all parameters

f(x) = s f = s★ term parameters are optional

★ all parameters are optional

Examples

★ try(s) = s <+ id★ repeat(s) = try(s; repeat(s))

Page 136: Building DSLs with the Spoofax Language Workbench

Rules with Parameters

Transform all elements of a list

Invert order of elements of a list

Pair elements of two lists

inverse(|ys) : [] -> ys

inverse(|ys) : [x|xs] -> <inverse(|[x|ys])> xs

map(s) : [] -> []

map(s) : [x|xs] -> [<s>x | <map(s)> xs]

zip(s) : ([],[]) -> []

zip(s) : ([x|xs],[y|ys]) -> [<s>(x,y) | <zip(s)>(xs,ys)]

Page 137: Building DSLs with the Spoofax Language Workbench

Traversal Combinators

all(s)★ apply s to all direct sub-terms (children)

one(s)★ apply s to exactly one sub-term

some(s)★ apply s to at least one sub-term

Page 138: Building DSLs with the Spoofax Language Workbench

Traversal Strategies

topdown(s) = s; all(topdown(s))★ apply s to all sub-terms in top-down order

bottomup(s) = all(bottomup(s)); s★ apply s to all sub-terms in bottom-up order

oncetd(s) = s <+ one(oncetd(s))★ apply s to one sub-term

alltd(s) = s <+ all(alltd(s))★ apply s to frontier

Page 139: Building DSLs with the Spoofax Language Workbench

Rewrite in Context: Local Traversal

Inline : [def@TemplateDef(f,[],elem1*) | def1*] -> [def | def2*] where def2* := <alltd((Call(f) -> Call("container", elem1*)))> def1*

local traversal

Page 140: Building DSLs with the Spoofax Language Workbench

Dynamic Rewrite Rules

Page 141: Building DSLs with the Spoofax Language Workbench

Rewrite in Context: Not Optimal

requires def before use local traversal for each declaration

Inline : [def@TemplateDef(f,[],elem1*) | def1*] -> [def | def2*] where def2* := <alltd((Call(f) -> Call("container", elem1*)))> def1*

Page 142: Building DSLs with the Spoofax Language Workbench

Dynamic Rewrite Rules

separate traversal from rule definition (binding closures)

declare-inline : TemplateDef(f,[],elem1*) -> TemplateDef(f,[],elem1*) where rules( InlineTemplate : Call(f) -> Call("container", elem1*) )

inline = alltd(declare-inline); topdown(try(InlineTemplate))

Inline : [def@TemplateDef(f,[],elem1*) | def1*] -> [def | def2*] where def2* := <alltd((Call(f) -> Call("container", elem1*)))> def1*

Page 143: Building DSLs with the Spoofax Language Workbench

outputPost(p) -> navigate post(p) { output(p.name) }where define outputPost(pst : Post) { navigate post(pst) { output(pst.name) } }

Inlining as Rewrite Problem (Revisited)

(informal)

declare-inline : TemplateDef(f,[],elem1*) -> TemplateDef(f,[],elem1*) where rules( InlineTemplate : Call(f) -> Call("container", elem1*) )

(formal; but not yet complete)

Page 144: Building DSLs with the Spoofax Language Workbench

Template Parameterdefine page blog(b : Blog) { header{output(b.name)} list{ for(p : Post in b.posts) { listitem{ outputPost(p) } } }}define outputPost(pst : Post) { navigate post(pst) { output(pst.name) }}define list() { <ul> elements </ul> }define listitem() { <li> elements </li> }define header() { <h1> elements </h1> }

define page blog ( b : Blog ) { <h1> output(b.name) </h1> <ul> for ( p : Post in b.posts ) { <li> navigate post(p) { output(p.name) } </li> } </ul>}

Page 145: Building DSLs with the Spoofax Language Workbench

declare-inline : def@TemplateDef(mod*,f,param*,elem1*) -> def where rules( InlineTemplate : Call(f, e*, []) -> Call("container", [], elem3*) where elem3* := <substitute> (param*, e*, elem1*) ) substitute : (param*, e*, elem1*) -> elem2* where {| Subst : <zip(bind-arg)> (param*, e*) ; elem2* := <alltd(Subst)> elem1* |} bind-arg : (Param(x, t), e) -> (Param(x, t), e) where rules( Subst : Var(x) -> e )

Inlining Templates with Parameters

Page 146: Building DSLs with the Spoofax Language Workbench

declare-inline : def@TemplateDef(mod*,f,param*,elem1*) -> def where rules( InlineTemplate : Call(f, e*, elem2*) -> Call("container", [], elem3*) where {| Subst : rules( Subst : Elements() -> Call("container",[],elem2*) ) ; elem3* := <substitute> (param*, e*, elem1*) |} )

define list() { <ul> elements </ul> }

Element Parameters

Page 147: Building DSLs with the Spoofax Language Workbench

Removing Intermediate Structures

rules // remove containers desugar-container : [Call("container",[], elem1*) | elem2*] -> [elem1*, elem2*] desugar : elem1* -> elem2* where elem2* := <at-suffix(desugar-container)> elem1*

container needed to replace single call with multiple elements

Page 148: Building DSLs with the Spoofax Language Workbench

Inlining Strategy

module template-inlining

imports libstratego-libimports include/nwlimports desugar

strategies inline-all = desugar-all; alltd(declare-inline); innermost(desugar <+ InlineTemplate)

rules declare-inline : ...

Page 149: Building DSLs with the Spoofax Language Workbench

module template-inlining

imports libstratego-libimports include/nwlimports desugar

strategies inline-all = desugar-all; alltd(declare-inline); innermost(desugar <+ InlineTemplate)

rules declare-inline : TemplateDef(mod*,f,param*,elem1*) -> TemplateDef(mod*,f,param*,elem1*) where rules( InlineTemplate : Call(f, e*, elem2*) -> Call("container", [], elem3*) where {| Subst : rules( Subst : Elements() -> Call("container", [], elem2*) ) ; elem3* := <substitute> (param*, e*, elem1*) |} ) substitute : (param*, e*, elem1*) -> elem2* where {| Subst : <zip(bind-arg)> (param*, e*) ; elem2* := <alltd(Subst)> elem1* |} bind-arg : (Param(x, t), e) -> (Param(x, t), e) where rules( Subst : Var(x) -> e )

rules // remove containers desugar-container : [Call("container",[], elem1*) | elem2*] -> [elem1*, elem2*] desugar : elem1* -> elem2* where elem2* := <at-suffix(desugar-container)> elem1*

Template Inlining Transformation