src/Pure/Isar/outer_syntax.ML
changeset 29311 4c830711e6f1
parent 28436 4faf705a177d
child 29315 b074c05f00ad
equal deleted inserted replaced
29310:1a633304fa5e 29311:4c830711e6f1
     1 (*  Title:      Pure/Isar/outer_syntax.ML
     1 (*  Title:      Pure/Isar/outer_syntax.ML
     2     ID:         $Id$
       
     3     Author:     Markus Wenzel, TU Muenchen
     2     Author:     Markus Wenzel, TU Muenchen
     4 
     3 
     5 The global Isabelle/Isar outer syntax.
     4 The global Isabelle/Isar outer syntax.
     6 
     5 
     7 Note: the syntax for files is statically determined at the very
     6 Note: the syntax for files is statically determined at the very
     8 beginning; for interactive processing it may change dynamically.
     7 beginning; for interactive processing it may change dynamically.
     9 *)
     8 *)
    10 
     9 
    11 signature OUTER_SYNTAX =
    10 signature OUTER_SYNTAX =
    12 sig
    11 sig
    13   type parser_fn = OuterLex.token list ->
    12   type 'a parser = 'a OuterParse.parser
    14     (Toplevel.transition -> Toplevel.transition) * OuterLex.token list
    13   val command: string -> string -> OuterKeyword.T ->
    15   val command: string -> string -> OuterKeyword.T -> parser_fn -> unit
    14     (Toplevel.transition -> Toplevel.transition) parser -> unit
    16   val markup_command: ThyOutput.markup -> string -> string -> OuterKeyword.T -> parser_fn -> unit
    15   val markup_command: ThyOutput.markup -> string -> string -> OuterKeyword.T ->
    17   val improper_command: string -> string -> OuterKeyword.T -> parser_fn -> unit
    16     (Toplevel.transition -> Toplevel.transition) parser -> unit
       
    17   val improper_command: string -> string -> OuterKeyword.T ->
       
    18     (Toplevel.transition -> Toplevel.transition) parser -> unit
       
    19   val internal_command: string -> (Toplevel.transition -> Toplevel.transition) parser -> unit
    18   val local_theory: string -> string -> OuterKeyword.T ->
    20   val local_theory: string -> string -> OuterKeyword.T ->
    19     (OuterParse.token list -> (local_theory -> local_theory) * OuterLex.token list) -> unit
    21     (local_theory -> local_theory) parser -> unit
    20   val local_theory_to_proof': string -> string -> OuterKeyword.T ->
    22   val local_theory_to_proof': string -> string -> OuterKeyword.T ->
    21     (OuterParse.token list -> (bool -> local_theory -> Proof.state) * OuterLex.token list) -> unit
    23     (bool -> local_theory -> Proof.state) parser -> unit
    22   val local_theory_to_proof: string -> string -> OuterKeyword.T ->
    24   val local_theory_to_proof: string -> string -> OuterKeyword.T ->
    23     (OuterParse.token list -> (local_theory -> Proof.state) * OuterLex.token list) -> unit
    25     (local_theory -> Proof.state) parser -> unit
    24   val print_outer_syntax: unit -> unit
    26   val print_outer_syntax: unit -> unit
    25   val scan: Position.T -> string -> OuterLex.token list
    27   val scan: Position.T -> string -> OuterLex.token list
    26   val parse: Position.T -> string -> Toplevel.transition list
    28   val parse: Position.T -> string -> Toplevel.transition list
    27   val process_file: Path.T -> theory -> theory
    29   val process_file: Path.T -> theory -> theory
    28   type isar
    30   type isar
    34 structure OuterSyntax: OUTER_SYNTAX =
    36 structure OuterSyntax: OUTER_SYNTAX =
    35 struct
    37 struct
    36 
    38 
    37 structure T = OuterLex;
    39 structure T = OuterLex;
    38 structure P = OuterParse;
    40 structure P = OuterParse;
       
    41 type 'a parser = 'a P.parser;
       
    42 
    39 
    43 
    40 
    44 
    41 (** outer syntax **)
    45 (** outer syntax **)
    42 
    46 
    43 (* parsers *)
    47 (* command parsers *)
    44 
    48 
    45 type parser_fn = T.token list -> (Toplevel.transition -> Toplevel.transition) * T.token list;
    49 datatype command = Command of
    46 
       
    47 datatype parser = Parser of
       
    48  {comment: string,
    50  {comment: string,
    49   markup: ThyOutput.markup option,
    51   markup: ThyOutput.markup option,
    50   int_only: bool,
    52   int_only: bool,
    51   parse: parser_fn};
    53   parse: (Toplevel.transition -> Toplevel.transition) parser};
    52 
    54 
    53 fun make_parser comment markup int_only parse =
    55 fun make_command comment markup int_only parse =
    54   Parser {comment = comment, markup = markup, int_only = int_only, parse = parse};
    56   Command {comment = comment, markup = markup, int_only = int_only, parse = parse};
    55 
    57 
    56 
    58 
    57 (* parse command *)
    59 (* parse command *)
    58 
    60 
    59 local
    61 local
    61 fun terminate false = Scan.succeed ()
    63 fun terminate false = Scan.succeed ()
    62   | terminate true = P.group "end of input" (Scan.option P.sync -- P.semicolon >> K ());
    64   | terminate true = P.group "end of input" (Scan.option P.sync -- P.semicolon >> K ());
    63 
    65 
    64 fun body cmd (name, _) =
    66 fun body cmd (name, _) =
    65   (case cmd name of
    67   (case cmd name of
    66     SOME (Parser {int_only, parse, ...}) =>
    68     SOME (Command {int_only, parse, ...}) =>
    67       P.!!! (Scan.prompt (name ^ "# ") (P.tags |-- parse >> pair int_only))
    69       P.!!! (Scan.prompt (name ^ "# ") (P.tags |-- parse >> pair int_only))
    68   | NONE => sys_error ("no parser for outer syntax command " ^ quote name));
    70   | NONE => sys_error ("no parser for outer syntax command " ^ quote name));
    69 
    71 
    70 in
    72 in
    71 
    73 
    83 
    85 
    84 (** global outer syntax **)
    86 (** global outer syntax **)
    85 
    87 
    86 local
    88 local
    87 
    89 
    88 val global_parsers = ref (Symtab.empty: parser Symtab.table);
    90 val global_commands = ref (Symtab.empty: command Symtab.table);
    89 val global_markups = ref ([]: (string * ThyOutput.markup) list);
    91 val global_markups = ref ([]: (string * ThyOutput.markup) list);
    90 
    92 
    91 fun change_parsers f = CRITICAL (fn () =>
    93 fun change_commands f = CRITICAL (fn () =>
    92  (change global_parsers f;
    94  (change global_commands f;
    93   global_markups :=
    95   global_markups :=
    94     Symtab.fold (fn (name, Parser {markup = SOME m, ...}) => cons (name, m) | _ => I)
    96     Symtab.fold (fn (name, Command {markup = SOME m, ...}) => cons (name, m) | _ => I)
    95       (! global_parsers) []));
    97       (! global_commands) []));
    96 
    98 
    97 in
    99 in
    98 
   100 
    99 (* access current syntax *)
   101 (* access current syntax *)
   100 
   102 
   101 fun get_parsers () = CRITICAL (fn () => ! global_parsers);
   103 fun get_commands () = CRITICAL (fn () => ! global_commands);
   102 fun get_markups () = CRITICAL (fn () => ! global_markups);
   104 fun get_markups () = CRITICAL (fn () => ! global_markups);
   103 
   105 
   104 fun get_parser () = Symtab.lookup (get_parsers ());
   106 fun get_command () = Symtab.lookup (get_commands ());
   105 fun get_syntax () = CRITICAL (fn () => (OuterKeyword.get_lexicons (), get_parser ()));
   107 fun get_syntax () = CRITICAL (fn () => (OuterKeyword.get_lexicons (), get_command ()));
   106 
   108 
   107 fun is_markup kind name = AList.lookup (op =) (get_markups ()) name = SOME kind;
   109 fun is_markup kind name = AList.lookup (op =) (get_markups ()) name = SOME kind;
   108 
   110 
   109 
   111 
   110 (* augment syntax *)
   112 (* augment syntax *)
   111 
   113 
   112 fun add_parser name kind parser = CRITICAL (fn () =>
   114 fun add_command name kind cmd = CRITICAL (fn () =>
   113  (OuterKeyword.command name kind;
   115  (OuterKeyword.command name kind;
   114   if not (Symtab.defined (get_parsers ()) name) then ()
   116   if not (Symtab.defined (get_commands ()) name) then ()
   115   else warning ("Redefining outer syntax command " ^ quote name);
   117   else warning ("Redefining outer syntax command " ^ quote name);
   116   change_parsers (Symtab.update (name, parser))));
   118   change_commands (Symtab.update (name, cmd))));
   117 
   119 
   118 fun command name comment kind parse =
   120 fun command name comment kind parse =
   119   add_parser name kind (make_parser comment NONE false parse);
   121   add_command name kind (make_command comment NONE false parse);
   120 
   122 
   121 fun markup_command markup name comment kind parse =
   123 fun markup_command markup name comment kind parse =
   122   add_parser name kind (make_parser comment (SOME markup) false parse);
   124   add_command name kind (make_command comment (SOME markup) false parse);
   123 
   125 
   124 fun improper_command name comment kind parse =
   126 fun improper_command name comment kind parse =
   125   add_parser name kind (make_parser comment NONE true parse);
   127   add_command name kind (make_command comment NONE true parse);
   126 
   128 
   127 end;
   129 end;
       
   130 
       
   131 fun internal_command name parse =
       
   132   command name "(internal)" OuterKeyword.control (parse >> (fn tr => Toplevel.no_timing o tr));
   128 
   133 
   129 
   134 
   130 (* local_theory commands *)
   135 (* local_theory commands *)
   131 
   136 
   132 fun local_theory_command do_print trans name comment kind parse =
   137 fun local_theory_command do_print trans name comment kind parse =
   133   command name comment kind (P.opt_target -- parse
   138   command name comment kind (P.opt_target -- parse
   134     >> (fn (loc, f) => (if do_print then Toplevel.print else I) o trans loc f));
   139     >> (fn (loc, f) => (if do_print then Toplevel.print else I) o trans loc f));
   135 
   140 
   136 val local_theory           = local_theory_command false Toplevel.local_theory;
   141 val local_theory = local_theory_command false Toplevel.local_theory;
   137 val local_theory_to_proof' = local_theory_command true Toplevel.local_theory_to_proof';
   142 val local_theory_to_proof' = local_theory_command true Toplevel.local_theory_to_proof';
   138 val local_theory_to_proof  = local_theory_command true Toplevel.local_theory_to_proof;
   143 val local_theory_to_proof = local_theory_command true Toplevel.local_theory_to_proof;
   139 
   144 
   140 
   145 
   141 (* inspect syntax *)
   146 (* inspect syntax *)
   142 
   147 
   143 fun dest_parsers () =
   148 fun dest_commands () =
   144   get_parsers () |> Symtab.dest |> sort_wrt #1
   149   get_commands () |> Symtab.dest |> sort_wrt #1
   145   |> map (fn (name, Parser {comment, int_only, ...}) => (name, comment, int_only));
   150   |> map (fn (name, Command {comment, int_only, ...}) => (name, comment, int_only));
   146 
   151 
   147 fun print_outer_syntax () =
   152 fun print_outer_syntax () =
   148   let
   153   let
   149     fun pretty_cmd (name, comment, _) =
   154     fun pretty_cmd (name, comment, _) =
   150       Pretty.block [Pretty.str (name ^ ":"), Pretty.brk 2, Pretty.str comment];
   155       Pretty.block [Pretty.str (name ^ ":"), Pretty.brk 2, Pretty.str comment];
   151     val (int_cmds, cmds) = List.partition #3 (dest_parsers ());
   156     val (int_cmds, cmds) = List.partition #3 (dest_commands ());
   152   in
   157   in
   153     [Pretty.strs ("syntax keywords:" :: map quote (OuterKeyword.dest_keywords ())),
   158     [Pretty.strs ("syntax keywords:" :: map quote (OuterKeyword.dest_keywords ())),
   154       Pretty.big_list "commands:" (map pretty_cmd cmds),
   159       Pretty.big_list "commands:" (map pretty_cmd cmds),
   155       Pretty.big_list "interactive-only commands:" (map pretty_cmd int_cmds)]
   160       Pretty.big_list "interactive-only commands:" (map pretty_cmd int_cmds)]
   156     |> Pretty.chunks |> Pretty.writeln
   161     |> Pretty.chunks |> Pretty.writeln
   192 
   197 
   193 fun parse pos str =
   198 fun parse pos str =
   194   Source.of_string str
   199   Source.of_string str
   195   |> Symbol.source {do_recover = false}
   200   |> Symbol.source {do_recover = false}
   196   |> T.source {do_recover = SOME false} OuterKeyword.get_lexicons pos
   201   |> T.source {do_recover = SOME false} OuterKeyword.get_lexicons pos
   197   |> toplevel_source false NONE get_parser
   202   |> toplevel_source false NONE get_command
   198   |> Source.exhaust;
   203   |> Source.exhaust;
   199 
   204 
   200 
   205 
   201 (* process file *)
   206 (* process file *)
   202 
   207 
   223 
   228 
   224 fun isar term : isar =
   229 fun isar term : isar =
   225   Source.tty
   230   Source.tty
   226   |> Symbol.source {do_recover = true}
   231   |> Symbol.source {do_recover = true}
   227   |> T.source {do_recover = SOME true} OuterKeyword.get_lexicons Position.none
   232   |> T.source {do_recover = SOME true} OuterKeyword.get_lexicons Position.none
   228   |> toplevel_source term (SOME true) get_parser;
   233   |> toplevel_source term (SOME true) get_command;
   229 
   234 
   230 
   235 
   231 (* prepare toplevel commands -- fail-safe *)
   236 (* prepare toplevel commands -- fail-safe *)
   232 
   237 
   233 val not_singleton = "Exactly one command expected";
   238 val not_singleton = "Exactly one command expected";
   234 
   239 
   235 fun prepare_span parser span =
   240 fun prepare_span commands span =
   236   let
   241   let
   237     val range_pos = Position.encode_range (ThyEdit.span_range span);
   242     val range_pos = Position.encode_range (ThyEdit.span_range span);
   238     val toks = ThyEdit.span_content span;
   243     val toks = ThyEdit.span_content span;
   239     val _ = List.app ThyEdit.report_token toks;
   244     val _ = List.app ThyEdit.report_token toks;
   240   in
   245   in
   241     (case Source.exhaust (toplevel_source false NONE (K parser) (Source.of_list toks)) of
   246     (case Source.exhaust (toplevel_source false NONE (K commands) (Source.of_list toks)) of
   242       [tr] => (tr, true)
   247       [tr] => (tr, true)
   243     | [] => (Toplevel.ignored range_pos, false)
   248     | [] => (Toplevel.ignored range_pos, false)
   244     | _ => (Toplevel.malformed range_pos not_singleton, true))
   249     | _ => (Toplevel.malformed range_pos not_singleton, true))
   245     handle ERROR msg => (Toplevel.malformed range_pos msg, true)
   250     handle ERROR msg => (Toplevel.malformed range_pos msg, true)
   246   end;
   251   end;
   247 
   252 
   248 fun prepare_unit parser (cmd, proof, proper_proof) =
   253 fun prepare_unit commands (cmd, proof, proper_proof) =
   249   let
   254   let
   250     val (tr, proper_cmd) = prepare_span parser cmd;
   255     val (tr, proper_cmd) = prepare_span commands cmd;
   251     val proof_trs = map (prepare_span parser) proof |> filter #2 |> map #1;
   256     val proof_trs = map (prepare_span commands) proof |> filter #2 |> map #1;
   252   in
   257   in
   253     if proper_cmd andalso proper_proof then [(tr, proof_trs)]
   258     if proper_cmd andalso proper_proof then [(tr, proof_trs)]
   254     else map (rpair []) (if proper_cmd then tr :: proof_trs else proof_trs)
   259     else map (rpair []) (if proper_cmd then tr :: proof_trs else proof_trs)
   255   end;
   260   end;
   256 
   261 
   257 fun prepare_command pos str =
   262 fun prepare_command pos str =
   258   let val (lexs, parser) = get_syntax () in
   263   let val (lexs, commands) = get_syntax () in
   259     (case ThyEdit.parse_spans lexs pos str of
   264     (case ThyEdit.parse_spans lexs pos str of
   260       [span] => #1 (prepare_span parser span)
   265       [span] => #1 (prepare_span commands span)
   261     | _ => Toplevel.malformed pos not_singleton)
   266     | _ => Toplevel.malformed pos not_singleton)
   262   end;
   267   end;
   263 
   268 
   264 
   269 
   265 (* load_thy *)
   270 (* load_thy *)
   266 
   271 
   267 fun load_thy name pos text time =
   272 fun load_thy name pos text time =
   268   let
   273   let
   269     val (lexs, parser) = get_syntax ();
   274     val (lexs, commands) = get_syntax ();
   270 
   275 
   271     val _ = Present.init_theory name;
   276     val _ = Present.init_theory name;
   272 
   277 
   273     val toks = Source.exhausted (ThyEdit.token_source lexs pos (Source.of_list text));
   278     val toks = Source.exhausted (ThyEdit.token_source lexs pos (Source.of_list text));
   274     val spans = Source.exhaust (ThyEdit.span_source toks);
   279     val spans = Source.exhaust (ThyEdit.span_source toks);
   275     val _ = List.app ThyEdit.report_span spans;
   280     val _ = List.app ThyEdit.report_span spans;
   276     val units = Source.exhaust (ThyEdit.unit_source (Source.of_list spans))
   281     val units = Source.exhaust (ThyEdit.unit_source (Source.of_list spans))
   277       |> maps (prepare_unit parser);
   282       |> maps (prepare_unit commands);
   278 
   283 
   279     val _ = Present.theory_source name
   284     val _ = Present.theory_source name
   280       (fn () => HTML.html_mode (implode o map ThyEdit.present_span) spans);
   285       (fn () => HTML.html_mode (implode o map ThyEdit.present_span) spans);
   281 
   286 
   282     val _ = if time then writeln ("\n**** Starting theory " ^ quote name ^ " ****") else ();
   287     val _ = if time then writeln ("\n**** Starting theory " ^ quote name ^ " ****") else ();