Library Malfunction.Deserialize

Require Import Malfunction.Malfunction.

Require Import Ceres.Ceres.
Require Import String List.

Import ListNotations.

Local Open Scope list.
Local Open Scope string.

#[export] Instance Deserialize_Ident : Deserialize Ident.t :=
  fun l e
    match e with
    | Atom_ (Raw (String sym s)) ⇒ if String.eqb (String sym EmptyString) "$" then
                             inr (bytestring.String.of_string s) else inl (DeserError l "identifier needs to start with an $")
    | List _inl (DeserError l "could not read 'ident', got list")
    | _inl (DeserError l "could not read 'ident', got non-string atom")
    end.

#[export] Instance SemiIntegral_int : SemiIntegral int :=
  fun nSome (Int63.of_Z n).


Definition Deserialize_tag : Deserialize int :=
  Deser.match_con "tag" nil
                  [ ("tag", Deser.con1 (fun xx) _from_sexp ) ].

Notation con2c f := (Deser.con2 (fun x yf (x, y))).

#[export] Instance Deserialize_case : Deserialize case :=
  fun l c
    match c with
    | Atom_ "_" ⇒ inr Deftag
    | List [e1; e2]match from_sexp (A := int) e1, from_sexp (A := int) e2 with
                       | inr i1, inr i2inr (Intrange (i1, i2))
                       | inl e, _inl e
                      | _, inl einl e
                       end
    | _match from_sexp (A := int) c with inr iinr (Tag i) | inl einl e end
    end.

Definition int_of_nat n := Int63.of_Z (Stdlib.ZArith.BinInt.Z.of_nat n).

Definition Mif c e1 e2 := Mswitch (c, [ ([ Tag (int_of_nat 0) ], e1 ); ([Deftag ], e2) ]).
Definition splitlast {A} (a : A) (l : list A) : (list A × A) :=
  match rev l with
  | b :: l(a :: rev l, b)
  | _([], a)
  end.

Definition splitfirst (l : list binding) : (list binding × t) :=
  match rev l with
  | Unnamed b :: l(rev l, b)
  | _(nil, (Mvar ((bytestring.String.of_string "ERROR"))))
  end.


#[bypass_check(guard)]
Fixpoint ds (l : loc) (e : sexp) {struct e} : error + t :=
  match Deserialize_Ident l e with inr iinr (Mvar i) | _
    match _from_sexp (A := int) l e with inr iinr (Mnum (numconst_Int i)) | _
    Deser.match_con "t" nil
                    [
                      ("lambda", con2c Mlambda Deserialize_list ds);
                      ("apply", fun l f ematch _sexp_to_list ds [] 0 l e with
                                             | inr (x :: l) ⇒ inr (Mapply (x, l))
                                             | inr []inl (DeserError l (MsgStr "application without function"))
                                             | inl erinl er
                                             end);

                      ("let", fun l f ematch e with
                                           | x :: e
                                              let (e, last) := splitlast x e in
                                              match _sexp_to_list dsb [] 0 l e, ds l last with
                                              | inr bds, inr bodyinr (Mlet (bds, body))
                                              | inl er, _inl er
                                              | _, inl erinl er
                                              end
                                           | _inl (DeserError l (MsgStr "let without body"))
                                           end);
                                           
                      ("switch", con2c Mswitch ds (@Deserialize_list _ (@Deserialize_prod _ _ _ ds)));
                      ("if", Deser.con3 Mif ds ds ds);
                      ("lazy", Deser.con1 Mlazy ds);
                      ("force", Deser.con1 Mlazy ds);
                      ("block", con2c Mblock Deserialize_tag (@Deserialize_list t ds));
                      ("field", con2c Mfield _from_sexp ds);
                      ("==", Deser.con2 (fun x1 x2Mnumop2 (embed_binary_comparison Eq, embed_inttype Int, x1, x2)) ds ds)
                    ] l e
    end end
with dsb (l : loc) (e : sexp) {struct e} : error + binding :=
  match Deser.match_con "binding" nil
                    [
                      ("_", Deser.con1 Unnamed ds );
                      ("rec",
                      fun l f ematch _sexp_to_list (@Deserialize_prod _ _ _ ds) [] 0 l e with inr linr (Recursive l) | inl erinl er end)
                    ] l e
  with inr rinr r
  | _match e with
         | List [ id; x ]match Deserialize_Ident l id, ds l x with
                             | inr id, inr rinr (Named (id, r))
                             | _, _match ds l e with inr rinr (Unnamed r) | inl einl e end
                             end
         | _match ds l e with inr rinr (Unnamed r) | inl einl e end
         end
  end.

#[export] Instance Deserialize_t : Deserialize t := ds.
#[export] Instance Deserialize_binding : Deserialize binding := dsb.