src/Pure/Thy/bibtex.scala
author wenzelm
Sat, 11 Mar 2023 16:11:26 +0100
changeset 77617 58b7f3fb73cb
parent 77369 df17355f1e2c
child 78614 4da5cdaa4dcd
permissions -rw-r--r--
tuned signature;
Ignore whitespace changes - Everywhere: Within whitespace: At end of lines:
67274
4588f714a78a clarified directories;
wenzelm
parents: 67272
diff changeset
     1
/*  Title:      Pure/Thy/bibtex.scala
58523
937c479e62fe some support for bibtex files;
wenzelm
parents:
diff changeset
     2
    Author:     Makarius
937c479e62fe some support for bibtex files;
wenzelm
parents:
diff changeset
     3
58544
340f130b3d38 bibtex support in ML: document antiquotation @{cite} with markup;
wenzelm
parents: 58543
diff changeset
     4
BibTeX support.
58523
937c479e62fe some support for bibtex files;
wenzelm
parents:
diff changeset
     5
*/
937c479e62fe some support for bibtex files;
wenzelm
parents:
diff changeset
     6
937c479e62fe some support for bibtex files;
wenzelm
parents:
diff changeset
     7
package isabelle
937c479e62fe some support for bibtex files;
wenzelm
parents:
diff changeset
     8
937c479e62fe some support for bibtex files;
wenzelm
parents:
diff changeset
     9
67290
98b6cd12f963 implicit thy_load context for bibtex files;
wenzelm
parents: 67289
diff changeset
    10
import java.io.{File => JFile}
98b6cd12f963 implicit thy_load context for bibtex files;
wenzelm
parents: 67289
diff changeset
    11
58528
7d6b8f8893e8 more explicit item kind;
wenzelm
parents: 58527
diff changeset
    12
import scala.collection.mutable
58523
937c479e62fe some support for bibtex files;
wenzelm
parents:
diff changeset
    13
import scala.util.parsing.combinator.RegexParsers
64824
330ec9bc4b75 tuned signature;
wenzelm
parents: 60215
diff changeset
    14
import scala.util.parsing.input.Reader
76972
6c542f2aab85 basic support for update_cite_commands;
wenzelm
parents: 76933
diff changeset
    15
import scala.util.matching.Regex
58523
937c479e62fe some support for bibtex files;
wenzelm
parents:
diff changeset
    16
77011
3e48f8c6afc9 parse citations from raw source, without formal context;
wenzelm
parents: 77010
diff changeset
    17
import isabelle.{Token => Isar_Token}
3e48f8c6afc9 parse citations from raw source, without formal context;
wenzelm
parents: 77010
diff changeset
    18
58523
937c479e62fe some support for bibtex files;
wenzelm
parents:
diff changeset
    19
75393
87ebf5a50283 clarified formatting, for the sake of scala3;
wenzelm
parents: 73730
diff changeset
    20
object Bibtex {
69255
800b1ce96fce more general support for Isabelle/PIDE file formats -- less hardwired Bibtex operations;
wenzelm
parents: 68224
diff changeset
    21
  /** file format **/
800b1ce96fce more general support for Isabelle/PIDE file formats -- less hardwired Bibtex operations;
wenzelm
parents: 68224
diff changeset
    22
75393
87ebf5a50283 clarified formatting, for the sake of scala3;
wenzelm
parents: 73730
diff changeset
    23
  class File_Format extends isabelle.File_Format {
69255
800b1ce96fce more general support for Isabelle/PIDE file formats -- less hardwired Bibtex operations;
wenzelm
parents: 68224
diff changeset
    24
    val format_name: String = "bibtex"
69257
039edba27102 clarified signature;
wenzelm
parents: 69255
diff changeset
    25
    val file_ext: String = "bib"
69255
800b1ce96fce more general support for Isabelle/PIDE file formats -- less hardwired Bibtex operations;
wenzelm
parents: 68224
diff changeset
    26
76792
23f433294173 support for generic File_Format.parse_data, with persistent result in document model;
wenzelm
parents: 76779
diff changeset
    27
    override def parse_data(name: String, text: String): Bibtex.Entries =
77030
d7dc5b1e4381 proper positions for Isabelle/ML, instead of Isabelle/Scala;
wenzelm
parents: 77029
diff changeset
    28
      Bibtex.Entries.parse(text, Isar_Token.Pos.file(name))
76792
23f433294173 support for generic File_Format.parse_data, with persistent result in document model;
wenzelm
parents: 76779
diff changeset
    29
69257
039edba27102 clarified signature;
wenzelm
parents: 69255
diff changeset
    30
    override def theory_suffix: String = "bibtex_file"
69259
wenzelm
parents: 69257
diff changeset
    31
    override def theory_content(name: String): String =
76859
6e1bf28d5a80 more standard master_dir;
wenzelm
parents: 76849
diff changeset
    32
      """theory "bib" imports Pure begin bibtex_file "." end"""
76849
d431a9340163 more systematic Sessions.illegal_theory, based on File_Format.theory_excluded;
wenzelm
parents: 76829
diff changeset
    33
    override def theory_excluded(name: String): Boolean = name == "bib"
69255
800b1ce96fce more general support for Isabelle/PIDE file formats -- less hardwired Bibtex operations;
wenzelm
parents: 68224
diff changeset
    34
75941
4bbbbaa656f1 clarified modules;
wenzelm
parents: 75906
diff changeset
    35
    override def html_document(snapshot: Document.Snapshot): Option[Browser_Info.HTML_Document] = {
69255
800b1ce96fce more general support for Isabelle/PIDE file formats -- less hardwired Bibtex operations;
wenzelm
parents: 68224
diff changeset
    36
      val name = snapshot.node_name
800b1ce96fce more general support for Isabelle/PIDE file formats -- less hardwired Bibtex operations;
wenzelm
parents: 68224
diff changeset
    37
      if (detect(name.node)) {
76829
f2a8ba0b8c96 more robust: avoid detour via somewhat fragile Node.Name.path;
wenzelm
parents: 76793
diff changeset
    38
        val title = "Bibliography " + quote(snapshot.node_name.file_name)
69255
800b1ce96fce more general support for Isabelle/PIDE file formats -- less hardwired Bibtex operations;
wenzelm
parents: 68224
diff changeset
    39
        val content =
800b1ce96fce more general support for Isabelle/PIDE file formats -- less hardwired Bibtex operations;
wenzelm
parents: 68224
diff changeset
    40
          Isabelle_System.with_tmp_file("bib", "bib") { bib =>
76933
dd53bb198eb1 tuned signature: more uniform operations;
wenzelm
parents: 76859
diff changeset
    41
            File.write(bib, snapshot.node.source)
69255
800b1ce96fce more general support for Isabelle/PIDE file formats -- less hardwired Bibtex operations;
wenzelm
parents: 68224
diff changeset
    42
            Bibtex.html_output(List(bib), style = "unsort", title = title)
800b1ce96fce more general support for Isabelle/PIDE file formats -- less hardwired Bibtex operations;
wenzelm
parents: 68224
diff changeset
    43
          }
75941
4bbbbaa656f1 clarified modules;
wenzelm
parents: 75906
diff changeset
    44
        Some(Browser_Info.HTML_Document(title, content))
69255
800b1ce96fce more general support for Isabelle/PIDE file formats -- less hardwired Bibtex operations;
wenzelm
parents: 68224
diff changeset
    45
      }
800b1ce96fce more general support for Isabelle/PIDE file formats -- less hardwired Bibtex operations;
wenzelm
parents: 68224
diff changeset
    46
      else None
800b1ce96fce more general support for Isabelle/PIDE file formats -- less hardwired Bibtex operations;
wenzelm
parents: 68224
diff changeset
    47
    }
800b1ce96fce more general support for Isabelle/PIDE file formats -- less hardwired Bibtex operations;
wenzelm
parents: 68224
diff changeset
    48
  }
800b1ce96fce more general support for Isabelle/PIDE file formats -- less hardwired Bibtex operations;
wenzelm
parents: 68224
diff changeset
    49
800b1ce96fce more general support for Isabelle/PIDE file formats -- less hardwired Bibtex operations;
wenzelm
parents: 68224
diff changeset
    50
800b1ce96fce more general support for Isabelle/PIDE file formats -- less hardwired Bibtex operations;
wenzelm
parents: 68224
diff changeset
    51
67203
85784e16bec8 expose bibtex errors;
wenzelm
parents: 67014
diff changeset
    52
  /** bibtex errors **/
85784e16bec8 expose bibtex errors;
wenzelm
parents: 67014
diff changeset
    53
75393
87ebf5a50283 clarified formatting, for the sake of scala3;
wenzelm
parents: 73730
diff changeset
    54
  def bibtex_errors(dir: Path, root_name: String): List[String] = {
77035
28ac56e59d23 tuned signature;
wenzelm
parents: 77032
diff changeset
    55
    val log_path = dir + Path.explode(root_name).blg
67203
85784e16bec8 expose bibtex errors;
wenzelm
parents: 67014
diff changeset
    56
    if (log_path.is_file) {
67300
0bfbf5b9d6ba more accurate message patterns;
wenzelm
parents: 67293
diff changeset
    57
      val Error1 = """^(I couldn't open database file .+)$""".r
73730
2f023b2b0e1e more uniform bibtex error, without using perl (see 4710dd5093a3);
wenzelm
parents: 73565
diff changeset
    58
      val Error2 = """^(I found no .+)$""".r
2f023b2b0e1e more uniform bibtex error, without using perl (see 4710dd5093a3);
wenzelm
parents: 73565
diff changeset
    59
      val Error3 = """^(.+)---line (\d+) of file (.+)""".r
67203
85784e16bec8 expose bibtex errors;
wenzelm
parents: 67014
diff changeset
    60
      Line.logical_lines(File.read(log_path)).flatMap(line =>
85784e16bec8 expose bibtex errors;
wenzelm
parents: 67014
diff changeset
    61
        line match {
67300
0bfbf5b9d6ba more accurate message patterns;
wenzelm
parents: 67293
diff changeset
    62
          case Error1(msg) => Some("Bibtex error: " + msg)
73730
2f023b2b0e1e more uniform bibtex error, without using perl (see 4710dd5093a3);
wenzelm
parents: 73565
diff changeset
    63
          case Error2(msg) => Some("Bibtex error: " + msg)
2f023b2b0e1e more uniform bibtex error, without using perl (see 4710dd5093a3);
wenzelm
parents: 73565
diff changeset
    64
          case Error3(msg, Value.Int(l), file) =>
67203
85784e16bec8 expose bibtex errors;
wenzelm
parents: 67014
diff changeset
    65
            val path = File.standard_path(file)
85784e16bec8 expose bibtex errors;
wenzelm
parents: 67014
diff changeset
    66
            if (Path.is_wellformed(path)) {
85784e16bec8 expose bibtex errors;
wenzelm
parents: 67014
diff changeset
    67
              val pos = Position.Line_File(l, (dir + Path.explode(path)).canonical.implode)
85784e16bec8 expose bibtex errors;
wenzelm
parents: 67014
diff changeset
    68
              Some("Bibtex error" + Position.here(pos) + ":\n  " + msg)
85784e16bec8 expose bibtex errors;
wenzelm
parents: 67014
diff changeset
    69
            }
85784e16bec8 expose bibtex errors;
wenzelm
parents: 67014
diff changeset
    70
            else None
85784e16bec8 expose bibtex errors;
wenzelm
parents: 67014
diff changeset
    71
          case _ => None
85784e16bec8 expose bibtex errors;
wenzelm
parents: 67014
diff changeset
    72
        })
85784e16bec8 expose bibtex errors;
wenzelm
parents: 67014
diff changeset
    73
    }
85784e16bec8 expose bibtex errors;
wenzelm
parents: 67014
diff changeset
    74
    else Nil
85784e16bec8 expose bibtex errors;
wenzelm
parents: 67014
diff changeset
    75
  }
85784e16bec8 expose bibtex errors;
wenzelm
parents: 67014
diff changeset
    76
85784e16bec8 expose bibtex errors;
wenzelm
parents: 67014
diff changeset
    77
85784e16bec8 expose bibtex errors;
wenzelm
parents: 67014
diff changeset
    78
67272
c41a032d8386 check bibtex database: errors and warnings;
wenzelm
parents: 67257
diff changeset
    79
  /** check database **/
c41a032d8386 check bibtex database: errors and warnings;
wenzelm
parents: 67257
diff changeset
    80
75393
87ebf5a50283 clarified formatting, for the sake of scala3;
wenzelm
parents: 73730
diff changeset
    81
  def check_database(database: String): (List[(String, Position.T)], List[(String, Position.T)]) = {
67275
5e427586cb57 check bibtex database on ML side -- for semantic PIDE editing;
wenzelm
parents: 67274
diff changeset
    82
    val chunks = parse(Line.normalize(database))
5e427586cb57 check bibtex database on ML side -- for semantic PIDE editing;
wenzelm
parents: 67274
diff changeset
    83
    var chunk_pos = Map.empty[String, Position.T]
67272
c41a032d8386 check bibtex database: errors and warnings;
wenzelm
parents: 67257
diff changeset
    84
    val tokens = new mutable.ListBuffer[(Token, Position.T)]
c41a032d8386 check bibtex database: errors and warnings;
wenzelm
parents: 67257
diff changeset
    85
    var line = 1
c41a032d8386 check bibtex database: errors and warnings;
wenzelm
parents: 67257
diff changeset
    86
    var offset = 1
c41a032d8386 check bibtex database: errors and warnings;
wenzelm
parents: 67257
diff changeset
    87
67276
abac35ee3565 clarified positions;
wenzelm
parents: 67275
diff changeset
    88
    def make_pos(length: Int): Position.T =
abac35ee3565 clarified positions;
wenzelm
parents: 67275
diff changeset
    89
      Position.Offset(offset) ::: Position.End_Offset(offset + length) ::: Position.Line(line)
67272
c41a032d8386 check bibtex database: errors and warnings;
wenzelm
parents: 67257
diff changeset
    90
75393
87ebf5a50283 clarified formatting, for the sake of scala3;
wenzelm
parents: 73730
diff changeset
    91
    def advance_pos(tok: Token): Unit = {
67272
c41a032d8386 check bibtex database: errors and warnings;
wenzelm
parents: 67257
diff changeset
    92
      for (s <- Symbol.iterator(tok.source)) {
c41a032d8386 check bibtex database: errors and warnings;
wenzelm
parents: 67257
diff changeset
    93
        if (Symbol.is_newline(s)) line += 1
c41a032d8386 check bibtex database: errors and warnings;
wenzelm
parents: 67257
diff changeset
    94
        offset += 1
c41a032d8386 check bibtex database: errors and warnings;
wenzelm
parents: 67257
diff changeset
    95
      }
c41a032d8386 check bibtex database: errors and warnings;
wenzelm
parents: 67257
diff changeset
    96
    }
c41a032d8386 check bibtex database: errors and warnings;
wenzelm
parents: 67257
diff changeset
    97
c41a032d8386 check bibtex database: errors and warnings;
wenzelm
parents: 67257
diff changeset
    98
    def get_line_pos(l: Int): Position.T =
c41a032d8386 check bibtex database: errors and warnings;
wenzelm
parents: 67257
diff changeset
    99
      if (0 < l && l <= tokens.length) tokens(l - 1)._2 else Position.none
c41a032d8386 check bibtex database: errors and warnings;
wenzelm
parents: 67257
diff changeset
   100
c41a032d8386 check bibtex database: errors and warnings;
wenzelm
parents: 67257
diff changeset
   101
    for (chunk <- chunks) {
c41a032d8386 check bibtex database: errors and warnings;
wenzelm
parents: 67257
diff changeset
   102
      val name = chunk.name
c41a032d8386 check bibtex database: errors and warnings;
wenzelm
parents: 67257
diff changeset
   103
      if (name != "" && !chunk_pos.isDefinedAt(name)) {
67276
abac35ee3565 clarified positions;
wenzelm
parents: 67275
diff changeset
   104
        chunk_pos += (name -> make_pos(chunk.heading_length))
67272
c41a032d8386 check bibtex database: errors and warnings;
wenzelm
parents: 67257
diff changeset
   105
      }
c41a032d8386 check bibtex database: errors and warnings;
wenzelm
parents: 67257
diff changeset
   106
      for (tok <- chunk.tokens) {
67276
abac35ee3565 clarified positions;
wenzelm
parents: 67275
diff changeset
   107
        tokens += (tok.copy(source = tok.source.replace("\n", " ")) -> make_pos(tok.source.length))
67272
c41a032d8386 check bibtex database: errors and warnings;
wenzelm
parents: 67257
diff changeset
   108
        advance_pos(tok)
c41a032d8386 check bibtex database: errors and warnings;
wenzelm
parents: 67257
diff changeset
   109
      }
c41a032d8386 check bibtex database: errors and warnings;
wenzelm
parents: 67257
diff changeset
   110
    }
c41a032d8386 check bibtex database: errors and warnings;
wenzelm
parents: 67257
diff changeset
   111
75394
42267c650205 tuned formatting;
wenzelm
parents: 75393
diff changeset
   112
    Isabelle_System.with_tmp_dir("bibtex") { tmp_dir =>
67272
c41a032d8386 check bibtex database: errors and warnings;
wenzelm
parents: 67257
diff changeset
   113
      File.write(tmp_dir + Path.explode("root.bib"),
c41a032d8386 check bibtex database: errors and warnings;
wenzelm
parents: 67257
diff changeset
   114
        tokens.iterator.map(p => p._1.source).mkString("", "\n", "\n"))
c41a032d8386 check bibtex database: errors and warnings;
wenzelm
parents: 67257
diff changeset
   115
      File.write(tmp_dir + Path.explode("root.aux"),
c41a032d8386 check bibtex database: errors and warnings;
wenzelm
parents: 67257
diff changeset
   116
        "\\bibstyle{plain}\n\\bibdata{root}\n\\citation{*}")
c41a032d8386 check bibtex database: errors and warnings;
wenzelm
parents: 67257
diff changeset
   117
      Isabelle_System.bash("\"$ISABELLE_BIBTEX\" root", cwd = tmp_dir.file)
c41a032d8386 check bibtex database: errors and warnings;
wenzelm
parents: 67257
diff changeset
   118
c41a032d8386 check bibtex database: errors and warnings;
wenzelm
parents: 67257
diff changeset
   119
      val Error = """^(.*)---line (\d+) of file root.bib$""".r
c41a032d8386 check bibtex database: errors and warnings;
wenzelm
parents: 67257
diff changeset
   120
      val Warning = """^Warning--(.+)$""".r
c41a032d8386 check bibtex database: errors and warnings;
wenzelm
parents: 67257
diff changeset
   121
      val Warning_Line = """--line (\d+) of file root.bib$""".r
c41a032d8386 check bibtex database: errors and warnings;
wenzelm
parents: 67257
diff changeset
   122
      val Warning_in_Chunk = """^Warning--(.+) in (.+)$""".r
c41a032d8386 check bibtex database: errors and warnings;
wenzelm
parents: 67257
diff changeset
   123
c41a032d8386 check bibtex database: errors and warnings;
wenzelm
parents: 67257
diff changeset
   124
      val log_file = tmp_dir + Path.explode("root.blg")
c41a032d8386 check bibtex database: errors and warnings;
wenzelm
parents: 67257
diff changeset
   125
      val lines = if (log_file.is_file) Line.logical_lines(File.read(log_file)) else Nil
c41a032d8386 check bibtex database: errors and warnings;
wenzelm
parents: 67257
diff changeset
   126
67301
e255c76db052 clarified signature;
wenzelm
parents: 67300
diff changeset
   127
      val (errors, warnings) =
69294
085f31ae902d more robust;
wenzelm
parents: 69259
diff changeset
   128
        if (lines.isEmpty) (Nil, Nil)
085f31ae902d more robust;
wenzelm
parents: 69259
diff changeset
   129
        else {
085f31ae902d more robust;
wenzelm
parents: 69259
diff changeset
   130
          lines.zip(lines.tail ::: List("")).flatMap(
085f31ae902d more robust;
wenzelm
parents: 69259
diff changeset
   131
            {
085f31ae902d more robust;
wenzelm
parents: 69259
diff changeset
   132
              case (Error(msg, Value.Int(l)), _) =>
085f31ae902d more robust;
wenzelm
parents: 69259
diff changeset
   133
                Some((true, (msg, get_line_pos(l))))
085f31ae902d more robust;
wenzelm
parents: 69259
diff changeset
   134
              case (Warning_in_Chunk(msg, name), _) if chunk_pos.isDefinedAt(name) =>
77148
9b3a8565464d more accurate Word.capitalize: do not touch name;
wenzelm
parents: 77035
diff changeset
   135
                Some((false, (Word.capitalize(msg) + " in entry " + quote(name), chunk_pos(name))))
69294
085f31ae902d more robust;
wenzelm
parents: 69259
diff changeset
   136
              case (Warning(msg), Warning_Line(Value.Int(l))) =>
085f31ae902d more robust;
wenzelm
parents: 69259
diff changeset
   137
                Some((false, (Word.capitalize(msg), get_line_pos(l))))
085f31ae902d more robust;
wenzelm
parents: 69259
diff changeset
   138
              case (Warning(msg), _) =>
085f31ae902d more robust;
wenzelm
parents: 69259
diff changeset
   139
                Some((false, (Word.capitalize(msg), Position.none)))
085f31ae902d more robust;
wenzelm
parents: 69259
diff changeset
   140
              case _ => None
085f31ae902d more robust;
wenzelm
parents: 69259
diff changeset
   141
            }
085f31ae902d more robust;
wenzelm
parents: 69259
diff changeset
   142
          ).partition(_._1)
085f31ae902d more robust;
wenzelm
parents: 69259
diff changeset
   143
        }
67301
e255c76db052 clarified signature;
wenzelm
parents: 67300
diff changeset
   144
      (errors.map(_._2), warnings.map(_._2))
75394
42267c650205 tuned formatting;
wenzelm
parents: 75393
diff changeset
   145
    }
67272
c41a032d8386 check bibtex database: errors and warnings;
wenzelm
parents: 67257
diff changeset
   146
  }
c41a032d8386 check bibtex database: errors and warnings;
wenzelm
parents: 67257
diff changeset
   147
75393
87ebf5a50283 clarified formatting, for the sake of scala3;
wenzelm
parents: 73730
diff changeset
   148
  object Check_Database extends Scala.Fun_String("bibtex_check_database") {
72756
72ac27ea12b2 more positions;
wenzelm
parents: 72729
diff changeset
   149
    val here = Scala_Project.here
75393
87ebf5a50283 clarified formatting, for the sake of scala3;
wenzelm
parents: 73730
diff changeset
   150
    def apply(database: String): String = {
72193
742d94015918 clarified signature;
wenzelm
parents: 70230
diff changeset
   151
      import XML.Encode._
742d94015918 clarified signature;
wenzelm
parents: 70230
diff changeset
   152
      YXML.string_of_body(pair(list(pair(string, properties)), list(pair(string, properties)))(
742d94015918 clarified signature;
wenzelm
parents: 70230
diff changeset
   153
        check_database(database)))
742d94015918 clarified signature;
wenzelm
parents: 70230
diff changeset
   154
    }
67275
5e427586cb57 check bibtex database on ML side -- for semantic PIDE editing;
wenzelm
parents: 67274
diff changeset
   155
  }
5e427586cb57 check bibtex database on ML side -- for semantic PIDE editing;
wenzelm
parents: 67274
diff changeset
   156
67272
c41a032d8386 check bibtex database: errors and warnings;
wenzelm
parents: 67257
diff changeset
   157
c41a032d8386 check bibtex database: errors and warnings;
wenzelm
parents: 67257
diff changeset
   158
64828
e837f6bf653c clarified modules;
wenzelm
parents: 64824
diff changeset
   159
  /** document model **/
e837f6bf653c clarified modules;
wenzelm
parents: 64824
diff changeset
   160
67290
98b6cd12f963 implicit thy_load context for bibtex files;
wenzelm
parents: 67289
diff changeset
   161
  /* entries */
64828
e837f6bf653c clarified modules;
wenzelm
parents: 64824
diff changeset
   162
77032
c066335efd2e clarified signature;
wenzelm
parents: 77031
diff changeset
   163
  sealed case class Entry(name: String, pos: Position.T) {
c066335efd2e clarified signature;
wenzelm
parents: 77031
diff changeset
   164
    def encode: String = YXML.string_of_body(XML.Encode.properties(Markup.Name(name) ::: pos))
c066335efd2e clarified signature;
wenzelm
parents: 77031
diff changeset
   165
  }
c066335efd2e clarified signature;
wenzelm
parents: 77031
diff changeset
   166
76776
011759a7f2f6 clarified signature: more explicit types;
wenzelm
parents: 76204
diff changeset
   167
  object Entries {
011759a7f2f6 clarified signature: more explicit types;
wenzelm
parents: 76204
diff changeset
   168
    val empty: Entries = apply(Nil)
011759a7f2f6 clarified signature: more explicit types;
wenzelm
parents: 76204
diff changeset
   169
77032
c066335efd2e clarified signature;
wenzelm
parents: 77031
diff changeset
   170
    def apply(entries: List[Entry], errors: List[String] = Nil): Entries =
76778
4086a0e4723b clarified signature: internalize errors (but: the parser rarely fails);
wenzelm
parents: 76776
diff changeset
   171
      new Entries(entries, errors)
76776
011759a7f2f6 clarified signature: more explicit types;
wenzelm
parents: 76204
diff changeset
   172
77030
d7dc5b1e4381 proper positions for Isabelle/ML, instead of Isabelle/Scala;
wenzelm
parents: 77029
diff changeset
   173
    def parse(text: String, start: Isar_Token.Pos = Isar_Token.Pos.start): Entries = {
77032
c066335efd2e clarified signature;
wenzelm
parents: 77031
diff changeset
   174
      val entries = new mutable.ListBuffer[Entry]
77030
d7dc5b1e4381 proper positions for Isabelle/ML, instead of Isabelle/Scala;
wenzelm
parents: 77029
diff changeset
   175
      var pos = start
76779
caeb732db09f more bibtex errors;
wenzelm
parents: 76778
diff changeset
   176
      var err_line = 0
76778
4086a0e4723b clarified signature: internalize errors (but: the parser rarely fails);
wenzelm
parents: 76776
diff changeset
   177
4086a0e4723b clarified signature: internalize errors (but: the parser rarely fails);
wenzelm
parents: 76776
diff changeset
   178
      try {
4086a0e4723b clarified signature: internalize errors (but: the parser rarely fails);
wenzelm
parents: 76776
diff changeset
   179
        for (chunk <- Bibtex.parse(text)) {
77030
d7dc5b1e4381 proper positions for Isabelle/ML, instead of Isabelle/Scala;
wenzelm
parents: 77029
diff changeset
   180
          val pos1 = pos.advance(chunk.source)
76778
4086a0e4723b clarified signature: internalize errors (but: the parser rarely fails);
wenzelm
parents: 76776
diff changeset
   181
          if (chunk.name != "" && !chunk.is_command) {
77032
c066335efd2e clarified signature;
wenzelm
parents: 77031
diff changeset
   182
            entries += Entry(chunk.name, pos.position(pos1.offset))
76778
4086a0e4723b clarified signature: internalize errors (but: the parser rarely fails);
wenzelm
parents: 76776
diff changeset
   183
          }
77030
d7dc5b1e4381 proper positions for Isabelle/ML, instead of Isabelle/Scala;
wenzelm
parents: 77029
diff changeset
   184
          if (chunk.is_malformed && err_line == 0) { err_line = pos.line }
d7dc5b1e4381 proper positions for Isabelle/ML, instead of Isabelle/Scala;
wenzelm
parents: 77029
diff changeset
   185
          pos = pos1
76778
4086a0e4723b clarified signature: internalize errors (but: the parser rarely fails);
wenzelm
parents: 76776
diff changeset
   186
        }
76779
caeb732db09f more bibtex errors;
wenzelm
parents: 76778
diff changeset
   187
caeb732db09f more bibtex errors;
wenzelm
parents: 76778
diff changeset
   188
        val err_pos =
77030
d7dc5b1e4381 proper positions for Isabelle/ML, instead of Isabelle/Scala;
wenzelm
parents: 77029
diff changeset
   189
          if (err_line == 0 || pos.file.isEmpty) Position.none
d7dc5b1e4381 proper positions for Isabelle/ML, instead of Isabelle/Scala;
wenzelm
parents: 77029
diff changeset
   190
          else Position.Line_File(err_line, pos.file)
76779
caeb732db09f more bibtex errors;
wenzelm
parents: 76778
diff changeset
   191
        val errors =
caeb732db09f more bibtex errors;
wenzelm
parents: 76778
diff changeset
   192
          if (err_line == 0) Nil
caeb732db09f more bibtex errors;
wenzelm
parents: 76778
diff changeset
   193
          else List("Malformed bibtex file" + Position.here(err_pos))
caeb732db09f more bibtex errors;
wenzelm
parents: 76778
diff changeset
   194
caeb732db09f more bibtex errors;
wenzelm
parents: 76778
diff changeset
   195
        apply(entries.toList, errors = errors)
76776
011759a7f2f6 clarified signature: more explicit types;
wenzelm
parents: 76204
diff changeset
   196
      }
76778
4086a0e4723b clarified signature: internalize errors (but: the parser rarely fails);
wenzelm
parents: 76776
diff changeset
   197
      catch { case ERROR(msg) => apply(Nil, errors = List(msg)) }
64828
e837f6bf653c clarified modules;
wenzelm
parents: 64824
diff changeset
   198
    }
e837f6bf653c clarified modules;
wenzelm
parents: 64824
diff changeset
   199
  }
e837f6bf653c clarified modules;
wenzelm
parents: 64824
diff changeset
   200
77032
c066335efd2e clarified signature;
wenzelm
parents: 77031
diff changeset
   201
  final class Entries private(val entries: List[Entry], val errors: List[String]) {
76793
fa70b536ec50 tuned output;
wenzelm
parents: 76792
diff changeset
   202
    override def toString: String = "Bibtex.Entries(" + entries.length + ")"
fa70b536ec50 tuned output;
wenzelm
parents: 76792
diff changeset
   203
76778
4086a0e4723b clarified signature: internalize errors (but: the parser rarely fails);
wenzelm
parents: 76776
diff changeset
   204
    def ::: (other: Entries): Entries =
77219
a10161fbc6de proper orientation for right-associative operations;
wenzelm
parents: 77218
diff changeset
   205
      new Entries(other.entries ::: entries, other.errors ::: errors)
66150
c2e19b9e1398 tuned signature;
wenzelm
parents: 66118
diff changeset
   206
  }
c2e19b9e1398 tuned signature;
wenzelm
parents: 66118
diff changeset
   207
77028
f5896dea6fce more direct check of bibtex entries via Isabelle/Scala;
wenzelm
parents: 77025
diff changeset
   208
  object Session_Entries extends Scala.Fun("bibtex_session_entries") {
f5896dea6fce more direct check of bibtex entries via Isabelle/Scala;
wenzelm
parents: 77025
diff changeset
   209
    val here = Scala_Project.here
f5896dea6fce more direct check of bibtex entries via Isabelle/Scala;
wenzelm
parents: 77025
diff changeset
   210
f5896dea6fce more direct check of bibtex entries via Isabelle/Scala;
wenzelm
parents: 77025
diff changeset
   211
    override def invoke(session: Session, args: List[Bytes]): List[Bytes] = {
f5896dea6fce more direct check of bibtex entries via Isabelle/Scala;
wenzelm
parents: 77025
diff changeset
   212
      val sessions = session.resources.sessions_structure
f5896dea6fce more direct check of bibtex entries via Isabelle/Scala;
wenzelm
parents: 77025
diff changeset
   213
      val id = Value.Long.parse(Library.the_single(args).text)
f5896dea6fce more direct check of bibtex entries via Isabelle/Scala;
wenzelm
parents: 77025
diff changeset
   214
      val qualifier =
f5896dea6fce more direct check of bibtex entries via Isabelle/Scala;
wenzelm
parents: 77025
diff changeset
   215
        session.get_state().lookup_id(id) match {
f5896dea6fce more direct check of bibtex entries via Isabelle/Scala;
wenzelm
parents: 77025
diff changeset
   216
          case None => Sessions.DRAFT
f5896dea6fce more direct check of bibtex entries via Isabelle/Scala;
wenzelm
parents: 77025
diff changeset
   217
          case Some(st) => sessions.theory_qualifier(st.command.node_name.theory)
f5896dea6fce more direct check of bibtex entries via Isabelle/Scala;
wenzelm
parents: 77025
diff changeset
   218
        }
f5896dea6fce more direct check of bibtex entries via Isabelle/Scala;
wenzelm
parents: 77025
diff changeset
   219
      val res =
f5896dea6fce more direct check of bibtex entries via Isabelle/Scala;
wenzelm
parents: 77025
diff changeset
   220
        if (qualifier == Sessions.DRAFT || !sessions.defined(qualifier)) Nil
77030
d7dc5b1e4381 proper positions for Isabelle/ML, instead of Isabelle/Scala;
wenzelm
parents: 77029
diff changeset
   221
        else qualifier :: sessions(qualifier).bibtex_entries.entries.map(_.encode)
77028
f5896dea6fce more direct check of bibtex entries via Isabelle/Scala;
wenzelm
parents: 77025
diff changeset
   222
      res.map(Bytes.apply)
f5896dea6fce more direct check of bibtex entries via Isabelle/Scala;
wenzelm
parents: 77025
diff changeset
   223
    }
f5896dea6fce more direct check of bibtex entries via Isabelle/Scala;
wenzelm
parents: 77025
diff changeset
   224
  }
f5896dea6fce more direct check of bibtex entries via Isabelle/Scala;
wenzelm
parents: 77025
diff changeset
   225
64828
e837f6bf653c clarified modules;
wenzelm
parents: 64824
diff changeset
   226
e837f6bf653c clarified modules;
wenzelm
parents: 64824
diff changeset
   227
58523
937c479e62fe some support for bibtex files;
wenzelm
parents:
diff changeset
   228
  /** content **/
937c479e62fe some support for bibtex files;
wenzelm
parents:
diff changeset
   229
58529
cd4439d8799c support for bibtex token markup;
wenzelm
parents: 58528
diff changeset
   230
  private val months = List(
58523
937c479e62fe some support for bibtex files;
wenzelm
parents:
diff changeset
   231
    "jan",
937c479e62fe some support for bibtex files;
wenzelm
parents:
diff changeset
   232
    "feb",
937c479e62fe some support for bibtex files;
wenzelm
parents:
diff changeset
   233
    "mar",
937c479e62fe some support for bibtex files;
wenzelm
parents:
diff changeset
   234
    "apr",
937c479e62fe some support for bibtex files;
wenzelm
parents:
diff changeset
   235
    "may",
937c479e62fe some support for bibtex files;
wenzelm
parents:
diff changeset
   236
    "jun",
937c479e62fe some support for bibtex files;
wenzelm
parents:
diff changeset
   237
    "jul",
937c479e62fe some support for bibtex files;
wenzelm
parents:
diff changeset
   238
    "aug",
937c479e62fe some support for bibtex files;
wenzelm
parents:
diff changeset
   239
    "sep",
937c479e62fe some support for bibtex files;
wenzelm
parents:
diff changeset
   240
    "oct",
937c479e62fe some support for bibtex files;
wenzelm
parents:
diff changeset
   241
    "nov",
937c479e62fe some support for bibtex files;
wenzelm
parents:
diff changeset
   242
    "dec")
58529
cd4439d8799c support for bibtex token markup;
wenzelm
parents: 58528
diff changeset
   243
  def is_month(s: String): Boolean = months.contains(s.toLowerCase)
58523
937c479e62fe some support for bibtex files;
wenzelm
parents:
diff changeset
   244
58529
cd4439d8799c support for bibtex token markup;
wenzelm
parents: 58528
diff changeset
   245
  private val commands = List("preamble", "string")
cd4439d8799c support for bibtex token markup;
wenzelm
parents: 58528
diff changeset
   246
  def is_command(s: String): Boolean = commands.contains(s.toLowerCase)
58523
937c479e62fe some support for bibtex files;
wenzelm
parents:
diff changeset
   247
77031
02738f4333ee clarified signature;
wenzelm
parents: 77030
diff changeset
   248
  sealed case class Entry_Type(
58526
f05ccce3eca2 SideKick parser for bibtex entries;
wenzelm
parents: 58524
diff changeset
   249
    kind: String,
58523
937c479e62fe some support for bibtex files;
wenzelm
parents:
diff changeset
   250
    required: List[String],
937c479e62fe some support for bibtex files;
wenzelm
parents:
diff changeset
   251
    optional_crossref: List[String],
75393
87ebf5a50283 clarified formatting, for the sake of scala3;
wenzelm
parents: 73730
diff changeset
   252
    optional_other: List[String]
87ebf5a50283 clarified formatting, for the sake of scala3;
wenzelm
parents: 73730
diff changeset
   253
  ) {
70230
8ba266889dee more bibtex fields;
wenzelm
parents: 69367
diff changeset
   254
    val optional_standard: List[String] = List("url", "doi", "ee")
8ba266889dee more bibtex fields;
wenzelm
parents: 69367
diff changeset
   255
58533
dfbfc92118eb fields are case-insensitive;
wenzelm
parents: 58532
diff changeset
   256
    def is_required(s: String): Boolean = required.contains(s.toLowerCase)
58529
cd4439d8799c support for bibtex token markup;
wenzelm
parents: 58528
diff changeset
   257
    def is_optional(s: String): Boolean =
70230
8ba266889dee more bibtex fields;
wenzelm
parents: 69367
diff changeset
   258
      optional_crossref.contains(s.toLowerCase) ||
8ba266889dee more bibtex fields;
wenzelm
parents: 69367
diff changeset
   259
      optional_other.contains(s.toLowerCase) ||
8ba266889dee more bibtex fields;
wenzelm
parents: 69367
diff changeset
   260
      optional_standard.contains(s.toLowerCase)
58529
cd4439d8799c support for bibtex token markup;
wenzelm
parents: 58528
diff changeset
   261
70230
8ba266889dee more bibtex fields;
wenzelm
parents: 69367
diff changeset
   262
    def fields: List[String] =
8ba266889dee more bibtex fields;
wenzelm
parents: 69367
diff changeset
   263
      required ::: optional_crossref ::: optional_other ::: optional_standard
8ba266889dee more bibtex fields;
wenzelm
parents: 69367
diff changeset
   264
58524
f805b366a497 context menu for bibtex entries;
wenzelm
parents: 58523
diff changeset
   265
    def template: String =
58526
f05ccce3eca2 SideKick parser for bibtex entries;
wenzelm
parents: 58524
diff changeset
   266
      "@" + kind + "{,\n" + fields.map(x => "  " + x + " = {},\n").mkString + "}\n"
58524
f805b366a497 context menu for bibtex entries;
wenzelm
parents: 58523
diff changeset
   267
  }
58523
937c479e62fe some support for bibtex files;
wenzelm
parents:
diff changeset
   268
77031
02738f4333ee clarified signature;
wenzelm
parents: 77030
diff changeset
   269
  val known_entries: List[Entry_Type] =
58524
f805b366a497 context menu for bibtex entries;
wenzelm
parents: 58523
diff changeset
   270
    List(
77031
02738f4333ee clarified signature;
wenzelm
parents: 77030
diff changeset
   271
      Entry_Type("Article",
58524
f805b366a497 context menu for bibtex entries;
wenzelm
parents: 58523
diff changeset
   272
        List("author", "title"),
f805b366a497 context menu for bibtex entries;
wenzelm
parents: 58523
diff changeset
   273
        List("journal", "year"),
f805b366a497 context menu for bibtex entries;
wenzelm
parents: 58523
diff changeset
   274
        List("volume", "number", "pages", "month", "note")),
77031
02738f4333ee clarified signature;
wenzelm
parents: 77030
diff changeset
   275
      Entry_Type("InProceedings",
58524
f805b366a497 context menu for bibtex entries;
wenzelm
parents: 58523
diff changeset
   276
        List("author", "title"),
f805b366a497 context menu for bibtex entries;
wenzelm
parents: 58523
diff changeset
   277
        List("booktitle", "year"),
f805b366a497 context menu for bibtex entries;
wenzelm
parents: 58523
diff changeset
   278
        List("editor", "volume", "number", "series", "pages", "month", "address",
f805b366a497 context menu for bibtex entries;
wenzelm
parents: 58523
diff changeset
   279
          "organization", "publisher", "note")),
77031
02738f4333ee clarified signature;
wenzelm
parents: 77030
diff changeset
   280
      Entry_Type("InCollection",
58524
f805b366a497 context menu for bibtex entries;
wenzelm
parents: 58523
diff changeset
   281
        List("author", "title", "booktitle"),
f805b366a497 context menu for bibtex entries;
wenzelm
parents: 58523
diff changeset
   282
        List("publisher", "year"),
f805b366a497 context menu for bibtex entries;
wenzelm
parents: 58523
diff changeset
   283
        List("editor", "volume", "number", "series", "type", "chapter", "pages",
f805b366a497 context menu for bibtex entries;
wenzelm
parents: 58523
diff changeset
   284
          "edition", "month", "address", "note")),
77031
02738f4333ee clarified signature;
wenzelm
parents: 77030
diff changeset
   285
      Entry_Type("InBook",
58524
f805b366a497 context menu for bibtex entries;
wenzelm
parents: 58523
diff changeset
   286
        List("author", "editor", "title", "chapter"),
f805b366a497 context menu for bibtex entries;
wenzelm
parents: 58523
diff changeset
   287
        List("publisher", "year"),
f805b366a497 context menu for bibtex entries;
wenzelm
parents: 58523
diff changeset
   288
        List("volume", "number", "series", "type", "address", "edition", "month", "pages", "note")),
77031
02738f4333ee clarified signature;
wenzelm
parents: 77030
diff changeset
   289
      Entry_Type("Proceedings",
58524
f805b366a497 context menu for bibtex entries;
wenzelm
parents: 58523
diff changeset
   290
        List("title", "year"),
f805b366a497 context menu for bibtex entries;
wenzelm
parents: 58523
diff changeset
   291
        List(),
f805b366a497 context menu for bibtex entries;
wenzelm
parents: 58523
diff changeset
   292
        List("booktitle", "editor", "volume", "number", "series", "address", "month",
f805b366a497 context menu for bibtex entries;
wenzelm
parents: 58523
diff changeset
   293
          "organization", "publisher", "note")),
77031
02738f4333ee clarified signature;
wenzelm
parents: 77030
diff changeset
   294
      Entry_Type("Book",
58524
f805b366a497 context menu for bibtex entries;
wenzelm
parents: 58523
diff changeset
   295
        List("author", "editor", "title"),
f805b366a497 context menu for bibtex entries;
wenzelm
parents: 58523
diff changeset
   296
        List("publisher", "year"),
f805b366a497 context menu for bibtex entries;
wenzelm
parents: 58523
diff changeset
   297
        List("volume", "number", "series", "address", "edition", "month", "note")),
77031
02738f4333ee clarified signature;
wenzelm
parents: 77030
diff changeset
   298
      Entry_Type("Booklet",
58524
f805b366a497 context menu for bibtex entries;
wenzelm
parents: 58523
diff changeset
   299
        List("title"),
f805b366a497 context menu for bibtex entries;
wenzelm
parents: 58523
diff changeset
   300
        List(),
f805b366a497 context menu for bibtex entries;
wenzelm
parents: 58523
diff changeset
   301
        List("author", "howpublished", "address", "month", "year", "note")),
77031
02738f4333ee clarified signature;
wenzelm
parents: 77030
diff changeset
   302
      Entry_Type("PhdThesis",
58524
f805b366a497 context menu for bibtex entries;
wenzelm
parents: 58523
diff changeset
   303
        List("author", "title", "school", "year"),
f805b366a497 context menu for bibtex entries;
wenzelm
parents: 58523
diff changeset
   304
        List(),
f805b366a497 context menu for bibtex entries;
wenzelm
parents: 58523
diff changeset
   305
        List("type", "address", "month", "note")),
77031
02738f4333ee clarified signature;
wenzelm
parents: 77030
diff changeset
   306
      Entry_Type("MastersThesis",
58524
f805b366a497 context menu for bibtex entries;
wenzelm
parents: 58523
diff changeset
   307
        List("author", "title", "school", "year"),
f805b366a497 context menu for bibtex entries;
wenzelm
parents: 58523
diff changeset
   308
        List(),
f805b366a497 context menu for bibtex entries;
wenzelm
parents: 58523
diff changeset
   309
        List("type", "address", "month", "note")),
77031
02738f4333ee clarified signature;
wenzelm
parents: 77030
diff changeset
   310
      Entry_Type("TechReport",
58524
f805b366a497 context menu for bibtex entries;
wenzelm
parents: 58523
diff changeset
   311
        List("author", "title", "institution", "year"),
f805b366a497 context menu for bibtex entries;
wenzelm
parents: 58523
diff changeset
   312
        List(),
f805b366a497 context menu for bibtex entries;
wenzelm
parents: 58523
diff changeset
   313
        List("type", "number", "address", "month", "note")),
77031
02738f4333ee clarified signature;
wenzelm
parents: 77030
diff changeset
   314
      Entry_Type("Manual",
58524
f805b366a497 context menu for bibtex entries;
wenzelm
parents: 58523
diff changeset
   315
        List("title"),
f805b366a497 context menu for bibtex entries;
wenzelm
parents: 58523
diff changeset
   316
        List(),
f805b366a497 context menu for bibtex entries;
wenzelm
parents: 58523
diff changeset
   317
        List("author", "organization", "address", "edition", "month", "year", "note")),
77031
02738f4333ee clarified signature;
wenzelm
parents: 77030
diff changeset
   318
      Entry_Type("Unpublished",
58524
f805b366a497 context menu for bibtex entries;
wenzelm
parents: 58523
diff changeset
   319
        List("author", "title", "note"),
f805b366a497 context menu for bibtex entries;
wenzelm
parents: 58523
diff changeset
   320
        List(),
f805b366a497 context menu for bibtex entries;
wenzelm
parents: 58523
diff changeset
   321
        List("month", "year")),
77031
02738f4333ee clarified signature;
wenzelm
parents: 77030
diff changeset
   322
      Entry_Type("Misc",
58524
f805b366a497 context menu for bibtex entries;
wenzelm
parents: 58523
diff changeset
   323
        List(),
f805b366a497 context menu for bibtex entries;
wenzelm
parents: 58523
diff changeset
   324
        List(),
f805b366a497 context menu for bibtex entries;
wenzelm
parents: 58523
diff changeset
   325
        List("author", "title", "howpublished", "month", "year", "note")))
58523
937c479e62fe some support for bibtex files;
wenzelm
parents:
diff changeset
   326
77031
02738f4333ee clarified signature;
wenzelm
parents: 77030
diff changeset
   327
  def known_entry(kind: String): Option[Entry_Type] =
66150
c2e19b9e1398 tuned signature;
wenzelm
parents: 66118
diff changeset
   328
    known_entries.find(entry => entry.kind.toLowerCase == kind.toLowerCase)
58529
cd4439d8799c support for bibtex token markup;
wenzelm
parents: 58528
diff changeset
   329
58523
937c479e62fe some support for bibtex files;
wenzelm
parents:
diff changeset
   330
937c479e62fe some support for bibtex files;
wenzelm
parents:
diff changeset
   331
937c479e62fe some support for bibtex files;
wenzelm
parents:
diff changeset
   332
  /** tokens and chunks **/
937c479e62fe some support for bibtex files;
wenzelm
parents:
diff changeset
   333
75393
87ebf5a50283 clarified formatting, for the sake of scala3;
wenzelm
parents: 73730
diff changeset
   334
  object Token {
87ebf5a50283 clarified formatting, for the sake of scala3;
wenzelm
parents: 73730
diff changeset
   335
    object Kind extends Enumeration {
58529
cd4439d8799c support for bibtex token markup;
wenzelm
parents: 58528
diff changeset
   336
      val COMMAND = Value("command")
cd4439d8799c support for bibtex token markup;
wenzelm
parents: 58528
diff changeset
   337
      val ENTRY = Value("entry")
58523
937c479e62fe some support for bibtex files;
wenzelm
parents:
diff changeset
   338
      val KEYWORD = Value("keyword")
937c479e62fe some support for bibtex files;
wenzelm
parents:
diff changeset
   339
      val NAT = Value("natural number")
58529
cd4439d8799c support for bibtex token markup;
wenzelm
parents: 58528
diff changeset
   340
      val STRING = Value("string")
58531
454962877285 more explicit chunk name;
wenzelm
parents: 58530
diff changeset
   341
      val NAME = Value("name")
58523
937c479e62fe some support for bibtex files;
wenzelm
parents:
diff changeset
   342
      val IDENT = Value("identifier")
58535
4815429974fe more explicit comments;
wenzelm
parents: 58534
diff changeset
   343
      val SPACE = Value("white space")
4815429974fe more explicit comments;
wenzelm
parents: 58534
diff changeset
   344
      val COMMENT = Value("ignored text")
58523
937c479e62fe some support for bibtex files;
wenzelm
parents:
diff changeset
   345
      val ERROR = Value("bad input")
937c479e62fe some support for bibtex files;
wenzelm
parents:
diff changeset
   346
    }
937c479e62fe some support for bibtex files;
wenzelm
parents:
diff changeset
   347
  }
937c479e62fe some support for bibtex files;
wenzelm
parents:
diff changeset
   348
75393
87ebf5a50283 clarified formatting, for the sake of scala3;
wenzelm
parents: 73730
diff changeset
   349
  sealed case class Token(kind: Token.Kind.Value, source: String) {
58530
7ee248f19ca9 clarified Chunk -- avoid ooddities;
wenzelm
parents: 58529
diff changeset
   350
    def is_kind: Boolean =
7ee248f19ca9 clarified Chunk -- avoid ooddities;
wenzelm
parents: 58529
diff changeset
   351
      kind == Token.Kind.COMMAND ||
7ee248f19ca9 clarified Chunk -- avoid ooddities;
wenzelm
parents: 58529
diff changeset
   352
      kind == Token.Kind.ENTRY ||
7ee248f19ca9 clarified Chunk -- avoid ooddities;
wenzelm
parents: 58529
diff changeset
   353
      kind == Token.Kind.IDENT
58531
454962877285 more explicit chunk name;
wenzelm
parents: 58530
diff changeset
   354
    def is_name: Boolean =
454962877285 more explicit chunk name;
wenzelm
parents: 58530
diff changeset
   355
      kind == Token.Kind.NAME ||
454962877285 more explicit chunk name;
wenzelm
parents: 58530
diff changeset
   356
      kind == Token.Kind.IDENT
58535
4815429974fe more explicit comments;
wenzelm
parents: 58534
diff changeset
   357
    def is_ignored: Boolean =
4815429974fe more explicit comments;
wenzelm
parents: 58534
diff changeset
   358
      kind == Token.Kind.SPACE ||
4815429974fe more explicit comments;
wenzelm
parents: 58534
diff changeset
   359
      kind == Token.Kind.COMMENT
67276
abac35ee3565 clarified positions;
wenzelm
parents: 67275
diff changeset
   360
    def is_malformed: Boolean =
abac35ee3565 clarified positions;
wenzelm
parents: 67275
diff changeset
   361
      kind == Token.Kind.ERROR
abac35ee3565 clarified positions;
wenzelm
parents: 67275
diff changeset
   362
    def is_open: Boolean =
abac35ee3565 clarified positions;
wenzelm
parents: 67275
diff changeset
   363
      kind == Token.Kind.KEYWORD && (source == "{" || source == "(")
58523
937c479e62fe some support for bibtex files;
wenzelm
parents:
diff changeset
   364
  }
937c479e62fe some support for bibtex files;
wenzelm
parents:
diff changeset
   365
75393
87ebf5a50283 clarified formatting, for the sake of scala3;
wenzelm
parents: 73730
diff changeset
   366
  case class Chunk(kind: String, tokens: List[Token]) {
77010
fead2b33acdc tuned signature: fewer warnings in IntelliJ IDEA;
wenzelm
parents: 77007
diff changeset
   367
    val source: String = tokens.map(_.source).mkString
58526
f05ccce3eca2 SideKick parser for bibtex entries;
wenzelm
parents: 58524
diff changeset
   368
58530
7ee248f19ca9 clarified Chunk -- avoid ooddities;
wenzelm
parents: 58529
diff changeset
   369
    private val content: Option[List[Token]] =
58523
937c479e62fe some support for bibtex files;
wenzelm
parents:
diff changeset
   370
      tokens match {
59319
wenzelm
parents: 58609
diff changeset
   371
        case Token(Token.Kind.KEYWORD, "@") :: body if body.nonEmpty =>
58529
cd4439d8799c support for bibtex token markup;
wenzelm
parents: 58528
diff changeset
   372
          (body.init.filterNot(_.is_ignored), body.last) match {
58530
7ee248f19ca9 clarified Chunk -- avoid ooddities;
wenzelm
parents: 58529
diff changeset
   373
            case (tok :: Token(Token.Kind.KEYWORD, "{") :: toks, Token(Token.Kind.KEYWORD, "}"))
7ee248f19ca9 clarified Chunk -- avoid ooddities;
wenzelm
parents: 58529
diff changeset
   374
            if tok.is_kind => Some(toks)
7ee248f19ca9 clarified Chunk -- avoid ooddities;
wenzelm
parents: 58529
diff changeset
   375
7ee248f19ca9 clarified Chunk -- avoid ooddities;
wenzelm
parents: 58529
diff changeset
   376
            case (tok :: Token(Token.Kind.KEYWORD, "(") :: toks, Token(Token.Kind.KEYWORD, ")"))
7ee248f19ca9 clarified Chunk -- avoid ooddities;
wenzelm
parents: 58529
diff changeset
   377
            if tok.is_kind => Some(toks)
7ee248f19ca9 clarified Chunk -- avoid ooddities;
wenzelm
parents: 58529
diff changeset
   378
58528
7d6b8f8893e8 more explicit item kind;
wenzelm
parents: 58527
diff changeset
   379
            case _ => None
58523
937c479e62fe some support for bibtex files;
wenzelm
parents:
diff changeset
   380
          }
58528
7d6b8f8893e8 more explicit item kind;
wenzelm
parents: 58527
diff changeset
   381
        case _ => None
58526
f05ccce3eca2 SideKick parser for bibtex entries;
wenzelm
parents: 58524
diff changeset
   382
      }
f05ccce3eca2 SideKick parser for bibtex entries;
wenzelm
parents: 58524
diff changeset
   383
f05ccce3eca2 SideKick parser for bibtex entries;
wenzelm
parents: 58524
diff changeset
   384
    def name: String =
58530
7ee248f19ca9 clarified Chunk -- avoid ooddities;
wenzelm
parents: 58529
diff changeset
   385
      content match {
58531
454962877285 more explicit chunk name;
wenzelm
parents: 58530
diff changeset
   386
        case Some(tok :: _) if tok.is_name => tok.source
58523
937c479e62fe some support for bibtex files;
wenzelm
parents:
diff changeset
   387
        case _ => ""
937c479e62fe some support for bibtex files;
wenzelm
parents:
diff changeset
   388
      }
58530
7ee248f19ca9 clarified Chunk -- avoid ooddities;
wenzelm
parents: 58529
diff changeset
   389
67276
abac35ee3565 clarified positions;
wenzelm
parents: 67275
diff changeset
   390
    def heading_length: Int =
abac35ee3565 clarified positions;
wenzelm
parents: 67275
diff changeset
   391
      if (name == "") 1
73359
d8a0e996614b tuned --- fewer warnings;
wenzelm
parents: 73340
diff changeset
   392
      else {
d8a0e996614b tuned --- fewer warnings;
wenzelm
parents: 73340
diff changeset
   393
        tokens.takeWhile(tok => !tok.is_open).foldLeft(0) { case (n, tok) => n + tok.source.length }
d8a0e996614b tuned --- fewer warnings;
wenzelm
parents: 73340
diff changeset
   394
      }
67276
abac35ee3565 clarified positions;
wenzelm
parents: 67275
diff changeset
   395
58530
7ee248f19ca9 clarified Chunk -- avoid ooddities;
wenzelm
parents: 58529
diff changeset
   396
    def is_ignored: Boolean = kind == "" && tokens.forall(_.is_ignored)
76779
caeb732db09f more bibtex errors;
wenzelm
parents: 76778
diff changeset
   397
    def is_malformed: Boolean = tokens.exists(_.is_malformed)
58543
9c1389befa56 maintain Document_Model.bibtex_entries;
wenzelm
parents: 58538
diff changeset
   398
    def is_command: Boolean = Bibtex.is_command(kind) && name != "" && content.isDefined
77031
02738f4333ee clarified signature;
wenzelm
parents: 77030
diff changeset
   399
    def is_entry: Boolean = Bibtex.known_entry(kind).isDefined && name != "" && content.isDefined
58523
937c479e62fe some support for bibtex files;
wenzelm
parents:
diff changeset
   400
  }
937c479e62fe some support for bibtex files;
wenzelm
parents:
diff changeset
   401
937c479e62fe some support for bibtex files;
wenzelm
parents:
diff changeset
   402
937c479e62fe some support for bibtex files;
wenzelm
parents:
diff changeset
   403
937c479e62fe some support for bibtex files;
wenzelm
parents:
diff changeset
   404
  /** parsing **/
937c479e62fe some support for bibtex files;
wenzelm
parents:
diff changeset
   405
937c479e62fe some support for bibtex files;
wenzelm
parents:
diff changeset
   406
  // context of partial line-oriented scans
937c479e62fe some support for bibtex files;
wenzelm
parents:
diff changeset
   407
  abstract class Line_Context
58589
d9350ec0937e tuned signature;
wenzelm
parents: 58544
diff changeset
   408
  case object Ignored extends Line_Context
58590
472b9fbcc7f0 back to liberal spaces of official syntax (in contrast to 4b190c763097), e.g. relevant for easychair.bib;
wenzelm
parents: 58589
diff changeset
   409
  case object At extends Line_Context
472b9fbcc7f0 back to liberal spaces of official syntax (in contrast to 4b190c763097), e.g. relevant for easychair.bib;
wenzelm
parents: 58589
diff changeset
   410
  case class Item_Start(kind: String) extends Line_Context
58591
3c1a8c1c6b3b more accurate item name, depending on kind;
wenzelm
parents: 58590
diff changeset
   411
  case class Item_Open(kind: String, end: String) extends Line_Context
3c1a8c1c6b3b more accurate item name, depending on kind;
wenzelm
parents: 58590
diff changeset
   412
  case class Item(kind: String, end: String, delim: Delimited) extends Line_Context
58590
472b9fbcc7f0 back to liberal spaces of official syntax (in contrast to 4b190c763097), e.g. relevant for easychair.bib;
wenzelm
parents: 58589
diff changeset
   413
58528
7d6b8f8893e8 more explicit item kind;
wenzelm
parents: 58527
diff changeset
   414
  case class Delimited(quoted: Boolean, depth: Int)
77010
fead2b33acdc tuned signature: fewer warnings in IntelliJ IDEA;
wenzelm
parents: 77007
diff changeset
   415
  val Closed: Delimited = Delimited(false, 0)
58523
937c479e62fe some support for bibtex files;
wenzelm
parents:
diff changeset
   416
937c479e62fe some support for bibtex files;
wenzelm
parents:
diff changeset
   417
  private def token(kind: Token.Kind.Value)(source: String): Token = Token(kind, source)
937c479e62fe some support for bibtex files;
wenzelm
parents:
diff changeset
   418
  private def keyword(source: String): Token = Token(Token.Kind.KEYWORD, source)
937c479e62fe some support for bibtex files;
wenzelm
parents:
diff changeset
   419
937c479e62fe some support for bibtex files;
wenzelm
parents:
diff changeset
   420
68224
1f7308050349 prefer HTTPS;
wenzelm
parents: 67301
diff changeset
   421
  // See also https://ctan.org/tex-archive/biblio/bibtex/base/bibtex.web
58523
937c479e62fe some support for bibtex files;
wenzelm
parents:
diff changeset
   422
  // module @<Scan for and process a \.{.bib} command or database entry@>.
937c479e62fe some support for bibtex files;
wenzelm
parents:
diff changeset
   423
75393
87ebf5a50283 clarified formatting, for the sake of scala3;
wenzelm
parents: 73730
diff changeset
   424
  object Parsers extends RegexParsers {
58523
937c479e62fe some support for bibtex files;
wenzelm
parents:
diff changeset
   425
    /* white space and comments */
937c479e62fe some support for bibtex files;
wenzelm
parents:
diff changeset
   426
937c479e62fe some support for bibtex files;
wenzelm
parents:
diff changeset
   427
    override val whiteSpace = "".r
937c479e62fe some support for bibtex files;
wenzelm
parents:
diff changeset
   428
58535
4815429974fe more explicit comments;
wenzelm
parents: 58534
diff changeset
   429
    private val space = """[ \t\n\r]+""".r ^^ token(Token.Kind.SPACE)
58590
472b9fbcc7f0 back to liberal spaces of official syntax (in contrast to 4b190c763097), e.g. relevant for easychair.bib;
wenzelm
parents: 58589
diff changeset
   430
    private val spaces = rep(space)
58523
937c479e62fe some support for bibtex files;
wenzelm
parents:
diff changeset
   431
58528
7d6b8f8893e8 more explicit item kind;
wenzelm
parents: 58527
diff changeset
   432
58535
4815429974fe more explicit comments;
wenzelm
parents: 58534
diff changeset
   433
    /* ignored text */
58528
7d6b8f8893e8 more explicit item kind;
wenzelm
parents: 58527
diff changeset
   434
7d6b8f8893e8 more explicit item kind;
wenzelm
parents: 58527
diff changeset
   435
    private val ignored: Parser[Chunk] =
58609
d0cb70d66bc1 removed pointless regexp flags;
wenzelm
parents: 58596
diff changeset
   436
      rep1("""(?i)([^@]+|@[ \t]*comment)""".r) ^^ {
58535
4815429974fe more explicit comments;
wenzelm
parents: 58534
diff changeset
   437
        case ss => Chunk("", List(Token(Token.Kind.COMMENT, ss.mkString))) }
58523
937c479e62fe some support for bibtex files;
wenzelm
parents:
diff changeset
   438
58536
402a8e8107a7 more total chunk_line: recovery via ignored_line;
wenzelm
parents: 58535
diff changeset
   439
    private def ignored_line: Parser[(Chunk, Line_Context)] =
58589
d9350ec0937e tuned signature;
wenzelm
parents: 58544
diff changeset
   440
      ignored ^^ { case a => (a, Ignored) }
58536
402a8e8107a7 more total chunk_line: recovery via ignored_line;
wenzelm
parents: 58535
diff changeset
   441
58523
937c479e62fe some support for bibtex files;
wenzelm
parents:
diff changeset
   442
937c479e62fe some support for bibtex files;
wenzelm
parents:
diff changeset
   443
    /* delimited string: outermost "..." or {...} and body with balanced {...} */
937c479e62fe some support for bibtex files;
wenzelm
parents:
diff changeset
   444
58534
573ce5ad13bc clarified nesting of delimiters;
wenzelm
parents: 58533
diff changeset
   445
    // see also bibtex.web: scan_a_field_token_and_eat_white, scan_balanced_braces
58523
937c479e62fe some support for bibtex files;
wenzelm
parents:
diff changeset
   446
    private def delimited_depth(delim: Delimited): Parser[(String, Delimited)] =
75393
87ebf5a50283 clarified formatting, for the sake of scala3;
wenzelm
parents: 73730
diff changeset
   447
      new Parser[(String, Delimited)] {
73120
c3589f2dff31 more informative errors: simplify diagnosis of spurious failures reported by users;
wenzelm
parents: 72957
diff changeset
   448
        require(if (delim.quoted) delim.depth > 0 else delim.depth >= 0,
c3589f2dff31 more informative errors: simplify diagnosis of spurious failures reported by users;
wenzelm
parents: 72957
diff changeset
   449
          "bad delimiter depth")
58523
937c479e62fe some support for bibtex files;
wenzelm
parents:
diff changeset
   450
77010
fead2b33acdc tuned signature: fewer warnings in IntelliJ IDEA;
wenzelm
parents: 77007
diff changeset
   451
        def apply(in: Input): ParseResult[(String, Delimited)] = {
58523
937c479e62fe some support for bibtex files;
wenzelm
parents:
diff changeset
   452
          val start = in.offset
937c479e62fe some support for bibtex files;
wenzelm
parents:
diff changeset
   453
          val end = in.source.length
937c479e62fe some support for bibtex files;
wenzelm
parents:
diff changeset
   454
937c479e62fe some support for bibtex files;
wenzelm
parents:
diff changeset
   455
          var i = start
937c479e62fe some support for bibtex files;
wenzelm
parents:
diff changeset
   456
          var q = delim.quoted
937c479e62fe some support for bibtex files;
wenzelm
parents:
diff changeset
   457
          var d = delim.depth
937c479e62fe some support for bibtex files;
wenzelm
parents:
diff changeset
   458
          var finished = false
937c479e62fe some support for bibtex files;
wenzelm
parents:
diff changeset
   459
          while (!finished && i < end) {
937c479e62fe some support for bibtex files;
wenzelm
parents:
diff changeset
   460
            val c = in.source.charAt(i)
58534
573ce5ad13bc clarified nesting of delimiters;
wenzelm
parents: 58533
diff changeset
   461
58523
937c479e62fe some support for bibtex files;
wenzelm
parents:
diff changeset
   462
            if (c == '"' && d == 0) { i += 1; d = 1; q = true }
58532
af2fc25662b6 clarified nesting of delimiters;
wenzelm
parents: 58531
diff changeset
   463
            else if (c == '"' && d == 1 && q) {
af2fc25662b6 clarified nesting of delimiters;
wenzelm
parents: 58531
diff changeset
   464
              i += 1; d = 0; q = false; finished = true
af2fc25662b6 clarified nesting of delimiters;
wenzelm
parents: 58531
diff changeset
   465
            }
58523
937c479e62fe some support for bibtex files;
wenzelm
parents:
diff changeset
   466
            else if (c == '{') { i += 1; d += 1 }
58534
573ce5ad13bc clarified nesting of delimiters;
wenzelm
parents: 58533
diff changeset
   467
            else if (c == '}') {
573ce5ad13bc clarified nesting of delimiters;
wenzelm
parents: 58533
diff changeset
   468
              if (d == 1 && !q || d > 1) { i += 1; d -= 1; if (d == 0) finished = true }
573ce5ad13bc clarified nesting of delimiters;
wenzelm
parents: 58533
diff changeset
   469
              else {i = start; finished = true }
58532
af2fc25662b6 clarified nesting of delimiters;
wenzelm
parents: 58531
diff changeset
   470
            }
58523
937c479e62fe some support for bibtex files;
wenzelm
parents:
diff changeset
   471
            else if (d > 0) i += 1
937c479e62fe some support for bibtex files;
wenzelm
parents:
diff changeset
   472
            else finished = true
937c479e62fe some support for bibtex files;
wenzelm
parents:
diff changeset
   473
          }
937c479e62fe some support for bibtex files;
wenzelm
parents:
diff changeset
   474
          if (i == start) Failure("bad input", in)
58528
7d6b8f8893e8 more explicit item kind;
wenzelm
parents: 58527
diff changeset
   475
          else {
7d6b8f8893e8 more explicit item kind;
wenzelm
parents: 58527
diff changeset
   476
            val s = in.source.subSequence(start, i).toString
7d6b8f8893e8 more explicit item kind;
wenzelm
parents: 58527
diff changeset
   477
            Success((s, Delimited(q, d)), in.drop(i - start))
7d6b8f8893e8 more explicit item kind;
wenzelm
parents: 58527
diff changeset
   478
          }
58523
937c479e62fe some support for bibtex files;
wenzelm
parents:
diff changeset
   479
        }
937c479e62fe some support for bibtex files;
wenzelm
parents:
diff changeset
   480
      }.named("delimited_depth")
937c479e62fe some support for bibtex files;
wenzelm
parents:
diff changeset
   481
58528
7d6b8f8893e8 more explicit item kind;
wenzelm
parents: 58527
diff changeset
   482
    private def delimited: Parser[Token] =
7d6b8f8893e8 more explicit item kind;
wenzelm
parents: 58527
diff changeset
   483
      delimited_depth(Closed) ^?
7d6b8f8893e8 more explicit item kind;
wenzelm
parents: 58527
diff changeset
   484
        { case (s, delim) if delim == Closed => Token(Token.Kind.STRING, s) }
58523
937c479e62fe some support for bibtex files;
wenzelm
parents:
diff changeset
   485
58534
573ce5ad13bc clarified nesting of delimiters;
wenzelm
parents: 58533
diff changeset
   486
    private def recover_delimited: Parser[Token] =
58609
d0cb70d66bc1 removed pointless regexp flags;
wenzelm
parents: 58596
diff changeset
   487
      """["{][^@]*""".r ^^ token(Token.Kind.ERROR)
58534
573ce5ad13bc clarified nesting of delimiters;
wenzelm
parents: 58533
diff changeset
   488
58590
472b9fbcc7f0 back to liberal spaces of official syntax (in contrast to 4b190c763097), e.g. relevant for easychair.bib;
wenzelm
parents: 58589
diff changeset
   489
    def delimited_line(ctxt: Item): Parser[(Chunk, Line_Context)] =
472b9fbcc7f0 back to liberal spaces of official syntax (in contrast to 4b190c763097), e.g. relevant for easychair.bib;
wenzelm
parents: 58589
diff changeset
   490
      delimited_depth(ctxt.delim) ^^ { case (s, delim1) =>
472b9fbcc7f0 back to liberal spaces of official syntax (in contrast to 4b190c763097), e.g. relevant for easychair.bib;
wenzelm
parents: 58589
diff changeset
   491
        (Chunk(ctxt.kind, List(Token(Token.Kind.STRING, s))), ctxt.copy(delim = delim1)) } |
472b9fbcc7f0 back to liberal spaces of official syntax (in contrast to 4b190c763097), e.g. relevant for easychair.bib;
wenzelm
parents: 58589
diff changeset
   492
      recover_delimited ^^ { case a => (Chunk(ctxt.kind, List(a)), Ignored) }
58523
937c479e62fe some support for bibtex files;
wenzelm
parents:
diff changeset
   493
937c479e62fe some support for bibtex files;
wenzelm
parents:
diff changeset
   494
937c479e62fe some support for bibtex files;
wenzelm
parents:
diff changeset
   495
    /* other tokens */
937c479e62fe some support for bibtex files;
wenzelm
parents:
diff changeset
   496
937c479e62fe some support for bibtex files;
wenzelm
parents:
diff changeset
   497
    private val at = "@" ^^ keyword
937c479e62fe some support for bibtex files;
wenzelm
parents:
diff changeset
   498
937c479e62fe some support for bibtex files;
wenzelm
parents:
diff changeset
   499
    private val nat = "[0-9]+".r ^^ token(Token.Kind.NAT)
937c479e62fe some support for bibtex files;
wenzelm
parents:
diff changeset
   500
58591
3c1a8c1c6b3b more accurate item name, depending on kind;
wenzelm
parents: 58590
diff changeset
   501
    private val name = """[\x21-\x7f&&[^"#%'(),={}]]+""".r ^^ token(Token.Kind.NAME)
3c1a8c1c6b3b more accurate item name, depending on kind;
wenzelm
parents: 58590
diff changeset
   502
58529
cd4439d8799c support for bibtex token markup;
wenzelm
parents: 58528
diff changeset
   503
    private val identifier =
cd4439d8799c support for bibtex token markup;
wenzelm
parents: 58528
diff changeset
   504
      """[\x21-\x7f&&[^"#%'(),={}0-9]][\x21-\x7f&&[^"#%'(),={}]]*""".r
cd4439d8799c support for bibtex token markup;
wenzelm
parents: 58528
diff changeset
   505
cd4439d8799c support for bibtex token markup;
wenzelm
parents: 58528
diff changeset
   506
    private val ident = identifier ^^ token(Token.Kind.IDENT)
58523
937c479e62fe some support for bibtex files;
wenzelm
parents:
diff changeset
   507
77010
fead2b33acdc tuned signature: fewer warnings in IntelliJ IDEA;
wenzelm
parents: 77007
diff changeset
   508
    val other_token: Parser[Token] = "[=#,]".r ^^ keyword | (nat | (ident | space))
58528
7d6b8f8893e8 more explicit item kind;
wenzelm
parents: 58527
diff changeset
   509
7d6b8f8893e8 more explicit item kind;
wenzelm
parents: 58527
diff changeset
   510
58591
3c1a8c1c6b3b more accurate item name, depending on kind;
wenzelm
parents: 58590
diff changeset
   511
    /* body */
3c1a8c1c6b3b more accurate item name, depending on kind;
wenzelm
parents: 58590
diff changeset
   512
3c1a8c1c6b3b more accurate item name, depending on kind;
wenzelm
parents: 58590
diff changeset
   513
    private val body =
3c1a8c1c6b3b more accurate item name, depending on kind;
wenzelm
parents: 58590
diff changeset
   514
      delimited | (recover_delimited | other_token)
3c1a8c1c6b3b more accurate item name, depending on kind;
wenzelm
parents: 58590
diff changeset
   515
3c1a8c1c6b3b more accurate item name, depending on kind;
wenzelm
parents: 58590
diff changeset
   516
    private def body_line(ctxt: Item) =
3c1a8c1c6b3b more accurate item name, depending on kind;
wenzelm
parents: 58590
diff changeset
   517
      if (ctxt.delim.depth > 0)
3c1a8c1c6b3b more accurate item name, depending on kind;
wenzelm
parents: 58590
diff changeset
   518
        delimited_line(ctxt)
3c1a8c1c6b3b more accurate item name, depending on kind;
wenzelm
parents: 58590
diff changeset
   519
      else
3c1a8c1c6b3b more accurate item name, depending on kind;
wenzelm
parents: 58590
diff changeset
   520
        delimited_line(ctxt) |
3c1a8c1c6b3b more accurate item name, depending on kind;
wenzelm
parents: 58590
diff changeset
   521
        other_token ^^ { case a => (Chunk(ctxt.kind, List(a)), ctxt) } |
3c1a8c1c6b3b more accurate item name, depending on kind;
wenzelm
parents: 58590
diff changeset
   522
        ctxt.end ^^ { case a => (Chunk(ctxt.kind, List(keyword(a))), Ignored) }
3c1a8c1c6b3b more accurate item name, depending on kind;
wenzelm
parents: 58590
diff changeset
   523
3c1a8c1c6b3b more accurate item name, depending on kind;
wenzelm
parents: 58590
diff changeset
   524
58530
7ee248f19ca9 clarified Chunk -- avoid ooddities;
wenzelm
parents: 58529
diff changeset
   525
    /* items: command or entry */
58528
7d6b8f8893e8 more explicit item kind;
wenzelm
parents: 58527
diff changeset
   526
58530
7ee248f19ca9 clarified Chunk -- avoid ooddities;
wenzelm
parents: 58529
diff changeset
   527
    private val item_kind =
58529
cd4439d8799c support for bibtex token markup;
wenzelm
parents: 58528
diff changeset
   528
      identifier ^^ { case a =>
cd4439d8799c support for bibtex token markup;
wenzelm
parents: 58528
diff changeset
   529
        val kind =
cd4439d8799c support for bibtex token markup;
wenzelm
parents: 58528
diff changeset
   530
          if (is_command(a)) Token.Kind.COMMAND
77031
02738f4333ee clarified signature;
wenzelm
parents: 77030
diff changeset
   531
          else if (known_entry(a).isDefined) Token.Kind.ENTRY
58529
cd4439d8799c support for bibtex token markup;
wenzelm
parents: 58528
diff changeset
   532
          else Token.Kind.IDENT
cd4439d8799c support for bibtex token markup;
wenzelm
parents: 58528
diff changeset
   533
        Token(kind, a)
cd4439d8799c support for bibtex token markup;
wenzelm
parents: 58528
diff changeset
   534
      }
cd4439d8799c support for bibtex token markup;
wenzelm
parents: 58528
diff changeset
   535
58591
3c1a8c1c6b3b more accurate item name, depending on kind;
wenzelm
parents: 58590
diff changeset
   536
    private val item_begin =
3c1a8c1c6b3b more accurate item name, depending on kind;
wenzelm
parents: 58590
diff changeset
   537
      "{" ^^ { case a => ("}", keyword(a)) } |
3c1a8c1c6b3b more accurate item name, depending on kind;
wenzelm
parents: 58590
diff changeset
   538
      "(" ^^ { case a => (")", keyword(a)) }
3c1a8c1c6b3b more accurate item name, depending on kind;
wenzelm
parents: 58590
diff changeset
   539
3c1a8c1c6b3b more accurate item name, depending on kind;
wenzelm
parents: 58590
diff changeset
   540
    private def item_name(kind: String) =
3c1a8c1c6b3b more accurate item name, depending on kind;
wenzelm
parents: 58590
diff changeset
   541
      kind.toLowerCase match {
3c1a8c1c6b3b more accurate item name, depending on kind;
wenzelm
parents: 58590
diff changeset
   542
        case "preamble" => failure("")
3c1a8c1c6b3b more accurate item name, depending on kind;
wenzelm
parents: 58590
diff changeset
   543
        case "string" => identifier ^^ token(Token.Kind.NAME)
3c1a8c1c6b3b more accurate item name, depending on kind;
wenzelm
parents: 58590
diff changeset
   544
        case _ => name
3c1a8c1c6b3b more accurate item name, depending on kind;
wenzelm
parents: 58590
diff changeset
   545
      }
3c1a8c1c6b3b more accurate item name, depending on kind;
wenzelm
parents: 58590
diff changeset
   546
58531
454962877285 more explicit chunk name;
wenzelm
parents: 58530
diff changeset
   547
    private val item_start =
58590
472b9fbcc7f0 back to liberal spaces of official syntax (in contrast to 4b190c763097), e.g. relevant for easychair.bib;
wenzelm
parents: 58589
diff changeset
   548
      at ~ spaces ~ item_kind ~ spaces ^^
58528
7d6b8f8893e8 more explicit item kind;
wenzelm
parents: 58527
diff changeset
   549
        { case a ~ b ~ c ~ d => (c.source, List(a) ::: b ::: List(c) ::: d) }
7d6b8f8893e8 more explicit item kind;
wenzelm
parents: 58527
diff changeset
   550
58530
7ee248f19ca9 clarified Chunk -- avoid ooddities;
wenzelm
parents: 58529
diff changeset
   551
    private val item: Parser[Chunk] =
58591
3c1a8c1c6b3b more accurate item name, depending on kind;
wenzelm
parents: 58590
diff changeset
   552
      (item_start ~ item_begin ~ spaces) into
3c1a8c1c6b3b more accurate item name, depending on kind;
wenzelm
parents: 58590
diff changeset
   553
        { case (kind, a) ~ ((end, b)) ~ c =>
3c1a8c1c6b3b more accurate item name, depending on kind;
wenzelm
parents: 58590
diff changeset
   554
            opt(item_name(kind)) ~ rep(body) ~ opt(end ^^ keyword) ^^ {
3c1a8c1c6b3b more accurate item name, depending on kind;
wenzelm
parents: 58590
diff changeset
   555
              case d ~ e ~ f => Chunk(kind, a ::: List(b) ::: c ::: d.toList ::: e ::: f.toList) } }
58528
7d6b8f8893e8 more explicit item kind;
wenzelm
parents: 58527
diff changeset
   556
58530
7ee248f19ca9 clarified Chunk -- avoid ooddities;
wenzelm
parents: 58529
diff changeset
   557
    private val recover_item: Parser[Chunk] =
58609
d0cb70d66bc1 removed pointless regexp flags;
wenzelm
parents: 58596
diff changeset
   558
      at ~ "[^@]*".r ^^ { case a ~ b => Chunk("", List(a, Token(Token.Kind.ERROR, b))) }
58528
7d6b8f8893e8 more explicit item kind;
wenzelm
parents: 58527
diff changeset
   559
58591
3c1a8c1c6b3b more accurate item name, depending on kind;
wenzelm
parents: 58590
diff changeset
   560
3c1a8c1c6b3b more accurate item name, depending on kind;
wenzelm
parents: 58590
diff changeset
   561
    /* chunks */
58523
937c479e62fe some support for bibtex files;
wenzelm
parents:
diff changeset
   562
58528
7d6b8f8893e8 more explicit item kind;
wenzelm
parents: 58527
diff changeset
   563
    val chunk: Parser[Chunk] = ignored | (item | recover_item)
58523
937c479e62fe some support for bibtex files;
wenzelm
parents:
diff changeset
   564
75393
87ebf5a50283 clarified formatting, for the sake of scala3;
wenzelm
parents: 73730
diff changeset
   565
    def chunk_line(ctxt: Line_Context): Parser[(Chunk, Line_Context)] = {
58530
7ee248f19ca9 clarified Chunk -- avoid ooddities;
wenzelm
parents: 58529
diff changeset
   566
      ctxt match {
58589
d9350ec0937e tuned signature;
wenzelm
parents: 58544
diff changeset
   567
        case Ignored =>
58538
299b82d12d53 proper treatment of @comment (amending 402a8e8107a7);
wenzelm
parents: 58536
diff changeset
   568
          ignored_line |
58590
472b9fbcc7f0 back to liberal spaces of official syntax (in contrast to 4b190c763097), e.g. relevant for easychair.bib;
wenzelm
parents: 58589
diff changeset
   569
          at ^^ { case a => (Chunk("", List(a)), At) }
472b9fbcc7f0 back to liberal spaces of official syntax (in contrast to 4b190c763097), e.g. relevant for easychair.bib;
wenzelm
parents: 58589
diff changeset
   570
472b9fbcc7f0 back to liberal spaces of official syntax (in contrast to 4b190c763097), e.g. relevant for easychair.bib;
wenzelm
parents: 58589
diff changeset
   571
        case At =>
472b9fbcc7f0 back to liberal spaces of official syntax (in contrast to 4b190c763097), e.g. relevant for easychair.bib;
wenzelm
parents: 58589
diff changeset
   572
          space ^^ { case a => (Chunk("", List(a)), ctxt) } |
472b9fbcc7f0 back to liberal spaces of official syntax (in contrast to 4b190c763097), e.g. relevant for easychair.bib;
wenzelm
parents: 58589
diff changeset
   573
          item_kind ^^ { case a => (Chunk(a.source, List(a)), Item_Start(a.source)) } |
472b9fbcc7f0 back to liberal spaces of official syntax (in contrast to 4b190c763097), e.g. relevant for easychair.bib;
wenzelm
parents: 58589
diff changeset
   574
          recover_item ^^ { case a => (a, Ignored) } |
472b9fbcc7f0 back to liberal spaces of official syntax (in contrast to 4b190c763097), e.g. relevant for easychair.bib;
wenzelm
parents: 58589
diff changeset
   575
          ignored_line
472b9fbcc7f0 back to liberal spaces of official syntax (in contrast to 4b190c763097), e.g. relevant for easychair.bib;
wenzelm
parents: 58589
diff changeset
   576
472b9fbcc7f0 back to liberal spaces of official syntax (in contrast to 4b190c763097), e.g. relevant for easychair.bib;
wenzelm
parents: 58589
diff changeset
   577
        case Item_Start(kind) =>
472b9fbcc7f0 back to liberal spaces of official syntax (in contrast to 4b190c763097), e.g. relevant for easychair.bib;
wenzelm
parents: 58589
diff changeset
   578
          space ^^ { case a => (Chunk(kind, List(a)), ctxt) } |
58591
3c1a8c1c6b3b more accurate item name, depending on kind;
wenzelm
parents: 58590
diff changeset
   579
          item_begin ^^ { case (end, a) => (Chunk(kind, List(a)), Item_Open(kind, end)) } |
58596
877c5ecee253 more total parser;
wenzelm
parents: 58591
diff changeset
   580
          recover_item ^^ { case a => (a, Ignored) } |
58590
472b9fbcc7f0 back to liberal spaces of official syntax (in contrast to 4b190c763097), e.g. relevant for easychair.bib;
wenzelm
parents: 58589
diff changeset
   581
          ignored_line
472b9fbcc7f0 back to liberal spaces of official syntax (in contrast to 4b190c763097), e.g. relevant for easychair.bib;
wenzelm
parents: 58589
diff changeset
   582
58591
3c1a8c1c6b3b more accurate item name, depending on kind;
wenzelm
parents: 58590
diff changeset
   583
        case Item_Open(kind, end) =>
58590
472b9fbcc7f0 back to liberal spaces of official syntax (in contrast to 4b190c763097), e.g. relevant for easychair.bib;
wenzelm
parents: 58589
diff changeset
   584
          space ^^ { case a => (Chunk(kind, List(a)), ctxt) } |
58591
3c1a8c1c6b3b more accurate item name, depending on kind;
wenzelm
parents: 58590
diff changeset
   585
          item_name(kind) ^^ { case a => (Chunk(kind, List(a)), Item(kind, end, Closed)) } |
3c1a8c1c6b3b more accurate item name, depending on kind;
wenzelm
parents: 58590
diff changeset
   586
          body_line(Item(kind, end, Closed)) |
58590
472b9fbcc7f0 back to liberal spaces of official syntax (in contrast to 4b190c763097), e.g. relevant for easychair.bib;
wenzelm
parents: 58589
diff changeset
   587
          ignored_line
472b9fbcc7f0 back to liberal spaces of official syntax (in contrast to 4b190c763097), e.g. relevant for easychair.bib;
wenzelm
parents: 58589
diff changeset
   588
472b9fbcc7f0 back to liberal spaces of official syntax (in contrast to 4b190c763097), e.g. relevant for easychair.bib;
wenzelm
parents: 58589
diff changeset
   589
        case item_ctxt: Item =>
58591
3c1a8c1c6b3b more accurate item name, depending on kind;
wenzelm
parents: 58590
diff changeset
   590
          body_line(item_ctxt) |
58590
472b9fbcc7f0 back to liberal spaces of official syntax (in contrast to 4b190c763097), e.g. relevant for easychair.bib;
wenzelm
parents: 58589
diff changeset
   591
          ignored_line
472b9fbcc7f0 back to liberal spaces of official syntax (in contrast to 4b190c763097), e.g. relevant for easychair.bib;
wenzelm
parents: 58589
diff changeset
   592
58530
7ee248f19ca9 clarified Chunk -- avoid ooddities;
wenzelm
parents: 58529
diff changeset
   593
        case _ => failure("")
7ee248f19ca9 clarified Chunk -- avoid ooddities;
wenzelm
parents: 58529
diff changeset
   594
      }
7ee248f19ca9 clarified Chunk -- avoid ooddities;
wenzelm
parents: 58529
diff changeset
   595
    }
58528
7d6b8f8893e8 more explicit item kind;
wenzelm
parents: 58527
diff changeset
   596
  }
58523
937c479e62fe some support for bibtex files;
wenzelm
parents:
diff changeset
   597
937c479e62fe some support for bibtex files;
wenzelm
parents:
diff changeset
   598
58528
7d6b8f8893e8 more explicit item kind;
wenzelm
parents: 58527
diff changeset
   599
  /* parse */
58523
937c479e62fe some support for bibtex files;
wenzelm
parents:
diff changeset
   600
937c479e62fe some support for bibtex files;
wenzelm
parents:
diff changeset
   601
  def parse(input: CharSequence): List[Chunk] =
64824
330ec9bc4b75 tuned signature;
wenzelm
parents: 60215
diff changeset
   602
    Parsers.parseAll(Parsers.rep(Parsers.chunk), Scan.char_reader(input)) match {
58523
937c479e62fe some support for bibtex files;
wenzelm
parents:
diff changeset
   603
      case Parsers.Success(result, _) => result
58526
f05ccce3eca2 SideKick parser for bibtex entries;
wenzelm
parents: 58524
diff changeset
   604
      case _ => error("Unexpected failure to parse input:\n" + input.toString)
58523
937c479e62fe some support for bibtex files;
wenzelm
parents:
diff changeset
   605
    }
58528
7d6b8f8893e8 more explicit item kind;
wenzelm
parents: 58527
diff changeset
   606
75393
87ebf5a50283 clarified formatting, for the sake of scala3;
wenzelm
parents: 73730
diff changeset
   607
  def parse_line(input: CharSequence, context: Line_Context): (List[Chunk], Line_Context) = {
64824
330ec9bc4b75 tuned signature;
wenzelm
parents: 60215
diff changeset
   608
    var in: Reader[Char] = Scan.char_reader(input)
58528
7d6b8f8893e8 more explicit item kind;
wenzelm
parents: 58527
diff changeset
   609
    val chunks = new mutable.ListBuffer[Chunk]
7d6b8f8893e8 more explicit item kind;
wenzelm
parents: 58527
diff changeset
   610
    var ctxt = context
7d6b8f8893e8 more explicit item kind;
wenzelm
parents: 58527
diff changeset
   611
    while (!in.atEnd) {
75420
73a2f3fe0e8c tuned --- fewer warnings in scala3;
wenzelm
parents: 75394
diff changeset
   612
      val result = Parsers.parse(Parsers.chunk_line(ctxt), in)
73a2f3fe0e8c tuned --- fewer warnings in scala3;
wenzelm
parents: 75394
diff changeset
   613
      (result : @unchecked) match {
60215
5fb4990dfc73 misc tuning, based on warnings by IntelliJ IDEA;
wenzelm
parents: 59319
diff changeset
   614
        case Parsers.Success((x, c), rest) => chunks += x; ctxt = c; in = rest
58528
7d6b8f8893e8 more explicit item kind;
wenzelm
parents: 58527
diff changeset
   615
        case Parsers.NoSuccess(_, rest) =>
7d6b8f8893e8 more explicit item kind;
wenzelm
parents: 58527
diff changeset
   616
          error("Unepected failure to parse input:\n" + rest.source.toString)
7d6b8f8893e8 more explicit item kind;
wenzelm
parents: 58527
diff changeset
   617
      }
7d6b8f8893e8 more explicit item kind;
wenzelm
parents: 58527
diff changeset
   618
    }
7d6b8f8893e8 more explicit item kind;
wenzelm
parents: 58527
diff changeset
   619
    (chunks.toList, ctxt)
7d6b8f8893e8 more explicit item kind;
wenzelm
parents: 58527
diff changeset
   620
  }
67243
6a93aaa3ed36 bibtex HTML output via external tool;
wenzelm
parents: 67203
diff changeset
   621
6a93aaa3ed36 bibtex HTML output via external tool;
wenzelm
parents: 67203
diff changeset
   622
6a93aaa3ed36 bibtex HTML output via external tool;
wenzelm
parents: 67203
diff changeset
   623
6a93aaa3ed36 bibtex HTML output via external tool;
wenzelm
parents: 67203
diff changeset
   624
  /** HTML output **/
6a93aaa3ed36 bibtex HTML output via external tool;
wenzelm
parents: 67203
diff changeset
   625
6a93aaa3ed36 bibtex HTML output via external tool;
wenzelm
parents: 67203
diff changeset
   626
  private val output_styles =
6a93aaa3ed36 bibtex HTML output via external tool;
wenzelm
parents: 67203
diff changeset
   627
    List(
67257
5035b6754fca clarified signature;
wenzelm
parents: 67256
diff changeset
   628
      "" -> "html-n",
67243
6a93aaa3ed36 bibtex HTML output via external tool;
wenzelm
parents: 67203
diff changeset
   629
      "plain" -> "html-n",
6a93aaa3ed36 bibtex HTML output via external tool;
wenzelm
parents: 67203
diff changeset
   630
      "alpha" -> "html-a",
6a93aaa3ed36 bibtex HTML output via external tool;
wenzelm
parents: 67203
diff changeset
   631
      "named" -> "html-n",
6a93aaa3ed36 bibtex HTML output via external tool;
wenzelm
parents: 67203
diff changeset
   632
      "paragraph" -> "html-n",
6a93aaa3ed36 bibtex HTML output via external tool;
wenzelm
parents: 67203
diff changeset
   633
      "unsort" -> "html-u",
6a93aaa3ed36 bibtex HTML output via external tool;
wenzelm
parents: 67203
diff changeset
   634
      "unsortlist" -> "html-u")
6a93aaa3ed36 bibtex HTML output via external tool;
wenzelm
parents: 67203
diff changeset
   635
6a93aaa3ed36 bibtex HTML output via external tool;
wenzelm
parents: 67203
diff changeset
   636
  def html_output(bib: List[Path],
67256
ce7d856680d1 proper title;
wenzelm
parents: 67251
diff changeset
   637
    title: String = "Bibliography",
67248
68177abb2988 isabelle.preview presents bibtex database files as well;
wenzelm
parents: 67243
diff changeset
   638
    body: Boolean = false,
67243
6a93aaa3ed36 bibtex HTML output via external tool;
wenzelm
parents: 67203
diff changeset
   639
    citations: List[String] = List("*"),
67257
5035b6754fca clarified signature;
wenzelm
parents: 67256
diff changeset
   640
    style: String = "",
75393
87ebf5a50283 clarified formatting, for the sake of scala3;
wenzelm
parents: 73730
diff changeset
   641
    chronological: Boolean = false
87ebf5a50283 clarified formatting, for the sake of scala3;
wenzelm
parents: 73730
diff changeset
   642
  ): String = {
75394
42267c650205 tuned formatting;
wenzelm
parents: 75393
diff changeset
   643
    Isabelle_System.with_tmp_dir("bibtex") { tmp_dir =>
67243
6a93aaa3ed36 bibtex HTML output via external tool;
wenzelm
parents: 67203
diff changeset
   644
      /* database files */
6a93aaa3ed36 bibtex HTML output via external tool;
wenzelm
parents: 67203
diff changeset
   645
69367
34b7550b66c7 tuned signature;
wenzelm
parents: 69366
diff changeset
   646
      val bib_files = bib.map(_.drop_ext)
75393
87ebf5a50283 clarified formatting, for the sake of scala3;
wenzelm
parents: 73730
diff changeset
   647
      val bib_names = {
69366
b6dacf6eabe3 clarified signature;
wenzelm
parents: 69294
diff changeset
   648
        val names0 = bib_files.map(_.file_name)
67243
6a93aaa3ed36 bibtex HTML output via external tool;
wenzelm
parents: 67203
diff changeset
   649
        if (Library.duplicates(names0).isEmpty) names0
6a93aaa3ed36 bibtex HTML output via external tool;
wenzelm
parents: 67203
diff changeset
   650
        else names0.zipWithIndex.map({ case (name, i) => (i + 1).toString + "-" + name })
6a93aaa3ed36 bibtex HTML output via external tool;
wenzelm
parents: 67203
diff changeset
   651
      }
6a93aaa3ed36 bibtex HTML output via external tool;
wenzelm
parents: 67203
diff changeset
   652
6a93aaa3ed36 bibtex HTML output via external tool;
wenzelm
parents: 67203
diff changeset
   653
      for ((a, b) <- bib_files zip bib_names) {
77035
28ac56e59d23 tuned signature;
wenzelm
parents: 77032
diff changeset
   654
        Isabelle_System.copy_file(a.bib, tmp_dir + Path.basic(b).bib)
67243
6a93aaa3ed36 bibtex HTML output via external tool;
wenzelm
parents: 67203
diff changeset
   655
      }
6a93aaa3ed36 bibtex HTML output via external tool;
wenzelm
parents: 67203
diff changeset
   656
6a93aaa3ed36 bibtex HTML output via external tool;
wenzelm
parents: 67203
diff changeset
   657
6a93aaa3ed36 bibtex HTML output via external tool;
wenzelm
parents: 67203
diff changeset
   658
      /* style file */
6a93aaa3ed36 bibtex HTML output via external tool;
wenzelm
parents: 67203
diff changeset
   659
6a93aaa3ed36 bibtex HTML output via external tool;
wenzelm
parents: 67203
diff changeset
   660
      val bst =
6a93aaa3ed36 bibtex HTML output via external tool;
wenzelm
parents: 67203
diff changeset
   661
        output_styles.toMap.get(style) match {
6a93aaa3ed36 bibtex HTML output via external tool;
wenzelm
parents: 67203
diff changeset
   662
          case Some(base) => base + (if (chronological) "c" else "") + ".bst"
6a93aaa3ed36 bibtex HTML output via external tool;
wenzelm
parents: 67203
diff changeset
   663
          case None =>
6a93aaa3ed36 bibtex HTML output via external tool;
wenzelm
parents: 67203
diff changeset
   664
            error("Bad style for bibtex HTML output: " + quote(style) +
6a93aaa3ed36 bibtex HTML output via external tool;
wenzelm
parents: 67203
diff changeset
   665
              "\n(expected: " + commas_quote(output_styles.map(_._1)) + ")")
6a93aaa3ed36 bibtex HTML output via external tool;
wenzelm
parents: 67203
diff changeset
   666
        }
73317
df49ca5da9d0 clarified modules: more like ML;
wenzelm
parents: 73120
diff changeset
   667
      Isabelle_System.copy_file(Path.explode("$BIB2XHTML_HOME/bst") + Path.explode(bst), tmp_dir)
67243
6a93aaa3ed36 bibtex HTML output via external tool;
wenzelm
parents: 67203
diff changeset
   668
6a93aaa3ed36 bibtex HTML output via external tool;
wenzelm
parents: 67203
diff changeset
   669
6a93aaa3ed36 bibtex HTML output via external tool;
wenzelm
parents: 67203
diff changeset
   670
      /* result */
6a93aaa3ed36 bibtex HTML output via external tool;
wenzelm
parents: 67203
diff changeset
   671
6a93aaa3ed36 bibtex HTML output via external tool;
wenzelm
parents: 67203
diff changeset
   672
      val in_file = Path.explode("bib.aux")
6a93aaa3ed36 bibtex HTML output via external tool;
wenzelm
parents: 67203
diff changeset
   673
      val out_file = Path.explode("bib.html")
6a93aaa3ed36 bibtex HTML output via external tool;
wenzelm
parents: 67203
diff changeset
   674
6a93aaa3ed36 bibtex HTML output via external tool;
wenzelm
parents: 67203
diff changeset
   675
      File.write(tmp_dir + in_file,
6a93aaa3ed36 bibtex HTML output via external tool;
wenzelm
parents: 67203
diff changeset
   676
        bib_names.mkString("\\bibdata{", ",", "}\n") +
6a93aaa3ed36 bibtex HTML output via external tool;
wenzelm
parents: 67203
diff changeset
   677
        citations.map(cite => "\\citation{" + cite + "}\n").mkString)
6a93aaa3ed36 bibtex HTML output via external tool;
wenzelm
parents: 67203
diff changeset
   678
6a93aaa3ed36 bibtex HTML output via external tool;
wenzelm
parents: 67203
diff changeset
   679
      Isabelle_System.bash(
6a93aaa3ed36 bibtex HTML output via external tool;
wenzelm
parents: 67203
diff changeset
   680
        "\"$BIB2XHTML_HOME/main/bib2xhtml.pl\" -B \"$ISABELLE_BIBTEX\"" +
67257
5035b6754fca clarified signature;
wenzelm
parents: 67256
diff changeset
   681
          " -u -s " + Bash.string(proper_string(style) getOrElse "empty") +
5035b6754fca clarified signature;
wenzelm
parents: 67256
diff changeset
   682
          (if (chronological) " -c" else "") +
77369
wenzelm
parents: 77368
diff changeset
   683
          if_proper(title, " -h " + Bash.string(title) + " ") +
67256
ce7d856680d1 proper title;
wenzelm
parents: 67251
diff changeset
   684
          " " + File.bash_path(in_file) + " " + File.bash_path(out_file),
67243
6a93aaa3ed36 bibtex HTML output via external tool;
wenzelm
parents: 67203
diff changeset
   685
        cwd = tmp_dir.file).check
6a93aaa3ed36 bibtex HTML output via external tool;
wenzelm
parents: 67203
diff changeset
   686
6a93aaa3ed36 bibtex HTML output via external tool;
wenzelm
parents: 67203
diff changeset
   687
      val html = File.read(tmp_dir + out_file)
6a93aaa3ed36 bibtex HTML output via external tool;
wenzelm
parents: 67203
diff changeset
   688
67248
68177abb2988 isabelle.preview presents bibtex database files as well;
wenzelm
parents: 67243
diff changeset
   689
      if (body) {
68177abb2988 isabelle.preview presents bibtex database files as well;
wenzelm
parents: 67243
diff changeset
   690
        cat_lines(
68177abb2988 isabelle.preview presents bibtex database files as well;
wenzelm
parents: 67243
diff changeset
   691
          split_lines(html).
68177abb2988 isabelle.preview presents bibtex database files as well;
wenzelm
parents: 67243
diff changeset
   692
            dropWhile(line => !line.startsWith("<!-- BEGIN BIBLIOGRAPHY")).reverse.
68177abb2988 isabelle.preview presents bibtex database files as well;
wenzelm
parents: 67243
diff changeset
   693
            dropWhile(line => !line.startsWith("<!-- END BIBLIOGRAPHY")).reverse)
68177abb2988 isabelle.preview presents bibtex database files as well;
wenzelm
parents: 67243
diff changeset
   694
      }
68177abb2988 isabelle.preview presents bibtex database files as well;
wenzelm
parents: 67243
diff changeset
   695
      else html
75394
42267c650205 tuned formatting;
wenzelm
parents: 75393
diff changeset
   696
    }
67243
6a93aaa3ed36 bibtex HTML output via external tool;
wenzelm
parents: 67203
diff changeset
   697
  }
76972
6c542f2aab85 basic support for update_cite_commands;
wenzelm
parents: 76933
diff changeset
   698
6c542f2aab85 basic support for update_cite_commands;
wenzelm
parents: 76933
diff changeset
   699
6c542f2aab85 basic support for update_cite_commands;
wenzelm
parents: 76933
diff changeset
   700
6c542f2aab85 basic support for update_cite_commands;
wenzelm
parents: 76933
diff changeset
   701
  /** cite commands and antiquotations **/
6c542f2aab85 basic support for update_cite_commands;
wenzelm
parents: 76933
diff changeset
   702
77014
9107e103754c clarified signature;
wenzelm
parents: 77012
diff changeset
   703
  /* cite commands */
9107e103754c clarified signature;
wenzelm
parents: 77012
diff changeset
   704
9107e103754c clarified signature;
wenzelm
parents: 77012
diff changeset
   705
  def cite_commands(options: Options): List[String] =
77218
86217697863c tuned signature;
wenzelm
parents: 77148
diff changeset
   706
    space_explode(',', options.string("document_cite_commands"))
77014
9107e103754c clarified signature;
wenzelm
parents: 77012
diff changeset
   707
9107e103754c clarified signature;
wenzelm
parents: 77012
diff changeset
   708
  val CITE = "cite"
9107e103754c clarified signature;
wenzelm
parents: 77012
diff changeset
   709
  val NOCITE = "nocite"
9107e103754c clarified signature;
wenzelm
parents: 77012
diff changeset
   710
9107e103754c clarified signature;
wenzelm
parents: 77012
diff changeset
   711
77020
wenzelm
parents: 77017
diff changeset
   712
  /* citations within theory source */
77012
2ac1b7f4f3e4 tuned comments;
wenzelm
parents: 77011
diff changeset
   713
77015
87552565d1a5 tuned signature;
wenzelm
parents: 77014
diff changeset
   714
  object Cite {
77016
a19ea85409cd clarified signature;
wenzelm
parents: 77015
diff changeset
   715
    def unapply(tree: XML.Tree): Option[Inner] =
a19ea85409cd clarified signature;
wenzelm
parents: 77015
diff changeset
   716
      tree match {
a19ea85409cd clarified signature;
wenzelm
parents: 77015
diff changeset
   717
        case XML.Elem(Markup(Markup.Latex_Cite.name, props), body) =>
a19ea85409cd clarified signature;
wenzelm
parents: 77015
diff changeset
   718
          val kind = Markup.Kind.unapply(props).getOrElse(CITE)
a19ea85409cd clarified signature;
wenzelm
parents: 77015
diff changeset
   719
          val citations = Markup.Citations.get(props)
a19ea85409cd clarified signature;
wenzelm
parents: 77015
diff changeset
   720
          val pos = props.filter(Markup.position_property)
a19ea85409cd clarified signature;
wenzelm
parents: 77015
diff changeset
   721
          Some(Inner(kind, citations, body, pos))
a19ea85409cd clarified signature;
wenzelm
parents: 77015
diff changeset
   722
        case _ => None
a19ea85409cd clarified signature;
wenzelm
parents: 77015
diff changeset
   723
      }
a19ea85409cd clarified signature;
wenzelm
parents: 77015
diff changeset
   724
77024
6e90e84f7e7c tuned signature;
wenzelm
parents: 77020
diff changeset
   725
    sealed case class Inner(kind: String, citations: String, location: XML.Body, pos: Position.T) {
77016
a19ea85409cd clarified signature;
wenzelm
parents: 77015
diff changeset
   726
      def nocite: Inner = copy(kind = NOCITE, location = Nil)
a19ea85409cd clarified signature;
wenzelm
parents: 77015
diff changeset
   727
77025
34219d664854 proper citations for unselected theories, notably for the default selection of the GUI panel;
wenzelm
parents: 77024
diff changeset
   728
      def latex_text: Latex.Text = {
34219d664854 proper citations for unselected theories, notably for the default selection of the GUI panel;
wenzelm
parents: 77024
diff changeset
   729
        val props =
34219d664854 proper citations for unselected theories, notably for the default selection of the GUI panel;
wenzelm
parents: 77024
diff changeset
   730
          (if (kind.nonEmpty) Markup.Kind(kind) else Nil) :::
34219d664854 proper citations for unselected theories, notably for the default selection of the GUI panel;
wenzelm
parents: 77024
diff changeset
   731
          Markup.Citations(citations) ::: pos
34219d664854 proper citations for unselected theories, notably for the default selection of the GUI panel;
wenzelm
parents: 77024
diff changeset
   732
        List(XML.Elem(Markup.Latex_Cite(props), location))
34219d664854 proper citations for unselected theories, notably for the default selection of the GUI panel;
wenzelm
parents: 77024
diff changeset
   733
      }
34219d664854 proper citations for unselected theories, notably for the default selection of the GUI panel;
wenzelm
parents: 77024
diff changeset
   734
77024
6e90e84f7e7c tuned signature;
wenzelm
parents: 77020
diff changeset
   735
      override def toString: String = citations
77016
a19ea85409cd clarified signature;
wenzelm
parents: 77015
diff changeset
   736
    }
a19ea85409cd clarified signature;
wenzelm
parents: 77015
diff changeset
   737
a19ea85409cd clarified signature;
wenzelm
parents: 77015
diff changeset
   738
    sealed case class Outer(kind: String, body: String, start: Isar_Token.Pos) {
a19ea85409cd clarified signature;
wenzelm
parents: 77015
diff changeset
   739
      def pos: Position.T = start.position()
a19ea85409cd clarified signature;
wenzelm
parents: 77015
diff changeset
   740
a19ea85409cd clarified signature;
wenzelm
parents: 77015
diff changeset
   741
      def parse: Option[Inner] = {
a19ea85409cd clarified signature;
wenzelm
parents: 77015
diff changeset
   742
        val tokens = Isar_Token.explode(Parsers.keywords, body)
a19ea85409cd clarified signature;
wenzelm
parents: 77015
diff changeset
   743
        Parsers.parse_all(Parsers.inner(pos), Isar_Token.reader(tokens, start)) match {
a19ea85409cd clarified signature;
wenzelm
parents: 77015
diff changeset
   744
          case Parsers.Success(res, _) => Some(res)
a19ea85409cd clarified signature;
wenzelm
parents: 77015
diff changeset
   745
          case _ => None
a19ea85409cd clarified signature;
wenzelm
parents: 77015
diff changeset
   746
        }
a19ea85409cd clarified signature;
wenzelm
parents: 77015
diff changeset
   747
      }
a19ea85409cd clarified signature;
wenzelm
parents: 77015
diff changeset
   748
a19ea85409cd clarified signature;
wenzelm
parents: 77015
diff changeset
   749
      def errors: List[String] =
a19ea85409cd clarified signature;
wenzelm
parents: 77015
diff changeset
   750
        if (parse.isDefined) Nil
a19ea85409cd clarified signature;
wenzelm
parents: 77015
diff changeset
   751
        else List("Malformed citation" + Position.here(pos))
a19ea85409cd clarified signature;
wenzelm
parents: 77015
diff changeset
   752
a19ea85409cd clarified signature;
wenzelm
parents: 77015
diff changeset
   753
      override def toString: String =
a19ea85409cd clarified signature;
wenzelm
parents: 77015
diff changeset
   754
        parse match {
a19ea85409cd clarified signature;
wenzelm
parents: 77015
diff changeset
   755
          case Some(inner) => inner.toString
a19ea85409cd clarified signature;
wenzelm
parents: 77015
diff changeset
   756
          case None => "<malformed>"
a19ea85409cd clarified signature;
wenzelm
parents: 77015
diff changeset
   757
        }
a19ea85409cd clarified signature;
wenzelm
parents: 77015
diff changeset
   758
    }
a19ea85409cd clarified signature;
wenzelm
parents: 77015
diff changeset
   759
77015
87552565d1a5 tuned signature;
wenzelm
parents: 77014
diff changeset
   760
    object Parsers extends Parse.Parsers {
77017
05219e08c3e9 uniform keywords for embedded syntax;
wenzelm
parents: 77016
diff changeset
   761
      val keywords: Keyword.Keywords = Thy_Header.bootstrap_keywords + "in" + "using"
77015
87552565d1a5 tuned signature;
wenzelm
parents: 77014
diff changeset
   762
87552565d1a5 tuned signature;
wenzelm
parents: 77014
diff changeset
   763
      val location: Parser[String] = embedded ~ $$$("in") ^^ { case x ~ _ => x } | success("")
87552565d1a5 tuned signature;
wenzelm
parents: 77014
diff changeset
   764
      val citations: Parser[String] = rep1sep(name, $$$("and")) ^^ (x => x.mkString(","))
87552565d1a5 tuned signature;
wenzelm
parents: 77014
diff changeset
   765
      val kind: Parser[String] = $$$("using") ~! name ^^ { case _ ~ x => x } | success(CITE)
87552565d1a5 tuned signature;
wenzelm
parents: 77014
diff changeset
   766
77016
a19ea85409cd clarified signature;
wenzelm
parents: 77015
diff changeset
   767
      def inner(pos: Position.T): Parser[Inner] =
a19ea85409cd clarified signature;
wenzelm
parents: 77015
diff changeset
   768
        location ~ citations ~ kind ^^
a19ea85409cd clarified signature;
wenzelm
parents: 77015
diff changeset
   769
          { case x ~ y ~ z => Inner(z, y, XML.string(x), pos) }
77015
87552565d1a5 tuned signature;
wenzelm
parents: 77014
diff changeset
   770
    }
87552565d1a5 tuned signature;
wenzelm
parents: 77014
diff changeset
   771
87552565d1a5 tuned signature;
wenzelm
parents: 77014
diff changeset
   772
    def parse(
87552565d1a5 tuned signature;
wenzelm
parents: 77014
diff changeset
   773
      cite_commands: List[String],
87552565d1a5 tuned signature;
wenzelm
parents: 77014
diff changeset
   774
      text: String,
87552565d1a5 tuned signature;
wenzelm
parents: 77014
diff changeset
   775
      start: Isar_Token.Pos = Isar_Token.Pos.start
77016
a19ea85409cd clarified signature;
wenzelm
parents: 77015
diff changeset
   776
    ): List[Outer] = {
77015
87552565d1a5 tuned signature;
wenzelm
parents: 77014
diff changeset
   777
      val controls = cite_commands.map(s => Symbol.control_prefix + s + Symbol.control_suffix)
87552565d1a5 tuned signature;
wenzelm
parents: 77014
diff changeset
   778
      val special = (controls ::: controls.map(Symbol.decode)).toSet
87552565d1a5 tuned signature;
wenzelm
parents: 77014
diff changeset
   779
87552565d1a5 tuned signature;
wenzelm
parents: 77014
diff changeset
   780
      val Parsers = Antiquote.Parsers
87552565d1a5 tuned signature;
wenzelm
parents: 77014
diff changeset
   781
      Parsers.parseAll(Parsers.rep(Parsers.antiquote_special(special)), text) match {
87552565d1a5 tuned signature;
wenzelm
parents: 77014
diff changeset
   782
        case Parsers.Success(ants, _) =>
87552565d1a5 tuned signature;
wenzelm
parents: 77014
diff changeset
   783
          var pos = start
77016
a19ea85409cd clarified signature;
wenzelm
parents: 77015
diff changeset
   784
          val result = new mutable.ListBuffer[Outer]
77015
87552565d1a5 tuned signature;
wenzelm
parents: 77014
diff changeset
   785
          for (ant <- ants) {
87552565d1a5 tuned signature;
wenzelm
parents: 77014
diff changeset
   786
            ant match {
87552565d1a5 tuned signature;
wenzelm
parents: 77014
diff changeset
   787
              case Antiquote.Control(source) =>
87552565d1a5 tuned signature;
wenzelm
parents: 77014
diff changeset
   788
                for {
87552565d1a5 tuned signature;
wenzelm
parents: 77014
diff changeset
   789
                  head <- Symbol.iterator(source).nextOption
87552565d1a5 tuned signature;
wenzelm
parents: 77014
diff changeset
   790
                  kind <- Symbol.control_name(Symbol.encode(head))
87552565d1a5 tuned signature;
wenzelm
parents: 77014
diff changeset
   791
                } {
87552565d1a5 tuned signature;
wenzelm
parents: 77014
diff changeset
   792
                  val rest = source.substring(head.length)
87552565d1a5 tuned signature;
wenzelm
parents: 77014
diff changeset
   793
                  val (body, pos1) =
87552565d1a5 tuned signature;
wenzelm
parents: 77014
diff changeset
   794
                    if (rest.isEmpty) (rest, pos)
87552565d1a5 tuned signature;
wenzelm
parents: 77014
diff changeset
   795
                    else (Scan.Parsers.cartouche_content(rest), pos.advance(Symbol.open))
77016
a19ea85409cd clarified signature;
wenzelm
parents: 77015
diff changeset
   796
                  result += Outer(kind, body, pos1)
77015
87552565d1a5 tuned signature;
wenzelm
parents: 77014
diff changeset
   797
                }
87552565d1a5 tuned signature;
wenzelm
parents: 77014
diff changeset
   798
              case _ =>
87552565d1a5 tuned signature;
wenzelm
parents: 77014
diff changeset
   799
            }
87552565d1a5 tuned signature;
wenzelm
parents: 77014
diff changeset
   800
            pos = pos.advance(ant.source)
87552565d1a5 tuned signature;
wenzelm
parents: 77014
diff changeset
   801
          }
87552565d1a5 tuned signature;
wenzelm
parents: 77014
diff changeset
   802
          result.toList
87552565d1a5 tuned signature;
wenzelm
parents: 77014
diff changeset
   803
        case _ => error("Unexpected failure parsing special antiquotations:\n" + text)
87552565d1a5 tuned signature;
wenzelm
parents: 77014
diff changeset
   804
      }
87552565d1a5 tuned signature;
wenzelm
parents: 77014
diff changeset
   805
    }
77011
3e48f8c6afc9 parse citations from raw source, without formal context;
wenzelm
parents: 77010
diff changeset
   806
  }
77020
wenzelm
parents: 77017
diff changeset
   807
wenzelm
parents: 77017
diff changeset
   808
wenzelm
parents: 77017
diff changeset
   809
  /* update old forms: @{cite ...} and \cite{...} */
wenzelm
parents: 77017
diff changeset
   810
wenzelm
parents: 77017
diff changeset
   811
  def cite_antiquotation(name: String, body: String): String =
wenzelm
parents: 77017
diff changeset
   812
    """\<^""" + name + """>\<open>""" + body + """\<close>"""
wenzelm
parents: 77017
diff changeset
   813
wenzelm
parents: 77017
diff changeset
   814
  def cite_antiquotation(name: String, location: String, citations: List[String]): String = {
wenzelm
parents: 77017
diff changeset
   815
    val body =
77368
wenzelm
parents: 77219
diff changeset
   816
      if_proper(location, Symbol.cartouche(location) + " in ") +
77020
wenzelm
parents: 77017
diff changeset
   817
      citations.map(quote).mkString(" and ")
wenzelm
parents: 77017
diff changeset
   818
    cite_antiquotation(name, body)
wenzelm
parents: 77017
diff changeset
   819
  }
wenzelm
parents: 77017
diff changeset
   820
wenzelm
parents: 77017
diff changeset
   821
  private val Cite_Command = """\\(cite|nocite|citet|citep)((?:\[[^\]]*\])?)\{([^}]*)\}""".r
wenzelm
parents: 77017
diff changeset
   822
  private val Cite_Macro = """\[\s*cite_macro\s*=\s*"?(\w+)"?\]\s*""".r
wenzelm
parents: 77017
diff changeset
   823
wenzelm
parents: 77017
diff changeset
   824
  def update_cite_commands(str: String): String =
wenzelm
parents: 77017
diff changeset
   825
    Cite_Command.replaceAllIn(str, { m =>
wenzelm
parents: 77017
diff changeset
   826
      val name = m.group(1)
wenzelm
parents: 77017
diff changeset
   827
      val loc = m.group(2)
wenzelm
parents: 77017
diff changeset
   828
      val location =
wenzelm
parents: 77017
diff changeset
   829
        if (loc.startsWith("[") && loc.endsWith("]")) loc.substring(1, loc.length - 1)
wenzelm
parents: 77017
diff changeset
   830
        else loc
77218
86217697863c tuned signature;
wenzelm
parents: 77148
diff changeset
   831
      val citations = space_explode(',', m.group(3)).map(_.trim)
77020
wenzelm
parents: 77017
diff changeset
   832
      Regex.quoteReplacement(cite_antiquotation(name, location, citations))
wenzelm
parents: 77017
diff changeset
   833
    })
wenzelm
parents: 77017
diff changeset
   834
wenzelm
parents: 77017
diff changeset
   835
  def update_cite_antiquotation(cite_commands: List[String], str: String): String = {
wenzelm
parents: 77017
diff changeset
   836
    val opt_body =
wenzelm
parents: 77017
diff changeset
   837
      for {
wenzelm
parents: 77017
diff changeset
   838
        str1 <- Library.try_unprefix("@{cite", str)
wenzelm
parents: 77017
diff changeset
   839
        str2 <- Library.try_unsuffix("}", str1)
wenzelm
parents: 77017
diff changeset
   840
      } yield str2.trim
wenzelm
parents: 77017
diff changeset
   841
wenzelm
parents: 77017
diff changeset
   842
    opt_body match {
wenzelm
parents: 77017
diff changeset
   843
      case None => str
wenzelm
parents: 77017
diff changeset
   844
      case Some(body0) =>
wenzelm
parents: 77017
diff changeset
   845
        val (name, body1) =
wenzelm
parents: 77017
diff changeset
   846
          Cite_Macro.findFirstMatchIn(body0) match {
wenzelm
parents: 77017
diff changeset
   847
            case None => (CITE, body0)
wenzelm
parents: 77017
diff changeset
   848
            case Some(m) => (m.group(1), Cite_Macro.replaceAllIn(body0, ""))
wenzelm
parents: 77017
diff changeset
   849
          }
wenzelm
parents: 77017
diff changeset
   850
        val body2 = body1.replace("""\<close>""", """\<close> in""")
wenzelm
parents: 77017
diff changeset
   851
        if (cite_commands.contains(name)) cite_antiquotation(name, body2)
wenzelm
parents: 77017
diff changeset
   852
        else cite_antiquotation(CITE, body2 + " using " + quote(name))
wenzelm
parents: 77017
diff changeset
   853
    }
wenzelm
parents: 77017
diff changeset
   854
  }
58523
937c479e62fe some support for bibtex files;
wenzelm
parents:
diff changeset
   855
}