Module: internal 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 // define open generic %define-domain (gf :: , domain :: ) => (); define open generic %remove-domain (gf :: , domain-spec, in-library :: ) => (didit? :: ); define sealed generic domain-type (d, i :: ) => (t :: ); define sealed generic domain-type-setter (t :: , d, i :: ); define sealed generic domain-number-required (d) => (n :: ); // BOOTED: // define abstract primary class () ... // slot domain-library :: ... // slot domain-next :: false-or() ... // ... // // define primary class () ... // slot domain-method ... // ... // // define primary class () // ... // repeated slot domain-type ... // size-getter: domain-number-required // ... define inline method domain-type (d :: , i :: ) => (t :: ) %method-specializer(domain-method(d), i) end method; define inline method domain-number-required (d :: ) => (n :: ) %method-number-required(domain-method(d)) end method; define inline method domain-type (d :: , i :: ) => (t :: ) element(d, i) end method; define inline method domain-number-required (d :: ) => (n :: ) size(d) end method; define inline method domain-type (d :: , i :: ) => (t :: ) %method-specializer(d, i) end method; define inline method domain-number-required (d :: ) => (n :: ) %method-number-required(d) end method; //define method domain-type (d :: , i :: ) => (t :: ) // let m :: = %load-byte(pdisp$v-typemask, pdisp$s-typemask, properties(d)); // // I keep being impressed that Dylan doesn't have this. // // n.b. This definition doesn't work on negative numbers and should be done by table lookup. // local method logcount (m :: ) => (c :: ) // (iterate loop (m :: = m, c :: = 0) => (c :: ) // if (m == 0) c else loop(ash(m, -1), if(logbit?(0, m)) c + 1 else c end) end // end iterate); // end method; // if (logbit?(i, m)) // partial-dispatch-type(d, logcount(logand(m, ash(1, i) - 1))) // else // // end if //end method; define method domain-type (d :: , i :: ) => (t :: ) let m :: = %load-byte(pdisp$v-typemask, pdisp$s-typemask, properties(d)); // I keep being impressed that Dylan doesn't have this. // n.b. This definition doesn't work on negative numbers and should be done by table lookup. local method logcount (m :: ) => (c :: ) local method loop (m :: , c :: ) => (c :: ) if (m == 0) c else loop(ash(m, -1), if (logbit?(0, m)) c + 1 else c end) end end method; loop(m, 0) end method; if (logbit?(i, m)) partial-dispatch-type(d, logcount(logand(m, ash(1, i) - 1))) else end if end method; define method domain-number-required (d :: ) => (n :: ) signature-number-required(function-signature(parent-gf(d))) end method; define sealed generic domain-types (d) => (types :: ); // @@@@ Does this need to be faster? Copy down over types supported by domain-type. define method domain-types (d) => (v :: ) let n :: = domain-number-required(d); let v :: = make(, size: n); for (i :: from 0 below n) vector-element(v, i) := domain-type(d, i) end; v end method; define inline method domain-types (d :: ) => (v :: ) function-specializers(d) end method; define inline-only function library-in? (lib :: , vec :: ) => (ans :: ) // member?(lib, vec) let n :: = size(vec); without-bounds-checks iterate loop (i :: = 0) if (i == n) #f elseif (vec[i] == lib) #t else loop(i + 1) end end; end without-bounds-checks; end function; define function compute-all-used-libraries (usedvec :: , ans :: ) => (ans :: ) let n :: = size(usedvec); local method loop (i :: , ans :: ) => (ans :: ) if (i == n) ans else let used :: = vector-element(usedvec, i); let lib :: = used-library(used); if (member?(lib, ans)) loop(i + 1, ans) else loop(i + 1, compute-all-used-libraries(used-libraries(lib), pair(lib, ans))) end if end if end method; loop(0, ans) end function; define function library-visible-from? (is-this-visible :: , from-this :: ) => (well? :: ) is-this-visible == from-this | begin let all-visible :: = all-used-libraries(from-this); let all-visible :: = if (size(all-visible) ~== 0) all-visible else let used-libs :: = used-libraries(from-this); if (size(used-libs) == 0) #[] else let v :: = as(, compute-all-used-libraries(used-libs, list(from-this))); all-used-libraries(from-this) := v end if end if; library-in?(is-this-visible, all-visible) end end function; define constant $runtime-library :: = make(, name: "the runtime system"); define constant $runtime-module :: = make(, name: "phony module", home: $runtime-library); kludge-up-init-value(, class-module, $runtime-module); //define function define-domain-on-sealed-generic (g :: , d :: ) // => (c :: ) // let t = domain-types(d); // let args = vector(g, t); // make(, // generic-function: g, operation: %define-domain, arguments: args, // format-string: "Defining sealed domain %= %= on sealed function from external library.", // format-arguments: args) //end function; //define method %define-domain (gf :: , d :: ) => () // bletch(define-domain-on-sealed-generic(gf, d)) //end method; define function %define-domain (gf :: , lib :: , #rest types) => () let ntypes :: = size(types); if (~every?(method(x) instance?(x, ) end, types)) error(make(, format-string: "One or more types in a domain being defined on %= from library %=,\n" "are not types: %=", format-arguments: vector(gf, lib, as(, types)))) elseif (ntypes ~== function-number-required(gf)) error(make(, format-string: "Incorrect number of types in domain definition on %= from %=;\n" "expected %=, got %=: %=", format-arguments: vector(gf, lib, function-number-required(gf), ntypes, as(, types)))) else let d :: = make(, library: lib, size: ntypes); for (i from 0 below ntypes) domain-type(d, i) := types[i] end; %add-domains(gf, vector(d)); end if end function; define function %add-method-domain (gf :: , m :: , lib :: , checked?) => (lossage :: ) let d :: = make(, next: #f, method: m, library: lib); if (checked?) (method (#rest v) %add-domains-internal(gf, v) end)(d) else domain-next(d) := incremental-gf-domain-info(gf); incremental-gf-domain-info(gf) := d; #() end if end function; define method %add-domains (gf :: , domains :: ) => () let lossage :: = (with-object-lock (gf) %add-domains-internal(gf, domains) end with-object-lock); bletch-stack(lossage); end method; define function %add-domains-internal (gf :: , domains :: ) => (lossage :: ) let lossage :: = #(); for (d :: in domains) let domain-lib :: = domain-library(d); if (incremental-gf-sealed?(gf) & ~library-visible-from?(domain-lib, incremental-gf-library(gf))) let args = vector(gf, d); lossage := add!(lossage, make(, generic-function: gf, operation: %define-domain, arguments: args, format-string: "Defining sealed domain %= %= on sealed generic from external library.", format-arguments: args)) elseif (type-complete?(gf) & type-complete?(d)) let gflib :: = incremental-gf-library(gf); let libs :: = incremental-gf-method-libraries(gf); let nlibs :: = size(libs); let meths :: = #(); for (m :: in generic-function-methods(gf), i :: from 0) let lib :: = if (i < nlibs) vector-element(libs, i) else gflib end; if (~library-visible-from?(lib, domain-lib) & ~domain-disjoint?(d, m, $empty-subjunctive-class-universe, #f)) // If there is a method which falls under the domain, and is defined by // a library not visible from the domain's library, we should barf. meths := add!(meths, m) end if end for; if (~empty?(meths)) // This is an error, not a warning, because the method is already there -- // it's too late to barf about adding the method. lossage := add!(lossage, make(, generic-function: gf, domain: d, operation: %define-domain, format-string: "Defining sealed domain %= %= conflicts " "with methods %= defined in other libraries.", format-arguments: vector(gf, domain-types(d), meths))) else %add-domain-unconditionally(gf, d) end if else note-generic-function-incomplete-domain(gf, d) end if end for; lossage end function; define method %add-domains (gf :: , domains) => () end method; define method %add-domain (gf :: , domain :: ) => () (method(#rest v) %add-domains(gf, v) end)(domain) end method; define function %add-nonsiblinged-domain (gf :: , d :: ) => () (with-object-lock (gf) domain-next(d) := incremental-gf-domain-info(gf); incremental-gf-domain-info(gf) := d; end with-object-lock) end function; define function %add-domain-unconditionally (g :: , d :: ) => () let (old :: false-or(), predecessor :: false-or()) = lookup-domain(d, g); if (old) let old :: = old; domain-next(d) := domain-next(old) end if; if (predecessor) let predecessor :: = predecessor; domain-next(predecessor) := d else incremental-gf-domain-info(g) := d end if; end function; define method %remove-domain (gf :: , domain-spec, library :: ) => (didit? :: ) (with-object-lock(gf) (begin let (old-d :: false-or(), predecessor :: false-or()) = lookup-domain(domain-spec, gf); if (old-d) let old-d :: = old-d; let nxt = domain-next(old-d); if (predecessor) let predecessor :: = predecessor; domain-next(predecessor) := nxt else incremental-gf-domain-info(gf) := nxt end if; #t else #f end if end) end with-object-lock) end method; // Subpart for remove-method only. gf is locked. define function %remove-method-domain (gf :: , m :: , in-lib :: ) => () local method lookup-domain-1 (link :: false-or(), predecessor :: false-or()) => (domain :: false-or(), predecessor :: false-or()) if (link) let link :: = link; if (in-lib == domain-library(link) & instance?(link, ) & domain-method(link) == m) let nxt = domain-next(link); if (predecessor) let predecessor :: = predecessor; domain-next(predecessor) := nxt else incremental-gf-domain-info(gf) := nxt end if else lookup-domain-1(domain-next(link), link) end if end if end method; lookup-domain-1(incremental-gf-domain-info(gf), #f) end function; define sealed generic domain-match? (d1, d2) => (match? :: ); define method domain-match? (d1, d2) => (match? :: ) local method loop (i :: ) let i :: = i - 1; if (i < 0) #t elseif (same-specializer?(domain-type(d1, i), domain-type(d2, i))) loop(i) else #f end if end method; loop(domain-number-required(d1)) end method; define copy-down-method domain-match? (d1 :: , d2 :: ) => (match? :: ); define copy-down-method domain-match? (d1 :: , d2 :: ) => (match? :: ); define method domain-match? (d1 :: , d2 :: ) => (match? :: ); domain-match?(d2, d1) end method; define copy-down-method domain-match? (d1 :: , d2 :: ) => (match? :: ); define copy-down-method domain-match? (d1 :: , d2 :: ) => (match? :: ); define copy-down-method domain-match? (d1 :: , d2 :: ) => (match? :: ); // engine-node/engine-node domain equivalence: define copy-down-method domain-match? (d1 :: , d2 :: ) => (match? :: ); define function lookup-domain (d :: , g :: ) => (domain :: false-or(), predecessor :: false-or()) let in-lib :: = domain-library(d); local method lookup-domain-1 (link :: false-or(), predecessor :: false-or()) => (domain :: false-or(), predecessor :: false-or()) if (link) let link :: = link; if (in-lib == domain-library(link) & domain-match?(link, d)) values(link, predecessor) else lookup-domain-1(domain-next(link), link) end if else values(#f, predecessor) end if end method; lookup-domain-1(incremental-gf-domain-info(g), #f) end function; define class () constant slot sealed-generic-function-error-domain :: , required-init-keyword: domain:; end class; define generic domain-conflict? (g :: , frob, lib :: false-or(), incremental? :: , opstring) => (ans :: false-or()); define method domain-conflict? (g :: , frob, lib :: false-or(), incremental? :: , opstring) => (ans :: false-or()) ~incremental? & make(, generic-function: g, format-string: "Cannot %s %= in library %= on %= because the generic is sealed.", format-arguments: vector(opstring, frob, lib, g)) end method; define method domain-conflict? (g :: , frob, lib :: , incremental? :: , opstring) => (ans :: false-or()) if (incremental-gf-sealed?(g)) // Then it's only ok if we are the defining library. (lib ~== incremental-gf-library(g) & make(, generic-function: g, format-string: "Cannot %s %= in %= on %= because the generic is sealed.", format-arguments: vector(opstring, frob, lib, g))) else // Otherwise, check to see if the method conflicts with any domains not // known about in lib, where it was defined. let d :: false-or() = incremental-gf-domain-info(g); if (d) let d :: = d; local method loop (d :: ) let dlib :: = domain-library(d); // If we're an incremental addition, we permit the method to be added if // it's visible from the library where the domain is defined - it's a // permissible re- or additional-definition. Otherwise we only permit // the addition for the same library. if (if (incremental?) library-visible-from?(lib, domain-library(d)) else lib == dlib end | domain-disjoint?(d, frob, $empty-subjunctive-class-universe, #f)) let d2 :: false-or() = domain-next(d); if (d2) let d2 :: = d2; loop(d2) end if else let dt = domain-types(d); make(, generic-function: g, domain: dt, format-string: "Cannot %s %= in %= on %= because " "it is blocked by the sealed domain over %= defined in %=.", format-arguments: vector(opstring, frob, lib, g, dt, dlib)) end if end method; loop(d) end if end if end method; // Is the domain not applicable to a set of arguments of the given types? define sealed generic domain-disjoint? (d1, d2, scu :: , dep :: ) => (well? :: ); define function grounded-disjoint-types? (t1 :: , t2 :: , scu :: , dep :: ) => (well? :: ) if (instance?(t1, )) let t1 :: = t1; if (instance?(t2, )) let t2 :: = t2; disjoint-types-1?(t1, t2, scu, dep) else disjoint-types?(t1, t2, scu, dep) end if else disjoint-types?(t1, t2, scu, dep) end if end function; define method domain-disjoint? (d1, d2, scu :: , dep :: ) => (well? :: ) block (return) for (i :: from 0 below domain-number-required(d1)) if (grounded-disjoint-types?(domain-type(d1, i), domain-type(d2, i), scu, dep)) return(#t) end if end for; #f end end method; define copy-down-method domain-disjoint? (d1 :: , d2 :: , scu :: , dep :: ) => (well? :: ); define method domain-disjoint? (d1 :: , d2 :: , scu :: , dep :: ) => (well? :: ) domain-disjoint?(d2, d1, scu, dep) end method; define copy-down-method domain-disjoint? (d1 :: , d2 :: , scu :: , dep :: ) => (well? :: ); define method domain-disjoint? (d1 :: , d2 :: , scu :: , dep :: ) => (well? :: ) domain-disjoint?(d2, d1, scu, dep) end method; define copy-down-method domain-disjoint? (d1 :: , d2 :: , scu :: , dep :: ) => (well? :: ); define method domain-disjoint? (d1 :: , d2 :: , scu :: , dep :: ) => (well? :: ) domain-disjoint?(d2, d1, scu, dep) end method; define copy-down-method domain-disjoint? (d1 :: , d2 :: , scu :: , dep :: ) => (well? :: ); define method domain-disjoint? (d1 :: , d2 :: , scu :: , dep :: ) => (well? :: ) domain-disjoint?(d2, d1, scu, dep) end method; define method type-complete? (d :: ) => (well? :: ) local method loop (i :: ) => (well? :: ) let i = i - 1; if (i < 0) #t elseif (~type-complete?(domain-type(d, i))) #f else loop(i) end if end method; loop(domain-number-required(d)) end method; define method recompute-type-complete! (d :: ) => (well? :: ) type-complete?(d) end method; define method map-congruency-classes (f :: , d :: ) => () for (i from 0 below domain-number-required(d)) map-congruency-classes(f, domain-type(d, i)) end for end method; define method reduce-incomplete-classes (f :: , d :: , ans) => (ans) local method loop (i :: , ans) => (ans) let i = i - 1; if (i < 0) ans else loop(i, reduce-incomplete-classes(f, domain-type(d, i), ans)) end if end method; loop(domain-number-required(d), ans) end method; define method type-complete? (d :: ) => (well? :: ) type-complete?(domain-method(d)) end method; define method recompute-type-complete! (d :: ) => (well? :: ) recompute-type-complete!(domain-method(d)) end method; define method map-congruency-classes (f :: , d :: ) => () map-congruency-classes(f, domain-method(d)) end method; define method reduce-incomplete-classes (f :: , d :: , ans) => (ans) reduce-incomplete-classes(f, domain-method(d), ans) end method;