SLIDE 1 From Object Algebras to Attribute Grammars
Tillmann Rendel · Jonathan Brachthäuser · Klaus Ostermann University of Marburg · University of Tübingen http://www.informatik.uni-marburg.de/~rendel/oa2ag
Presentation by Tillmann Rendel at the International Conference
- n Object-Oriented Programming, Systems, Languages, and Applications
Portland, Oregon, October 23, 2014
SLIDE 2
Tree Traversals
1/15
SLIDE 3
Tree Traversals
1/15
SLIDE 4 Tree Traversals
How to structure a program that contains multiple traversals
1/15
SLIDE 5 Visitor Pattern
in object-oriented programming
Folds & Traversal Schemes
in functional programming
Church Encoding
in theoretical work
Attribute Grammars
for compiler construction
2/15
SLIDE 6 Visitor Pattern
in object-oriented programming
Folds & Traversal Schemes
in functional programming
Church Encoding
in theoretical work
Attribute Grammars
for compiler construction
Hinze (2006) Chirica & Martin (1979) Johnsson (1987) Gibbons (2006) Buchlovsky & Thielecke (2006) Oliveira et al. (2008) Oliveira et al. (2013) Middelkoop et al. (2011)
2/15
SLIDE 7 Visitor Pattern
in object-oriented programming
Folds & Traversal Schemes
in functional programming
Church Encoding
in theoretical work
Attribute Grammars
for compiler construction
Hinze (2006) Chirica & Martin (1979) Johnsson (1987) Gibbons (2006) Buchlovsky & Thielecke (2006) Oliveira et al. (2008) Oliveira et al. (2013)
this paper
Middelkoop et al. (2011)
2/15
SLIDE 8 Visitor Pattern
in object-oriented programming
Folds & Traversal Schemes
in functional programming
Church Encoding
in theoretical work
Object Algebras
in Scala
Attribute Grammars
for compiler construction
Hinze (2006) Chirica & Martin (1979) Johnsson (1987) Gibbons (2006) Buchlovsky & Thielecke (2006) Oliveira et al. (2008) Oliveira et al. (2013)
this paper
Middelkoop et al. (2011)
2/15
SLIDE 9
Bottom-Up Data Flow
3/15
SLIDE 10 Synthesized Attributes
Grammar
e0 → n { Lit } e1 → e2 "+" e3 { Add }
Signature
trait Sig[E] { def Lit: Int ⇒ E def Add: (E, E) ⇒ E }
Equations
e0.value = n e1 .value = e2.value + e3.value
Algebra
val Alg = new Sig[Int] { def Lit = n ⇒ n def Add = (e2, e3) ⇒ e2 + e3 }
4/15
SLIDE 11 Synthesized Attributes
Grammar
e0 → n { Lit } e1 → e2 "+" e3 { Add }
Signature
trait Sig[E] { def Lit: Int ⇒ E def Add: (E, E) ⇒ E }
Equations
e0.value = n e1 .value = e2.value + e3.value
Algebra
val Alg = new Sig[Int] { def Lit = n ⇒ n def Add = (e2, e3) ⇒ e2 + e3 }
4/15
SLIDE 12 Synthesized Attributes
Grammar
e0 → n { Lit } e1 → e2 "+" e3 { Add }
Signature
trait Sig[E] { def Lit: Int ⇒ E def Add: (E, E) ⇒ E }
Equations
e0.value = n e1 .value = e2.value + e3.value
Algebra
val Alg = new Sig[Int] { def Lit = n ⇒ n def Add = (e2, e3) ⇒ e2 + e3 }
4/15
SLIDE 13 Synthesized Attributes
Grammar
e0 → n { Lit } e1 → e2 "+" e3 { Add }
Signature
trait Sig[E] { def Lit: Int ⇒ E def Add: (E, E) ⇒ E }
Equations
e0.value = n e1 .value = e2.value + e3.value
Algebra
val Alg = new Sig[Int] { def Lit = n ⇒ n def Add = (e2, e3) ⇒ e2 + e3 }
4/15
SLIDE 14 Synthesized Attributes
Grammar
e0 → n { Lit } e1 → e2 "+" e3 { Add }
Signature
trait Sig[E] { def Lit: Int ⇒ E def Add: (E, E) ⇒ E }
Equations
e0.value = n e1 .value = e2.value + e3.value
Algebra
val Alg = new Sig[Int] { def Lit = n ⇒ n def Add = (e2, e3) ⇒ e2 + e3 }
4/15
SLIDE 15 Synthesized Attributes
Grammar
e0 → n { Lit } e1 → e2 "+" e3 { Add }
Signature
trait Sig[E] { def Lit: Int ⇒ E def Add: (E, E) ⇒ E }
Equations
e0.value = n e1 .value = e2.value + e3.value
Algebra
val Alg = new Sig[Int] { def Lit = n ⇒ n def Add = (e2, e3) ⇒ e2 + e3 }
4/15
SLIDE 16 Synthesized Attributes
Grammar
e0 → n { Lit } e1 → e2 "+" e3 { Add }
Signature
trait Sig[E] { def Lit: Int ⇒ E def Add: (E, E) ⇒ E }
Equations
e0.value = n e1 .value = e2.value + e3.value
Algebra
val Alg = new Sig[Int] { def Lit = n ⇒ n def Add = (e2, e3) ⇒ e2 + e3 }
4/15
SLIDE 17 Synthesized Attributes
Grammar
e0 → n { Lit } e1 → e2 "+" e3 { Add }
Signature
trait Sig[E] { def Lit: Int ⇒ E def Add: (E, E) ⇒ E }
Equations
e0.value = n e1 .value = e2.value + e3.value
Algebra
val Alg = new Sig[Int] { def Lit = n ⇒ n def Add = (e2, e3) ⇒ e2 + e3 }
4/15
SLIDE 18 Synthesized Attributes
Grammar
e0 → n { Lit } e1 → e2 "+" e3 { Add }
Signature
trait Sig[E] { def Lit: Int ⇒ E def Add: (E, E) ⇒ E }
Equations
e0.value = n e1 .value = e2.value + e3.value
Algebra
val Alg = new Sig[Int] { def Lit = n ⇒ n def Add = (e2, e3) ⇒ e2 + e3 }
4/15
SLIDE 19 Synthesized Attributes
Grammar
e0 → n { Lit } e1 → e2 "+" e3 { Add }
Signature
trait Sig[E] { def Lit: Int ⇒ E def Add: (E, E) ⇒ E }
Equations
e0.value = n e1 .value = e2.value + e3.value
Algebra
val Alg = new Sig[Int] { def Lit = n ⇒ n def Add = (e2, e3) ⇒ e2 + e3 }
4/15
SLIDE 20
Top-Down Data Flow
5/15
SLIDE 21 Inherited Attributes
Grammar
e0 → n { Lit } e1 → e2 "+" e3 { Add }
Signature
trait Sig[E] { def Add1: E ⇒ E def Add2: (E, E) ⇒ E }
Equations
e2 .left = true
e3.left = false
Algebra
val Alg = new Sig[Bool] { def Add1 = e ⇒ true def Add2 = (e1, e2) ⇒ false }
6/15
SLIDE 22 Inherited Attributes
Grammar
e0 → n { Lit } e1 → e2 "+" e3 { Add }
Signature
trait Sig[E] { def Add1: E ⇒ E def Add2: (E, E) ⇒ E }
Equations
e2 .left = true
e3.left = false
Algebra
val Alg = new Sig[Bool] { def Add1 = e ⇒ true def Add2 = (e1, e2) ⇒ false }
6/15
SLIDE 23 Inherited Attributes
Grammar
e0 → n { Lit } e1 → e2 "+" e3 { Add }
Signature
trait Sig[E] { def Add1: E ⇒ E def Add2: (E, E) ⇒ E }
Equations
e2 .left = true
e3.left = false
Algebra
val Alg = new Sig[Bool] { def Add1 = e ⇒ true def Add2 = (e1, e2) ⇒ false }
6/15
SLIDE 24 Inherited Attributes
Grammar
e0 → n { Lit } e1 → e2 "+" e3 { Add }
Signature
trait Sig[E] { def Add1: E ⇒ E def Add2: (E, E) ⇒ E }
Equations
e2 .left = true
e3.left = false
Algebra
val Alg = new Sig[Bool] { def Add1 = e ⇒ true def Add2 = (e1, e2) ⇒ false }
6/15
SLIDE 25 Inherited Attributes
Grammar
e0 → n { Lit } e1 → e2 "+" e3 { Add }
Signature
trait Sig[E] { def Add1: E ⇒ E def Add2: (E, E) ⇒ E }
Equations
e2 .left = true
e3.left = false
Algebra
val Alg = new Sig[Bool] { def Add1 = e ⇒ true def Add2 = (e1, e2) ⇒ false }
6/15
SLIDE 26 Inherited Attributes
Grammar
e0 → n { Lit } e1 → e2 "+" e3 { Add }
Signature
trait Sig[E] { def Add1: E ⇒ E def Add2: (E, E) ⇒ E }
Equations
e2 .left = true
e3.left = false
Algebra
val Alg = new Sig[Bool] { def Add1 = e ⇒ true def Add2 = (e1, e2) ⇒ false }
6/15
SLIDE 27
Composition
7/15
SLIDE 28
Composition
compose 7/15
SLIDE 29
Composition
compose 7/15
SLIDE 30
Composition
assemble 7/15
SLIDE 31 compose( , ) =
Extensible Records
trait HasValue { def value: Int } trait HasLeft { def left: Bool } def mix[A, B]: (A, B) ⇒ A with B
Dependency Tracking
trait Sig[-E, -C, +O] { def Lit: Int ⇒ C ⇒ O def Add: (E, E) ⇒ C ⇒ O }
8/15
SLIDE 32 compose( , ) =
Extensible Records
trait HasValue { def value: Int } trait HasLeft { def left: Bool } def mix[A, B]: (A, B) ⇒ A with B
Dependency Tracking
trait Sig[-E, -C, +O] { def Lit: Int ⇒ C ⇒ O def Add: (E, E) ⇒ C ⇒ O }
8/15
SLIDE 33 compose( , ) =
Extensible Records
trait HasValue { def value: Int } trait HasLeft { def left: Bool } def mix[A, B]: (A, B) ⇒ A with B
Dependency Tracking
trait Sig[-E, -C, +O] { def Lit: Int ⇒ C ⇒ O def Add: (E, E) ⇒ C ⇒ O }
8/15
SLIDE 34 compose( , ) =
Extensible Records
trait HasValue { def value: Int } trait HasLeft { def left: Bool } def mix[A, B]: (A, B) ⇒ A with B
Dependency Tracking
trait Sig[-E, -C, +O] { def Lit: Int ⇒ C ⇒ O def Add: (E, E) ⇒ C ⇒ O }
8/15
SLIDE 35 compose( , ) =
Extensible Records
trait HasValue { def value: Int } trait HasLeft { def left: Bool } def mix[A, B]: (A, B) ⇒ A with B
Dependency Tracking
trait Sig[-E, -C, +O] { def Lit: Int ⇒ C ⇒ O def Add: (E, E) ⇒ C ⇒ O }
8/15
SLIDE 36 compose( , ) =
Extensible Records
trait HasValue { def value: Int } trait HasLeft { def left: Bool } def mix[A, B]: (A, B) ⇒ A with B
Dependency Tracking
trait Sig[-E, -C, +O] { def Lit: Int ⇒ C ⇒ O def Add: (E, E) ⇒ C ⇒ O }
8/15
SLIDE 37 compose( , ) =
Extensible Records
trait HasValue { def value: Int } trait HasLeft { def left: Bool } def mix[A, B]: (A, B) ⇒ A with B
Dependency Tracking
trait Sig[-E, -C, +O] { def Lit: Int ⇒ C ⇒ O def Add: (E, E) ⇒ C ⇒ O }
8/15
SLIDE 38 compose( , ) =
Extensible Records
trait HasValue { def value: Int } trait HasLeft { def left: Bool } def mix[A, B]: (A, B) ⇒ A with B
Dependency Tracking
trait Sig[-E, -C, +O] { def Lit: Int ⇒ C ⇒ O def Add: (E, E) ⇒ C ⇒ O }
8/15
current node
SLIDE 39 compose( , ) =
Extensible Records
trait HasValue { def value: Int } trait HasLeft { def left: Bool } def mix[A, B]: (A, B) ⇒ A with B
Dependency Tracking
trait Sig[-E, -C, +O] { def Lit: Int ⇒ C ⇒ O def Add: (E, E) ⇒ C ⇒ O }
8/15
current node children
SLIDE 40 compose( , ) =
Extensible Records
trait HasValue { def value: Int } trait HasLeft { def left: Bool } def mix[A, B]: (A, B) ⇒ A with B
Dependency Tracking
trait Sig[-E, -C, +O] { def Lit: Int ⇒ C ⇒ O def Add: (E, E) ⇒ C ⇒ O }
8/15
current node context from parent
SLIDE 41 compose( , ) =
Extensible Records
trait HasValue { def value: Int } trait HasLeft { def left: Bool } def mix[A, B]: (A, B) ⇒ A with B
Dependency Tracking
trait Sig[-E, -C, +O] { def Lit: Int ⇒ C ⇒ O def Add: (E, E) ⇒ C ⇒ O }
8/15
current node
SLIDE 42 compose( , ) =
Extensible Records
trait HasValue { def value: Int } trait HasLeft { def left: Bool } def mix[A, B]: (A, B) ⇒ A with B
Dependency Tracking
trait Sig[-E, -C, +O] { def Lit: Int ⇒ C ⇒ O def Add: (E, E) ⇒ C ⇒ O }
8/15
SLIDE 43 compose( , ) =
Extensible Records
trait HasValue { def value: Int } trait HasLeft { def left: Bool } def mix[A, B]: (A, B) ⇒ A with B
Dependency Tracking
trait Sig[-E, -C, +O] { def Lit: Int ⇒ C ⇒ O def Add: (E, E) ⇒ C ⇒ O }
8/15
SLIDE 44 compose( , ) =
Extensible Records
trait HasValue { def value: Int } trait HasLeft { def left: Bool } def mix[A, B]: (A, B) ⇒ A with B
Dependency Tracking
trait Sig[-E, -C, +O] { def Lit: Int ⇒ C ⇒ O def Add: (E, E) ⇒ C ⇒ O }
8/15
SLIDE 45 compose( , ) =
Composing two algebras
def compose [E1, C1, O1, E2, C2 >: C1 with O1, O2] (alg1: Sig[E1, C1, O1], alg2: Sig[E2, C2, O2]): Sig[E1 with E2, C1, O1 with O2]
9/15
SLIDE 46 compose( , ) =
Composing two algebras
def compose [E1, C1, O1, E2, C2 >: C1 with O1, O2] (alg1: Sig[E1, C1, O1], alg2: Sig[E2, C2, O2]): Sig[E1 with E2, C1, O1 with O2]
9/15
SLIDE 47 compose( , ) =
Composing two algebras
def compose [E1, C1, O1, E2, C2 >: C1 with O1, O2] (alg1: Sig[E1, C1, O1], alg2: Sig[E2, C2, O2]): Sig[E1 with E2, C1, O1 with O2]
9/15
SLIDE 48 compose( , ) =
Composing two algebras
def compose [E1, C1, O1, E2, C2 >: C1 with O1, O2] (alg1: Sig[E1, C1, O1], alg2: Sig[E2, C2, O2]): Sig[E1 with E2, C1, O1 with O2]
9/15
SLIDE 49 compose( , ) =
Composing two algebras
def compose [E1, C1, O1, E2, C2 >: C1 with O1, O2] (alg1: Sig[E1, C1, O1], alg2: Sig[E2, C2, O2]): Sig[E1 with E2, C1, O1 with O2]
9/15
SLIDE 50 compose( , ) =
Composing two algebras
def compose [E1, C1, O1, E2, C2 >: C1 with O1, O2] (alg1: Sig[E1, C1, O1], alg2: Sig[E2, C2, O2]): Sig[E1 with E2, C1, O1 with O2]
9/15
SLIDE 51 compose( , ) =
Composing two algebras
def compose [E1, C1, O1, E2, C2 >: C1 with O1, O2] (alg1: Sig[E1, C1, O1], alg2: Sig[E2, C2, O2]): Sig[E1 with E2, C1, O1 with O2]
9/15
SLIDE 52 compose( , ) =
Composing two algebras
def compose [E1, C1, O1, E2, C2 >: C1 with O1, O2] (alg1: Sig[E1, C1, O1], alg2: Sig[E2, C2, O2]): Sig[E1 with E2, C1, O1 with O2]
9/15
SLIDE 53 Assembling a one-pass traversal
def assemble [C, O] (alg1: Sig1[C with O, C, O], alg2: Sig2[C with O, C, C]): Sig[C ⇒ C with O]
assemble( , ) =
10/15
SLIDE 54 Assembling a one-pass traversal
def assemble [C, O] (alg1: Sig1[C with O, C, O], alg2: Sig2[C with O, C, C]): Sig[C ⇒ C with O]
assemble( , ) =
10/15
SLIDE 55 Assembling a one-pass traversal
def assemble [C, O] (alg1: Sig1[C with O, C, O], alg2: Sig2[C with O, C, C]): Sig[C ⇒ C with O]
assemble( , ) =
10/15
SLIDE 56 Assembling a one-pass traversal
def assemble [C, O] (alg1: Sig1[C with O, C, O], alg2: Sig2[C with O, C, C]): Sig[C ⇒ C with O]
assemble( , ) =
10/15
SLIDE 57 Assembling a one-pass traversal
def assemble [C, O] (alg1: Sig1[C with O, C, O], alg2: Sig2[C with O, C, C]): Sig[C ⇒ C with O]
assemble( , ) =
10/15
SLIDE 58
Results
Object algebras correspond to synthesized attributes (bottom-up data-flow) We extend object algebras to support inherited attributes (top-down data flow) We assemble multiple algebras to support L-attributed grammars (arbitrary one-pass compiler) 11/15
SLIDE 59
Results
Object algebras correspond to synthesized attributes (bottom-up data-flow) We extend object algebras to support inherited attributes (top-down data flow) We assemble multiple algebras to support L-attributed grammars (arbitrary one-pass compiler) 11/15
SLIDE 60
Results
Object algebras correspond to synthesized attributes (bottom-up data-flow) We extend object algebras to support inherited attributes (top-down data flow) We assemble multiple algebras to support L-attributed grammars (arbitrary one-pass compiler) 11/15
SLIDE 61
11/15
Results
Object algebras correspond to synthesized attributes (bottom-up data-flow) We extend object algebras to support inherited attributes (top-down data flow) We assemble multiple algebras to support L-attributed grammars (arbitrary one-pass compiler)
SLIDE 62 Modularizing a One-Pass Compiler
- existing one-pass compiler for a subset of C
- 9 nonterminals
- written for teaching at Aarhus university
(not by the authors of the present paper) 12/15
SLIDE 63 Monolithic compiler
1 file 807 lines of Java code entangled
Modularized compiler
1620 lines of Scala code modular 13/15
SLIDE 64
Properties of the Encoding
Modular Attributes are defined and type-checked separately Scalable Scala code size is linear in AG specification size. Compositional Each AG artifact is represented as a Scala value. 14/15
SLIDE 65
Properties of the Encoding
Modular Attributes are defined and type-checked separately Scalable Scala code size is linear in AG specification size. Compositional Each AG artifact is represented as a Scala value. 14/15
SLIDE 66
Properties of the Encoding
Modular Attributes are defined and type-checked separately Scalable Scala code size is linear in AG specification size. Compositional Each AG artifact is represented as a Scala value. 14/15
SLIDE 67
Properties of the Encoding
Modular Attributes are defined and type-checked separately Scalable Scala code size is linear in AG specification size Compositional Each AG artifact is represented as a Scala value. 14/15
SLIDE 68 Conclusions
Object Algebras
in Scala
Attribute Grammars
for compiler construction
Rendel et al. (2014)
15/15
SLIDE 69 Conclusions
Object Algebras
in Scala Benefits for OA
inherited attributes
extensive AG research
encode more AG features
Attribute Grammars
for compiler construction
Rendel et al. (2014)
15/15
SLIDE 70 Conclusions
Object Algebras
in Scala Benefits for OA
inherited attributes
extensive AG research
encode more AG features
Attribute Grammars
for compiler construction Benefits for AG
compositional encoding
abstraction via meta language
AG compiler to object algebras
Rendel et al. (2014)
15/15
SLIDE 71 Conclusions
Object Algebras
in Scala Benefits for OA
inherited attributes
extensive AG research
encode more AG features
Attribute Grammars
for compiler construction Benefits for AG
compositional encoding
abstraction via meta language
AG compiler to object algebras
Rendel et al. (2014)
Thank You!