Theory Nullable_Set

section ‹Nullable Set›

theory Nullable_Set
imports Grammar
begin


definition lhSet :: "('n, 't) prods  'n set" where
  "lhSet ps = set (map fst ps)"

fun nullableGamma :: "('n, 't) rhs  'n set  bool" where
  "nullableGamma [] _ = True"
| "nullableGamma ((T _)#_) _ = False"
| "nullableGamma ((NT x)#gamma') nu = (if x  nu then nullableGamma gamma' nu else False)"

definition updateNu :: "('n, 't) prod  'n set  'n set" where
  "updateNu  λ(x, gamma) nu. (if nullableGamma gamma nu then insert x nu else nu)"

definition nullablePass :: "('n, 't) prods  'n set  'n set" where
  "nullablePass ps nu = foldr updateNu ps nu"

function mkNullableSet' :: "('n, 't) prods  'n set  'n set" where
  "mkNullableSet' ps nu = (let nu' = nullablePass ps nu in
  (if nu=nu' then nu else mkNullableSet' ps nu'))"
  by auto

definition mkNullableSet :: "('n, 't) grammar  'n set" where
  "mkNullableSet g = mkNullableSet' (prods g) {}"


subsection ‹Termination›

definition countNullCands :: "('n, 't) prods  'n set  nat" where
  "countNullCands ps nu = (let candidates = lhSet ps in card (candidates - nu))"

lemma nullablePass_subset: "(nu::'n set)  (nullablePass ps nu)"
  by (induction ps) (auto simp add: nullablePass_def updateNu_def)

lemma nullablePass_Nil[simp]: "nullablePass [] nu = nu"
  by (simp add: nullablePass_def)

lemma nullablePass_cons[simp]: "nullablePass ((y,gamma)#ps') nu =
  (if nullableGamma gamma (nullablePass ps' nu) then insert y else id) (nullablePass ps' nu)"
  by (simp add: nullablePass_def updateNu_def)

lemma nullable_pass_mono:
  "nullablePass ps nu  nullablePass (qs @ ps) nu"
  by (induction qs) (auto simp add: nullablePass_def updateNu_def)

lemma nullablePass_subset_lhSet:
  "nullablePass ps nu  lhSet ps  nu"
  by (induction ps arbitrary: nu; fastforce split: if_split_asm simp: lhSet_def)

lemma nullablePass_neq_candidates_lt:
  assumes "nu  nullablePass ps nu"
  shows "countNullCands ps (nullablePass ps nu) < countNullCands ps nu"
proof -
  have "finite (lhSet ps)" by (simp add: lhSet_def)
  then have A: "finite (lhSet ps - nu)" using finite_subset[where B = "lhSet ps"] lhSet_def by auto
  moreover have B: "lhSet ps - nullablePass ps nu  lhSet ps - nu"
    using nullablePass_subset nullablePass_subset_lhSet assms by fastforce
  ultimately show ?thesis unfolding countNullCands_def by (simp add: Let_def psubset_card_mono)
qed

termination mkNullableSet'
  using nullablePass_neq_candidates_lt
  by (relation "measure (λ(ps, nu). countNullCands ps nu)") force+

subsection ‹Correctness Definitions›

definition nullable_set_sound :: "'n set  ('n, 't) grammar  bool" where
  "nullable_set_sound nu g = (x  nu. nullable_sym g (NT x))"

definition nullable_set_complete :: "'n set  ('n, 't) grammar  bool" where
  "nullable_set_complete nu g = (x. nullable_sym g (NT x)  x  nu)"

abbreviation nullable_set_for :: "'n set  ('n, 't) grammar  bool" where
  "nullable_set_for nu g  nullable_set_sound nu g  nullable_set_complete nu g"


subsection ‹Soundness›

lemma nu_sound_nullableGamma_sound:
  "nullable_set_sound nu g  nullableGamma gamma nu  nullable_gamma g gamma"
  by (induction rule: nullableGamma.induct)
     (auto
       intro: nullable_sym_nullable_gamma.intros split: if_split_asm simp: nullable_set_sound_def)

lemma nullablePass_preserves_soundness':
  "nullable_set_sound nu g  set ps  set (prods g)
   nullable_set_sound (nullablePass ps nu) g"
proof (induction ps)
  case Nil
  then show ?case by simp
next
  case (Cons p ps)
  obtain x gamma where p_def: "p = (x, gamma)" by fastforce
  show ?case
  proof (cases "nullableGamma gamma (nullablePass ps nu)")
    case True
    have "nullable_set_sound (nullablePass ps nu) g"
      using Cons.IH Cons.prems(1,2) by force
    moreover have "nullablePass (p # ps) nu  nullablePass ps nu  {x}"
      by (cases "nullableGamma gamma (nullablePass ps nu)")
        (auto simp add: p = (x, gamma))
    ultimately show ?thesis
      using Cons.prems(2) True nu_sound_nullableGamma_sound nullable_sym.simps p_def
      by (fastforce simp add: nullable_set_sound_def)
  next
    case False
    then show ?thesis using Cons p_def by force
  qed
qed

lemma nullablePass_preserves_soundness:
  "nullable_set_sound nu g  nullable_set_sound (nullablePass (prods g) nu) g"
  using nullablePass_preserves_soundness' by auto

lemmas [simp del] = mkNullableSet'.simps

lemma mkNullableSet'_preserves_soundness:
  "nullable_set_sound nu g  nullable_set_sound (mkNullableSet' (prods g) nu) g"
  by (induction "prods g" nu rule: mkNullableSet'.induct)
     (subst mkNullableSet'.simps, auto intro: nullablePass_preserves_soundness simp: Let_def)

lemma empty_nu_sound: "nullable_set_sound {} g"
  by (simp add: nullable_set_sound_def)

theorem mkNullableSet_sound: "nullable_set_sound (mkNullableSet g) g"
  unfolding mkNullableSet_def using empty_nu_sound by (rule mkNullableSet'_preserves_soundness)


subsection ‹Completeness›

lemma nullablePass_add_equal: "x  nu  nullablePass ps nu = insert x (nullablePass ps nu)"
  using nullablePass_subset by fastforce

lemma nullable_gamma_nullableGamma_true:
  "nullable_gamma g ys  x. (NT x)  set ys  x  nu  nullableGamma ys nu"
  by (induction rule: nullableGamma.induct; force elim: nullable_gamma.cases nullable_sym.cases)

lemma nullableGamma_saturated_if_nullablePass_fixpoint:
  assumes "nu = nullablePass ps nu"
  shows "(x, gamma)  set ps. nullableGamma gamma nu  x  nu"
  using assms nullablePass_add_equal by (induction ps) (fastforce split: if_split_asm)+

lemma nullablePass_equal_complete':
  assumes "nu = nullablePass (prods g) nu"
  shows "nullable_sym g s  y. s = NT y  y  nu"
    "nullable_gamma g ys  y. NT y  set ys  y  nu"
  using assms
proof (induction rule: nullable_sym_nullable_gamma.inducts)
  case (NullableSym x gamma)
  then show ?case
    using nullableGamma_saturated_if_nullablePass_fixpoint nullable_gamma_nullableGamma_true
    by force
qed auto

lemma nullablePass_equal_complete: "nu = (nullablePass (prods g) nu)  nullable_set_complete nu g"
  by (simp add: nullablePass_equal_complete' nullable_set_complete_def)

lemma mkNullableSet'_complete: "nullable_set_complete (mkNullableSet' (prods g) nu) g"
proof (induction "prods g" nu rule: mkNullableSet'.induct)
  case (1 nu)
  then show ?case
    by (subst mkNullableSet'.simps)
      (simp add: Let_def nullable_set_complete_def nullablePass_equal_complete')
qed

theorem mkNullableSet_complete: "nullable_set_complete (mkNullableSet g) g"
  unfolding mkNullableSet_def
  using mkNullableSet'_complete by blast

theorem mkNullableSet_correct: "nullable_set_for (mkNullableSet g) g"
  using mkNullableSet_sound mkNullableSet_complete by auto

end