Module: dfmc-common Synopsis: The symbolic top-level-form/definition framework 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 // Used by browser API define abstract compiler-open class () end; //// TODO: Work out this variable name forward-reference soup. Perhaps we //// should move the AST node definitions forward, out of the reader. define constant = ; /* */ define constant = ; /* */ //// Top level forms. define thread variable *interactive-compilation-layer* = #f; define macro form-properties-definer { define ?adjectives:* form-properties ?:name (?supers:*) ?slots end } => { define form-properties-aux (?adjectives) ?name (?supers) (?slots) (?slots) (?slots) end; } slots: { } => { } { ?slot:*; ...} => { ?slot; ...} slot: { ?mods:* slot ?:name :: ?type:expression = ?init:expression, ?rest:* } => { ?mods slot ?name :: ?type = ?init, ?rest } { ?mods:* slot ?:name = ?init:expression, ?rest:* } => { ?mods slot ?name :: = ?init, ?rest } end macro; define macro form-properties-aux-definer {define form-properties-aux (?adjectives:*) ?:name (?supers:*) (?slots:*) (?cslots) (?mergers) end } => { define ?adjectives dood-class ?name (?supers) ?cslots end; define form-properties-accessors ?name ?slots end; define method form-properties-class (form :: ?name) => (c :: singleton(?name)) ?name end method; define method merge-form-properties! (old :: ?name, new :: ?name, #next next-method) next-method(); ?mergers; end method; } cslots: { } => { } { ?cslot:*; ...} => { ?cslot; ...} cslot: { ?mods:* slot ?slot-name :: ?type:expression = ?init:expression, ?rest:* } => { ?mods slot ?slot-name :: ?type = ?init, ?rest } mergers: { } => { } { ?merger; ... } => { ?merger; ... } merger: { ?mods:* slot ?slot-name :: ?type:expression = ?init:expression, ?rest:* } => { old.?slot-name := new.?slot-name } slot-name: { ?:name } => { "shadowable-" ## ?name } end macro; define macro form-properties-accessors-definer { define form-properties-accessors ?class:name end } => { } { define form-properties-accessors ?class:name ?mods:* slot ?accessor:name :: ?type:expression = ?default:expression, ?rest:*; ?more:* end } => { define method ?accessor (object :: ?class) => (val :: ?type) let p = form-properties-in-context (current-library-description(), object, #f); if (p) "shadowable-" ## ?accessor(p) else ?default end end method; define method ?accessor ## "-setter" (value, object :: ?class) let p = form-properties-in-context (current-library-description(), object, #t); "shadowable-" ## ?accessor ## "-setter" (value, p) end method; define form-properties-accessors ?class ?more end } end macro; // All modifiable slots of top level forms must be defined in subclasses // of via define form-properties. A slot is modifiable // if its contents can change after the end of the top-level processing // transaction that created the slot. define abstract class () end; define generic form-top-level-installable? (form :: ) => (installable?); // Default define method form-top-level-installable? (form :: ) => (no) #f end method; define generic form-properties-class (form :: ) => c :: subclass(); define function make-default-form-properties (form :: ) => (p :: ); make(form-properties-class(form)) end function; define generic merge-form-properties! (old :: , new :: ); // Base case. define method merge-form-properties! (old :: , new :: ) end method; define form-properties () lazy slot form-init-method = #f; lazy slot form-system-init-method = #f; slot form-properties-flags :: = 0; end; define packed-slots form-properties-flags (, ) boolean slot form-evaluation-tried-and-failed? = #f; boolean slot form-stripped? = #f; end packed-slots; define method initialize (x :: , #rest all-keys, #key, #all-keys) next-method(); apply(initialize-packed-slots, x, all-keys) end method; define compiler-open generic form-source-location (object); define compiler-open dood-class /* abstract */ () constant slot form-compilation-record :: = current-compilation-record(); lazy constant slot form-source-location, required-init-keyword: source-location:; lazy slot form-parent-form :: false-or() = #f, init-keyword: parent-form:; // sequence number used to establish the ordering of forms - given two // forms with the same parent, the one with the smaller sequence number // comes first. Note that the the numbers are not guaranteed to be // sequential or start with 0. In addition, derived forms created after // the initial top-level conversion are given sequence number of -1, // since they are assumed to be basically order independent wrt other // forms with the same parent. constant slot form-sequence-number :: = next-form-sequence-number(); lazy slot form-dependencies = #f; lazy slot form-referenced-variables = #f; end; define constant = ; // TODO: NEED REAL LIMITED SEQUENCE STORED IN SLOT // = limited(, of: ); // HACK: SHOULD TIGHTEN TO AFTER FIXING PACKED-SLOTS define compiler-open generic form-properties-in-context (context, form :: , create?) => (p :: false-or()); define generic form-parent-form (form :: ) => (parent :: false-or()); define generic form-adjectives (form :: ) => (adjectives :: false-or()); define method form-adjectives (form :: ) => (adjectives :: false-or()); #f end method; define compiler-open generic form-define-word (form :: ) => (word :: false-or()); define method form-define-word (form :: ) => (word :: false-or()); #f end method; define generic form-library (form :: ) => (library-description); define generic form-original-library (form :: ) => (false-or-library-description); define compiler-open generic form-macro-word-class (form :: ) => (class); define method form-macro-word-class (form :: ) => (class) #f end; // We can get to the library via the compilation record. define method form-library (form :: ) => (library-description) compilation-record-library(form-compilation-record(form)) end method; define method form-original-library (form :: ) => (library-description) compilation-record-original-library(form-compilation-record(form)) end method; define inline function form-downloaded? (form :: ) => (well? :: ) compilation-record-downloaded?(form-compilation-record(form)) end function; define inline function form-interactive? (form :: ) => (well? :: ) compilation-record-interactive?(form-compilation-record(form)) end function; // Return the form's top level methods. This is used as the starting // point for code generation/optimisation. define compiler-open generic form-top-level-methods (form :: ) => (method-models); // The default is just the init method, if there is one. define method form-top-level-methods (form :: ) => (method-models) let init = form-init-method(form); let so-far = if (init) list(init) else #() end; let system-init = form-system-init-method(form); let so-far = if (system-init) pair(system-init, so-far) else so-far end; so-far end method; define compiler-open generic install-top-level-forms (form :: ) => (); // Totally undo the handling of the form. define compiler-open generic retract-top-level-form (form :: ) => (); // Sequencing/dependencies // Current form or compilation record being processed define thread variable *current-dependent* = #f; define constant $no-dependent = #"no-dependent"; // The current compilation stage taking place define thread variable *current-stage* = #f; define macro with-dependent { with-dependent (?stage:expression of ?dependent:expression) ?:body end } => { do-with-dependent(?stage, ?dependent, method () ?body end) } end macro; define inline function do-with-dependent (stage, dependent, f) if (*current-stage* == stage & *current-dependent* == dependent) f() else debug-assert(~*interactive-compilation-layer* | ~compilation-record-downloaded? (compilation-record-of(dependent)), // Trying to compile something that's already been downloaded? "Changing interactive context to downloaded dependent?"); debug-assert(current-library-description? (compilation-record-library (compilation-record-of(dependent))), "New dependent %s is not in current context %s!", dependent, current-library-description()); dynamic-bind (*current-stage* = stage, *current-dependent* = dependent) f() end; end if; end function; define macro without-dependency-tracking { without-dependency-tracking ?:body end } => { dynamic-bind (*current-stage* = #"testing", *current-dependent* = $no-dependent) ?body end } end macro; define compiler-open generic remove-dependent-program-conditions (dependent, stages); // Dependency tracking // Name dependencies define constant dep$name-syntax = ash(1, 0); define constant dep$name-macro-ref = ash(1, 1); define constant dep$name-binding = ash(1, 2); // This is the same as dep$name-binding, but indicates that the dependency // should count as a "reference" for warning/browsing purposes. define constant dep$name-binding-ref = ash(1, 3); // Binding dependencies. define constant dep$active-definition = ash(1, 4); define constant dep$defined? = ash(1, 5); define constant dep$modifying-definitions = ash(1, 6); define constant dep$model-object = ash(1, 7); define constant dep$modifying-models = ash(1, 8); define constant dep$count = 9; define constant dep$all = ash(1, dep$count) - 1; // Compilation stages. For now (and maybe forever) everything from // models on is one stage. define constant stage$0 = 0 * dep$count; define constant stage$1 = 1 * dep$count; define constant stage$count = 2; // TODO: The folding version works around a bug in the compiler that // prevents it folding away the inlined representation of ash that ends // up in the body of make-dependency-condition (not to mention the // debug-assert's which defeat it anyway). define inline function make-dependency-condition (stage :: , kind :: ) => (condition :: ); debug-assert(stage == stage$0 | stage == stage$1, "Invalid stage %s", stage); debug-assert(0 <= kind & kind <= dep$all, "Invalid kind %s", kind); ash(kind, stage); end function; define macro folding-make-dependency-condition { folding-make-dependency-condition(?stage:expression, ?kind:expression) } => { ash(?kind, ?stage) } end macro; define constant stage$0-mask = folding-make-dependency-condition(stage$0, dep$all); define constant stage$1-mask = folding-make-dependency-condition(stage$1, dep$all); define constant stage$all :: = logior(stage$0-mask, stage$1-mask); define constant $top-level-processing = stage$0; define constant $top-level-processing-mask = stage$0-mask; define constant $compilation = stage$1; define constant $compilation-mask = stage$1-mask; define function dependency-stage-match? (stage, stage-mask) logand(make-dependency-condition(stage, dep$all), stage-mask) ~== 0 end function; define compiler-open generic note-binding-dependency-of (dependent, condition, binding); define compiler-open generic note-name-dependency-of (dependent, condition, binding, name, module); // These functions retract all dependents on the thing being changed, so // that nothing depends on it any more. For instance note-adding-definition // retracts everything which depended on 'binding' being undefined, note- // removing-definition retracts everything which depended on 'binding' having // 'definition' as its definition, etc. define compiler-open generic note-adding-definition (binding, definition); define compiler-open generic note-removing-definition (binding, definition); define compiler-open generic note-adding-modifying-definition (binding, definition); define compiler-open generic note-removing-modifying-definition (binding, definition); define compiler-open generic note-removing-model-object (binding); define compiler-open generic note-removing-modifying-models (binding); define compiler-open generic note-changing-binding (binding, name, module, new-binding); define function note-binding-dependency (binding, kind) let dependent = *current-dependent*; unless (dependent == $no-dependent) let stage = *current-stage*; unless (dependent & stage) error("Unknown dependent for binding dependency on %s", binding.name); end; note-binding-dependency-of(dependent, make-dependency-condition(stage, kind), binding) end; end function; define function note-name-dependency (binding, kind, word, module) let dependent = *current-dependent*; unless (dependent == $no-dependent) let stage = *current-stage*; unless (dependent & stage) error("Unknown dependent for binding dependency on %s", binding.name); end; note-name-dependency-of(dependent, make-dependency-condition(stage, kind), binding, word, module) end; end function; define thread variable *last-form-sequence-number* = #f; define macro with-form-creation { with-form-creation ?:body end } => { dynamic-bind (*last-form-sequence-number* = -1) ?body end; } end macro; // Assumes body doesn't do anything fancy, just makes a form. define macro with-boot-form-creation { with-boot-form-creation (?seq:expression of ?parent:expression) ?:body end } => { do-with-boot-form-creation(?seq, ?parent, method () ?body end) } end macro; define inline function do-with-boot-form-creation (seq, parent, fn) dynamic-bind (*last-form-sequence-number* = seq - 1, *current-dependent* = parent, *current-stage* = $top-level-processing) fn() end; end function; define function next-form-sequence-number () => sequence-number; if (*current-stage* == $top-level-processing) *last-form-sequence-number* := *last-form-sequence-number* + 1 else -1 end end function; define function current-compilation-record () compilation-record-of(*current-dependent*) end; define method compilation-record-of (cr :: ) cr end method; define method compilation-record-of (form :: ) form-compilation-record(form) end method; define method compilation-record-of (none == #"no-dependent") #f end method; // True if form is defined after base define generic defined-after? (base, form) => form-after-base?; // True if form is defined before base define generic defined-before? (base, form) => form-before-base?; define inline function library-defined-after? (base-lib, other-lib) // one of the arguments should be the current dependent, so // should be in the current library. This makes things simpler... if (*interactive-compilation-layer*) debug-assert(~other-lib | ~base-lib); ~other-lib else debug-assert(current-library-in-context?(other-lib) | current-library-in-context?(base-lib)); current-library-in-context?(other-lib) end; end function; define inline method defined-after? (cr1 :: , cr2 :: ) => after?; let lib1 = cr1.compilation-record-original-library; let lib2 = cr2.compilation-record-original-library; if (lib1 == lib2) let n1 :: = cr1.compilation-record-sequence-number; let n2 :: = cr2.compilation-record-sequence-number; n1 < n2 else library-defined-after?(lib1, lib2) end; end method; define inline method defined-before? (cr1 :: , cr2 :: ) => before?; defined-after?(cr2, cr1) end; define method defined-after? (cr :: , form :: ) => after?; let form-cr :: = form.form-compilation-record; defined-after?(cr, form-cr) end; define method defined-after? (form :: , cr :: ) => after?; defined-before?(cr, form) end; define method defined-before? (cr :: , form :: ) => before?; let form-cr :: = form.form-compilation-record; defined-before?(cr, form-cr) end; define method defined-before? (form :: , cr :: ) => before?; defined-after?(cr, form) end; define method defined-after? (base :: , form :: ) => after?; let base-cr = base.form-compilation-record; let form-cr = form.form-compilation-record; let base-lib = base-cr.compilation-record-original-library; let form-lib = form-cr.compilation-record-original-library; if (base-lib ~== form-lib) library-defined-after?(base-lib, form-lib) elseif (base-cr == form-cr) local method after?(base, form) let base-parent = base.form-parent-form; let form-parent = form.form-parent-form; if (base-parent == form-parent) base.form-sequence-number < form.form-sequence-number elseif (base == form-parent) #t elseif (base-parent == #f) after?(base, form-parent) else after?(base-parent, form) end end method; after?(base, form) else base-cr.compilation-record-sequence-number < form-cr.compilation-record-sequence-number end end method; define method defined-before? (base :: , form :: ) => before?; defined-after?(form, base) end method; // All non-definition top-level forms are classed as miscellaneous // initialisations. define compiler-open generic retract-body-fragments (x); define method retract-body-fragments (x) end method; define compiler-open generic form-body (form) => (body); define compiler-open generic form-body-setter (value, form); define dood-class () lazy slot form-body, required-init-keyword: body:; end; define method retract-body-fragments (x :: ) next-method(); form-body(x) := #f; end method; define method strip-incremental-slots (x :: ) next-method(); retract-body-fragments(x); end method; // A top-level form which is a macro call. // This exists only for browsers and is otherwise ignored by the compiler define dood-class () // form-derived-forms must be lazy to prevent circularities in proxies // for booted compiler forms. lazy slot form-derived-forms :: ; constant slot form-define-word :: false-or(), required-init-keyword: define-word:; end; define method form-top-level-methods (form :: ) => (method-models) #() end method; // A modified top-level-form has space for adjectives. define /* abstract */ dood-class () lazy constant slot form-adjectives :: false-or(), required-init-keyword: adjectives:; end dood-class; // A defining-form is something of the syntactic form "define ... end". // It may be a macro, and so we may not know its semantics or whether // it actually defines, or is associated with, a variable in any way. We // can browse its adjectives, at least. define /* abstract */ compiler-open class () end class; define compiler-open form-properties () end; define packed-slots form-properties-flags (, ) boolean slot form-top-level-installed? = #f; end packed-slots; define inline method shadowable-form-top-level-installed? (form :: ) => (res :: ) // HACK: REALLY WANT THIS TO WORK WITH A TRANSPARENT ACCESSOR let offset :: // = packed-slots-end-count(); = $end-count-; logbit?(offset, shadowable-form-properties-flags(form)) end method; // Special case... Override the automatically generated definition above, // because need to default to #t, not #f, even though want the slot // initialized to #f. define method form-top-level-installed? (form :: ) => (well? :: ); ~form-top-level-installable?(form) | begin let p = form-properties-in-context (current-library-description(), form, #f); if (p) shadowable-form-top-level-installed?(p) else #t end end end method; define method form-top-level-installable? (form :: ) => (yes) #t end method; // A variable-defining form defines zero or more variables and installs // their models in bindings. define form-properties () end; define inline function pack-installed? (x) => (z :: ) select (x) #f => 0; #"processing" => 1; #t => 2; end select; end function; define inline function unpack-installed? (x :: ) => (res) select (x) 0 => #f ; 1 => #"processing" ; 2 => #t ; end select; end function; define packed-slots form-properties-flags (, ) eval slot form-models-installed? = #f, field-size: 2, pack-function: pack-installed?, unpack-function: unpack-installed?; end packed-slots; /* abstract */ define compiler-open dood-class (, ) lazy constant slot form-variable-name-or-names :: type-union(, ), required-init-keyword: variable-name:; slot form-properties :: = 0; end dood-class; define inline function pack-inline-policy (policy :: ) => (encoding :: ) select (policy) #"default-inline" => 0; #"inline" => 1; #"inline-only" => 2; #"may-inline" => 3; #"not-inline" => 4; end select; end function; define inline function unpack-inline-policy (encoding :: ) => (policy :: ) select (encoding) 0 => #"default-inline"; 1 => #"inline" ; 2 => #"inline-only" ; 3 => #"may-inline" ; 4 => #"not-inline" ; end select; end function; /* define generic form-inline-policy (form :: ) => (policy :: ); define method form-inline-policy (form :: ) => (policy :: ) #"default-inline" end method; */ define packed-slots form-properties (, ) eval slot form-inline-policy = #"default-inline", field-size: 3, pack-function: pack-inline-policy, unpack-function: unpack-inline-policy, init-keyword: inline-policy:; end packed-slots; define compiler-open generic form-variable-names (form :: ) => (names :: ); // This method must be overridden by multiple-binding forms define method form-variable-names (form :: ) => (names :: ); let name-or-names = form-variable-name-or-names(form); debug-assert(instance?(name-or-names, )); list(name-or-names); end method; // A convenience for the case where a form defines a single variable. define compiler-open generic form-variable-name (form :: ) => (name :: ); // This method must be overridden by multiple-binding forms define method form-variable-name (form :: ) => (name :: ) form-variable-name-or-names(form) end method; define compiler-open generic model-variable-using-definition (form :: , model-object) => (variable-name); // This method must be overridden by multiple-binding forms define method model-variable-using-definition (form :: , model-object) => (variable-name) form-variable-name(form) end; /* abstract */ define compiler-open class () end class; define packed-slots form-properties (, ) end packed-slots; define compiler-open generic form-compile-stage-only? (form :: ) => (compile-stage-only?); define method form-compile-stage-only? (form :: ) => (compile-stage-only?) #f end method; define compiler-open generic form-implicitly-defined? (form :: ) => (implicit?); define method form-implicitly-defined? (form :: ) => (implicit?) #f end method; // Queries as to whether the variable definition defines normal module // variables or special variables of some kind to do with threads. // Default is normal. define compiler-open generic form-thread? (form :: ) => (result :: ); define method form-thread? (form :: ) => (result :: ) #f end method; define compiler-open generic form-locked? (form :: ) => (result :: ); define method form-locked? (form :: ) => (result :: ) #f end method; define compiler-open generic form-atomic? (form :: ) => (result :: ); define method form-atomic? (form :: ) => (result :: ) #f end method; // A modifying form is a modifying definition of its variable(s). // Since it's not a primary definition, it must store its model somewhere // other than the binding... define form-properties () lazy slot form-model = #f; end; define compiler-open generic form-model (form); define compiler-open generic form-model-setter (model, form); define /* abstract */ compiler-open class (, ) end; define packed-slots form-properties (, ) end packed-slots; // A typed-definition is a definition-form with space for an // explicitly-declared type. define /* abstract */ compiler-open dood-class () lazy constant slot form-type-expressions :: , required-init-keyword: type-expressions:; end dood-class; define generic form-type-expressions (form :: ) => (expressions :: ); define generic form-type-expression (form :: , name :: ) => (expression :: ); // Namespace functions. // The following functions are used during the boot to generate // definitions for distinguished core libraries and modules. define compiler-open generic define-parsed-library (name, #key source-location, use-clauses, create-clauses, export-clauses) => (library-definition, library); define compiler-open generic define-parsed-module (name, #key source-location, use-clauses, create-clauses, export-clauses) => (module-definition, module); //// Model computation. // Variable-defining forms implement this method to compute and install // the model objects for the variables they defined. A default method // on compute-and-install-form-model-objects exists which calls // compute-form-model-object on each variable name and then stores the // result if this is more convenient. define compiler-open generic maybe-compute-and-install-form-model-objects (form :: ) => (); define compiler-open generic finish-installing-form-model-objects (form :: ) => (); define compiler-open generic compute-and-install-form-model-objects (form :: ) => (); define compiler-open generic compute-form-model-object (form :: , name :: ) => (model-object); // Code computation. define compiler-open generic compute-and-install-form-dfm (form :: ) => (); define method compute-and-install-form-dfm (form :: ) => () // Nothing needs doing by default. end method; //// Incremental/loose compilation. // In the context of the current compilation, should the form in question // be compiled statically or expanded to code to create the corresponding // object dynamically? define compiler-open generic form-dynamic? (form :: ) => (loose? :: ); define compiler-open generic form-binding-guaranteed-initialized? (form :: ) => (always? :: ); define method form-binding-guaranteed-initialized? (form :: ) => (always? :: ) #f end method; // Make a "hollow" model object for static binding that will be filled // in at run time. define compiler-open generic compute-form-hollow-object (form :: ) => (hollow-model); //// Interactive compilation. define compiler-open generic form-incremental? (form :: ) => (well? :: ); define compiler-open generic form-redefinition? (form :: ) => (well? :: ); // eof