280 likes | 389 Views
Strategic Programming in Java Pierre-Etienne Moreau Antoine Reilles. Stratego User Day, December, 1 st , 2006. Context. Protheo group, Nancy (C. Kirchner) 4 permanent researchers 12 PhD students Application domain: improve the quality of software
E N D
Strategic Programming in JavaPierre-Etienne MoreauAntoine Reilles Stratego User Day, December, 1st, 2006
Context • Protheo group, Nancy (C. Kirchner) • 4 permanent researchers • 12 PhD students • Application domain: improve the quality of software • semantics of (rule and graph based) languages: Rho calculus • theorem provers: deduction modulo, termination • rule based programming: Elan, …, Tom • 4 PhD students involved • program analysis • compilation • certification • language design (constraint, business rule)
Motivations • After 20 years of research in rewriting • We are convinced that: • specifying by rewriting is a nice idea • But, we now want to: • make rewriting usable in practice • integrate rewriting into existing programming environments
Motivations • Our approach: • take the best of ASF+SDF, ELAN, Maude, and Stratego • design and develop a set of Java tools that offer similar constructs: • algebraic data-type (Gom) • equational pattern matching (Tom) • strategic programming (Java Library + Gom) • simplification rules (Tom + Gom)
Short presentation of Tom • A Java program is a Tom program • import pil.term.types.*; • import java.util.*; • public class Pil { • … • public final static void main(String[] args) { • Expr p1 = …; • System.out.println("p1 = " + p1); • … • } • }
Tom adds algebraic data-types to Java • Gom supports many-sorted first order signature • import pil.term.types.*; • import java.util.*; • public class Pil { • … • public final static void main(String[] args) { • Expr p1 = …; • System.out.println("p1 = " + p1); • … • } • } %gom { module Term imports int String abstract syntax Bool = | True() | False() | Eq(e1:Expr, e2:Expr) Expr = | Var(name:String) | Let(var:Expr, e:Expr, body:Expr) | Seq(i1:Expr, i2:Expr) | If(cond:Bool, e1:Expr, e2:Expr) | a() | b() }
An algebraic term is a Java object • Back-quote (`) to build a term • import pil.term.types.*; • import java.util.*; • public class Pil { • … • public final static void main(String[] args) { • Expr p1 = • System.out.println("p1 = " + p1); • … • } • } %gom { module Term imports int String abstract syntax Bool = | True() | False() | Eq(e1:Expr, e2:Expr) Expr = | Var(name:String) | Let(var:Expr, e:Expr, body:Expr) | Seq(i1:Expr, i2:Expr) | If(cond:Bool, e1:Expr, e2:Expr) | a() | b() } `Let(Var("x"),a(), Let(Var("y"),b(),Var("x")));
Tom adds pattern matching to Java • %match supports syntactic and associative pattern matching • import pil.term.types.*; • import java.util.*; • public class Pil { • … • public final static void main(String[] args) { • Expr p1 = …; • System.out.println("p1 = " + p1); • …(pretty(p1)); • } • … • } public static String pretty(Object o) { %match(o) { Var(name) -> { return `name; } Let(var,expr,body) -> { return "let " + pretty(`var) + " <- " + pretty(`expr) + " in " + pretty(`body); } … } return o.toString(); }
Summary • Tom offers 3 new constructs: • %gom : generate a typed data structure • ` : build a term • %match : syntactic matching and list-matching Data structure Gom Tom Pattern matching Tom+Java Java
Strategies in Tom • Tom is powerful, but clearly not enough • There is no separation between Transformation and Control • Question: • starting from the JJTraveler library (J. Visser, OOPSLA 2001) • studying ASF+SDF, ELAN and Stratego • can we design a powerful strategy language, usable in Java ? • The answer is Yes
First inspiration: Stratego [E. Visser] • Elementaty strategies • Id • Fail • Transformation rule • Basic strategies • Sequence • Choice • All • One • Not • Parameterized and recursive strategies • Try(s) = Choice(s,Identity) • Repeat(s) = Try(Sequence(s,Repeat(s))) • Repeat(s) = mu x . Try(Sequence(s,x)) • BottomUp(s) = mu x . Sequence(All(x),s)
Second inspiration: JJTraveler [J. Visser] • public interface Visitor { • /** Pay a visit to any visitable object. */ • public Visitable visit(Visitable any) throws VisitFailure; • } • public interface Visitable { • /** Returns the number of children of any visitable. */ • public abstract int getChildCount(); • /** Returns the ith child of any visitable. */ • public abstract Visitable getChildAt(int i); • /** Replaces the ith child of any visitable, and returns this visitable. */ • public abstract Visitable setChildAt(int i, Visitable child); • }
Visitor combination and traversal control module term T1 = a() | b() | f(arg:T1) T2 = … class termForward { Visitable visit(Visitable v) { return ((termAbstractType)v).accept(this); } T1 visit_T1(T1 arg) { return default.visit(arg); } T2 visit_T2(T2 arg) { return default.visit(arg); } } class T1 extends termAbstractType { termAbstractType accept(termVisitor v) { v.visit_T1(this); } } abstract class termAbstractType { termAbstractType accept(termVisitor v); } interface termVisitor { T1 visit_T1(T1 arg); T2 visit_T2(T2 arg); } class Rule extends termForward { public Rule() { super(new Fail()); } T1 visit_T1(T1 arg) { // code that implements lhs -> rhs // … return new Fail().visit(arg); } } This is a Strategy!
TopDown(Rule) Sequence s All T1 t = `f(a,f(a,b)); Visitor v = new TopDown(new Rule()); v.visit(t); • TopDown(s) = mu x . Sequence(s,All(x)) public class TopDown extends Sequence { public TopDown(Visitor s) { super(s,new All(this)); } } public class TopDown extends Sequence { public TopDown(Visitor s) { super(s,null); then = new All(this); } } public class Sequence implements Visitor { public Visitor first; public Visitor then; … public Visitable visit(Visitable any) throws VisitFailure { return then.visit(first.visit(any)); } }
Comparison with Stratego • Each elementary strategy is an object • Execution is done via interpretation (method call) • Failure is implemented via Java exception
Elementary strategies in Tom • A Rule is an elementary strategy • Identity and Fail are also elementary strategies • a strategy is built using ` • “x” and “z” are parameters of sort String • the rule is applied once, at root position %strategy RenameVar(n1:String,n2:String) extends Identity() { visit Expr { Var(n) -> { if(`n==n1) return `Var(n2); } } } Expr p1 = `Let(Var("x"),a(), Let(Var("y"),b(),Var("x"))); Expr p2 = `RenameVar("x","z").visit(p1); > Let(Var("x"),a(), Let(Var("y"),b(),Var("x")))
Basic strategies Expr p1 = `Let(Var("x"),a(), Let(Var("y"),b(),Var("x"))); Expr p2 = `BottomUp(RenameVar("x","z”)).visit(p1); > Let(Var(”z"),a(), Let(Var("y"),b(),Var(”z"))) • Big difference wrt. JJTraveler: • BottomUp is user defined using the mu operator public Strategy BottomUp(Strategy s) { return `mu(MuVar("x"),Sequence(All(MuVar("x")),s)); }
Key ingredients Mu Mu x Sequence x Sequence Rule All Rule All x • A strategy is a term • can be built • matched • traversed • Mu-expansion is done by applying a BottomUp
How does it work? • `Choice(Rule(),Fail()); • is equivalent to: • new Choice(new Rule(), new Fail()) • We have Choice(x,y) << s • `BottomUp(Expand()).apply(s) • returns a new Strategy • %typeterm Strategy { • implement • equals(t1,t2) • } • %op Strategy Fail() { • is_fsym(t) • make() • } • %op Strategy Choice(s1, s2) { • is_fsym(t) • get_slot(s1,t) • get_slot(s2,t) • make(first,then) • } • { VisitableVisitor } • { t1.equals(t2) } • { t instanceof Fail } • { new Fail() } • { t instanceof Choice} • { t.getChildAt(FIRST) } • { t.getChildAt(THEN) } • { new Choice(first,then) }
Parameterized strategies • a strategy can be parameterized by values: • a strategy can do side effects: %strategy RenameVar(n1:String,n2:String) extends Identity() { … } %strategy CollectVar(c:Collection) extends Identity() { visit Expr { v@Var(_) -> { c.add(v) } } } Collection set = new HashSet(); `BottomUp(CollectVar(set)).apply(p1);
Strategies parameterized by a strategy Mu x Sequence All Sequence Debug Rule • sometimes we need to recursively call the current calling strategy • this breaks separation between rules and control • solution: give the calling context as argument • `BottomUp(Rule()).visit() • %strategy Rule() extends Identity() { • visit Expr { • Let(v,e,body) -> { … `BottomUp(Rule()).visit(body); … } • } • } %strategy Rule(s:Strategy) extends Identity() { visit Expr { Let(v,e,body) -> { %match(s) { Mu(x, Choice(…)) -> { Strategy ds = `debugWeaver().visit(s); … `ds.visit(body); } } } } } `mu(MuVar(“s”),BottomUp(Rule(MuVar(“s”)).visit() %strategy Rule(s:Strategy) extends Identity() { visit Expr { Let(v,e,body) -> { … `s.apply(body); } } }
From ASF+SDF, ELAN, Maude and Stratego • From ASF+SDF • list-matching (associative matching with neutral element) • From Stratego • traversal strategies (All, One) • naming and semantics of operators • From ELAN • statically typed rules and strategies • non-determinism experience • no AC, no backtracking • strategy as a term • New in Tom • identity based strategies • strategy can be applied on a strategy • notion of position
Identity based strategies • %strategy Rule() extends Fail() { • // a -> b • // default: failure • } • Innermost(s) = mu x . All(x) ; ((s ; x) <+ Id) • `Innermost(Rule()).visit(t); • %strategy RuleId() extends Identity() { • // a -> b • // default: x -> x • } • InnermostId(s) = mu x . All(x) ; SequenceId(s,x) • `InnermostId(RuleId()).visit(t);
Other nice features • A strategy knows where it is applied (global information) • useful to implement non-deterministic search (prover, model checker) • Gom provides a hook mechanism • useful to maintain terms in canonical form (sorted list) • Anti-pattern matching • car(white,!ecological) • XML pattern matching • <A>(_*, x, _*, x, _*)</A> -> { … } • Strategy library for graphs • useful for bytecode analysis
Conclusion • There are many similarities with Stratego • Integration of rules and strategies in a Java environment • Pure Java implementation • Main advantages: • Tom and Java data-structures are compliant • makes debugging easier • any Java program is a Tom program • no deployment problem • Room for improvements: • better integration into Java • connection to SDF • support for concrete syntax