aboutsummaryrefslogtreecommitdiffstats
From Coq Require Import Ascii.
From mininix Require Export res nix.operational_props.
From stdpp Require Import options.

Section val.
  Local Unset Elimination Schemes.
  Inductive val :=
    | VLit (bl : base_lit) (Hbl : base_lit_ok bl)
    | VClo (x : string) (E : gmap string (kind * thunk)) (e : expr)
    | VCloMatch (E : gmap string (kind * thunk))
                (ms : gmap string (option expr))
                (strict : bool) (e : expr)
    | VList (ts : list thunk)
    | VAttr (ts : gmap string thunk)
  with thunk :=
    | Forced (v : val) : thunk
    | Thunk (E : gmap string (kind * thunk)) (e : expr) : thunk
    | Indirect (x : string)
               (E : gmap string (kind * thunk))
               (tαs : gmap string (expr + thunk)).
End val.

Notation VLitI bl := (VLit bl I).

Notation tattr := (expr + thunk)%type.
Notation env := (gmap string (kind * thunk)).

Definition maybe_VLit (v : val) : option base_lit :=
  if v is VLit bl _ then Some bl else None.
Global Instance maybe_VList : Maybe VList := λ v,
  if v is VList ts then Some ts else None.
Global Instance maybe_VAttr : Maybe VAttr := λ v,
  if v is VAttr ts then Some ts else None.

Fixpoint interp_eq_list_body (i : nat) (ts1 ts2 : list thunk) : expr :=
  match ts1, ts2 with
  | [], [] => ELit (LitBool true)
  | _ :: ts1, _ :: ts2 =>
      EIf (EBinOp EqOp (EId' ("1" +:+ pretty i)) (EId' ("2" +:+ pretty i)))
          (interp_eq_list_body (S i) ts1 ts2) (ELit (LitBool false))
  | _, _ => ELit (LitBool false)
  end.

Definition interp_eq_list (ts1 ts2 : list thunk) : thunk :=
  Thunk (kmap (λ n : nat, String "1" (pretty n)) ((ABS,.) <$> map_seq 0 ts1) ∪
         kmap (λ n : nat, String "2" (pretty n)) ((ABS,.) <$> map_seq 0 ts2)) $
    interp_eq_list_body 0 ts1 ts2.

Fixpoint interp_lt_list_body (i : nat) (ts1 ts2 : list thunk) : expr :=
  match ts1, ts2 with
  | [], _ => ELit (LitBool true)
  | _ :: ts1, _ :: ts2 =>
      EIf (EBinOp LtOp (EId' ("1" +:+ pretty i)) (EId' ("2" +:+ pretty i)))
          (ELit (LitBool true))
          (EIf (EBinOp EqOp (EId' ("1" +:+ pretty i)) (EId' ("2" +:+ pretty i)))
            (interp_lt_list_body (S i) ts1 ts2) (ELit (LitBool false)))
  | _ :: _, [] => ELit (LitBool false)
  end.

Definition interp_lt_list (ts1 ts2 : list thunk) : thunk :=
  Thunk (kmap (λ n : nat, String "1" (pretty n)) ((ABS,.) <$> map_seq 0 ts1) ∪
         kmap (λ n : nat, String "2" (pretty n)) ((ABS,.) <$> map_seq 0 ts2)) $
    interp_lt_list_body 0 ts1 ts2.

Definition interp_eq_attr (ts1 ts2 : gmap string thunk) : thunk :=
  Thunk (kmap (String "1") ((ABS,.) <$> ts1) ∪
         kmap (String "2") ((ABS,.) <$> ts2)) $
    sem_and_attr $ map_imap (λ x _,
      Some (EBinOp EqOp (EId' ("1" +:+ x)) (EId' ("2" +:+ x)))) ts1.

Definition interp_eq (v1 v2 : val) : option thunk :=
  match v1, v2 with
  | VLit bl1 _, VLit bl2 _ =>
      Some $ Forced $ VLitI (LitBool $ sem_eq_base_lit bl1 bl2)
  | VClo _ _ _, VClo _ _ _ => None
  | VList ts1, VList ts2 => Some $
      if decide (length ts1 = length ts2) then interp_eq_list ts1 ts2
      else Forced $ VLitI (LitBool false)
  | VAttr ts1, VAttr ts2 => Some $
      if decide (dom ts1 = dom ts2) then interp_eq_attr ts1 ts2
      else Forced $ VLitI (LitBool false)
  | _, _ => Some $ Forced $ VLitI (LitBool false)
  end.

Definition type_of_val (v : val) : string :=
  match v with
  | VLit bl _ => type_of_base_lit bl
  | VClo _ _ _ | VCloMatch _ _ _ _ => "lambda"
  | VList _ => "list"
  | VAttr _ => "set"
  end.

Global Instance val_inhabited : Inhabited val := populate (VLitI inhabitant).
Global Instance thunk_inhabited : Inhabited thunk := populate (Forced inhabitant).

Definition interp_bin_op (op : bin_op) (v1 : val) : option (val → option thunk) :=
  if decide (op = EqOp) then
    Some (interp_eq v1)
  else if decide (op = TypeOfOp) then
    Some $ λ v2,
      guard (maybe_VLit v2 = Some LitNull);;
      Some $ Forced $ VLitI (LitString $ type_of_val v1)
  else
    match v1 with
    | VLit (LitNum n1) Hn1 =>
        if maybe RoundOp op is Some m then
          Some $ λ v2,
            guard (maybe_VLit v2 = Some LitNull);;
            Some $ Forced $ VLit
              (LitNum $ NInt $ float_to_bounded_Z $ Float.round m $ num_to_float n1)
              (float_to_bounded_Z_ok _)
        else
          '(f ↾ Hf) ← option_to_eq_Some (sem_bin_op_num op n1);
          Some $ λ v2,
            if v2 is VLit (LitNum n2) Hn2 then
              '(bl ↾ Hbl) ← option_to_eq_Some (f n2);
              Some $ Forced $ VLit bl (sem_bin_op_num_ok Hn1 Hn2 Hf Hbl)
            else None
    | VLit (LitString s1) _ =>
        match op with
        | SingletonAttrOp => Some $ λ v2,
            guard (maybe_VLit v2 = Some LitNull);;
            Some $ Forced $ VClo "t" ∅ (EAttr {[ s1 := AttrN (EId' "t") ]})
        | MatchStringOp => Some $ λ v2,
            guard (maybe_VLit v2 = Some LitNull);;
            match s1 with
            | EmptyString => Some $ Forced $ VAttr {[
                "empty" := Forced (VLitI (LitBool true));
                "head" := Forced (VLitI LitNull);
                "tail" := Forced (VLitI LitNull) ]}
            | String a s1 => Some $ Forced $ VAttr {[
                "empty" := Forced (VLitI (LitBool false));
                "head" := Forced (VLitI (LitString (String a EmptyString)));
                "tail" := Forced (VLitI (LitString s1)) ]}
            end
        | _ =>
            '(f ↾ Hf) ← option_to_eq_Some (sem_bin_op_string op);
            Some $ λ v2,
              bl2 ← maybe_VLit v2;
              s2 ← maybe LitString bl2;
              Some $ Forced $ VLit (f s1 s2) (sem_bin_op_string_ok Hf)
        end
    | VClo _ _ _ =>
       match op with
       | FunctionArgsOp => Some $ λ v2,
            guard (maybe_VLit v2 = Some LitNull);;
            Some (Forced (VAttr ∅))
        | _ => None
        end
    | VCloMatch _ ms _ _ =>
        match op with
        | FunctionArgsOp => Some $ λ v2,
            guard (maybe_VLit v2 = Some LitNull);;
            Some $ Forced $ VAttr $
              (λ m, Forced $ VLitI (LitBool (from_option (λ _, true) false m))) <$> ms
        | _ => None
        end
    | VList ts1 =>
        match op with
        | LtOp => Some $ λ v2,
            ts2 ← maybe VList v2;
            Some (interp_lt_list ts1 ts2)
        | MatchListOp => Some $ λ v2,
            guard (maybe_VLit v2 = Some LitNull);;
            match ts1 with
            | [] => Some $ Forced $ VAttr {[
                "empty" := Forced (VLitI (LitBool true));
                "head" := Forced (VLitI LitNull);
                "tail" := Forced (VLitI LitNull) ]}
            | t :: ts1 => Some $ Forced $ VAttr {[
                "empty" := Forced (VLitI (LitBool false));
                "head" := t;
                "tail" := Forced (VList ts1) ]}
            end
        | AppendListOp => Some $ λ v2,
            ts2 ← maybe VList v2;
            Some (Forced (VList (ts1 ++ ts2)))
        | _ => None
        end
    | VAttr ts1 =>
        match op with
        | SelectAttrOp => Some $ λ v2,
            bl ← maybe_VLit v2;
            x ← maybe LitString bl;
            ts1 !! x
        | UpdateAttrOp => Some $ λ v2,
            ts2 ← maybe VAttr v2;
            Some $ Forced $ VAttr $ ts2 ∪ ts1
        | HasAttrOp => Some $ λ v2,
            bl ← maybe_VLit v2;
            x ← maybe LitString bl;
            Some $ Forced $ VLitI (LitBool $ bool_decide (is_Some (ts1 !! x)))
        | DeleteAttrOp => Some $ λ v2,
            bl ← maybe_VLit v2;
            x ← maybe LitString bl;
            Some $ Forced $ VAttr $ delete x ts1
        | MatchAttrOp => Some $ λ v2,
            guard (maybe_VLit v2 = Some LitNull);;
            match map_minimal_key attr_le ts1 with
            | None => Some $ Forced $ VAttr {[
                "empty" := Forced (VLitI (LitBool true));
                "key" := Forced (VLitI LitNull);
                "head" := Forced (VLitI LitNull);
                "tail" := Forced (VLitI LitNull) ]}
            | Some x => Some $ Forced $ VAttr {[
                "empty" := Forced (VLitI (LitBool false));
                "key" := Forced (VLitI (LitString x));
                "head" := ts1 !!! x;
                "tail" := Forced (VAttr (delete x ts1)) ]}
            end
        | _ => None
        end
    | _ => None
    end.

Definition interp_match
    (ts : gmap string thunk) (mds : gmap string (option expr))
    (strict : bool) : option (gmap string tattr) :=
  map_mapM id $ merge (λ mt mmd,
    (* Some (Some _) means keep, Some None means fail, None means skip *)
    match mt, mmd with
    | Some t, Some _ => Some $ Some (inr t)
    | None, Some (Some e) => Some $ Some (inl e)
    | None, Some _ => Some None (* bad *)
    | Some _, None => guard strict;; Some None
    | _, _ => None (* skip *)
    end) ts mds.

Definition force_deep1
    (force_deep : val → res val)
    (interp_thunk : thunk → res val) (v : val) : res val :=
  match v with
  | VList ts => VList ∘ fmap Forced <$>
      mapM (mbind force_deep ∘ interp_thunk) ts
  | VAttr ts => VAttr ∘ fmap Forced <$>
      map_mapM_sorted attr_le (mbind force_deep ∘ interp_thunk) ts
  | _ => mret v
  end.

Definition indirects_env (E : env) (tαs : gmap string tattr) : env :=
  map_imap (λ y _, Some (ABS, Indirect y E tαs)) tαs ∪ E.

Definition attr_to_tattr (E : env) (α : attr) : tattr :=
  from_attr inl (inr ∘ Thunk E) α.

Definition interp1
    (force_deep : val → res val)
    (interp : env → expr → res val)
    (interp_thunk : thunk → res val)
    (interp_app : val → thunk → res val)
    (E : env) (e : expr) : res val :=
  match e with
  | ELit bl =>
      bl_ok ← guard (base_lit_ok bl);
      mret (VLit bl bl_ok)
  | EId x mke =>
      '(_,t) ← Res $ union_kinded (E !! x) (prod_map id (Thunk ∅) <$> mke);
      interp_thunk t
  | EAbs x e => mret (VClo x E e)
  | EAbsMatch ms strict e => mret (VCloMatch E ms strict e)
  | EApp e1 e2 =>
      v1 ← interp E e1;
      interp_app v1 (Thunk E e2)
  | ESeq μ' e1 e2 =>
      v ← interp E e1;
      (if μ' is DEEP then force_deep else mret) v;;
      interp E e2
  | EList es => mret (VList (Thunk E <$> es))
  | EAttr αs =>
      let E' := indirects_env E (attr_to_tattr E <$> αs) in
      mret (VAttr (from_attr (Thunk E') (Thunk E) <$> αs))
  | ELetAttr k e1 e2 =>
      v1 ← interp E e1;
      ts ← Res (maybe VAttr v1);
      interp (union_kinded ((k,.) <$> ts) E) e2
  | EBinOp op e1 e2 =>
      v1 ← interp E e1;
      f ← Res (interp_bin_op op v1);
      v2 ← interp E e2;
      t2 ← Res (f v2);
      interp_thunk t2
  | EIf e1 e2 e3 =>
      v1 ← interp E e1;
      '(b : bool) ← Res (maybe_VLit v1 ≫= maybe LitBool);
      interp E (if b then e2 else e3)
  end.

Definition interp_thunk1
    (interp : env → expr → res val)
    (interp_thunk : thunk → res val)
    (t : thunk) : res val :=
  match t with
  | Forced v => mret v
  | Thunk E e => interp E e
  | Indirect x E tαs =>
      tα ← Res $ tαs !! x;
      match tα with
      | inl e => interp (indirects_env E tαs) e
      | inr t => interp_thunk t
      end
  end.

Definition interp_app1
    (interp : env → expr → res val)
    (interp_thunk : thunk → res val)
    (interp_app : val → thunk → res val)
    (v1 : val) (t2 : thunk) : res val :=
  match v1 with
  | VClo x E e =>
      interp (<[x:=(ABS, t2)]> E) e
  | VCloMatch E ms strict e =>
      vt ← interp_thunk t2;
      ts ← Res (maybe VAttr vt);
      tαs ← Res $ interp_match ts ms strict;
      interp (indirects_env E tαs) e
  | VAttr ts =>
      t ← Res (ts !! "__functor");
      vt ← interp_thunk t;
      v ← interp_app vt (Forced v1);
      interp_app v t2
  | _ => mfail
  end.

Fixpoint force_deep (n : nat) (v : val) : res val :=
  match n with
  | O => NoFuel
  | S n => force_deep1 (force_deep n) (interp_thunk n) v
  end
with interp (n : nat) (E : env) (e : expr) : res val :=
  match n with
  | O => NoFuel
  | S n => interp1 (force_deep n) (interp n) (interp_thunk n) (interp_app n) E e
  end
with interp_thunk (n : nat) (t : thunk) : res val :=
  match n with
  | O => NoFuel
  | S n => interp_thunk1 (interp n) (interp_thunk n) t
  end
with interp_app (n : nat) (v1 : val) (t2 : thunk) : res val :=
  match n with
  | O => NoFuel
  | S n => interp_app1 (interp n) (interp_thunk n) (interp_app n) v1 t2
  end.

Definition force_deep' (n : nat) (μ : mode) : val → res val :=
  match μ with SHALLOW => mret | DEEP => force_deep n end.

Definition interp' (n : nat) (μ : mode) (E : env) (e : expr) : res val :=
  interp n E e ≫= force_deep' n μ.

Global Opaque force_deep interp interp_thunk interp_app.