src/Pure/Tools/check_keywords.scala
changeset 80886 5d562dd387ae
parent 80884 f097ca0989e0
--- a/src/Pure/Tools/check_keywords.scala	Mon Sep 16 15:39:35 2024 +0200
+++ b/src/Pure/Tools/check_keywords.scala	Mon Sep 16 15:49:36 2024 +0200
@@ -8,45 +8,88 @@
 
 
 object Check_Keywords {
-  def conflicts(
-    keywords: Keyword.Keywords,
-    check: Set[String],
-    input: CharSequence,
-    start: Token.Pos
-  ): List[(Token, Position.T)] = {
-    object Parsers extends Parse.Parsers {
-      private val conflict =
-        position(token("token", tok => !(tok.is_command || tok.is_keyword) && check(tok.source)))
-      private val other = token("token", _ => true)
-      private val item = conflict ^^ (x => Some(x)) | other ^^ (_ => None)
-
-      val result: List[(Token, Position.T)] =
-        parse_all(rep(item), Token.reader(Token.explode(keywords, input), start)) match {
-          case Success(res, _) => for (case Some(x) <- res) yield x
-          case bad => error(bad.toString)
-        }
-    }
-    Parsers.result
-  }
+  /* session theories for keyword conflicts */
 
   def check_keywords(
-    progress: Progress,
-    keywords: Keyword.Keywords,
+    options: Options,
     check: Set[String],
-    paths: List[Path]
+    dirs: List[Path] = Nil,
+    progress: Progress,
   ): Unit = {
-    val parallel_args =
-      paths.map(path => (File.read(path), Token.Pos.file(File.standard_path(path))))
+    val deps = Sessions.deps(Sessions.load_structure(options, dirs = dirs), progress = progress)
 
     val bad =
-      Par_List.map({ (arg: (String, Token.Pos)) =>
-        progress.expose_interrupt()
-        conflicts(keywords, check, arg._1, arg._2)
-      }, parallel_args).flatten
+      Par_List.maps[(Keyword.Keywords, String, Token.Pos), (Token, Position.T)](
+        { case (keywords, input, start) =>
+          progress.expose_interrupt()
+
+          object Parsers extends Parse.Parsers {
+            private val conflict =
+              position(token("token", tok => !(tok.is_command || tok.is_keyword) && check(tok.source)))
+            private val other = token("token", _ => true)
+            private val item = conflict ^^ (x => Some(x)) | other ^^ (_ => None)
+
+            val result: List[(Token, Position.T)] =
+              parse_all(rep(item), Token.reader(Token.explode(keywords, input), start)) match {
+                case Success(res, _) => for (case Some(x) <- res) yield x
+                case bad => error(bad.toString)
+              }
+          }
+          Parsers.result
+        },
+        List.from(
+          for {
+            session <- deps.sessions_structure.imports_topological_order.iterator
+            base = deps(session)
+            node_name <- base.proper_session_theories
+          } yield {
+            val path = node_name.path
+            val input = File.read(path)
+            val start = Token.Pos.file(File.standard_path(path))
+            (base.overall_syntax.keywords, input, start)
+          })
+      )
 
     for ((tok, pos) <- bad) {
       progress.echo_warning(
         "keyword conflict: " + tok.kind.toString + " " + quote(tok.content) + Position.here(pos))
     }
   }
+
+
+  /* Isabelle tool wrapper */
+
+  val isabelle_tool =
+    Isabelle_Tool("check_keywords", "check theory sources for conflicts with proposed keywords",
+      Scala_Project.here,
+      { args =>
+        var dirs: List[Path] = Nil
+        var options = Options.init()
+        var verbose = false
+
+        val getopts = Getopts("""
+Usage: isabelle check_keywords [OPTIONS] KEYWORDS
+
+  Options are:
+    -d DIR       include session directory
+    -o OPTION    override Isabelle system OPTION (via NAME=VAL or NAME)
+    -v           verbose
+
+  Check theory sources from all session directories for conflicts with
+  newly proposed keywords (outer syntax). This reveals occurrences of
+  identifiers that would have to be quoted.
+""",
+        "d:" -> (arg => dirs = dirs ::: List(Path.explode(arg))),
+        "o:" -> (arg => options = options + arg),
+        "v" -> (_ => verbose = true))
+
+        val keywords = getopts(args)
+        if (keywords.isEmpty) getopts.usage()
+
+        val progress = new Console_Progress(verbose = verbose)
+
+        progress.interrupt_handler {
+          check_keywords(options, keywords.toSet, dirs = dirs, progress = progress)
+        }
+      })
 }