1structure Holmake_types :> Holmake_types = 2struct 3 4open internal_functions 5 6datatype pretoken = DEFN of string | RULE of string | EOF 7 8datatype frag = LIT of string | VREF of string 9type quotation = frag list 10type env = (string, quotation)Binarymap.dict 11type rule_info = {dependencies : string list, commands : string list} 12type raw_rule_info = { targets : quotation, dependencies : quotation, 13 commands : quotation list } 14type ruledb = 15 (string, {dependencies: string list, commands: quotation list}) Binarymap.dict 16datatype token = HM_defn of string * quotation 17 | HM_rule of raw_rule_info 18 19fun normquote acc [] = List.rev acc 20 | normquote acc [x] = List.rev (x::acc) 21 | normquote acc (LIT s1 :: LIT s2 :: t) = normquote acc (LIT (s1 ^ s2) :: t) 22 | normquote acc (h :: t) = normquote (h::acc) t 23 24(* for strings that are not commands *) 25fun is_special c = c = #"#" orelse c = #"$" orelse c = #"\\" 26 27fun ok_symbolvars c = c = #"<" orelse c = #"@" 28 29fun check_for_vref (startc, endc) acc ss k = let 30 open Substring 31 (* scan forward for balancing endc *) 32 fun recurse (count, depth, ss) = 33 case getc ss of 34 NONE => raise Fail ("Unclosed variable reference, beginning: $"^ 35 str startc ^ 36 string (slice(ss, 0, SOME(Int.min(size ss, 10))))) 37 | SOME(c, ss') => if c = endc then 38 if depth = 0 then (count, slice(ss, 1, NONE)) 39 else recurse (count + 1, depth - 1, ss') 40 else if c = startc then 41 recurse (count + 1, depth + 1, ss') 42 else recurse (count + 1, depth, ss') 43 val (varlength, rest) = recurse(0, 0, ss) 44in 45 k (VREF (string (slice(ss, 0, SOME varlength))) :: acc) rest 46end 47 48fun quotable c = 49 case c of 50 #" " => true 51 | #"\\" => true 52 | #":" => true 53 | #"#" => true 54 | _ => false 55 56fun extract_quotation0 cmd acc ss = let 57 open Substring 58 val (normal, rest) = splitl (not o is_special) ss 59 val acc = if size normal > 0 then LIT (string normal) :: acc 60 else acc 61 val extract = extract_quotation0 cmd 62in 63 case Substring.getc rest of 64 NONE => List.rev acc 65 | SOME(c, rest) => let 66 fun chew_newline acc rest = let 67 val rest = dropl Char.isSpace rest 68 in 69 if size rest = 0 then List.rev acc 70 else extract (LIT " " :: acc) rest 71 end 72 in 73 case c of 74 #"#" => if not cmd then 75 (* rest of line is comment; drop it *) List.rev acc 76 else extract (LIT "#" :: acc) rest 77 | #"\\" => 78 if cmd then 79 if size rest > 0 andalso sub(rest,0) = #"\n" andalso 80 not Systeml.isUnix 81 then 82 chew_newline acc rest 83 else extract (LIT "\\" :: acc) rest 84 else let 85 (* need to look at what comes next *) 86 in 87 case Substring.getc rest of 88 NONE => List.rev (LIT "\\" :: acc) 89 | SOME (c, rest') => let 90 in 91 case c of 92 #"\n" => chew_newline acc rest 93 | #"#" => extract (LIT "#" :: acc) rest' 94 | _ => extract (LIT ("\\" ^ str c) :: acc) rest' 95 end 96 end 97 | #"$" => (* check for well-formed variable *) let 98 in 99 case Substring.getc rest of 100 NONE => (* gnu make silently drops it ; weird *) List.rev acc 101 | SOME (c, rest) => let 102 in 103 case c of 104 #"$" => (* becomes a dollar-sign *) 105 extract (LIT "$" :: acc) rest 106 | #"(" => check_for_vref (c, #")") acc rest extract 107 | #"{" => check_for_vref (c, #"}") acc rest extract 108 | _ => 109 if Char.isAlphaNum c orelse c = #"_" orelse 110 ok_symbolvars c 111 then 112 extract (VREF (str c) :: acc) rest 113 else 114 raise Fail ("Bad variable name: "^str c) 115 end 116 end 117 | _ => raise Fail "Can't happen" 118 end 119end 120 121 122val extract_normal_quotation = normquote [] o extract_quotation0 false [] 123val extract_cmd_quotation = normquote [] o extract_quotation0 true [] 124 125 126fun mem e [] = false 127 | mem e (h::t) = e = h orelse mem e t 128 129fun strip_trailing_ws ss = let 130 (* can't just use dropr Char.isSpace, because the first space 131 might be protected with a backslash *) 132 open Substring 133 val (ok, spaces) = splitr Char.isSpace ss 134 val (ok0, bslashes) = splitr (fn c => c = #"\\") ok 135in 136 if size bslashes mod 2 = 0 then ok 137 else if size spaces > 0 then 138 slice(ss, 0, SOME (size ok + 1)) 139 else ok 140end 141 142fun convert_newlines ss0 = let 143 (* replace \r\n with \n to normalise against windows convention *) 144 open Substring 145 fun recurse acc ss0 = let 146 val (ss1, ss2) = position "\r\n" ss0 147 in 148 if size ss2 = 0 then concat (List.rev (ss1::acc)) 149 else recurse (ss1::acc) (Substring.slice(ss2, 1, NONE)) 150 end 151in 152 Substring.full (recurse [] ss0) 153end 154 155fun to_token pt = 156 case pt of 157 DEFN s => let 158 open Substring 159 val ss = convert_newlines (full s) 160 fun endp c = c <> #"=" andalso not (Char.isSpace c) 161 val (varname, rest) = splitl endp ss 162 val rest = dropl Char.isSpace rest 163 val rest = #2 (valOf (getc rest)) (* drops = sign *) 164 val rest = dropl Char.isSpace rest 165 in 166 HM_defn(string varname, extract_normal_quotation rest) 167 end 168 | RULE s => let 169 open Substring 170 val ss = convert_newlines (full s) 171 val idx = valOf (find_unescaped [#":"] ss) 172 val (tgts, rest) = splitAt(ss, idx) 173 val tgts = strip_trailing_ws tgts 174 175 val rest = #2 (valOf (getc rest)) (* drop the colon *) 176 val (deps, rest) = 177 splitAt(rest, valOf (find_unescaped [#"\n"] rest)) 178 handle Option => (* happens if the dependencies are terminated 179 by an eof character *) 180 splitAt(rest, size rest - 1) 181 val rest = #2 (valOf (getc rest)) (* drop the newline/eof *) 182 val deps = (* cut any comment on this line *) 183 case find_unescaped [#"#"] deps of 184 NONE => deps 185 | SOME i => #1 (splitAt(deps, i)) 186 val deps = dropl Char.isSpace (strip_trailing_ws deps) 187 188 fun docmds acc ss = 189 if size ss = 0 then List.rev acc 190 else 191 case find_unescaped [#"\n"] ss of 192 NONE => (* cut out initial TAB, and final EOF character *) 193 List.rev (extract_cmd_quotation 194 (slice(ss,1,SOME(size ss - 2))) :: acc) 195 | SOME i => let 196 val (cmd, rest) = splitAt(ss, i) 197 val rest = slice(rest, 1, NONE) (* drop newline *) 198 val cmd = slice(cmd, 1, NONE) (* drop TAB *) 199 in 200 docmds (extract_cmd_quotation cmd :: acc) rest 201 end 202 in 203 HM_rule {commands = docmds [] rest, 204 dependencies = extract_normal_quotation deps, 205 targets = extract_normal_quotation tgts} 206 end 207 | EOF => raise Fail "No EOF-equivalent" 208 209val commafy = String.concatWith ", " 210 211fun argtokenize ss = let 212 open Substring 213 val sz = size ss 214 fun recurse pdepth start i acc = 215 if i = sz then 216 if pdepth = 0 then List.rev (slice(ss,start,NONE) :: acc) 217 else raise Fail "argtokenize: too few right parens" 218 else let 219 val c = sub(ss,i) 220 in 221 if c = #"(" then recurse (pdepth + 1) start (i + 1) acc 222 else if c = #")" then 223 if pdepth = 0 then raise Fail "argtokenize: too many right parens" 224 else recurse (pdepth - 1) start (i + 1) acc 225 else if c = #"," then 226 if pdepth = 0 then recurse pdepth (i + 1) (i + 1) 227 (slice(ss,start,SOME (i-start)) :: acc) 228 else recurse pdepth start (i + 1) acc 229 else 230 recurse pdepth start (i + 1) acc 231 end 232in 233 recurse 0 0 0 [] 234end 235 236fun perform_substitution env q = let 237 open Substring 238 fun envfn s = 239 case Binarymap.peek(env, s) of 240 NONE => (case OS.Process.getEnv s of 241 NONE => [LIT ""] 242 | SOME s => [LIT s]) 243 | SOME q => q 244 fun finisher q = 245 case normquote [] q of 246 [LIT s] => s 247 | [] => "" 248 | _ => raise Fail "Can't happen" 249 fun recurse visited fraglist = 250 case fraglist of 251 [] => [] 252 | (LIT s :: rest) => LIT s :: recurse visited rest 253 | VREF s :: rest => let 254 val ss = full s 255 val (fnpart, spc_rest) = position " " ss 256 val eval = finisher o recurse visited o extract_normal_quotation 257 val result = 258 if size spc_rest > 0 then let 259 (* have a function call to evaluate *) 260 val fnname = eval fnpart 261 val args = argtokenize 262 (dropl Char.isSpace 263 (dropr Char.isSpace spc_rest)) 264 in 265 [LIT (function_call (fnname, args, eval))] 266 end 267 else let 268 val varname = eval ss 269 val _ = not (mem varname visited) orelse 270 raise Fail ("Variable loop through: "^ 271 commafy visited) 272 val s_expanded0 = envfn varname 273 in 274 recurse (s :: visited) s_expanded0 275 end 276 in 277 result @ recurse visited rest 278 end 279in 280 finisher (recurse [] q) 281end 282 283fun dequote s = let 284 open Substring 285 val ss = full s 286 fun recurse acc ss = let 287 val (normal, rest) = splitl (fn c => c <> #"\\") ss 288 val acc = string normal :: acc 289 in 290 case getc rest of 291 NONE => String.concat (List.rev acc) 292 | SOME (_, rest) => let 293 in 294 case getc rest of 295 NONE => String.concat (List.rev ("\\" :: acc)) 296 | SOME (c, rest) => 297 if quotable c then recurse (str c :: acc) rest 298 else recurse (str c :: "\\" :: acc) rest 299 end 300 end 301in 302 recurse [] ss 303end 304 305fun is_pseudo_target s = s = ".PHONY" 306 307val empty_ruledb = Binarymap.mkDict String.compare 308type depdb = (string,string list) Binarymap.dict 309 310fun app_insert (ddb, s, slist) = 311 case Binarymap.peek(ddb, s) of 312 NONE => Binarymap.insert(ddb, s, slist) 313 | SOME existing => Binarymap.insert(ddb, s, existing @ slist) 314 315fun extend_ruledb warn env {targets,dependencies,commands} (rdb,ddb) = let 316 val tgts = map dequote (tokenize (perform_substitution env targets)) 317 val deps = map dequote (tokenize (perform_substitution env dependencies)) 318in 319 if null commands then 320 (rdb, 321 List.foldl (fn (tgt, ddb) => app_insert(ddb, tgt, deps)) ddb tgts, tgts) 322 else let 323 val info = {dependencies = deps, commands = commands} 324 fun foldthis (t, dict) = 325 case Binarymap.peek(dict, t) of 326 NONE => Binarymap.insert(dict, t, info) 327 | SOME _ => let 328 in 329 warn ("Later rule for `"^t^ 330 "' takes precedence over earlier one."); 331 Binarymap.insert(dict, t, info) 332 end 333 in 334 (List.foldl foldthis rdb tgts, ddb, tgts) 335 end 336end 337 338fun ins (k,v) env = Binarymap.insert(env,k,v) 339infix |> 340fun x |> f = f x 341 342fun get_rule_info rdb env tgt = 343 case Binarymap.peek(rdb, tgt) of 344 NONE => NONE 345 | SOME {dependencies, commands} => let 346 val dep1 = [LIT (hd dependencies)] handle Empty => [LIT ""] 347 val env = env |> ins("<", dep1) |> ins("@", [LIT tgt]) 348 in 349 SOME {dependencies = dependencies, 350 commands = map (perform_substitution env) commands} 351 end 352 353 354val base_environment0 = let 355 open Systeml 356 infix ++ 357 fun p1 ++ p2 = OS.Path.concat(p1,p2) 358 val alist = 359 [("CC", [LIT CC]), 360 ("CP", if OS = "winNT" then [LIT "copy /b"] else [LIT "/bin/cp"]), 361 ("DEFAULT_TARGETS", 362 [VREF ("patsubst %.sml,%.uo,$(patsubst %Theory.sml,,"^ 363 "$(patsubst %Script.sml,%Theory.uo,$(wildcard *.sml)))")]), 364 ("HOLDIR", [LIT HOLDIR]), 365 ("MLLEX", [VREF "protect $(HOLDIR)/tools/mllex/mllex.exe"]), 366 ("MLYACC", [VREF "protect $(HOLDIR)/tools/mlyacc/src/mlyacc.exe"]), 367 ("ML_SYSNAME", [LIT ML_SYSNAME]), 368 ("MV", if OS = "winNT" then [LIT "move", LIT "/y"] else [LIT "/bin/mv"]), 369 ("OS", [LIT OS]), 370 ("SIGOBJ", [VREF "HOLDIR", LIT "/sigobj"]), 371 ("UNQUOTE", [VREF ("protect $(HOLDIR)/" ^ xable_string "/bin/unquote")])] @ 372 (if Systeml.ML_SYSNAME = "poly" then 373 [("POLY", [LIT (Systeml.protect Systeml.POLY)]), 374 ("POLYC", [LIT (Systeml.protect Systeml.POLYC)]), 375 ("POLY_VERSION", [LIT (Int.toString Systeml.POLY_VERSION)]), 376 ("POLYMLLIBDIR", [LIT (Systeml.protect Systeml.POLYMLLIBDIR)])] 377 else []) 378in 379 List.foldl (fn ((k,v), a) => Binarymap.insert(a, k, v)) 380 (Binarymap.mkDict String.compare) 381 alist 382end 383 384fun base_environment () = let 385 val kernelid = 386 let 387 val strm = TextIO.openIn Holmake_tools.kernelid_fname 388 val s = 389 case TextIO.inputLine strm of 390 NONE => "" 391 | SOME s => hd (String.tokens Char.isSpace s) handle Empty => "" 392 393 in 394 s before TextIO.closeIn strm 395 end handle IO.Io _ => "" 396in 397 Binarymap.insert(base_environment0, "KERNELID", [LIT kernelid]) 398end 399 400fun lookup e k = 401 case Binarymap.peek(e, k) of 402 NONE => (case OS.Process.getEnv k of 403 NONE => [LIT ""] 404 | SOME s => [LIT s]) 405 | SOME q => normquote [] q 406 407 408fun env_extend (k, v) e = Binarymap.insert(e,k,v) 409 410end (* struct *) 411