open Genlex
let map_chan_lines f ch =
let rec map_chan_lines_acc f ch result =
try
let line = input_line ch in
map_chan_lines_acc f ch (result ^ (f line) ^ "\n")
with
End_of_file -> result
in
map_chan_lines_acc f ch "";;
let tapp a l = l @ (a :: []);;
let lex_dep_spec = make_lexer ["("; ")"; ","; ":"; ";"];;
type package = { name: string; installed: bool; deps: string list };;
let rec parse_line = parser
[< 'Kwd "("; installed = parse_installed; 'Kwd ")"; 'Kwd "(";
packs = parse_new installed; 'Kwd ")" >] -> packs
and parse_installed = parser
[< 'Ident i; instlist = parse_installed_rest ({ name=i; installed=true;
deps=[] } :: []) >] -> instlist
| [< >] -> []
and parse_installed_rest acc = parser
[< 'Kwd ","; 'Ident i; rest = parse_installed_rest (tapp { name=i;
installed=true; deps=[] } acc) >] -> rest
| [< >] -> acc
and parse_new instlist = parser
[< 'Ident i; deplist = parse_deps; newlist = parse_new_rest
(tapp { name=i; installed=false; deps=deplist } instlist) >] -> newlist
and parse_new_rest acc = parser
[< 'Kwd ","; 'Ident i; deplist = parse_deps;
rest = parse_new_rest (tapp { name=i; installed=false; deps=deplist }
acc) >] -> rest
| [< >] -> acc
and parse_deps = parser
[< 'Kwd ":"; 'Ident i; ds = parse_deps_rest (i :: []) >] -> ds
| [< >] -> []
and parse_deps_rest acc = parser
[< 'Kwd ";"; 'Ident i; ds = parse_deps_rest (tapp i acc) >] -> ds
| [< >] -> acc;;
let parse_dep_spec input =
parse_line(lex_dep_spec(Stream.of_string (String.uppercase input)));;
let is_known_pack spec p =
List.mem p (List.map (fun pa -> pa.name) spec);;
let deps_ok spec =
let depended = List.flatten (List.map (fun p -> p.deps) spec) in
List.for_all (is_known_pack spec) depended;;
let pack_with_name name spec =
List.find (fun p -> p.name = name) spec;;
let has_broken_deps p spec =
not (List.for_all (fun p -> is_known_pack spec p) p.deps);;
let broken_deps p spec =
List.filter (fun d -> not (is_known_pack spec d) ||
has_broken_deps (pack_with_name d spec) spec) p.deps;;
let dep_failure_message spec =
let newpacks = List.filter (fun pos -> not pos.installed) spec in
let brokenpacks = List.filter (fun p -> has_broken_deps p spec) newpacks in
let brokenpack_to_string p =
p.name ^ ":" ^ (String.concat ";" (broken_deps p spec)) in
"Dependency check failed: " ^
String.concat ", " (List.map brokenpack_to_string (List.sort (fun a b ->
compare a.name b.name) brokenpacks));;
let rec is_dep_of a b spec =
List.mem a.name b.deps ||
List.exists (fun x -> is_dep_of a (pack_with_name x spec) spec) b.deps;;
let install_order spec =
let depcmp a b =
if a = b then 0 else
if is_dep_of a b spec then -1 else
if is_dep_of b a spec then 1 else
compare a.name b.name
in
let newpacks = List.filter (fun pos -> not pos.installed) spec in
let newpacks_sorted = List.sort depcmp newpacks in
let newnames = List.map (fun p -> p.name) newpacks_sorted in
"Install order: " ^ (String.concat "," newnames);;
let resolve_deps specstr =
let spec = parse_dep_spec specstr in
if deps_ok spec then install_order spec else dep_failure_message spec;;
let process datafile =
map_chan_lines resolve_deps datafile;;
let main _ =
let infile = open_in "Prog2a.dat" in
let outfile = open_out "Prog2a.out" in
output_string outfile (process infile);;
main ();;
exit 0;;