起始状态: { INIT, A, G }
i: { INIT, A, G } → { B, C, D, E, F, H, I, K }
f: { B, C, D, E, F, H, I, K } → { D, E, F, J, K }
_0-9a-zA-Z: { D, E, F, J, K } → { D, E, F, K }
_0-9a-zA-Z: { D, E, F, K } → { D, E, F, K }
_0-9a-eg-zA-Z: { B, C, D, E, F, H, I, K } → { D, E, F, K }
_a-hj-zA-Z: { INIT, A, G } → { B, C, D, E, F, K }
_0-9a-zA-Z: { B, C, D, E, F, K } → { D, E, F, K }
S -> S; S -> ID := E; S -> ID := E + E; S -> ID := ID + E; S -> ID := ID + NAT; S -> ID := ID + NAT; PRINT(L) -> ID := ID + NAT; PRINT(L, E) -> ID := ID + NAT; PRINT(E, E) -> ID := ID + NAT; PRINT(ID, E) -> ID := ID + NAT; PRINT(ID, ID)
解析树 (Parsing Tree): 是派生过程的图形化表示,它具有以下性质:
根节点是语法的初始符号
每个内部节点是一个非终结符,每个叶子节点是一个终结符
每一个父节点和它的所有子节点共同对应一条产生式规则
1 2 3 4 5 6 7
E / | \ E E * E -> E * E / | \ | -> E + E * E E + E ID -> ID + E * E | | -> ID + ID * E ID ID -> ID + ID * ID
E E / | \ / | \ E + E E * E | / | \ / | \ | ID E * E E + E ID | | | | ID ID ID ID
为了消除该歧义,引入了新的非终结符 F,并将文法改写为:
1 2
E -> F E -> E + E F -> F * F F -> ( E ) F -> ID
这时,加法的因子 E 可以继续展开产生乘法,但乘法的因子 F 无法再展开产生加法,从而规定了乘法的优先级高于加法。
1 2 3 4 5 6 7 8 9
E / | \ E E + E -> E + E | | -> E + F F F -> E + F * F | / | \ -> E + F * ID ID F * F -> E + ID * ID | | -> F + ID * ID ID ID -> ID + ID * ID
歧义2:加法的结合律 / 乘法的结合律
1 2 3 4 5 6 7 8 9
E E / | \ / | \ E + E E + E | / | \ / | \ | F E + E E + E F | | | | | | ID F F F F ID | | | | ID ID ID ID
为了消除该歧义,再次引入新的非终结符 G,并将文法改写为:
1 2
E -> F E -> E + F F -> F * G F -> G G -> ( E ) G -> ID
这时,只有加法左侧的因子 E 能够继续展开产生加法,从而规定了加法是左结合的。乘法同理。
1 2 3 4 5 6 7 8 9 10 11
E / | \ E E + F -> E + F / | \ | -> E + F + F E + F G -> F + F + F | | | -> G + F + F F G ID -> ID + F + F | | -> ID + G + F G ID -> ID + ID + F | -> ID + ID + G ID -> ID + ID + ID
ID + ID + ID E -> E + F -> G + ID + ID -> E + G -> F + ID + ID -> E + ID -> E + ID + ID -> E + F + ID -> E + G + ID -> E + G + ID -> E + F + ID -> E + ID + ID -> E + ID -> F + ID + ID -> E + G -> G + ID + ID -> E + F -> ID + ID + ID -> E
5. 移入规约分析
思想: 这是最常见的自底向上分析方法
它从左到右扫描输入的标记串,从左向右进行规约
目标是计算出最左规约(或者说最右派生)
核心操作: 移入 (Shift)、规约 (Reduce)
扫描线的右侧全部都是终结符
规约操作都发生在扫描线的左侧紧贴扫描线的区域内
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| ID + ID + ID -> ID | + ID + ID -> G | + ID + ID -> F | + ID + ID -> E | + ID + ID -> E + | ID + ID -> E + ID | + ID -> E + G | + ID -> E + F | + ID -> E | + ID -> E + | ID -> E + ID | -> E + G | -> E + F | -> E |
移入与规约的选择问题:
既能移入,又能规约的情形,应当选择移入还是规约?
有多种规约方案的情形,应当如何选择?
6. 已移入部分的结构
这部分在探讨扫描线左侧的可行结构,这有助于我们解决“移入与规约的选择问题”。
已移入部分的结构特征:
已移入规约的部分可以分为 n 段,每段对应一条产生式
第 i 段拼接上第 i+1 条产生式的左侧非终结符是第 i 条产生式右侧符号串的一个前缀
例子:
F * (E) | 中已移入规约的部分可以分为: F *( E )
分别对应产生式:E -> . FF -> F * . GG -> ( E ) .
F * (E + | ID ) 中已移入规约的部分可以分为: F *(E +
分别对应产生式:E -> . FF -> F * . GG -> ( . E )E -> E + . F
7. 已移入部分的结构判定
这部分提出了判定可行的扫描线左侧结构的算法,是用于解决“移入与规约的选择问题”的一个判断方法。
核心思想: 以扫描线左侧的最右段为状态,构建 NFA,该 NFA 中的所有状态都是终止状态
新增一段带来的变化对应一条 ϵ 边,其他加入符号带来的变化对应一条普通边
要判断一串符号串是否是可行的扫描线左侧结构,只需判断这个符号串能否被这个 NFA 接受
状态转移:
普通转移: 当从一个状态 S 读入一个符号 X 时,它会转移到一个新的状态 S'
S' 中的项是 S 中形如 A -> α . X β 的项将点右移一位后得到的结果,即 A -> α X . β
ϵ-转移: 在一个状态中,如果存在一个项 A -> α . B β(其中 B 是非终结符),那么必须为接下来可能出现的 B 做好准备
因此,所有 B 的产生式对应的初始项,如 B -> . γ,都必须被递归地加入到当前状态中
判定过程:
从包含初始产生式 START -> . E 的初始状态开始
每次移入一个符号,就根据状态转移规则进入下一个状态
如果某一步无法找到合法的转移,说明输入的符号串不可行
例子:判断 E + F + | ... 是否是可行的扫描线左侧结构:
1 2 3 4 5 6 7 8 9 10
E + F + | ...
[] START -> . E E -> . F E -> . E + F F -> . G F -> . F * G G -> . ( E ) G -> . ID
1 2 3 4 5
E + F + | ...
[E] START -> E . E -> E . + F
1 2 3 4 5 6 7 8
E + F + | ...
[E +] E -> E + . F F -> . G F -> . F * G G -> . ID G -> . ( E )
1 2 3 4 5
E + F + | ...
[E + F] E -> E + F . F -> F . * G
1 2 3 4
E + F + | ...
[E + F +] ( No possible state )
8. 基于 follow 集合的判定法
这部分用于解决“移入与规约的选择问题”的另一个判断方法。
定义: 假设 X 是一个终结符,
X 是 Follow( Y ) 的元素当且仅当 ... Y X ... 是可能被完全规约的
X 是 First( Y ) 的元素当且仅当 X ... 是可能被规约为 Y 的
First 集合的计算:
对任意终结符 X , X 都是 First( X ) 的元素
对任意产生式 Y -> Z ... ,First( Z ) 的元素都是 First( Y ) 的元素
Follow 集合的计算:
对任意产生式 U -> ... Y Z ... ,First( Z ) 是 Follow( Y ) 的子集
Fixpoint number_of_comp (ei: expr_int): Z := match ei with | EAdd e1 e2 => number_of_comp e1 + number_of_comp e2 + 1 | ESub e1 e2 => number_of_comp e1 + number_of_comp e2 + 1 | EMul e1 e2 => number_of_comp e1 + number_of_comp e2 + 1 | EVar v => 0 | EConst n => 0 end.
8.2 带类型表达式的几个基本合法性条件
我们定义表达式中常数的合法性条件,即每个常数都落在其类型规定的范围之内。
首先定义每个整数类型对应的数值范围。
1 2 3 4 5 6 7
Definition const_in_range (n: Z) (t: IntType): Prop := match t with | Build_IntType sz Signed => - Z.pow 2 (sz - 1) <= n < Z.pow 2 (sz - 1) | Build_IntType sz Unsigned => 0 <= n < Z.pow 2 sz end.
接着定义“表达式中所有常数全都满足类型范围要求”的合法性条件。
1 2 3 4 5 6 7
Fixpoint consts_in_range (e: expr): Prop := match e with | EConst n t => const_in_range n t | EVar v t => True | EBinop op e1 e2 t => consts_in_range e1 /\ consts_in_range e2 | EUnop op e1 t => consts_in_range e1 end.
Lecture 5:表达式指称语义
1. SimpleWhile 整数类型表达式的指称语义
程序状态集合:state≜var_name→Z
1
Definition state: Type := var_name -> Z.
整数类型表达式 e 的行为:定义为 e 在每个程序状态上的值
[[e]]:state→Z
[[e]](s) 表示表达式 e 在程序状态 s 上的求值结果
从而有如下的具体定义:
[[n]](s)=n
[[x]](s)=s(x)
[[e1+e2]](s)=[[e1]](s)+[[e2]](s)
[[e1−e2]](s)=[[e1]](s)−[[e2]](s)
[[e1∗e2]](s)=[[e1]](s)∗[[e2]](s)
1 2 3 4 5 6 7 8
Fixpoint eval_expr_int (e: expr_int) (s: state) : Z := match e with | EConst n => n | EVar X => s X | EAdd e1 e2 => eval_expr_int e1 s + eval_expr_int e2 s | ESub e1 e2 => eval_expr_int e1 s - eval_expr_int e2 s | EMul e1 e2 => eval_expr_int e1 s * eval_expr_int e2 s end.
Definition test_true (D: state -> Prop): state -> state -> Prop := Rels.test D.
Definition test_false (D: state -> Prop): state -> state -> Prop := Rels.test (Sets.complement D).
Definition if_sem (D0: state -> Prop) (D1 D2: state -> state -> Prop): state -> state -> Prop := (test_true D0 ∘ D1) ∪ (test_false D0 ∘ D2).
循环语句
定义 1:iterLBn([[e]],[[c]]) 表示执行循环体 n 次
iterLB 0([[e]],[[c]])= test_false ([[e]]); iterLB n+1([[e]],[[c]])= test_true ([[e]])∘[[c]]∘iterLBn([[e]],[[c]]); [[ while (e) do {c}]]=n∈N⋃iterLBn([[e]],[[c]]).
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
Fixpoint iterLB (D0: state -> Prop) (D1: state -> state -> Prop) (n: nat): state -> state -> Prop := match n with | O => test_false D0 | S n0 => test_true D0 ∘ D1 ∘ iterLB D0 D1 n0 end.
Definition while_sem (D0: state -> Prop) (D1: state -> state -> Prop): state -> state -> Prop := ⋃ (iterLB D0 D1).
定义 2: boundedLB n([[e]],[[c]]) 表示执行循环体小于 n 次
boundedLB 0([[e]],[[c]])=∅ boundedLB n+1([[e]],[[c]])= test_true ([[e]])∘[[c]]∘ boundedLB n([[e]],[[c]])∪ test_false ([[e]])[[ while (e) do {c}]]=n∈N⋃ boundedLB n([[e]],[[c]])
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
Fixpoint boundedLB (D0: state -> Prop) (D1: state -> state -> Prop) (n: nat): state -> state -> Prop := match n with | O => H | S n0 => (test_true D0 ∘ D1 ∘ boundedLB D0 D1 n0) ∪ (test_false D0) end.
Definition while_sem (D0: state -> Prop) (D1: state -> state -> Prop): state -> state -> Prop := ⋃ (boundedLB D0 D1).
下面是程序语句指称语义的递归定义。
1 2 3 4 5 6 7 8 9 10 11 12 13
Fixpoint eval_com (c: com): state -> state -> Prop := match c with | CSkip => skip_sem | CAsgn X e => asgn_sem X (eval_expr_int e) | CSeq c1 c2 => seq_sem (eval_com c1) (eval_com c2) | CIf e c1 c2 => if_sem (eval_expr_bool e) (eval_com c1) (eval_com c2) | CWhile e c1 => while_sem (eval_expr_bool e) (eval_com c1) end.
Class Monad (M:Type -> Type):Type := { bind:forall {A B:Type}, M A -> (A -> M B) -> M B; ret:forall {A:Type}, A -> M A; }.
接着定义集合单子。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
Module SetMonad.
Definition M (A: Type): Type := A -> Prop.
Definition bind: forall (A B: Type) (f: M A) (g: A -> M B), M B := fun (A B: Type) (f: M A) (g: A -> M B) => fun b: B => exists a: A, a ∈ f /\ b ∈ g a.
Definition ret: forall (A: Type) (a: A), M A := fun (A: Type) (a: A) => Sets.singleton a.
Record M (A: Type): Type := { nrm: A -> Prop; err: Prop; }.
Definition ret (A: Type) (a: A): M A := {| nrm := Sets.singleton a; err := False; |}.
Definition bind (A B: Type) (f: M A) (g: A -> M B): M B := {| nrm := fun b => exists a, a ∈ f.(nrm) /\ b ∈ (g a).(nrm); err := f.(err) \/ exists a, a ∈ f.(nrm) /\ (g a).(err); |}.
Definition const_sem (n: Z): state -> SetMonadE.M Z := fun s => assert (Int64.min_signed <= n <= Int64.max_signed);; ret n.
Definition var_sem (X: var_name): state -> SetMonadE.M Z := fun s => ret (s X).
Definition add_sem (D1 D2: state -> SetMonadE.M Z): state -> SetMonadE.M Z := fun s => x <- D1 s;; y <- D2 s;; assert (Int64.min_signed <= x + y <= Int64.max_signed);; ret (x + y).
Definition sub_sem (D1 D2: state -> SetMonadE.M Z): state -> SetMonadE.M Z := fun s => x <- D1 s;; y <- D2 s;; assert (Int64.min_signed <= x - y <= Int64.max_signed);; ret (x - y).
Definition mul_sem (D1 D2: state -> SetMonadE.M Z): state -> SetMonadE.M Z := fun s => x <- D1 s;; y <- D2 s;; assert (Int64.min_signed <= x * y <= Int64.max_signed);; ret (x * y).
1 2 3 4 5 6 7 8 9 10 11 12 13 14
Fixpoint eval_expr_int (e: expr_int): state -> SetMonadE.M Z := match e with | EConst n => const_sem n | EVar X => var_sem X | EAdd e1 e2 => add_sem (eval_expr_int e1) (eval_expr_int e2) | ESub e1 e2 => sub_sem (eval_expr_int e1) (eval_expr_int e2) | EMul e1 e2 => mul_sem (eval_expr_int e1) (eval_expr_int e2) end.
5.4 SimpleWhile 的整数类型表达式指称语义(考虑变量初始化)
1 2 3
Inductive val: Type := | Var_U: val | Var_I (i: Z): val.
Definition var_sem (X: var_name): state -> SetMonadE.M Z := fun s => match s X with | Var_U => abort | Var_I n => ret n end.
其他语义算子的定义代码都不需要修改。
1 2 3 4 5 6 7 8 9 10 11 12 13 14
Fixpoint eval_expr_int (e: expr_int): state -> SetMonadE.M Z := match e with | EConst n => const_sem n | EVar X => var_sem X | EAdd e1 e2 => add_sem (eval_expr_int e1) (eval_expr_int e2) | ESub e1 e2 => sub_sem (eval_expr_int e1) (eval_expr_int e2) | EMul e1 e2 => mul_sem (eval_expr_int e1) (eval_expr_int e2) end.
6. While 语言表达式的指称语义
常量、变量、加、减、乘对应的语义算子都可以沿用先前的定义。下面先定义除法和取余对应的语义算子,具体规定细节参考了 C 标准。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
Definition div_sem (D1 D2: state -> SetMonadE.M Z): state -> SetMonadE.M Z := fun s => x <- D1 s;; y <- D2 s;; assert (y <> 0);; assert (y <> -1 \/ x <> Int64.min_signed);; ret (Z.quot x y).
Definition mod_sem (D1 D2: state -> SetMonadE.M Z): state -> SetMonadE.M Z := fun s => x <- D1 s;; y <- D2 s;; assert (y <> 0);; assert (y <> -1 \/ x <> Int64.min_signed);; ret (Z.rem x y).
C 标准规定:
在任何情况下,整数运算中的 C 表达式 a / b 与 a % b 永远满足 a = b * (a / b) + a % b。
Definition eq_sem (D1 D2: state -> SetMonadE.M Z): state -> SetMonadE.M Z := fun s => x <- D1 s;; y <- D2 s;; choice (assume (x = y);; ret 1) (assume (x <> y);; ret 0).
Definition neq_sem (D1 D2: state -> SetMonadE.M Z): state -> SetMonadE.M Z := fun s => x <- D1 s;; y <- D2 s;; choice (assume (x <> y);; ret 1) (assume (x = y);; ret 0).
Definition lt_sem (D1 D2: state -> SetMonadE.M Z): state -> SetMonadE.M Z := fun s => x <- D1 s;; y <- D2 s;; choice (assume (x < y);; ret 1) (assume (x >= y);; ret 0).
Definition le_sem (D1 D2: state -> SetMonadE.M Z): state -> SetMonadE.M Z := fun s => x <- D1 s;; y <- D2 s;; choice (assume (x <= y);; ret 1) (assume (x > y);; ret 0).
Definition gt_sem (D1 D2: state -> SetMonadE.M Z): state -> SetMonadE.M Z := fun s => x <- D1 s;; y <- D2 s;; choice (assume (x > y);; ret 1) (assume (x <= y);; ret 0).
Definition ge_sem (D1 D2: state -> SetMonadE.M Z): state -> SetMonadE.M Z := fun s => x <- D1 s;; y <- D2 s;; choice (assume (x >= y);; ret 1) (assume (x < y);; ret 0).
Definition and_sem (D1 D2: state -> SetMonadE.M Z): state -> SetMonadE.M Z := fun s => x <- D1 s;; choice (assume (x <> 0);; y <- D2 s;; choice (assume (y <> 0);; ret 1) (assume (y = 0);; ret 0)) (assume (x = 0);; ret 0). (* 这里实现了短路求值 *)
Definition or_sem (D1 D2: state -> SetMonadE.M Z): state -> SetMonadE.M Z := fun s => x <- D1 s;; choice (assume (x <> 0);; ret 1) (assume (x = 0);; y <- D2 s;; choice (assume (y <> 0);; ret 1) (assume (y = 0);; ret 0)).
除了定义二元运算符的语义算子之外,还有两个一元运算符需要定义其语义算子。
1 2 3 4 5 6 7 8 9 10 11 12 13 14
Definition not_sem (D: state -> SetMonadE.M Z): state -> SetMonadE.M Z := fun s => x <- D s;; choice (assume (x <> 0);; ret 0) (assume (x = 0);; ret 1).
Definition neg_sem (D: state -> SetMonadE.M Z): state -> SetMonadE.M Z := fun s => x <- D s;; assert (x <> Int64.min_signed);; ret (- x).
Definition binop_sem (op: binop) (D1 D2: state -> SetMonadE.M Z): state -> SetMonadE.M Z := match op with | OPlus => add_sem D1 D2 | OMinus => sub_sem D1 D2 | OMul => mul_sem D1 D2 | ODiv => div_sem D1 D2 | OMod => mod_sem D1 D2 | OLt => lt_sem D1 D2 | OLe => le_sem D1 D2 | OGt => gt_sem D1 D2 | OGe => ge_sem D1 D2 | OEq => eq_sem D1 D2 | ONe => neq_sem D1 D2 | OAnd => and_sem D1 D2 | OOr => or_sem D1 D2 end.
Definition unop_sem (op: unop) (D: state -> SetMonadE.M Z): state -> SetMonadE.M Z := match op with | ONeg => neg_sem D | ONot => not_sem D end.
最终的递归定义如下。
1 2 3 4 5 6 7 8 9 10 11
Fixpoint eval_expr (e: expr): state -> SetMonadE.M Z := match e with | EConst n => const_sem n | EVar X => var_sem X | EBinop op e1 e2 => binop_sem op (eval_expr e1) (eval_expr e2) | EUnop op e1 => unop_sem op (eval_expr e1) end.
7. WhileDeref 语言表达式的指称语义
我们在 While 语言的基础上引入内存。
首先,每个程序状态 s 中除了包含每个变量的值 s.(var) 还包含内存地址上存储的值 s.(mem) ,这些内存地址是指存储变量数值之外的额外内存存储空间。而对于每个内存地址而言,又要首先定义是否有该地址的读写权限。据此,可以重新定义 var_sem。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
Record state: Type := { var: var_name -> val; (** 变量的值 *) mem: Z -> mem_val; (** 额 外 内 存 空 间 上 存 储 的 值 *) }.
Definition var_sem (X: var_name): state -> SetMonadE.M Z := fun s => match s.(var) X with | Var_U => abort | Var_I n => ret n end.
接下来定义解引用的语义算子。
1 2 3 4 5 6 7 8 9
Definition deref_sem (D: state -> SetMonadE.M Z): state -> SetMonadE.M Z := fun s => x <- D s;; match s.(mem) x with | Mem_NoPerm => abort | Mem_HasPerm Var_U => abort | Mem_HasPerm (Var_I n) => ret n end.
其他语义算子保持不变。最终的递归定义如下。
1 2 3 4 5 6 7 8 9 10 11 12 13
Fixpoint eval_expr (e: expr): state -> SetMonadE.M Z := match e with | EConst n => const_sem n | EVar X => var_sem X | EBinop op e1 e2 => binop_sem op (eval_expr e1) (eval_expr e2) | EUnop op e1 => unop_sem op (eval_expr e1) | EDeref e1 => deref_sem (eval_expr e1) end.
Definition asgn_sem (X: var_name) (D: state -> SetMonadE.M Z): CDenote := {| nrm := fun s1 s2 => exists i, i ∈ (D s1).(nrm) /\ s2 X = Var_I i /\ (forall Y, X <> Y -> s2 Y = s1 Y); err := fun s1 => (D s1).(err); |}.
Fixpoint eval_com (c: com): CDenote := match c with | CSkip => skip_sem | CAsgn X e => asgn_sem X (eval_expr e) | CSeq c1 c2 => seq_sem (eval_com c1) (eval_com c2) | CIf e c1 c2 => if_sem (eval_expr e) (eval_com c1) (eval_com c2) | CWhile e c1 => while_sem (eval_expr e) (eval_com c1) | CBreak => brk_sem | CContinue => cnt_sem end.
5. SetMonad 中加入循环
首先定义循环体的计算结果,其结果要么是 continue 终止,要么是 break 终止。
1 2 3
Inductive ContinueOrBreak (A B: Type): Type := | by_continue (a: A) | by_break (b: B).
下面用不动点定义 repeat 循环。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
Definition repeat_break_f {A B: Type} (body: A -> SetMonad.M (ContinueOrBreak A B)) (W: A -> SetMonad.M B) (a: A): SetMonad.M B := x <- body a;; match x with | by_continue a' => W a' | by_break b => ret b end.
Definition repeat_break {A B: Type} (body: A -> SetMonad.M (ContinueOrBreak A B)): A -> SetMonad.M B := Kleene_LFix (repeat_break_f body).
下面还可以定义循环体中的 continue 语句和 break 语句。
1 2 3 4 5 6 7
Definition continue {A B: Type} (a: A): SetMonad.M (ContinueOrBreak A B) := ret (by_continue a).
Definition break {A B: Type} (b: B): SetMonad.M (ContinueOrBreak A B) := ret (by_break b).
下面是一些加入循环的 SetMonad 的例子。
例 1:3x + 1
1 2 3 4 5 6 7 8 9 10 11
Definition body_3x1 (x: Z): SetMonad.M (ContinueOrBreak Z Z) := choice (assume (x <= 1);; break x) (choice (assume (exists k, x = 2 * k);; continue (x / 2)) (assume (exists k, k <> 0 /\ x = 2 * k + 1);; continue (3 * x + 1))).
Definition run_3x1: Z -> SetMonad.M Z := repeat_break body_3x1.
例 2:二分查找
1 2 3 4 5 6 7 8 9 10 11 12 13 14
Definition body_binary_search (P: Z -> Prop): Z * Z -> SetMonad.M (ContinueOrBreak (Z * Z) Z) := fun '(lo, hi) => choice (assume (lo + 1 = hi);; break lo) (assume (lo + 1 < hi);; let mid := (lo + hi) / 2in choice (assume (P mid);; continue (mid, hi)) (assume (~ P mid);; continue (lo, mid))).
Definition binary_search (P: Z -> Prop) (lo hi: Z): SetMonad.M Z := repeat_break (body_binary_search P) (lo, hi).
例 3:归并
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
Definition body_merge: list Z * list Z * list Z -> SetMonad.M (ContinueOrBreak (list Z * list Z * list Z) (list Z)) := fun '(l1, l2, l3) => match l1, l2 with | nil, _ => break (l3 ++ l2) | _, nil => break (l3 ++ l1) | x :: l1', y :: l2' => choice (assume (x <= y);; continue (l1', l2, l3 ++ x :: nil)) (assume (y <= x);; continue (l1, l2', l3 ++ y :: nil)) end.
Definition merge l l0 := repeat_break body_merge (l, l0, nil).
Lecture 10:单子程序上的霍尔逻辑
1. 单子程序上的霍尔逻辑
集合单子上,霍尔三元组会退化为霍尔二元组。
1 2
Definition Hoare {A: Type} (c: SetMonad.M A) (P: A -> Prop): Prop := forall a, a ∈ c -> P a.
各个单子算子满足下面性质。
1 2 3 4 5 6 7 8
Theorem Hoare_bind {A B: Type}: forall (f: SetMonad.M A) (g: A -> SetMonad.M B) (P: A -> Prop) (Q: B -> Prop), Hoare f P -> (forall a, P a -> Hoare (g a) Q) -> Hoare (bind f g) Q.
1 2 3
Theorem Hoare_ret {A: Type}: forall (a: A) (P: A -> Prop), P a -> Hoare (ret a) P.
1 2 3 4 5
Theorem Hoare_conseq {A: Type}: forall (f: SetMonad.M A) (P Q: A -> Prop), (forall a, P a -> Q a) -> Hoare f P -> Hoare f Q.
1 2 3 4 5
Theorem Hoare_conjunct {A: Type}: forall (f: SetMonad.M A) (P Q: A -> Prop), Hoare f P -> Hoare f Q -> Hoare f (fun a => P a /\ Q a).
1 2 3 4 5 6
Theorem Hoare_choice {A: Type}: forall (f g: SetMonad.M A) (P: A -> Prop), Hoare f P -> Hoare g P -> Hoare (choice f g) P.
1 2 3 4 5 6
Theorem Hoare_assume_bind {A: Type}: forall (P: Prop) (f: SetMonad.M A) (Q: A -> Prop), (P -> Hoare f Q) -> (Hoare (assume P;; f) Q).
1 2 3 4 5 6 7 8 9 10
Theorem Hoare_repeat_break {A B: Type}: forall (body: A -> SetMonad.M (ContinueOrBreak A B)) (P: A -> Prop) (Q: B -> Prop), (forall a, P a -> Hoare (body a) (fun x => match x with | by_continue a => P a | by_break b => Q b end)) -> (forall a, P a -> Hoare (repeat_break body a) Q).
2. 霍尔逻辑证明
2.1 3x + 1
1 2 3 4
Theorem functional_correctness_3x1: forall n: Z, n >= 1 -> Hoare (run_3x1 n) (fun m => m = 1).
2.2 二分查找
1 2 3 4 5 6 7
Theorem functional_correctness_binary_search: forall (P: Z -> Prop) lo hi, (forall n m, n <= m -> P m -> P n) -> P lo -> ~ P hi -> Hoare (binary_search P lo hi) (fun x => P x /\ ~ P (x + 1)).
Definition any (A: Type): SetMonad.M A := Sets.full.
Theorem Hoare_any_bind {A B: Type}: forall (f: A -> SetMonad.M B) (Q: B -> Prop), (forall a, Hoare (f a) Q) -> Hoare (bind (any A) f) Q.
Lemma Hoare_Kleene_LFix2 {A1 A2 B: Type}: forall (a1: A1) (a2: A2) (Q: B -> Prop) (f: (A1 -> A2 -> SetMonad.M B) -> (A1 -> A2 -> SetMonad.M B)), (forall n, Hoare (Nat.iter n f H a1 a2) Q) -> Hoare (Kleene_LFix f a1 a2) Q.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
Definition body_ext_euclid (W: Z -> Z -> SetMonad.M (Z * Z)) (a b: Z): SetMonad.M (Z * Z) := choice (assume (b = 0);; choice (assume (a >= 0);; ret (1, 0)) (assume (a <= 0);; ret (-1, 0))) (assume (b <> 0);; q <- any Z;; r <- any Z;; assume (a = r + q * b);; '(x, y) <- W b r;; ret (y, x - q * y)).
Definition ext_euclid (a b: Z): SetMonad.M (Z * Z) := Kleene_LFix body_ext_euclid a b.
1 2 3 4 5 6 7 8 9 10 11 12 13
Lemma functional_correctness_ext_euclid_aux: forall W, (forall a b, Hoare (W a b) (fun '(x, y) => a * x + b * y = Z.gcd a b)) -> (forall a b, Hoare (body_ext_euclid W a b) (fun '(x, y) => a * x + b * y = Z.gcd a b)).
Theorem functional_correctness_ext_euclid: forall a b, Hoare (ext_euclid a b) (fun '(x, y) => a * x + b * y = Z.gcd a b).
2.5 归并排序
1 2 3 4 5 6 7 8 9 10 11 12
Definition gmergesort_f (W: list Z -> SetMonad.M (list Z)): list Z -> SetMonad.M (list Z) := fun x => choice (ext_sort x) (assume(length x >= 2)%nat;; '(p1, q1) <- ext_split x ;; p2 <- W (p1) ;; q2 <- W (q1) ;; merge p2 q2).
Fixpoint list_iter {A B: Type} (f: A -> B -> SetMonad.M B) (l: list A) (b: B): SetMonad.M B := match l with | nil => ret b | a :: l0 => b0 <- f a b;; list_iter f l0 b0 end.
1 2 3 4 5 6 7
Theorem Hoare_list_iter {A B: Type}: forall (f: A -> B -> SetMonad.M B) (P: list A -> B -> Prop), (forall b l a, P l b -> Hoare (f a b) (fun b0 => P (l ++ a :: nil) b0)) -> (forall b l, P nil b -> Hoare (list_iter f l b) (fun b0 => P l b0)).
Lecture 11:分离逻辑
基本概念
考虑 WhileDeref 语言中有关内存地址的读写,需要新的程序逻辑推理规则。
断言 P∗Q 表示:可以将程序状态中的内存拆分成为互不相交的两部分,其一满足 P 另一个满足 Q;
即,程序状态 s 满足性质 P∗Q 当且仅当存在 s1 与 s2 使得:
s1 满足 P,
s2 满足 Q,
s.vars=s1.vars=s2.vars 并且 s.mem=s1.mem⊎s2.mem
简写为 s1⊕s2⇓s,
P∗Q 中的星号称为分离合取
例如:
* x == m && * y == n && x != y 可以写作 store(x, m) * store(y, n)
* * x == 0 && * x != x 可以写作 exists u. store(x, u) * store(u, 0)
Definition M (Σ A: Type): Type := Σ -> A -> Σ -> Prop.
Definition bind (Σ A B: Type) (f: M Σ A) (g: A -> M Σ B): M Σ B := fun (s1: Σ) (b: B) (s3: Σ) => exists (a: A) (s2: Σ), (s1, a, s2) ∈ f /\ (s2, b, s3) ∈ g a.
Definition ret (Σ A: Type) (a0: A): M Σ A := fun (s1: Σ) (a: A) (s2: Σ) => a = a0 /\ s1 = s2.
Definition assume {Σ: Type} (P: Σ -> Prop): StateRelMonad.M Σ unit := fun s1 _ s2 => P s1 /\ s1 = s2.
Definition choice {Σ A: Type} (f g: StateRelMonad.M Σ A): StateRelMonad.M Σ A := f ∪ g.
Definition any {Σ: Type} (A: Type): StateRelMonad.M Σ A := fun s1 _ s2 => s1 = s2.
Definition repeat_break_f {Σ A B: Type} (body: A -> StateRelMonad.M Σ (ContinueOrBreak A B)) (W: A -> StateRelMonad.M Σ B) (a: A): StateRelMonad.M Σ B := x <- body a;; match x with | by_continue a' => W a' | by_break b => ret b end.
Definition repeat_break {Σ A B: Type} (body: A -> StateRelMonad.M Σ (ContinueOrBreak A B)): A -> StateRelMonad.M Σ B := Kleene_LFix (repeat_break_f body).
Definition continue {Σ A B: Type} (a: A): StateRelMonad.M Σ (ContinueOrBreak A B) := ret (by_continue a).
Definition break {Σ A B: Type} (b: B): StateRelMonad.M Σ (ContinueOrBreak A B) := ret (by_break b).
Definition Hoare {Σ A: Type} (P: Σ -> Prop) (c: StateRelMonad.M Σ A) (Q: A -> Σ -> Prop): Prop := forall s1 a s2, P s1 -> (s1, a, s2) P c -> Q a s2.
Theorem Hoare_bind {Σ A B: Type}: forall (P: Σ -> Prop) (f: StateRelMonad.M Σ A) (Q: A -> Σ -> Prop) (g: A -> StateRelMonad.M Σ B) (R: B -> Σ -> Prop), Hoare P f Q -> (forall a, Hoare (Q a) (g a) R) -> Hoare P (bind f g) R.
Theorem Hoare_ret {Σ A: Type}: forall (P: A -> Σ -> Prop) (a0: A), Hoare (P a0) (ret a0) P.
Theorem Hoare_choice {Σ A: Type}: forall P (f g: StateRelMonad.M Σ A) Q, Hoare P f Q -> Hoare P g Q -> Hoare P (choice f g) Q.
Theorem Hoare_assume_bind {Σ A: Type}: forall P (Q: Σ -> Prop) (f: StateRelMonad.M Σ A) R, Hoare (fun s => Q s /\ P s) f R -> Hoare P (assume Q;; f) R.
Theorem Hoare_any_bind {Σ A B: Type}: forall P Q (f: A -> StateRelMonad.M Σ B), (forall a, Hoare P (f a) Q) -> (Hoare P (bind (any A) f) Q).
Theorem Hoare_conseq {Σ A: Type}: forall (P1 P2: Σ -> Prop) f (Q1 Q2: A -> Σ -> Prop), (forall s, P1 s -> P2 s) -> (forall b s, Q2 b s -> Q1 b s) -> Hoare P2 f Q2 -> Hoare P1 f Q1.
Theorem Hoare_conseq_pre {Σ A: Type}: forall (P1 P2: Σ -> Prop) f (Q: A -> Σ -> Prop), (forall s, P1 s -> P2 s) -> Hoare P2 f Q -> Hoare P1 f Q.
Theorem Hoare_conseq_post {Σ A: Type}: forall (P: Σ -> Prop) f (Q1 Q2: A -> Σ -> Prop), (forall b s, Q2 b s -> Q1 b s) -> Hoare P f Q2 -> Hoare P f Q1.
Theorem Hoare_conj {Σ A: Type}: forall (P: Σ -> Prop) f (Q1 Q2: A -> Σ -> Prop), Hoare P f Q1 -> Hoare P f Q2 -> Hoare P f (fun a s => Q1 a s /\ Q2 a s).
Theorem Hoare_forall {Σ A: Type}: forall (X: Type) (P: Σ -> Prop) f (Q: X -> A -> Σ -> Prop), (forall x, Hoare P f (Q x)) -> Hoare P f (fun a s => forall x, Q x a s).
Theorem Hoare_pre_ex {Σ A: Type}: forall (X: Type) (P: X -> Σ -> Prop) f (Q: A -> Σ -> Prop), (forall x, Hoare (P x) f Q) -> Hoare (fun s => exists x, P x s) f Q.
Theorem Hoare_ret ' {Σ A: Type}: forall (P: Σ -> Prop) (Q: A -> Σ -> Prop) (a0: A), (forall s, P s -> Q a0 s) -> Hoare P (ret a0) Q.
Theorem Hoare_repeat_break {Σ A B: Type}: forall (body: A -> StateRelMonad.M Σ (ContinueOrBreak A B)) (P: A -> Σ -> Prop) (Q: B -> Σ -> Prop), (forall a, Hoare (P a) (body a) (fun x s => match x with | by_continue a => P a s | by_break b => Q b s end)) -> (forall a, Hoare (P a) (repeat_break body a) Q).