| 5829 |      1 | (*  Title:      Pure/Isar/outer_syntax.ML
 | 
|  |      2 |     ID:         $Id$
 | 
|  |      3 |     Author:     Markus Wenzel, TU Muenchen
 | 
|  |      4 | 
 | 
|  |      5 | The global Isabelle/Isar outer syntax.
 | 
| 6247 |      6 | 
 | 
|  |      7 | TODO:
 | 
|  |      8 |   - cleanup;
 | 
|  |      9 |   - avoid string constants;
 | 
| 5829 |     10 | *)
 | 
|  |     11 | 
 | 
|  |     12 | signature BASIC_OUTER_SYNTAX =
 | 
|  |     13 | sig
 | 
|  |     14 |   val main: unit -> unit
 | 
| 5883 |     15 |   val loop: unit -> unit
 | 
| 5829 |     16 |   val help: unit -> unit
 | 
|  |     17 | end;
 | 
|  |     18 | 
 | 
|  |     19 | signature OUTER_SYNTAX =
 | 
|  |     20 | sig
 | 
|  |     21 |   include BASIC_OUTER_SYNTAX
 | 
|  |     22 |   type token
 | 
|  |     23 |   type parser
 | 
|  |     24 |   val parser: bool -> string -> string ->
 | 
|  |     25 |     (token list -> (Toplevel.transition -> Toplevel.transition) * token list) -> parser
 | 
| 5883 |     26 |   val print_outer_syntax: unit -> unit
 | 
| 5952 |     27 |   val commands: unit -> string list
 | 
| 5829 |     28 |   val add_keywords: string list -> unit
 | 
|  |     29 |   val add_parsers: parser list -> unit
 | 
| 6247 |     30 |   val theory_header: token list -> (string * string list * (string * bool) list) * token list
 | 
| 6199 |     31 |   val deps_thy: string -> bool -> Path.T -> string list * Path.T list
 | 
|  |     32 |   val load_thy: string -> bool -> bool -> Path.T -> unit
 | 
| 5829 |     33 |   val isar: Toplevel.isar
 | 
|  |     34 | end;
 | 
|  |     35 | 
 | 
|  |     36 | structure OuterSyntax: OUTER_SYNTAX =
 | 
|  |     37 | struct
 | 
|  |     38 | 
 | 
|  |     39 | 
 | 
|  |     40 | (** outer syntax **)
 | 
|  |     41 | 
 | 
|  |     42 | (* parsers *)
 | 
|  |     43 | 
 | 
|  |     44 | type token = OuterLex.token;
 | 
|  |     45 | type parser_fn = token list -> (Toplevel.transition -> Toplevel.transition) * token list;
 | 
|  |     46 | 
 | 
|  |     47 | datatype parser =
 | 
|  |     48 |   Parser of string * string * bool * parser_fn;
 | 
|  |     49 | 
 | 
|  |     50 | fun parser int_only name comment parse = Parser (name, comment, int_only, parse);
 | 
|  |     51 | 
 | 
|  |     52 | 
 | 
|  |     53 | (* parse command *)
 | 
|  |     54 | 
 | 
| 6199 |     55 | local open OuterParse in
 | 
|  |     56 | 
 | 
| 5829 |     57 | fun command_name cmd =
 | 
|  |     58 |   group "command"
 | 
|  |     59 |     (position (Scan.one (OuterLex.keyword_pred (is_some o cmd)) >> OuterLex.val_of));
 | 
|  |     60 | 
 | 
|  |     61 | fun command_body cmd (name, _) =
 | 
|  |     62 |   let val (int_only, parse) = the (cmd name)
 | 
|  |     63 |   in !!! (Scan.prompt (name ^ "# ") (parse >> pair int_only)) end;
 | 
|  |     64 | 
 | 
|  |     65 | fun command cmd =
 | 
|  |     66 |   $$$ ";" >> K None ||
 | 
|  |     67 |   command_name cmd :-- command_body cmd >> (fn ((name, pos), (int_only, f)) =>
 | 
|  |     68 |     Some (Toplevel.empty |> Toplevel.name name |> Toplevel.position pos |>
 | 
|  |     69 |       Toplevel.interactive int_only |> f));
 | 
|  |     70 | 
 | 
| 6199 |     71 | end;
 | 
|  |     72 | 
 | 
| 5829 |     73 | 
 | 
|  |     74 | 
 | 
|  |     75 | (** global syntax state **)
 | 
|  |     76 | 
 | 
|  |     77 | val global_lexicon = ref Scan.empty_lexicon;
 | 
|  |     78 | val global_parsers = ref (Symtab.empty: (string * (bool * parser_fn)) Symtab.table);
 | 
|  |     79 | 
 | 
| 5952 |     80 | fun commands () = Symtab.keys (! global_parsers);
 | 
|  |     81 | 
 | 
| 5829 |     82 | 
 | 
|  |     83 | (* print syntax *)
 | 
|  |     84 | 
 | 
|  |     85 | fun print_outer_syntax () =
 | 
|  |     86 |   let
 | 
|  |     87 |     val keywords = map implode (Scan.dest_lexicon (! global_lexicon));
 | 
|  |     88 |     fun pretty_cmd (name, (comment, _)) =
 | 
|  |     89 |       Pretty.block [Pretty.str (name ^ ":"), Pretty.brk 2, Pretty.str comment];
 | 
|  |     90 |     val (int_cmds, cmds) = partition (#1 o #2 o #2) (Symtab.dest (! global_parsers));
 | 
|  |     91 |   in
 | 
| 6107 |     92 |     Pretty.writeln (Pretty.strs ("syntax keywords:" :: map quote keywords));
 | 
| 6095 |     93 |     Pretty.writeln (Pretty.big_list "proper commands:" (map pretty_cmd cmds));
 | 
|  |     94 |     Pretty.writeln (Pretty.big_list "improper commands (interactive-only):"
 | 
|  |     95 |       (map pretty_cmd int_cmds))
 | 
| 5829 |     96 |   end;
 | 
|  |     97 | 
 | 
|  |     98 | 
 | 
|  |     99 | (* augment syntax *)
 | 
|  |    100 | 
 | 
|  |    101 | fun add_keywords keywords =
 | 
|  |    102 |   global_lexicon := Scan.extend_lexicon (! global_lexicon) (map Symbol.explode keywords);
 | 
|  |    103 | 
 | 
|  |    104 | fun add_parser (tab, Parser (name, comment, int_only, parse)) =
 | 
|  |    105 |  (if is_none (Symtab.lookup (tab, name)) then ()
 | 
|  |    106 |   else warning ("Redefined outer syntax command " ^ quote name);
 | 
|  |    107 |   Symtab.update ((name, (comment, (int_only, parse))), tab));
 | 
|  |    108 | 
 | 
|  |    109 | fun add_parsers parsers =
 | 
|  |    110 |   (global_parsers := foldl add_parser (! global_parsers, parsers);
 | 
|  |    111 |     add_keywords (map (fn Parser (name, _, _, _) => name) parsers));
 | 
|  |    112 | 
 | 
|  |    113 | 
 | 
|  |    114 | (* get current lexer / parser *)
 | 
|  |    115 | 
 | 
|  |    116 | (*Note: the syntax for files is statically determined at the very
 | 
|  |    117 |   beginning; for interactive processing it may change dynamically.*)
 | 
|  |    118 | 
 | 
|  |    119 | fun get_lexicon () = ! global_lexicon;
 | 
|  |    120 | fun get_parser () = apsome snd o curry Symtab.lookup (! global_parsers);
 | 
|  |    121 | 
 | 
|  |    122 | 
 | 
|  |    123 | 
 | 
|  |    124 | (** read theory **)
 | 
|  |    125 | 
 | 
| 6247 |    126 | (* theory keyword *)
 | 
|  |    127 | 
 | 
|  |    128 | val theoryN = "theory";
 | 
|  |    129 | val theory_keyword = OuterParse.$$$ theoryN;
 | 
|  |    130 | 
 | 
|  |    131 | 
 | 
| 5829 |    132 | (* source *)
 | 
|  |    133 | 
 | 
|  |    134 | fun no_command cmd =
 | 
|  |    135 |   Scan.one ((not o OuterLex.keyword_pred ((is_some o cmd) orf equal ";")) andf OuterLex.not_eof);
 | 
|  |    136 | 
 | 
|  |    137 | fun recover cmd =
 | 
|  |    138 |   Scan.prompt "recover# " (Scan.one OuterLex.not_eof -- Scan.repeat (no_command cmd));
 | 
|  |    139 | 
 | 
|  |    140 | fun source do_recover cmd src =
 | 
|  |    141 |   src
 | 
| 6199 |    142 |   |> Source.source OuterLex.stopper (Scan.bulk (fn xs => OuterParse.!!! (command (cmd ())) xs))
 | 
| 5829 |    143 |     (if do_recover then Some (fn xs => recover (cmd ()) xs) else None)
 | 
|  |    144 |   |> Source.mapfilter I;
 | 
|  |    145 | 
 | 
|  |    146 | 
 | 
|  |    147 | (* detect header *)
 | 
|  |    148 | 
 | 
| 6199 |    149 | fun scan_header get_lexicon scan (src, pos) =
 | 
| 5829 |    150 |   src
 | 
|  |    151 |   |> Symbol.source false
 | 
| 6199 |    152 |   |> OuterLex.source false get_lexicon pos
 | 
|  |    153 |   |> Source.source OuterLex.stopper (Scan.single scan) None
 | 
| 5829 |    154 |   |> (fst o the o Source.get_single);
 | 
|  |    155 | 
 | 
| 6247 |    156 | val check_header_lexicon = Scan.make_lexicon [Symbol.explode theoryN];
 | 
| 5829 |    157 | 
 | 
| 6199 |    158 | fun is_old_theory src =
 | 
| 6247 |    159 |   is_none (scan_header (K check_header_lexicon) (Scan.option theory_keyword) src);
 | 
| 6199 |    160 | 
 | 
|  |    161 | fun warn_theory_style path is_old =
 | 
|  |    162 |   let
 | 
|  |    163 |     val style = if is_old then "old" else "new";
 | 
|  |    164 |     val _ = warning ("Assuming " ^ style ^ "-style theory format for " ^ quote (Path.pack path));
 | 
|  |    165 |   in is_old end;
 | 
|  |    166 | 
 | 
|  |    167 | 
 | 
|  |    168 | (* deps_thy --- inspect theory header *)
 | 
|  |    169 | 
 | 
| 6247 |    170 | val header_lexicon =
 | 
|  |    171 |   Scan.make_lexicon (map Symbol.explode ["(", ")", "+", ":", "=", "files", theoryN]);
 | 
| 6199 |    172 | 
 | 
|  |    173 | local open OuterParse in
 | 
|  |    174 | 
 | 
| 6247 |    175 | val file_name = ($$$ "(" |-- !!! (name --| $$$ ")")) >> rpair false || name >> rpair true;
 | 
|  |    176 | 
 | 
|  |    177 | val theory_head =
 | 
|  |    178 |   (name -- ($$$ "=" |-- enum1 "+" name) --
 | 
|  |    179 |     Scan.optional ($$$ "files" |-- !!! (Scan.repeat1 file_name)) [])
 | 
|  |    180 |   >> (fn ((A, Bs), files) => (A, Bs, files));
 | 
|  |    181 | 
 | 
|  |    182 | val theory_header = theory_head --| (Scan.ahead eof || $$$ ":");
 | 
|  |    183 | val only_header = theory_keyword |-- theory_head --| Scan.ahead eof;
 | 
|  |    184 | val new_header = theory_keyword |-- !!! theory_header;
 | 
| 6199 |    185 | 
 | 
|  |    186 | val old_header =
 | 
|  |    187 |   name -- ($$$ "=" |-- name -- Scan.repeat ($$$ "+" |-- name))
 | 
| 6247 |    188 |   >> (fn (A, (B, Bs)) => (A, B :: Bs, []: (string * bool) list));
 | 
| 6199 |    189 | 
 | 
|  |    190 | end;
 | 
| 5829 |    191 | 
 | 
| 6199 |    192 | fun deps_thy name ml path =
 | 
|  |    193 |   let
 | 
|  |    194 |     val src = Source.of_file path;
 | 
|  |    195 |     val is_old = warn_theory_style path (is_old_theory src);
 | 
| 6247 |    196 |     val (name', parents, files) =
 | 
| 6199 |    197 |       (*Note: old style headers dynamically depend on the current lexicon :-( *)
 | 
|  |    198 |       if is_old then scan_header ThySyn.get_lexicon (Scan.error old_header) src
 | 
| 6247 |    199 |       else scan_header (K header_lexicon) (Scan.error new_header) src;
 | 
| 6199 |    200 | 
 | 
|  |    201 |     val ml_path = ThyLoad.ml_path name;
 | 
|  |    202 |     val ml_file = if not ml orelse is_none (ThyLoad.check_file ml_path) then [] else [ml_path];
 | 
|  |    203 |   in
 | 
|  |    204 |     if name <> name' then
 | 
|  |    205 |       error ("Filename " ^ quote (Path.pack path) ^ " does not match theory name " ^ quote name)
 | 
| 6247 |    206 |     else (parents, map (Path.unpack o #1) files @ ml_file)
 | 
| 6199 |    207 |   end;
 | 
|  |    208 | 
 | 
|  |    209 | 
 | 
|  |    210 | (* load_thy --- read text (including header) *)
 | 
|  |    211 | 
 | 
| 6247 |    212 | fun try_ml_file name ml time =
 | 
| 6199 |    213 |   let
 | 
|  |    214 |     val path = ThyLoad.ml_path name;
 | 
| 6247 |    215 |     val tr = Toplevel.imperative (fn () => ThyInfo.load_file time path);
 | 
|  |    216 |     val tr_name = if time then "time_use" else "use";
 | 
| 6199 |    217 |   in
 | 
|  |    218 |     if not ml orelse is_none (ThyLoad.check_file path) then ()
 | 
| 6247 |    219 |     else Toplevel.excursion [Toplevel.empty |> Toplevel.name tr_name |> tr]
 | 
| 6199 |    220 |   end;
 | 
|  |    221 | 
 | 
|  |    222 | fun parse_thy (src, pos) =
 | 
| 6247 |    223 |   let
 | 
|  |    224 |     val lex_src =
 | 
|  |    225 |       src
 | 
|  |    226 |       |> Symbol.source false
 | 
|  |    227 |       |> OuterLex.source false (K (get_lexicon ())) pos;
 | 
|  |    228 |     val only_head =
 | 
|  |    229 |       lex_src
 | 
|  |    230 |       |> Source.source OuterLex.stopper (Scan.single (Scan.option only_header)) None
 | 
|  |    231 |       |> (fst o the o Source.get_single);
 | 
|  |    232 |   in
 | 
|  |    233 |     (case only_head of
 | 
|  |    234 |       None =>
 | 
|  |    235 |         lex_src
 | 
|  |    236 |         |> source false (K (get_parser ()))
 | 
|  |    237 |         |> Source.exhaust
 | 
|  |    238 |     | Some spec =>
 | 
|  |    239 |         [Toplevel.empty |> Toplevel.name theoryN |> IsarThy.theory spec,
 | 
|  |    240 |           Toplevel.empty |> Toplevel.name "end" |> Toplevel.exit])
 | 
|  |    241 |   end;
 | 
| 5829 |    242 | 
 | 
| 6247 |    243 | fun run_thy name path =
 | 
|  |    244 |   let val (src, pos) = Source.of_file path in
 | 
|  |    245 |     if is_old_theory (src, pos) then ThySyn.load_thy name (Source.exhaust src)
 | 
|  |    246 |     else (Toplevel.excursion (parse_thy (src, pos))
 | 
|  |    247 |       handle exn => error (Toplevel.exn_message exn))
 | 
|  |    248 |   end;
 | 
| 6199 |    249 | 
 | 
|  |    250 | fun load_thy name ml time path =
 | 
| 6247 |    251 |  (if time then
 | 
|  |    252 |     timeit (fn () =>
 | 
|  |    253 |      (writeln ("\n**** Starting theory " ^ quote name ^ " ****");
 | 
|  |    254 |       setmp Goals.proof_timing true (run_thy name) path;
 | 
|  |    255 |       writeln ("**** Finished theory " ^ quote name ^ " ****\n")))
 | 
|  |    256 |   else run_thy name path;
 | 
|  |    257 |   Context.context (ThyInfo.get_theory name);
 | 
|  |    258 |   try_ml_file name ml time);
 | 
| 5829 |    259 | 
 | 
|  |    260 | 
 | 
|  |    261 | (* interactive source of state transformers *)
 | 
|  |    262 | 
 | 
|  |    263 | val isar =
 | 
|  |    264 |   Source.tty
 | 
|  |    265 |   |> Symbol.source true
 | 
|  |    266 |   |> OuterLex.source true get_lexicon (Position.line_name 1 "stdin")
 | 
|  |    267 |   |> source true get_parser;
 | 
|  |    268 | 
 | 
|  |    269 | 
 | 
|  |    270 | 
 | 
|  |    271 | (** the read-eval-print loop **)
 | 
|  |    272 | 
 | 
| 5923 |    273 | (* main loop *)
 | 
|  |    274 | 
 | 
| 5883 |    275 | fun loop () = (Context.reset_context (); Toplevel.loop isar);
 | 
| 5829 |    276 | 
 | 
|  |    277 | fun main () =
 | 
|  |    278 |  (Toplevel.set_state Toplevel.toplevel;
 | 
|  |    279 |   ml_prompts "ML> " "ML# ";
 | 
| 6199 |    280 |   writeln (Session.welcome ());
 | 
| 5883 |    281 |   loop ());
 | 
| 5829 |    282 | 
 | 
|  |    283 | 
 | 
|  |    284 | (* help *)
 | 
|  |    285 | 
 | 
|  |    286 | fun help () =
 | 
|  |    287 |   writeln ("This is Isabelle's underlying ML system (" ^ ml_system ^ ");\n\
 | 
| 5883 |    288 |     \invoke 'loop();' to enter the Isar loop.");
 | 
| 5829 |    289 | 
 | 
|  |    290 | 
 | 
|  |    291 | end;
 | 
|  |    292 | 
 | 
| 6199 |    293 | (*setup theory syntax dependent operations*)
 | 
|  |    294 | ThyLoad.deps_thy_fn := OuterSyntax.deps_thy;
 | 
|  |    295 | ThyLoad.load_thy_fn := OuterSyntax.load_thy;
 | 
|  |    296 | structure ThyLoad: THY_LOAD = ThyLoad;
 | 
|  |    297 | 
 | 
| 5829 |    298 | structure BasicOuterSyntax: BASIC_OUTER_SYNTAX = OuterSyntax;
 | 
|  |    299 | open BasicOuterSyntax;
 |