1 (* Title: Pure/General/path.ML
2 Author: Markus Wenzel, TU Muenchen
4 Algebra of file-system paths: basic POSIX notation, extended by named
5 roots (e.g. //foo) and variables (e.g. $BAR).
11 val is_current: T -> bool
14 val named_root: string -> T
16 val basic: string -> T
17 val variable: string -> T
18 val is_absolute: T -> bool
19 val is_basic: T -> bool
20 val append: T -> T -> T
21 val appends: T list -> T
22 val make: string list -> T
23 val implode: T -> string
24 val explode: string -> T
25 val pretty: T -> Pretty.T
26 val print: T -> string
29 val ext: string -> T -> T
30 val split_ext: T -> T * string
32 val position: T -> Position.T
35 structure Path: PATH =
48 fun err_elem msg chs = error (msg ^ " path element specification: " ^ quote (implode chs));
50 fun check_elem (chs as []) = err_elem "Illegal" chs
51 | check_elem (chs as ["~"]) = err_elem "Illegal" chs
52 | check_elem (chs as ["~", "~"]) = err_elem "Illegal" chs
54 (case inter (op =) ["/", "\\", ":"] chs of
56 | bads => err_elem ("Illegal character(s) " ^ commas_quote bads ^ " in") chs);
60 val root_elem = Root o implode o check_elem;
61 val basic_elem = Basic o implode o check_elem;
62 val variable_elem = Variable o implode o check_elem;
69 datatype T = Path of elem list; (*reversed elements*)
71 fun rep (Path xs) = xs;
73 fun is_current (Path []) = true
74 | is_current _ = false;
76 val current = Path [];
77 val root = Path [Root ""];
78 fun named_root s = Path [root_elem (raw_explode s)];
79 fun basic s = Path [basic_elem (raw_explode s)];
80 fun variable s = Path [variable_elem (raw_explode s)];
81 val parent = Path [Parent];
83 fun is_absolute (Path xs) =
84 (case try List.last xs of
88 fun is_basic (Path [Basic _]) = true
94 fun apply (y as Root _) _ = [y]
95 | apply Parent (xs as (Root _ :: _)) = xs
96 | apply Parent (Basic _ :: rest) = rest
97 | apply y xs = y :: xs;
99 fun append (Path xs) (Path ys) = Path (fold_rev apply ys xs);
100 fun appends paths = Library.foldl (uncurry append) (current, paths);
101 val make = appends o map basic;
103 fun norm elems = fold_rev apply elems [];
110 fun implode_elem (Root "") = ""
111 | implode_elem (Root s) = "//" ^ s
112 | implode_elem (Basic s) = s
113 | implode_elem (Variable s) = "$" ^ s
114 | implode_elem Parent = "..";
118 fun implode_path (Path []) = "."
119 | implode_path (Path [Root ""]) = "/"
120 | implode_path (Path xs) = space_implode "/" (rev (map implode_elem xs));
129 fun explode_elem ".." = Parent
130 | explode_elem "~" = Variable "USER_HOME"
131 | explode_elem "~~" = Variable "ISABELLE_HOME"
133 (case raw_explode s of
134 "$" :: cs => variable_elem cs
135 | cs => basic_elem cs);
138 rev o map explode_elem o filter_out (fn c => c = "" orelse c = ".");
142 fun explode_path str =
143 let val (roots, raw_elems) =
144 (case take_prefix (equal "") (space_explode "/" str) |>> length of
146 | (1, es) => ([Root ""], es)
147 | (_, []) => ([Root ""], [])
148 | (_, e :: es) => ([root_elem (raw_explode e)], es))
149 in Path (norm (explode_elems raw_elems @ roots)) end;
157 let val s = implode_path path
158 in Pretty.mark (Isabelle_Markup.path s) (Pretty.str (quote s)) end;
160 val print = Pretty.str_of o pretty;
165 fun split_path f (Path (Basic s :: xs)) = f (Path xs, s)
166 | split_path _ path = error ("Cannot split path into dir/base: " ^ print path);
168 val dir = split_path #1;
169 val base = split_path (fn (_, s) => Path [Basic s]);
172 | ext e = split_path (fn (prfx, s) => append prfx (basic (s ^ "." ^ e)));
174 val split_ext = split_path (fn (prfx, s) => apfst (append prfx)
175 (case take_suffix (fn c => c <> ".") (raw_explode s) of
176 ([], _) => (Path [Basic s], "")
177 | (cs, e) => (Path [Basic (implode (take (length cs - 1) cs))], implode e)));
180 (* expand variables *)
182 fun eval (Variable s) = rep (explode_path (getenv_strict s))
185 val expand = rep #> maps eval #> norm #> Path;
188 (* source position -- with smart replacement ISABELLE_HOME *)
190 val isabelle_home = explode_path "~~";
194 val s = implode_path path;
195 val prfx = implode_path (expand isabelle_home) ^ "/";
198 (case try (unprefix prfx) s of
200 | SOME s' => "~~/" ^ s')
204 (*final declarations of this structure!*)
205 val implode = implode_path;
206 val explode = explode_path;