Providing Rapid Feedback in Generated Modular Language Environments - - PowerPoint PPT Presentation

providing rapid feedback in generated modular language
SMART_READER_LITE
LIVE PREVIEW

Providing Rapid Feedback in Generated Modular Language Environments - - PowerPoint PPT Presentation

Providing Rapid Feedback in Generated Modular Language Environments Lennart Kats ( me ) Maartje de Jonge Emma Nilsson-Nyman Eelco Visser OOPSLA 2009 October 29, 2009 Software Engineering Research Group


slide-1
SLIDE 1

Providing Rapid Feedback in Generated Modular Language Environments

Lennart Kats (me) Maartje de Jonge Emma Nilsson-Nyman Eelco Visser

Software Engineering Research Group

OOPSLA 2009 October 29, 2009

slide-2
SLIDE 2

Domain-Specific Languages

  • Encapsulate domain knowledge
  • Eliminate boilerplate code
  • Domain-specific analysis / verification /
  • ptimization
slide-3
SLIDE 3

UI Data models

Integration and Composition of DSLs

HQL Workflow Access control Data validation Actions

(www.webdsl.org)

slide-4
SLIDE 4

Markus Voelter [Practical Product Lines 2009] DSLs without IDE support are a nonstarter!

slide-5
SLIDE 5

Spoofax/IMP: Efficient Editor Development

  • Existing platform: Eclipse with IMP
  • DSLs to define editor components
  • Grammar and a parser generator:

SDF, (J)SGLR

slide-6
SLIDE 6

Code Folding Semantic errors Outline References HQL Brace matching

slide-7
SLIDE 7

FOLDING/OUTLINE entity User { ... } entity BlogPost { poster : User body : String } REFERENCE

slide-8
SLIDE 8

entity User { name : String password : String homepage } entity BlogPost { poster : User body : String }

What about incorrect and incomplete programs?

NORMAL PARSING }

slide-9
SLIDE 9

entity User { name : String password : String homepage } entity BlogPost { poster : User body : String }

What about incorrect and incomplete programs?

NORMAL PARSING ERROR NEVER PARSED Parse failure: No abstract syntax tree

slide-10
SLIDE 10

Mini-Demo

Error Recovery in a data modeling language

slide-11
SLIDE 11

Parsing with SDF and SGLR

  • Language composition without

shift/reduce conflicts

  • Ambiguities can be inspected
  • Declarative disambiguation
slide-12
SLIDE 12

token token token token token token token token token token token

x x x

token

x

Recovery: Backtracking, skip/insert tokens, etc.

Normal LR Parser (S)GLR Parser

Recovery: ?

slide-13
SLIDE 13

Do we really need to dive into this intimidating algorithm?

slide-14
SLIDE 14

Island Grammars

IDENTIFICATION DIVISION. PROGRAM-ID. EXAMPLE. PROCEDURE DIVISION. CALL X. YADA. YADA YADA. CALL Y. CALL X. CALL Y. WATER WATER

  • Parse only interesting bits (Islands)
  • Skip the remaining characters (Water)

[Van Deursen & Kuipers, 1999] Grammar-based “error recovery”!

slide-15
SLIDE 15

Island Grammars

~[\ \t\n]+ → WATER {avoid} [Van Deursen & Kuipers, 1999]

slide-16
SLIDE 16

Running Example: Java

slide-17
SLIDE 17
  • 1. Take the entire Java grammar
  • 2. Add water
  • 3. Mix it together

Error Recovery Recipe

slide-18
SLIDE 18

Mixing Java with Water

public class TemperatureMonitor { private Fridge fridge; public void trigger(Temperature temp) { if (temp.greaterThan(MAX)) // missing { fridge.startCooling(); } return; } public TemperatureMonitor(Fridge fridge) { this.fridge = fridge; } } ERROR

slide-19
SLIDE 19

Mixing Java with Water

public class TemperatureMonitor { private Fridge fridge; public void trigger(Temperature temp) { if (temp.greaterThan(MAX)) // missing { fridge.startCooling(); } return; } public TemperatureMonitor(Fridge fridge) { this.fridge = fridge; } } NORMAL PARSING ERROR

slide-20
SLIDE 20

Mixing Java with Water

public class TemperatureMonitor { private Fridge fridge; public void trigger(Temperature temp) { if (temp.greaterThan(MAX)) // missing { fridge.startCooling(); } return; } public TemperatureMonitor(Fridge fridge) { this.fridge = fridge; } } } NORMAL PARSING ERROR

slide-21
SLIDE 21

Mixing Java with Water

public class TemperatureMonitor { private Fridge fridge; public void trigger(Temperature temp) { if (temp.greaterThan(MAX)) // missing { fridge.startCooling(); } return; } public TemperatureMonitor(Fridge fridge) { this.fridge = fridge; } } } NORMAL PARSING ERROR UNEXPECTED KEYWORD

slide-22
SLIDE 22

Mixing Java with Water

public class TemperatureMonitor { private Fridge fridge; public void trigger(Temperature temp) { if (temp.greaterThan(MAX)) // missing { fridge.startCooling(); } return; } public TemperatureMonitor(Fridge fridge) { this.fridge = fridge; } } New production: WATER → MethodDec {cons(“WATER”)} New production: WATER → MethodDec {cons(“WATER”),recover}

slide-23
SLIDE 23

New production: WATER → MethodDec {cons(“WATER”),recover} New production: WATER → Stm {cons(“WATER”),recover}

Mixing Java with Water

public class TemperatureMonitor { private Fridge fridge; public void trigger(Temperature temp) { if (temp.greaterThan(MAX)) // missing { fridge.startCooling(); } return; } public TemperatureMonitor(Fridge fridge) { this.fridge = fridge; } } WATER

slide-24
SLIDE 24

Mixing Java with Water

public class TemperatureMonitor { private Fridge fridge; public void trigger(Temperature temp) { if (temp.greaterThan(MAX)) // missing { fridge.startCooling(); } return; } public TemperatureMonitor(Fridge fridge) { this.fridge = fridge; } } New production: WATER → Stm {cons(“WATER”),recover} WATER

slide-25
SLIDE 25

Mixing Java with Water

public class TemperatureMonitor { private Fridge fridge; public void trigger(Temperature temp) { if (temp.greaterThan(MAX)) // missing { fridge.startCooling(); } return; } public TemperatureMonitor(Fridge fridge) { this.fridge = fridge; } } WATER LAYOUT PROBLEMATIC TOKEN

slide-26
SLIDE 26

Mixing Java with Water

public class TemperatureMonitor { private Fridge fridge; public void trigger(Temperature temp) { if (temp.greaterThan(MAX)) // missing { fridge.startCooling(); } return; } public TemperatureMonitor(Fridge fridge) { this.fridge = fridge; } } New production: WATER → LAYOUT {cons(“WATER”),recover} WATER LAYOUT PROBLEMATIC TOKEN

slide-27
SLIDE 27

Mixing Java with Water

public class TemperatureMonitor { private Fridge fridge; public void trigger(Temperature temp) { if (temp.greaterThan(MAX)) // missing { fridge.startCooling(); } return; } public TemperatureMonitor(Fridge fridge) { this.fridge = fridge; } } WATER New production: WATER → LAYOUT {cons(“WATER”),recover} LAYOUT

slide-28
SLIDE 28

Reflection: Water-based Recovery Rules

  • Works with any existing grammar
  • Can only remove fragments
slide-29
SLIDE 29

Danger of Floods

public class Fridge { public void startCooling() { cooling.commence(); // missing } public void onResetButtonPress() { log.message(“Reset button pressed”); power.reboot(); } }

slide-30
SLIDE 30

Danger of Floods

public class Fridge { public void startCooling() { cooling.commence(); // missing } public void onResetButtonPress() { log.message(“Reset button pressed”); power.reboot(); } } WATER So why not parse methods with missing closing brackets?

slide-31
SLIDE 31

“if” “(” Expr Stm → Stm {cons(“If”), recover}

Productions for Missing Literals

“if” Expr “)” Stm → Stm {cons(“If”), recover} “if” Expr Stm → Stm {cons(“If”), recover} “while” “(“ Expr Stm → Stm {cons(“While”), ...} ... and this, and this, and this: why not add rules like this: not going to scale.

slide-32
SLIDE 32

IF BOPEN … BCLOSE … → … [\i][\f] → IF [\(] → BOPEN [\)] → BCLOSE

Productions for Missing Literals

→ BCLOSE {recover} → “}” {recover} “)” so, we can write (using the literal instead of BCLOSE): what it means internally: “if” “(” Expr “)” Stm → Stm {cons(“If”)}

slide-33
SLIDE 33

Applying Insertion Rules

public class Fridge { public void startCooling() { cooling.commence(); // missing } public void onResetButtonPress() { log.message(“Reset button pressed”); power.reboot(); } } New production: → “}” {recover}

slide-34
SLIDE 34

Applying Insertion Rules

public class Fridge { public void startCooling() { cooling.commence(); // missing } public void onResetButtonPress() { log.message(“Reset button pressed”); power.reboot(); } } INSERT } New production: → “}” {recover}

slide-35
SLIDE 35

So who's gonna write all those recovery rules?

  • Water
  • Closing brackets (“}”)
  • Opening brackets (“{”)
  • Separators (“,”)
  • Comments
  • String literals

Recovery Rules

We derive them from the grammar!

slide-36
SLIDE 36

Customization of Recovery Rules

→ “class” {reject} “[|” → “|[“ {recover} “|]” → “]|” {recover}

slide-37
SLIDE 37

Putting Things Together

y = f ( x + 1 ; y = f ( x + 1 ) ; y = f ( x ) + 1 ; y = f + 1 ; y = f ( 1 ) ; y = ( x ) + 1 ; y = ( x + 1 ) ; y = x + 1 ; y = f ; y = ( x ) ; y = f ( ) ;

  • Water
  • Insertion: “)”

y = x ; f ( x + 1 ) ; f ( 1 ) ; y ( x + 1 ) ; y ( x ) ; y () ; y ( 1 ) ; y = 1 ; ;

slide-38
SLIDE 38

Putting Things Together

For recovery, parallel parsing does not scale...

slide-39
SLIDE 39

Putting Things Together

Why not do backtracking for recovery?

slide-40
SLIDE 40

Recovery Using Backtracking

public class TemperatureMonitor { private Fridge fridge; public void trigger(Temperature temp) { if (temp.greaterThan(MAX)) // missing { fridge.startCooling(); } return; } public TemperatureMonitor(Fridge fridge) { this.fridge = fridge; } } ERROR

slide-41
SLIDE 41

Recovery Using Backtracking

public class TemperatureMonitor { private Fridge fridge; public void trigger(Temperature temp) { if (temp.greaterThan(MAX)) // missing { fridge.startCooling(); } return; } public TemperatureMonitor(Fridge fridge) { this.fridge = fridge; } }

  • 1. NORMAL PARSING

ERROR

slide-42
SLIDE 42

Recovery Using Backtracking

public class TemperatureMonitor { private Fridge fridge; public void trigger(Temperature temp) { if (temp.greaterThan(MAX)) // missing { fridge.startCooling(); } return; } public TemperatureMonitor(Fridge fridge) { this.fridge = fridge; } }

  • 1. NORMAL PARSING

ERROR POINT OF DETECTION

slide-43
SLIDE 43

Recovery Using Backtracking

public class TemperatureMonitor { private Fridge fridge; public void trigger(Temperature temp) { if (temp.greaterThan(MAX)) // missing { fridge.startCooling(); } return; } public TemperatureMonitor(Fridge fridge) { this.fridge = fridge; } }

  • 2. BACKTRACKING
  • 1. NORMAL PARSING
slide-44
SLIDE 44

Recovery Using Backtracking

public class TemperatureMonitor { private Fridge fridge; public void trigger(Temperature temp) { if (temp.greaterThan(MAX)) // missing { fridge.startCooling(); } return; } public TemperatureMonitor(Fridge fridge) { this.fridge = fridge; } } 1 RULE 1 RULE 2 RULES 1 RULE 2 RULES 3 RULES 1 RULE 2 RULES 3 RULES 4 RULES {

slide-45
SLIDE 45

Evaluation

Implementation:

  • JSGLR
  • Spoofax/IMP (WebDSL, Stratego, PIL, ...)

Test grammars:

  • Java
  • Java-SQL
  • Stratego
  • Stratego-Java
slide-46
SLIDE 46

Evaluation

Measurements:

  • qualitative [Penello & DeRemer '78]:

excellent / good / poor

  • tree alignment distance [Jian et al '94]
  • textual 'diffs'
slide-47
SLIDE 47

Recovery Quality (Stratego-Java)

slide-48
SLIDE 48

Continued Work

“Natural and Flexible Error Recovery for Generated Parsers” [SLE 2009]

  • indentation-based region selection
  • fall-back mechanism
  • bridge parsing [Nilsson-Nyman et al, 2008]

(www.strategoxt.org/Stratego/PermissiveGrammars)

slide-49
SLIDE 49

Continued Work

“Natural and Flexible Error Recovery for Generated Parsers” [SLE 2009]

  • comparison with JDT
  • performance, quality
  • synergy between recovery techniques

(www.strategoxt.org/Stratego/PermissiveGrammars)

slide-50
SLIDE 50

www.strategoxt.org/Stratego/PermissiveGrammars www.strategoxt.org/Stratego/Spoofax-IMP www.lennartkats.net

Software Engineering Research Group

Conclusion

SGLR/SDF

  • modular specifications
  • composable languages

Recovery production rules

  • transparent
  • flexible
  • language-independent