Module: dfmc-definitions Synopsis: Slot conversion. 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 //// Class clause parsing. // There are three kinds of class clause to handle - slot definitions, // inherited slot modifications, and init-arg specifications. // TODO: Turn into a program error. define program-warning slot condition-variable-name, required-init-keyword: variable-name:; format-string "Unrecognized clause in the definition of class %= " "- ignoring."; format-arguments variable-name; end program-warning; define function parse-class-clauses (class-name, clauses) => (slot-specs, inherited-slot-specs, initarg-specs, metaclass-spec) collecting (slot-specs, inherited-slot-specs, initargs-specs) // use collect-first, since macro pattern-matching runs right-hand sides // from bottom up (= right to left) let metaclass-spec = #f; macro-case (clauses) { ?clauses:* } => values (collected(slot-specs), collected(inherited-slot-specs), collected(initargs-specs), metaclass-spec); clauses: { } => #f; { ?clause:*; ... } => macro-case (clause) { inherited slot ?spec:* } => collect-first-into (inherited-slot-specs, parse-inherited-slot-clause(clause, class-name, spec)); { ?adjectives:* slot ?spec:* } => collect-first-into (slot-specs, parse-slot-clause(clause, class-name, adjectives, spec)); { required keyword ?spec:* } => collect-first-into (initargs-specs, parse-keyword-clause(clause, class-name, #t, spec)); { keyword ?spec:* } => collect-first-into (initargs-specs, parse-keyword-clause(clause, class-name, #f, spec)); { ?adjectives:* metaclass ?spec:* } => metaclass-spec := parse-metaclass-clause(class-name, adjectives, spec); { ?other:* } => note(, source-location: fragment-source-location(other), variable-name: class-name); end macro-case; end macro-case; end; end; //// Slot definitions. define abstract dood-class () slot spec-slot-properties :: = 0; lazy constant slot spec-init-expression = #f, init-keyword: expression:; end dood-class; define packed-slots spec-slot-properties (, ) boolean slot spec-init-supplied? = #f, init-keyword: init-supplied?:; boolean slot spec-init-expression? = #f, init-keyword: init-expression?:; boolean slot spec-init-value? = #f, init-keyword: init-value?:; 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 abstract dood-class () lazy constant slot spec-init-keyword :: false-or() = #f, init-keyword: init-keyword:; lazy constant slot spec-type-expression = make-variable-name-fragment(#""), init-keyword: type:; end dood-class; define packed-slots spec-slot-properties (, ) boolean slot spec-init-keyword-required? = #f, init-keyword: init-keyword-required?:; end packed-slots; define dood-class (, ) lazy slot form-getter-definition = #f; // #f for virtual slots. lazy slot form-setter-definition = #f; lazy constant slot spec-getter, required-init-keyword: getter:; lazy constant slot spec-setter, required-init-keyword: setter-name:; // TODO: COULD BE PACKED AS WELL constant slot spec-allocation = #"instance", init-keyword: allocation:; end dood-class; define packed-slots spec-slot-properties (, ) boolean slot spec-sealed? = #f, init-keyword: sealed?:; boolean slot spec-constant? = #f, init-keyword: constant?:; boolean slot spec-volatile? = #f, init-keyword: volatile?:; boolean slot spec-atomic? = #f, init-keyword: atomic?:; boolean slot spec-raw? = #f, init-keyword: raw?:; end packed-slots; define dood-class () // lazy slot form-size-getter-definition = #f; lazy constant slot spec-size-getter = #f, init-keyword: size-getter:; lazy constant slot spec-size-init-keyword = #f, init-keyword: size-init-keyword:; lazy constant slot spec-size-init-expression = #f, init-keyword: size-init-expression:; end dood-class; define leaf packed-slots spec-slot-properties (, ) boolean slot spec-size-init-supplied? = #f, init-keyword: size-init-supplied?:; end packed-slots; define method form-variable-names (form :: ) => (seq :: ) form.form-variable-name-or-names end method; define method form-defined-bindings (form :: ) => (seq :: ) choose(method (binding) let defn = untracked-binding-definition(binding, default: #f); defn & ~form-ignored?(defn) end, map(untracked-lookup-binding, form.form-variable-names)) end; define method form-variable-name (form :: ) => (name :: ) // error("who's calling this?"); form.form-variable-names.first end method; define generic make-slot-spec (class :: , #key, #all-keys); define method make-slot-spec (class == , #rest initargs, #key allocation) if (fragment-repeated?(allocation)) apply(make-slot-spec, , initargs) else next-method() end; end method; define method make-slot-spec (class :: subclass(), #rest initargs, #key required-init-keyword, init-keyword) if (required-init-keyword) apply(next-method, class, init-keyword: as(, required-init-keyword), init-keyword-required?: #t, initargs) elseif (init-keyword) apply(next-method, class, init-keyword: as(, init-keyword), initargs) else next-method() end end method; define method make-slot-spec (class :: subclass(), #rest initargs, #key init-expression = unsupplied(), init-value = unsupplied(), init-function = unsupplied()) if (supplied?(init-expression)) apply(make, class, init-supplied?: #t, init-expression?: #t, expression: init-expression, initargs); elseif (supplied?(init-value)) apply(make, class, init-supplied?: #t, init-value?: #t, expression: init-value, initargs) elseif (supplied?(init-function)) apply(make, class, init-supplied?: #t, expression: init-function, initargs) else apply(make, class, initargs) end; end method; define program-warning slot condition-slot-name, required-init-keyword: slot-name:; slot condition-setter-name, required-init-keyword: setter-name:; format-string "Slot %s declared constant but with setter: %s " "- ignoring the setter option."; format-arguments slot-name, setter-name; end program-warning; define method make-slot-spec (class :: subclass(), #rest initargs, #key getter, setter, size-getter, constant?) if (~constant? & ~setter) setter := as-name(#{ ?getter ## "-setter" }); end; collecting (names) local method maybe-collect-name (name) if (name) macro-case (name) { #f } => #f; { ?:name } => begin collect-into(names, name); name end; end; end; end; collect-into(names, getter); let setter-name = maybe-collect-name(setter); maybe-collect-name(size-getter); if (constant? & setter-name) note(, source-location: fragment-source-location(setter), slot-name: getter, setter-name: setter); setter-name := #f; end; apply(next-method, class, setter-name: setter-name, variable-name: collected(names), constant?: constant? | ~setter-name, initargs) end; end method; define method make-slot-spec (class :: subclass(), #rest initargs, #key size-init-value = unsupplied()) if (supplied?(size-init-value)) apply(next-method, class, size-init-supplied?: #t, size-init-expression: size-init-value, initargs) else next-method() end; end method; define function fragment-repeated? (fragment) macro-case (fragment) { #"repeated" } => #t; { ?other:* } => #f; end; end; define function spec-repeated? (spec :: ) instance?(spec, ) end; define function spec-virtual? (spec :: ) spec-allocation(spec) == #"virtual" end; // Adjective specifications. // TODO: constant define property => sealed?: = #f value sealed = #t; value open = #f; end property; define property => allocation: = #"instance" value instance = #"instance"; value class = #"class"; value each-subclass = #"each-subclass"; value virtual = #"virtual"; value repeated = #"repeated"; end property; define property => constant?: = #f value constant = #t; end property; define property => inline-policy: = #"default-inline" value inline = #"inline"; value may-inline = #"may-inline"; value default-inline = #"default-inline"; value not-inline = #"not-inline"; end property; define property => atomic?: = #f value atomic = #t; end property; define property => volatile?: = #f value volatile = #f; end property; define property => raw?: = #f value raw = #t; end property; define constant $slot-adjectives = list(, , , , , , ); // Option specifications. define option => setter: :: expression end; define option => init-value: :: expression end; define option => init-function: :: expression excludes ; end option; define option => init-keyword: :: symbol end; define option => required-init-keyword: :: symbol excludes , , ; end option; define option => size-getter: :: expression end option; define option => size-init-value: :: expression end option; define option => size-init-keyword: :: symbol end option; define option => type: :: expression end; define constant $slot-options = list(, , , , , , , , ); // Slot parser. // TODO: Cross check adjectives and options, or merge the two into // a single kind of declaration. // TODO: redundancy check on init-expression vs. rest // TODO: redundancy check on :: type vs. type: define function parse-slot-clause (clause, name, adjectives, spec) let (adjective-initargs, adjectives) = parse-property-adjectives($slot-adjectives, adjectives, name); let (getter-spec, option-initargs) = macro-case (spec) { ?getter:name = ?init:expression, ?options:* } => values(getter, concatenate(list(init-expression: init), options)); { ?getter:name, ?options:* } => values(getter, options); { ?getter:name :: ?type:expression = ?init:expression, ?options:* } => values(getter, concatenate(list(type: type, init-expression: init), options)); { ?getter:name :: ?type:expression, ?options:* } => values(getter, concatenate(list(type: type), options)); options: { ?keys/vals:* } => parse-options($slot-options, keys/vals, name); end macro-case; let all-initargs = concatenate(adjective-initargs, option-initargs); apply(make-slot-spec, , source-location: fragment-source-location(clause), adjectives: adjectives, getter: getter-spec, all-initargs); end; // Slot method generation. define function generate-slot-method-definition-forms (slot-specs :: ) => (forms :: ) reduce(maybe-add-one-slot-method-definition-forms, #(), slot-specs); end; define function maybe-add-one-slot-method-definition-forms (forms :: , slot-spec :: ) case spec-virtual?(slot-spec) | spec-raw?(slot-spec) => forms; otherwise => with-expansion-source-form (slot-spec) add-one-slot-method-definition-forms(forms, slot-spec); end; end; end function; define method add-one-slot-method-definition-forms (forms :: , slot-spec :: ) => (forms :: ) let class-binding = form-variable-binding(form-parent-form(slot-spec)); let type-expression = spec-type-expression(slot-spec); let getter-name = spec-getter(slot-spec); let setter-name = spec-setter(slot-spec); let mods = if (spec-sealed?(slot-spec)) #{ sealed } else #{ } end; let params = if (spec-repeated?(slot-spec)) #{ object :: ?class-binding, index :: } else #{ object :: ?class-binding } end; let getter-fragment = #{ define ?mods method ?getter-name (?params) => (value :: ?type-expression) end }; // TODO: This as-body forces the current hygiene context to be // remembered since templates currently don't record that but // fragments do. let getter-fragment = as-body(getter-fragment); let getter-forms = top-level-convert(slot-spec, getter-fragment); let getter-form = first(getter-forms); form-class(getter-form) := if (spec-repeated?(slot-spec)) #"repeated-getter" else #"getter" end; form-getter-definition(slot-spec) := getter-form; forms := concatenate(getter-forms, forms); if (~spec-constant?(slot-spec) & setter-name) let setter-fragment = #{ define ?mods method ?setter-name (value :: ?type-expression, ?params) => (value :: ?type-expression) end }; // TODO: This as-body forces the current hygiene context to be // remembered since templates currently don't record that but // fragments do. let setter-fragment = as-body(setter-fragment); let setter-forms = top-level-convert(slot-spec, setter-fragment); let setter-form = first(setter-forms); form-class(setter-form) := if (spec-repeated?(slot-spec)) #"repeated-setter" else #"setter" end; form-setter-definition(slot-spec) := setter-form; forms := concatenate(setter-forms, forms); end; forms end method; /* define method add-one-slot-method-definition-forms (forms :: , slot-spec :: ) => (forms :: ) let class-name = form-variable-name(form-parent-form(slot-spec)); let type-expression = spec-type-expression(slot-spec); let getter-name = spec-getter(slot-spec); let setter-name = spec-setter(slot-spec); let sealed? = spec-sealed?(slot-spec); let adjectives = if (sealed?) list(#"adjectives") else #() end; let object-var-spec = make(, variable-name: #{ object }, type-expression: class-name); let value-var-spec = make(, variable-name: #{ value }, type-expression: type-expression); let getter-required = if (spec-repeated?(slot-spec)) vector(object-var-spec, make(, variable-name: #{ index }, type-expression: #{ })) else vector(object-var-spec) end; let getter-values-spec = make(, required-variable-specs: vector(value-var-spec), rest-variable-spec: #f); let getter-sig = make(, arguments-spec: make(, required-variable-specs: getter-required, next-variable-spec: #f, rest-variable-spec: #f, key?: #f, key-variable-specs: #()), values-spec: getter-values-spec); let getter-form = make(, parent: slot-spec, source-location: form-source-location(slot-spec), variable-name: getter-name, adjectives: adjectives, signature: getter-sig, body: #{ }, signature-and-body-fragment: #f, inline-policy: #"default-inline", sealed?: sealed?, sideways?: #f); form-class(getter-form) := if (spec-repeated?(slot-spec)) #"repeated-getter" else #"getter" end; form-getter-definition(slot-spec) := getter-form; // TODO: Domain definitions. let getter-forms = list(getter-form); forms := concatenate(getter-forms, forms); if (~spec-constant?(slot-spec) & setter-name) let setter-sig = make(, arguments-spec: make(, required-variable-specs: concatenate(vector(value-var-spec), getter-required), next-variable-spec: #f, rest-variable-spec: #f, key?: #f, key-variable-specs: #()), values-spec: getter-values-spec); let setter-form = make(, parent: slot-spec, source-location: form-source-location(slot-spec), variable-name: setter-name, adjectives: adjectives, signature: setter-sig, body: #{ }, signature-and-body-fragment: #f, inline-policy: #"default-inline", sealed?: sealed?, sideways?: #f); form-class(setter-form) := if (spec-repeated?(slot-spec)) #"repeated-setter" else #"setter" end; form-setter-definition(slot-spec) := setter-form; let setter-forms = list(setter-form); forms := concatenate(setter-forms, forms); end; forms end method; */ define method add-one-slot-method-definition-forms (forms :: , slot-spec :: ) => (forms :: ) let forms = next-method(); let size-getter-name = spec-size-getter(slot-spec); if (size-getter-name) let class-binding = form-variable-binding(form-parent-form(slot-spec)); let mods = if (spec-sealed?(slot-spec)) #{ sealed } else #{ } end; let size-getter-fragment = #{ define ?mods method ?size-getter-name (object :: ?class-binding) => (size :: ) end }; let size-getter-fragment = as-body(size-getter-fragment); let size-getter-forms = top-level-convert(slot-spec, size-getter-fragment); let size-getter-form = first(size-getter-forms); form-class(size-getter-form) := #"getter"; // form-size-getter-definition(slot-spec) := size-getter-form; concatenate(size-getter-forms, forms) else forms end end method; //// Inherited slot modifications. define dood-class () lazy constant slot spec-getter, required-init-keyword: getter:; end dood-class; define constant $inherited-slot-options = list(, , , ); define program-warning slot condition-getter-name, required-init-keyword: getter-name:; slot condition-type-expression, required-init-keyword: type:; format-string "Inherited slot clause may not specify a type %= :: %= " "-- ignoring the type."; format-arguments getter-name, type; end program-warning; define function parse-inherited-slot-clause (clause, name, spec) let (getter, option-initargs) = macro-case (spec) { ?getter:name = ?init:expression, ?options:* } => values(getter, concatenate(list(init-expression: init), options)); { ?getter:name, ?options:* } => values(getter, options); { ?getter:name :: ?type:expression = ?init:expression, ?options:* } => begin note(, source-location: fragment-source-location(clause), getter-name: getter, type: type); values(getter, concatenate(list(init-expression: init), options)); end; { ?getter:name :: ?type:expression, ?options:* } => begin note(, source-location: fragment-source-location(clause), getter-name: getter, type: type); values(getter, options); end; options: { ?keys/vals:* } => parse-options($inherited-slot-options, keys/vals, name); end macro-case; apply(make-slot-spec, , getter: getter, option-initargs); end; //// Initarg definitons. define dood-class () lazy constant slot form-source-location, required-init-keyword: source-location:; end dood-class; define constant $initialization-argument-options = list(, , ); define program-warning slot condition-keyword, required-init-keyword: keyword:; slot condition-type-expression, required-init-keyword: type:; format-string "Initialization argument specification may not specify a type %= :: %= " "-- ignoring the type. Perhaps you want to use the \"type:\" option."; format-arguments keyword, type; end program-warning; define function parse-keyword-clause (clause, name, required?, spec) let (keyword, option-init-args) = macro-case (spec) { ?keyword:token } => values(keyword, #()); { ?keyword:token = ?init:expression, ?options } => values(keyword, concatenate(list(init-expression:, init), options)); { ?keyword:token, ?options } => values(keyword, options); { ?keyword:token :: ?type:expression = ?init:expression, ?options } => begin note(, source-location: fragment-source-location(clause), keyword: keyword, type: type); values(keyword, concatenate(list(init-expression:, init), options)) end; { ?keyword:token :: ?type:expression, ?options } => begin note(, source-location: fragment-source-location(clause), keyword: keyword, type: type); values(keyword, options) end; options: { ?keys/vals:* } => parse-options($initialization-argument-options, keys/vals, name); end; apply(make-slot-spec, , source-location: fragment-source-location(clause), init-keyword: keyword, init-keyword-required?: required?, option-init-args) end; //// Metaclass definitions. define dood-class () lazy constant slot spec-metaclass-name, required-init-keyword: metaclass-name:; lazy constant slot spec-metaclass-initargs, required-init-keyword: metaclass-initargs:; end dood-class; define function parse-metaclass-clause (name, adjectives, spec) => (spec) parse-metaclass-adjectives(name, adjectives); macro-case (spec) { ?metaclass:name, ?initargs:* } => make(, metaclass-name: as(, metaclass), metaclass-initargs: parse-property-list(initargs)); end; end; define function parse-metaclass-adjectives (name, adjectives) => () // Just check that there aren't any. parse-property-adjectives(#(), adjectives, name); end; // eof