Qcert.Backend.JSONtoData


Section JSONtoData.


  Context {fdata:foreign_data}.
  Fixpoint json_brands (d:list json) : option (list string) :=
    match d with
    | nil ⇒ Some nil
    | (jstring s1) :: d' ⇒
      match json_brands d' with
      | Some s' ⇒ Some (s1::s')
      | None ⇒ None
      end
    | _ ⇒ None
    end.

  Section toData.
    Context {ftojson:foreign_to_JSON}.


    Fixpoint json_to_data_pre (j:json) : data :=
      match foreign_to_JSON_to_data j with
      | Some fd ⇒ dforeign fd
      | None ⇒
        match j with
        | jnil ⇒ dunit
        | jnumber n ⇒ dnat n
        | jbool b ⇒ dbool b
        | jstring s ⇒ dstring s
        | jarray c ⇒ dcoll (map json_to_data_pre c)
        | jobject nil ⇒ drec nil
        | jobject ((s1,j')::nil) ⇒
          if (string_dec s1 "left") then dleft (json_to_data_pre j')
          else if (string_dec s1 "right") then dright (json_to_data_pre j')
               else drec ((s1, json_to_data_pre j')::nil)
        | jobject ((s1,jarray j1)::(s2,j2)::nil) ⇒
          if (string_dec s1 "type") then
            if (string_dec s2 "data") then
              match (json_brands j1) with
              | Some br ⇒ dbrand br (json_to_data_pre j2)
              | None ⇒ drec ((s1, dcoll (map json_to_data_pre j1))::(s2, json_to_data_pre j2)::nil)
              end
            else drec ((s1, dcoll (map json_to_data_pre j1))::(s2, json_to_data_pre j2)::nil)
          else drec ((s1, dcoll (map json_to_data_pre j1))::(s2, json_to_data_pre j2)::nil)
        | jobject ((s1,j1)::(s2,jarray j2)::nil) ⇒
          if (string_dec s1 "data") then
            if (string_dec s2 "type") then
              match (json_brands j2) with
              | Some br ⇒ dbrand br (json_to_data_pre j1)
              | None ⇒ drec ((s1, json_to_data_pre j1)::(s2, dcoll (map json_to_data_pre j2))::nil)
              end
            else drec ((s1, json_to_data_pre j1)::(s2, dcoll (map json_to_data_pre j2))::nil)
          else drec ((s1, json_to_data_pre j1)::(s2, dcoll (map json_to_data_pre j2))::nil)
        | jobject r ⇒ (drec (map (fun x ⇒ (fst x, json_to_data_pre (snd x))) r))
        | jforeign fd ⇒ dforeign fd
        end
      end.

    Definition json_to_data h (j:json) :=
      normalize_data h (json_to_data_pre j).


    Fixpoint json_enhanced_to_data_pre (j:json) :=
      match foreign_to_JSON_to_data j with
      | Some fd ⇒ dforeign fd
      | None ⇒
        match j with
        | jnil ⇒ dright dunit
        | jnumber n ⇒ dnat n
        | jbool b ⇒ dbool b
        | jstring s ⇒ dstring s
        | jarray c ⇒ dcoll (map json_enhanced_to_data_pre c)
        | jobject r ⇒
          let rfields := domain r in
          if (in_dec string_dec "key"%string rfields)
          then
            match edot r "key" with
            | Some (jstring key) ⇒ dleft (dstring key)
            | _ ⇒ drec (map (fun x ⇒ (fst x, json_enhanced_to_data_pre (snd x))) r)
            end
          else
            if (in_dec string_dec "$class"%string rfields)
            then
              match r with
              | (s1,jstring j1) :: rest ⇒
                if (string_dec s1 "$class") then
                  match (json_brands ((jstring j1)::nil)) with
                  | Some br ⇒ dbrand br (drec (map (fun x ⇒ (fst x, json_enhanced_to_data_pre (snd x))) rest))
                  | None ⇒ drec ((s1, dstring j1)::(map (fun x ⇒ (fst x, json_enhanced_to_data_pre (snd x))) rest))
                  end
                else drec (map (fun x ⇒ (fst x, json_enhanced_to_data_pre (snd x))) r)
              | _ ⇒
                drec (map (fun x ⇒ (fst x, json_enhanced_to_data_pre (snd x))) r)
              end
            else
              drec (map (fun x ⇒ (fst x, json_enhanced_to_data_pre (snd x))) r)
        | jforeign fd ⇒ dforeign fd
        end
      end.

    Definition json_enhanced_to_data h (j:json) :=
      normalize_data h (json_enhanced_to_data_pre j).

  End toData.

  Section toJSON.
    Context {ftojson:foreign_to_JSON}.

    Fixpoint data_enhanced_to_json (d:data) : json :=
      match d with
      | dunit ⇒ jnil
      | dnat n ⇒ jnumber n
      | dbool b ⇒ jbool b
      | dstring s ⇒ jstring s
      | dcoll c ⇒ jarray (map data_enhanced_to_json c)
      | drec r ⇒ jobject (map (fun x ⇒ (fst x, data_enhanced_to_json (snd x))) r)
      | dleft d' ⇒ jobject (("left"%string, data_enhanced_to_json d')::nil)
      | dright d' ⇒ jobject (("right"%string, data_enhanced_to_json d')::nil)
      | dbrand b (drec r) ⇒ jobject (("$class "%string, jarray (map jstring b))::(map (fun x ⇒ (fst x, data_enhanced_to_json (snd x))) r))
      | dbrand b d' ⇒ jobject (("$class"%string, jarray (map jstring b))::("$data"%string, (data_enhanced_to_json d'))::nil)
      | dforeign fd ⇒ foreign_to_JSON_from_data fd
      end.

    Fixpoint data_to_json (d:data) : json :=
      match d with
      | dunit ⇒ jnil
      | dnat n ⇒ jnumber n
      | dbool b ⇒ jbool b
      | dstring s ⇒ jstring s
      | dcoll c ⇒ jarray (map data_to_json c)
      | drec r ⇒ jobject (map (fun x ⇒ (fst x, data_to_json (snd x))) r)
      | dleft d' ⇒ jobject (("left"%string, data_to_json d')::nil)
      | dright d' ⇒ jobject (("right"%string, data_to_json d')::nil)
      | dbrand b d' ⇒ jobject (("type"%string, jarray (map jstring b))::("data"%string, (data_to_json d'))::nil)
      | dforeign fd ⇒ foreign_to_JSON_from_data fd
      end.
  End toJSON.

  Section toJavascript.

    Fixpoint data_enhanced_to_js (quotel:string) (d:data) : json :=
      match d with
      | dunit ⇒ jnil
      | dnat n ⇒ jnumber n
      | dbool b ⇒ jbool b
      | dstring s ⇒ jstring s
      | dcoll c ⇒ jarray (map (data_enhanced_to_js quotel) c)
      | drec r ⇒ jobject (map (fun x ⇒ (fst x, (data_enhanced_to_js quotel) (snd x))) r)
      | dleft d' ⇒ jobject (("left"%string, data_enhanced_to_js quotel d')::nil)
      | dright d' ⇒ jobject (("right"%string, data_enhanced_to_js quotel d')::nil)
      | dbrand b (drec r) ⇒ jobject (("$class "%string, jarray (map jstring b))::(map (fun x ⇒ (fst x, data_enhanced_to_js quotel (snd x))) r))
      | dbrand b d' ⇒ jobject (("$class"%string, jarray (map jstring b))::("$data"%string, (data_enhanced_to_js quotel d'))::nil)
      | dforeign fd ⇒ jforeign fd
      end.

    Fixpoint data_to_js (quotel:string) (d:data) : json :=
      match d with
      | dunit ⇒ jnil
      | dnat n ⇒ jnumber n
      | dbool b ⇒ jbool b
      | dstring s ⇒ jstring s
      | dcoll c ⇒ jarray (map (data_to_js quotel) c)
      | drec r ⇒ jobject (map (fun x ⇒ (fst x, (data_to_js quotel) (snd x))) r)
      | dleft d' ⇒ jobject (("left"%string, data_to_js quotel d')::nil)
      | dright d' ⇒ jobject (("right"%string, data_to_js quotel d')::nil)
      | dbrand b d' ⇒ jobject (("type"%string, jarray (map jstring b))::("data"%string, (data_to_js quotel d'))::nil)
      | dforeign fd ⇒ jforeign fd
      end.

  End toJavascript.

  Section toRType.
    Context {ftype:foreign_type}.
    Context {ftypeToJSON:foreign_type_to_JSON}.

    Fixpoint json_to_rtype₀ (j:json) : rtype₀ :=
      match j with
      | jnil ⇒ Unit₀
      | jnumber _ ⇒ Unit₀
      | jbool _ ⇒ Unit₀
      | jarray _ ⇒ Unit₀
      | jstring "String" ⇒ String₀
      | jstring "Nat" ⇒ Nat₀
      | jstring "Bool" ⇒ Bool₀
      | jstring _ ⇒ Unit₀
      | jobject nil ⇒ Rec₀ Open nil
      | jobject (("$brand"%string,jarray jl)::nil) ⇒
        match json_brands jl with
        | Some brs ⇒ Brand₀ brs
        | None ⇒ Unit₀
        end
      | jobject (("$coll"%string,j')::nil) ⇒ Coll₀ (json_to_rtype₀ j')
      | jobject (("$option"%string,j')::nil) ⇒ Either₀ (json_to_rtype₀ j') Unit₀
      | jobject jl ⇒ Rec₀ Open (map (fun kj ⇒ ((fst kj), (json_to_rtype₀ (snd kj)))) jl)
      | jforeign _ ⇒ Unit₀
      end.

    Definition json_to_rtype {br:brand_relation} (j:json) :=
      normalize_rtype₀_to_rtype (json_to_rtype₀ j).

    Fixpoint json_to_rtype₀_with_fail (j:json) : option rtype₀ :=
      match j with
      | jnil ⇒ Some Unit₀
      | jnumber _ ⇒ None
      | jbool _ ⇒ None
      | jarray _ ⇒ None
      | jstring "String" ⇒ Some String₀
      | jstring "Nat" ⇒ Some Nat₀
      | jstring "Bool" ⇒ Some Bool₀
      | jstring s ⇒ lift Foreign₀ (foreign_to_string_to_type s)
      | jobject nil ⇒ Some (Rec₀ Open nil)
      | jobject (("$brand"%string,jarray jl)::nil) ⇒
        match json_brands jl with
        | Some brs ⇒ Some (Brand₀ brs)
        | None ⇒ None
        end
      | jobject (("$coll"%string,j')::nil) ⇒ lift Coll₀ (json_to_rtype₀_with_fail j')
      | jobject (("$option"%string,j')::nil) ⇒ lift (fun x ⇒ Either₀ x Unit₀) (json_to_rtype₀_with_fail j')
      | jobject jl ⇒
        lift (fun x ⇒ Rec₀ Open x)
             ((fix rmap_rec (l: list (string × json)) : option (list (string × rtype₀)) :=
                 match l with
                 | nil ⇒ Some nil
                 | (x,y)::l' ⇒
                   match rmap_rec l' with
                   | None ⇒ None
                   | Some l'' ⇒
                     match json_to_rtype₀_with_fail y with
                     | None ⇒ None
                     | Some y'' ⇒ Some ((x,y'') :: l'')
                     end
                   end
                 end) jl)
      | jforeign _ ⇒ None
      end.

    Definition json_to_rtype_with_fail {br:brand_relation} (j:json) : option rtype :=
      lift normalize_rtype₀_to_rtype (json_to_rtype₀_with_fail j).

  End toRType.

  Section toDType.
    Context {ftype:foreign_type}.
    Context {ftypeToJSON:foreign_type_to_JSON}.

    Definition json_to_drtype {br:brand_relation} (j:json) : drtype :=
      match j with
      | jobject (("$dist"%string,j')::nil) ⇒ Tdistr (json_to_rtype j')
      | _ ⇒ Tlocal (json_to_rtype j)
      end.

    Definition json_to_drtype_with_fail {br:brand_relation} (j:json) : option drtype :=
      match j with
      | jobject (("$dist"%string,j')::nil) ⇒ lift Tdistr (json_to_rtype_with_fail j')
      | _ ⇒ lift Tlocal (json_to_rtype_with_fail j)
      end.

  End toDType.

  Section toVType.
    Context {ftype:foreign_type}.
    Context {ftypeToJSON:foreign_type_to_JSON}.

    Definition json_to_vrtype_with_fail {br:brand_relation} (j:json) : option (string × rtype) :=
      match j with
      | jobject (("dist"%string,jstring s)::("type"%string,j')::nil) ⇒ Some (s,json_to_rtype j')
      | jobject (("type"%string,j')::("dist"%string,jstring s)::nil) ⇒ Some (s,json_to_rtype j')
      | _ ⇒ None
      end.

  End toVType.

  Section RoundTripping.
    Inductive json_data : data → Prop :=
    | json_null : json_data dunit
    | json_nat n : json_data (dnat n)
    | json_bool b : json_data (dbool b)
    | json_string s : json_data (dstring s)
    | json_array dl : Forall (fun d ⇒ json_data d) dl → json_data (dcoll dl)
    | json_rec r :
        is_list_sorted ODT_lt_dec (domain r) = true →
        Forall (fun ab ⇒ json_data (snd ab)) r →
        json_data (drec r)
    .

    Inductive pure_data : data → Prop :=
    | pure_null : pure_data dunit
    | pure_nat n : pure_data (dnat n)
    | pure_bool b : pure_data (dbool b)
    | pure_string s : pure_data (dstring s)
    | pure_array dl : Forall (fun d ⇒ pure_data d) dl → pure_data (dcoll dl)
    | pure_rec r :
        assoc_lookupr string_dec r "$left"%string = None →
        assoc_lookupr string_dec r "$right"%string = None →
        assoc_lookupr string_dec r "$class"%string = None →
        is_list_sorted ODT_lt_dec (domain r) = true →
        Forall (fun ab ⇒ pure_data (snd ab)) r →
        pure_data (drec r)
    | pure_left d :
        pure_data d → pure_data (dleft d)
    | pure_right d :
        pure_data d → pure_data (dright d)
    | pure_brand b r :
        pure_data (drec r) → pure_data (dbrand b (drec r))
    .

    Lemma pure_dcoll_inv c:
      Forall (fun d : data ⇒ pure_data d) c ↔ pure_data (dcoll c).

    Lemma no_assoc_with_map (r:list (string×data)) (f:data→data) s:
      assoc_lookupr string_dec r s = None →
      assoc_lookupr string_dec (map (fun x ⇒ (fst x, f (snd x))) r) s = None.

    Lemma domains_with_map (r:list (string×data)) (f:data→data):
      domain (map (fun x : string × data ⇒ (fst x, f (snd x))) r) = domain r.

    Lemma assoc_lookupr_skip {A} (a:string×A) (l:list (string×A)) (s:string):
      assoc_lookupr string_dec (a::l) s = None →
      assoc_lookupr string_dec l s = None.

    Lemma pure_drec_cons_inv a r:
      pure_data (drec (a::r)) → (pure_data (drec r) ∧ pure_data (snd a)).
  End RoundTripping.

End JSONtoData.