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