src/Pure/General/symbol.scala
author wenzelm
Sat, 27 Dec 2008 14:57:30 +0100
changeset 29174 d4058295affb
parent 29140 e7ac5bb20aed
child 29569 f3f529b5d8fb
permissions -rw-r--r--
proper class IsabelleSystem -- no longer static;
tuned;
     1 /*  Title:      Pure/General/symbol.scala
     2     Author:     Makarius
     3 
     4 Detecting and recoding Isabelle symbols.
     5 */
     6 
     7 package isabelle
     8 
     9 import java.util.regex.Pattern
    10 import java.io.File
    11 import scala.io.Source
    12 import scala.collection.jcl.HashMap
    13 
    14 
    15 object Symbol {
    16 
    17   /** Symbol regexps **/
    18 
    19   private def compile(s: String) =
    20     Pattern.compile(s, Pattern.COMMENTS | Pattern.DOTALL)
    21 
    22   private val plain_pattern = compile(""" [^\\ \ud800-\udfff] | [\ud800-\udbff][\udc00-\udfff] """)
    23 
    24   private val symbol_pattern = compile(""" \\ \\? < (?:
    25       \^? [A-Za-z][A-Za-z0-9_']* |
    26       \^raw: [\x20-\x7e\u0100-\uffff && [^.>]]* ) >""")
    27 
    28   private val bad_symbol_pattern = compile("(?!" + symbol_pattern + ")" +
    29     """ \\ \\? < (?: (?! \s | [\"`\\] | \(\* | \*\) | \{\* | \*\} ) . )*""")
    30 
    31   // total pattern
    32   val pattern = compile(plain_pattern + "|" + symbol_pattern + "|" + bad_symbol_pattern + "| .")
    33 
    34 
    35 
    36   /** Recoding **/
    37 
    38   private class Recoder(list: List[(String, String)]) {
    39     private val (min, max) = {
    40       var min = '\uffff'
    41       var max = '\u0000'
    42       for ((x, _) <- list) {
    43         val c = x(0)
    44         if (c < min) min = c
    45         if (c > max) max = c
    46       }
    47       (min, max)
    48     }
    49     private val table = {
    50       val table = new HashMap[String, String]
    51       for ((x, y) <- list) table + (x -> y)
    52       table
    53     }
    54     def recode(text: String) = {
    55       val len = text.length
    56       val matcher = pattern.matcher(text)
    57       val result = new StringBuilder(len)
    58       var i = 0
    59       while (i < len) {
    60         val c = text(i)
    61         if (min <= c && c <= max) {
    62           matcher.region(i, len)
    63           matcher.lookingAt
    64           val x = matcher.group
    65           table.get(x) match {
    66             case Some(y) => result.append(y)
    67             case None => result.append(x)
    68           }
    69           i = matcher.end
    70         }
    71         else { result.append(c); i += 1 }
    72       }
    73       result.toString
    74     }
    75   }
    76 
    77 
    78 
    79   /** Symbol interpretation **/
    80 
    81   class Interpretation(isabelle_system: IsabelleSystem) {
    82 
    83     private var symbols = new HashMap[String, HashMap[String, String]]
    84     private var decoder: Recoder = null
    85     private var encoder: Recoder = null
    86 
    87     def decode(text: String) = decoder.recode(text)
    88     def encode(text: String) = encoder.recode(text)
    89 
    90 
    91     /* read symbols */
    92 
    93     private val empty_pattern = compile(""" ^\s* (?: \#.* )? $ """)
    94     private val blank_pattern = compile(""" \s+ """)
    95     private val key_pattern = compile(""" (.+): """)
    96 
    97     private def read_line(line: String) = {
    98       def err() = error("Bad symbol specification (line " + line + ")")
    99 
   100       def read_props(props: List[String], tab: HashMap[String, String]): Unit = {
   101         props match {
   102           case Nil => ()
   103           case _ :: Nil => err()
   104           case key :: value :: rest => {
   105             val key_matcher = key_pattern.matcher(key)
   106             if (key_matcher.matches) {
   107               tab + (key_matcher.group(1) -> value)
   108               read_props(rest, tab)
   109             }
   110             else err ()
   111           }
   112         }
   113       }
   114 
   115       if (!empty_pattern.matcher(line).matches) {
   116         blank_pattern.split(line).toList match {
   117           case Nil => err()
   118           case symbol :: props => {
   119             val tab = new HashMap[String, String]
   120             read_props(props, tab)
   121             symbols + (symbol -> tab)
   122           }
   123         }
   124       }
   125     }
   126 
   127     private def read_symbols(path: String) = {
   128       val file = new File(isabelle_system.platform_path(path))
   129       if (file.canRead) {
   130         for (line <- Source.fromFile(file).getLines) read_line(line)
   131       }
   132     }
   133 
   134 
   135     /* init tables */
   136 
   137     private def get_code(entry: (String, HashMap[String, String])) = {
   138       val (symbol, props) = entry
   139       val code =
   140         try { Integer.decode(props("code")).intValue }
   141         catch {
   142           case _: NoSuchElementException => error("Missing code for symbol " + symbol)
   143           case _: NumberFormatException => error("Bad code for symbol " + symbol)
   144         }
   145       (symbol, new String(Character.toChars(code)))
   146     }
   147 
   148     private def init_recoders() = {
   149       val list = symbols.elements.toList.map(get_code)
   150       decoder = new Recoder(list ++ (for ((x, y) <- list) yield ("\\" + x, y)))
   151       encoder = new Recoder(for ((x, y) <- list) yield (y, x))
   152     }
   153 
   154 
   155     /* constructor */
   156 
   157     read_symbols("$ISABELLE_HOME/etc/symbols")
   158     read_symbols("$ISABELLE_HOME_USER/etc/symbols")
   159     init_recoders()
   160   }
   161 
   162 }