support "tag" marker with scope;
authorwenzelm
Fri, 12 Apr 2019 17:09:21 +0200
changeset 70134 e69f00f4b897
parent 70130 c7866e763e9f
child 70135 ad6d4a14adb5
support "tag" marker with scope;
src/Pure/Isar/outer_syntax.ML
src/Pure/Isar/toplevel.ML
src/Pure/Thy/document_marker.ML
src/Pure/Thy/document_source.ML
src/Pure/Thy/thy_header.ML
src/Pure/Thy/thy_output.ML
--- a/src/Pure/Isar/outer_syntax.ML	Thu Apr 11 21:33:21 2019 +0200
+++ b/src/Pure/Isar/outer_syntax.ML	Fri Apr 12 17:09:21 2019 +0200
@@ -233,7 +233,7 @@
         |> Keyword.is_proof_open keywords name ? Toplevel.skip_proof_open
         |> Keyword.is_proof_close keywords name ? Toplevel.skip_proof_close;
       val command_markers =
-        Parse.command |-- Document_Source.tags >>
+        Parse.command |-- Document_Source.old_tags >>
           (fn tags => Toplevel.markers (map Document_Marker.legacy_tag tags @ markers) tr0);
     in
       (case lookup_commands thy name of
--- a/src/Pure/Isar/toplevel.ML	Thu Apr 11 21:33:21 2019 +0200
+++ b/src/Pure/Isar/toplevel.ML	Fri Apr 12 17:09:21 2019 +0200
@@ -312,18 +312,18 @@
           | exn as FAILURE _ => raise exn
           | exn => raise FAILURE (state, exn);
 
-fun apply_markers markers (state as State ((node, pr_ctxt), prev_thy)) =
+fun apply_markers name markers (state as State ((node, pr_ctxt), prev_thy)) =
   let
     val state' =
       Runtime.controlled_execution (try generic_theory_of state)
-        (fn () => State ((node, fold Document_Marker.evaluate markers pr_ctxt), prev_thy)) ();
+        (fn () => State ((node, fold (Document_Marker.evaluate name) markers pr_ctxt), prev_thy)) ();
   in (state', NONE) end
   handle exn => (state, SOME exn);
 
 in
 
-fun apply_trans int trans markers state =
-  (apply_union int trans state |> apply_markers markers)
+fun apply_trans int name markers trans state =
+  (apply_union int trans state |> apply_markers name markers)
   handle FAILURE (alt_state, exn) => (alt_state, SOME exn) | exn => (state, SOME exn);
 
 end;
@@ -616,9 +616,9 @@
 
 local
 
-fun app int (tr as Transition {markers, trans, ...}) =
+fun app int (tr as Transition {name, markers, trans, ...}) =
   setmp_thread_position tr
-    (Timing.protocol (name_of tr) (pos_of tr) (apply_trans int trans markers)
+    (Timing.protocol (name_of tr) (pos_of tr) (apply_trans int name markers trans)
       ##> Option.map (fn UNDEF => ERROR (type_error tr) | exn => exn));
 
 in
--- a/src/Pure/Thy/document_marker.ML	Thu Apr 11 21:33:21 2019 +0200
+++ b/src/Pure/Thy/document_marker.ML	Fri Apr 12 17:09:21 2019 +0200
@@ -10,7 +10,8 @@
   val check: Proof.context -> string * Position.T -> string * (Token.src -> marker)
   val setup: binding -> 'a context_parser -> ('a -> marker) -> theory -> theory
   val setup0: binding -> 'a parser -> ('a -> marker) -> theory -> theory
-  val evaluate: Input.source -> marker
+  val evaluate: string -> Input.source -> marker
+  val command_name: Proof.context -> string
   val legacy_tag: string -> Input.source
 end;
 
@@ -45,9 +46,12 @@
 
 (* evaluate inner syntax *)
 
+val config_command_name = Config.declare_string ("command_name", \<^here>) (K "");
+val command_name = Config.apply config_command_name;
+
 val parse_marker = Parse.token Parse.liberal_name ::: Parse.!!! Parse.args;
 
-fun evaluate input ctxt =
+fun evaluate cmd_name input ctxt =
   let
     val syms = Input.source_explode input
       |> drop_prefix (fn (s, _) => s = Symbol.marker);
@@ -60,7 +64,26 @@
       (case Token.read_body (Thy_Header.get_keywords' ctxt) (Parse.list parse_marker) body of
         SOME res => res
       | NONE => error ("Bad input" ^ Position.here pos));
-  in fold (fn src => #2 (check ctxt (Token.name_of_src src)) src) markers ctxt end;
+  in
+    ctxt
+    |> Config.put config_command_name cmd_name
+    |> fold (fn src => #2 (check ctxt (Token.name_of_src src)) src) markers
+    |> Config.put config_command_name (command_name ctxt)
+  end;
+
+
+(* tag with scope *)
+
+val parse_tag =
+  Scan.state :|-- (fn context =>
+    let
+      val ctxt = Context.proof_of context;
+      val scope0 =
+        if Keyword.is_theory_goal (Thy_Header.get_keywords' ctxt) (command_name ctxt)
+        then Document_Source.Command
+        else Document_Source.Proof;
+      val tag = Scan.optional Document_Source.tag_scope scope0 -- Document_Source.tag_name >> swap;
+    in Scan.lift tag end);
 
 
 (* concrete markers *)
@@ -70,7 +93,7 @@
 
 val _ =
   Theory.setup
-    (setup0 (Binding.make ("tag", \<^here>)) Parse.name Document_Source.update_tags #>
+    (setup (Binding.make ("tag", \<^here>)) parse_tag Document_Source.update_tags #>
      setup0 (Binding.make ("title", \<^here>)) Parse.embedded (meta_data Markup.meta_title) #>
      setup0 (Binding.make ("creator", \<^here>)) Parse.embedded (meta_data Markup.meta_creator) #>
      setup0 (Binding.make ("contributor", \<^here>)) Parse.embedded (meta_data Markup.meta_contributor) #>
@@ -84,6 +107,6 @@
         in meta_data Markup.meta_description arg ctxt end));
 
 fun legacy_tag name =
-  Input.string (cartouche ("tag " ^ Symbol_Pos.quote_string_qq name));
+  Input.string (cartouche ("tag (proof) " ^ Symbol_Pos.quote_string_qq name));
 
 end;
--- a/src/Pure/Thy/document_source.ML	Thu Apr 11 21:33:21 2019 +0200
+++ b/src/Pure/Thy/document_source.ML	Fri Apr 12 17:09:21 2019 +0200
@@ -14,9 +14,16 @@
   val improper: Token.T list parser
   val improper_end: Token.T list parser
   val blank_end: Token.T list parser
-  val get_tags: Proof.context -> string list
-  val update_tags: string -> Proof.context -> Proof.context
-  val tags: string list parser
+  datatype scope = Command | Proof
+  type tag = string * scope
+  val eq_tag: tag * tag -> bool
+  val update_tags: tag -> Proof.context -> Proof.context
+  val get_tags: Proof.context -> tag list
+  type tagging = tag list
+  val update_tagging: Proof.context -> tagging -> tag option * tagging
+  val tag_scope: scope parser
+  val tag_name: string parser
+  val old_tags: string list parser
   val annotation: unit parser
 end;
 
@@ -43,27 +50,62 @@
 val blank_end = Scan.repeat (Scan.unless space_proper (Scan.one Token.is_blank));
 
 
-(* syntactic tags (old-style) *)
+(** syntactic tags **)
+
+(* scope *)
+
+datatype scope = Command | Proof;
+
+type tag = string * scope;
+val eq_tag: tag * tag -> bool = eq_fst op =;
+
+
+(* context data *)
 
 structure Tags = Proof_Data
 (
-  type T = string list;
+  type T = tag list;
   fun init _ = [];
 );
 
+val update_tags = Tags.map o update eq_tag;
+
 val get_tags = Tags.get;
-val update_tags = Tags.map o update (op =);
+
+
+(* command tagging *)
+
+type tagging = tag list;
+
+fun update_tagging ctxt tagging =
+  let
+    val tagging' = fold (update eq_tag) (get_tags ctxt) tagging;
+    val nested_tagging' = filter (fn (_, scope) => scope = Proof) tagging';
+  in (try hd tagging', nested_tagging') end;
+
 
-val tag_name = Parse.group (fn () => "document tag name") (Parse.short_ident || Parse.string);
+(* parse scope and name *)
+
+val scope = Parse.reserved "command" >> K Command || Parse.reserved "proof" >> K Proof;
+
+val tag_scope =
+  Parse.group (fn () => "document tag scope") (Parse.$$$ "(" |-- Parse.!!! (scope --| Parse.$$$ ")"));
 
-val tag = (improper -- Parse.$$$ "%" -- improper) |-- Parse.!!! (tag_name --| blank_end);
-val tags = Scan.repeat tag;
+val tag_name =
+  Parse.group (fn () => "document tag name") (Parse.short_ident || Parse.string);
+
+
+(* syntactic tags (old-style) *)
+
+val old_tag = (improper -- Parse.$$$ "%" -- improper) |-- Parse.!!! (tag_name --| blank_end);
+
+val old_tags = Scan.repeat old_tag;
 
 
 (* semantic markers (operation on presentation context) *)
 
 val marker = improper |-- Parse.document_marker --| blank_end;
 
-val annotation = Scan.repeat (tag >> K () || marker >> K ()) >> K ();
+val annotation = Scan.repeat (old_tag >> K () || marker >> K ()) >> K ();
 
 end;
--- a/src/Pure/Thy/thy_header.ML	Thu Apr 11 21:33:21 2019 +0200
+++ b/src/Pure/Thy/thy_header.ML	Fri Apr 12 17:09:21 2019 +0200
@@ -137,7 +137,7 @@
 
 val keyword_spec =
   Parse.group (fn () => "outer syntax keyword specification")
-    (Parse.name -- opt_files -- Document_Source.tags);
+    (Parse.name -- opt_files -- Document_Source.old_tags);
 
 val keyword_decl =
   Scan.repeat1 Parse.string_position --
--- a/src/Pure/Thy/thy_output.ML	Thu Apr 11 21:33:21 2019 +0200
+++ b/src/Pure/Thy/thy_output.ML	Fri Apr 12 17:09:21 2019 +0200
@@ -240,21 +240,22 @@
 fun open_delim delim e = edge #2 Latex.begin_delim e #> delim #> edge #2 Latex.end_delim e;
 fun close_delim delim e = edge #1 Latex.begin_delim e #> delim #> edge #1 Latex.end_delim e;
 
-fun document_tag cmd_pos state state' tag_stack =
+fun document_tag cmd_pos state state' tagging_stack =
   let
     val ctxt' = Toplevel.presentation_context state';
     val nesting = Toplevel.level state' - Toplevel.level state;
 
-    val (tag, tags) = tag_stack;
-    val tag' = try hd (fold (update (op =)) (Document_Source.get_tags ctxt') (the_list tag));
-    val tag_stack' =
-      if nesting = 0 andalso not (Toplevel.is_proof state) then tag_stack
-      else if nesting >= 0 then (tag', replicate nesting tag @ tags)
+    val (tagging, taggings) = tagging_stack;
+    val (tag', tagging') = Document_Source.update_tagging ctxt' tagging;
+
+    val tagging_stack' =
+      if nesting = 0 andalso not (Toplevel.is_proof state) then tagging_stack
+      else if nesting >= 0 then (tagging', replicate nesting tagging @ taggings)
       else
-        (case drop (~ nesting - 1) tags of
+        (case drop (~ nesting - 1) taggings of
           tg :: tgs => (tg, tgs)
         | [] => err_bad_nesting (Position.here cmd_pos));
-  in (tag', tag_stack') end;
+  in (tag', tagging_stack') end;
 
 fun read_tag s =
   (case space_explode "%" s of
@@ -290,7 +291,7 @@
   end;
 
 fun present_span command_tag span state state'
-    (tag_stack, active_tag, newline, latex, present_cont) =
+    (tagging_stack, active_tag, newline, latex, present_cont) =
   let
     val ctxt' = Toplevel.presentation_context state';
     val present = fold (fn (tok, (flag, 0)) =>
@@ -300,8 +301,10 @@
 
     val Span ((cmd_name, cmd_pos), srcs, span_newline) = span;
 
-    val (tag', tag_stack') = document_tag cmd_pos state state' tag_stack;
-    val active_tag' = if is_some tag' then tag' else command_tag cmd_name state state' active_tag;
+    val (tag', tagging_stack') = document_tag cmd_pos state state' tagging_stack;
+    val active_tag' =
+      if is_some tag' then Option.map #1 tag'
+      else command_tag cmd_name state state' active_tag;
     val edge = (active_tag, active_tag');
 
     val newline' =
@@ -318,7 +321,7 @@
     val present_cont' =
       if newline then (present (#3 srcs), present (#4 srcs))
       else (I, present (#3 srcs) #> present (#4 srcs));
-  in (tag_stack', active_tag', newline', latex', present_cont') end;
+  in (tagging_stack', active_tag', newline', latex', present_cont') end;
 
 fun present_trailer ((_, tags), active_tag, _, latex, present_cont) =
   if not (null tags) then err_bad_nesting " at end of theory"
@@ -447,7 +450,7 @@
       | present st ((span, (tr, st')) :: rest) = present_command tr span st st' #> present st' rest;
   in
     if length command_results = length spans then
-      ((NONE, []), NONE, true, [], (I, I))
+      (([], []), NONE, true, [], (I, I))
       |> present (Toplevel.init_toplevel ()) (spans ~~ command_results)
       |> present_trailer
       |> rev