https://gitlab.com/nomadic-labs/mi-cho-coq
Raw File
Tip revision: 92c0f63a21698d185ba9162d3728c6c45591e44a authored by Yann Regis-Gianas on 23 March 2021, 08:13:23 UTC
Micheline_lexer: Remove unused argument in range_aux
Tip revision: 92c0f63
micheline2michelson.v
Require Import Ascii String List.
Require Import List.
Import ListNotations.
Require Import untyped_syntax micheline_syntax error location.
Require Import syntax_type.
Require Import Lia.
Require Import trie.
Require micheline_lexer.
Require Coq.Program.Wf.
Import error.Notations.
Import untyped_syntax.notations.
Open Scope string.

Inductive string_literal :=
| S_ABS
| S_ADD
| S_ADDRESS
| S_AMOUNT
| S_AND
| S_APPLY
| S_ASSERT
| S_ASSERT_LEFT
| S_ASSERT_NONE
| S_ASSERT_RIGHT
| S_ASSERT_SOME
| S_BALANCE
| S_BLAKE2B
| S_CAR
| S_CAST
| S_CDR
| S_CHAIN_ID
| S_CHECK_SIGNATURE
| S_COMPARE
| S_CONCAT
| S_CONS
| S_CONTRACT
| S_CREATE_CONTRACT
| S_DIG
| S_DIP
| S_DROP
| S_DUG
| S_DUP
| S_EDIV
| S_EMPTY_BIG_MAP
| S_EMPTY_MAP
| S_EMPTY_SET
| S_EQ
| S_EXEC
| S_Elt
| S_FAIL
| S_FAILWITH
| S_False
| S_GE
| S_GET
| S_GT
| S_HASH_KEY
| S_IF
| S_IF_CONS
| S_IF_LEFT
| S_IF_NIL
| S_IF_NONE
| S_IF_RIGHT
| S_IF_SOME
| S_IMPLICIT_ACCOUNT
| S_INT
| S_ISNAT
| S_ITER
| S_LAMBDA
| S_LE
| S_LEFT
| S_LOOP
| S_LOOP_LEFT
| S_LSL
| S_LSR
| S_LT
| S_Left
| S_MAP
| S_MEM
| S_MUL
| S_NEG
| S_NEQ
| S_NIL
| S_NONE
| S_NOT
| S_NOW
| S_None
| S_OR
| S_PACK
| S_PAIR
| S_PUSH
| S_Pair
| S_RENAME
| S_RIGHT
| S_Right
| S_SELF
| S_SENDER
| S_SET_DELEGATE
| S_SHA256
| S_SHA512
| S_SIZE
| S_SLICE
| S_SOME
| S_SOURCE
| S_SUB
| S_SWAP
| S_Some
| S_TRANSFER_TOKENS
| S_True
| S_UNIT
| S_UNPACK
| S_UPDATE
| S_Unit
| S_XOR
| S_address
| S_big_map
| S_bool
| S_bytes
| S_chain_id
| S_code
| S_contract
| S_int
| S_key
| S_key_hash
| S_lambda
| S_list
| S_map
| S_mutez
| S_nat
| S_operation
| S_option
| S_or
| S_pair
| S_parameter
| S_set
| S_signature
| S_storage
| S_string
| S_timestamp
| S_unit
| S_ : String.string -> string_literal.

Definition string_literals :=
  [
    ("ABS", S_ABS);
  ("ADD", S_ADD);
  ("ADDRESS", S_ADDRESS);
  ("AMOUNT", S_AMOUNT);
  ("AND", S_AND);
  ("APPLY", S_APPLY);
  ("ASSERT", S_ASSERT);
  ("ASSERT_LEFT", S_ASSERT_LEFT);
  ("ASSERT_NONE", S_ASSERT_NONE);
  ("ASSERT_RIGHT", S_ASSERT_RIGHT);
  ("ASSERT_SOME", S_ASSERT_SOME);
  ("BALANCE", S_BALANCE);
  ("BLAKE2B", S_BLAKE2B);
  ("CAR", S_CAR);
  ("CAST", S_CAST);
  ("CDR", S_CDR);
  ("CHAIN_ID", S_CHAIN_ID);
  ("CHECK_SIGNATURE", S_CHECK_SIGNATURE);
  ("COMPARE", S_COMPARE);
  ("CONCAT", S_CONCAT);
  ("CONS", S_CONS);
  ("CONTRACT", S_CONTRACT);
  ("CREATE_CONTRACT", S_CREATE_CONTRACT);
  ("DIG", S_DIG);
  ("DIP", S_DIP);
  ("DROP", S_DROP);
  ("DUG", S_DUG);
  ("DUP", S_DUP);
  ("EDIV", S_EDIV);
  ("EMPTY_BIG_MAP", S_EMPTY_BIG_MAP);
  ("EMPTY_MAP", S_EMPTY_MAP);
  ("EMPTY_SET", S_EMPTY_SET);
  ("EQ", S_EQ);
  ("EXEC", S_EXEC);
  ("Elt", S_Elt);
  ("FAIL", S_FAIL);
  ("FAILWITH", S_FAILWITH);
  ("False", S_False);
  ("GE", S_GE);
  ("GET", S_GET);
  ("GT", S_GT);
  ("HASH_KEY", S_HASH_KEY);
  ("IF", S_IF);
  ("IF_CONS", S_IF_CONS);
  ("IF_LEFT", S_IF_LEFT);
  ("IF_NIL", S_IF_NIL);
  ("IF_NONE", S_IF_NONE);
  ("IF_RIGHT", S_IF_RIGHT);
  ("IF_SOME", S_IF_SOME);
  ("IMPLICIT_ACCOUNT", S_IMPLICIT_ACCOUNT);
  ("INT", S_INT);
  ("ISNAT", S_ISNAT);
  ("ITER", S_ITER);
  ("LAMBDA", S_LAMBDA);
  ("LE", S_LE);
  ("LEFT", S_LEFT);
  ("LOOP", S_LOOP);
  ("LOOP_LEFT", S_LOOP_LEFT);
  ("LSL", S_LSL);
  ("LSR", S_LSR);
  ("LT", S_LT);
  ("Left", S_Left);
  ("MAP", S_MAP);
  ("MEM", S_MEM);
  ("MUL", S_MUL);
  ("NEG", S_NEG);
  ("NEQ", S_NEQ);
  ("NIL", S_NIL);
  ("NONE", S_NONE);
  ("NOT", S_NOT);
  ("NOW", S_NOW);
  ("None", S_None);
  ("OR", S_OR);
  ("PACK", S_PACK);
  ("PAIR", S_PAIR);
  ("PUSH", S_PUSH);
  ("Pair", S_Pair);
  ("RENAME", S_RENAME);
  ("RIGHT", S_RIGHT);
  ("Right", S_Right);
  ("SELF", S_SELF);
  ("SENDER", S_SENDER);
  ("SET_DELEGATE", S_SET_DELEGATE);
  ("SHA256", S_SHA256);
  ("SHA512", S_SHA512);
  ("SIZE", S_SIZE);
  ("SLICE", S_SLICE);
  ("SOME", S_SOME);
  ("SOURCE", S_SOURCE);
  ("SUB", S_SUB);
  ("SWAP", S_SWAP);
  ("Some", S_Some);
  ("TRANSFER_TOKENS", S_TRANSFER_TOKENS);
  ("True", S_True);
  ("UNIT", S_UNIT);
  ("UNPACK", S_UNPACK);
  ("UPDATE", S_UPDATE);
  ("Unit", S_Unit);
  ("XOR", S_XOR);
  ("address", S_address);
  ("big_map", S_big_map);
  ("bool", S_bool);
  ("bytes", S_bytes);
  ("chain_id", S_chain_id);
  ("code", S_code);
  ("contract", S_contract);
  ("int", S_int);
  ("key", S_key);
  ("key_hash", S_key_hash);
  ("lambda", S_lambda);
  ("list", S_list);
  ("map", S_map);
  ("mutez", S_mutez);
  ("nat", S_nat);
  ("operation", S_operation);
  ("option", S_option);
  ("or", S_or);
  ("pair", S_pair);
  ("parameter", S_parameter);
  ("set", S_set);
  ("signature", S_signature);
  ("storage", S_storage);
  ("string", S_string);
  ("timestamp", S_timestamp);
  ("unit", S_unit)
  ].

Definition string_literal_dict_aux : String.string -> Datatypes.option string_literal :=
  Eval cbv in
    extract (dict string_literals 1000) I.

Definition make_string_literal (s : String.string) : string_literal :=
  match string_literal_dict_aux s with
  | Some l => l
  | None => S_ s
  end.

Definition loc_pmicheline := loc_gmicheline string_literal.

Definition to_pmicheline := map_gmicheline make_string_literal.

Definition micheline2michelson_sctype (bem : loc_pmicheline) : M simple_comparable_type :=
  let 'Mk_loc_micheline ((b, e), m) := bem in
  match m with
  | PRIM (_, S_string) _ nil => Return string
  | PRIM (_, S_nat) _ nil => Return nat
  | PRIM (_, S_int) _ nil => Return int
  | PRIM (_, S_bytes) _ nil => Return bytes
  | PRIM (_, S_bool) _ nil => Return bool
  | PRIM (_, S_mutez) _ nil => Return mutez
  | PRIM (_, S_key_hash) _ nil => Return key_hash
  | PRIM (_, S_timestamp) _ nil => Return timestamp
  | PRIM (_, S_address) _ nil => Return address
  | _ => Failed _ (Expansion b e)
  end.

Fixpoint micheline2michelson_ctype (bem : loc_pmicheline) : M comparable_type :=
  let 'Mk_loc_micheline ((b, e), m) := bem in
  match m with
  | PRIM (_, S_pair) _ (a :: b :: nil) =>
    let! a := micheline2michelson_sctype a in
    let! b := micheline2michelson_ctype b in
    Return (Cpair a b)
  | _ =>
    let! a := micheline2michelson_sctype bem in
    Return (Comparable_type_simple a)
  end.

Definition extract_one_field_annot_from_list annots : M annot_o :=
  match annots with
  | nil => Return None
  | Mk_annot (_, _, annot) :: nil => Return (Some annot)
  | _ :: Mk_annot (b, e, _) :: _ => Failed _ (Expansion b e)
  end.

Definition is_field_annot annot :=
  match annot with
  | Mk_annot (_, _, EmptyString) => false
  | Mk_annot (_, _, String c _) => micheline_lexer.eqb_ascii c "%"%char
  end.

Definition extract_one_field_annot (bem : loc_pmicheline) : M annot_o :=
  let 'Mk_loc_micheline ((b, e), m) := bem in
  match m with
  | PRIM p annots args =>
    let filtered_annots := List.filter is_field_annot annots in
    extract_one_field_annot_from_list filtered_annots
  | _ => Return None
  end.

Fixpoint micheline2michelson_type (bem : loc_pmicheline) : M type :=
  try (let! ty := micheline2michelson_sctype bem in Return (Comparable_type ty))
      (let 'Mk_loc_micheline ((b, e), m) := bem in
       match m with
       | PRIM (_, S_key) _ nil => Return key
       | PRIM (_, S_unit) _ nil => Return unit
       | PRIM (_, S_signature) _ nil => Return signature
       | PRIM (_, S_operation) _ nil => Return operation
       | PRIM (_, S_chain_id) _ nil => Return chain_id
       | PRIM (_, S_option) _ (a :: nil) =>
        let! a := micheline2michelson_type a in
        Return (option a)
       | PRIM (_, S_list) _ (a :: nil) =>
        let! a := micheline2michelson_type a in
        Return (list a)
       | PRIM (_, S_contract) _ (a :: nil) =>
        let! a := micheline2michelson_type a in
        Return (contract a)
       | PRIM (_, S_set) _ (a :: nil) =>
        let! a := micheline2michelson_ctype a in
        Return (set a)
       | PRIM (_, S_pair) _ (a :: b :: nil) =>
        let! a := micheline2michelson_type a in
        let! b := micheline2michelson_type b in
        Return (pair a b)
       | PRIM (_, S_or) _ (a :: b :: nil) =>
        let! an := extract_one_field_annot a in
        let! bn := extract_one_field_annot b in
        let! a := micheline2michelson_type a in
        let! b := micheline2michelson_type b in
        Return (or a an b bn)
       | PRIM (_, S_lambda) _ (a :: b :: nil) =>
        let! a := micheline2michelson_type a in
        let! b := micheline2michelson_type b in
        Return (lambda a b)
       | PRIM (_, S_map) _ (a :: b :: nil) =>
        let! a := micheline2michelson_ctype a in
        let! b := micheline2michelson_type b in
        Return (map a b)
       | PRIM (_, S_big_map) _ (a :: b :: nil) =>
        let! a := micheline2michelson_ctype a in
        let! b := micheline2michelson_type b in
        Return (big_map a b)
       | _ => Failed _ (Expansion b e)
       end).

Fixpoint micheline2michelson_data (bem : loc_pmicheline) : M concrete_data :=
  let 'Mk_loc_micheline ((b, e), m) := bem in
  match m with
  | SEQ l =>
    let! l :=
      (fix micheline2michelson_data_list (l : Datatypes.list loc_pmicheline) : M (Datatypes.list concrete_data) :=
        match l with
        | nil => Return nil
        | m :: l =>
          let! d := micheline2michelson_data m in
          let! l := micheline2michelson_data_list l in
          Return (d :: l)
        end
      ) l in
    Return (Concrete_seq l)
  | STR s => Return (String_constant s)
  | NUMBER z => Return (Int_constant z)
  | BYTES s => Return (Bytes_constant s)
  | PRIM (_, S_Unit) _ nil => Return Unit
  | PRIM (_, S_True) _ nil => Return True_
  | PRIM (_, S_False) _ nil => Return False_
  | PRIM (_, S_Pair) _ (a :: b :: nil) =>
    let! a := micheline2michelson_data a in
    let! b := micheline2michelson_data b in
    Return (Pair a b)
  | PRIM (_, S_Elt) _ (a :: b :: nil) =>
    let! a := micheline2michelson_data a in
    let! b := micheline2michelson_data b in
    Return (Elt a b)
  | PRIM (_, S_Left) _ (a :: nil) =>
    let! a := micheline2michelson_data a in
    Return (Left a)
  | PRIM (_, S_Right) _ (a :: nil) =>
    let! a := micheline2michelson_data a in
    Return (Right a)
  | PRIM (_, S_Some) _ (a :: nil) =>
    let! a := micheline2michelson_data a in
    Return (Some_ a)
  | PRIM (_, S_None) _ nil => Return None_
  | PRIM _ _ _ => Failed _ (Expansion b e)
  end.

Definition op_of_string (s : string_literal) b e :=
  match s with
  | S_EQ => Return EQ
  | S_NEQ => Return NEQ
  | S_LE => Return LE
  | S_GE => Return GE
  | S_LT => Return LT
  | S_GT => Return GT
  | _ => Failed _ (Expansion b e)
  end.

Definition FAIL := Instruction_seq {UNIT; FAILWITH}.
Definition ASSERT := Instruction_seq {IF_ IF_bool {} {FAIL}}.

Definition IF_op_of_string (s : String.string) b e bt bf :=
  match s with
  | String "I" (String "F" s) =>
    let s := make_string_literal s in
    let! op := op_of_string s b e in
    Return (op ;; IF_ IF_bool bt bf ;; NOOP)
  | _ => Failed _ (Expansion b e)
  end.

Definition ASSERT_op_of_string (s : String.string) b e :=
  match s with
  | String "A" (String "S" (String "S" (String "E" (String "R" (String "T" (String "_" s)))))) =>
    let s := make_string_literal s in
    let! op := op_of_string s b e in
    Return (op ;; ASSERT ;; NOOP)
  | _ => Failed _ (Expansion b e)
  end.

Definition ASSERT_NONE := Instruction_seq {IF_NONE {} {FAIL}}.
Definition ASSERT_SOME := Instruction_seq {IF_NONE {FAIL} {}}.
Definition ASSERT_LEFT := Instruction_seq {IF_LEFT {} {FAIL}}.
Definition ASSERT_RIGHT := Instruction_seq {IF_LEFT {FAIL} {}}.

Fixpoint DUP_Sn n : instruction_seq :=
  match n with
  | 0 => {DUP}
  | S n => {DIP 1 (DUP_Sn n); SWAP}
  end.

Definition IF_SOME bt bf := Instruction_seq {IF_NONE bf bt}.
Definition IF_RIGHT bt bf := Instruction_seq {IF_LEFT bf bt}.
Definition IF_NIL bt bf := Instruction_seq {IF_CONS bf bt}.

Inductive cadr : Set :=
| Cadr_CAR : cadr -> cadr
| Cadr_CDR : cadr -> cadr
| Cadr_nil : cadr.

Fixpoint micheline2michelson_cadr (x : cadr) : instruction_seq :=
  match x with
  | Cadr_CAR x => CAR ;; micheline2michelson_cadr x
  | Cadr_CDR x => CDR ;; micheline2michelson_cadr x
  | Cadr_nil => {}
  end.

Fixpoint micheline2michelson_set_cadr (x : cadr) : instruction_seq :=
  match x with
  | Cadr_CAR Cadr_nil =>
    {CDR; SWAP; PAIR}
  | Cadr_CDR Cadr_nil =>
    {CAR; PAIR}
  | Cadr_CAR x =>
    {DUP; DIP 1 (CAR;; micheline2michelson_set_cadr x); CDR; SWAP; PAIR}
  | Cadr_CDR x =>
    {DUP; DIP 1 (CDR;; micheline2michelson_set_cadr x); CAR; PAIR}
  | Cadr_nil => {} (* Should not happen *)
  end.

Fixpoint micheline2michelson_map_cadr (x : cadr) (code : instruction_seq) : instruction_seq :=
  match x with
  | Cadr_CAR Cadr_nil =>
    {DUP; CDR; DIP 1 (CAR;; code); SWAP; PAIR}
  | Cadr_CDR Cadr_nil =>
    {DUP; CDR} ;;; code ;;; {SWAP; CAR; PAIR}
  | Cadr_CAR x =>
    {DUP; DIP 1 (CAR;; micheline2michelson_map_cadr x code); CDR; SWAP; PAIR}
  | Cadr_CDR x =>
    {DUP; DIP 1 (CDR;; micheline2michelson_map_cadr x code); CAR; PAIR}
  | Cadr_nil => code (* Should not happen *)
  end.

(* PAPAIR stuff *)

Inductive direction := Left | Right.

Inductive papair_token := token_P | token_A | token_I.

Fixpoint lex_papair (s : String.string) (fail : exception) :
  M (Datatypes.list papair_token) :=
  match s with
  | String "P" s =>
    let! l := lex_papair s fail in
    Return (cons token_P l)
  | String "A" s =>
    let! l := lex_papair s fail in
    Return (cons token_A l)
  | String "I" s =>
    let! l := lex_papair s fail in
    Return (cons token_I l)
  | "R"%string => Return nil
  | _ => Failed _ fail
  end.

Inductive papair_d : direction -> Set :=
| Papair_d_P d : papair_d Left -> papair_d Right -> papair_d d
| Papair_d_A : papair_d Left
| Papair_d_I : papair_d Right.

Fixpoint parse_papair (d : direction) (s : Datatypes.list papair_token) (fail : exception)
         (fuel : Datatypes.nat) (Hs : List.length s < fuel) {struct fuel} :
  M (papair_d d * sig (fun r : Datatypes.list papair_token => List.length r < List.length s)).
Proof.
  destruct fuel.
  - destruct (PeanoNat.Nat.nlt_0_r _ Hs).
  - destruct s as [|c s].
    + exact (Failed _ fail).
    + refine (match c, d
              return M (papair_d d *
                        (sig (fun r : Datatypes.list papair_token => List.length r < S (List.length s))))
              with
              | token_P, d1 =>
                let! (l, exist _ s1 Hl) := parse_papair Left s fail fuel _ in
                let! (r, exist _ s2 Hr) := parse_papair Right s1 fail fuel _ in
                Return (Papair_d_P d l r, exist _ s2 _)
              | token_A, Left => Return (Papair_d_A, exist _ s _)
              | token_I, Right => Return (Papair_d_I, exist _ s _)
              | _, _ => Failed _ fail
              end); simpl in *; lia.
Defined.

Inductive papair_left : Set :=
| Papair_l_P : papair_left -> papair_right -> papair_left
| Papair_l_A : papair_left
with papair_right : Set :=
| Papair_r_P : papair_left -> papair_right -> papair_right
| Papair_r_I : papair_right.

Fixpoint papair_l_of_papair_d (x : papair_d Left) : papair_left :=
  match x in papair_d Left return papair_left with
  | Papair_d_A => Papair_l_A
  | Papair_d_P Left l r =>
    Papair_l_P (papair_l_of_papair_d l) (papair_r_of_papair_d r)
  end
with
papair_r_of_papair_d (x : papair_d Right) : papair_right :=
  match x in papair_d Right return papair_right with
  | Papair_d_I => Papair_r_I
  | Papair_d_P Right l r =>
    Papair_r_P (papair_l_of_papair_d l) (papair_r_of_papair_d r)
  end.

Fixpoint size_l (x : papair_left) :=
  match x with
  | Papair_l_A => 0
  | Papair_l_P l r => 1 + size_l l + size_r r
  end
with
size_r (y : papair_right) :=
  match y with
  | Papair_r_I => 0
  | Papair_r_P l r => 1 + size_l l + size_r r
  end.

Inductive papair : Set :=
| Papair_PAIR : papair
| Papair_A : papair -> papair
| Papair_I : papair -> papair
| Papair_P : papair -> papair -> papair.

Program Fixpoint papair_of_l_r (x : papair_left) (y : papair_right) {measure (size_l x + size_r y)}: papair :=
  match x, y with
  | Papair_l_A, Papair_r_I => Papair_PAIR
  | Papair_l_A, Papair_r_P l r =>
    Papair_A (papair_of_l_r l r)
  | Papair_l_P l r, Papair_r_I =>
    Papair_I (papair_of_l_r l r)
  | Papair_l_P xl xr, Papair_r_P yl yr =>
    Papair_P (papair_of_l_r xl xr) (papair_of_l_r yl yr)
  end.
Next Obligation.
  simpl.
  lia.
Defined.
Next Obligation.
  simpl.
  lia.
Defined.
Next Obligation.
  simpl.
  lia.
Defined.

Fixpoint micheline2michelson_papair (x : papair) : instruction_seq :=
  match x with
  | Papair_PAIR => {PAIR}
  | Papair_A y => {DIP 1 (micheline2michelson_papair y); PAIR}
  | Papair_I x => micheline2michelson_papair x ;;; {PAIR}
  | Papair_P x y => micheline2michelson_papair x ;;;
                    {DIP 1 (micheline2michelson_papair y); PAIR}
  end.

Fixpoint micheline2michelson_unpapair (x : papair) : instruction :=
  match x with
  | Papair_PAIR => UNPAIR
  | Papair_A y => Instruction_seq {UNPAIR; DIP 1 { micheline2michelson_unpapair y }}
  | Papair_I x => Instruction_seq {UNPAIR; micheline2michelson_unpapair x}
  | Papair_P x y => Instruction_seq {UNPAIR;
                    DIP 1 {micheline2michelson_unpapair y};
                    micheline2michelson_unpapair x}
  end.

Definition parse_papair_full (s : String.string) (fail : exception) :
  M instruction_seq :=
  let! toks : Datatypes.list papair_token := lex_papair s fail in
  let! (l, exist _ toks2 Htoks2) := parse_papair Left toks fail (S (List.length toks)) ltac:(simpl; lia) in
  let! (r, exist _ toks3 Htoks3) := parse_papair Right toks2 fail (S (List.length toks2)) ltac:(simpl; lia) in
  match toks3 with
  | nil => Return
                  (micheline2michelson_papair
                      (papair_of_l_r
                        (papair_l_of_papair_d l)
                        (papair_r_of_papair_d r)))
  | _ => Failed _ fail
  end.

Definition parse_unpapair_full (s : String.string) (fail : exception)
  : M instruction :=
  let! toks : Datatypes.list papair_token := lex_papair s fail in
  let! (l, exist _ toks2 Htoks2) := parse_papair Left toks fail (S (List.length toks)) ltac:(simpl; lia) in
  let! (r, exist _ toks3 Htoks3) := parse_papair Right toks2 fail (S (List.length toks2)) ltac:(simpl; lia) in
  match toks3 with
  | nil => Return
                  (micheline2michelson_unpapair
                      (papair_of_l_r
                        (papair_l_of_papair_d l)
                        (papair_r_of_papair_d r)))
  | _ => Failed _ fail
  end.


Definition return_instruction (i : instruction) : M instruction_seq :=
  Return {i}%michelson_untyped.

Definition return_opcode (op : opcode) : M instruction_seq :=
  return_instruction (instruction_opcode op).

Definition return_macro (i : instruction_seq) : M instruction_seq :=
  return_instruction (Instruction_seq i).

Fixpoint micheline2michelson_instruction (bem : loc_pmicheline) : M instruction_seq :=
  let 'Mk_loc_micheline ((b, e), m) := bem in
  match m with
  | SEQ l =>
    (fix micheline2michelson_instr_seq (l : Datatypes.list loc_pmicheline) : M instruction_seq :=
       match l with
       | nil => Return NOOP
       | i1 :: i2 =>
        let! i1 := micheline2michelson_instruction i1 in
        let! i2 := micheline2michelson_instr_seq i2 in
        Return (i1 ;;; i2)
       end) l
  | PRIM (_, S_FAILWITH) _ nil => return_instruction FAILWITH
  | PRIM (_, S_EXEC) _ nil => return_instruction EXEC
  | PRIM (_, S_APPLY) _ nil => return_opcode APPLY
  | PRIM (_, S_DROP) _ nil => return_opcode (DROP 1)
  | PRIM (_, S_DROP) _ (Mk_loc_micheline (_, NUMBER n) :: nil) =>
    return_opcode (DROP (BinInt.Z.to_nat n))
  | PRIM (_, S_DUP) _ nil => return_opcode DUP
  | PRIM (_, S_SWAP) _ nil => return_opcode SWAP
  | PRIM (_, S_UNIT) _ nil => return_opcode UNIT
  | PRIM (_, S_EQ) _ nil => return_opcode EQ
  | PRIM (_, S_NEQ) _ nil => return_opcode NEQ
  | PRIM (_, S_LT) _ nil => return_opcode LT
  | PRIM (_, S_GT) _ nil => return_opcode GT
  | PRIM (_, S_LE) _ nil => return_opcode LE
  | PRIM (_, S_GE) _ nil => return_opcode GE
  | PRIM (_, S_OR) _ nil => return_opcode OR
  | PRIM (_, S_AND) _ nil => return_opcode AND
  | PRIM (_, S_XOR) _ nil => return_opcode XOR
  | PRIM (_, S_NOT) _ nil => return_opcode NOT
  | PRIM (_, S_NEG) _ nil => return_opcode NEG
  | PRIM (_, S_ABS) _ nil => return_opcode ABS
  | PRIM (_, S_ISNAT) _ nil => return_opcode ISNAT
  | PRIM (_, S_INT) _ nil => return_opcode INT
  | PRIM (_, S_ADD) _ nil => return_opcode ADD
  | PRIM (_, S_SUB) _ nil => return_opcode SUB
  | PRIM (_, S_MUL) _ nil => return_opcode MUL
  | PRIM (_, S_EDIV) _ nil => return_opcode EDIV
  | PRIM (_, S_LSL) _ nil => return_opcode LSL
  | PRIM (_, S_LSR) _ nil => return_opcode LSR
  | PRIM (_, S_COMPARE) _ nil => return_opcode COMPARE
  | PRIM (_, S_CONCAT) _ nil => return_opcode CONCAT
  | PRIM (_, S_SIZE) _ nil => return_opcode SIZE
  | PRIM (_, S_SLICE) _ nil => return_opcode SLICE
  | PRIM (_, S_PAIR) _ nil => return_opcode PAIR
  | PRIM (_, S_CAR) _ nil => return_opcode CAR
  | PRIM (_, S_CDR) _ nil => return_opcode CDR
  | PRIM (_, S_GET) _ nil => return_opcode GET
  | PRIM (_, S_SOME) _ nil => return_opcode SOME
  | PRIM (_, S_NONE) _ (ty :: nil) =>
    let! ty := micheline2michelson_type ty in
    return_opcode (NONE ty)
  | PRIM (_, S_LEFT) _ (ty :: nil) =>
    let! ty := micheline2michelson_type ty in
    return_opcode (LEFT ty)
  | PRIM (_, S_RIGHT) _ (ty :: nil) =>
    let! ty := micheline2michelson_type ty in
    return_opcode (RIGHT ty)
  | PRIM (_, S_CONS) _ nil => return_opcode CONS
  | PRIM (_, S_NIL) _ (ty :: nil) =>
    let! ty := micheline2michelson_type ty in
    return_opcode (NIL ty)
  | PRIM (_, S_TRANSFER_TOKENS) _ nil =>
    return_opcode TRANSFER_TOKENS
  | PRIM (_, S_SET_DELEGATE) _ nil => return_opcode SET_DELEGATE
  | PRIM (_, S_BALANCE) _ nil => return_opcode BALANCE
  | PRIM (_, S_ADDRESS) _ nil => return_opcode ADDRESS
  | PRIM (_, S_CONTRACT) _ (ty :: nil) =>
    let! an := extract_one_field_annot bem in
    let! ty := micheline2michelson_type ty in
    return_opcode (CONTRACT an ty)
  | PRIM (_, S_SOURCE) _ nil => return_opcode SOURCE
  | PRIM (_, S_SENDER) _ nil => return_opcode SENDER
  | PRIM (_, S_SELF) _ nil =>
    let! an := extract_one_field_annot bem in
    return_instruction (SELF an)
  | PRIM (_, S_AMOUNT) _ nil => return_opcode AMOUNT
  | PRIM (_, S_IMPLICIT_ACCOUNT) _ nil => return_opcode IMPLICIT_ACCOUNT
  | PRIM (_, S_NOW) _ nil => return_opcode NOW
  | PRIM (_, S_PACK) _ nil => return_opcode PACK
  | PRIM (_, S_UNPACK) _ (ty :: nil) =>
    let! ty := micheline2michelson_type ty in
    return_opcode (UNPACK ty)
  | PRIM (_, S_HASH_KEY) _ nil => return_opcode HASH_KEY
  | PRIM (_, S_BLAKE2B) _ nil => return_opcode BLAKE2B
  | PRIM (_, S_SHA256) _ nil => return_opcode SHA256
  | PRIM (_, S_SHA512) _ nil => return_opcode SHA512
  | PRIM (_, S_CHECK_SIGNATURE) _ nil =>
    return_opcode CHECK_SIGNATURE
  | PRIM (_, S_MEM) _ nil => return_opcode MEM
  | PRIM (_, S_UPDATE) _ nil => return_opcode UPDATE
  | PRIM (_, S_CHAIN_ID) _ nil => return_opcode CHAIN_ID
  | PRIM (_, S_LOOP) _ (i :: nil) =>
    let! i := micheline2michelson_instruction i in
    return_instruction (LOOP i)
  | PRIM (_, S_LOOP_LEFT) _ (i :: nil) =>
    let! i := micheline2michelson_instruction i in
    return_instruction (LOOP_LEFT i)
  | PRIM (_, S_DIP) _ (i :: nil) =>
    let! i := micheline2michelson_instruction i in
    return_instruction (DIP 1 i)
  | PRIM (_, S_DIP) _ (Mk_loc_micheline (_, NUMBER n) :: i :: nil) =>
    let! i := micheline2michelson_instruction i in
    return_instruction (DIP (BinInt.Z.to_nat n) i)
  | PRIM (_, S_DIG) _ (Mk_loc_micheline (_, NUMBER n) :: nil) =>
    return_opcode (DIG (BinInt.Z.to_nat n))
  | PRIM (_, S_DUG) _ (Mk_loc_micheline (_, NUMBER n) :: nil) =>
    return_opcode (DUG (BinInt.Z.to_nat n))
  | PRIM (_, S_ITER) _ (i :: nil) =>
    let! i := micheline2michelson_instruction i in
    return_instruction (ITER i)
  | PRIM (_, S_MAP) _ (i :: nil) =>
    let! i := micheline2michelson_instruction i in
    return_instruction (MAP i)
  | PRIM (_, S_CREATE_CONTRACT) _
               (Mk_loc_micheline
                  (_, SEQ
                        ((Mk_loc_micheline (_, PRIM (_, S_storage) _ (storage_ty :: nil))) ::
                        (Mk_loc_micheline (_, PRIM (_, S_parameter) _ (params_ty :: nil)) as param) ::
                        (Mk_loc_micheline (_, PRIM (_, S_code) _ (i :: nil))) :: nil)) ::
                  nil) =>
    let! an := extract_one_field_annot param in
    let! i := micheline2michelson_instruction i in
    let! sty := micheline2michelson_type storage_ty in
    let! pty := micheline2michelson_type params_ty in
    return_instruction (CREATE_CONTRACT sty pty an i)
  | PRIM (_, S_CREATE_CONTRACT) _
               (Mk_loc_micheline
                  (_, SEQ
                        ((Mk_loc_micheline (_, PRIM (_, S_parameter) _ (params_ty :: nil)) as param) ::
                        (Mk_loc_micheline (_, PRIM (_, S_storage) _ (storage_ty :: nil))) ::
                        (Mk_loc_micheline (_, PRIM (_, S_code) _ (i :: nil))) :: nil)) ::
                  nil) =>
    let! an := extract_one_field_annot param in
    let! i := micheline2michelson_instruction i in
    let! sty := micheline2michelson_type storage_ty in
    let! pty := micheline2michelson_type params_ty in
    return_instruction (CREATE_CONTRACT sty pty an i)
  | PRIM (_, S_EMPTY_SET) _ (cty :: nil) =>
    let! cty := micheline2michelson_ctype cty in
    return_opcode (EMPTY_SET cty)
  | PRIM (_, S_EMPTY_MAP) _ (kty :: vty :: nil) =>
    let! kty := micheline2michelson_ctype kty in
    let! vty := micheline2michelson_type vty in
    return_opcode (EMPTY_MAP kty vty)
  | PRIM (_, S_EMPTY_BIG_MAP) _ (kty :: vty :: nil) =>
    let! kty := micheline2michelson_ctype kty in
    let! vty := micheline2michelson_type vty in
    return_opcode (EMPTY_BIG_MAP kty vty)
  | PRIM (_, S_IF) _ (i1 :: i2 :: nil) =>
    let! i1 := micheline2michelson_instruction i1 in
    let! i2 := micheline2michelson_instruction i2 in
    return_instruction (IF_ IF_bool i1 i2)
  | PRIM (_, S_IF_NONE) _ (i1 :: i2 :: nil) =>
    let! i1 := micheline2michelson_instruction i1 in
    let! i2 := micheline2michelson_instruction i2 in
    return_instruction (IF_NONE i1 i2)
  | PRIM (_, S_IF_LEFT) _ (i1 :: i2 :: nil) =>
    let! i1 := micheline2michelson_instruction i1 in
    let! i2 := micheline2michelson_instruction i2 in
    return_instruction (IF_LEFT i1 i2)
  | PRIM (_, S_IF_CONS) _ (i1 :: i2 :: nil) =>
    let! i1 := micheline2michelson_instruction i1 in
    let! i2 := micheline2michelson_instruction i2 in
    return_instruction (IF_CONS i1 i2)
  | PRIM (_, S_LAMBDA) _ (a :: b :: i :: nil) =>
    let! a := micheline2michelson_type a in
    let! b := micheline2michelson_type b in
    let! i := micheline2michelson_instruction i in
    return_instruction (LAMBDA a b i)
  | PRIM (_, S_PUSH) _ (a :: v :: nil) =>
    let! a := micheline2michelson_type a in
    let! v :=
      match a with
      | lambda _ _ =>
        let! i := micheline2michelson_instruction v in
        Return (Instruction i)
      | _ => micheline2michelson_data v
      end in
    return_instruction (PUSH a v)

  | PRIM (_, S_RENAME) _ _ => Return NOOP
  | PRIM (_, S_CAST) _ _ => Return NOOP


  (* Macros *)
  | PRIM (_, S_FAIL) _ nil => return_instruction FAIL
  | PRIM (_, S_ASSERT) _ nil => return_instruction ASSERT
  | PRIM (_, S_ASSERT_NONE) _ nil => return_instruction ASSERT_NONE
  | PRIM (_, S_ASSERT_SOME) _ nil => return_instruction ASSERT_SOME
  | PRIM (_, S_ASSERT_LEFT) _ nil => return_instruction ASSERT_LEFT
  | PRIM (_, S_ASSERT_RIGHT) _ nil => return_instruction ASSERT_RIGHT

  | PRIM (_, S_IF_SOME) _ (i1 :: i2 :: nil) =>
    let! i1 := micheline2michelson_instruction i1 in
    let! i2 := micheline2michelson_instruction i2 in
    return_instruction (IF_SOME i1 i2)
  | PRIM (_, S_IF_RIGHT) _ (i1 :: i2 :: nil) =>
    let! i1 := micheline2michelson_instruction i1 in
    let! i2 := micheline2michelson_instruction i2 in
    return_instruction (IF_RIGHT i1 i2)
  | PRIM (_, S_IF_NIL) _ (i1 :: i2 :: nil) =>
    let! i1 := micheline2michelson_instruction i1 in
    let! i2 := micheline2michelson_instruction i2 in
    return_instruction (IF_NIL i1 i2)

  | PRIM (_, S_ (String "C" (String "M" (String "P" s)))) _ nil =>
    let s := make_string_literal s in
    let! op := op_of_string s b e in
    return_macro {COMPARE; op}
  | PRIM (_,
       S_ (String "I" (String "F" (String "C" (String "M" (String "P" s)))))) _ (i1 :: i2 :: nil) =>
    let! i1 := micheline2michelson_instruction i1 in
    let! i2 := micheline2michelson_instruction i2 in
    let s := make_string_literal s in
    let! op := op_of_string s b e in
    return_macro {COMPARE; op; IF_ IF_bool i1 i2}
  | PRIM (_,
       S_ (String "I" (String "F" s))) _ (i1 :: i2 :: nil) =>
    let! i1 := micheline2michelson_instruction i1 in
    let! i2 := micheline2michelson_instruction i2 in
    let s := make_string_literal s in
    let! op := op_of_string s b e in
    return_macro {op; IF_ IF_bool i1 i2}
  | PRIM (_,
          S_ (String "A" (String "S" (String "S" (String "E" (String "R" (String "T"
              (String "_" (String "C" (String "M" (String "P" s))))))))))) _ nil =>
    let s := make_string_literal s in
    let! op := op_of_string s b e in
    return_macro {COMPARE; op; IF_ IF_bool {} {FAIL}}

  | PRIM (_, S_ (String "A" (String "S" (String "S" (String "E" (String "R" (String "T"
                                                                                    (String "_" s)))))))) _ nil =>
    let s := make_string_literal s in
    let! op := op_of_string s b e in
    return_macro {op; IF_ IF_bool {} {FAIL}}

  | PRIM (_, S_ "CR") _ nil =>
    Failed _ (Expansion_prim b e "CR")
  | PRIM (_, S_ "SET_CR") _ nil =>
    Failed _ (Expansion_prim b e "SET_CR")
  | PRIM (_, S_ "MAP_CR") _ nil =>
    Failed _ (Expansion_prim b e "MAP_CR")

  (* CADAAR *)
  | PRIM (_, S_ (String "C" s)) _ nil =>
    let prim := String "C" s in
    let get_cadr := fix get_cadr s :=
                      match s with
                      | "R" => Return Cadr_nil
                      | String "A" s =>
                        let! x := get_cadr s in
                        Return (Cadr_CAR x)
                      | String "D" s =>
                        let! x := get_cadr s in
                        Return (Cadr_CDR x)
                      | _ => Failed _ (Expansion_prim b e prim)
                      end in
    let! x := get_cadr s in
    return_macro (micheline2michelson_cadr x)

  | PRIM (_, S_ (String "S" (String "E"(String "T"(String "_"(String "C" s))))))
         _ nil =>
    let prim := String "S" (String "E"(String "T"(String "_"(String "C" s)))) in
    let get_cadr := fix get_cadr s :=
                      match s with
                      | "R" => Return Cadr_nil
                      | String "A" s =>
                        let! x := get_cadr s in
                        Return (Cadr_CAR x)
                      | String "D" s =>
                        let! x := get_cadr s in
                        Return (Cadr_CDR x)
                      | _ => Failed _ (Expansion_prim b e prim)
                      end in
    let! x := get_cadr s in
    return_macro (micheline2michelson_set_cadr x)

  | PRIM (_, S_ (String "M" (String "A"(String "P"(String "_"(String "C" s))))))
         _ (a :: nil) =>
    let prim := String "M" (String "A"(String "P"(String "_"(String "C" s)))) in
    let get_cadr := fix get_cadr s :=
                      match s with
                      | "R" => Return Cadr_nil
                      | String "A" s =>
                        let! x := get_cadr s in
                        Return (Cadr_CAR x)
                      | String "D" s =>
                        let! x := get_cadr s in
                        Return (Cadr_CDR x)
                      | _ => Failed _ (Expansion_prim b e prim)
                      end in
    let! x := get_cadr s in
    let! code := micheline2michelson_instruction a in
    return_macro (micheline2michelson_map_cadr x code)

  | PRIM (_, S_ (String "D" (String "I" s))) _ (a :: nil) =>
    let is_diip := fix is_diip s :=
                     match s with
                     | "P" => true
                     | String "I" s => is_diip s
                     | _ => false
                     end in
    if is_diip s then
      let! a := micheline2michelson_instruction a in
      return_instruction (DIP (String.length s) a)
    else Failed _ (Expansion_prim b e (String "D" (String "I" s)))
  | PRIM (_, S_DUP) _ (Mk_loc_micheline (_, NUMBER n) :: nil) =>
    match BinInt.Z.to_nat n with
    | S n => return_macro (DUP_Sn n)
    | O => Failed _ (Expansion b e)
    end
  | PRIM (_, S_ (String "D" (String "U" (String "U" s)))) _ nil =>
    let is_duup := fix is_duup s :=
                     match s with
                     | "P" => true
                     | String "U" s => is_duup s
                     | _ => false
                     end in
    if is_duup s then return_macro (DUP_Sn (String.length s))
    else Failed _ (Expansion_prim b e (String "D" (String "U" (String "U" s))))

  (* PAPAIR *)
  | PRIM (_, S_ (String "P" s)) _ nil =>
    let prim := String "P" s in
    let fail := Expansion_prim b e prim in
    let! full := parse_papair_full s fail in
    return_macro full

  (* UNPAPAIR *)
  | PRIM (_, S_ (String "U" (String "N" (String "P" s)))) _ nil =>
    let prim := String "U" (String "N" (String "P" s)) in
    let fail := Expansion_prim b e prim in
    let! full := parse_unpapair_full s fail in
    return_instruction full

  (* Unknown case *)
  | PRIM (_, S_ s) _ _ => Failed _ (Expansion_prim b e s)
  | _ => Failed _ (Expansion b e)
  end.

Record untyped_michelson_file :=
  Mk_untyped_michelson_file
    { root_annotation : annot_o;
      parameter : type;
      storage : type;
      code : instruction_seq }.

Record untyped_michelson_file_opt :=
  Mk_untyped_michelson_file_opt
    { root_annot : annot_o;
      parameter_opt : Datatypes.option type;
      storage_opt : Datatypes.option type;
      code_opt : Datatypes.option instruction_seq }.

Definition read_parameter (ty : type) (root_annot : annot_o)
           (f : untyped_michelson_file_opt) :=
  match f.(parameter_opt) with
  | None => Return {| root_annot := root_annot;
                      parameter_opt := Some ty;
                      storage_opt := f.(storage_opt);
                      code_opt := f.(code_opt) |}
  | Some _ => Failed _ Parsing
  end.

Definition read_storage (ty : type) (f : untyped_michelson_file_opt) :=
  match f.(storage_opt) with
  | None => Return {| root_annot := f.(root_annot);
                      parameter_opt := f.(parameter_opt);
                      storage_opt := Some ty;
                      code_opt := f.(code_opt) |}
  | Some _ => Failed _ Parsing
  end.

Definition read_code (c : instruction_seq) (f : untyped_michelson_file_opt) :=
  match f.(code_opt) with
  | None => Return {| root_annot := f.(root_annot);
                      parameter_opt := f.(parameter_opt);
                      storage_opt := f.(storage_opt);
                      code_opt := Some c |}
  | Some _ => Failed _ Parsing
  end.

Definition micheline2michelson_file (m : Datatypes.list loc_micheline) : M untyped_michelson_file :=
  let l :=
      match m with
      | Mk_loc_micheline (_, SEQ l) :: nil => l
      | l => l
      end
  in
  let! a :=
    error.list_fold_left
      (fun (a : untyped_michelson_file_opt) (lm : loc_micheline) =>
        let lm := to_pmicheline lm in
        let 'Mk_loc_micheline (_, _, m) := lm in
        match m with
        | PRIM (_, _, S_parameter) _ (cons param nil) =>
          let! an := extract_one_field_annot lm in
          let! ty := micheline2michelson_type param in
          read_parameter ty an a
        | PRIM (_, _, S_storage) _ (cons storage nil) =>
          let! ty := micheline2michelson_type storage in
          read_storage ty a
        | PRIM (_, _, S_code) _ (cons code nil) =>
          let! c := micheline2michelson_instruction code in
          read_code c a
        | _ => Failed _ Parsing
        end)
      l
      {| root_annot := None;
         parameter_opt := None;
         storage_opt := None;
         code_opt := None |} in
    match a.(parameter_opt), a.(storage_opt), a.(code_opt) with
    | Some param, Some storage, Some code =>
      Return {| root_annotation := a.(root_annot);
                parameter := param;
                storage := storage;
                code := code |}
    | _, _, _ => Failed _ Parsing
    end.
back to top