Module:   dfmc-definitions
Synopsis: The module definition processor.
Author:   Keith Playford
Copyright:    Original Code is Copyright (c) 1995-2004 Functional Objects, Inc.
              All rights reserved.
License:      Functional Objects Library Public License Version 1.0
Dual-license: GNU Lesser General Public License
Warranty:     Distributed WITHOUT WARRANTY OF ANY KIND

//// Module definitions.

define class <module-definition> (<namespace-defining-form>) end;

define method form-define-word 
    (form :: <module-definition>) => (word :: <symbol>)
  #"module"
end method;

// TODO: These definition objects are really stubs. The real story for
// definition-based namespace processing is yet to come.

define &definition module-definer
  { define ?mods:* module ?:name ?clauses:* end }
    => do-define-module(form, mods, name, clauses);
end &definition;


define sideways method make-module-definition (#key name,
					       source-location = #f,
					       parent-form = #f,
					       use-clauses,
					       create-clauses = #(),
					       export-clauses = #())
  make(<module-definition>,
       source-location: source-location,
       adjectives: #(),
       name: name,
       parent-form: parent-form,
       use-clauses: use-clauses,
       create-clauses: create-clauses,
       export-clauses: export-clauses);
end method;

define method do-define-module (form, mods, name, clauses)
  let (uses, creates, exports)
    = parse-namespace-clauses(name, clauses);
  let definition
    = define-parsed-module(name.fragment-identifier,
                           source-location: fragment-source-location(form),
                           use-clauses:     uses,
                           create-clauses:  creates,
                           export-clauses:  exports);
  list(definition)
end method;

define method generate-initializer-source-with-namespace
    (form :: <module-definition>, module :: <module>) 
 => (source)
  with-expansion-source-form (form)
    let model      = namespace-model(module);
    let var-name   = namespace-model-variable(module);
    let code       = #{ define constant ?var-name = ?model; ?var-name };
    code
  end;
end method;

// also used by boot code.
define sideways method define-parsed-module (name, #key source-location = #f,
					                use-clauses,
					                create-clauses,
					                export-clauses)
 => (module :: <module-definition>, module :: false-or(<module>))
  let definition = make(<module-definition>,
			source-location: source-location,
			adjectives:      #(),
			name:            name,
			use-clauses:     use-clauses,
			create-clauses:  create-clauses,
			export-clauses:  export-clauses);
  /*
  let module     = compiling-dylan-library?()
                     & install-top-level-form(definition);
  */
  let module 
    = if (compiling-dylan-library?() | single-file-project-hack?())
        pre-install-top-level-form(definition);
      else
        #f
      end;
  values(definition, module)
end method;

define method single-file-project-hack? () => (well? :: <boolean>)
  // If our source record name doesn't match our actual module, we're
  // pre-switch in a single file project hack.
  let cr = current-compilation-record();
  let sr = compilation-record-source-record(cr);
  let cr-module = compilation-record-module(cr);
  namespace-name(cr-module) ~== source-record-module-name(sr)
end method;

define serious-program-warning <library-not-yet-defined>
  slot condition-module-name,
    required-init-keyword: module-name:;
  format-string "No define library seen for the current library - "
                "skipping definition of the %s module.";
  format-arguments module-name;
end serious-program-warning;

define method pre-install-top-level-form (form :: <module-definition>)
  with-dependent ($top-level-processing of form)
    if (current-library-defined?())
      let module =
        define-and-install-module(definition:     form,
                                  name:           form-namespace-name(form),
				  use-clauses:    form-use-clauses(form),
				  create-clauses: form-create-clauses(form),
				  export-clauses: form-export-clauses(form));
      module
    end if;
  end with-dependent;
end method;

define method install-top-level-form (form :: <module-definition>)
  with-dependent ($top-level-processing of form)
    if (~current-library-defined?())
      note(<library-not-yet-defined>,
           source-location: form-source-location(form),
           module-name:     form-namespace-name(form));
      #f
    else
      let module 
        = lookup-module(form-namespace-name(form), default: #f)
            | pre-install-top-level-form(form);
      if (module)
	let ld = current-library-description();
        let module-cr = form-compilation-record(form);
	// Force reparsing of any cr's with undefined modules (except
        // ourselves, since we must be attempting the single-file 
        // project hack if we're doing anything at all).
	for (cr in ld.library-description-compilation-records)
	  if (cr ~== module-cr & ~cr.compilation-record-module)
	    ld.compiled-to-definitions? := #f;
	    cr.compilation-record-definitions-installed? := #f;
	  end;
	end;
	// For some reason no longer remembered by anybody, we don't create
	// the constant variable definitions for bootstrapped modules in
	// the dylan library...
	unless (booted-module?(module))
	  let initializer-source
	    = generate-initializer-source-with-namespace(form, module);
	  add-derived-top-level-fragment(form, initializer-source);
	end;
      end;
      form.form-top-level-installed? := #t;
      module
    end if;
  end with-dependent;
end method;

define method retract-using-modules (module :: <module>)
  let name = namespace-name(module);
  for (m in defined-modules-in(home-library(module)))
    if (namespace-uses?(m, name))
      // Note that 'm' can't be a <dylan-user-module> since that doesn't
      // use anything in its own library (i.e. the one in the Dylan library
      // doesn't use anything, and the ones in other libraries only use Dylan).
      // All other modules have namespace-definition's so it's safe to use it.
      retract-top-level-form(m.namespace-definition);
    end;
  end;
end method;

define function retract-derived-definitions (definition :: <module-definition>)
  let cr = form-compilation-record(definition);
  for (form in compilation-record-top-level-forms(cr))
    when (form.form-parent-form == definition)
      retract-top-level-form(form);
    end;
  end;
end function;

define method uninstall-top-level-form (form :: <module-definition>)
  retract-derived-definitions(form);
  let ld = form-library(form);
  let library = ld.language-definition;
  let name = form-namespace-name(form);
  let module = lookup-module-in(library, name, default: #f);
  if (module & module.namespace-definition == form)
    retract-using-modules(module);
    // Force rescan of cr's so find the delete module
    ld.compiled-to-definitions? := #f;
    for (cr in ld.library-description-compilation-records)
      if (cr.compilation-record-module == module)
	cr.compilation-record-definitions-installed? := #f;
      end;
    end;
    undefine-module!(module);
    namespace-model(module) := #f;
  end;
  form.form-top-level-installed? := #f;
end method;



// eof