(*
 * Copyright 2018, Data61
 * Commonwealth Scientific and Industrial Research Organisation (CSIRO)
 * ABN 41 687 119 230.
 *
 * This software may be distributed and modified according to the terms of
 * the BSD 2-Clause license. Note that NO WARRANTY is provided.
 * See "LICENSE_BSD2.txt" for details.
 *
 * @TAG(DATA61_BSD)
 *)

theory Read_Table
imports
  "Cogent.Cogent"
  Specialised_Lemma_Utils
begin

(*
 * Read Cogent-C type mapping (".table" file) generated by cogent compiler.
 * The format is:
 *   Cogent_type :=: C_type
 * e.g.
 *   TRecord [(TPrim (Num U32), False)] Unboxed :=: t1
 * We parse each pair by reading Cogent_type as an Isabelle term.
 * C_type is a C identifier so parsing it is trivial.
 *)

ML \<open>
fun read_table (file_name:string) thy =
  let
    val path_to_c = (Resources.master_directory thy |> File.platform_path) ^ "/" ^ file_name;
    val path_to_table = (unsuffix ".c" path_to_c) ^ ".table";
    val input_file = TextIO.openIn path_to_table;

    val lines = split_lines (TextIO.inputAll input_file);
    val pos_lines = (1 upto length lines) ~~ lines;
    fun report pos = path_to_table ^ ":" ^ string_of_int pos ^ ": ";

    val tymap = pos_lines
                |> filter (fn (_, l) => not (String.isPrefix "--" l) andalso
                                        not (String.isPrefix " " l) andalso
                                        not (String.size l = 0))
                |> map (fn (pos, l) =>
                    case split_on " :=: " l of
                        [cogentT, cT] => (pos, cogentT, cT)
                      | _ => error (report pos ^ "expected \" :=: \""))
                : (int * string * string) list;

    val ctxt = Proof_Context.init_global thy
    val tymap = tymap
                |> map (fn (pos, cogentT, cT) => let
                    fun err () = error (report pos ^ "failed to parse Cogent type:" ^ cogentT)
                    val cogentT = Syntax.read_term ctxt cogentT
                                handle ERROR _ => err ()
                    val _ = if type_of cogentT = @{typ Cogent.type} then () else err ()
                    in (pos, cogentT, cT) end)
                : (int * term * string) list;

    fun decode_sigil _   ((Const (@{const_name Boxed}, _)) $ (Const (@{const_name Writable}, _)) $ _) = Writable
      | decode_sigil _   ((Const (@{const_name Boxed}, _)) $ (Const (@{const_name ReadOnly}, _)) $ _) = ReadOnly
      | decode_sigil _   (Const (@{const_name Unboxed},  _)) = Unboxed
      | decode_sigil pos t = raise TERM (report pos ^ "bad sigil", [t]);

    fun decode_type (_, Const (@{const_name TCon}, _) $ _ $ _ $ _, cT) =
            UAbstract cT
      | decode_type (pos, Const (@{const_name TRecord}, _) $ _ $ sigil, cT) =
            URecord (cT, decode_sigil pos sigil)
      | decode_type (_, Const (@{const_name TSum}, _) $ variants, cT) =
            USum (cT, variants)
      | decode_type (_, Const (@{const_name TProduct}, _) $ _ $ _, cT) =
            UProduct cT
      | decode_type (pos, t, _) =
            raise TERM (report pos ^ "unrecognised type", [t]);

    val uvals = map decode_type tymap |> rm_redundancy
   in
    uvals
   end : uval list;
\<close>
end
