|
1 structure Chunks = |
|
2 struct |
|
3 |
|
4 (** theory data **) |
|
5 |
|
6 structure ChunkData = TheoryDataFun |
|
7 ( |
|
8 type T = (ML_Lex.token list * stamp) NameSpace.table |
|
9 val empty = NameSpace.empty_table; |
|
10 val copy = I; |
|
11 val extend = I; |
|
12 fun merge _ tables : T = NameSpace.merge_tables (eq_snd (op =)) tables handle Symtab.DUP dup => |
|
13 error ("Attempt to merge different versions of ML chunk " ^ quote dup); |
|
14 ); |
|
15 |
|
16 fun declare_chunk name thy = |
|
17 (Sign.full_name thy name, |
|
18 ChunkData.map (NameSpace.extend_table (Sign.naming_of thy) [(name, ([], stamp ()))]) thy |
|
19 handle Symtab.DUP _ => error ("Duplicate definition of ML chunk " ^ quote name)); |
|
20 |
|
21 fun augment_chunk tok name = |
|
22 ChunkData.map (apsnd (Symtab.map_entry name (apfst (cons tok)))); |
|
23 |
|
24 fun the_chunk thy name = |
|
25 let val (space, tab) = ChunkData.get thy |
|
26 in case Symtab.lookup tab (NameSpace.intern space name) of |
|
27 NONE => error ("Unknown ML chunk " ^ quote name) |
|
28 | SOME (toks, _) => rev toks |
|
29 end; |
|
30 |
|
31 |
|
32 (** parsers **) |
|
33 |
|
34 val not_eof = ML_Lex.stopper |> Scan.is_stopper #> not; |
|
35 |
|
36 val scan_space = Scan.many Symbol.is_blank; |
|
37 |
|
38 fun scan_cmt scan = |
|
39 Scan.one (ML_Lex.kind_of #> equal ML_Lex.Comment) >> |
|
40 (ML_Lex.content_of #> Symbol.explode #> Scan.finite Symbol.stopper scan #> fst); |
|
41 |
|
42 val scan_chunks = Scan.depend (fn (open_chunks, thy) => |
|
43 scan_cmt ((Scan.this_string "(*" -- scan_space -- Scan.this_string "@chunk" -- |
|
44 scan_space) |-- Symbol.scan_id --| |
|
45 (scan_space -- Scan.this_string "*)") >> (fn name => |
|
46 let val (name', thy') = declare_chunk name thy |
|
47 in ((name' :: open_chunks, thy'), "") end)) |
|
48 || |
|
49 scan_cmt (Scan.this_string "(*" -- scan_space -- Scan.this_string "@end" -- |
|
50 scan_space -- Scan.this_string "*)" >> (fn _ => |
|
51 (case open_chunks of |
|
52 [] => error "end without matching chunk encountered" |
|
53 | _ :: open_chunks' => ((open_chunks', thy), "")))) |
|
54 || |
|
55 Scan.one not_eof >> (fn tok => |
|
56 ((open_chunks, fold (augment_chunk tok) open_chunks thy), ML_Lex.content_of tok))); |
|
57 |
|
58 fun chunks_of s thy = |
|
59 let val toks = Source.exhaust (ML_Lex.source (Source.of_string s)) |
|
60 in case Scan.finite' ML_Lex.stopper (Scan.repeat (Scan.error scan_chunks)) |
|
61 (([], thy), toks) of |
|
62 (toks', (([], thy'), _)) => (implode toks', thy') |
|
63 | (_, ((open_chunks, _), _)) => |
|
64 error ("Open chunks at end of text: " ^ commas_quote open_chunks) |
|
65 end; |
|
66 |
|
67 |
|
68 (** use command **) |
|
69 |
|
70 fun load_ml dir raw_path thy = |
|
71 (case ThyLoad.check_ml dir raw_path of |
|
72 NONE => error ("Could not find ML file " ^ quote (Path.implode raw_path)) |
|
73 | SOME (path, _) => |
|
74 let val (s, thy') = chunks_of (File.read path) thy |
|
75 in |
|
76 Context.theory_map (ThyInfo.exec_file false raw_path) thy' |
|
77 end); |
|
78 |
|
79 fun use_chunks path thy = |
|
80 load_ml (ThyInfo.master_directory (Context.theory_name thy)) path thy; |
|
81 |
|
82 val _ = |
|
83 OuterSyntax.command "use_chunks" "eval ML text from file" |
|
84 (OuterKeyword.tag_ml OuterKeyword.thy_decl) |
|
85 (OuterParse.path >> (Toplevel.theory o use_chunks)); |
|
86 |
|
87 |
|
88 (** antiquotation **) |
|
89 |
|
90 val str_of_source = space_implode " " o map OuterLex.unparse o #2 o #1 o Args.dest_src; |
|
91 |
|
92 val verbatim = space_implode "\\verb,|," o map (enclose "\\verb|" "|") o space_explode "|"; |
|
93 |
|
94 val transform_cmts = |
|
95 Scan.finite ML_Lex.stopper (Scan.repeat |
|
96 ( scan_cmt |
|
97 (Scan.this_string "(*{" |-- |
|
98 Scan.repeat (Scan.unless (Scan.this_string "}*)") |
|
99 (Scan.one (not o Symbol.is_eof))) --| |
|
100 Scan.this_string "}*)" >> |
|
101 (enclose "\\hfill\\textrm{\\textit{" "}}" o implode)) |
|
102 || Scan.one not_eof >> ML_Lex.content_of)) #> |
|
103 fst; |
|
104 |
|
105 fun output_chunk src ctxt name = |
|
106 let |
|
107 val toks = the_chunk (ProofContext.theory_of ctxt) name; |
|
108 val (spc, toks') = (case toks of |
|
109 [] => ("", []) |
|
110 | [tok] => ("", [tok]) |
|
111 | tok :: toks' => |
|
112 let |
|
113 val (toks'', tok') = split_last toks' |
|
114 val toks''' = |
|
115 if ML_Lex.kind_of tok' = ML_Lex.Space then toks'' |
|
116 else toks' |
|
117 in |
|
118 if ML_Lex.kind_of tok = ML_Lex.Space then |
|
119 (tok |> ML_Lex.content_of |> Symbol.explode |> |
|
120 take_suffix (equal " ") |> snd |> implode, |
|
121 toks''') |
|
122 else ("", tok :: toks''') |
|
123 end) |
|
124 val txt = spc ^ implode (transform_cmts toks') |
|
125 in |
|
126 ((if ! ThyOutput.source then str_of_source src else txt) |
|
127 |> (if ! ThyOutput.quotes then quote else I) |
|
128 |> (if ! ThyOutput.display then enclose "\\begin{alltt}\n" "\n\\end{alltt}" |
|
129 else split_lines #> map verbatim #> space_implode "\\isasep\\isanewline%\n")) |
|
130 end; |
|
131 |
|
132 val _ = ThyOutput.add_commands |
|
133 [("ML_chunk", ThyOutput.args (Scan.lift Args.name) output_chunk)]; |
|
134 |
|
135 end; |