grammarware memes
DESCRIPTION
Slides for mini-tutorial at the International Conference on Sofware Language Engineering (SLE 2012) in Dresden, Germany, September 27, 2012.TRANSCRIPT
Grammarware MemesEelco Visser & Guido Wachsmuth
Background
Spoofax Language Workbench
WebDSL Web Programming Language
define page post(p: Post, title: String) { init{ p.update(); } title{ output(p.title) } bloglayout(p.blog){ placeholder view { postView(p) } postComments(p) }} define ajax postView(p: Post) { pageHeader2{ output(p.title) } postBodyLayout(p) { postContent(p) postExtendedContent(p) postActions(p) }}
Researchr Bibliography Management System
WebLab Programming Education on the Web
Some Philosophical Considerations
Grammarware vs Modelware?
GPCEOOPSLA
MODELSICMT
Grammarware vs Modelware?
GPCEOOPSLA
MODELSICMTSLE
Functional vs Object-Oriented vs Logic vs ...
Remember the Programming Language Wars?
ICFP OOPSLA
Remember the Programming Language Wars?
ICFP OOPSLAGPCE
Scala: Object-Oriented or Functional?
‘closure’: a purely functional feature?
X-ware is a Memeplex
memeplex: selection of memes from the meme pool
What is Grammarware?
Grammarware is about
(Parsing) Text
module users
imports library
entity User { email : String password : String isAdmin : Bool}
Grammarware is about
Structure
Module( "users", [ Imports("library") , Entity( "User" , [ Property("email", Type("String")) , Property("password", Type("String")) , Property("isAdmin", Type("Bool")) ] ) ])
Grammarware: Text & Structure
Grammarware is about Transformation
Source to Source Transformation
Source to Source Transformation
Transformation & Analysis
name resolution
type checking
desugaring
code generation
Some Memes from the Spoofax Memeplex
EnFun: Entities with Functions
module blog entity String { function plus(that:String): String } entity Bool { } entity Set<T> { function add(x: T) function remove(x: T) function member(x: T): Bool } entity Blog { posts : Set<Post> function newPost(): Post { var p : Post := Post.new(); posts.add(p); } } entity Post { title : String }
Structure
Signature & Terms
constructors Module : ID * List(Definition) -> Module Imports : ID -> Definition
Module( "application", [Imports("library"), Imports("users"), Imports("frontend")])
Entities & Properties
constructors Entity : ID * List(Property) -> Definition Type : ID -> Type New : Type -> Exp
constructors Property : ID * Type -> Property This : Exp PropAccess : Exp * ID -> Exp
Module("users", [ Imports("library") , Entity("User" , [ Property("email", Type("String")) , Property("password", Type("String")) , Property("isAdmin", Type("Bool"))])])
Constants & Operators & Variables
constructors String : STRING -> Exp Int : INT -> Exp False : Exp True : Exp
constructors VarDecl : ID * Type -> Stat VarDeclInit : ID * Type * Exp -> Stat Assign : Exp * Exp -> Stat Var : ID -> Exp
constructors Geq : Exp * Exp -> Exp Leq : Exp * Exp -> Exp Gt : Exp * Exp -> Exp Lt : Exp * Exp -> Exp Eq : Exp * Exp -> Exp Mul : Exp * Exp -> Exp Minus : Exp * Exp -> Exp Plus : Exp * Exp -> Exp Or : Exp * Exp -> Exp And : Exp * Exp -> Exp Not : Exp -> Exp
Statements & Functions
constructors Exp : Exp -> Stat Block : List(Stat) -> Stat Seq : List(Stat) -> Stat While : Exp * List(Stat) -> Stat IfElse : Exp * List(Stat) * List(ElseIf) * Option(Else) -> Stat Else : List(Stat) -> Else ElseIf : Exp * List(Stat) -> ElseIf
constructors FunDef : ID * List(Arg) * Type * List(Stat) -> Property FunDecl : ID * List(Arg) * Type -> Property Arg : ID * Type -> Arg Return : Exp -> Stat MethCall : Exp * ID * List(Exp) -> Exp ThisCall : ID * List(Exp) -> Exp
Transformation
Transformation by Strategic Rewriting
rules desugar: Plus(e1, e2) -> MethCall(e1, "plus", [e2]) desugar: Or(e1, e2) -> MethCall(e1, "or", [e2])
desugar : VarDeclInit(x, t, e) -> Seq([VarDecl(x, t), Assign(Var(x), e)])
strategies desugar-all = topdown(repeat(desugar))
Return-Lifting Applied
function fact(n: Int): Int { var res: Int; if(n == 0) { res := 1; } else { res := this * fact(n - 1); } return res;}
function fact(n: Int): Int {
if(n == 0) { return 1; } else { return this * fact(n - 1); }
}
Return-Lifting Rules
rules lift-return-all = alltd(lift-return; normalize-all) lift-return : FunDef(x, arg*, Some(t), stat1*) -> FunDef(x, arg*, Some(t), Seq([ VarDecl(y, t), Seq(stat2*), Return(Var(y)) ])) where y := <new>; stat2* := <alltd(replace-return(|y))> stat1* replace-return(|y) : Return(e) -> Assign(y, e)
Seq Normalization
strategies normalize-all = innermost-L(normalize) rules // seq lifting
normalize : [Seq(stat1*) | stat2*@[_|_]] -> [Seq([stat1*,stat2*])]
normalize : [stat, Seq(stat*)] -> [Seq([stat | stat*])]
normalize : Block([Seq(stat1*)]) -> Block(stat1*)
normalize : FunDef(x, arg*, t, [Seq(stat*)]) -> FunDef(x, arg*, t, stat*)
Interpretation
eval
Small Step Operational Semantics
rules // driver
steps = repeat(step) step: State([Frame(fn, this, slots, cont)|f*], heap) -> State(stack', heap') where cont' := <eval> cont; stack' := <update-stack (|...)> [Frame(..., cont')|f*]; heap' := <update-heap(|...)> heap
Small Step Operational Semantics
eval(|this, slots, heap): Var(x) -> val where val := <lookup> (x, slots)eval(|this, slots, heap): PropAccess(Ptr(p), prop) -> val where Object(_, props) := <lookup> (p, heap); val := <lookup> (prop, props)
From Text to Structure
Declarative Syntax Definition
Entity("User", [ Property("first", Type("String")), Property("last", Type("String"))])
signature constructors Entity : ID * List(Property) -> Definition Type : ID -> Type Property : ID * Type -> Property
Declarative Syntax Definition
entity User { first : String last : String}
Entity("User", [ Property("first", Type("String")), Property("last", Type("String"))])
signature constructors Entity : ID * List(Property) -> Definition Type : ID -> Type Property : ID * Type -> Property
Declarative Syntax Definition
entity User { first : String last : String}
Entity("User", [ Property("first", Type("String")), Property("last", Type("String"))])
signature constructors Entity : ID * List(Property) -> Definition Type : ID -> Type Property : ID * Type -> Property
context-free syntax "entity" ID "{" Property* "}" -> Definition {"Entity"} ID -> Type {"Type"} ID ":" Type -> Property {"Property"}
Declarative Syntax Definition
entity User { first : String last : String}
Entity("User", [ Property("first", Type("String")), Property("last", Type("String"))])
signature constructors Entity : ID * List(Property) -> Definition Type : ID -> Type Property : ID * Type -> Property
context-free syntax "entity" ID "{" Property* "}" -> Definition {"Entity"} ID -> Type {"Type"} ID ":" Type -> Property {"Property"}
Context-free Syntax
constructors True : Exp False : Exp Not : Exp -> Exp And : Exp * Exp -> Exp Or : Exp * Exp -> Exp
context-free syntax "true" -> Exp {"True"} "false" -> Exp {"False"} "!" Exp -> Exp {"Not"} Exp "&&" Exp -> Exp {"And"} Exp "||" Exp -> Exp {"Or"}
Lexical Syntax
constructors True : Exp False : Exp Not : Exp -> Exp And : Exp * Exp -> Exp Or : Exp * Exp -> Exp
context-free syntax "true" -> Exp {"True"} "false" -> Exp {"False"} "!" Exp -> Exp {"Not"} Exp "&&" Exp -> Exp {"And"} Exp "||" Exp -> Exp {"Or"}
lexical syntax [a-zA-Z][a-zA-Z0-9]* -> ID "-"? [0-9]+ -> INT [\ \t\n\r] -> LAYOUT
constructors : String -> ID : String -> INT
scannerless generalized (LR) parsing
Ambiguity
constructors True : Exp False : Exp Not : Exp -> Exp And : Exp * Exp -> Exp Or : Exp * Exp -> Exp
context-free syntax "true" -> Exp {"True"} "false" -> Exp {"False"} "!" Exp -> Exp {"Not"} Exp "&&" Exp -> Exp {"And"} Exp "||" Exp -> Exp {"Or"}
isPublic || isDraft && (author == principal())
amb([ And(Or(Var("isPublic"), Var("isDraft")), Eq(Var("author"), ThisCall("principal", []))), Or(Var("isPublic"), And(Var("isDraft"), Eq(Var("author"), ThisCall("principal", []))))])
Disambiguation by Encoding Precedence
constructors True : Exp False : Exp Not : Exp -> Exp And : Exp * Exp -> Exp Or : Exp * Exp -> Exp
context-free syntax "true" -> Exp {"True"} "false" -> Exp {"False"} "!" Exp -> Exp {"Not"} Exp "&&" Exp -> Exp {"And"} Exp "||" Exp -> Exp {"Or"}
context-free syntax "(" Exp ")" -> Exp0 {bracket} "true" -> Exp0 {"True"} "false" -> Exp0 {"False"} Exp0 -> Exp1 "!" Exp0 -> Exp1 {"Not"} Exp1 -> Exp2 Exp1 "&&" Exp2 -> Exp2 {"And"} Exp2 -> Exp3 Exp2 "||" Exp3 -> Exp3 {"Or"}
Declarative Disambiguation
context-free syntax "true" -> Exp {"True"} "false" -> Exp {"False"} "!" Exp -> Exp {"Not"} Exp "&&" Exp -> Exp {"And", left} Exp "||" Exp -> Exp {"Or", left} "(" Exp ")" -> Exp {bracket}context-free priorities {left: Exp.Not} > {left: Exp.Mul} > {left: Exp.Plus Exp.Minus} > {left: Exp.And} > {non-assoc: Exp.Eq Exp.Lt Exp.Gt Exp.Leq Exp.Geq}
isPublic || isDraft && (author == principal())
Or(Var("isPublic"), And(Var("isDraft"), Eq(Var("author"), ThisCall("principal", []))))
Generating Text from Structure
Code Generation by String Concatenation
to-java: Property(x, Type(t)) -> <concat-strings>[ "private ", t, " ", x, ";\n\n", "public ", t, " get_", x, " {\n", " return ", x, ";\n", "}\n", "public void set_", x, " (", t, " ", x, ") {\n", " this.", x, " = ", x, ";\n", "}" ]
String Interpolation Templates
to-java: Property(x, Type(t)) -> $[ private [t] [x]; public [t] get_[x] { return [x]; } public void set_[x] ([t] [x]) { this.[x] = [x]; } ]
Code Generation by Model Transformation
Generating Structured Representations
to-java: Property(x, Type(t)) -> [ Field([Private()], Type(t), Var(x)), Method([Public()], Type(t), $[get_[x]], [], [ Return(Var(x)) ]), Method([Public()], None(), $[set_[x]], [Param(Type(t), x)], [ Assign(FieldAccess(This(), x), Var(x)) ]) ]
Concrete Object Syntax
to-java: Property(x, Type(t)) -> |[ private t x; public t get_#x { return x; } public void set_#x (t x) { this.x = x; } ]|
Rewriting with Concrete Object Syntax
module desugar
imports Enfun strategies desugar-all = topdown(repeat(desugar)) rules desugar: |[ !e ]| -> |[ e.not() ]| desugar: |[ e1 && e2 ]| -> |[ e1.and(e2) ]| desugar: |[ e1 || e2 ]| -> |[ e1.or(e2) ]| desugar: |[ e1 + e2 ]| -> |[ e1.plus(e2) ]| desugar: |[ e1 * e2 ]| -> |[ e1.times(e2) ]| desugar: |[ e1 - e2 ]| -> |[ e1.minus(e2) ]|
Rewriting with Concrete Object Syntax
lift-return-cs : |[ function x(arg*) : t { stat1* } ]| -> |[ function x(arg*): t { var y : t; ( stat2* ) return y; } ]| where y := <new>; stat2* := <alltd(replace-return(|y))> stat1*
lift-return : FunDef(x, arg*, Some(t), stat1*) -> FunDef(x, arg*, Some(t), Seq([ VarDecl(y, t), Seq(stat2*), Return(Var(y)) ])) where y := <new>; stat2* := <alltd(replace-return(|y))> stat1*
Pretty-Printing by Model Transformation
Code Formatting
module users entity String { } entity User { first : String last : String }
module users
entity String {}
entity User { first : String last : String }
Module( "demo1", [ Entity("String", None(), None(), []) , Entity( "User" , [ Property("first", Type("String")) , Property("last", Type("String")) ] ) ])
Pretty-Printer Specification
prettyprint-Definition : Entity(x, prop*) -> [ H([SOpt(HS(), "0")] , [ S("entity ") , <pp-one-Z(prettyprint-ID)> x , S(" {") ] ) , <pp-indent(|"2")> [<pp-V-list(prettyprint-Property)> p*] , H([SOpt(HS(), "0")], [S("}")] ) ]
Structure Editors
Discoverability
Editors with Syntactic Completion
Completion Templates
completions
completion template Definition : "entity ID { }" = "entity " <ID:ID> " {\n\t" (cursor) "\n}" (blank) completion template Type : "ID" = <ID:ID> completion template Property : "ID : ID " = <ID:ID> " : " <ID:Type> (blank)
Syntax Templates: Structure + Layout
templates
Definition.Entity = < entity <ID> { <Property*; separator="\n"> } >
Type.Type = <<ID>>
Property.Property = < <ID> : <Type> >
prettyprint-Definition = ?Entity(x, prop*) ; ![ H( [SOpt(HS(), "0")] , [ S("entity ") , <pp-one-Z(prettyprint-ID)> x , S(" {") ] ) , <pp-indent(|"2")> [<pp-V-list(prettyprint-Property)> p*] , H([SOpt(HS(), "0")], [S("}")] ) ]
context-free syntax "entity" ID "{" Property* "}" -> Definition {"Entity"} ID -> Type {"Type"} ID ":" Type -> Property {"Property"}
completions
completion template Definition : "entity ID { }" = "entity " <ID:ID> " {\n\t" (cursor) "\n}" (blank) completion template Type : "ID<>" = <ID:ID> "<" <:Type> ">" completion template Property : "ID : ID " = <ID:ID> " : " <ID:Type> (blank)
signature constructors Entity : ID * List(Property) -> Definition Type : ID -> Type Property : ID * Type -> Property
Type Checking
Example: Checking Method Calls
module renaming
entity String { function plus(that:String): String { ... } } entity User { firstName : String lastName : String fullName : String function rename(first: String, last: String) { fullName := first.plus(0); // argument has wrong type fullName := first.plus(); // too few arguments fullName := first.plus(last, last); // too many arguments fullName := first.plus(last); // correct } }
Constraint Checks
rules // errors type-error : (e, t) -> (e, $[Type [<pp>t] expected instead of [<pp>t']]) where <type-of> e => t'; not(<subtype>(t', t))
rules // functions constraint-error: |[ e.x(e*) ]| -> (x, $[Expects [m] arguments, found [n]]) where <lookup-type> x => (t*, t); <length> e* => n; <length> t* => m; not(<eq>(m, n)) constraint-error: |[ e.x(e*) ]| -> <zip; filter(type-error)> (e*, t*) where <lookup-type> x => (t*, t)
Type Analysis
rules // constants type-of : |[ true ]| -> TypeBool() type-of : |[ false ]| -> TypeBool() type-of : Int(i) -> TypeInt() type-of : String(x) -> TypeString()
Types of Names
rules // names
type-of : Var(x) -> <lookup-type> x
type-of : |[ e.x ]| -> t where <lookup-type> x => t
type-of : |[ e.x(e*) ]| -> t where <lookup-type> x => (t*, t) type-of : |[ f(e*) ]| -> t where <lookup-type> f => (t*, t)
Analysis: Name Resolution
+
Definitions and References
test type refers to entity [[ module test entity [[String]] { } entity User { first : [[String]] last : String }]] resolve #2 to #1
definition
reference
From Tree to Graph
Module( "test", [ Entity("String", []) , Entity( "User" , [ Property("first", ) , Property("last", ) ] ) ])
Unique Names
Module( "users"{[Module(), "users"]} , [ Entity("String"{[Type(), "String", "users"]}, []) , Entity( "User"{[Type(), "User", "users"]} , [ Property( "first"{[Property(), "first", "User", "users"]} , Type("String"{[Type(), "String", "users"]})) , Property( "last"{[Property(), "last", "User", "users"]} , Type("String"{[Type(), "String", "users"]})) ] ) ] )
+ index mapping unique names to values
Spoofax Name Binding Language
namespaces Module Type
rules
Entity(c, _) : defines Type c Type(x, _) : refers to Type x Module(m, _) : defines Module m scopes Type Imports(m) : imports Type from Module m
See the full story in SLE talk on Friday
abstract from name resolution algorithmics
spoofax.org
http://spoofax.org