src/Pure/Tools/check_keywords.scala
author wenzelm
Tue, 24 Sep 2024 17:41:05 +0200
changeset 80939 3eca969b3c43
parent 80886 5d562dd387ae
permissions -rw-r--r--
tuned;

/*  Title:      Pure/Tools/check_keywords.scala
    Author:     Makarius

Check theory sources for conflicts with proposed keywords.
*/

package isabelle


object Check_Keywords {
  /* session theories for keyword conflicts */

  def check_keywords(
    options: Options,
    check: Set[String],
    dirs: List[Path] = Nil,
    progress: Progress,
  ): Unit = {
    val deps = Sessions.deps(Sessions.load_structure(options, dirs = dirs), progress = progress)

    val bad =
      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)
        }
      })
}