Module: dfmc-definitions Synopsis: The macro 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 //// Macro definitions. // Macro definition objects. define dood-class () lazy constant slot form-macro-rules, required-init-keyword: macro-rules:; end; define method form-define-word (form :: ) => (word :: ) #"macro" end method; // Browser support define method macro-definition-word (form :: ) let word = form.form-macro-word; let word-class = form.form-macro-word-class; let macro-kind = select (word-class) $function-word-only-token => #"function"; $local-declaration-word-only-token => #"declaration"; $begin-word-only-token => #"statement"; $define-body-word-only-token => #"define"; $define-list-word-only-token => #"define"; otherwise => #"special"; end select; values(word, macro-kind); end method; //// The real macro converter. // Install for callback by the constraint matching code define sideways method expand-for-macro-constraint (call :: ) => (expansion) expand-for-macro-constraint-using-definition (macro-definition(fragment-macro(call)), call); end method; define method expand-for-macro-constraint-using-definition (definition :: , call :: ) => (expansion) let expander = form-expander(definition); as-fragment-tokens(expander(#f, call)); end method; define &definition macro-definer { define ?mods:* \macro ?:name ?rules:* end } => with-native-template-evaluation do-define-macro(form, mods, name, rules); end; end &definition; define function do-compile-macro (name, adjectives, rules, #key definition-context = fragment-module(name)) let (main-rule-set, aux-rule-sets) = parse-macro-rules(name, rules); let compiled-main = compile-rule-set-spec(main-rule-set); let compiled-aux = map(compile-rule-set-spec, aux-rule-sets); let compiled-exp = make(, name: name, module: definition-context, adjectives: adjectives, main-rule-set: compiled-main, aux-rule-sets: compiled-aux); let expander = block () generate-expander-function(compiled-exp); /* exception (e :: ) signal("Macro function generation failed for %=.", name); method (env, form) signal("Stub expansion for %=.", name); #{ make() } end; */ end; let compiled-macro = make-macro-descriptor-matching (main-rule-set.spec-rule-specs.first.spec-pattern-spec, referenced-names: expander-referenced-names(compiled-exp), expander-function: method (#rest r) // signal("Expanding %=!", name); apply(expander, r); end); local method rule-macro-expander (env, fragment) expand-macro-call(compiled-macro, fragment) end; values(compiled-macro, rule-macro-expander) end; define method do-define-macro (fragment :: , mods, name, rules) // Need to at least the pre-processing/analysis here, right now to // determine the word involved. let (initargs, adjectives) = parse-macro-adjectives(name, mods); let (compiled-macro, expander) = do-compile-macro(name, adjectives, rules); let definition = apply(make, , source-location: fragment-source-location(fragment), variable-name: name, adjectives: adjectives, macro-rules: rules, macro-object: compiled-macro, expander: expander, initargs); install-top-level-form(definition); list(definition) end method; define method dood-reinitialize (dood :: , form :: ) => () with-dood-context (dood-root(dood)) with-dependent-context ($top-level-processing of form) // format-out("REINITIALIZING %s\n", form); let (compiled-macro, expander) = do-compile-macro (form.form-variable-name, form.form-adjectives, form.form-macro-rules); form.form-macro-object := compiled-macro; form.form-expander := expander; end; end; end method; define abstract class () constant slot spec-elements, required-init-keyword: elements:; end class; define class () end; define abstract class () end; define class () end; define method make-macro-descriptor-matching (spec :: , #rest initargs) apply(make, , word-class: $define-body-word-only-token, suffix: "-definer", initargs) end method; define class () end; define method make-macro-descriptor-matching (spec :: , #rest initargs) apply(make, , word-class: $define-list-word-only-token, suffix: "-definer", initargs) end method; define class () constant slot spec-word, required-init-keyword: word:; end class; ignore(spec-word); define method make-macro-descriptor-matching (spec :: , #rest initargs) apply(make, , word-class: $begin-word-only-token, initargs) end method; define class () constant slot spec-word, required-init-keyword: word:; end class; define method make-macro-descriptor-matching (spec :: , #rest initargs) apply(make, , word-class: $function-word-only-token, initargs) end method; define class () constant slot spec-word, required-init-keyword: word:; end class; define method make-macro-descriptor-matching (spec :: , #rest initargs) apply(make, , word-class: $local-declaration-word-only-token, initargs) end method; define class () constant slot spec-word, required-init-keyword: word:; end class; define method make-macro-descriptor-matching (spec :: , #rest initargs) apply(make, , word-class: $unreserved-name-token, initargs) end method; define abstract class () end; define class () constant slot spec-elements, required-init-keyword: elements:; end class; define class () constant slot spec-expression, required-init-keyword: expression:; end class; define class () constant slot spec-pattern-spec, required-init-keyword: pattern-spec:; constant slot spec-template-spec, required-init-keyword: template-spec:; end class; define abstract class () constant slot spec-rule-specs, required-init-keyword: rule-specs:; end class; define class () end; define class () constant slot spec-name, required-init-keyword: name:; end class; define method parse-macro-rules (name, f) => (main-rule-set, aux-rule-sets) let (main-rule-set, aux-rules-f) = parse-macro-main-rule-set(name, f); collecting (aux-rule-sets) iterate walk (input-f = aux-rules-f) macro-case (input-f) { } => #t; { ?stuff:* } => begin let (next-set, remaining-f) = parse-macro-aux-rule-set(name, stuff); collect-into(aux-rule-sets, next-set); walk(remaining-f); end; end; end; let aux-rule-sets = collected(aux-rule-sets); values(main-rule-set, aux-rule-sets); end; end method; define serious-program-warning () slot condition-macro-name, required-init-keyword: macro-name:; format-string "Invalid main rule set in the definition of the macro %s."; format-arguments macro-name; end serious-program-warning; define serious-program-warning () slot condition-macro-name, required-init-keyword: macro-name:; format-string "Inconsistent main rule shapes in the definition of the macro %s."; format-arguments macro-name; end serious-program-warning; define method main-rule-pattern-specs-consistent? (specs :: ) => (well? :: ) let first-class = specs.first.spec-pattern-spec.object-class; every?(method (spec) instance?(spec-pattern-spec(spec), first-class) end, specs.tail) end method; define method parse-macro-main-rule-set (name, rules-frag) => (set, remains) collecting (rule-specs) macro-case (rules-frag) { ?rules } => begin let specs = collected(rule-specs); if (empty?(specs) | ~instance? (specs.first.spec-pattern-spec, )) note(, source-location: fragment-source-location(rules-frag) | fragment-source-location(name), macro-name: name); // Doesn't return. elseif (~main-rule-pattern-specs-consistent?(specs)) note(, source-location: fragment-source-location(rules-frag) | fragment-source-location(name), macro-name: name); // Doesn't return. else values(make(, rule-specs: specs), rules); end; end; rules: { { ?lhs:* } => { ?rhs:* }; ... } => begin collect-first-into (rule-specs, make(, pattern-spec: lhs, template-spec: make(, elements: rhs))); ... end; { { ?lhs:* } => { ?rhs:* } ... } => begin collect-first-into (rule-specs, make(, pattern-spec: lhs, template-spec: make(, elements: rhs))); ... end; { { ?lhs:* } => ?rhs:expression; ... } => begin collect-first-into (rule-specs, make(, pattern-spec: lhs, template-spec: make(, expression: rhs))); ... end; { { ?lhs:* } => ?rhs:expression ... } => begin collect-first-into (rule-specs, make(, pattern-spec: lhs, template-spec: make(, expression: rhs))); ... end; { ?other:* } => other; lhs: { \define ?stuff:* \end } => make(, elements: extract-define-word(name, stuff)); { \define ?stuff:* } => make(, elements: extract-define-word(name, stuff)); { ?word:name ?stuff:* \end } => begin check-macro-word(name, word); make(, word: word, elements: stuff); end; { ?word:name (?stuff:*) } => begin check-macro-word(name, word); make(, word: word, elements: stuff); end; { ?word:name } => begin check-macro-word(name, word); make(, word: word, elements: make(, fragments: #())); end; { ?word:name ?stuff:* } => begin check-macro-word(name, word); make(, word: word, elements: stuff); end; { ?stuff:* } => make(, elements: stuff); end; end; end method; define serious-program-warning () slot condition-macro-name, required-init-keyword: macro-name:; slot condition-macro-word, required-init-keyword: macro-word:; format-string "This main rule pattern of the macro %s starts with %s, which does not " "match the macro name."; format-arguments macro-name, macro-word; end serious-program-warning; define method check-macro-word (name :: , word :: ) => () if (fragment-name(name) ~== fragment-name(word)) note(, source-location: fragment-source-location(word) | fragment-source-location(name), macro-name: name, macro-word: word); end; end method; define serious-program-warning () slot condition-macro-name, required-init-keyword: macro-name:; format-string "The macro %s has a main rule pattern like a defining macro, but its name " "does not end in \"-definer\"."; format-arguments macro-name; end serious-program-warning; define serious-program-warning () slot condition-macro-name, required-init-keyword: macro-name:; format-string "This main rule pattern of the defining macro %s does not match the macro " "name."; format-arguments macro-name; end serious-program-warning; define method extract-define-word (name :: , pattern) => (new-pattern) let define-word = suffixed-name?(fragment-name(name), "-definer"); if (~define-word) note(, source-location: fragment-source-location(pattern) | fragment-source-location(name), macro-name: name); end; let f* = fragment-fragments(pattern); let modifiers-pattern = #(); block (return) for (f*-cursor = f* then f*-cursor.tail, until: empty?(f*-cursor)) let f = f*-cursor.head; if (instance?(f, ) & fragment-name(f) == define-word) // We've found the define word. return(make(, fragments: concatenate! (reverse!(modifiers-pattern), pair(make(), f*-cursor.tail)))); else modifiers-pattern := pair(f, modifiers-pattern); end; finally // Malformed pattern. note(, source-location: fragment-source-location(pattern) | fragment-source-location(name), macro-name: name); // Doesn't return. end; end; end method; define method parse-macro-aux-rule-set (name, rules) => (set, remains) macro-case (rules) { ?set-name:symbol ?more:* } => begin let (specs, remains) = parse-macro-rule-set(name, more); values(make(, name: set-name, rule-specs: specs), remains); end; end; end method; define serious-program-warning () slot condition-rule-name, required-init-keyword: rule-name:; format-string "Invalid auxiliary rule set %s in macro definition."; format-arguments rule-name; end serious-program-warning; define method parse-macro-rule-set (name, rules-frag) => (set, remains) collecting (rule-specs) macro-case (rules-frag) { ?rules } => begin let specs = collected(rule-specs); if (empty?(specs)) note(, source-location: fragment-source-location(rules-frag) | fragment-source-location(name), rule-name: name); // Doesn't return. else values(specs, rules); end; end; rules: { { ?lhs:* } => { ?rhs:* }; ... } => begin collect-first-into (rule-specs, make(, pattern-spec: make(, elements: lhs), template-spec: make(, elements: rhs))); ... end; { { ?lhs:* } => { ?rhs:* } ... } => begin collect-first-into (rule-specs, make(, pattern-spec: make(, elements: lhs), template-spec: make(, elements: rhs))); ... end; { { ?lhs:* } => ?rhs:expression; ... } => begin collect-first-into (rule-specs, make(, pattern-spec: make(, elements: lhs), template-spec: make(, expression: rhs))); ... end; { { ?lhs:* } => ?rhs:expression ... } => begin collect-first-into (rule-specs, make(, pattern-spec: make(, elements: lhs), template-spec: make(, expression: rhs))); ... end; { ?other:* } => other; end; end; end method; //// Compilation of pattern specs to their internal representation. define method compile-rule-set-spec (set :: ) make(, rewrite-rules: map(compile-rule-spec, spec-rule-specs(set))); end method; // TODO: Remove this symbol/keyword gyration due to emulator humbug. define method compile-rule-set-spec (set :: ) let name = as(, as(, fragment-value(spec-name(set)))); make(, rewrite-rules: map(compile-rule-spec, spec-rule-specs(set)), name: name, variable-name: pattern-variable-name(make-variable-name-fragment(name)), rewriter-variable-name: pattern-variable-name (make-variable-name-fragment (as(, concatenate(as(, name), "-rewriter"))))); end method; define method compile-rule-spec (rule :: ) let pattern = compile-pattern-spec(spec-pattern-spec(rule)); let template = spec-template-spec(rule); if (instance?(template, )) make(, pattern: pattern, template-code: spec-expression(template)); else let template = compile-template-spec(spec-template-spec(rule)); make(, pattern: pattern, template: template); end; end method; define method compile-pattern-spec (spec :: ) compile-pattern-spec-elements(fragment-fragments(spec-elements(spec))); end method; define method compile-pattern-spec-elements (f* :: ) if (empty?(f*)) #() else compile-pattern-spec-element(f*.head, f*.tail); end; end method; define method compile-pattern-spec-element (f :: , f*) pair(compile-one-pattern-spec-element(f), compile-pattern-spec-elements(f*)); end method; define method compile-pattern-spec-element (f :: , f*) let sep = f*.head; if (instance?(sep, )) let type = f*.tail.head; if (instance?(type, )) // A variable pattern. pair(make(, source-location: fragment-source-location(f), variable-name-pattern: compile-one-pattern-spec-element(f), type-expression-pattern: compile-one-pattern-spec-element(type)), compile-pattern-spec-elements(f*.tail.tail)); else pair(compile-one-pattern-spec-element(f), compile-pattern-spec-elements(f*)); end; else pair(compile-one-pattern-spec-element(f), compile-pattern-spec-elements(f*)); end; end method; define method compile-pattern-spec-element (f :: , f*) let (pattern-part, next-part) = split-at-kept-semicolon(f*); let next-part = next-part | #(); macro-case (pair(f, pattern-part)) { ?properties:* } => pair(properties, compile-pattern-spec-elements(next-part)); properties: { \#rest ?rest:*, ?hash-key-opt:* } => make(, source-location: fragment-source-location(f), rest-pattern: compile-rest-pattern-spec-element(rest), key-patterns: hash-key-opt); { ?hash-key-opt:* } => make(, key-patterns: hash-key-opt); hash-key-opt: { } => #(); { \#key, \#all-keys } => #(); // TODO: CORRECTNESS: Tag all-keys. { \#key ?keys:* } => keys; keys: { } => #(); { \#all-keys } => #(); // TODO: CORRECTNESS: Tag all-keys. { ?key:*, ... } => pair(key, ...); key: { ?var:* = ?default:expression } => compile-key-pattern-spec-element(var, default); { ?var:* } => compile-key-pattern-spec-element(var, #f); end; end method; define method compile-rest-pattern-spec-element (f :: ) compile-one-pattern-spec-element(f); end method; define method compile-rest-pattern-spec-element (f :: ) compile-one-pattern-spec-element(f); end method; define method compile-key-pattern-spec-element (f :: , default) compile-key-pattern-name (, fragment-pattern-variable(f), default); end method; define method compile-key-pattern-spec-element (f :: , default) compile-key-pattern-name (, fragment-name(f), default); end method; define method compile-key-pattern-name (class :: , f :: , default) make(class, source-location: fragment-source-location(f), symbol-name: fragment-name(f), variable-name: pattern-variable-name(f), constraint: fragment-constraint(f), default-expression: default); end method; define method compile-key-pattern-name (class :: , f :: , default) make(class, source-location: fragment-source-location(f), symbol-name: as(, fragment-name(f)), variable-name: pattern-variable-name(f), constraint: #f, default-expression: default); end method; define method compile-one-pattern-spec-element (f :: ) f end method; define method compile-one-pattern-spec-element (f :: ) make(, source-location: fragment-source-location(f), nested-pattern: compile-pattern-spec-elements(fragment-nested-fragments(f))); end method; define method compile-one-pattern-spec-element (f :: ) make(, source-location: fragment-source-location(f), nested-pattern: compile-pattern-spec-elements(fragment-nested-fragments(f))); end method; define method compile-one-pattern-spec-element (f :: ) make(, source-location: fragment-source-location(f), nested-pattern: compile-pattern-spec-elements(fragment-nested-fragments(f))); end method; define method compile-one-pattern-spec-element (f :: ) make(, source-location: fragment-source-location(f), symbol-name: as(, fragment-name(f)), variable-name: fragment-name(f), constraint: fragment-constraint(f)); end method; define method compile-one-pattern-spec-element (f :: ) let prefix = fragment-prefix(f); let suffix = fragment-suffix(f); let var = fragment-pattern-variable(f); if (prefix | suffix) make(, source-location: fragment-source-location(f), nested-pattern: compile-name-pattern-spec(var), prefix: prefix, suffix: suffix); else compile-name-pattern-spec(var); end; end method; define method compile-one-pattern-spec-element (f :: ) make(, source-location: fragment-source-location(f), symbol-name: #"...", variable-name: pattern-variable-name(make-variable-name-fragment(#"...")), constraint: #"*"); end method; define method compile-name-pattern-spec (f :: ) make(, source-location: fragment-source-location(f), symbol-name: fragment-name(f), variable-name: pattern-variable-name(f), constraint: fragment-constraint(f)); end method; define method compile-name-pattern-spec (f :: ) make(, source-location: fragment-source-location(f), symbol-name: as(, fragment-name(f)), variable-name: pattern-variable-name(f), constraint: #f); end method; define serious-program-warning slot condition-match-name, required-init-keyword: match-name:; format-string "Coercing matches are not supported - " "using the simple pattern variable name %s."; format-arguments match-name; end serious-program-warning; define method compile-name-pattern-spec (f :: ) let match-name = as(, fragment-value(f)); note(, source-location: fragment-source-location(f), match-name: match-name); make(, source-location: fragment-source-location(f), symbol-name: match-name, variable-name: pattern-variable-name (make-variable-name-fragment(match-name)), constraint: #f); end method; // Common error cases. define serious-program-warning slot condition-constrained-name, required-init-keyword: constrained-name:; slot condition-constraint, required-init-keyword: constraint:; format-string "Constrained name without a leading query - " "using ?%s:%s."; format-arguments constrained-name, constraint; end serious-program-warning; define method compile-one-pattern-spec-element (f :: ) note(, source-location: fragment-source-location(f), constrained-name: fragment-name(f), constraint: fragment-constraint(f)); compile-key-pattern-name(, f, #f); end method; define serious-program-warning slot condition-name, required-init-keyword: name:; format-string "Unexpected ?=%s in macro pattern - " "using the unadorned name %s."; format-arguments name, name again; end serious-program-warning; define method compile-one-pattern-spec-element (f :: ) note(, source-location: fragment-source-location(f), name: fragment-name(f)); compile-one-pattern-spec-element(fragment-name(f)); end method; define method compile-rest-pattern-spec-element (f :: ) // Gives us default constrained name correction as above. next-method(); end method; define method compile-rest-pattern-spec-element (f :: ) parser-error-handler(#f, f, #()); end method; define method compile-key-pattern-spec-element (f :: , default) parser-error-handler(#f, f, #()); end method; /* define method compile-one-pattern-spec-element (f :: ) make(, symbol-name: as(, fragment-name(f)), variable-name: fragment-name(f), constraint: fragment-constraint(f)); end method; */ //// Compilation of template specs to their internal representation. define method compile-template-spec (spec :: ) end method; define method compile-template-spec (spec :: ) compile-template-spec-elements(fragment-fragments(spec-elements(spec))); end method; define method compile-template-spec-elements (f* :: ) if (empty?(f*)) #() else compile-template-spec-element(f*.head, f*.tail); end; end method; define method compile-template-spec-element (f :: , f*) pair(compile-one-template-spec-element(f), compile-template-spec-elements(f*)); end method; define serious-program-warning slot condition-pattern-variable-name, required-init-keyword: pattern-variable-name:; format-string "Sequence substitution ??%s not followed by a valid separator or " "ellipsis - using ?%s ..."; format-arguments pattern-variable-name; end serious-program-warning; define method compile-template-spec-element (f :: , f*) let separator = f*.head; if (instance?(separator, ) & instance?(f*.tail.head, )) let var = pattern-variable-name(fragment-name(f)); pair(make(, source-location: fragment-source-location(f), variable-name: var, separator: separator), compile-template-spec-elements(f*.tail.tail)); else let var = pattern-variable-name(fragment-name(f)); let f*-remains = if (~instance?(separator, )) note(, source-location: fragment-source-location(f), pattern-variable-name: var); f* else f*.tail end; pair(make(, source-location: fragment-source-location(f), variable-name: var, separator: #f), compile-template-spec-elements(f*-remains)); end; end method; define method compile-one-template-spec-element (f :: ) f end method; define method compile-one-template-spec-element (f :: ) let prefix = fragment-prefix(f); let suffix = fragment-suffix(f); let var = fragment-pattern-variable(f); if (prefix | suffix) make(, source-location: fragment-source-location(f), variable-name: #f, name-substitution: compile-name-template-spec(var), prefix: prefix | "", suffix: suffix | ""); else compile-name-template-spec(var); end; end method; define method compile-one-template-spec-element (f :: ) make(, source-location: fragment-source-location(f), variable-name: pattern-variable-name(make-variable-name-fragment(#"..."))); end method; define method compile-one-template-spec-element (f :: ) make(, source-location: fragment-source-location(f), template: compile-template-spec-elements (fragment-nested-fragments(fragment-template(f)))); end method; define method compile-one-template-spec-element (f :: ) make(, source-location: fragment-source-location(f), rule-name: fragment-rule-name(f), template: compile-template-spec-elements (fragment-nested-fragments(fragment-template(f)))); end method; define method compile-name-template-spec (f :: ) make(, source-location: fragment-source-location(f), variable-name: pattern-variable-name(f)); end method; define method compile-name-template-spec (f :: ) make(, source-location: fragment-source-location(f), variable-name: pattern-variable-name (make-variable-name-fragment(fragment-value(f)))); end method; define method compile-name-template-spec (f :: ) make(, source-location: fragment-source-location(f), variable-name: pattern-variable-name (make-variable-name-fragment(as(, fragment-value(f))))); end method; define program-warning slot condition-pattern-variable-name, required-init-keyword: pattern-variable-name:; format-string "The template substitution %s has a constraint - ignoring."; format-arguments pattern-variable-name; end program-warning; define method compile-name-template-spec (f :: ) note(, source-location: fragment-source-location(f), pattern-variable-name: fragment-name(f)); make(, source-location: fragment-source-location(f), variable-name: pattern-variable-name(f)); end method; define method compile-one-template-spec-element (f :: ) make(f.object-class, left-delimiter: fragment-left-delimiter(f), nested-fragments: compile-template-spec-elements(fragment-nested-fragments(f)), right-delimiter: fragment-right-delimiter(f)); end method; //// Adjectives. // For testing... define property => dude?: = #f value dude = #t; end property; define property => traced?: = #f value traced = #t; end property; define constant macro-adjectives = list(, ); define method parse-macro-adjectives (name, adjectives-form) => (initargs, adjectives) parse-property-adjectives(macro-adjectives, adjectives-form, name) end method; //// Pseudo-macro "macro" define ¯o \macro { \macro end } => #{ } end ¯o; //// Utility. define function macro-fragment? (fragment) => (well? :: ) instance?(fragment, ) & (lookup-binding(fragment-macro(fragment)) == dylan-binding(#"macro")) end function; // eof