| author | wenzelm | 
| Sat, 15 Feb 2025 14:37:41 +0100 | |
| changeset 82181 | a0d1d772ccab | 
| parent 81826 | 57b0a02e2774 | 
| permissions | -rw-r--r-- | 
| 34136 | 1 | /* Title: Pure/library.scala | 
| 2 | Author: Makarius | |
| 3 | ||
| 4 | Basic library. | |
| 5 | */ | |
| 6 | ||
| 7 | package isabelle | |
| 8 | ||
| 38258 | 9 | |
| 63781 | 10 | import scala.annotation.tailrec | 
| 51981 
a8ffd3692f57
more scalable Library.separate -- NB: JVM has tiny fixed-size stack;
 wenzelm parents: 
51616diff
changeset | 11 | import scala.collection.mutable | 
| 59224 | 12 | import scala.util.matching.Regex | 
| 51981 
a8ffd3692f57
more scalable Library.separate -- NB: JVM has tiny fixed-size stack;
 wenzelm parents: 
51616diff
changeset | 13 | |
| 37018 | 14 | |
| 75393 | 15 | object Library {
 | 
| 63789 | 16 | /* resource management */ | 
| 17 | ||
| 75393 | 18 |   def using[A <: AutoCloseable, B](a: A)(f: A => B): B = {
 | 
| 69393 
ed0824ef337e
static type for Library.using: avoid Java 11 warnings on "Illegal reflective access";
 wenzelm parents: 
68715diff
changeset | 19 |     try { f(a) }
 | 
| 
ed0824ef337e
static type for Library.using: avoid Java 11 warnings on "Illegal reflective access";
 wenzelm parents: 
68715diff
changeset | 20 |     finally { if (a != null) a.close() }
 | 
| 63789 | 21 | } | 
| 22 | ||
| 77515 | 23 | def using_option[A <: AutoCloseable, B](opt: Option[A])(f: A => B): Option[B] = | 
| 24 | opt.map(a => using(a)(f)) | |
| 25 | ||
| 78198 | 26 |   def using_optional[A <: AutoCloseable, B](opt: Option[A])(f: Option[A] => B): B = {
 | 
| 27 |     try { f(opt) }
 | |
| 28 |     finally {
 | |
| 29 |       opt match {
 | |
| 30 | case Some(a) if a != null => a.close() | |
| 31 | case _ => | |
| 32 | } | |
| 33 | } | |
| 34 | } | |
| 35 | ||
| 63789 | 36 | |
| 57909 
0fb331032f02
more compact representation of special string values;
 wenzelm parents: 
57831diff
changeset | 37 | /* integers */ | 
| 
0fb331032f02
more compact representation of special string values;
 wenzelm parents: 
57831diff
changeset | 38 | |
| 
0fb331032f02
more compact representation of special string values;
 wenzelm parents: 
57831diff
changeset | 39 | private val small_int = 10000 | 
| 75393 | 40 |   private lazy val small_int_table = {
 | 
| 57909 
0fb331032f02
more compact representation of special string values;
 wenzelm parents: 
57831diff
changeset | 41 | val array = new Array[String](small_int) | 
| 
0fb331032f02
more compact representation of special string values;
 wenzelm parents: 
57831diff
changeset | 42 | for (i <- 0 until small_int) array(i) = i.toString | 
| 
0fb331032f02
more compact representation of special string values;
 wenzelm parents: 
57831diff
changeset | 43 | array | 
| 
0fb331032f02
more compact representation of special string values;
 wenzelm parents: 
57831diff
changeset | 44 | } | 
| 
0fb331032f02
more compact representation of special string values;
 wenzelm parents: 
57831diff
changeset | 45 | |
| 75393 | 46 |   def is_small_int(s: String): Boolean = {
 | 
| 57909 
0fb331032f02
more compact representation of special string values;
 wenzelm parents: 
57831diff
changeset | 47 | val len = s.length | 
| 
0fb331032f02
more compact representation of special string values;
 wenzelm parents: 
57831diff
changeset | 48 | 1 <= len && len <= 4 && | 
| 
0fb331032f02
more compact representation of special string values;
 wenzelm parents: 
57831diff
changeset | 49 | s.forall(c => '0' <= c && c <= '9') && | 
| 
0fb331032f02
more compact representation of special string values;
 wenzelm parents: 
57831diff
changeset | 50 | (len == 1 || s(0) != '0') | 
| 
0fb331032f02
more compact representation of special string values;
 wenzelm parents: 
57831diff
changeset | 51 | } | 
| 
0fb331032f02
more compact representation of special string values;
 wenzelm parents: 
57831diff
changeset | 52 | |
| 
0fb331032f02
more compact representation of special string values;
 wenzelm parents: 
57831diff
changeset | 53 | def signed_string_of_long(i: Long): String = | 
| 
0fb331032f02
more compact representation of special string values;
 wenzelm parents: 
57831diff
changeset | 54 | if (0 <= i && i < small_int) small_int_table(i.toInt) | 
| 
0fb331032f02
more compact representation of special string values;
 wenzelm parents: 
57831diff
changeset | 55 | else i.toString | 
| 
0fb331032f02
more compact representation of special string values;
 wenzelm parents: 
57831diff
changeset | 56 | |
| 
0fb331032f02
more compact representation of special string values;
 wenzelm parents: 
57831diff
changeset | 57 | def signed_string_of_int(i: Int): String = | 
| 
0fb331032f02
more compact representation of special string values;
 wenzelm parents: 
57831diff
changeset | 58 | if (0 <= i && i < small_int) small_int_table(i) | 
| 
0fb331032f02
more compact representation of special string values;
 wenzelm parents: 
57831diff
changeset | 59 | else i.toString | 
| 
0fb331032f02
more compact representation of special string values;
 wenzelm parents: 
57831diff
changeset | 60 | |
| 
0fb331032f02
more compact representation of special string values;
 wenzelm parents: 
57831diff
changeset | 61 | |
| 48996 | 62 | /* separated chunks */ | 
| 36688 | 63 | |
| 75393 | 64 |   def separate[A](s: A, list: List[A]): List[A] = {
 | 
| 51981 
a8ffd3692f57
more scalable Library.separate -- NB: JVM has tiny fixed-size stack;
 wenzelm parents: 
51616diff
changeset | 65 | val result = new mutable.ListBuffer[A] | 
| 
a8ffd3692f57
more scalable Library.separate -- NB: JVM has tiny fixed-size stack;
 wenzelm parents: 
51616diff
changeset | 66 | var first = true | 
| 
a8ffd3692f57
more scalable Library.separate -- NB: JVM has tiny fixed-size stack;
 wenzelm parents: 
51616diff
changeset | 67 |     for (x <- list) {
 | 
| 
a8ffd3692f57
more scalable Library.separate -- NB: JVM has tiny fixed-size stack;
 wenzelm parents: 
51616diff
changeset | 68 |       if (first) {
 | 
| 
a8ffd3692f57
more scalable Library.separate -- NB: JVM has tiny fixed-size stack;
 wenzelm parents: 
51616diff
changeset | 69 | first = false | 
| 
a8ffd3692f57
more scalable Library.separate -- NB: JVM has tiny fixed-size stack;
 wenzelm parents: 
51616diff
changeset | 70 | result += x | 
| 
a8ffd3692f57
more scalable Library.separate -- NB: JVM has tiny fixed-size stack;
 wenzelm parents: 
51616diff
changeset | 71 | } | 
| 
a8ffd3692f57
more scalable Library.separate -- NB: JVM has tiny fixed-size stack;
 wenzelm parents: 
51616diff
changeset | 72 |       else {
 | 
| 
a8ffd3692f57
more scalable Library.separate -- NB: JVM has tiny fixed-size stack;
 wenzelm parents: 
51616diff
changeset | 73 | result += s | 
| 
a8ffd3692f57
more scalable Library.separate -- NB: JVM has tiny fixed-size stack;
 wenzelm parents: 
51616diff
changeset | 74 | result += x | 
| 
a8ffd3692f57
more scalable Library.separate -- NB: JVM has tiny fixed-size stack;
 wenzelm parents: 
51616diff
changeset | 75 | } | 
| 36688 | 76 | } | 
| 51981 
a8ffd3692f57
more scalable Library.separate -- NB: JVM has tiny fixed-size stack;
 wenzelm parents: 
51616diff
changeset | 77 | result.toList | 
| 
a8ffd3692f57
more scalable Library.separate -- NB: JVM has tiny fixed-size stack;
 wenzelm parents: 
51616diff
changeset | 78 | } | 
| 36688 | 79 | |
| 56600 | 80 | def separated_chunks(sep: Char => Boolean, source: CharSequence): Iterator[CharSequence] = | 
| 48996 | 81 |     new Iterator[CharSequence] {
 | 
| 80478 
902e6da44a68
notable performance tuning for Library.separated_chunks variants;
 wenzelm parents: 
80441diff
changeset | 82 | private val length = source.length | 
| 
902e6da44a68
notable performance tuning for Library.separated_chunks variants;
 wenzelm parents: 
80441diff
changeset | 83 | private var start = -1 | 
| 
902e6da44a68
notable performance tuning for Library.separated_chunks variants;
 wenzelm parents: 
80441diff
changeset | 84 | private var stop = -1 | 
| 
902e6da44a68
notable performance tuning for Library.separated_chunks variants;
 wenzelm parents: 
80441diff
changeset | 85 | |
| 
902e6da44a68
notable performance tuning for Library.separated_chunks variants;
 wenzelm parents: 
80441diff
changeset | 86 |       private def end(i: Int): Int = {
 | 
| 
902e6da44a68
notable performance tuning for Library.separated_chunks variants;
 wenzelm parents: 
80441diff
changeset | 87 | var j = i | 
| 
902e6da44a68
notable performance tuning for Library.separated_chunks variants;
 wenzelm parents: 
80441diff
changeset | 88 |         while (j < length && !sep(source.charAt(j))) { j += 1 }
 | 
| 
902e6da44a68
notable performance tuning for Library.separated_chunks variants;
 wenzelm parents: 
80441diff
changeset | 89 | j | 
| 
902e6da44a68
notable performance tuning for Library.separated_chunks variants;
 wenzelm parents: 
80441diff
changeset | 90 | } | 
| 
902e6da44a68
notable performance tuning for Library.separated_chunks variants;
 wenzelm parents: 
80441diff
changeset | 91 | |
| 
902e6da44a68
notable performance tuning for Library.separated_chunks variants;
 wenzelm parents: 
80441diff
changeset | 92 | // init | 
| 
902e6da44a68
notable performance tuning for Library.separated_chunks variants;
 wenzelm parents: 
80441diff
changeset | 93 |       if (!source.isEmpty) { start = 0; stop = end(0) }
 | 
| 
902e6da44a68
notable performance tuning for Library.separated_chunks variants;
 wenzelm parents: 
80441diff
changeset | 94 | |
| 
902e6da44a68
notable performance tuning for Library.separated_chunks variants;
 wenzelm parents: 
80441diff
changeset | 95 | def hasNext: Boolean = 0 <= start && stop <= length | 
| 
902e6da44a68
notable performance tuning for Library.separated_chunks variants;
 wenzelm parents: 
80441diff
changeset | 96 | |
| 
902e6da44a68
notable performance tuning for Library.separated_chunks variants;
 wenzelm parents: 
80441diff
changeset | 97 | def next(): CharSequence = | 
| 
902e6da44a68
notable performance tuning for Library.separated_chunks variants;
 wenzelm parents: 
80441diff
changeset | 98 |         if (hasNext) {
 | 
| 
902e6da44a68
notable performance tuning for Library.separated_chunks variants;
 wenzelm parents: 
80441diff
changeset | 99 | val chunk = source.subSequence(start, stop) | 
| 
902e6da44a68
notable performance tuning for Library.separated_chunks variants;
 wenzelm parents: 
80441diff
changeset | 100 |           if (stop < length) { start = stop + 1; stop = end(start) }
 | 
| 
902e6da44a68
notable performance tuning for Library.separated_chunks variants;
 wenzelm parents: 
80441diff
changeset | 101 |           else { start = -1; stop = -1 }
 | 
| 
902e6da44a68
notable performance tuning for Library.separated_chunks variants;
 wenzelm parents: 
80441diff
changeset | 102 | chunk | 
| 48996 | 103 | } | 
| 80478 
902e6da44a68
notable performance tuning for Library.separated_chunks variants;
 wenzelm parents: 
80441diff
changeset | 104 | else throw new NoSuchElementException | 
| 
902e6da44a68
notable performance tuning for Library.separated_chunks variants;
 wenzelm parents: 
80441diff
changeset | 105 | } | 
| 
902e6da44a68
notable performance tuning for Library.separated_chunks variants;
 wenzelm parents: 
80441diff
changeset | 106 | |
| 
902e6da44a68
notable performance tuning for Library.separated_chunks variants;
 wenzelm parents: 
80441diff
changeset | 107 | def separated_chunks(sep: Char, source: String): Iterator[String] = | 
| 
902e6da44a68
notable performance tuning for Library.separated_chunks variants;
 wenzelm parents: 
80441diff
changeset | 108 |     new Iterator[String] {
 | 
| 
902e6da44a68
notable performance tuning for Library.separated_chunks variants;
 wenzelm parents: 
80441diff
changeset | 109 | private var start = -1 | 
| 
902e6da44a68
notable performance tuning for Library.separated_chunks variants;
 wenzelm parents: 
80441diff
changeset | 110 | private var stop = -1 | 
| 
902e6da44a68
notable performance tuning for Library.separated_chunks variants;
 wenzelm parents: 
80441diff
changeset | 111 | |
| 
902e6da44a68
notable performance tuning for Library.separated_chunks variants;
 wenzelm parents: 
80441diff
changeset | 112 | private def end(i: Int): Int = | 
| 
902e6da44a68
notable performance tuning for Library.separated_chunks variants;
 wenzelm parents: 
80441diff
changeset | 113 |         source.indexOf(sep, start) match {
 | 
| 
902e6da44a68
notable performance tuning for Library.separated_chunks variants;
 wenzelm parents: 
80441diff
changeset | 114 | case -1 => source.length | 
| 
902e6da44a68
notable performance tuning for Library.separated_chunks variants;
 wenzelm parents: 
80441diff
changeset | 115 | case j => j | 
| 
902e6da44a68
notable performance tuning for Library.separated_chunks variants;
 wenzelm parents: 
80441diff
changeset | 116 | } | 
| 48996 | 117 | |
| 80478 
902e6da44a68
notable performance tuning for Library.separated_chunks variants;
 wenzelm parents: 
80441diff
changeset | 118 | // init | 
| 
902e6da44a68
notable performance tuning for Library.separated_chunks variants;
 wenzelm parents: 
80441diff
changeset | 119 |       if (source.nonEmpty) { start = 0; stop = end(0) }
 | 
| 
902e6da44a68
notable performance tuning for Library.separated_chunks variants;
 wenzelm parents: 
80441diff
changeset | 120 | |
| 
902e6da44a68
notable performance tuning for Library.separated_chunks variants;
 wenzelm parents: 
80441diff
changeset | 121 | def hasNext: Boolean = | 
| 
902e6da44a68
notable performance tuning for Library.separated_chunks variants;
 wenzelm parents: 
80441diff
changeset | 122 | 0 <= start && start <= stop && stop <= source.length | 
| 
902e6da44a68
notable performance tuning for Library.separated_chunks variants;
 wenzelm parents: 
80441diff
changeset | 123 | |
| 
902e6da44a68
notable performance tuning for Library.separated_chunks variants;
 wenzelm parents: 
80441diff
changeset | 124 | def next(): String = | 
| 
902e6da44a68
notable performance tuning for Library.separated_chunks variants;
 wenzelm parents: 
80441diff
changeset | 125 |         if (hasNext) {
 | 
| 
902e6da44a68
notable performance tuning for Library.separated_chunks variants;
 wenzelm parents: 
80441diff
changeset | 126 | val chunk = source.substring(start, stop) | 
| 
902e6da44a68
notable performance tuning for Library.separated_chunks variants;
 wenzelm parents: 
80441diff
changeset | 127 |           if (stop < source.length) { start = stop + 1; stop = end(start) }
 | 
| 
902e6da44a68
notable performance tuning for Library.separated_chunks variants;
 wenzelm parents: 
80441diff
changeset | 128 |           else { start = -1; stop = -1 }
 | 
| 
902e6da44a68
notable performance tuning for Library.separated_chunks variants;
 wenzelm parents: 
80441diff
changeset | 129 | chunk | 
| 48996 | 130 | } | 
| 80478 
902e6da44a68
notable performance tuning for Library.separated_chunks variants;
 wenzelm parents: 
80441diff
changeset | 131 | else throw new NoSuchElementException | 
| 43598 | 132 | } | 
| 133 | ||
| 48996 | 134 | def space_explode(sep: Char, str: String): List[String] = | 
| 80478 
902e6da44a68
notable performance tuning for Library.separated_chunks variants;
 wenzelm parents: 
80441diff
changeset | 135 | separated_chunks(sep, str).toList | 
| 48996 | 136 | |
| 137 | ||
| 138 | /* lines */ | |
| 139 | ||
| 77007 | 140 | def count_newlines(str: String): Int = str.count(_ == '\n') | 
| 141 | ||
| 75859 
7164f537370f
proper treatment of empty lines (amending 08f89f0e8a62);
 wenzelm parents: 
75709diff
changeset | 142 |   def terminate_lines(lines: IterableOnce[String]): String = {
 | 
| 
7164f537370f
proper treatment of empty lines (amending 08f89f0e8a62);
 wenzelm parents: 
75709diff
changeset | 143 | val it = lines.iterator | 
| 
7164f537370f
proper treatment of empty lines (amending 08f89f0e8a62);
 wenzelm parents: 
75709diff
changeset | 144 |     if (it.isEmpty) "" else it.mkString("", "\n", "\n")
 | 
| 
7164f537370f
proper treatment of empty lines (amending 08f89f0e8a62);
 wenzelm parents: 
75709diff
changeset | 145 | } | 
| 51983 
32692ce4c61a
more frugal line termination, to cope with huge log files (see also 016cb7d8f297);
 wenzelm parents: 
51981diff
changeset | 146 | |
| 73362 | 147 | def cat_lines(lines: IterableOnce[String]): String = | 
| 148 |     lines.iterator.mkString("\n")
 | |
| 48996 | 149 | |
| 81826 | 150 | def make_lines(lines: String*): String = cat_lines(lines) | 
| 151 | ||
| 43670 
7f933761764b
prefer space_explode/split_lines as in Isabelle/ML;
 wenzelm parents: 
43652diff
changeset | 152 |   def split_lines(str: String): List[String] = space_explode('\n', str)
 | 
| 
7f933761764b
prefer space_explode/split_lines as in Isabelle/ML;
 wenzelm parents: 
43652diff
changeset | 153 | |
| 62590 | 154 | def prefix_lines(prfx: String, str: String): String = | 
| 73963 | 155 | isabelle.setup.Library.prefix_lines(prfx, str) | 
| 62590 | 156 | |
| 73736 | 157 | def indent_lines(n: Int, str: String): String = | 
| 158 | prefix_lines(Symbol.spaces(n), str) | |
| 159 | ||
| 75393 | 160 |   def first_line(source: CharSequence): String = {
 | 
| 56600 | 161 | val lines = separated_chunks(_ == '\n', source) | 
| 73344 | 162 | if (lines.hasNext) lines.next().toString | 
| 48996 | 163 | else "" | 
| 164 | } | |
| 165 | ||
| 73332 | 166 | def trim_line(s: String): String = | 
| 73963 | 167 | isabelle.setup.Library.trim_line(s) | 
| 73332 | 168 | |
| 169 | def trim_split_lines(s: String): List[String] = | |
| 170 | split_lines(trim_line(s)).map(trim_line) | |
| 171 | ||
| 65932 | 172 |   def encode_lines(s: String): String = s.replace('\n', '\u000b')
 | 
| 173 |   def decode_lines(s: String): String = s.replace('\u000b', '\n')
 | |
| 174 | ||
| 50847 | 175 | |
| 176 | /* strings */ | |
| 177 | ||
| 80440 | 178 |   def string_builder(hint: Int = 0)(body: StringBuilder => Unit): String = {
 | 
| 179 | val builder = new StringBuilder(if (hint <= 0) 16 else hint) | |
| 180 | body(builder) | |
| 181 | builder.toString | |
| 64355 | 182 | } | 
| 183 | ||
| 50847 | 184 | def try_unprefix(prfx: String, s: String): Option[String] = | 
| 185 | if (s.startsWith(prfx)) Some(s.substring(prfx.length)) else None | |
| 186 | ||
| 55033 | 187 | def try_unsuffix(sffx: String, s: String): Option[String] = | 
| 188 | if (s.endsWith(sffx)) Some(s.substring(0, s.length - sffx.length)) else None | |
| 189 | ||
| 65606 | 190 | def perhaps_unprefix(prfx: String, s: String): String = try_unprefix(prfx, s) getOrElse s | 
| 191 | def perhaps_unsuffix(sffx: String, s: String): String = try_unsuffix(sffx, s) getOrElse s | |
| 192 | ||
| 65903 | 193 | def isolate_substring(s: String): String = new String(s.toCharArray) | 
| 64820 
00488a8c042f
Line.Document consists of independently allocated strings;
 wenzelm parents: 
64370diff
changeset | 194 | |
| 71864 | 195 | def strip_ansi_color(s: String): String = | 
| 73355 | 196 |     s.replaceAll("\u001b\\[\\d+m", "")
 | 
| 71864 | 197 | |
| 43598 | 198 | |
| 48996 | 199 | /* quote */ | 
| 46196 | 200 | |
| 67820 
e30d6368c7c8
clarified argument formats: explicit Unit, allow XML.Elem as well;
 wenzelm parents: 
67436diff
changeset | 201 | def single_quote(s: String): String = "'" + s + "'" | 
| 
e30d6368c7c8
clarified argument formats: explicit Unit, allow XML.Elem as well;
 wenzelm parents: 
67436diff
changeset | 202 | |
| 43598 | 203 | def quote(s: String): String = "\"" + s + "\"" | 
| 56843 
b2bfcd8cda80
support for path completion based on file-system content;
 wenzelm parents: 
56730diff
changeset | 204 | |
| 
b2bfcd8cda80
support for path completion based on file-system content;
 wenzelm parents: 
56730diff
changeset | 205 | def try_unquote(s: String): Option[String] = | 
| 
b2bfcd8cda80
support for path completion based on file-system content;
 wenzelm parents: 
56730diff
changeset | 206 |     if (s.startsWith("\"") && s.endsWith("\"")) Some(s.substring(1, s.length - 1))
 | 
| 
b2bfcd8cda80
support for path completion based on file-system content;
 wenzelm parents: 
56730diff
changeset | 207 | else None | 
| 
b2bfcd8cda80
support for path completion based on file-system content;
 wenzelm parents: 
56730diff
changeset | 208 | |
| 58592 | 209 | def perhaps_unquote(s: String): String = try_unquote(s) getOrElse s | 
| 210 | ||
| 43598 | 211 |   def commas(ss: Iterable[String]): String = ss.iterator.mkString(", ")
 | 
| 48362 | 212 |   def commas_quote(ss: Iterable[String]): String = ss.iterator.map(quote).mkString(", ")
 | 
| 43598 | 213 | |
| 36688 | 214 | |
| 51983 
32692ce4c61a
more frugal line termination, to cope with huge log files (see also 016cb7d8f297);
 wenzelm parents: 
51981diff
changeset | 215 | /* CharSequence */ | 
| 34141 | 216 | |
| 75393 | 217 |   class Reverse(text: CharSequence, start: Int, end: Int) extends CharSequence {
 | 
| 73120 
c3589f2dff31
more informative errors: simplify diagnosis of spurious failures reported by users;
 wenzelm parents: 
72214diff
changeset | 218 | require(0 <= start && start <= end && end <= text.length, "bad reverse range") | 
| 34141 | 219 | |
| 220 | def this(text: CharSequence) = this(text, 0, text.length) | |
| 221 | ||
| 222 | def length: Int = end - start | |
| 223 | def charAt(i: Int): Char = text.charAt(end - i - 1) | |
| 224 | ||
| 225 | def subSequence(i: Int, j: Int): CharSequence = | |
| 226 | if (0 <= i && i <= j && j <= length) new Reverse(text, end - j, end - i) | |
| 227 | else throw new IndexOutOfBoundsException | |
| 228 | ||
| 80441 | 229 | override def toString: String = | 
| 230 |       string_builder(hint = length) { buf => for (i <- 0 until length) buf.append(charAt(i)) }
 | |
| 34141 | 231 | } | 
| 232 | ||
| 75393 | 233 |   class Line_Termination(text: CharSequence) extends CharSequence {
 | 
| 51983 
32692ce4c61a
more frugal line termination, to cope with huge log files (see also 016cb7d8f297);
 wenzelm parents: 
51981diff
changeset | 234 | def length: Int = text.length + 1 | 
| 
32692ce4c61a
more frugal line termination, to cope with huge log files (see also 016cb7d8f297);
 wenzelm parents: 
51981diff
changeset | 235 | def charAt(i: Int): Char = if (i == text.length) '\n' else text.charAt(i) | 
| 
32692ce4c61a
more frugal line termination, to cope with huge log files (see also 016cb7d8f297);
 wenzelm parents: 
51981diff
changeset | 236 | def subSequence(i: Int, j: Int): CharSequence = | 
| 
32692ce4c61a
more frugal line termination, to cope with huge log files (see also 016cb7d8f297);
 wenzelm parents: 
51981diff
changeset | 237 | if (j == text.length + 1) new Line_Termination(text.subSequence(i, j - 1)) | 
| 
32692ce4c61a
more frugal line termination, to cope with huge log files (see also 016cb7d8f297);
 wenzelm parents: 
51981diff
changeset | 238 | else text.subSequence(i, j) | 
| 
32692ce4c61a
more frugal line termination, to cope with huge log files (see also 016cb7d8f297);
 wenzelm parents: 
51981diff
changeset | 239 | override def toString: String = text.toString + "\n" | 
| 
32692ce4c61a
more frugal line termination, to cope with huge log files (see also 016cb7d8f297);
 wenzelm parents: 
51981diff
changeset | 240 | } | 
| 
32692ce4c61a
more frugal line termination, to cope with huge log files (see also 016cb7d8f297);
 wenzelm parents: 
51981diff
changeset | 241 | |
| 34141 | 242 | |
| 59224 | 243 | /* regular expressions */ | 
| 244 | ||
| 245 | def make_regex(s: String): Option[Regex] = | |
| 246 |     try { Some(new Regex(s)) } catch { case ERROR(_) => None }
 | |
| 247 | ||
| 64871 | 248 |   def is_regex_meta(c: Char): Boolean = """()[]{}\^$|?*+.<>-=!""".contains(c)
 | 
| 249 | ||
| 250 | def escape_regex(s: String): String = | |
| 71601 | 251 |     if (s.exists(is_regex_meta)) {
 | 
| 64871 | 252 | (for (c <- s.iterator) | 
| 253 |        yield { if (is_regex_meta(c)) "\\" + c.toString else c.toString }).mkString
 | |
| 254 | } | |
| 255 | else s | |
| 256 | ||
| 59224 | 257 | |
| 61883 
c0f34fe6aa61
clarified length of block with pre-existant forced breaks;
 wenzelm parents: 
60215diff
changeset | 258 | /* lists */ | 
| 
c0f34fe6aa61
clarified length of block with pre-existant forced breaks;
 wenzelm parents: 
60215diff
changeset | 259 | |
| 
c0f34fe6aa61
clarified length of block with pre-existant forced breaks;
 wenzelm parents: 
60215diff
changeset | 260 | def take_prefix[A](pred: A => Boolean, xs: List[A]): (List[A], List[A]) = | 
| 
c0f34fe6aa61
clarified length of block with pre-existant forced breaks;
 wenzelm parents: 
60215diff
changeset | 261 | (xs.takeWhile(pred), xs.dropWhile(pred)) | 
| 56686 | 262 | |
| 75393 | 263 |   def take_suffix[A](pred: A => Boolean, xs: List[A]): (List[A], List[A]) = {
 | 
| 67434 | 264 | val rev_xs = xs.reverse | 
| 265 | (rev_xs.dropWhile(pred).reverse, rev_xs.takeWhile(pred).reverse) | |
| 266 | } | |
| 267 | ||
| 268 | def trim[A](pred: A => Boolean, xs: List[A]): List[A] = | |
| 269 | take_suffix(pred, take_prefix(pred, xs)._2)._1 | |
| 270 | ||
| 60215 | 271 | def member[A, B](xs: List[A])(x: B): Boolean = xs.contains(x) | 
| 56688 | 272 | def insert[A](x: A)(xs: List[A]): List[A] = if (xs.contains(x)) xs else x :: xs | 
| 273 | def remove[A, B](x: B)(xs: List[A]): List[A] = if (member(xs)(x)) xs.filterNot(_ == x) else xs | |
| 274 | def update[A](x: A)(xs: List[A]): List[A] = x :: remove(x)(xs) | |
| 63734 
133e3e84e6fb
some support for merge of Isabelle/jEdit shortcuts wrt. jEdit keymap;
 wenzelm parents: 
62590diff
changeset | 275 | |
| 63867 | 276 | def merge[A](xs: List[A], ys: List[A]): List[A] = | 
| 277 | if (xs.eq(ys)) xs | |
| 278 | else if (xs.isEmpty) ys | |
| 279 | else ys.foldRight(xs)(Library.insert(_)(_)) | |
| 280 | ||
| 75393 | 281 |   def distinct[A](xs: List[A], eq: (A, A) => Boolean = (x: A, y: A) => x == y): List[A] = {
 | 
| 63734 
133e3e84e6fb
some support for merge of Isabelle/jEdit shortcuts wrt. jEdit keymap;
 wenzelm parents: 
62590diff
changeset | 282 | val result = new mutable.ListBuffer[A] | 
| 64207 | 283 | xs.foreach(x => if (!result.exists(y => eq(x, y))) result += x) | 
| 63734 
133e3e84e6fb
some support for merge of Isabelle/jEdit shortcuts wrt. jEdit keymap;
 wenzelm parents: 
62590diff
changeset | 284 | result.toList | 
| 
133e3e84e6fb
some support for merge of Isabelle/jEdit shortcuts wrt. jEdit keymap;
 wenzelm parents: 
62590diff
changeset | 285 | } | 
| 63781 | 286 | |
| 75393 | 287 |   def duplicates[A](lst: List[A], eq: (A, A) => Boolean = (x: A, y: A) => x == y): List[A] = {
 | 
| 63781 | 288 | val result = new mutable.ListBuffer[A] | 
| 289 | @tailrec def dups(rest: List[A]): Unit = | |
| 290 |       rest match {
 | |
| 291 | case Nil => | |
| 292 | case x :: xs => | |
| 64207 | 293 | if (!result.exists(y => eq(x, y)) && xs.exists(y => eq(x, y))) result += x | 
| 63781 | 294 | dups(xs) | 
| 295 | } | |
| 296 | dups(lst) | |
| 297 | result.toList | |
| 298 | } | |
| 65761 | 299 | |
| 68715 | 300 | def replicate[A](n: Int, a: A): List[A] = | 
| 301 | if (n < 0) throw new IllegalArgumentException | |
| 302 | else if (n == 0) Nil | |
| 303 |     else {
 | |
| 304 | val res = new mutable.ListBuffer[A] | |
| 305 | (1 to n).foreach(_ => res += a) | |
| 306 | res.toList | |
| 307 | } | |
| 308 | ||
| 78943 | 309 | def the_single[A](xs: List[A], message: => String = "Single argument expected"): A = | 
| 73571 | 310 |     xs match {
 | 
| 311 | case List(x) => x | |
| 78943 | 312 | case _ => error(message) | 
| 73571 | 313 | } | 
| 314 | ||
| 65761 | 315 | |
| 316 | /* proper values */ | |
| 317 | ||
| 75295 
38398766be6b
command-line arguments for "isabelle vscode", similar to "isabelle jedit";
 wenzelm parents: 
74794diff
changeset | 318 | def proper_bool(b: Boolean): Option[Boolean] = | 
| 
38398766be6b
command-line arguments for "isabelle vscode", similar to "isabelle jedit";
 wenzelm parents: 
74794diff
changeset | 319 | if (!b) None else Some(b) | 
| 
38398766be6b
command-line arguments for "isabelle vscode", similar to "isabelle jedit";
 wenzelm parents: 
74794diff
changeset | 320 | |
| 65761 | 321 | def proper_string(s: String): Option[String] = | 
| 322 | if (s == null || s == "") None else Some(s) | |
| 323 | ||
| 324 | def proper_list[A](list: List[A]): Option[List[A]] = | |
| 325 | if (list == null || list.isEmpty) None else Some(list) | |
| 72214 | 326 | |
| 77367 | 327 | def if_proper[A](x: Iterable[A], body: => String): String = | 
| 328 | if (x == null || x.isEmpty) "" else body | |
| 329 | ||
| 77614 | 330 | def if_proper(b: Boolean, body: => String): String = | 
| 331 | if (!b) "" else body | |
| 332 | ||
| 72214 | 333 | |
| 334 | /* reflection */ | |
| 335 | ||
| 75393 | 336 |   def is_subclass[A, B](a: Class[A], b: Class[B]): Boolean = {
 | 
| 73339 | 337 | import scala.language.existentials | 
| 75393 | 338 |     @tailrec def subclass(c: Class[_]): Boolean = {
 | 
| 339 |       c == b || { val d = c.getSuperclass; d != null && subclass(d) }
 | |
| 72214 | 340 | } | 
| 341 | subclass(a) | |
| 342 | } | |
| 76788 | 343 | |
| 344 | def as_subclass[C](c: Class[C])(x: AnyRef): Option[C] = | |
| 345 | if (x == null || is_subclass(x.getClass, c)) Some(x.asInstanceOf[C]) else None | |
| 34136 | 346 | } |