(* HCoop Domtool (http://hcoop.sourceforge.net/) * Copyright (c) 2006, Adam Chlipala * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *) (* Topological sorting of source files to take dependencies into account *) structure Order :> ORDER = struct open Ast structure SS = StringSet structure SM = StringMap fun predNeeded G (p, _) = case p of CRoot => SS.empty | CConst s => if Env.lookupContext G s then SS.empty else SS.singleton s | CPrefix p => predNeeded G p | CNot p => predNeeded G p | CAnd (p1, p2) => SS.union (predNeeded G p1, predNeeded G p2) fun unionCT ((c1, t1), (c2, t2)) = (SS.union (c1, c2), SS.union (t1, t2)) fun typNeeded G (t, _) = case t of TBase s => if Env.lookupType G s then (SS.empty, SS.empty) else (SS.empty, SS.singleton s) | TList t => typNeeded G t | TArrow (t1, t2) => unionCT (typNeeded G t1, typNeeded G t2) | TAction (p, d, r) => let val recordNeeded = SM.foldl (fn (t, ss) => unionCT (ss, typNeeded G t)) in recordNeeded (recordNeeded (predNeeded G p, SS.empty) d) r end | TNested (p, t) => unionCT ((predNeeded G p, SS.empty), typNeeded G t) | TError => raise Fail "TError during dependency analysis" | TUnif _ => raise Fail "TUnif during dependency analysis" val empty = ((SS.empty, SS.empty), SS.empty) fun unionCTE (((c1, t1), v1), ((c2, t2), v2)) = ((SS.union (c1, c2), SS.union (t1, t2)), SS.union (v1, v2)) val dt = (TError, ErrorMsg.dummyLoc) fun expNeeded G (e, _) = case e of EInt _ => ((SS.empty, if Env.lookupType G "int" then SS.empty else SS.singleton "int"), SS.empty) | EString _ => ((SS.empty, if Env.lookupType G "string" then SS.empty else SS.singleton "string"), SS.empty) | EList es => foldl (fn (e, ss) => unionCTE (ss, expNeeded G e)) empty es | ELam (x, to, e) => let val G' = Env.bindVal G (x, dt, NONE) in case to of NONE => expNeeded G' e | SOME t => unionCTE ((typNeeded G t, SS.empty), expNeeded G' e) end | EVar x => (case Env.lookupVal G x of NONE => ((SS.empty, SS.empty), SS.singleton x) | _ => empty) | EApp (e1, e2) => unionCTE (expNeeded G e1, expNeeded G e2) | ESkip => empty | ESet (_, e) => expNeeded G e | EGet (x, _, e) => expNeeded (Env.bindVal G (x, dt, NONE)) e | ESeq es => foldl (fn (e, ss) => unionCTE (ss, expNeeded G e)) empty es | ELocal (e1, e2) => unionCTE (expNeeded G e1, expNeeded G e2) | EWith (e1, e2) => unionCTE (expNeeded G e1, expNeeded G e2) fun declNeeded G (d, _, _) = case d of DExternType name => (Env.bindType G name, empty) | DExternVal (name, t) => (Env.bindVal G (name, dt, NONE), (typNeeded G t, SS.empty)) | DVal (name, to, e) => (Env.bindVal G (name, dt, NONE), case to of NONE => expNeeded G e | SOME t => unionCTE ((typNeeded G t, SS.empty), expNeeded G e)) | DContext name => (Env.bindContext G name, empty) fun fileSig (_, ds, eo) = let val (G', needed) = foldl (fn (d, (G, needed)) => let val (G', needed') = declNeeded G d in (G', unionCTE (needed, needed')) end) (Env.empty, empty) ds val needed = case eo of NONE => needed | SOME e => unionCTE (needed, expNeeded G' e) in (((Env.contexts G', Env.types G'), Env.vals G'), needed) end fun printSig ((cs, ts), vs) = (print "Contexts:"; SS.app (fn s => (print " "; print s; print ";")) cs; print "\n Types:"; SS.app (fn s => (print " "; print s; print ";")) ts; print "\n Values:"; SS.app (fn s => (print " "; print s; print ";")) vs; print "\n") fun mergeProvide kind fname (m1, m2) = SS.foldl (fn (name, provide) => (case SM.find (provide, name) of NONE => () | SOME fname' => ErrorMsg.error NONE (String.concat ["Files ", fname', " and ", fname, " both provide ", kind, " ", name]); SM.insert (provide, name, fname))) m1 m2 fun order fnames = let fun doFile (fname, (provideC, provideT, provideV, require)) = let val file = Parse.parse fname val (((provideC', provideT'), provideV'), require') = fileSig file in (mergeProvide "context" fname (provideC, provideC'), mergeProvide "type" fname (provideT, provideT'), mergeProvide "value" fname (provideV, provideV'), SM.insert (require, fname, require')) end val (provideC, provideT, provideV, require) = foldl doFile (SM.empty, SM.empty, SM.empty, SM.empty) fnames val require = SM.mapi (fn (fname, ((rc, rt), rv)) => let fun consider (kind, provide) = SS.foldl (fn (name, need) => case SM.find (provide, name) of NONE => (ErrorMsg.error NONE ("File " ^ fname ^ " uses undefined " ^ kind ^ " " ^ name); need) | SOME fname' => SS.add (need, fname')) val need = consider ("context", provideC) SS.empty rc val need = consider ("type", provideT) need rt val need = consider ("value", provideV) need rv in need end) require fun loop (ready, waiting, order) = case SS.find (fn _ => true) ready of NONE => if SM.numItems waiting = 0 then rev order else (ErrorMsg.error NONE "Cyclic dependency in source files"; order) | SOME next => let val (ready', waiting') = SM.foldli (fn (fname, requires, (ready', waiting')) => let val requires' = SS.delete (requires, next) handle NotFound => requires in if SS.numItems requires' = 0 then (SS.add (ready', fname), waiting') else (ready', SM.insert (waiting', fname, requires')) end) (SS.delete (ready, next), SM.empty) waiting in loop (ready', waiting', next :: order) end val (ready, waiting) = SM.foldli (fn (fname, requires, (ready, waiting)) => if SS.numItems requires = 0 then (SS.add (ready, fname), waiting) else (ready, SM.insert (waiting, fname, requires))) (SS.empty, SM.empty) require in (*SM.appi (fn (name, fname) => print ("Context " ^ name ^ " in " ^ fname ^ "\n")) provideC; SM.appi (fn (name, fname) => print ("Type " ^ name ^ " in " ^ fname ^ "\n")) provideT; SM.appi (fn (name, fname) => print ("Value " ^ name ^ " in " ^ fname ^ "\n")) provideV;*) (*SM.appi (fn (fname, requires) => (print fname; print " requires:"; SS.app (fn fname' => (print " "; print fname')) requires; print "\n")) require;*) ({provideC = provideC, provideT = provideT, provideV = provideV}, loop (ready, waiting, [])) end type providers = {provideC : string SM.map, provideT : string SM.map, provideV : string SM.map} fun providesContext (p : providers, s) = SM.find (#provideC p, s) fun providesType (p : providers, s) = SM.find (#provideT p, s) fun providesValue (p : providers, s) = SM.find (#provideV p, s) end