(* Sukyoung Ryu, Oukseh Lee, Youil Kim Copyright(c) 2000-2004 KAIST/SNU Research On Program Analysis System (National Creative Research Initiative Center 1998-2003) http://ropas.snu.ac.kr/n All rights reserved. This file is distributed under the terms of an Open Source License. *) structure NMakeGen = struct open Set structure SS = Make(struct type t = string val compare = Pervasives.compare end) open Format open Location open String_ast.Ast open Unix open Filename structure L = List exception Assert_failure of (string * int * int) val print = print_string (* Collect free module identifiers in the ast *) val free_names = ref SS.empty (* Collect bound module identifiers in the ast *) val bound_names = ref SS.empty (* (filename, free_names, bound_names) list *) val infos = ref ([]: (string * SS.t * string list) list) (* 2002-04-27, bluewiz *) val unix_lib_used = ref false (* addmodule: SS.t -> Longid.t -> unit *) fun addmodule bv lid = if lid="Unix" then unix_lib_used := true; if not (SS.mem lid bv) then free_names := SS.add lid !free_names (* add_bound: Longid.t -> unit *) fun add_bound lid = bound_names := SS.add lid !bound_names (* add_ty: SS.t -> ty -> unit *) fun add_ty bv ty = case ty of VarTy _ => () | ConstTy (tys, (strids, _, _), _) => (L.iter (fn (lid,_) => addmodule bv lid) strids; L.iter (add_ty bv) tys) | RecordTy (labtys, _) => L.iter (fn (_, ty', _) => add_ty bv ty') labtys | TupleTy (tys, _) => L.iter (add_ty bv) tys | FunTy (ty1, ty2, _) => (add_ty bv ty1; add_ty bv ty2) (* add_opt: (SS.t -> 'a -> unit) -> SS.t -> 'a option *) (* -> unit *) fun add_opt add_fn bv opt = case opt of None => () | Some x => add_fn bv x (* add_pat: SS.t -> pat -> unit *) fun add_pat bv pat = case pat of ConPat ((strids, _, _), _) => L.iter (fn (lid,_) => addmodule bv lid) strids | AppPat ((strids, _, _), pat', _) => (L.iter (fn (lid,_) => addmodule bv lid) strids; add_pat bv pat') | RecordPat (labpats, _) => L.iter (fn (_, pat', _) => add_pat bv pat') labpats | SubRecordPat (labpats, _) => L.iter (fn (_, pat', _) => add_pat bv pat') labpats | RefPat (pat', _) => add_pat bv pat' | ConstraintPat (pat', ty, _) => (add_pat bv pat'; add_ty bv ty) | AsPat (_, ty_opt, pat', _) => (add_opt add_ty bv ty_opt; add_pat bv pat') | TuplePat (pats, _) => L.iter (add_pat bv) pats | ListPat (pats, _) => L.iter (add_pat bv) pats | ArrayPat (pats, _) => L.iter (add_pat bv) pats | OrPat (pats, _) => L.iter (add_pat bv) pats | _ => () (* add_exp: SS.t -> exp -> unit *) fun add_exp bv exp = case exp of VarExp ((strids, _, _), _) => L.iter (fn (lid,_) => addmodule bv lid) strids | ConExp ((strids, _, _), _) => L.iter (fn (lid,_) => addmodule bv lid) strids | AppExp (exp1, exp2, _) => (add_exp bv exp1; List.iter (add_exp bv) exp2) | RecordExp (labexps, _) => L.iter (fn (_, exp', _) => add_exp bv exp') labexps | RecordFieldExp (exp', _, _) => add_exp bv exp' | ArrayFieldExp (exp1, exp2, _) => (add_exp bv exp1; add_exp bv exp2) | UpdateArrayExp (exp1, exp2, exp3, _) => (add_exp bv exp1; add_exp bv exp2; add_exp bv exp3) | SubstRecordExp (exp1, _, exp2, _) => (add_exp bv exp1; add_exp bv exp2) | TupleExp (exps, _) => L.iter (add_exp bv) exps | ListExp (exps, _) => L.iter (add_exp bv) exps | ArrayExp (exps, _) => L.iter (add_exp bv) exps | LetExp (dec, exp', _) => (add_dec bv dec; add_exp bv exp') | HandleExp (exp', rules, _) => (add_exp bv exp'; add_rules bv rules) | RaiseExp (exp', _) => add_exp bv exp' | FnExp (fnrules, _) => add_fnrules bv fnrules | AssignExp (exp1, exp2, _) => (add_exp bv exp1; add_exp bv exp2) | RefExp (exp', _) => add_exp bv exp' | DeRefExp (exp', _) => add_exp bv exp' | SeqExp (exps, _) => L.iter (add_exp bv) exps | CaseExp (exp', rules, _) => (add_exp bv exp'; add_rules bv rules) | IfExp (exp1, exp2, exp3, _) => (add_exp bv exp1; add_exp bv exp2; add_exp bv exp3) | WhileExp (exp1, exp2, _) => (add_exp bv exp1; add_exp bv exp2) | ForExp (_, exp1, exp2, exp3, exp4, _) => (add_exp bv exp1; add_exp bv exp2; add_exp bv exp3; add_exp bv exp4) | ConstraintExp (exp', ty, _) => (add_exp bv exp'; add_ty bv ty) | _ => () (* add_rules: SS.t -> rule list -> unit *) and add_rules bv rules = L.iter (fn (p,e,_) => (add_pat bv p; add_exp bv e)) rules (* add_fnrules: SS.t -> fnrule list -> unit *) and add_fnrules bv fnrules = L.iter (fn (ps,e,_) => (L.iter (add_pat bv) ps; add_exp bv e)) fnrules (* add_dec: SS.t -> dec -> unit *) and add_dec bv dec = case dec of ValDec (_, vbs, _) => L.iter (fn (_,pat,exp,_) => (add_pat bv pat; add_exp bv exp)) vbs | FunDec (_, fbs, _) => L.iter (fn (bodies, _) => L.iter (fn (_, pats, exp, _) => (L.iter (add_pat bv) pats; add_exp bv exp)) bodies) fbs | TypeDec (tbs, _) => L.iter (add_tb bv) tbs | ExceptionDec (ebs, _) => L.iter (fn con => add_con bv con) ebs | LocalDec (dec1, dec2, _) => (add_dec bv dec1; add_dec bv dec2) | OpenDec (strlids, _) => L.iter (fn (strids, (strid, _), _) => (L.iter (fn (lid,_) => addmodule bv lid) strids; addmodule bv strid)) strlids | SeqDec (decs, _) => L.iter (add_dec bv) decs (* add_tb: SS.t -> typebind -> unit *) and add_tb bv tb = case tb of TypeBind (_,_,ty,_) => add_ty bv ty | DataBind (_,_,cons,_) => L.iter (add_con bv) cons (* add_con: SS.t -> con -> unit *) and add_con bv (_, ty_opt, _) = add_opt add_ty bv ty_opt (* add_spec: SS.t -> spec -> unit *) and add_spec bv spec = case spec of ValSpec (valdescs, _) => L.iter (fn (_,ty,_) => add_ty bv ty) valdescs | TypeSpec (typedescs, _) => L.iter (add_typedesc bv) typedescs | ExnSpec (exndescs, _) => L.iter (fn con => add_con bv con) exndescs | IncludeSpec (sigexp, _) => add_sigexp bv sigexp | StrSpec (strdescs, _) => L.iter (fn ((strid,_),sigexp,_) => (addmodule bv strid; add_sigexp bv sigexp)) strdescs | SeqSpec (specs, _) => L.iter (add_spec bv) specs (* add_typedesc: SS.t -> typedesc -> unit *) and add_typedesc bv typedesc = case typedesc of TypeDesc _ => () | TypeBindDesc (_,_,ty,_) => add_ty bv ty | DataDesc (_,_,cons,_) => L.iter (add_con bv) cons (* add_sigexp: SS.t -> sigexp -> unit *) and add_sigexp bv sigexp = case sigexp of VarSig ((sigid, _), _) => addmodule bv sigid | SigSig (spec, _) => add_spec bv spec | ConstraintSig (sigexp', (longtbs, _), _) => (add_sigexp bv sigexp'; L.iter (fn (_,(strids,_,_),ty,_) => (L.iter (fn (lid,_) => addmodule bv lid) strids; add_ty bv ty)) longtbs) (* add_sigbd: SS.t -> sigbind -> SS.t *) and add_sigbd bv ((sigid,_), sigexp, _) = (add_bound sigid; add_sigexp bv sigexp; SS.add sigid bv) (* add_sigdec: SS.t -> sigdec -> SS.t *) and add_sigdec bv (sigbds,_) = L.fold_left add_sigbd bv sigbds (* add_strexp: SS.t -> strexp -> unit *) and add_strexp bv strexp = case strexp of VarStr ((strids, (strid, _), _), _) => (L.iter (fn (lid,_) => addmodule bv lid) strids; addmodule bv strid) | StrStr (strdec, _) => (add_strdec' bv strdec; ()) | SigStr (strexp', sigexp, _) => (add_strexp bv strexp'; add_sigexp bv sigexp) | FctAppStr ((fctid,_), strexps, _) => (addmodule bv fctid; L.iter (add_strexp bv) strexps) (* add_fctdec: SS.t -> fctdec -> SS.t *) and add_fctdec bv ((fctid,_), fctargs, sigexp_opt, strexp, _) = let val bv'' = L.fold_left (fn bv' ((strid,_),sigexp,_) => (add_sigexp bv sigexp; SS.add strid bv')) bv fctargs in (add_bound fctid; add_opt add_sigexp bv sigexp_opt; add_strexp bv'' strexp; SS.add fctid bv) end (* add_strdec': SS.t -> strdec -> SS.t *) and add_strdec' bv strdec = case strdec of SimpleDec (dec, _) => (add_dec bv dec; bv) | StrDec (strbds, _) => L.fold_left (fn bv' ((strid,_),sigexp_opt,strexp,_) => (add_opt add_sigexp bv' sigexp_opt; add_strexp bv' strexp; SS.add strid bv')) bv strbds | SeqStrDec (strdecs, _) => L.fold_left add_strdec' bv strdecs (* add_strdec: SS.t -> strdec -> SS.t *) and add_strdec bv strdec = case strdec of SimpleDec (dec, _) => (add_dec bv dec; bv) | StrDec (strbds, _) => L.fold_left (fn bv' ((strid,_),sigexp_opt,strexp,_) => (add_bound strid; add_opt add_sigexp bv' sigexp_opt; add_strexp bv' strexp; SS.add strid bv')) bv strbds | SeqStrDec (strdecs, _) => L.fold_left add_strdec bv strdecs (* add_topdec: SS.t -> topdec -> SS.t *) and add_topdec bv topdec = case topdec of Sig (sigdec, _) => add_sigdec bv sigdec | Fct (fctdec, _) => add_fctdec bv fctdec | Str (strdec, _) => add_strdec bv strdec | SeqTopDec (topdecs, _) => L.fold_left add_topdec bv topdecs (********************** Analyze This **********************) val paths = ref ["."] val recur = ref ([]: string list) (* 2002-04-19, bluewiz *) (* signature EnvSig = sig val clear: unit -> unit val put: string -> string -> unit val get: string -> string val update: string -> string -> unit val iter: (string -> string -> unit) -> unit end *) structure Env = struct val env = ref Map.empty fun clear () = env := Map.empty fun put var value = env := Map.add var value !env fun get var = Map.find var !env fun exists var = (ignore (get var); true) handle Not_found => false fun update var value = env := Map.add var value (Map.remove var !env) fun iter f = Map.iter f !env end fun init_env () = Env.clear (); Env.put "NMLC" "nmlc"; Env.put "NMLO" "nmlo"; Env.put "NLEX" "nlex"; Env.put "NYACC" "nyacc"; Env.put "NYACC" "nyacc"; Env.put "OCAMLLEX" "ocamllex"; Env.put "OCAMLYACC" "ocamlyacc"; Env.put "COMPFLAGS" ""; Env.put "DEPFLAGS" ""; Env.put "LINKFLAGS" ""; Env.put "LEXFLAGS" ""; Env.put "YACCFLAGS" ""; Env.put "NMAKEGEN" "" val _ = init_env () val nlex = ref (Env.get "NLEX") val nyacc = ref (Env.get "NYACC") val ocamllex = ref (Env.get "OCAMLLEX") val ocamlyacc = ref (Env.get "OCAMLYACC") val lexflags = ref (Env.get "LEXFLAGS") val yaccflags = ref (Env.get "YACCFLAGS") val nf = ref false val dp = ref false val interactive = ref true val object = ref (if Sys.os_type = "Win32" then "run.exe" else "run") val native = ref (if Sys.os_type = "Win32" then "fly.exe" else "fly") val ocamldep = ref "" val input = ref ([]: string list) val lexs = ref ([]: string list) val yacs = ref ([]: string list) val ocamllexs = ref ([]: string list) val ocamlyacs = ref ([]: string list) open Utils val obj = ref SM.empty val ocamlobj = ref SM.empty (* keep: (string * SS.t * string list) -> unit *) fun keepInfo info = infos := info::(!infos) fun ask s = ( print_string s; print_string " (y/[n])? "; Format.print_flush(); let val x = read_line() in x = "y" || x = "Y" end) fun warning x = ( print_string "Warning: "; print_string x; print_string "\n"; Format.print_flush()) fun fail x = warning ("nmakegen fails to deal with " ^ x) fun stop x = (print_string "Stopped."; print_newline(); exit 0) (* Process one file *) (* file_dependencies: string -> unit *) fun file_dependencies src_file = (Location.input_name := src_file; if Sys.file_exists src_file then if check_suffix src_file ".n" then (let val _ = (free_names := SS.empty;bound_names := SS.empty) val base = chop_suffix src_file ".n" val ic = open_in src_file in let val lb = Lexing.from_channel ic in (ignore(add_topdec SS.empty (Nparse.parse lb)); keepInfo (base,!free_names, SS.elements !bound_names); close_in ic) end handle x => (close_in ic; raise x) end handle x => let fun report_err err = case err of Lexer.Error(err, start, stop) => (fail src_file; fprintf Format.err_formatter "@[%a%a@]@." Location.print {loc_start = start, loc_end = stop, loc_ghost = false} Lexer.report_error err) | Syntaxerr.Error err => (fail src_file; fprintf Format.err_formatter "@[%a@]@." Syntaxerr.report_error err) | Sys_error msg => (fail src_file; fprintf Format.err_formatter "@[I/O error:@ %s@]@." msg) | x => fail src_file in report_err x end) else let val (deps, objs) = Ncamldep.file_dependencies (!ocamlobj) (!paths) src_file in (ocamldep := !ocamldep ^ deps; ocamlobj := objs) end handle x => (print "Warning: ocamldep fails to deal with "; print src_file; print_newline())) fun getFname file = if Sys.file_exists file && !interactive then if ask ("overwrite " ^ file) then file else stop() else file (* Make a .nfiles *) fun quest x = (if Sys.file_exists x then "overwrite " else "generate ")^x fun ask_gen x = if !interactive then ask (quest x) else true fun ask_gen2 x y = if !interactive then ask (quest x ^ " and " ^ quest y) else true fun nfiles pr dir = let val dc = Sys.opendir dir fun full name = dir ^ "/" ^ name in while true do let val file = Sys.readdir dc val is_suffix = check_suffix file val detach = chop_suffix file in if is_suffix ".n" || is_suffix ".ml" || is_suffix ".mli" then pr (full file) else if is_suffix ".nl" then let val name = detach ".nl" val new = full (name^".n") in if ask_gen new then case system ((!nlex)^" "^(!lexflags)^" "^full file) of WEXITED 0 => (lexs := (!lexs) @ [full name]; pr new) | _ => fail (full file) end else if is_suffix ".ny" then let val name = detach ".ny" val new = full (name^".n") in if ask_gen new then case system ((!nyacc)^" "^(!yaccflags)^" "^full file) of WEXITED 0 => (yacs := (!yacs) @ [full name]; pr new) | _ => fail (full file) end else if is_suffix ".mll" then let val name = detach ".mll" val new = full (name ^ ".ml") in if ask_gen new then case system ((!ocamllex)^" "^(!lexflags)^" "^full file) of WEXITED 0 => (ocamllexs := (!ocamllexs) @ [full name]; pr new) | _ => fail (full file) end else if is_suffix ".mly" then let val name = detach ".mly" val new = full (name^".ml") and newi = full (name^".mli") in if ask_gen2 new newi then case system ((!ocamlyacc)^" "^(!yaccflags)^" "^full file) of WEXITED 0 => (ocamlyacs := (!ocamlyacs) @ [full name]; pr new; pr newi) | _ => fail (full file) end end end handle End_of_file => () end fun mkNfiles () = let val oc = open_out (getFname ".nfiles") val dirs = ref (!paths) fun collect path dir = let val dc = Sys.opendir (path^dir) in if not (L.mem dir !dirs) then dirs := (!dirs)@[path^dir]; while true do let val file = Sys.readdir dc in if file <> "." andalso file <> ".." then collect (path^dir^"/") file end end handle End_of_file => Sys.closedir dc end handle Not_found => () in (((L.iter (collect "") (!recur); L.iter (nfiles (fn s => (output_string oc s; output_string oc "\n"))) (!dirs)) handle _ => (close_out oc; stop())); close_out oc) end (* command line arguments *) fun fn_I d = (input := !input@["-I",d]; paths := !paths@[d]) fun fn_r d = (input := !input@["-r",d]; recur := !recur@[d]) fun fn_f _ = (input := !input@["-f"]; interactive := false) fun fn_d _ = (input := !input@["-d"]; dp := true) fun fn_o n = (input := !input@["-o",n]; object := n) fun fn_x n = (input := !input@["-x",n]; native := n) val usage = "Usage: nmakegen [options] [variable=value]*" val doc_I = "