/**
 * This module defines generic strategies for packing a
 * set of modules reachable from a root module and for
 * flattening a set of modules into a single program.
 * 
 * Module systems allow the definition of a program to be
 * split into separate units stored in separate files. For
 * languages that do not support separate compilation (such
 * as grammar formalisms) these separate units need to be
 * combined to create the whole program.
 * 
 * Aspects of module packing and flattening
 * 
 * - finding the module associated with the module name
 * - doing something with the module, i.e., adding it to the result
 * - finding the imports in a module
 * - keeping track of which modules have already been inlined
 */
module strategy/pack/common
imports 
  term/string 
  strategy/pack/graph

rules

  PackInit : root -> (root, (), [])

strategies

 /**
  * Packing a module consists of collecting all modules into
  * a single file.
  */
  pack(parser, imp) = 
    PackInit; 
    graph-nodes(Fst; parser, get-imports(imp), \ (n,x,xs) -> [x|xs] \ )

  get-imports(imp) =
    collect(imp); concat

rules

  WriteMod(getname, write, ext) : 
    mod -> <write>(<add-extension>(<getname>mod, <ext>()), mod)

strategies

 /**
  * Unpacking is the reverse of packing, i.e., writing each module
  * in a list of modules to a separate file.
  */
  unpack(wrapper: (term -> term) * term -> term, getname, ext) = 
    wrapper(WriteMod(getname, WriteToTextFile, ext))

strategies

 /**
  * <flatten> (root, mods)| produces a flattened version of the root module.
  */
  flatten(imp, nameeq, getcontent) = 
    \ (root, mods) -> (root, mods, []) \;
    graph-nodes(lookup(nameeq),
		get-imports(imp), 
		\ (_,x,y) -> <conc>(<getcontent>x, y)\ )