Module: dfmc-macro-expander Synopsis: Objects describing complete expanders. 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 define abstract class () end; define class () constant slot expander-name = #f, init-keyword: name:; constant slot expander-module = #f, init-keyword: module:; constant slot expander-adjectives = #(), init-keyword: adjectives:; constant slot expander-main-rule-set, required-init-keyword: main-rule-set:; constant slot expander-aux-rule-sets, required-init-keyword: aux-rule-sets:; constant slot expander-aux-rule-set-table = make(); slot expander-aux-rule-set-env :: ; slot expander-intermediate-words = #(); slot expander-referenced-names = #(); end class; define method initialize (exp :: , #key) next-method(); let table = expander-aux-rule-set-table(exp); for (set in expander-aux-rule-sets(exp), env-index from 0) rule-set-rewriter-env-index(set) := env-index; table[rule-set-name(set)] := set; finally expander-aux-rule-set-env(exp) := make(, size: env-index); end; check-pattern-variables(exp); compute-and-install-intermediate-words(exp); end method; define program-warning slot condition-pattern-variable-name, required-init-keyword: pattern-variable-name:; format-string "The pattern variable %s is unconstrained and not rewritten by an " "auxiliary rule - using %s:*."; format-arguments pattern-variable-name, pattern-variable-name again; end program-warning; define method check-pattern-variables (exp :: ) local method check-match (m) let constraint = match-constraint(m); if (~constraint) let name = match-symbol-name(m); let aux = element(expander-aux-rule-set-table(exp), name, default: #f); match-constraint(m) := #"*"; if (~aux) note(, source-location: match-source-location(m), pattern-variable-name: name); end; end; end method; do-binding-matches(check-match, exp); end method; define method compute-and-install-intermediate-words (exp :: ) => () let seen = make(
); local method process-body-match-words (f, f-tail) if (~empty?(f-tail)) process-body-match-terminator(exp, f-tail.head, seen); end; end method; do-body-match-tails(process-body-match-words, exp); // format-out("Intermediate words: %=\n", expander-intermediate-words(exp)); end method; define method process-body-match-terminator (exp :: , f, seen) => () end method; define method process-body-match-terminator (exp :: , f :: , seen) => () expander-intermediate-words(exp) := pair(fragment-name(f), expander-intermediate-words(exp)); end method; define method process-body-match-terminator (exp :: , f :: , seen) => () let key = match-symbol-name(f); if (~element(seen, key, default: #f)) element(seen, key) := #t; let aux-rewrite = element(expander-aux-rule-set-table(exp), key, default: #f); if (aux-rewrite) for (rule in rule-set-rewrite-rules(aux-rewrite)) let pattern = rule-pattern(rule); if (~empty?(pattern)) process-body-match-terminator(exp, pattern.head, seen); end; end; end; end; end method; define method do-binding-matches (f :: , exp :: ) => () do-binding-matches(f, expander-main-rule-set(exp)); for (set in expander-aux-rule-sets(exp)) do-binding-matches(f, set); end; end method; define method do-body-match-tails (f :: , exp :: ) => () do-body-match-tails(f, expander-main-rule-set(exp)); for (set in expander-aux-rule-sets(exp)) do-body-match-tails(f, set); end; end method; define method compile-rewrite-rule-templates! (exp :: ) compile-rule-set-templates!(exp, expander-main-rule-set(exp)); for (aux-set in expander-aux-rule-sets(exp)) compile-rule-set-templates!(exp, aux-set); end; end method; define serious-program-warning slot condition-pattern-variable-name, required-init-keyword: pattern-variable-name:; format-string "The pattern variable %s is referenced in the template but not bound " "on the left hand side of the rule."; format-arguments pattern-variable-name; end serious-program-warning; define serious-program-warning slot condition-pattern-variable-name, required-init-keyword: pattern-variable-name:; format-string "The pattern variable %s referenced in the template is " "bound in an incompatible way on the left hand side of the rule."; format-arguments pattern-variable-name; end serious-program-warning; define method compile-rule-set-templates! (exp :: , set :: ) for (rule in rule-set-rewrite-rules(set)) compile-rule-template!(exp, rule, rule-template(rule)); rule-compiled-template(rule) := compile-macro-template(rule-template(rule)); end; end method; define method compile-rule-template! (exp :: , rule :: , template) => () local method record-referenced-name (name) expander-referenced-names(exp) := pair(name, expander-referenced-names(exp)); end method; compute-template-references (template, record-referenced-name); do-template-substitutions (method (subst :: ) if (instance?(subst, )) let name = element-variable-name(subst); // let index = lookup-variable-name-env-index(rule, name); let (index, match?) = lookup-substitution-variable-index(rule, subst); if (index) if (match?) element-env-index(subst) := index; else note(, // source-location: fragment-source-location(name), source-location: element-source-location(subst), pattern-variable-name: name); end; else note(, // source-location: fragment-source-location(name), source-location: element-source-location(subst), pattern-variable-name: name); end; elseif (instance?(subst, )) let aux-set = element(expander-aux-rule-set-table(exp), fragment-name(element-rule-name(subst)), default: #f); let aux-rule-index = rule-set-rewriter-env-index(aux-set); element-aux-rule-env(subst) := expander-aux-rule-set-env(exp); element-aux-rule-index(subst) := aux-rule-index; compile-rule-template!(exp, rule, element-template(subst)); element-compiled-template(subst) := compile-macro-template(element-template(subst)); else compile-rule-template!(exp, rule, element-template(subst)); element-compiled-template(subst) := compile-macro-template(element-template(subst)); end; end, template); end method; define function compile-macro-template (template) => (template) // break("Compiling template"); generate-template-function(template) end function; define thread variable *expansion-stream* = #f; define thread variable *trace-stream* = #f; define function traced-expander? (exp :: ) => (well? :: ) member?(#"traced", expander-adjectives(exp)) end function; define function generate-expander-function (exp :: ) => (expander-function :: ) compile-rewrite-rule-templates!(exp); let traced? = traced-expander?(exp); let name = expander-name(exp); let f = if (dude-expander?(exp)) generate-dude-expander-function(exp); else generate-rewrite-rule-expander-function(exp); end; let module = expander-module(exp); method (env, form) with-expansion-module (module) with-new-hygiene-context (#"unknown") if (traced?) format-out("%s > %s\n", name, form); end; let expansion = f(call-as-fragment-tokens(form)); if (traced?) format-out("%s < %s\n", name, expansion); end; if (*expansion-stream*) format(*expansion-stream*, "\n%s", expansion); end; expansion end end end; end function; define macro with-macroexpansion-output { with-macroexpansion-output (?args:*) ?:body end } => { do-with-macroexpansion-output(method () ?body end, ?args) } end macro; define open generic do-with-macroexpansion-output (thunk :: , #key); define method do-with-macroexpansion-output (thunk :: , #key expansion-stream, trace-stream) dynamic-bind (*expansion-stream* = expansion-stream) dynamic-bind (*trace-stream* = trace-stream) thunk() end; end; end method; define method compiling-for-macroexpansion? () => (well? :: ) (*expansion-stream* | *trace-stream*) & #t end method; // eof