Slides for keynote at the Modularity 2014 conference in Lugano on April 23, 2014. (A considerable part of the talk consisted of live programming the syntax definition and name binding rules for a little language.)
47. Syntax: Phrase Structure of Programs
}
int fib ( int n ) {
if ( n <= 1 )
return 1 ;
else
return fib ( n - 2 ) + fib ( n - 1 ) ;
48. }int fib ( int n ) { if ( n <= 1 ) return 1 ; else return fib ( n - 2 ) + fib ( n - 1 ) ;
Syntax: Phrase Structure of Programs
49. Exp.IntExp.Int
Exp.Leq Statement.Return Exp.Sub Exp.Sub
Exp.Call Exp.Call
Param.Param
IntType
ID
}int fib ( int n ) { if ( n <= 1 ) return 1 ; else return fib ( n - 2 ) + fib ( n - 1 ) ;
Exp.IntExp.Var Exp.IntExp.VarExp.VarIntType
Syntax: Phrase Structure of Programs
50. Exp.IntExp.Int
Exp.Leq Statement.Return Exp.Sub Exp.Sub
Exp.Call Exp.Call
Exp.Add
Param.Param
IntType
ID
}int fib ( int n ) { if ( n <= 1 ) return 1 ; else return fib ( n - 2 ) + fib ( n - 1 ) ;
Exp.IntExp.Var Exp.IntExp.VarExp.VarIntType
Syntax: Phrase Structure of Programs
51. Exp.IntExp.Int
Exp.Leq Statement.Return Exp.Sub Exp.Sub
Exp.Call Exp.Call
Exp.Add
Statement.Return
Param.Param
IntType
ID
}int fib ( int n ) { if ( n <= 1 ) return 1 ; else return fib ( n - 2 ) + fib ( n - 1 ) ;
Exp.IntExp.Var Exp.IntExp.VarExp.VarIntType
Syntax: Phrase Structure of Programs
52. Exp.IntExp.Int
Exp.Leq Statement.Return Exp.Sub Exp.Sub
Exp.Call Exp.Call
Exp.Add
Statement.Return
Param.Param
IntType
Statement.If
ID
}int fib ( int n ) { if ( n <= 1 ) return 1 ; else return fib ( n - 2 ) + fib ( n - 1 ) ;
Exp.IntExp.Var Exp.IntExp.VarExp.VarIntType
Syntax: Phrase Structure of Programs
53. Exp.IntExp.Int
Exp.Leq Statement.Return Exp.Sub Exp.Sub
Exp.Call Exp.Call
Exp.Add
Statement.Return
Param.Param
IntType
Definition.Function
Statement.If
ID
}int fib ( int n ) { if ( n <= 1 ) return 1 ; else return fib ( n - 2 ) + fib ( n - 1 ) ;
Exp.IntExp.Var Exp.IntExp.VarExp.VarIntType
Syntax: Phrase Structure of Programs
54. Function
IntType IntType
Param
Var Int Int
Leq Return
Return
Call
Add
Var Int
Sub
Var Int
Sub
Call
If
fib n n 1 1 fib n 2 fib n 1
Abstract!
Syntax!
Tree
57. Type
Definition.Function
ID }( ) {Param* Statement*
Exp
Statement.Return
return ;
Statement.If
if ( ) elseExp Statement Statement
Exp.Add
+Exp Exp
Exp.Var
ID
Understanding Syntax =!
Understanding Tree Structure
parse(prettyprint(t)) = t
No need to understand !
how parse works!
58. Demo: Syntax Deļ¬nition in SDF3
Language Design
Syntax!
Deļ¬nition
Name !
Binding
Type!
Constraints
Dynamic!
Semantics
Transform
62. Name Binding & Scope Rules
int fib(int n) {
if(n <= 1)
return 1;
else
return fib(n - 2) + fib(n - 1);
}
what does this variable refer to?
Our contribution
!
- declarative language for name binding & scope rules
- generation of incremental name resolution algorithm
!
- Konat, Kats, Wachsmuth, Visser (SLE 2012)
- Wachsmuth, Konat, Vergu, Groenewegen, Visser (SLE 2013)
which function is being called here?
Needed for
!
- checking correct use of names and types
- lookup in interpretation and compilation
- navigation in IDE
- code completion
State-of-the-art
!
- programmatic encoding of name resolution algorithms
63. Demo: Name and Type Analysis in NaBL+TS
Language Design
Syntax!
Deļ¬nition
Name !
Binding
Type!
Constraints
Dynamic!
Semantics
Transform
64. Declarative Name Binding and Scope Rules
binding rules
!
Param(t, name) :
defines Variable name
Var(name) :
refers to Variable name
!
Function(t, name, param*, s) :
defines Function name
scopes Variable, Function
!
Call(name, exp*) :
refers to Function name
Incremental name resolution algorithm
Name checks
Semantic code completion
Reference resolution
65. Semantics of Name Binding?
binding rules
!
Param(t, name) :
defines Variable name
Var(name) :
refers to Variable name
!
Function(t, name, param*, s) :
defines Function name
scopes Variable, Function
!
Call(name, exp*) :
refers to Function name
Research: how to characterize correctness of the result of
name resolution without appealing to the algorithm itself?
Analogy: declarative semantics of syntax deļ¬nition
68. rules
Num(i) --> I(i)
Ifz(e1, e2, e3) --> v
where e1 --> I(i), i = 0, e2 --> v
Ifz(e1, e2, e3) --> v
where e1 --> I(i), i != 0, e3 --> v
Add(e1, e2) --> I(addInt(i, j))
where e1 --> I(i), e2 --> I(j)
Sub(e1, e2) --> I(subInt(i, j))
where e1 --> I(i), e2 --> I(j)
Mul(e1, e2) --> I(mulInt(i, j))
where e1 --> I(i), e2 --> I(j)
DynSem: Dynamic Semantics Speciļ¬cation
module semantics
!
rules
!
E env |- Var(x) --> v
where env[x] => T(e, env'),
E env' |- e --> v
E env |- Fun(Param(x, t), e) --> C(x, e, env)
!
E env |- App(e1, e2) --> v
where E env |- e1 --> C(x, e, env'),
E {x |--> T(e2, env), env'} |- e --> v
E env |- Fix(Param(x, t), e) --> v
where
E {x |--> T(Fix(Param(x,t),e),env), env} |- e --> v
E env |- Let(x, t, e1, e2) --> v
where E {x |--> T(e1, env), env} |- e2 --> v
69. Implicitly-Modular Structural Operational Semantics (I-MSOS)*
rules
!
E env |- Var(x) --> v
where env[x] => T(e, env'),
E env' |- e --> v
!
Add(e1, e2) --> I(addInt(i, j))
where e1 --> I(i),
e2 --> I(j)
rules
!
E env |- Var(x) --> v
where env[x] => T(e, env'),
E env' |- e --> v
!
E env |- Add(e1, e2) --> I(addInt(i, j))
where E env |- e1 --> I(i),
E env |- e2 --> I(j)explicate
* P. D. Mosses. Modular structural operational semantics. JLP, 60-61:195ā228, 2004.
M. Churchill, P. D. Mosses, and P. Torrini. Reusable components of semantic speciļ¬cations. In MODULARITY, April 2014.
70. rules
Ifz(e1, e2, e3) --> v
where e1 --> I(i), i = 0, e2 --> v
Ifz(e1, e2, e3) --> v
where e1 --> I(i), i != 0, e3 --> v
Interpreter Generation
rules
E env |- Ifz(e1, e2, e3) --> v
where E env |- e1 --> I(i),
[i = 0, E env |- e2 --> v] +
i != 0, E env |- e3 --> v]
explicate
& merge
71. module PCF
sorts Exp Param Type
templates
Exp.Var = [[ID]]
Exp.App = [[Exp] [Exp]] {left}
Exp.Fun = [
fun [Param] (
[Exp]
)
]
Exp.Fix = [
fix [Param] (
[Exp]
)
]
Exp.Let = [
let [ID] : [Type] =
[Exp]
in [Exp]
]
Exp.Num = [[INT]]
Exp.Add = [[Exp] + [Exp]] {left}
Exp.Sub = [[Exp] - [Exp]] {left}
Exp.Mul = [[Exp] * [Exp]] {left}
Exp = [([Exp])] {bracket}
Exp.Ifz = [
ifz [Exp] then
[Exp]
else
[Exp]
]
Type.IntType = [int]
Type.FunType = [[Type] -> [Type]]
Param.Param = [[ID] : [Type]]
context-free priorities
!
Exp.App > Exp.Mul > {left: Exp.Add Exp.Sub}
> Exp.Ifz
module types
type rules
!
Var(x) : t
where definition of x : t
Param(x, t) : t
Fun(p, e) : FunType(tp, te)
where p : tp and e : te
App(e1, e2) : tr
where e1 : FunType(tf, tr) and e2 : ta
and tf == ta
else error "type mismatch" on e2
!
Fix(p, e) : tp
where p : tp and e : te
and tp == te
else error "type mismatch" on p
Let(x, tx, e1, e2) : t2
where e2 : t2 and e1 : t1
and t1 == tx
else error "type mismatch" on e1
Num(i) : IntType()
!
Ifz(e1, e2, e3) : t2
where e1 : IntType() and e2 : t2 and e3 : t3
and t2 == t3
else error "types not compatible" on e3
e@Add(e1, e2) + e@Sub(e1, e2) + e@Mul(e1, e2) : IntType()
where e1 : IntType()
else error "Int type expected" on e
and e2 : IntType()
else error "Int type expected" on e
module semantics
!
rules
!
E env |- Var(x) --> v
where env[x] => T(e, env'),
E env' |- e --> v
E env |- Fun(Param(x, t), e) --> C(x, e, env)
!
E env |- App(e1, e2) --> v
where E env |- e1 --> C(x, e, env'),
E {x |--> T(e2, env), env'} |- e --> v
E env |- Fix(Param(x, t), e) --> v
where
E {x |--> T(Fix(Param(x,t),e),env), env} |- e --> v
E env |- Let(x, t, e1, e2) --> v
where E {x |--> T(e1, env), env} |- e2 --> v
rules
Num(i) --> I(i)
Ifz(e1, e2, e3) --> v
where e1 --> I(i), i = 0, e2 --> v
Ifz(e1, e2, e3) --> v
where e1 --> I(i), i != 0, e3 --> v
Add(e1, e2) --> I(addInt(i, j))
where e1 --> I(i), e2 --> I(j)
Sub(e1, e2) --> I(subInt(i, j))
where e1 --> I(i), e2 --> I(j)
Mul(e1, e2) --> I(mulInt(i, j))
where e1 --> I(i), e2 --> I(j)
module names
namespaces Variable
binding rules
!
Var(x) :
refers to Variable x
Param(x, t) :
defines Variable x of type t
Fun(p, e) :
scopes Variable
Fix(p, e) :
scopes Variable
Let(x, t, e1, e2) :
defines Variable x of type t in e2
First Little (Big) Step: From PCF in Spoofax ā¦
72. else
[Exp]
]
Type.IntType = [int]
Type.FunType = [[Type] -> [Type]]
Param.Param = [[ID] : [Type]]
context-free priorities
!
Exp.App > Exp.Mul > {left: Exp.Add Exp.Sub}
> Exp.Ifz
Ifz(e1, e2, e3) : t2
where e1 : IntType() and e2 : t2 and e3 : t3
and t2 == t3
else error "types not compatible" on e3
e@Add(e1, e2) + e@Sub(e1, e2) + e@Mul(e1, e2) : IntType()
where e1 : IntType()
else error "Int type expected" on e
and e2 : IntType()
else error "Int type expected" on e
Ifz(e1, e2, e3) --> v
where e1 --> I(i), i != 0, e3 --> v
Add(e1, e2) --> I(addInt(i, j))
where e1 --> I(i), e2 --> I(j)
Sub(e1, e2) --> I(subInt(i, j))
where e1 --> I(i), e2 --> I(j)
Mul(e1, e2) --> I(mulInt(i, j))
where e1 --> I(i), e2 --> I(j)
Param(x, t) :
defines Variable x of type t
Fun(p, e) :
scopes Variable
Fix(p, e) :
scopes Variable
Let(x, t, e1, e2) :
defines Variable x of type t in e2
Inductive has_type (C: Context) : term -> term -> Prop :=
| VarC_ht ns k0 t x k1 : lookup C x ns k0 t -> has_type C (Co VarC [Id x k0] k1) t
| ParamC_ht x t k0 : has_type C (Co ParamC [x;t] k0) t
| FunC_ht k0 t_p t_e p e k1 : has_type C p t_p -> has_type C e t_e -> has_type C (Co FunC [p;e] k1) (Co FunTypeC [t_p;t_e] k0)
| FixC_ht t_p t_e p e k0 : has_type C p t_p -> has_type C e t_e -> (t_p = t_e) -> has_type C (Co FixC [p;e] k0) t_p
| AppC_ht t_r k0 t_f t_a e1 e2 k1 : has_type C e1 (Co FunTypeC [t_f;t_r] k0) -> has_type C e2 t_a -> (t_f = t_a) -> has_type C (Co AppC [e1;e2] k1) t_r
| LetC_ht t2 t1 x t_x e1 e2 k0 : has_type C e2 t2 -> has_type C e1 t1 -> (t1 = t_x) -> has_type C (Co LetC [x;t_x;e1;e2] k0) t2
| NumC_ht k0 i k1 : has_type C (Co NumC [i] k1) (Co IntTypeC [] k0)
| IfzC_ht k0 t2 t3 e1 e2 e3 k1 : has_type C e1 (Co IntTypeC [] k0) -> has_type C e2 t2 -> has_type C e3 t3 -> (t2 = t3) -> has_type C (Co IfzC [e1;e2;e3] k1) t2
| AddC_ht k2 k0 k1 e1 e2 k3 : has_type C e1 (Co IntTypeC [] k0) -> has_type C e2 (Co IntTypeC [] k1) -> has_type C (Co AddC [e1;e2] k3) (Co IntTypeC [] k2)
| SubC_ht k2 k0 k1 e1 e2 k3 : has_type C e1 (Co IntTypeC [] k0) -> has_type C e2 (Co IntTypeC [] k1) -> has_type C (Co SubC [e1;e2] k3) (Co IntTypeC [] k2)
| MulC_ht k2 k0 k1 e1 e2 k3 : has_type C e1 (Co IntTypeC [] k0) -> has_type C e2 (Co IntTypeC [] k1) -> has_type C (Co MulC [e1;e2] k3) (Co IntTypeC [] k2)
| HT_eq e ty1 ty2 (hty1: has_type C e ty1) (tyeq: term_eq ty1 ty2) : has_type C e ty2
.
Inductive semantics_cbn : Env -> term -> value -> Prop :=
| Var0C_sem env' e env x k0 v : get_env x env e env' -> semantics_cbn env' e v -> semantics_cbn env (Co VarC [x] k0) v
| Fun0C_sem t k1 k0 x e env : semantics_cbn env (Co FunC [Co ParamC [x;t] k1;e] k0) (Clos x e env)
| Fix0C_sem k1 k0 env x t k3 e k2 v : semantics_cbn { x |--> (Co FixC [Co ParamC [x;t] k1;e] k0,env), env } e v -> semantics_cbn env (Co FixC [Co ParamC [x;t] k3;e] k2) v
| App0C_sem env' x e env e1 e2 k0 v : semantics_cbn env e1 (Clos x e env') -> semantics_cbn { x |--> (e2,env), env' } e v -> semantics_cbn env (Co AppC [e1;e2] k0) v
| Let0C_sem env x t e1 e2 k0 v : semantics_cbn { x |--> (e1,env), env } e2 v -> semantics_cbn env (Co LetC [x;t;e1;e2] k0) v
| Num0C_sem env k0 i : semantics_cbn env (Co NumC [i] k0) (Natval i)
| Ifz0C_sem i env e1 e2 e3 k0 v : semantics_cbn env e1 (Natval i) -> (i = 0) -> semantics_cbn env e2 v -> semantics_cbn env (Co IfzC [e1;e2;e3] k0) v
| Ifz1C_sem i env e1 e2 e3 k0 v : semantics_cbn env e1 (Natval i) -> (i <> 0) -> semantics_cbn env e3 v -> semantics_cbn env (Co IfzC [e1;e2;e3] k0) v
| Add0C_sem env e1 e2 k0 i j : semantics_cbn env e1 (Natval i) -> semantics_cbn env e2 (Natval j) -> semantics_cbn env (Co AddC [e1;e2] k0) (plus i j)
| Sub0C_sem env e1 e2 k0 i j : semantics_cbn env e1 (Natval i) -> semantics_cbn env e2 (Natval j) -> semantics_cbn env (Co SubC [e1;e2] k0) (minus i j)
| Mul0C_sem env e1 e2 k0 i j : semantics_cbn env e1 (Natval i) -> semantics_cbn env e2 (Natval j) -> semantics_cbn env (Co MulC [e1;e2] k0) (mult i j)
.
Inductive ID_NS : Set :=
| VariableNS
.
!Definition NS :=
ID_NS
.
!Inductive scopesR : term -> NS -> Prop :=
| Fun_scopes_Variable p e k0 : scopesR (Co FunC [p;e] k0) VariableNS
| Fix_scopes_Variable p e k0 : scopesR (Co FixC [p;e] k0) VariableNS
.
!Definition scopes_R :=
scopesR
.
!Inductive definesR : term -> Ident -> NS -> key -> Prop :=
| Param_defines_Variable x k1 t k0 : definesR (Co ParamC [Id x k1;t] k0) x VariableNS k1
.
!Definition defines_R :=
definesR
.
!Inductive refers_toR : term -> Ident -> NS -> key -> Prop :=
| Var_refers_to_Variable x k1 k0 : refers_toR (Co VarC [Id x k1] k0) x VariableNS k1
.
!Definition refers_to_R :=
refers_toR
.
!Inductive typed_definesR : term -> Ident -> NS -> term -> key -> Prop :=
| Param_typed_defines_Variable x t k1 t k0 : typed_definesR (Co ParamC [Id x k1;t] k0) x VariableNS t k1
.
!Definition typed_defines_R :=
typed_definesR
.
Inductive sorts : Set :=
| Param_S
| ID_S
| INT_S
| Exp_S
| Type_S
.
!Parameter Ident : Set.
!Definition sort :=
sorts
.
!Definition Ident_Sort :=
ID_S
.
!Inductive Constructors :=
| INTC (n: nat)
| VarC
| FunC
| FixC
| AppC
| LetC
| ParamC
| NumC
| AddC
| SubC
| MulC
| DivC
| IfzC
| IntTypeC
| FunTypeC
.
!Definition constructors :=
Constructors
.
!Fixpoint
get_sig (x: constructors) : list sort * sort :=
match x with
| INTC n => ([],INT_S)
| VarC => ([ID_S],Exp_S)
| FunC => ([Param_S;Exp_S],Exp_S)
| FixC => ([Param_S;Exp_S],Exp_S)
| AppC => ([Exp_S;Exp_S],Exp_S)
| LetC => ([ID_S;Type_S;Exp_S;Exp_S],Exp_S)
| ParamC => ([ID_S;Type_S],Param_S)
| NumC => ([INT_S],Exp_S)
| AddC => ([Exp_S;Exp_S],Exp_S)
| SubC => ([Exp_S;Exp_S],Exp_S)
| MulC => ([Exp_S;Exp_S],Exp_S)
| DivC => ([Exp_S;Exp_S],Exp_S)
| IfzC => ([Exp_S;Exp_S;Exp_S],Exp_S)
| IntTypeC => ([],Type_S)
| FunTypeC => ([Type_S;Type_S],Type_S)
end.
ā¦ to PCF in Coq (+ manual proof of type preservation)