| 6118 |      1 | (*  Title:      Pure/General/path.ML
 | 
| 5011 |      2 |     ID:         $Id$
 | 
|  |      3 |     Author:     Markus Wenzel, TU Muenchen
 | 
| 8806 |      4 |     License:    GPL (GNU GENERAL PUBLIC LICENSE)
 | 
| 5011 |      5 | 
 | 
| 6183 |      6 | Abstract algebra of file paths (external encoding Unix-style).
 | 
| 5011 |      7 | *)
 | 
|  |      8 | 
 | 
|  |      9 | signature PATH =
 | 
|  |     10 | sig
 | 
| 6183 |     11 |   datatype elem = Root | Parent | Basic of string | Variable of string
 | 
|  |     12 |   eqtype T
 | 
|  |     13 |   val rep: T -> elem list
 | 
| 6460 |     14 |   val is_current: T -> bool
 | 
| 6183 |     15 |   val current: T
 | 
|  |     16 |   val root: T
 | 
|  |     17 |   val parent: T
 | 
|  |     18 |   val basic: string -> T
 | 
|  |     19 |   val variable: string -> T
 | 
|  |     20 |   val is_absolute: T -> bool
 | 
|  |     21 |   val is_basic: T -> bool
 | 
|  |     22 |   val append: T -> T -> T
 | 
| 6270 |     23 |   val appends: T list -> T
 | 
| 6319 |     24 |   val make: string list -> T
 | 
| 5011 |     25 |   val pack: T -> string
 | 
|  |     26 |   val unpack: string -> T
 | 
| 6183 |     27 |   val base: T -> T
 | 
|  |     28 |   val ext: string -> T -> T
 | 
| 7929 |     29 |   val drop_ext: T -> T
 | 
| 6319 |     30 |   val dir: T -> T
 | 
| 6183 |     31 |   val expand: T -> T
 | 
| 7714 |     32 |   val position: T -> Position.T
 | 
| 5011 |     33 | end;
 | 
|  |     34 | 
 | 
| 6187 |     35 | structure Path: PATH =
 | 
| 5011 |     36 | struct
 | 
|  |     37 | 
 | 
| 6183 |     38 | 
 | 
|  |     39 | (* path elements *)
 | 
|  |     40 | 
 | 
|  |     41 | datatype elem = Root | Parent | Basic of string | Variable of string;
 | 
|  |     42 | 
 | 
| 6223 |     43 | fun err_elem msg chs = error (msg ^ " path element specification: " ^ quote (implode chs));
 | 
| 6183 |     44 | 
 | 
| 6319 |     45 | fun check_elem (chs as []) = err_elem "Illegal" chs
 | 
|  |     46 |   | check_elem (chs as ["~"]) = err_elem "Illegal" chs
 | 
| 6223 |     47 |   | check_elem (chs as ["~", "~"]) = err_elem "Illegal" chs
 | 
|  |     48 |   | check_elem chs =
 | 
|  |     49 |       (case ["/", "\\", "$", ":"] inter_string chs of
 | 
|  |     50 |         [] => chs
 | 
|  |     51 |       | bads => err_elem ("Illegal character(s) " ^ commas_quote bads ^ " in") chs);
 | 
|  |     52 | 
 | 
|  |     53 | val basic_elem = Basic o implode o check_elem;
 | 
|  |     54 | val variable_elem = Variable o implode o check_elem;
 | 
| 6183 |     55 | 
 | 
|  |     56 | fun is_var (Variable _) = true
 | 
|  |     57 |   | is_var _ = false;
 | 
|  |     58 | 
 | 
|  |     59 | 
 | 
| 5011 |     60 | (* type path *)
 | 
|  |     61 | 
 | 
| 6183 |     62 | datatype T = Path of elem list;
 | 
|  |     63 | 
 | 
|  |     64 | fun rep (Path xs) = xs;
 | 
| 5011 |     65 | 
 | 
| 6460 |     66 | fun is_current (Path []) = true
 | 
|  |     67 |   | is_current _ = false;
 | 
|  |     68 | 
 | 
| 5011 |     69 | val current = Path [];
 | 
| 6183 |     70 | val root = Path [Root];
 | 
|  |     71 | val parent = Path [Parent];
 | 
|  |     72 | fun basic s = Path [basic_elem (explode s)];
 | 
|  |     73 | fun variable s = Path [variable_elem (explode s)];
 | 
| 5011 |     74 | 
 | 
| 6183 |     75 | fun is_absolute (Path (Root :: _)) = true
 | 
|  |     76 |   | is_absolute _ = false;
 | 
| 5011 |     77 | 
 | 
| 6183 |     78 | fun is_basic (Path [Basic _]) = true
 | 
|  |     79 |   | is_basic _ = false;
 | 
| 5011 |     80 | 
 | 
|  |     81 | 
 | 
|  |     82 | (* append and norm *)
 | 
|  |     83 | 
 | 
|  |     84 | (*append non-normal path (2n arg) to reversed normal one, result is normal*)
 | 
|  |     85 | fun rev_app xs [] = rev xs
 | 
| 6183 |     86 |   | rev_app _ (Root :: ys) = rev_app [Root] ys
 | 
|  |     87 |   | rev_app (x :: xs) (Parent :: ys) =
 | 
|  |     88 |       if x = Parent orelse is_var x then rev_app (Parent :: x :: xs) ys
 | 
|  |     89 |       else if x = Root then rev_app (x :: xs) ys
 | 
| 5011 |     90 |       else rev_app xs ys
 | 
|  |     91 |   | rev_app xs (y :: ys) = rev_app (y :: xs) ys;
 | 
|  |     92 | 
 | 
| 6183 |     93 | fun append (Path xs) (Path ys) = Path (rev_app (rev xs) ys);
 | 
| 6270 |     94 | fun appends paths = foldl (uncurry append) (current, paths);
 | 
| 6319 |     95 | val make = appends o map basic;
 | 
| 5011 |     96 | fun norm path = rev_app [] path;
 | 
|  |     97 | 
 | 
| 6183 |     98 | 
 | 
|  |     99 | (* pack *)
 | 
| 5011 |    100 | 
 | 
| 6183 |    101 | fun pack_elem Root = ""
 | 
|  |    102 |   | pack_elem Parent = ".."
 | 
|  |    103 |   | pack_elem (Basic s) = s
 | 
|  |    104 |   | pack_elem (Variable s) = "$" ^ s;
 | 
| 5011 |    105 | 
 | 
|  |    106 | fun pack (Path []) = "."
 | 
| 6183 |    107 |   | pack (Path (Root :: xs)) = "/" ^ space_implode "/" (map pack_elem xs)
 | 
|  |    108 |   | pack (Path xs) = space_implode "/" (map pack_elem xs);
 | 
| 5011 |    109 | 
 | 
|  |    110 | 
 | 
| 6183 |    111 | (* unpack *)
 | 
|  |    112 | 
 | 
|  |    113 | fun unpack_elem "" = Root
 | 
|  |    114 |   | unpack_elem ".." = Parent
 | 
|  |    115 |   | unpack_elem "~" = Variable "HOME"
 | 
|  |    116 |   | unpack_elem "~~" = Variable "ISABELLE_HOME"
 | 
|  |    117 |   | unpack_elem s =
 | 
|  |    118 |       (case explode s of
 | 
|  |    119 |         "$" :: cs => variable_elem cs
 | 
|  |    120 |       | cs => basic_elem cs);
 | 
|  |    121 | 
 | 
|  |    122 | val unpack_elems = map unpack_elem o filter_out (equal "" orf equal ".");
 | 
|  |    123 | 
 | 
|  |    124 | fun unpack str = Path (norm
 | 
|  |    125 |   (case space_explode "/" str of
 | 
|  |    126 |     "" :: ss => Root :: unpack_elems ss
 | 
|  |    127 |   | ss => unpack_elems ss));
 | 
|  |    128 | 
 | 
| 5011 |    129 | 
 | 
| 6183 |    130 | (* base element *)
 | 
|  |    131 | 
 | 
| 7929 |    132 | fun split_path f (path as Path xs) =
 | 
|  |    133 |   (case try split_last xs of
 | 
|  |    134 |     Some (prfx, Basic s) => f (prfx, s)
 | 
|  |    135 |   | _ => error ("Cannot split path into dir/base: " ^ quote (pack path)));
 | 
| 6183 |    136 | 
 | 
| 7929 |    137 | val base = split_path (fn (_, s) => Path [Basic s]);
 | 
| 6183 |    138 | 
 | 
| 7929 |    139 | fun ext "" path = path
 | 
|  |    140 |   | ext e path = split_path (fn (prfx, s) => append (Path prfx) (basic (s ^ "." ^ e))) path;
 | 
| 6183 |    141 | 
 | 
| 7929 |    142 | val drop_ext = split_path (fn (prfx, s) => append (Path prfx)
 | 
|  |    143 |   (case take_suffix (not_equal ".") (explode s) of
 | 
|  |    144 |     ([], _) => Path [Basic s]
 | 
|  |    145 |   | (cs, _) => Path [Basic (implode (take (length cs - 1, cs)))]));
 | 
|  |    146 | 
 | 
|  |    147 | val dir = split_path (fn (prfx, _) => Path prfx);
 | 
| 6319 |    148 | 
 | 
| 6183 |    149 | 
 | 
|  |    150 | (* evaluate variables *)
 | 
|  |    151 | 
 | 
|  |    152 | fun eval env (Variable s) = rep (env s)
 | 
|  |    153 |   | eval _ x = [x];
 | 
| 5011 |    154 | 
 | 
|  |    155 | fun evaluate env (Path xs) =
 | 
|  |    156 |   Path (norm (flat (map (eval env) xs)));
 | 
|  |    157 | 
 | 
| 6183 |    158 | val expand = evaluate (unpack o getenv);
 | 
| 5011 |    159 | 
 | 
| 7714 |    160 | val position = Position.line_name 1 o quote o pack o expand;
 | 
|  |    161 | 
 | 
| 5011 |    162 | 
 | 
|  |    163 | end;
 |