Theory OCL_Normalization

(*  Title:       Safe OCL
    Author:      Denis Nikiforov, March 2019
    Maintainer:  Denis Nikiforov <denis.nikif at gmail.com>
    License:     LGPL
*)
chapter ‹Normalization›
theory OCL_Normalization
  imports OCL_Typing
begin

(*** Normalization Rules ****************************************************)

section ‹Normalization Rules›

text ‹
  The following expression normalization rules includes two kinds of an
  abstract syntax tree transformations:
\begin{itemize}
\item determination of implicit types of variables, iterators, and
      tuple elements,
\item unfolding of navigation shorthands and safe navigation operators,
      described in \autoref{tab:norm_rules}.
\end{itemize}

  The following variables are used in the table:
\begin{itemize}
\item ▩‹x› is a non-nullable value,
\item ▩‹n› is a nullable value,
\item ▩‹xs› is a collection of non-nullable values,
\item ▩‹ns› is a collection of nullable values. 
\end{itemize}

\begin{table}[h!]
  \begin{center}
    \caption{Expression Normalization Rules}
    \label{tab:norm_rules}
    \begin{threeparttable}
    \begin{tabular}{c|c}
      \textbf{Orig. expr.} & \textbf{Normalized expression}\\
      \hline
      ▩‹x.op()› & ▩‹x.op()›\\
      ▩‹n.op()› & ▩‹n.op()›\tnote{*}\\
      ▩‹x?.op()› & ---\\
      ▩‹n?.op()› & ▩‹if n <> null then n.oclAsType(T[1]).op() else null endif›\tnote{**}\\
      ▩‹x->op()› & ▩‹x.oclAsSet()->op()›\\
      ▩‹n->op()› & ▩‹n.oclAsSet()->op()›\\
      ▩‹x?->op()› & ---\\
      ▩‹n?->op()› & ---\\
      \hline
      ▩‹xs.op()› & ▩‹xs->collect(x | x.op())›\\
      ▩‹ns.op()› & ▩‹ns->collect(n | n.op())›\tnote{*}\\
      ▩‹xs?.op()› & ---\\
      ▩‹ns?.op()› & ▩‹ns->selectByKind(T[1])->collect(x | x.op())›\\
      ▩‹xs->op()› & ▩‹xs->op()›\\
      ▩‹ns->op()› & ▩‹ns->op()›\\
      ▩‹xs?->op()› & ---\\
      ▩‹ns?->op()› & ▩‹ns->selectByKind(T[1])->op()›\\
    \end{tabular}
    \begin{tablenotes}
    \item[*] The resulting expression will be ill-typed if the operation is unsafe.
    An unsafe operation is an operation which is well-typed for
    a non-nullable source only.
    \item[**] It would be a good idea to prohibit such a transformation
    for safe operations. A safe operation is an operation which is well-typed
    for a nullable source. However, it is hard to define safe operations
    formally considering operations overloading, complex relations between
    operation parameters types (please see the typing rules for the equality
    operator), and user-defined operations.
    \end{tablenotes}
    \end{threeparttable}
  \end{center}
\end{table}

  Please take a note that name resolution of variables, types,
  attributes, and associations is out of scope of this section.
  It should be done on a previous phase during transformation
  of a concrete syntax tree to an abstract syntax tree.›

fun string_of_nat :: "nat ⇒ string" where
  "string_of_nat n = (if n < 10 then [char_of (48 + n)]
      else string_of_nat (n div 10) @ [char_of (48 + (n mod 10))])"

definition "new_vname ≡ String.implode ∘ string_of_nat ∘ fcard ∘ fmdom"

inductive normalize
    :: "('a :: ocl_object_model) type env ⇒ 'a expr ⇒ 'a expr ⇒ bool"
    (‹_ ⊢ _ ⇛/ _› [51,51,51] 50) and
    normalize_call (‹_ ⊢C _ ⇛/ _› [51,51,51] 50) and
    normalize_expr_list (‹_ ⊢L _ ⇛/ _› [51,51,51] 50)
    where
 LiteralN:
  "Γ ⊢ Literal a ⇛ Literal a"
|ExplicitlyTypedLetN:
  "Γ ⊢ init1 ⇛ init2 ⟹
   Γ(v ↦f τ) ⊢ body1 ⇛ body2 ⟹
   Γ ⊢ Let v (Some τ) init1 body1 ⇛ Let v (Some τ) init2 body2"
|ImplicitlyTypedLetN:
  "Γ ⊢ init1 ⇛ init2 ⟹
   Γ ⊢E init2 : τ ⟹
   Γ(v ↦f τ) ⊢ body1 ⇛ body2 ⟹
   Γ ⊢ Let v None init1 body1 ⇛ Let v (Some τ) init2 body2"
|VarN:
  "Γ ⊢ Var v ⇛ Var v"
|IfN:
  "Γ ⊢ a1 ⇛ a2 ⟹
   Γ ⊢ b1 ⇛ b2 ⟹
   Γ ⊢ c1 ⇛ c2 ⟹
   Γ ⊢ If a1 b1 c1 ⇛ If a2 b2 c2"

|MetaOperationCallN:
  "Γ ⊢ MetaOperationCall τ op ⇛ MetaOperationCall τ op"
|StaticOperationCallN:
  "Γ ⊢L params1 ⇛ params2 ⟹
   Γ ⊢ StaticOperationCall τ op params1 ⇛ StaticOperationCall τ op params2"

|OclAnyDotCallN:
  "Γ ⊢ src1 ⇛ src2 ⟹
   Γ ⊢E src2 : τ ⟹
   τ ≤ OclAny[?] ∨ τ ≤ Tuple fmempty ⟹
   (Γ, τ) ⊢C call1 ⇛ call2 ⟹
   Γ ⊢ Call src1 DotCall call1 ⇛ Call src2 DotCall call2"
|OclAnySafeDotCallN:
  "Γ ⊢ src1 ⇛ src2 ⟹
   Γ ⊢E src2 : τ ⟹
   OclVoid[?] ≤ τ ⟹
   (Γ, to_required_type τ) ⊢C call1 ⇛ call2 ⟹
   src3 = TypeOperationCall src2 DotCall OclAsTypeOp (to_required_type τ) ⟹
   Γ ⊢ Call src1 SafeDotCall call1 ⇛
       If (OperationCall src2 DotCall NotEqualOp [NullLiteral])
          (Call src3 DotCall call2)
          NullLiteral"
|OclAnyArrowCallN:
  "Γ ⊢ src1 ⇛ src2 ⟹
   Γ ⊢E src2 : τ ⟹
   τ ≤ OclAny[?] ∨ τ ≤ Tuple fmempty ⟹
   src3 = OperationCall src2 DotCall OclAsSetOp [] ⟹
   Γ ⊢E src3 : σ ⟹
   (Γ, σ) ⊢C call1 ⇛ call2 ⟹
   Γ ⊢ Call src1 ArrowCall call1 ⇛ Call src3 ArrowCall call2"

|CollectionArrowCallN:
  "Γ ⊢ src1 ⇛ src2 ⟹
   Γ ⊢E src2 : τ ⟹
   element_type τ _ ⟹
   (Γ, τ) ⊢C call1 ⇛ call2 ⟹
   Γ ⊢ Call src1 ArrowCall call1 ⇛ Call src2 ArrowCall call2"
|CollectionSafeArrowCallN:
  "Γ ⊢ src1 ⇛ src2 ⟹
   Γ ⊢E src2 : τ ⟹
   element_type τ σ ⟹
   OclVoid[?] ≤ σ ⟹
   src3 = TypeOperationCall src2 ArrowCall SelectByKindOp
              (to_required_type σ) ⟹
   Γ ⊢E src3 : ρ ⟹
   (Γ, ρ) ⊢C call1 ⇛ call2 ⟹
   Γ ⊢ Call src1 SafeArrowCall call1 ⇛ Call src3 ArrowCall call2"
|CollectionDotCallN:
  "Γ ⊢ src1 ⇛ src2 ⟹
   Γ ⊢E src2 : τ ⟹
   element_type τ σ ⟹
   (Γ, σ) ⊢C call1 ⇛ call2 ⟹
   it = new_vname Γ ⟹
   Γ ⊢ Call src1 DotCall call1 ⇛
    CollectIteratorCall src2 ArrowCall [it] (Some σ) (Call (Var it) DotCall call2)"
|CollectionSafeDotCallN:
  "Γ ⊢ src1 ⇛ src2 ⟹
   Γ ⊢E src2 : τ ⟹
   element_type τ σ ⟹
   OclVoid[?] ≤ σ ⟹
   ρ = to_required_type σ ⟹
   src3 = TypeOperationCall src2 ArrowCall SelectByKindOp ρ ⟹
   (Γ, ρ) ⊢C call1 ⇛ call2 ⟹
   it = new_vname Γ ⟹
   Γ ⊢ Call src1 SafeDotCall call1 ⇛
    CollectIteratorCall src3 ArrowCall [it] (Some ρ) (Call (Var it) DotCall call2)"

|TypeOperationN:
  "(Γ, τ) ⊢C TypeOperation op ty ⇛ TypeOperation op ty"
|AttributeN:
  "(Γ, τ) ⊢C Attribute attr ⇛ Attribute attr"
|AssociationEndN:
  "(Γ, τ) ⊢C AssociationEnd role from ⇛ AssociationEnd role from"
|AssociationClassN:
  "(Γ, τ) ⊢C AssociationClass 𝒜 from ⇛ AssociationClass 𝒜 from"
|AssociationClassEndN:
  "(Γ, τ) ⊢C AssociationClassEnd role ⇛ AssociationClassEnd role"
|OperationN:
  "Γ ⊢L params1 ⇛ params2 ⟹
   (Γ, τ) ⊢C Operation op params1 ⇛ Operation op params2"
|TupleElementN:
  "(Γ, τ) ⊢C TupleElement elem ⇛ TupleElement elem"

|ExplicitlyTypedIterateN:
  "Γ ⊢ res_init1 ⇛ res_init2 ⟹
   Γ ++f fmap_of_list (map (λit. (it, σ)) its) ⊢
      Let res res_t1 res_init1 body1 ⇛ Let res res_t2 res_init2 body2 ⟹
   (Γ, τ) ⊢C Iterate its (Some σ) res res_t1 res_init1 body1 ⇛
      Iterate its (Some σ) res res_t2 res_init2 body2"
|ImplicitlyTypedIterateN:
  "element_type τ σ ⟹
   Γ ⊢ res_init1 ⇛ res_init2 ⟹
   Γ ++f fmap_of_list (map (λit. (it, σ)) its) ⊢
      Let res res_t1 res_init1 body1 ⇛ Let res res_t2 res_init2 body2 ⟹
   (Γ, τ) ⊢C Iterate its None res res_t1 res_init1 body1 ⇛
      Iterate its (Some σ) res res_t2 res_init2 body2"

|ExplicitlyTypedIteratorN:
  "Γ ++f fmap_of_list (map (λit. (it, σ)) its) ⊢ body1 ⇛ body2  ⟹
   (Γ, τ) ⊢C Iterator iter its (Some σ) body1 ⇛ Iterator iter its (Some σ) body2"
|ImplicitlyTypedIteratorN:
  "element_type τ σ ⟹
   Γ ++f fmap_of_list (map (λit. (it, σ)) its) ⊢ body1 ⇛ body2  ⟹
   (Γ, τ) ⊢C Iterator iter its None body1 ⇛ Iterator iter its (Some σ) body2"

|ExprListNilN:
  "Γ ⊢L [] ⇛ []"
|ExprListConsN:
  "Γ ⊢ x ⇛ y ⟹
   Γ ⊢L xs ⇛ ys ⟹
   Γ ⊢L x # xs ⇛ y # ys"

(*** Elimination Rules ******************************************************)

section ‹Elimination Rules›

inductive_cases LiteralNE [elim]: "Γ ⊢ Literal a ⇛ b"
inductive_cases LetNE [elim]: "Γ ⊢ Let v t init body ⇛ b"
inductive_cases VarNE [elim]: "Γ ⊢ Var v ⇛ b"
inductive_cases IfNE [elim]: "Γ ⊢ If a b c ⇛ d"

inductive_cases MetaOperationCallNE [elim]: "Γ ⊢ MetaOperationCall τ op ⇛ b"
inductive_cases StaticOperationCallNE [elim]: "Γ ⊢ StaticOperationCall τ op as ⇛ b"
inductive_cases DotCallNE [elim]: "Γ ⊢ Call src DotCall call ⇛ b"
inductive_cases SafeDotCallNE [elim]: "Γ ⊢ Call src SafeDotCall call ⇛ b"
inductive_cases ArrowCallNE [elim]: "Γ ⊢ Call src ArrowCall call ⇛ b"
inductive_cases SafeArrowCallNE [elim]: "Γ ⊢ Call src SafeArrowCall call ⇛ b"

inductive_cases CallNE [elim]: "(Γ, τ) ⊢C call ⇛ b"
inductive_cases OperationCallNE [elim]: "(Γ, τ) ⊢C Operation op as ⇛ call"
inductive_cases IterateCallNE [elim]: "(Γ, τ) ⊢C Iterate its its_ty res res_t res_init body ⇛ call"
inductive_cases IteratorCallNE [elim]: "(Γ, τ) ⊢C Iterator iter its its_ty body ⇛ call"

inductive_cases ExprListNE [elim]: "Γ ⊢L xs ⇛ ys"

(*** Simplification Rules ***************************************************)

section ‹Simplification Rules›

inductive_simps normalize_alt_simps:
"Γ ⊢ Literal a ⇛ b"
"Γ ⊢ Let v t init body ⇛ b"
"Γ ⊢ Var v ⇛ b"
"Γ ⊢ If a b c ⇛ d"

"Γ ⊢ MetaOperationCall τ op ⇛ b"
"Γ ⊢ StaticOperationCall τ op as ⇛ b"
"Γ ⊢ Call src DotCall call ⇛ b"
"Γ ⊢ Call src SafeDotCall call ⇛ b"
"Γ ⊢ Call src ArrowCall call ⇛ b"
"Γ ⊢ Call src SafeArrowCall call ⇛ b"

"(Γ, τ) ⊢C call ⇛ b"
"(Γ, τ) ⊢C Operation op as ⇛ call"
"(Γ, τ) ⊢C Iterate its its_ty res res_t res_init body ⇛ call"
"(Γ, τ) ⊢C Iterator iter its its_ty body ⇛ call"

"Γ ⊢L [] ⇛ ys"
"Γ ⊢L x # xs ⇛ ys"

(*** Determinism ************************************************************)

section ‹Determinism›

lemma any_has_not_element_type:
  "element_type τ σ ⟹ τ ≤ OclAny[?] ∨ τ ≤ Tuple fmempty ⟹ False"
  by (erule element_type.cases; auto)

lemma any_has_not_element_type':
  "element_type τ σ ⟹ OclVoid[?] ≤ τ ⟹ False"
  by (erule element_type.cases; auto)

lemma
  normalize_det:
    "Γ ⊢ expr ⇛ expr1 ⟹
     Γ ⊢ expr ⇛ expr2 ⟹ expr1 = expr2" and
  normalize_call_det:
    "Γ_τ ⊢C call ⇛ call1 ⟹
     Γ_τ ⊢C call ⇛ call2 ⟹ call1 = call2" and
  normalize_expr_list_det:
    "Γ ⊢L xs ⇛ ys ⟹
     Γ ⊢L xs ⇛ zs ⟹ ys = zs"
  for Γ :: "('a :: ocl_object_model) type env"
  and Γ_τ :: "('a :: ocl_object_model) type env × 'a type"
proof (induct arbitrary: expr2 and call2 and zs
       rule: normalize_normalize_call_normalize_expr_list.inducts)
  case (LiteralN Γ a) thus ?case by auto
next
  case (ExplicitlyTypedLetN Γ init1 init2 v τ body1 body2) thus ?case
    by blast
next
  case (ImplicitlyTypedLetN Γ init1 init2 τ v body1 body2) thus ?case
    by (metis (mono_tags, lifting) LetNE option.distinct(1) typing_det)
next
  case (VarN Γ v) thus ?case by auto
next
  case (IfN Γ a1 a2 b1 b2 c1 c2) thus ?case
    apply (insert IfN.prems)
    apply (erule IfNE)
    by (simp add: IfN.hyps)
next
  case (MetaOperationCallN Γ τ op) thus ?case by auto
next
  case (StaticOperationCallN Γ params1 params2 τ op) thus ?case by blast
next
  case (OclAnyDotCallN Γ src1 src2 τ call1 call2) show ?case
    apply (insert OclAnyDotCallN.prems)
    apply (erule DotCallNE)
    using OclAnyDotCallN.hyps typing_det apply metis
    using OclAnyDotCallN.hyps any_has_not_element_type typing_det by metis
next
  case (OclAnySafeDotCallN Γ src1 src2 τ call1 call2) show ?case
    apply (insert OclAnySafeDotCallN.prems)
    apply (erule SafeDotCallNE)
    using OclAnySafeDotCallN.hyps typing_det comp_apply
    apply (metis (no_types, lifting) list.simps(8) list.simps(9))
    using OclAnySafeDotCallN.hyps typing_det any_has_not_element_type'
    by metis
next
  case (OclAnyArrowCallN Γ src1 src2 τ src3 σ call1 call2) show ?case
    apply (insert OclAnyArrowCallN.prems)
    apply (erule ArrowCallNE)
    using OclAnyArrowCallN.hyps typing_det comp_apply apply metis
    using OclAnyArrowCallN.hyps typing_det any_has_not_element_type
    by metis
next
  case (CollectionArrowCallN Γ src1 src2 τ uu call1 call2) show ?case
    apply (insert CollectionArrowCallN.prems)
    apply (erule ArrowCallNE)
    using CollectionArrowCallN.hyps typing_det any_has_not_element_type
    apply metis
    using CollectionArrowCallN.hyps typing_det by metis
next
  case (CollectionSafeArrowCallN Γ src1 src2 τ σ src3 ρ call1 call2) show ?case
    apply (insert CollectionSafeArrowCallN.prems)
    apply (erule SafeArrowCallNE)
    using CollectionSafeArrowCallN.hyps typing_det element_type_det by metis
next
  case (CollectionDotCallN Γ src1 src2 τ σ call1 call2 it) show ?case
    apply (insert CollectionDotCallN.prems)
    apply (erule DotCallNE)
    using CollectionDotCallN.hyps typing_det any_has_not_element_type
    apply metis
    using CollectionDotCallN.hyps typing_det element_type_det by metis
next
  case (CollectionSafeDotCallN Γ src1 src2 τ σ src3 call1 call2 it) show ?case
    apply (insert CollectionSafeDotCallN.prems)
    apply (erule SafeDotCallNE)
    using CollectionSafeDotCallN.hyps typing_det any_has_not_element_type'
    apply metis
    using CollectionSafeDotCallN.hyps typing_det element_type_det by metis
next
  case (TypeOperationN Γ τ op ty) thus ?case by auto
next
  case (AttributeN Γ τ attr) thus ?case by auto
next
  case (AssociationEndN Γ τ role "from") thus ?case by auto
next
  case (AssociationClassN Γ τ 𝒜 "from") thus ?case by auto
next
  case (AssociationClassEndN Γ τ role) thus ?case by auto
next
  case (OperationN Γ params1 params2 τ op) thus ?case by blast
next
  case (TupleElementN Γ τ elem) thus ?case by auto
next
  case (ExplicitlyTypedIterateN
    Γ res_init1 res_init2 σ its res res_t1 body1 res_t2 body2 τ)
  show ?case
    apply (insert ExplicitlyTypedIterateN.prems)
    apply (erule IterateCallNE)
    using ExplicitlyTypedIterateN.hyps element_type_det by blast+
next
  case (ImplicitlyTypedIterateN
    τ σ Γ res_init1 res_init2 its res res_t1 body1 res_t2 body2)
  show ?case
    apply (insert ImplicitlyTypedIterateN.prems)
    apply (erule IterateCallNE)
    using ImplicitlyTypedIterateN.hyps element_type_det by blast+
next
  case (ExplicitlyTypedIteratorN Γ σ its body1 body2 τ iter)
  show ?case
    apply (insert ExplicitlyTypedIteratorN.prems)
    apply (erule IteratorCallNE)
    using ExplicitlyTypedIteratorN.hyps element_type_det by blast+
next
  case (ImplicitlyTypedIteratorN τ σ Γ its body1 body2 iter)
  show ?case
    apply (insert ImplicitlyTypedIteratorN.prems)
    apply (erule IteratorCallNE)
    using ImplicitlyTypedIteratorN.hyps element_type_det by blast+
next
  case (ExprListNilN Γ) thus ?case
    using normalize_expr_list.cases by auto
next
  case (ExprListConsN Γ x y xs ys) thus ?case by blast
qed

(*** Normalized Expressions Typing ******************************************)

section ‹Normalized Expressions Typing›

text ‹
  Here is the final typing rules.›

inductive nf_typing (‹(1_/ ⊢/ (_ :/ _))› [51,51,51] 50) where
  "Γ ⊢ expr ⇛ exprN ⟹
   Γ ⊢E exprN : τ ⟹
   Γ ⊢ expr : τ"

lemma nf_typing_det:
  "Γ ⊢ expr : τ ⟹
   Γ ⊢ expr : σ ⟹ τ = σ"
  by (metis nf_typing.cases normalize_det typing_det)

(*** Code Setup *************************************************************)

section ‹Code Setup›

code_pred normalize .

code_pred nf_typing .

definition "check_type Γ expr τ ≡
  Predicate.eval (nf_typing_i_i_i Γ expr τ) ()"

definition "synthesize_type Γ expr ≡
  Predicate.singleton (λ_. OclInvalid)
    (Predicate.map errorable (nf_typing_i_i_o Γ expr))"

text ‹
  It is the only usage of the @{text OclInvalid} type.
  This type is not required to define typing rules.
  It is only required to make the typing function total.›

end