Dependency ordering
[hcoop/domtool2.git] / src / order.sml
diff --git a/src/order.sml b/src/order.sml
new file mode 100644 (file)
index 0000000..47ee1bc
--- /dev/null
@@ -0,0 +1,277 @@
+(* 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 =
+           let
+               val file = Parse.parse fname
+               val (provide, require) = fileSig file
+           in
+               print "\nFile ";
+               print fname;
+               print "\nPROVIDE:\n";
+               printSig provide;
+               print "\nREQUIRE:\n";
+               printSig require
+           end
+
+       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;*)
+
+       loop (ready, waiting, [])
+    end
+
+end