src/Pure/Isar/outer_syntax.ML
author wenzelm
Sat Aug 13 20:49:41 2011 +0200 (2011-08-13 ago)
changeset 44187 88d770052bac
parent 44186 806f0ec1a43d
child 44357 5f5649ac8235
permissions -rw-r--r--
simplified Toplevel.init_theory: discontinued special name argument;
     1 (*  Title:      Pure/Isar/outer_syntax.ML
     2     Author:     Markus Wenzel, TU Muenchen
     3 
     4 The global Isabelle/Isar outer syntax.
     5 
     6 Note: the syntax for files is statically determined at the very
     7 beginning; for interactive processing it may change dynamically.
     8 *)
     9 
    10 signature OUTER_SYNTAX =
    11 sig
    12   type outer_syntax
    13   val is_markup: outer_syntax -> Thy_Output.markup -> string -> bool
    14   val get_syntax: unit -> (Scan.lexicon * Scan.lexicon) * outer_syntax
    15   val command: string -> string -> Keyword.T ->
    16     (Toplevel.transition -> Toplevel.transition) parser -> unit
    17   val markup_command: Thy_Output.markup -> string -> string -> Keyword.T ->
    18     (Toplevel.transition -> Toplevel.transition) parser -> unit
    19   val improper_command: string -> string -> Keyword.T ->
    20     (Toplevel.transition -> Toplevel.transition) parser -> unit
    21   val internal_command: string ->
    22     (Toplevel.transition -> Toplevel.transition) parser -> unit
    23   val local_theory': string -> string -> Keyword.T ->
    24     (bool -> local_theory -> local_theory) parser -> unit
    25   val local_theory: string -> string -> Keyword.T ->
    26     (local_theory -> local_theory) parser -> unit
    27   val local_theory_to_proof': string -> string -> Keyword.T ->
    28     (bool -> local_theory -> Proof.state) parser -> unit
    29   val local_theory_to_proof: string -> string -> Keyword.T ->
    30     (local_theory -> Proof.state) parser -> unit
    31   val print_outer_syntax: unit -> unit
    32   val scan: Position.T -> string -> Token.T list
    33   val parse: Position.T -> string -> Toplevel.transition list
    34   val process_file: Path.T -> theory -> theory
    35   type isar
    36   val isar: TextIO.instream -> bool -> isar
    37   val prepare_span: outer_syntax -> Thy_Syntax.span -> Toplevel.transition * bool
    38   val prepare_element: outer_syntax -> (unit -> theory) -> Thy_Syntax.element ->
    39     (Toplevel.transition * Toplevel.transition list) list
    40   val prepare_command: Position.T -> string -> Toplevel.transition
    41 end;
    42 
    43 structure Outer_Syntax: OUTER_SYNTAX =
    44 struct
    45 
    46 (** outer syntax **)
    47 
    48 (* command parsers *)
    49 
    50 datatype command = Command of
    51  {comment: string,
    52   markup: Thy_Output.markup option,
    53   int_only: bool,
    54   parse: (Toplevel.transition -> Toplevel.transition) parser};
    55 
    56 fun make_command comment markup int_only parse =
    57   Command {comment = comment, markup = markup, int_only = int_only, parse = parse};
    58 
    59 
    60 (* parse command *)
    61 
    62 local
    63 
    64 fun terminate false = Scan.succeed ()
    65   | terminate true = Parse.group "end of input" (Scan.option Parse.sync -- Parse.semicolon >> K ());
    66 
    67 fun body cmd (name, _) =
    68   (case cmd name of
    69     SOME (Command {int_only, parse, ...}) =>
    70       Parse.!!! (Scan.prompt (name ^ "# ") (Parse.tags |-- parse >> pair int_only))
    71   | NONE => raise Fail ("No parser for outer syntax command " ^ quote name));
    72 
    73 in
    74 
    75 fun parse_command do_terminate cmd =
    76   Parse.semicolon >> K NONE ||
    77   Parse.sync >> K NONE ||
    78   (Parse.position Parse.command :-- body cmd) --| terminate do_terminate
    79     >> (fn ((name, pos), (int_only, f)) =>
    80       SOME (Toplevel.empty |> Toplevel.name name |> Toplevel.position pos |>
    81         Toplevel.interactive int_only |> f));
    82 
    83 end;
    84 
    85 
    86 (* type outer_syntax *)
    87 
    88 datatype outer_syntax = Outer_Syntax of
    89  {commands: command Symtab.table,
    90   markups: (string * Thy_Output.markup) list};
    91 
    92 fun make_outer_syntax commands markups =
    93   Outer_Syntax {commands = commands, markups = markups};
    94 
    95 val empty_outer_syntax = make_outer_syntax Symtab.empty [];
    96 
    97 
    98 fun map_commands f (Outer_Syntax {commands, ...}) =
    99   let
   100     val commands' = f commands;
   101     val markups' =
   102       Symtab.fold (fn (name, Command {markup = SOME m, ...}) => cons (name, m) | _ => I)
   103         commands' [];
   104   in make_outer_syntax commands' markups' end;
   105 
   106 fun dest_commands (Outer_Syntax {commands, ...}) =
   107   commands |> Symtab.dest |> sort_wrt #1
   108   |> map (fn (name, Command {comment, int_only, ...}) => (name, comment, int_only));
   109 
   110 fun lookup_commands (Outer_Syntax {commands, ...}) = Symtab.lookup commands;
   111 
   112 fun is_markup (Outer_Syntax {markups, ...}) kind name =
   113   AList.lookup (op =) markups name = SOME kind;
   114 
   115 
   116 
   117 (** global outer syntax **)
   118 
   119 local
   120 
   121 (*synchronized wrt. Keywords*)
   122 val global_outer_syntax = Unsynchronized.ref empty_outer_syntax;
   123 
   124 fun add_command name kind cmd = CRITICAL (fn () =>
   125  (Keyword.command name kind;
   126   Unsynchronized.change global_outer_syntax (map_commands (fn commands =>
   127    (if not (Symtab.defined commands name) then ()
   128     else warning ("Redefining outer syntax command " ^ quote name);
   129     Symtab.update (name, cmd) commands)))));
   130 
   131 in
   132 
   133 fun get_syntax () = CRITICAL (fn () => (Keyword.get_lexicons (), ! global_outer_syntax));
   134 
   135 fun lookup_commands_dynamic () = lookup_commands (! global_outer_syntax);
   136 
   137 fun command name comment kind parse =
   138   add_command name kind (make_command comment NONE false parse);
   139 
   140 fun markup_command markup name comment kind parse =
   141   add_command name kind (make_command comment (SOME markup) false parse);
   142 
   143 fun improper_command name comment kind parse =
   144   add_command name kind (make_command comment NONE true parse);
   145 
   146 fun internal_command name parse =
   147   command name "(internal)" Keyword.control (parse >> (fn tr => Toplevel.no_timing o tr));
   148 
   149 end;
   150 
   151 
   152 (* local_theory commands *)
   153 
   154 fun local_theory_command do_print trans name comment kind parse =
   155   command name comment kind (Parse.opt_target -- parse
   156     >> (fn (loc, f) => (if do_print then Toplevel.print else I) o trans loc f));
   157 
   158 val local_theory' = local_theory_command false Toplevel.local_theory';
   159 val local_theory = local_theory_command false Toplevel.local_theory;
   160 val local_theory_to_proof' = local_theory_command true Toplevel.local_theory_to_proof';
   161 val local_theory_to_proof = local_theory_command true Toplevel.local_theory_to_proof;
   162 
   163 
   164 (* inspect syntax *)
   165 
   166 fun print_outer_syntax () =
   167   let
   168     val (keywords, outer_syntax) =
   169       CRITICAL (fn () => (Keyword.dest_keywords (), #2 (get_syntax ())));
   170     fun pretty_cmd (name, comment, _) =
   171       Pretty.block [Pretty.str (name ^ ":"), Pretty.brk 2, Pretty.str comment];
   172     val (int_cmds, cmds) = List.partition #3 (dest_commands outer_syntax);
   173   in
   174     [Pretty.strs ("syntax keywords:" :: map quote keywords),
   175       Pretty.big_list "commands:" (map pretty_cmd cmds),
   176       Pretty.big_list "interactive-only commands:" (map pretty_cmd int_cmds)]
   177     |> Pretty.chunks |> Pretty.writeln
   178   end;
   179 
   180 
   181 
   182 (** toplevel parsing **)
   183 
   184 (* basic sources *)
   185 
   186 fun toplevel_source term do_recover cmd src =
   187   let
   188     val no_terminator =
   189       Scan.unless Parse.semicolon (Scan.one (Token.not_sync andf Token.not_eof));
   190     fun recover int =
   191       (int, fn _ => Scan.prompt "recover# " (Scan.repeat no_terminator) >> K [NONE]);
   192   in
   193     src
   194     |> Token.source_proper
   195     |> Source.source Token.stopper
   196       (Scan.bulk (Parse.$$$ "--" -- Parse.!!! Parse.doc_source >> K NONE || Parse.not_eof >> SOME))
   197         (Option.map recover do_recover)
   198     |> Source.map_filter I
   199     |> Source.source Token.stopper
   200         (Scan.bulk (fn xs => Parse.!!! (parse_command term (cmd ())) xs))
   201         (Option.map recover do_recover)
   202     |> Source.map_filter I
   203   end;
   204 
   205 
   206 (* off-line scanning/parsing *)
   207 
   208 fun scan pos str =
   209   Source.of_string str
   210   |> Symbol.source
   211   |> Token.source {do_recover = SOME false} Keyword.get_lexicons pos
   212   |> Source.exhaust;
   213 
   214 fun parse pos str =
   215   Source.of_string str
   216   |> Symbol.source
   217   |> Token.source {do_recover = SOME false} Keyword.get_lexicons pos
   218   |> toplevel_source false NONE lookup_commands_dynamic
   219   |> Source.exhaust;
   220 
   221 
   222 (* process file *)
   223 
   224 fun process_file path thy =
   225   let
   226     val trs = parse (Path.position path) (File.read path);
   227     val init = Toplevel.init_theory (K thy) Toplevel.empty;
   228     val result = fold Toplevel.command (init :: trs) Toplevel.toplevel;
   229   in
   230     (case (Toplevel.is_theory result, Toplevel.generic_theory_of result) of
   231       (true, Context.Theory thy') => thy'
   232     | _ => error "Bad result state: global theory expected")
   233   end;
   234 
   235 
   236 (* interactive source of toplevel transformers *)
   237 
   238 type isar =
   239   (Toplevel.transition, (Toplevel.transition option,
   240     (Token.T, (Token.T option, (Token.T, (Token.T,
   241       (Symbol_Pos.T, Position.T * (Symbol.symbol, (string, unit) Source.source)
   242   Source.source) Source.source) Source.source) Source.source)
   243   Source.source) Source.source) Source.source) Source.source;
   244 
   245 fun isar in_stream term : isar =
   246   Source.tty in_stream
   247   |> Symbol.source
   248   |> Token.source {do_recover = SOME true} Keyword.get_lexicons Position.none
   249   |> toplevel_source term (SOME true) lookup_commands_dynamic;
   250 
   251 
   252 (* prepare toplevel commands -- fail-safe *)
   253 
   254 val not_singleton = "Exactly one command expected";
   255 
   256 fun prepare_span outer_syntax span =
   257   let
   258     val commands = lookup_commands outer_syntax;
   259     val range_pos = Position.set_range (Thy_Syntax.span_range span);
   260     val toks = Thy_Syntax.span_content span;
   261     val _ = List.app Thy_Syntax.report_token toks;
   262   in
   263     (case Source.exhaust (toplevel_source false NONE (K commands) (Source.of_list toks)) of
   264       [tr] =>
   265         if Keyword.is_control (Toplevel.name_of tr) then
   266           (Toplevel.malformed range_pos "Illegal control command", true)
   267         else (tr, true)
   268     | [] => (Toplevel.ignored range_pos, false)
   269     | _ => (Toplevel.malformed range_pos not_singleton, true))
   270     handle ERROR msg => (Toplevel.malformed range_pos msg, true)
   271   end;
   272 
   273 fun prepare_element outer_syntax init {head, proof, proper_proof} =
   274   let
   275     val (tr, proper_head) = prepare_span outer_syntax head |>> Toplevel.modify_init init;
   276     val proof_trs = map (prepare_span outer_syntax) proof |> filter #2 |> map #1;
   277   in
   278     if proper_head andalso proper_proof then [(tr, proof_trs)]
   279     else map (rpair []) (if proper_head then tr :: proof_trs else proof_trs)
   280   end;
   281 
   282 fun prepare_command pos str =
   283   let val (lexs, outer_syntax) = get_syntax () in
   284     (case Thy_Syntax.parse_spans lexs pos str of
   285       [span] => #1 (prepare_span outer_syntax span)
   286     | _ => Toplevel.malformed pos not_singleton)
   287   end;
   288 
   289 end;
   290