Quellcodebibliothek Statistik Leitseite products/Sources/formale Sprachen/Isabelle/Pure/General/   (Beweissystem Isabelle Version 2025-1©)  Datei vom 16.11.2025 mit Größe 24 kB image not shown  

Quelle  mailman.scala   Sprache: Scala

 
/*  Title:      Pure/General/mailman.scala
    Author:     Makarius

Support for Mailman list servers, notably isabelle-users and isabelle-dev.
*/


package isabelle


import scala.annotation.tailrec
import scala.util.matching.Regex
import scala.util.matching.Regex.Match


object Mailman {
  /* mailing list messages */

  def is_address(s: String): Boolean =
    s.contains('@') && s.contains('.') && !s.contains(' ')

  private val standard_name: Map[String, String] =
    Map(
      "121171528:qq/com" -> "Guo Fan\n121171528:qq/com",
      "Aman Pohjola, Johannes (Data61, Kensington NSW)" -> "Johannes Aman Pohjola",
      "Andrei de AraÃjo Formiga" -> "Andrei de Araujo Formiga",
      "Benedikt/AHRENS:unice/fr" -> "Benedikt Ahrens\nBenedikt/AHRENS:unice/fr",
      "Berg, Nils Erik" -> "Nils Erik Berg",
      "Berger U/" -> "Ulrich Berger",
      "Bisping, Benjamin" -> "Benjamin Bisping",
      "Blanchette, J/C/" -> "Jasmin Christian Blanchette",
      "Buday Gergely István" -> "Gergely Buday",
      "CALaF1UJ9Uy0vGCu4WkBmbfuPDxG7nFm8hfeCMP+O3g7_5CQ0Bw:mail/gmail/com" -> "",
      "CRACIUN F/" -> "Florin Craciun",
      "Carsten Schuermann" -> "Carsten Schürmann",
      "Chris" -> "",
      "Christoph Lueth" -> "Christoph Lüth",
      "Claude Marche" -> "Claude Marché",
      "Daniel StÃwe" -> "Daniel Stüwe",
      "Daniel/Matichuk:data61/csiro/au" -> "Daniel Matichuk\nDaniel/Matichuk:data61/csiro/au",
      "Daniel/Matichuk:nicta/com/au" -> "Daniel Matichuk\nDaniel/Matichuk:nicta/com/au",
      "David MENTRE" -> "David MENTRÉ",
      "Dey, Katie" -> "Katie Dey",
      "Dr/ Brendan Patrick Mahony" -> "Brendan Mahony",
      "Farn" -> "Farn Wang",
      "Farquhar, Colin I" -> "Colin Farquhar",
      "Fernandez, Matthew" -> "Matthew Fernandez",
      "Filip Maric" -> "Filip Marić",
      "Filip MariÄ" -> "Filip Marić",
      "Fleury Mathias" -> "Mathias Fleury",
      "Francisco Jose CHAVES ALONSO" -> "Francisco Jose Chaves Alonso",
      "Frederic Tuong (Dr)" -> "Frederic Tuong",
      "Fulya" -> "Fulya Horozal",
      "George K/" -> "George Karabotsos",
      "Gidon Ernst" -> "Gidon ERNST",
      "Gransden, Thomas" -> "Thomas Gransden",
      "Hans-JÃrg Schurr" -> "Hans-Jörg Schurr",
      "Henri DEBRAT" -> "Henri Debrat",
      "Hitoshi Ohsaki (RTA publicity chair)" -> "Hitoshi Ohsaki",
      "Häuselmann Rafael" -> "Rafael Häuselmann",
      "Isabelle" -> "",
      "J/ Juhas (TUM)" -> "Jonatan Juhas",
      "Jackson, Vincent (Data61, Kensington NSW)" -> "Vincent Jackson",
      "Janney, Mark-P26816" -> "Mark Janney",
      "Jean François Molderez" -> "Jean-François Molderez",
      "Jean-Francois Molderez" -> "Jean-François Molderez",
      "John R Harrison" -> "John Harrison",
      "Jose DivasÃn" -> "Jose Divasón",
      "Julian" -> "",
      "Julien" -> "",
      "Klein, Gerwin (Data61, Kensington NSW)" -> "Gerwin Klein",
      "Kobayashi, Hidetsune" -> "Hidetsune Kobayashi",
      "Kylie Williams (IND)" -> "Kylie Williams",
      "Laarman, A/W/" -> "A/W/ Laarman",
      "Laurent Thery" -> "Laurent Théry",
      "Li, Chanjuan" -> "Li Chanjuan",
      "Lochbihler Andreas" -> "Andreas Lochbihler",
      "Luckhardt, Daniel" -> "Daniel Luckhardt",
      "Lutz Schroeder" -> "Lutz Schröder",
      "Lutz SchrÃder" -> "Lutz Schröder",
      "MACKENZIE Carlin" -> "Carlin MACKENZIE",
      "Makarius" -> "Makarius Wenzel",
      "Marco" -> "",
      "Mark" -> "",
      "Markus Mueller-Olm" -> "Markus Müller-Olm",
      "Markus" -> "",
      "Marmsoler, Diego" -> "Diego Marmsoler",
      "Martin Klebermass" -> "Martin Klebermaß",
      "Martyn Johnson via RT" -> "",
      "Mathias/Fleury:ens-rennes/fr" -> "Mathias Fleury\nmathias/fleury12:gmail/com",
      "Matthew" -> "",
      "Matthews, John R" -> "John Matthews",
      "McCarthy, Jim (C3ID)" -> "Jim McCarthy",
      "McCue, Brian" -> "Brian McCue",
      "Michael FÃrber" -> "Michael Färber",
      "Michel" -> "",
      "Miranda, Brando" -> "Brando Miranda",
      "Moscato, Mariano M/ \\(LARC-D320\\)\\[NATIONAL INSTITUTE OF AEROSPACE\\]" -> "Mariano M/ Moscato",
      "Mr Julian Fell" -> "Julian Fell",
      "Mueller Peter" -> "Peter Müller",
      "Munoz, Cesar Augusto (LARC-D320)" -> "Cesar A/ Munoz",
      "Nadel, Alexander" -> "Alexander Nadel",
      "Nagashima, Yutaka" -> "Yutaka Nagashima",
      "Norrish, Michael (Data61, Acton)" -> "Michael Norrish",
      "O'Leary, John W" -> "John W O'Leary",
      "Omar Montano Rivas" -> "Omar Montaño Rivas",
      "Omar MontaÃo Rivas" -> "Omar Montaño Rivas",
      "OndÅej KunÄar" -> "Ondřej Kunčar",
      "PALMER Jake" -> "Jake Palmer",
      "PAQUI LUCIO" -> "Paqui Lucio",
      "Pal, Abhik" -> "Abhik Pal",
      "Pasupuleti, Vijay" -> "Vijay Pasupuleti",
      "Peter Vincent Homeier" -> "Peter V/ Homeier",
      "Peter" -> "",
      "Philipp Ruemmer" -> "Philipp Rümmer",
      "Philipp RÃmmer" -> "Philipp Rümmer",
      "Piete Brooks via RT" -> "",
      "RTA publicity chair" -> "",
      "Raamsdonk, F/ van" -> "Femke van Raamsdonk",
      "Raul Gutierrez" -> "Raúl Gutiérrez",
      "Renà Thiemann" -> "René Thiemann",
      "Ridgway, John V/ E/" -> "John V/ E/ Ridgway",
      "Roggenbach M/" -> "Markus Roggenbach",
      "Rosu, Grigore" -> "Grigore Rosu",
      "Rozman, Mihaela" -> "Mihaela Rozman",
      "Schmaltz, J/" -> "Julien Schmaltz",
      "Serguei A/ Mokhov on behalf of PST-11" -> "Serguei A/ Mokhov",
      "Serguei Mokhov" -> "Serguei A/ Mokhov",
      "Shumeiko, Igor" -> "Igor Shumeiko",
      "Siek, Jeremy" -> "Jeremy Siek",
      "Silvio/Ranise:loria/fr" -> "Silvio Ranise\nSilvio/Ranise:loria/fr",
      "Siu, Tony" -> "Tony Siu",
      "Stüber, Sebastian" -> "Sebastian Stüber",
      "Thiemann, Rene" -> "René Thiemann",
      "Thiemann, René" -> "René Thiemann",
      "Thomas Arthur Leck Sewell" -> "Thomas Sewell",
      "Thomas Goethel" -> "Thomas Göthel",
      "Thomas/Sewell:data61/csiro/au" -> "Thomas Sewell\nThomas/Sewell:data61/csiro/au",
      "Tjark Weber via RT" -> "Tjark Weber",
      "Toby/Murray:data61/csiro/au" -> "Toby Murray\nToby/Murray:data61/csiro/au",
      "Urban, Christian" -> "Christian Urban",
      "Ursula Eschbach" -> "",
      "Van Staden Stephan" -> "Stephan van Staden",
      "Viktor Kuncak" -> "Viktor Kunčak",
      "Viorel Preoteasaa" -> "Viorel Preoteasa",
      "Wickerson, John P" -> "John Wickerson",
      "Wong, Yat" -> "Yat Wong",
      "YAMADA, Akihisa" -> "Akihisa Yamada",
      "YliÃs Falcone" -> "Yliès Falcone",
      "amir mohajeri" -> "Amir Mohajeri",
      "aniello murano" -> "Aniello Murano",
      "barzan stefania" -> "Stefania Barzan",
      "benhamou" -> "Belaid Benhamou",
      "charmi panchal" -> "Charmi Panchal",
      "chen kun" -> "Chen Kun",
      "chunhan wu" -> "Chunhan Wu",
      "daniel de la concepción sáez" -> "Daniel de la Concepción Sáez",
      "daniel/luckhardt:mathematik/uni-goettingen/de" -> "Logiker:gmx/net",
      "david streader" -> "David Streader",
      "eschbach:in/tum/de" -> "",
      "f/rabe:jacobs-university/de" -> "florian/rabe:fau/de",
      "florian:haftmann-online/de" -> "haftmann:in/tum/de",
      "fredegar:haftmann-online/de" -> "haftmann:in/tum/de",
      "gallais : ensl/org" -> "Guillaume Allais",
      "geng chen" -> "Geng Chen",
      "henning/seidler" -> "Henning Seidler",
      "hkb" -> "Hidetsune Kobayashi",
      "jobs-pm:inf/ethz/ch" -> "",
      "julien:RadboudUniversity" -> "",
      "jun sun" -> "Jun Sun",
      "jwang whu/edu/cn (jwang)" -> "jwang",
      "kostas pouliasis" -> "Kostas Pouliasis",
      "kristof/teichel:ptb/de" -> "Kristof Teichel\nkristof/teichel:ptb/de",
      "lucas cavalcante" -> "Lucas Cavalcante",
      "mahmoud abdelazim" -> "Mahmoud Abdelazim",
      "manish surolia" -> "Manish Surolia",
      "mantel" -> "Heiko Mantel",
      "marco caminati" -> "Marco Caminati",
      "mathias/fleury:ens-rennes/fr" -> "Mathias Fleury\nmathias/fleury12:gmail/com",
      "merz:loria/fr" -> "stephan/merz:loria/fr",
      "michel levy" -> "Michel Levy",
      "michel/levy2009:laposte/net" -> "Michel Levy\nmichel/levy2009:laposte/net",
      "nemouchi" -> "Yakoub Nemouchi",
      "noam neer" -> "Noam Neer",
      "olfa mraihi" -> "Olfa Mraihi",
      "pathsnottakenworkshop:gmail/com" -> "Leo Freitas\nleo/freitas:newcastle/ac/uk",
      "patrick barlatier" -> "Patrick Barlatier",
      "patrick dabou" -> "Patrick Dabou",
      "paul zimmermann" -> "Paul Zimmermann",
      "popescu2:illinois/edu" -> "Andrei Popescu",
      "recruiting" -> "",
      "recruiting:mais/informatik/tu-darmstadt/de" -> "",
      "roux cody" -> "Cody Roux",
      "scott constable" -> "Scott Constable",
      "superuser:mattweidner/com" -> "Matthew Weidner\nsuperuser:mattweidner/com",
      "urban:math/lmu/de" -> "Christian Urban\nurban:math/lmu/de",
      "veronique/cortier:loria/fr" -> "Veronique/Cortier:loria/fr",
      "vikram singh" -> "Vikram Singh",
      "wenzelm:in/tum/de" -> "makarius:sketis/net",
      "werner:lix/polytechnique/fr" -> "Benjamin Werner\nwerner:lix/polytechnique/fr",
      "wmansky:cs/princeton/edu" -> "William Mansky\nwmansky:cs/princeton/edu",
      "y/nemouchi:ensbiotech/edu/dz" -> "Yakoub Nemouchi\ny/nemouchi:ensbiotech/edu/dz",
      "ÐÑÐÐÐÑÐÐÐ ÐÐÐÐÐÐÐÑÐÐÐÑ ÐÐÐÑÐÐÐ" -> "",
      "\u2200X/X\u03c0 - Tutorials about Proofs" -> "Bruno Woltzenlogel Paleo",
    )

  private def tune(s: String): String =
    s.replace(64.toChar, 58.toChar).replace(46.toChar, 47.toChar)

  private def untune(s: String): String =
    s.replace(58.toChar, 64.toChar).replace(47.toChar, 46.toChar)

  def standard_author_info(author_info: List[String]): List[String] =
    author_info.flatMap(s =>
      split_lines(standard_name.get(tune(s)).map(untune).getOrElse(s))).distinct

  sealed case class Message(
    name: String,
    date: Date,
    title: String,
    author_info: List[String],
    body: String,
    tags: List[String]
  ) {
    if (author_info.isEmpty || author_info.exists(_.isEmpty)) {
      error("Bad author information in " + quote(name))
    }

    override def toString: String = name
   }

  object Messages {
    type Graph = isabelle.Graph[String, Message]

    def apply(msgs: List[Message]): Messages = {
      def make_node(g: Graph, node: String, msg: Message): Graph =
        if (g.defined(node) && Date.Ordering.compare(g.get_node(node).date, msg.date) >= 0) g
        else g.default_node(node, msg)

      def connect_nodes(g: Graph, nodes: List[String]): Graph =
        nodes match {
          case Nil => g
          case a :: bs => bs.foldLeft(g)({ case (g1, b) => g1.add_edge(a, b).add_edge(b, a) })
        }

      new Messages(msgs.sortBy(_.date)(Date.Ordering),
        msgs.foldLeft[Graph](Graph.string)(
          { case (graph, msg) =>
              val nodes = msg.author_info
              connect_nodes(nodes.foldLeft(graph)(make_node(_, _, msg)), nodes)
          }))
    }

    def find(dir: Path): Messages = {
      val msgs =
        for {
          archive <- List(Isabelle_Users, Isabelle_Dev)
          msg <- archive.find_messages(dir + Path.basic(archive.list_name))
        } yield msg
      Messages(msgs)
    }

    sealed case class Cluster(author_info: List[String]) {
      val (addresses, names) = author_info.partition(is_address)

      val name: String =
        names.headOption getOrElse {
          addresses match {
            case a :: _ => a.substring(0, a.indexOf('@')).replace('.'' ')
            case Nil => error("Empty cluster")
          }
        }

      val name_lowercase: String = Word.lowercase(name)

      def get_address: Option[String] = addresses.headOption

      def unique: Boolean = names.length == 1 && addresses.length == 1
      def multi: Boolean = names.length > 1 || addresses.length > 1

      def print: String = {
        val entries = names ::: (if (addresses.isEmpty) Nil else List("")) ::: addresses
        entries.mkString("\n * ""\n """)
      }
    }
  }

  class Messages private(val sorted: List[Message], val graph: Messages.Graph) {
    override def toString: String = "Messages(" + sorted.size + ")"

    object Node_Ordering extends scala.math.Ordering[String] {
      override def compare(a: String, b: String): Int =
        Date.Rev_Ordering.compare(graph.get_node(a).date, graph.get_node(b).date)
    }

    def get_cluster(msg: Message): Messages.Cluster =
      Messages.Cluster(graph.all_succs(msg.author_info).sorted.sorted(Node_Ordering))

    def get_name(msg: Message): String = get_cluster(msg).name

    def get_address(msg: Message): String =
      get_cluster(msg).get_address getOrElse error("No author address for " + quote(msg.name))

    def check(check_all: Boolean, check_multi: Boolean = false): Unit = {
      val clusters = sorted.map(get_cluster).distinct.sortBy(_.name_lowercase)

      if (check_all) {
        Output.writeln(cat_lines("clusters:" :: clusters.map(_.print)))
      }
      else {
        val multi = if (check_multi) clusters.filter(_.multi) else Nil
        if (multi.nonEmpty) {
          Output.writeln(cat_lines("ambiguous clusters:" :: multi.map(_.print)))
        }
      }

      val unknown = clusters.filter(cluster => cluster.get_address.isEmpty)
      if (unknown.nonEmpty) {
        Output.writeln(cat_lines("\nunknown mail:" :: unknown.map(_.print)))
      }
    }
  }


  /* mailing list archives */

  abstract class Archive(
    url: Url,
    name: String = "",
    tag: String = ""
  ) {
    def message_regex: Regex
    def message_content(name: String, lines: List[String]): Message

    def message_match(lines: List[String], re: Regex): Option[Match] =
      lines.flatMap(re.findFirstMatchIn(_)).headOption

    def make_name(str: String): String = {
      val s =
        str.trim.replaceAll("""\s+"""" ").replaceAll(" at ""@")
          .replace("mailbroy.informatik.tu-muenchen.de""in.tum.de")
          .replace("informatik.tu-muenchen.de""in.tum.de")
      if (s.startsWith("=") && s.endsWith("=")) "" else s
    }

    def make_body(lines: List[String]): String =
      cat_lines(Library.take_suffix[String](_.isEmpty, lines)._1)

    private val main_url: Url =
      Url(Library.take_suffix[Char](_ == '/', Url.trim_index(url).toString.toList)._1.mkString + "/")

    private val main_html = Url.read(main_url)

    val list_name: String = {
      val title =
        """The ([^</>]*) Archives""".r.findFirstMatchIn(main_html).map(_.group(1))
      (proper_string(name) orElse title).getOrElse(error("Failed to determine mailing list name"))
    }
    override def toString: String = list_name

    def full_name(href: String): String = list_name + "/" + href

    def list_tag: String = proper_string(tag).getOrElse(list_name)

    def read_text(href: String): String = Url.read(main_url.resolve(href))

    def hrefs_text: List[String] =
      """href="([^"]+\.txt(?:\.gz)?)"""".r.findAllMatchIn(main_html).map(_.group(1)).toList

    def hrefs_msg: List[String] =
      (for {
        href <- """href="([^"]+)/date.html"""".r.findAllMatchIn(main_html).map(_.group(1))
        html = read_text(href + "/date.html")
        msg <- message_regex.findAllMatchIn(html).map(_.group(1))
      } yield href + "/" + msg).toList

    def get(target_dir: Path, href: String, progress: Progress = new Progress): Option[Path] = {
      val dir = target_dir + Path.basic(list_name)
      val path = dir + Path.explode(href)
      val url = main_url.resolve(href)
      val connection = url.open_connection()
      try {
        val length = connection.getContentLengthLong
        val timestamp = connection.getLastModified
        if (path.is_file && File.size(path) == length && path.file.lastModified == timestamp) None
        else {
          Isabelle_System.make_directory(path.dir)
          progress.echo("Getting " + url)
          val bytes =
            using(connection.getInputStream)(Bytes.read_stream(_, hint = length.toInt max 1024))
          Bytes.write(path, bytes)
          path.file.setLastModified(timestamp)
          Some(path)
        }
      }
      finally { connection.getInputStream.close() }
    }

    def download_text(target_dir: Path, progress: Progress = new Progress): List[Path] =
      hrefs_text.flatMap(get(target_dir, _, progress = progress))

    def download_msg(target_dir: Path, progress: Progress = new Progress): List[Path] =
      hrefs_msg.flatMap(get(target_dir, _, progress = progress))

    def download(target_dir: Path, progress: Progress = new Progress): List[Path] =
      download_text(target_dir, progress = progress) :::
      download_msg(target_dir, progress = progress)

    def make_title(str: String): String = {
      val Trim1 = ("""\s*\Q[""" + list_tag + """]\E\s*(.*)""").r
      val Trim2 = """(?i:(?:re|fw|fwd)\s*:\s*)(.*)""".r
      val Trim3 = """\[\s*(.*?)\s*\]""".r
      @tailrec def trim(s: String): String =
        s match {
          case Trim1(s1) => trim(s1)
          case Trim2(s1) => trim(s1)
          case Trim3(s1) => trim(s1)
          case _ => s
        }
      trim(str)
    }

    def get_messages(): List[Message] =
      for (href <- hrefs_msg) yield message_content(href, split_lines(read_text(href)))

    def find_messages(dir: Path): List[Message] = {
      for {
        file <- File.find_files(dir.file, file => File.is_html(file.getName))
        rel_path <- File.relative_path(dir, File.path(file))
      }
      yield {
        val name = full_name(rel_path.implode)
        message_content(name, split_lines(File.read(file)))
      }
    }
  }

  private class Message_Chunk(bg: String = "", en: String = "") {
    def unapply(lines: List[String]): Option[List[String]] = {
      val res1 =
        if (bg.isEmpty) Some(lines)
        else {
          lines.dropWhile(_ != bg) match {
            case Nil => None
            case _ :: rest => Some(rest)
          }
        }
      if (en.isEmpty) res1
      else {
        res1 match {
          case None => None
          case Some(lines1) =>
            val lines2 = lines1.takeWhile(_ != en)
            if (lines1.drop(lines2.length).isEmpty) None else Some(lines2)
        }
      }
    }

    def get(lines: List[String]): List[String] =
      unapply(lines) getOrElse
        error("Missing delimiters:" + if_proper(bg, " ") + bg + if_proper(en, " ") + en)
  }


  /* isabelle-users mailing list */

  object Isabelle_Users extends Archive(
    Url("https://lists.cam.ac.uk/pipermail/cl-isabelle-users"),
    name = "isabelle-users", tag = "isabelle"
  ) {
    override def message_regex: Regex = """
  • """.r

        private object Head extends
          Message_Chunk(bg = "", en = "")

        private object Body extends
          Message_Chunk(bg = "", en = "")

        private object Date_Format {
          private val Trim1 = """\w+,\s*(.*)""".r
          private val Trim2 = """(.*?)\s*\(.*\)""".r
          private val Format =
            Date.Format(
              "d MMM uuuu H:m:s Z",
              "d MMM uuuu H:m:s z",
              "d MMM yy H:m:s Z",
              "d MMM yy H:m:s z")
          def unapply(s: String): Option[Date] = {
            val s0 = s.replaceAll("""\s+"""" ")
            val s1 =
              s0 match {
                case Trim1(s1) => s1
                case _ => s0
              }
            val s2 =
              s1 match {
                case Trim2(s2) => s2
                case _ => s1
              }
            Format.unapply(s2)
          }
        }

        override def make_name(str: String): String = {
          val s = Library.perhaps_unsuffix(" via Cl-isabelle-users"super.make_name(str))
          if (s == "cl-isabelle-users@lists.cam.ac.uk""" else s
        }

        object Address {
          private def anchor(s: String): String = """
    >""" + s + """</a>"""
          private def angl(s: String): String = """<""" + s + """>"""
          private def quot(s: String): String = """"""" + s + """""""
          private def paren(s: String): String = """\(""" + s + """\)"""
          private val adr = """([^<>]*? at [^<>]*?)"""
          private val any = """([^<>]*?)"""
          private val spc = """\s*"""

          private val Name1 = quot(anchor(any)).r
          private val Name2 = quot(any).r
          private val Name_Adr1 = (quot(anchor(any)) + spc + angl(anchor(adr))).r
          private val Name_Adr2 = (quot(any) + spc + angl(anchor(adr))).r
          private val Name_Adr3 = (anchor(any) + spc + angl(anchor(adr))).r
          private val Name_Adr4 = (any + spc + angl(anchor(adr))).r
          private val Adr_Name1 = (angl(anchor(adr)) + spc + paren(any)).r
          private val Adr_Name2 = (anchor(adr) + spc + paren(any)).r
          private val Adr1 = angl(anchor(adr)).r
          private val Adr2 = anchor(adr).r

          def parse(s: String): List[String] =
            s match {
              case Name1(a) => List(a)
              case Name2(a) => List(a)
              case Name_Adr1(a, b) => List(a, b)
              case Name_Adr2(a, b) => List(a, b)
              case Name_Adr3(a, b) => List(a, b)
              case Name_Adr4(a, b) => List(a, b)
              case Adr_Name1(b, a) => List(a, b)
              case Adr_Name2(b, a) => List(a, b)
              case Adr1(a) => List(a)
              case Adr2(a) => List(a)
              case _ => Nil
            }
        }

        override def message_content(name: String, lines: List[String]): Message = {
          def err(msg: String = ""): Nothing =
            error("Malformed message: " + name + if_proper(msg, "\n" + msg))

          val (head, body) =
            try { (Head.get(lines), make_body(Body.get(lines))) }
            catch { case ERROR(msg) => err(msg) }

          val date =
            message_match(head, """
  • Date:\s*(.*?)\s*
  • "
    "".r)
              .map(m => HTML.input(m.group(1))) match
            {
              case Some(Date_Format(d)) => d
              case Some(s) => err("Malformed Date: " + quote(s))
              case None => err("Missing Date")
            }

          val title =
            make_title(
              HTML.input(message_match(head, """
  • Subject:\s*(.*)
  • "
    "".r)
                .getOrElse(err("Missing Subject")).group(1)))

          def parse_author_info(re: Regex): List[String] =
            message_match(head, re) match {
              case None => Nil
              case Some(m) => Address.parse(m.group(1)).map(a => HTML.input(make_name(a)))
            }

          val author_info =
            (parse_author_info("""
  • From:\s*(.*?)\s*
  • "
    "".r) :::
              parse_author_info("""
  • Reply-to:\s*(.*?)\s*
  • "
    "".r))
            .distinct.filter(_.nonEmpty)

          if (author_info.isEmpty) err("Malformed author information")

          val tags = List(list_name)

          Message(name, date, title, standard_author_info(author_info), body, tags)
        }
      }


      /* isabelle-dev mailing list */

      object Isabelle_Dev extends Archive(Url("
    https://mailmanbroy.in.tum.de/pipermail/isabelle-dev")) {
        override def message_regex: Regex = """
  • """.r

        private object Head extends Message_Chunk(en = "")
        private object Body extends Message_Chunk(bg = "", en = "")

        private object Date_Format {
          val Format = Date.Format("E MMM d H:m:s z uuuu")
          def unapply(s: String): Option[Date] = Format.unapply(s.replaceAll("""\s+"""" "))
        }

        override def message_content(name: String, lines: List[String]): Message = {
          def err(msg: String = ""): Nothing =
            error("Malformed message: " + name + if_proper(msg, "\n" + msg))

          val (head, body) =
            try { (Head.get(lines), make_body(Body.get(lines))) }
            catch { case ERROR(msg) => err(msg) }

          val date =
            message_match(head, """\s*(.*)""".r).map(m => HTML.input(m.group(1))) match {
              case Some(Date_Format(d)) => d
              case Some(s) => err("Malformed Date: " + quote(s))
              case None => err("Missing Date")
            }

          val (title, author_address) = {
            message_match(head, """TITLE="([^"]+)">(.*)""".r) match {
              case Some(m) => (make_title(HTML.input(m.group(1))), make_name(HTML.input(m.group(2))))
              case None => err("Missing TITLE")
            }
          }

          val author_name =
            message_match(head, """\s*(.*)""".r) match {
              case None => err("Missing author")
              case Some(m) =>
                val a = make_name(HTML.input(m.group(1)))
                if (a == author_address) "" else a
            }

          val author_info = List(author_name, author_address)
          val tags = List(list_name)

          Message(name, date, title, standard_author_info(author_info), body, tags)
        }
      }
    }

  • ¤ Dauer der Verarbeitung: 0.18 Sekunden  (vorverarbeitet)  ¤

    *© Formatika GbR, Deutschland






    Wurzel

    Suchen

    Beweissystem der NASA

    Beweissystem Isabelle

    NIST Cobol Testsuite

    Cephes Mathematical Library

    Wiener Entwicklungsmethode

    Haftungshinweis

    Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

    Bemerkung:

    Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.