Theory Rewriting_Sterm

section ‹Sequential pattern matching›

theory Rewriting_Sterm
imports Rewriting_Pterm
begin

type_synonym srule = "name × sterm"

abbreviation closed_srules :: "srule list  bool" where
"closed_srules  list_all (closed  snd)"

primrec srule :: "srule  bool" where
"srule (_, rhs)  wellformed rhs  closed rhs  is_abs rhs"

lemma sruleI[intro!]: "wellformed rhs  closed rhs  is_abs rhs  srule (name, rhs)"
by simp

locale srules = constants C_info "fst |`| fset_of_list rs" for C_info and rs :: "srule list" +
  assumes all_rules: "list_all srule rs"
  assumes distinct: "distinct (map fst rs)"
  assumes not_shadows: "list_all (λ(_, rhs). ¬ shadows_consts rhs) rs"
  assumes swelldefined_rs: "list_all (λ(_, rhs). welldefined rhs) rs"
begin

lemma map: "is_map (set rs)"
using distinct by (rule distinct_is_map)

lemma clausesE:
  assumes "(name, rhs)  set rs"
  obtains cs where "rhs = Sabs cs"
proof -
  from assms have "is_abs rhs"
    using all_rules unfolding list_all_iff by auto
  then obtain cs where "rhs = Sabs cs"
    by (cases rhs) (auto simp: is_abs_def term_cases_def)
  with that show thesis .
qed

end

subsubsection ‹Rewriting›

inductive srewrite_step where
cons_match: "srewrite_step ((name, rhs) # rest) name rhs" |
cons_nomatch: "name  name'  srewrite_step rs name rhs  srewrite_step ((name', rhs') # rs) name rhs"

lemma srewrite_stepI0:
  assumes "(name, rhs)  set rs" "is_map (set rs)"
  shows "srewrite_step rs name rhs"
using assms proof (induction rs)
  case (Cons r rs)
  then obtain name' rhs' where "r = (name', rhs')" by force
  show ?case
    proof (cases "name = name'")
      case False
      show ?thesis
        unfolding r = _
        apply (rule srewrite_step.cons_nomatch)
        subgoal by fact
        apply (rule Cons)
        using False Cons(2) r = _ apply force
        using Cons(3) unfolding is_map_def by auto
    next
      case True
      have "rhs = rhs'"
        apply (rule is_mapD)
          apply fact
        unfolding r = _
        using Cons(2) r = _ apply simp
        using True apply simp
        done
      show ?thesis
        unfolding r = _ name = _ rhs = _
        by (rule srewrite_step.cons_match)
    qed
qed auto

lemma (in srules) srewrite_stepI: "(name, rhs)  set rs  srewrite_step rs name rhs"
using map
by (metis srewrite_stepI0)

hide_fact srewrite_stepI0

inductive srewrite :: "srule list  sterm  sterm  bool" (‹_/ s/ _ / _› [50,0,50] 50) for rs where
step: "srewrite_step rs name rhs  rs s Sconst name  rhs" |
beta: "rewrite_first cs t t'  rs s Sabs cs $s t  t'" |
"fun": "rs s t  t'  rs s t $s u  t' $s u" |
arg: "rs s u  u'  rs s t $s u  t $s u'"

code_pred srewrite .

abbreviation srewrite_rt :: "srule list  sterm  sterm  bool" (‹_/ s/ _ ⟶*/ _› [50,0,50] 50) where
"srewrite_rt rs  (srewrite rs)**"

global_interpretation srewrite: rewriting "srewrite rs" for rs
by standard (auto intro: srewrite.intros simp: app_sterm_def)+

code_pred (modes: i ⇒ i ⇒ o ⇒ bool) srewrite_step .
code_pred (modes: i ⇒ i ⇒ o ⇒ bool) srewrite .

subsubsection ‹Translation from @{typ pterm} to @{typ sterm}

text ‹
  In principle, any function of type @{typ ('a × 'b) fset  ('a × 'b) list} that orders
  by keys would do here. However, For simplicity's sake, we choose a fixed one
  (@{const ordered_fmap}) here.
›

primrec pterm_to_sterm :: "pterm  sterm" where
"pterm_to_sterm (Pconst name) = Sconst name" |
"pterm_to_sterm (Pvar name) = Svar name" |
"pterm_to_sterm (t $p u) = pterm_to_sterm t $s pterm_to_sterm u" |
"pterm_to_sterm (Pabs cs) = Sabs (ordered_fmap (map_prod id pterm_to_sterm |`| cs))"

lemma pterm_to_sterm:
  assumes "no_abs t"
  shows "pterm_to_sterm t = convert_term t"
using assms proof induction
  case (free name)
  show ?case
    apply simp
    apply (simp add: free_sterm_def free_pterm_def)
    done
next
  case (const name)
  show ?case
    apply simp
    apply (simp add: const_sterm_def const_pterm_def)
    done
next
  case (app t1 t2)
  then show ?case
    apply simp
    apply (simp add: app_sterm_def app_pterm_def)
    done
qed

text @{const sterm_to_pterm} has to be defined, for technical reasons, in
  @{theory CakeML_Codegen.Pterm}.
›

lemma pterm_to_sterm_wellformed:
  assumes "wellformed t"
  shows "wellformed (pterm_to_sterm t)"
using assms proof (induction t rule: pterm_induct)
  case (Pabs cs)
  show ?case
    apply simp
    unfolding map_prod_def id_apply
    apply (intro conjI)
    subgoal
      apply (subst list_all_iff_fset)
      apply (subst ordered_fmap_set_eq)
       apply (rule is_fmap_image)
      using Pabs apply simp
      apply (rule fBallI)
      apply (erule fimageE)
      apply auto[]
      using Pabs(2) apply auto[]
      apply (rule Pabs)
      using Pabs(2) by auto
    subgoal
      apply (rule ordered_fmap_distinct)
      apply (rule is_fmap_image)
      using Pabs(2) by simp
    subgoal
      apply (subgoal_tac "cs  {||}")
      including fset.lifting apply transfer
      unfolding ordered_map_def
      using Pabs(2) by auto
    done
qed auto

lemma pterm_to_sterm_sterm_to_pterm:
  assumes "wellformed t"
  shows "sterm_to_pterm (pterm_to_sterm t) = t"
using assms proof (induction t)
  case (Pabs cs)
  note fset_of_list_map[simp del]
  show ?case
    apply simp
    unfolding map_prod_def id_apply
    apply (subst ordered_fmap_image)
    subgoal
      apply (rule is_fmap_image)
      using Pabs by simp
    apply (subst ordered_fmap_set_eq)
    subgoal
      apply (rule is_fmap_image)
      apply (rule is_fmap_image)
      using Pabs by simp
    subgoal
      apply (subst fset.map_comp)
      apply (subst map_prod_def[symmetric])+
      unfolding o_def
      apply (subst prod.map_comp)
      apply (subst id_def[symmetric])+
      apply simp
      apply (subst map_prod_def)
      unfolding id_def
      apply (rule fset_map_snd_id)
      apply simp
      apply (rule Pabs)
      using Pabs(2) by (auto simp: snds.simps)
    done
qed auto

corollary pterm_to_sterm_frees: "wellformed t  frees (pterm_to_sterm t) = frees t"
by (metis pterm_to_sterm_sterm_to_pterm sterm_to_pterm_frees)

corollary pterm_to_sterm_closed:
  "closed_except t S  wellformed t  closed_except (pterm_to_sterm t) S"
unfolding closed_except_def
by (simp add: pterm_to_sterm_frees)

corollary pterm_to_sterm_consts: "wellformed t  consts (pterm_to_sterm t) = consts t"
by (metis pterm_to_sterm_sterm_to_pterm sterm_to_pterm_consts)

corollary (in constants) pterm_to_sterm_shadows:
  "wellformed t  shadows_consts t  shadows_consts (pterm_to_sterm t)"
unfolding shadows_consts_def
by (metis pterm_to_sterm_sterm_to_pterm sterm_to_pterm_all_frees)

definition compile :: "prule fset  srule list" where
"compile rs = ordered_fmap (map_prod id pterm_to_sterm |`| rs)"

subsubsection ‹Correctness of translation›

context prules begin

lemma compile_heads: "fst |`| fset_of_list (compile rs) = fst |`| rs"
  unfolding compile_def
  apply (subst ordered_fmap_set_eq)
   apply (subst map_prod_def, subst id_apply)
   apply (rule is_fmap_image)
   apply (rule fmap)
  apply simp
  done

lemma compile_rules: "srules C_info (compile rs)"
proof
  show "list_all srule (compile rs)"
    using fmap all_rules
    unfolding compile_def list_all_iff
    including fset.lifting
    apply transfer
    apply (subst ordered_map_set_eq)
    subgoal by simp
    subgoal
      unfolding map_prod_def id_def
      by (erule is_map_image)
    subgoal
      apply (rule ballI)
      apply safe
      subgoal
        apply (rule pterm_to_sterm_wellformed)
        apply fastforce
        done
      subgoal
        apply (rule pterm_to_sterm_closed)
         apply fastforce
        apply fastforce
        done
      subgoal for _ _ a b
        apply (erule ballE[where x = "(a, b)"])
         apply (cases b; auto)
            apply (auto simp: is_abs_def term_cases_def)
        done
      done
    done
next
  show "distinct (map fst (compile rs))"
    unfolding compile_def
    apply (rule ordered_fmap_distinct)
    unfolding map_prod_def id_def
    apply (rule is_fmap_image)
    apply (rule fmap)
    done
next
  have "list_all (λ(_, rhs). welldefined rhs) (compile rs)"
    unfolding compile_def
    apply (subst ordered_fmap_list_all)
    subgoal
      apply (subst map_prod_def)
      apply (subst id_apply)
      apply (rule is_fmap_image)
      by (fact fmap)
    apply simp
    apply (rule fBallI)
    subgoal for x
      apply (cases x, simp)
      apply (subst pterm_to_sterm_consts)
      using all_rules apply force
      using welldefined_rs by force
    done
  thus "list_all (λ(_, rhs). consts rhs |⊆| pre_constants.all_consts C_info (fst |`| fset_of_list (compile rs))) (compile rs)"
    by (simp add: compile_heads)
next
  interpret c: constants _ "fset_of_list (map fst (compile rs))"
    by (simp add: constants_axioms compile_heads)
  have all_consts: "c.all_consts = all_consts"
    by (simp add: compile_heads)

  note fset_of_list_map[simp del]
  have "list_all (λ(_, rhs). ¬ shadows_consts rhs) (compile rs)"
    unfolding compile_def
    apply (subst list_all_iff_fset)
    apply (subst ordered_fmap_set_eq)
     apply (subst map_prod_def)
    unfolding id_apply
     apply (rule is_fmap_image)
     apply (fact fmap)
    apply simp
    apply (rule fBall_pred_weaken[where P = "λ(_, rhs). ¬ shadows_consts rhs"])
    subgoal for x
      apply (cases x, simp)
      apply (subst (asm) pterm_to_sterm_shadows)
      using all_rules apply force
      by simp
    subgoal
      using not_shadows by force
    done
  thus "list_all (λ(_, rhs). ¬ pre_constants.shadows_consts C_info (fst |`| fset_of_list (compile rs)) rhs) (compile rs)"
    unfolding compile_heads all_consts .
next
  show "fdisjnt (fst |`| fset_of_list (compile rs)) C"
    unfolding compile_def
    apply (subst fset_of_list_map[symmetric])
    apply (subst ordered_fmap_keys)
     apply (subst map_prod_def)
     apply (subst id_apply)
     apply (rule is_fmap_image)
    using fmap disjnt by auto
next
  show "distinct all_constructors"
    by (fact distinct_ctr)
qed

sublocale prules_as_srules: srules C_info "compile rs"
by (fact compile_rules)

end

global_interpretation srelated: term_struct_rel_strong "(λp s. p = sterm_to_pterm s)"
proof (standard, goal_cases)
  case (5 name t)
  then show ?case by (cases t) (auto simp: const_sterm_def const_pterm_def split: option.splits)
next
  case (6 u1 u2 t)
  then show ?case by (cases t) (auto simp: app_sterm_def app_pterm_def split: option.splits)
qed (auto simp: const_sterm_def const_pterm_def app_sterm_def app_pterm_def)

lemma srelated_subst:
  assumes "srelated.P_env penv senv"
  shows "subst (sterm_to_pterm t) penv = sterm_to_pterm (subst t senv)"
using assms
proof (induction t arbitrary: penv senv)
  case (Svar name)
  thus ?case
    by (cases rule: fmrel_cases[where x = name]) auto
next
  case (Sabs cs)
  show ?case
    apply simp
    including fset.lifting
    apply (transfer' fixing: cs penv senv)
    unfolding set_map image_comp
    apply (rule image_cong[OF refl])
    unfolding comp_apply
    apply (case_tac x)
    apply hypsubst_thin
    apply simp
    apply (rule Sabs)
      apply assumption
     apply (simp add: snds.simps)
    apply rule
    apply (rule Sabs)
    done
qed auto

context begin

private lemma srewrite_step_non_empty: "srewrite_step rs' name rhs  rs'  []"
by (induct rule: srewrite_step.induct) auto

private lemma compile_consE:
  assumes "(name, rhs') # rest = compile rs" "is_fmap rs"
  obtains rhs where "rhs' = pterm_to_sterm rhs" "(name, rhs) |∈| rs" "rest = compile (rs - {| (name, rhs) |})"
proof -
  from assms have "ordered_fmap (map_prod id pterm_to_sterm |`| rs) = (name, rhs') # rest"
    unfolding compile_def
    by simp
  hence "(name, rhs')  set (ordered_fmap (map_prod id pterm_to_sterm |`| rs))"
    by simp

  have "(name, rhs') |∈| map_prod id pterm_to_sterm |`| rs"
    apply (rule ordered_fmap_sound)
    subgoal
      unfolding map_prod_def id_apply
      apply (rule is_fmap_image)
      apply fact
      done
    subgoal by fact
    done
  then obtain rhs where "rhs' = pterm_to_sterm rhs" "(name, rhs) |∈| rs"
    by auto

  have "rest = compile (rs - {| (name, rhs) |})"
    unfolding compile_def
    apply (subst inj_on_fimage_set_diff[where C = rs])
    subgoal
      apply (rule inj_onI)
      apply safe
       apply auto
      using is_fmap rs by (blast dest: is_fmapD)
    subgoal by simp
    subgoal using (name, rhs) |∈| rs by simp
    subgoal
      apply simp
      apply (subst ordered_fmap_remove)
        apply (subst map_prod_def)
      unfolding id_apply
        apply (rule is_fmap_image)
        apply fact
      using (name, rhs) |∈| rs apply force
      apply (subst rhs' = pterm_to_sterm rhs[symmetric])
      apply (subst ordered_fmap _ = _[unfolded id_def])
      by simp
    done

  show thesis
    by (rule that) fact+
qed

private lemma compile_correct_step:
  assumes "srewrite_step (compile rs) name rhs" "is_fmap rs" "fBall rs prule"
  shows "(name, sterm_to_pterm rhs) |∈| rs"
using assms proof (induction "compile rs" name rhs arbitrary: rs)
  case (cons_match name rhs' rest)
  then obtain rhs where "rhs' = pterm_to_sterm rhs" "(name, rhs) |∈| rs"
    by (auto elim: compile_consE)

  show ?case
    unfolding rhs' = _
    apply (subst pterm_to_sterm_sterm_to_pterm)
    using fbspec[OF fBall rs prule (name, rhs) |∈| rs] apply force
    by fact
next
  case (cons_nomatch name name1 rest rhs rhs1')
  then obtain rhs1 where "rhs1' = pterm_to_sterm rhs1" "(name1, rhs1) |∈| rs" "rest = compile (rs - {| (name1, rhs1) |})"
    by (auto elim: compile_consE)

  let ?rs' = "rs - {| (name1, rhs1) |}"
  have "(name, sterm_to_pterm rhs) |∈| ?rs'"
    proof (intro cons_nomatch)
      show "rest = compile ?rs'"
        by fact

      show "is_fmap (rs |-| {|(name1, rhs1)|})"
        using is_fmap rs
        by (rule is_fmap_subset) auto

      show "fBall ?rs' prule"
        using cons_nomatch by blast
    qed
  thus ?case
    by simp
qed

lemma compile_correct0:
  assumes "compile rs s u  u'" "prules C rs"
  shows "rs p sterm_to_pterm u  sterm_to_pterm u'"
using assms proof induction
  case (beta cs t t')
  then obtain pat rhs env where "(pat, rhs)  set cs" "match pat t = Some env" "t' = subst rhs env"
    by (auto elim: rewrite_firstE)

  then obtain env' where "match pat (sterm_to_pterm t) = Some env'" "srelated.P_env env' env"
    by (metis option.distinct(1) option.inject option.rel_cases srelated.match_rel)
  hence "subst (sterm_to_pterm rhs) env' = sterm_to_pterm (subst rhs env)"
    by (simp add: srelated_subst)

  let ?rhs' = "sterm_to_pterm rhs"

  have "(pat, ?rhs') |∈| fset_of_list (map (map_prod id sterm_to_pterm) cs)"
    using (pat, rhs)  set cs
    including fset.lifting
    by transfer' force

  note fset_of_list_map[simp del]
  show ?case
    apply simp
    apply (rule prewrite.intros)
     apply fact
    unfolding rewrite_step.simps
    apply (subst map_option_eq_Some)
    apply (intro exI conjI)
     apply fact
    unfolding t' = _
    by fact
next
  case (step name rhs)
  hence "(name, sterm_to_pterm rhs) |∈| rs"
    unfolding prules_def prules_axioms_def
    by (metis compile_correct_step)
  thus ?case
    by (auto intro: prewrite.intros)
qed (auto intro: prewrite.intros)

end

lemma (in prules) compile_correct:
  assumes "compile rs s u  u'"
  shows "rs p sterm_to_pterm u  sterm_to_pterm u'"
by (rule compile_correct0) (fact | standard)+

hide_fact compile_correct0

subsubsection ‹Completeness of translation›

global_interpretation srelated': term_struct_rel_strong "(λp s. pterm_to_sterm p = s)"
proof (standard, goal_cases)
  case (1 t name)
  then show ?case by (cases t) (auto simp: const_sterm_def const_pterm_def split: option.splits)
next
  case (3 t u1 u2)
  then show ?case by (cases t) (auto simp: app_sterm_def app_pterm_def split: option.splits)
qed (auto simp: const_sterm_def const_pterm_def app_sterm_def app_pterm_def)

corollary srelated_env_unique:
  "srelated'.P_env penv senv  srelated'.P_env penv senv'  senv = senv'"
apply (subst (asm) fmrel_iff)+
apply (subst (asm) option.rel_sel)+
apply (rule fmap_ext)
by (metis option.exhaust_sel)

lemma srelated_subst':
  assumes "srelated'.P_env penv senv" "wellformed t"
  shows "pterm_to_sterm (subst t penv) = subst (pterm_to_sterm t) senv"
using assms proof (induction t arbitrary: penv senv)
  case (Pvar name)
  thus ?case
    by (cases rule: fmrel_cases[where x = name]) auto
next
  case (Pabs cs)
  hence "is_fmap cs"
    by force

  show ?case
    apply simp
    unfolding map_prod_def id_apply
    apply (subst ordered_fmap_image[symmetric])
     apply fact
    apply (subst fset.map_comp[symmetric])
    apply (subst ordered_fmap_image[symmetric])
    subgoal by (rule is_fmap_image) fact
    apply (subst ordered_fmap_image[symmetric])
     apply fact
    apply auto
    apply (drule ordered_fmap_sound[OF is_fmap cs])
    subgoal for pat rhs
      apply (rule Pabs)
         apply assumption
        apply auto
      using Pabs by force+
    done
qed auto

lemma srelated_find_match:
  assumes "find_match cs t = Some (penv, pat, rhs)" "srelated'.P_env penv senv"
  shows "find_match (map (map_prod id pterm_to_sterm) cs) (pterm_to_sterm t) = Some (senv, pat, pterm_to_sterm rhs)"
proof -
  let ?cs' = "map (map_prod id pterm_to_sterm) cs"
  let ?t' = "pterm_to_sterm t"
  have *: "list_all2 (rel_prod (=) (λp s. pterm_to_sterm p = s)) cs ?cs'"
    unfolding list.rel_map
    by (auto intro: list.rel_refl)

  obtain senv0
    where "find_match ?cs' ?t' = Some (senv0, pat, pterm_to_sterm rhs)" "srelated'.P_env penv senv0"
    using srelated'.find_match_rel[OF * refl, where t = t, unfolded assms]
    unfolding option_rel_Some1 rel_prod_conv
    by auto
  with assms have "senv = senv0"
    by (metis srelated_env_unique)
  show ?thesis
    unfolding senv = _ by fact
qed

lemma (in prules) compile_complete:
  assumes "rs p t  t'" "wellformed t"
  shows "compile rs s pterm_to_sterm t  pterm_to_sterm t'"
using assms proof induction
  case (step name rhs)
  then show ?case
    apply simp
    apply rule
    apply (rule prules_as_srules.srewrite_stepI)
    unfolding compile_def
    apply (subst fset_of_list_elem[symmetric])
    apply (subst ordered_fmap_set_eq)
     apply (insert fmap)
     apply (rule is_fmapI)
     apply (force dest: is_fmapD)
    by (metis fimage_eqI id_def map_prod_simp)
next
  case (beta c cs t t')
  from beta obtain pat rhs penv where "c = (pat, rhs)" "match pat t = Some penv" "subst rhs penv = t'"
    by (metis (no_types, lifting) map_option_eq_Some rewrite_step.simps surj_pair)
  then obtain senv where "match pat (pterm_to_sterm t) = Some senv" "srelated'.P_env penv senv"
    by (metis option_rel_Some1 srelated'.match_rel)
  have "wellformed rhs"
    using beta c = _ prules.all_rules prule.simps
    by force
  then have "subst (pterm_to_sterm rhs) senv = pterm_to_sterm t'"
    using srelated_subst' _ = t' srelated'.P_env _ _
    by metis
  have "(pat, pterm_to_sterm rhs) |∈| map_prod id pterm_to_sterm |`| cs"
    using beta c = _
    by (metis fimage_eqI id_def map_prod_simp)
  have "is_fmap cs"
    using beta
    by auto
  have "find_match (ordered_fmap cs) t = Some (penv, pat, rhs)"
    apply (rule compatible_find_match)
    subgoal
      apply (subst ordered_fmap_set_eq[OF is_fmap cs])+
      using beta by simp
    subgoal
      unfolding list_all_iff
      apply rule
      apply (rename_tac x, case_tac x)
      apply simp
      apply (drule ordered_fmap_sound[OF is_fmap cs])
      using beta by auto
    subgoal
      apply (subst ordered_fmap_set_eq)
      by fact
    subgoal
      by fact
    subgoal
      using beta(1) c = _ is_fmap cs
      using fset_of_list_elem ordered_fmap_set_eq by fast
    done

  show ?case
    apply simp
    apply rule
    apply (subst _ = pterm_to_sterm t'[symmetric])
    apply (rule find_match_rewrite_first)
    unfolding map_prod_def id_apply
    apply (subst ordered_fmap_image[symmetric])
     apply fact
    apply (subst map_prod_def[symmetric])
    apply (subst id_def[symmetric])
    apply (rule srelated_find_match)
    by fact+
qed (auto intro: srewrite.intros)

subsubsection ‹Computability›

export_code compile
  checking Scala

end