diff --git a/compiler/macro-master.stanza b/compiler/macro-master.stanza index 8e36362a..fdaac77f 100644 --- a/compiler/macro-master.stanza +++ b/compiler/macro-master.stanza @@ -7,6 +7,7 @@ defpackage stz/macro-master : import stz/sexp-serializer import stz/sexp-checker import stz/params + import stz/verbose ;============================================================ ;========================= API ============================== @@ -33,18 +34,11 @@ public defmulti filename (p:MacroPlugin) -> String ; macro contains a superset of the expected packages. public defn load-macro-plugin (filename:String, expected-packages:Maybe>) -> MacroPlugin : + ;Report macro plugin. + vprintln("Load macro plugin %~." % [filename]) + ;Load the macro dynamic library. - val lib = - try : - dynamic-library-open(ensure-slash(filename)) - catch (e:Exception) : - throw(ErrorLoadingMacroPlugin(filename, e)) - - ;Test that it's a valid macro plugin - test-validity(lib) - - ;Call its main function for initialization. - call-main(lib) + val lib = load-macro-dylib(filename) ;Create the interface for communicating with it. val interface = PluginInterface(SExpIO()) @@ -58,10 +52,9 @@ public defn load-macro-plugin (filename:String, ;Check that the plugin supports the expected packages. if not empty?(expected-packages) : - if not subset?(value!(expected-packages), supported-packages) : - throw(UnexpectedPackagesInPlugin(filename, - supported-packages, - value!(expected-packages))) + val missing = difference(value!(expected-packages), supported-packages) + if not empty?(missing) : + throw(UnexpectedPackagesInPlugin(filename, missing)) ;Create the plugin new MacroPlugin : @@ -73,10 +66,47 @@ public defn load-macro-plugin (filename:String, defmethod filename (this) : filename -;Helper: Return true if xs is a subset of ys. -defn subset? (xs:Seqable, ys:Seqable) -> True|False : +;Helper: Return all items in 'xs' that does not exist in 'ys'. +defn difference (xs:Seqable, ys:Seqable) -> Tuple : val ys-set = to-hashset(ys) - for x in xs all? : ys-set[x] + to-tuple $ for x in xs filter : not ys-set[x] + +;============================================================ +;================== Already Loaded Set ====================== +;============================================================ + +;Each entry, PATH => LIB, means that the resolved path PATH +;has already been loaded and we retrieved and initialized +;library LIB. +val LOADED-LIBRARIES = HashTable() + +;Load and initialize the given dynamic library. +defn load-macro-dylib (filename:String) -> DynamicLibrary : + try : + ;Resolve the full path first. + val path = resolve-path!(ensure-slash(filename)) + + ;Test whether the library has already been loaded. + match(get?(LOADED-LIBRARIES, path)) : + ;Case: If the library has already been loaded, then + ;just return it directly. + (dylib:DynamicLibrary) : + dylib + ;Case: The library hasn't been loaded, so load and + ;initialize it. + (f:False) : + ;Load the library. + val dylib = dynamic-library-open(path) + ;Ensure it is a macro plugin by running a simple + ;handshake function. + test-validity(dylib) + ;Initialize the macro. + call-main(dylib) + ;And store it into the table. + LOADED-LIBRARIES[path] = dylib + dylib + catch (e:Exception) : + throw(ErrorLoadingMacroPlugin(filename, e)) ;============================================================ ;===================== Ensure Slash ========================= @@ -148,13 +178,11 @@ defmethod print (o:OutputStream, e:InvalidMacroPlugin) : ;packages. public defstruct UnexpectedPackagesInPlugin <: MacroPluginError : filename:String - supported-packages:Tuple - expected-packages:Tuple + missing:Tuple defmethod print (o:OutputStream, e:UnexpectedPackagesInPlugin) : - print(o, "Unexpected packages supported by macro plugin %~. The plugin \ - supports packages %_, but it was expected to support %_." % [ - filename(e), quotes(supported-packages(e)), quotes(expected-packages(e))]) + print(o, "The macro plugin %~ is missing syntax packages. It is stated to contain the \ + %_ syntax packages but does not." % [filename(e), quotes(missing(e))]) ;Helper: Add quotes and commas around names. defn quotes (ss:Seqable) -> ? : diff --git a/compiler/macroexpander.stanza b/compiler/macroexpander.stanza index d6929b91..4c3f1e77 100644 --- a/compiler/macroexpander.stanza +++ b/compiler/macroexpander.stanza @@ -7,6 +7,7 @@ defpackage stz/macroexpander : import stz/core-macros import stz/proj-manager import stz/proj-ir + import stz/verbose ;============================================================ ;====================== API ================================= @@ -116,9 +117,10 @@ public defn StanzaMacroexpander (sys:MacroSystem, ;Helper: Find a macro plugin using the proj file. defn find-using-proj () -> MacroPlugin|False : match(proj-manager:ProjManager) : - val filename = find-syntax-packages(proj-manager, packages) - match(filename:String) : - plugin(load-macro-from-file(LoadedFromProj, filename, One(packages))) + val plugin-details = find-syntax-packages(proj-manager, packages) + if not empty?(plugin-details) : + val [filename, stated-packages] = value!(plugin-details) + plugin(load-macro-from-file(LoadedFromProj, filename, One(stated-packages))) ;Helper: Return true if the root expander ;supports the given packages. diff --git a/compiler/params.stanza b/compiler/params.stanza index 1a89bb60..9a250fea 100644 --- a/compiler/params.stanza +++ b/compiler/params.stanza @@ -18,7 +18,7 @@ public defn compiler-flags () : to-tuple(COMPILE-FLAGS) ;========= Stanza Configuration ======== -public val STANZA-VERSION = [0 18 87] +public val STANZA-VERSION = [0 18 88] public var STANZA-INSTALL-DIR:String = "" public var OUTPUT-PLATFORM:Symbol = `platform public var STANZA-PKG-DIRS:List = List() diff --git a/compiler/proj-manager.stanza b/compiler/proj-manager.stanza index 619d7faf..8207dc83 100644 --- a/compiler/proj-manager.stanza +++ b/compiler/proj-manager.stanza @@ -67,8 +67,11 @@ public defmulti find-package (l:ProjManager, name:Symbol) -> PkgLocation|False public defmulti conditional-imports (l:ProjManager, packages:Seqable) -> Tuple ;Given the set of syntax packages we need, find the name of the plugin -;that supports all of them. Returns false if none could be found. -public defmulti find-syntax-packages (l:ProjManager, names:Tuple) -> String|False +;that supports all of them. If a plugin is found, then we return +;[filename, stated-packages] where: +;- filename: The filename of the macro plugin that supports the requested packages. +;- stated-packages: The syntax packages that the macro plugin is stated to support. +public defmulti find-syntax-packages (l:ProjManager, names:Tuple) -> Maybe<[String,Tuple]> ;Returns the list of all syntax-package groups listed in the ;.proj files. @@ -201,7 +204,7 @@ public defn ProjManager (proj:ProjFile, params:ProjParams, auxfile:AuxFile) : PkgLocation(name, src-path, pkg-file, read-pkg?) ;Find a set of syntax packages - defn find-syntax-packages (names:Tuple) -> String|False : + defn find-syntax-packages (names:Tuple) -> Maybe<[String,Tuple]> : ;Returns true if names is a superset of xs. defn superset-of-names? (xs:Seqable) -> True|False : val xs-set = to-hashset(xs) @@ -214,9 +217,9 @@ public defn ProjManager (proj:ProjFile, params:ProjParams, auxfile:AuxFile) : ;Note: We implicitly add `core and `tests to the supported packages. ;These are implicitly supported by all plugins even if not ;listed by the user. - value? $ for stmt in syntax-packages first : + for stmt in syntax-packages first : val added-packages = cat(`(core tests), packages(stmt)) - if superset-of-names?(added-packages) : One(filename(stmt)) + if superset-of-names?(added-packages) : One([filename(stmt), packages(stmt)]) else : None() ;Retrieve conditional imports