CookBook/chunks.ML
changeset 189 069d525f8f1d
parent 188 8939b8fd8603
child 190 ca0ac2e75f6d
equal deleted inserted replaced
188:8939b8fd8603 189:069d525f8f1d
     1 
       
     2 structure Chunks =
       
     3 struct
       
     4 
       
     5 (* rebuilding the output function from ThyOutput in order to *)
       
     6 (* enable the options [gray, linenos]                        *)
       
     7 
       
     8 val gray = ref false;
       
     9 val linenos = ref false
       
    10 
       
    11 fun output prts =
       
    12   prts
       
    13   |> (if ! ThyOutput.quotes then map Pretty.quote else I)
       
    14   |> (if ! ThyOutput.display then
       
    15     map (Output.output o Pretty.string_of o Pretty.indent (! ThyOutput.indent))
       
    16     #> space_implode "\\isasep\\isanewline%\n"
       
    17     #> (if ! linenos then (enclose "\\begin{linenos}%\n" "%\n\\end{linenos}") else I) 
       
    18     #> (if ! gray then (enclose "\\begin{graybox}%\n" "%\n\\end{graybox}") else I) 
       
    19     #> enclose "\\begin{isabelle}%\n" "%\n\\end{isabelle}"
       
    20   else
       
    21     map (Output.output o (if ! ThyOutput.break then Pretty.string_of else Pretty.str_of))
       
    22     #> space_implode "\\isasep\\isanewline%\n"
       
    23     #> enclose "\\isa{" "}");
       
    24 
       
    25 
       
    26 fun boolean "" = true
       
    27   | boolean "true" = true
       
    28   | boolean "false" = false
       
    29   | boolean s = error ("Bad boolean value: " ^ quote s);
       
    30 
       
    31 val _ = ThyOutput.add_options
       
    32  [("gray", Library.setmp gray o boolean),
       
    33   ("linenos", Library.setmp linenos o boolean)]
       
    34 
       
    35 
       
    36 
       
    37 
       
    38 (** theory data **)
       
    39 
       
    40 structure ChunkData = TheoryDataFun
       
    41 (
       
    42   type T = (ML_Lex.token list * stamp) NameSpace.table
       
    43   val empty = NameSpace.empty_table;
       
    44   val copy = I;
       
    45   val extend = I;
       
    46   fun merge _ tables : T = NameSpace.merge_tables (eq_snd (op =)) tables handle Symtab.DUP dup =>
       
    47     error ("Attempt to merge different versions of ML chunk " ^ quote dup);
       
    48 );
       
    49 
       
    50 fun declare_chunk name thy =
       
    51   (Sign.full_bname thy name,
       
    52    ChunkData.map (snd o NameSpace.define (Sign.naming_of thy)
       
    53      (Binding.name name, ([], stamp ()))) thy
       
    54    handle Symtab.DUP _ => error ("Duplicate definition of ML chunk " ^ quote name));
       
    55 
       
    56 fun augment_chunk tok name =
       
    57   ChunkData.map (apsnd (Symtab.map_entry name (apfst (cons tok))));
       
    58 
       
    59 fun the_chunk thy name =
       
    60   let val (space, tab) = ChunkData.get thy
       
    61   in case Symtab.lookup tab (NameSpace.intern space name) of
       
    62       NONE => error ("Unknown ML chunk " ^ quote name)
       
    63     | SOME (toks, _) => rev toks
       
    64   end;
       
    65 
       
    66 
       
    67 (** parsers **)
       
    68 
       
    69 val not_eof = ML_Lex.stopper |> Scan.is_stopper #> not;
       
    70 
       
    71 val scan_space = Scan.many Symbol.is_blank;
       
    72 
       
    73 fun scan_cmt scan =
       
    74   Scan.one (ML_Lex.kind_of #> equal ML_Lex.Comment) >>
       
    75   (ML_Lex.content_of #> Symbol.explode #> Scan.finite Symbol.stopper scan #> fst);
       
    76 
       
    77 val scan_chunks = Scan.depend (fn (open_chunks, thy) =>
       
    78   scan_cmt ((Scan.this_string "(*" -- scan_space -- Scan.this_string "@chunk" --
       
    79     scan_space) |-- Symbol.scan_id --|
       
    80     (scan_space -- Scan.this_string "*)") >> (fn name =>
       
    81       let val (name', thy') = declare_chunk name thy
       
    82       in ((name' :: open_chunks, thy'), "") end))
       
    83   ||
       
    84   scan_cmt (Scan.this_string "(*" -- scan_space -- Scan.this_string "@end" --
       
    85     scan_space -- Scan.this_string "*)" >> (fn _ =>
       
    86       (case open_chunks of
       
    87          [] => error "end without matching chunk encountered"
       
    88        | _ :: open_chunks' => ((open_chunks', thy), ""))))
       
    89   ||
       
    90   Scan.one not_eof >> (fn tok =>
       
    91     ((open_chunks, fold (augment_chunk tok) open_chunks thy), ML_Lex.content_of tok)));
       
    92 
       
    93 fun chunks_of s thy =
       
    94   let val toks = Source.exhaust (ML_Lex.source (Source.of_string s))
       
    95   in case Scan.finite' ML_Lex.stopper (Scan.repeat (Scan.error scan_chunks))
       
    96         (([], thy), toks) of
       
    97       (toks', (([], thy'), _)) => (implode toks', thy')
       
    98     | (_, ((open_chunks, _), _)) =>
       
    99         error ("Open chunks at end of text: " ^ commas_quote open_chunks)
       
   100   end;
       
   101 
       
   102 
       
   103 (** use command **)
       
   104 
       
   105 fun load_ml dir raw_path thy =
       
   106   (case ThyLoad.check_ml dir raw_path of
       
   107     NONE => error ("Could not find ML file " ^ quote (Path.implode raw_path))
       
   108   | SOME (path, _) =>
       
   109       let val (s, thy') = chunks_of (File.read path) thy
       
   110       in
       
   111         Context.theory_map (ThyInfo.exec_file false raw_path) thy'
       
   112       end);
       
   113 
       
   114 fun use_chunks path thy =
       
   115   load_ml (ThyInfo.master_directory (Context.theory_name thy)) path thy;
       
   116 
       
   117 val _ =
       
   118   OuterSyntax.command "use_chunks" "eval ML text from file"
       
   119     (OuterKeyword.tag_ml OuterKeyword.thy_decl)
       
   120     (OuterParse.path >> (Toplevel.theory o use_chunks));
       
   121 
       
   122 
       
   123 (** antiquotation **)
       
   124 
       
   125 val str_of_source = space_implode " " o map OuterLex.unparse o #2 o #1 o Args.dest_src;
       
   126 
       
   127 val verbatim = space_implode "\\verb,|," o map (enclose "\\verb|" "|") o space_explode "|";
       
   128 
       
   129 val transform_cmts =
       
   130   Scan.finite ML_Lex.stopper (Scan.repeat
       
   131     (   scan_cmt
       
   132           (Scan.this_string "(*{" |--
       
   133            Scan.repeat (Scan.unless (Scan.this_string "}*)")
       
   134              (Scan.one (not o Symbol.is_eof))) --|
       
   135            Scan.this_string "}*)" >>
       
   136            (Symbol.encode_raw o enclose "\\hfill\\textrm{\\textit{" "}}" o implode))
       
   137      || Scan.one not_eof >> ML_Lex.content_of)) #>
       
   138   fst;
       
   139 
       
   140 fun output_chunk {state: Toplevel.state, source = src, context = ctxt} name =
       
   141   let
       
   142     val toks = the_chunk (ProofContext.theory_of ctxt) name;
       
   143     val (spc, toks') = (case toks of
       
   144         [] => ("", [])
       
   145       | [tok] => ("", [tok])
       
   146       | tok :: toks' =>
       
   147           let
       
   148             val (toks'', tok') = split_last toks'
       
   149             val toks''' =
       
   150               if ML_Lex.kind_of tok' = ML_Lex.Space then toks''
       
   151               else toks'
       
   152           in
       
   153             if ML_Lex.kind_of tok = ML_Lex.Space then
       
   154               (tok |> ML_Lex.content_of |> Symbol.explode |>
       
   155                take_suffix (equal " ") |> snd |> implode,
       
   156                toks''')
       
   157             else ("", tok :: toks''')
       
   158           end)
       
   159     val txt = spc ^ implode (transform_cmts toks')
       
   160   in
       
   161     txt |> split_lines |>
       
   162     (map Pretty.str) |> output 
       
   163   end;
       
   164 
       
   165 val _ = ThyOutput.antiquotation "ML_chunk" (Scan.lift Args.name) output_chunk;
       
   166 
       
   167 end;