| author | nipkow | 
| Mon, 02 Apr 2012 20:12:10 +0200 | |
| changeset 47302 | 70239da25ef6 | 
| parent 46712 | 8650d9a95736 | 
| child 47661 | 012a887997f3 | 
| permissions | -rw-r--r-- | 
| 43600 | 1 | /* Title: Pure/General/path.scala | 
| 2 | Author: Makarius | |
| 3 | ||
| 43601 | 4 | Algebra of file-system paths: basic POSIX notation, extended by named | 
| 5 | roots (e.g. //foo) and variables (e.g. $BAR). | |
| 43600 | 6 | */ | 
| 7 | ||
| 8 | package isabelle | |
| 9 | ||
| 10 | ||
| 43697 
77ce24aa1770
explicit Document.Node.Header, with master_dir and thy_name;
 wenzelm parents: 
43670diff
changeset | 11 | import scala.util.matching.Regex | 
| 
77ce24aa1770
explicit Document.Node.Header, with master_dir and thy_name;
 wenzelm parents: 
43670diff
changeset | 12 | |
| 
77ce24aa1770
explicit Document.Node.Header, with master_dir and thy_name;
 wenzelm parents: 
43670diff
changeset | 13 | |
| 43600 | 14 | object Path | 
| 15 | {
 | |
| 16 | /* path elements */ | |
| 17 | ||
| 45244 | 18 | sealed abstract class Elem | 
| 43600 | 19 | private case class Root(val name: String) extends Elem | 
| 20 | private case class Basic(val name: String) extends Elem | |
| 21 | private case class Variable(val name: String) extends Elem | |
| 22 | private case object Parent extends Elem | |
| 23 | ||
| 24 | private def err_elem(msg: String, s: String): Nothing = | |
| 43652 | 25 | error (msg + " path element specification: " + quote(s)) | 
| 43600 | 26 | |
| 27 | private def check_elem(s: String): String = | |
| 28 |     if (s == "" || s == "~" || s == "~~") err_elem("Illegal", s)
 | |
| 29 | else | |
| 30 |       s.iterator.filter(c => c == '/' || c == '\\' || c == '$' || c == ':').toList match {
 | |
| 31 | case Nil => s | |
| 32 | case bads => | |
| 43652 | 33 |           err_elem ("Illegal character(s) " + commas_quote(bads.map(_.toString)) + " in", s)
 | 
| 43600 | 34 | } | 
| 35 | ||
| 36 | private def root_elem(s: String): Elem = Root(check_elem(s)) | |
| 37 | private def basic_elem(s: String): Elem = Basic(check_elem(s)) | |
| 38 | private def variable_elem(s: String): Elem = Variable(check_elem(s)) | |
| 39 | ||
| 40 | private def apply_elem(y: Elem, xs: List[Elem]): List[Elem] = | |
| 41 |     (y, xs) match {
 | |
| 42 | case (Root(_), _) => List(y) | |
| 43 | case (Parent, Root(_) :: _) => xs | |
| 44 | case (Parent, Basic(_) :: rest) => rest | |
| 45 | case _ => y :: xs | |
| 46 | } | |
| 47 | ||
| 48 | private def norm_elems(elems: List[Elem]): List[Elem] = | |
| 49 | (elems :\ (Nil: List[Elem]))(apply_elem) | |
| 50 | ||
| 51 | private def implode_elem(elem: Elem): String = | |
| 52 |     elem match {
 | |
| 53 |       case Root("") => ""
 | |
| 54 | case Root(s) => "//" + s | |
| 55 | case Basic(s) => s | |
| 56 | case Variable(s) => "$" + s | |
| 57 | case Parent => ".." | |
| 58 | } | |
| 59 | ||
| 60 | ||
| 61 | /* path constructors */ | |
| 62 | ||
| 45244 | 63 | val current: Path = new Path(Nil) | 
| 64 |   val root: Path = new Path(List(Root("")))
 | |
| 65 | def named_root(s: String): Path = new Path(List(root_elem(s))) | |
| 66 | def basic(s: String): Path = new Path(List(basic_elem(s))) | |
| 67 | def variable(s: String): Path = new Path(List(variable_elem(s))) | |
| 68 | val parent: Path = new Path(List(Parent)) | |
| 43600 | 69 | |
| 70 | ||
| 71 | /* explode */ | |
| 72 | ||
| 73 | private def explode_elem(s: String): Elem = | |
| 74 | if (s == "..") Parent | |
| 75 |     else if (s == "~") Variable("HOME")
 | |
| 76 |     else if (s == "~~") Variable("ISABELLE_HOME")
 | |
| 77 |     else if (s.startsWith("$")) variable_elem(s.substring(1))
 | |
| 78 | else basic_elem(s) | |
| 79 | ||
| 80 | private def explode_elems(ss: List[String]): List[Elem] = | |
| 81 | ss.filterNot(s => s.isEmpty || s == ".").map(explode_elem).reverse | |
| 82 | ||
| 83 | def explode(str: String): Path = | |
| 84 |   {
 | |
| 43670 
7f933761764b
prefer space_explode/split_lines as in Isabelle/ML;
 wenzelm parents: 
43669diff
changeset | 85 |     val ss = space_explode('/', str)
 | 
| 43600 | 86 | val r = ss.takeWhile(_.isEmpty).length | 
| 87 | val es = ss.dropWhile(_.isEmpty) | |
| 88 | val (roots, raw_elems) = | |
| 89 | if (r == 0) (Nil, es) | |
| 90 |       else if (r == 1) (List(Root("")), es)
 | |
| 91 |       else if (es.isEmpty) (List(Root("")), Nil)
 | |
| 92 | else (List(root_elem(es.head)), es.tail) | |
| 45244 | 93 | new Path(norm_elems(explode_elems(raw_elems) ++ roots)) | 
| 43600 | 94 | } | 
| 43669 | 95 | |
| 96 | def split(str: String): List[Path] = | |
| 43670 
7f933761764b
prefer space_explode/split_lines as in Isabelle/ML;
 wenzelm parents: 
43669diff
changeset | 97 |     space_explode(':', str).filterNot(_.isEmpty).map(explode)
 | 
| 43600 | 98 | } | 
| 99 | ||
| 43669 | 100 | |
| 46712 | 101 | final class Path private(private val elems: List[Path.Elem]) // reversed elements | 
| 43600 | 102 | {
 | 
| 103 | def is_current: Boolean = elems.isEmpty | |
| 104 | def is_absolute: Boolean = !elems.isEmpty && elems.last.isInstanceOf[Path.Root] | |
| 105 |   def is_basic: Boolean = elems match { case List(Path.Basic(_)) => true case _ => false }
 | |
| 106 | ||
| 45244 | 107 | def +(other: Path): Path = new Path((other.elems :\ elems)(Path.apply_elem)) | 
| 43600 | 108 | |
| 109 | ||
| 43604 | 110 | /* implode */ | 
| 43600 | 111 | |
| 43604 | 112 | def implode: String = | 
| 43600 | 113 |     elems match {
 | 
| 114 | case Nil => "." | |
| 115 |       case List(Path.Root("")) => "/"
 | |
| 43604 | 116 |       case _ => elems.map(Path.implode_elem).reverse.mkString("/")
 | 
| 43600 | 117 | } | 
| 118 | ||
| 43652 | 119 | override def toString: String = quote(implode) | 
| 43600 | 120 | |
| 121 | ||
| 122 | /* base element */ | |
| 123 | ||
| 124 | private def split_path: (Path, String) = | |
| 125 |     elems match {
 | |
| 45244 | 126 | case Path.Basic(s) :: xs => (new Path(xs), s) | 
| 43604 | 127 |       case _ => error("Cannot split path into dir/base: " + toString)
 | 
| 43600 | 128 | } | 
| 129 | ||
| 130 | def dir: Path = split_path._1 | |
| 45244 | 131 | def base: Path = new Path(List(Path.Basic(split_path._2))) | 
| 43600 | 132 | |
| 133 | def ext(e: String): Path = | |
| 134 | if (e == "") this | |
| 135 |     else {
 | |
| 136 | val (prfx, s) = split_path | |
| 43604 | 137 | prfx + Path.basic(s + "." + e) | 
| 43600 | 138 | } | 
| 43604 | 139 | |
| 43697 
77ce24aa1770
explicit Document.Node.Header, with master_dir and thy_name;
 wenzelm parents: 
43670diff
changeset | 140 |   private val Ext = new Regex("(.*)\\.([^.]*)")
 | 
| 
77ce24aa1770
explicit Document.Node.Header, with master_dir and thy_name;
 wenzelm parents: 
43670diff
changeset | 141 | |
| 
77ce24aa1770
explicit Document.Node.Header, with master_dir and thy_name;
 wenzelm parents: 
43670diff
changeset | 142 | def split_ext: (Path, String) = | 
| 
77ce24aa1770
explicit Document.Node.Header, with master_dir and thy_name;
 wenzelm parents: 
43670diff
changeset | 143 |   {
 | 
| 
77ce24aa1770
explicit Document.Node.Header, with master_dir and thy_name;
 wenzelm parents: 
43670diff
changeset | 144 | val (prefix, base) = split_path | 
| 
77ce24aa1770
explicit Document.Node.Header, with master_dir and thy_name;
 wenzelm parents: 
43670diff
changeset | 145 |     base match {
 | 
| 
77ce24aa1770
explicit Document.Node.Header, with master_dir and thy_name;
 wenzelm parents: 
43670diff
changeset | 146 | case Ext(b, e) => (prefix + Path.basic(b), e) | 
| 
77ce24aa1770
explicit Document.Node.Header, with master_dir and thy_name;
 wenzelm parents: 
43670diff
changeset | 147 | case _ => (Path.basic(base), "") | 
| 
77ce24aa1770
explicit Document.Node.Header, with master_dir and thy_name;
 wenzelm parents: 
43670diff
changeset | 148 | } | 
| 
77ce24aa1770
explicit Document.Node.Header, with master_dir and thy_name;
 wenzelm parents: 
43670diff
changeset | 149 | } | 
| 
77ce24aa1770
explicit Document.Node.Header, with master_dir and thy_name;
 wenzelm parents: 
43670diff
changeset | 150 | |
| 43604 | 151 | |
| 152 | /* expand */ | |
| 153 | ||
| 43664 | 154 | def expand: Path = | 
| 43604 | 155 |   {
 | 
| 156 | def eval(elem: Path.Elem): List[Path.Elem] = | |
| 157 |       elem match {
 | |
| 43664 | 158 | case Path.Variable(s) => | 
| 159 | Path.explode(Isabelle_System.getenv_strict(s)).elems | |
| 43604 | 160 | case x => List(x) | 
| 161 | } | |
| 162 | ||
| 45244 | 163 | new Path(Path.norm_elems(elems.map(eval).flatten)) | 
| 43604 | 164 | } | 
| 43600 | 165 | } |