module: macros rcs-header: $Header: /scm/cvs/src/d2c/compiler/parser/macros.dylan,v 1.12 2003/07/11 03:13:08 housel Exp $ copyright: see below //====================================================================== // // Copyright (c) 1995, 1996, 1997 Carnegie Mellon University // Copyright (c) 1998, 1999, 2000, 2001 Gwydion Dylan Maintainers // All rights reserved. // // Use and copying of this software and preparation of derivative // works based on this software are permitted, including commercial // use, provided that the following conditions are observed: // // 1. This copyright notice must be retained in full on any copies // and on appropriate parts of any derivative works. // 2. Documentation (paper or online) accompanying any system that // incorporates this software, or any part of it, must acknowledge // the contribution of the Gwydion Project at Carnegie Mellon // University, and the Gwydion Dylan Maintainers. // // This software is made available "as is". Neither the authors nor // Carnegie Mellon University make any warranty about the software, // its performance, or its conformity to any specification. // // Bug reports should be sent to ; questions, // comments and suggestions are welcome at . // Also, see http://www.gwydiondylan.org/ for updates and documentation. // //====================================================================== // // // Some kind of macro definition. // define abstract class () // // The intermediate words. Computed when the top level form is processed. slot macro-intermediate-words :: = #[], init-keyword: intermediate-words:; // // The main rule set. constant slot macro-main-rule-set :: , required-init-keyword: main-rule-set:; // // The auxiliary rule sets. constant slot macro-auxiliary-rule-sets :: , required-init-keyword: auxiliary-rule-sets:; end class ; define sealed domain make (singleton()); define sealed domain initialize (); // // // A body-style define macro definition. // define class () end class ; define sealed domain make (singleton()); // // // A list-style define macro definition. // define class () end class ; define sealed domain make (singleton()); // // // A statement macro definition. // define class () end class ; define sealed domain make (singleton()); // // // A function macro definition. // define class () end class ; define sealed domain make (singleton()); // Definition information query routines. // definition-syntax-info{} // -- method on imported GF // // For body-style define macros, strip off the -definer. // define method definition-syntax-info (defn :: , name :: ) => (real-name :: false-or(), category :: ); values(sans-definer(name), #"define-body"); end method definition-syntax-info; // definition-syntax-info{} // -- method on imported GF // // For list-style define macros, strip off the -definer. // define method definition-syntax-info (defn :: , name :: ) => (real-name :: false-or(), category :: ); values(sans-definer(name), #"define-list"); end method definition-syntax-info; // definition-syntax-info{} -- method on imported GF // // For function macros, use the name as is. // define method definition-syntax-info (defn :: , name :: ) => (real-name :: , category :: ); values(name, #"function"); end method definition-syntax-info; // definition-syntax-info{} // -- method on imported GF // // For statement macros, use the name as is. // define method definition-syntax-info (defn :: , name :: ) => (real-name :: , category :: ); values(name, #"begin"); end method definition-syntax-info; // sans-definer -- internal. // // Strip off the -definer if it is there and return #f if it isn't. // define method sans-definer (name :: ) => res :: false-or(); let name-str = as(, name); let name-size = name-str.size; if (name-size > 8) block (return) for (i from name-size - 8, char in "-definer") unless (as-lowercase(name-str[i]) == char) return(#f); end unless; end for; as(, copy-sequence(name-str, end: name-size - 8)); end block; else #f; end if; end method sans-definer; // definition-kind{} -- method on imported GF // define method definition-kind (defn :: ) => kind :: ; "body-style define macro"; end method definition-kind; // definition-kind{} -- method on imported GF // define method definition-kind (defn :: ) => kind :: ; "list-style define macro"; end method definition-kind; // definition-kind{} -- method on imported GF // define method definition-kind (defn :: ) => kind :: ; "function macro"; end method definition-kind; // definition-kind{} -- method on imported GF // define method definition-kind (defn :: ) => kind :: ; "statement macro"; end method definition-kind; // make() -- method on imported GF. // define method make (class == , #key module :: , library :: , defmacro :: , source-location :: = defmacro.source-location) => defn :: ; let rules = defmacro.defmacro-main-rule-set.rule-set-rules; let first-rule = rules.first; for (rule in rules) unless (rule.object-class == first-rule.object-class) compiler-fatal-error-location(defmacro, "Inconsistent rule styles"); end unless; end for; let defn-class = select (first-rule by instance?) => fix-define-rules(defmacro); ; => fix-define-rules(defmacro); ; => ; => ; end select; let name = defmacro.defmacro-name; let defn = make(defn-class, name: make(, symbol: name.token-symbol, module: module), source-location: source-location, library: library, main-rule-set: defmacro.defmacro-main-rule-set, auxiliary-rule-sets: defmacro.defmacro-auxiliary-rule-sets); annotate-variables(defn); find-intermediate-words(defn); defn; end method make; // fix-define-rules // fix-define-rules -- internal. // // Extract the modifiers pattern and the name from the head of each rule. // Puke if we can't find the name. // define method fix-define-rules (defmacro :: ) => (); let name = sans-definer(defmacro.defmacro-name.token-symbol); unless (name) compiler-fatal-error("Name of define macro doesn't end with -definer: %s", defmacro.defmacro-name); end unless; for (rule in defmacro.defmacro-main-rule-set.rule-set-rules) let (modifiers-pattern, name, remaining-pattern) = trim-modifiers-pattern(rule.rule-pattern, name); rule.define-rule-modifiers-pattern := modifiers-pattern; rule.rule-pattern := remaining-pattern; end for; end method fix-define-rules; // trim-modifiers-pattern -- internal. // // Grovel though pattern trying to find name. Return the modifiers pattern // (the stuff that preceeds name), the token that matched name, and the // remaining stuff from the pattern. // define generic trim-modifiers-pattern (pattern :: , name :: ) => (modifiers-pattern :: , name :: , remaining-pattern :: ); // trim-modifiers-pattern{} // // Catch-all method for patterns that can't be part of the modifier-pattern. // Puke because it means we couldn't find the name. // define method trim-modifiers-pattern (pattern :: , name :: ) => (modifiers-pattern :: , name :: , remaining-pattern :: ); compiler-fatal-error("Can't find the macro name (%s) in the rule.", name); end method trim-modifiers-pattern; // trim-modifiers-pattern{} // // A semicolon can't occur in the modifiers pattern so the name must be down // the left side, so extract the modifiers pattern from the left. When // returning, wrap the stuff left over on the left with a new semicolon pattern // that concatenates it with the original right hand side. // define method trim-modifiers-pattern (pattern :: , name :: ) => (modifiers-pattern :: , name :: , remaining-pattern :: ); let (modifiers-pattern, name, remaining-pattern) = trim-modifiers-pattern(pattern.pattern-left, name); values(modifiers-pattern, name, make(, left: remaining-pattern, right: pattern.pattern-right, last: pattern.pattern-last?)); end method trim-modifiers-pattern; // trim-modifiers-pattern{} // // A comma can't occur in the modifiers pattern so the name must be down // the left side, so extract the modifiers pattern from the left. When // returning, wrap the stuff left over on the left with a new comma pattern // that concatenates it with the original right hand side. // define method trim-modifiers-pattern (pattern :: , name :: ) => (modifiers-pattern :: , name :: , remaining-pattern :: ); let (modifiers-pattern, name, remaining-pattern) = trim-modifiers-pattern(pattern.pattern-left, name); values(modifiers-pattern, name, make(, left: remaining-pattern, right: pattern.pattern-right, last: pattern.pattern-last?)); end method trim-modifiers-pattern; // trim-modifiers-pattern{} // // Does most of the real work for this generic function. // define method trim-modifiers-pattern (pattern :: , name :: ) => (modifiers-pattern :: , name :: , remaining-pattern :: ); let left = pattern.pattern-left; if (instance?(left, )) // // If the left is a name pattern, then it is either a literal modifier // (i.e. part of the modifier pattern) or it is the name. if (left.pattern-name.token-symbol == name) // // The name matches. values(make(), left.pattern-name, pattern.pattern-right); else // // Recurse into the right hand side and prepend the name pattern onto // the start of the returned modifier pattern. But take care that // we don't make a with an empty right hand side. let (modifiers-pattern, name, remaining-pattern) = trim-modifiers-pattern(pattern.pattern-right, name); values(if (instance?(modifiers-pattern, )) left; else make(, left: left, right: modifiers-pattern, last: ~instance?(modifiers-pattern, )); end if, name, remaining-pattern); end if; elseif (instance?(left, )) // // Otherwise, if the left is a pattern-variable, it is part of the // modifiers pattern. So Recurse into the right hand side and // prepend the name pattern onto the start of the returned // modifier pattern. But take care that we don't make a // with an empty right hand side. let (modifiers-pattern, name, remaining-pattern) = trim-modifiers-pattern(pattern.pattern-right, name); values(if (instance?(modifiers-pattern, )) left; else make(, left: left, right: modifiers-pattern, last: ~instance?(modifiers-pattern, )); end if, name, remaining-pattern); else // // If the left is neither a name nor a pattern-varible, then there is // something wrong with the macro definition. compiler-fatal-error("Can't find the macro name (%s) in the rule.", name); end if; end method trim-modifiers-pattern; // trim-modifiers-pattern{} // // If the name matches, then great. Otherwise, puke because it means that // we couldn't find the name anywhere. We don't have to worry about this // name being part of the modifiers-pattern, because that case is picked off // above us in the method. // define method trim-modifiers-pattern (pattern :: , name :: ) => (modifiers-pattern :: , name :: , remaining-pattern :: ); if (pattern.pattern-name.token-symbol == name) values(make(), pattern.pattern-name, make()); else compiler-fatal-error("Can't find the macro name (%s) in the rule.", name); end if; end method trim-modifiers-pattern; // find-intermediate-words // // This stuff finds all the intermediate words in a macro definition. From // the DRM: // // * Define a body-variable to be a pattern variable that either has a // constraint of body or case-body, or names an auxiliary rule-set // where some left-hand side in that rule-set ends in a body-variable. // This is a least fixed point, so a recursive auxiliary rule-set does // not automatically make its name into a body-variable! Note that an // ellipsis that stands for a pattern variable is a body-variable when // that pattern variable is one. // // * Define an intermediate-variable to be a pattern variable that // either immediately follows a body-variable in a left-hand side, // or appears at the beginning of a left-hand side in an auxiliary // rule-set named by an itermediate-variable. // // * An intermediate word is a name that either immediately follows a // body-variable in a left-hand side, or occurs at the beginning of a // left-hand side in an auxiliary rule-set named by an intermediate- // variable. Intermediate words are not reserved, they are just used // as delimiters during the parsing for a pattern-variable with body // or case-body constraint. // // From that description, we derive the following algorithm. // // The first thing we do is find all the auxiliary rule sets that end // in body variables. Note that when we identify a rule-set as ending // in a body-variable, that means that any variable naming that // rule-set is now a body-variable, so we have to iterate until we find // no more rule sets ending in body variables. // // Then we scan over all the patterns looking for things that follow body // variables. We are interested in two: names (i.e. intermediate words) and // pattern variables (i.e. intermediate-variables). For intermediate- // variables discovered in the manner, if it names some auxiliary rule set, // then all of the patterns in that rule set also follow a body variable. // define method find-intermediate-words (defn :: ) => res :: ; let aux-rule-sets = defn.macro-auxiliary-rule-sets; // // Find the rule sets that are named by body variables. let again? = #t; while (again?) again? := #f; for (rule-set in aux-rule-sets) for (rule in rule-set.rule-set-rules, until: rule-set.rule-set-body-variable?) if (ends-in-body-variable?(rule.rule-pattern, rule-set.rule-set-name, aux-rule-sets)) rule-set.rule-set-body-variable? := #t; again? := #t; end if; end for; end for; end while; // // Find all the intermediate words. let results = #(); for (rule in defn.macro-main-rule-set.rule-set-rules) results := find-intermediate-words-in (rule.rule-pattern, #f, aux-rule-sets, results); end for; for (aux-rule-set in aux-rule-sets) for (rule in aux-rule-set.rule-set-rules) results := find-intermediate-words-in (rule.rule-pattern, aux-rule-set.rule-set-name, aux-rule-sets, results); end for; end for; // // Store 'em away for posterity. defn.macro-intermediate-words := as(, results); end method find-intermediate-words; // ends-in-body-variable? -- internal. // // Return #t iff the pattern ends in a body variable. Return #f iff not. // define generic ends-in-body-variable? (pattern :: , this-rule-set :: false-or(), aux-rule-sets :: ) => res :: ; // ends-in-body-variable?{} // // Catch-all method that returns #f. We explicitly enumerate the exceptions. // define method ends-in-body-variable? (pattern :: , this-rule-set :: false-or(), aux-rule-sets :: ) => res :: ; #f; end method ends-in-body-variable?; // ends-in-body-variable?{} // // A binary pattern ends in a body-variable if the right hand side ends // in a body variable. // define method ends-in-body-variable? (pattern :: , this-rule-set :: false-or(), aux-rule-sets :: ) => res :: ; ends-in-body-variable?(pattern.pattern-right, this-rule-set, aux-rule-sets); end method ends-in-body-variable?; // ends-in-body-variable?{} // // A pattern variable ends in a body-variable if it is a body variable. // And it is a body variable if the constraint is either body or case-body // or it names a rule-set we have previously determined ends in a body // variable. // define method ends-in-body-variable? (patvar :: , this-rule-set :: false-or(), aux-rule-sets :: ) => res :: ; let constraint = patvar.patvar-constraint; if (constraint == #"body" | constraint == #"case-body") #t; else let name = patvar.patvar-name | this-rule-set; if (name) let aux-rule-set = find-aux-rule-set(name, aux-rule-sets); aux-rule-set & aux-rule-set.rule-set-body-variable?; else #f; end if; end if; end method ends-in-body-variable?; // ends-in-body-variable?{} // // A typed-variable-pattern ends in a body variable if the type pattern // ends in a body variable. That would be a rather strange pattern, but // the description of intermediate words doesn't disallow it, so we have // to support it. // define method ends-in-body-variable? (pattern :: , this-rule-set :: false-or(), aux-rule-sets :: ) => res :: ; ends-in-body-variable? (pattern.variable-type-pattern, this-rule-set, aux-rule-sets); end method ends-in-body-variable?; // ends-in-body-variable?{} // // A typed-variable-pattern ends in a body variable if the type pattern // ends in a body variable. That would be a rather strange pattern, but // the description of intermediate words doesn't disallow it, so we have // to support it. // define method ends-in-body-variable? (pattern :: , this-rule-set :: false-or(), aux-rule-sets :: ) => res :: ; ends-in-body-variable? (pattern.bindings-value-pattern, this-rule-set, aux-rule-sets); end method ends-in-body-variable?; // find-aux-rule-set -- internal. // // Finds the auxiliary rule set named by symbol. This function is also used // way down below in do-replacement, but this is as good of a place as any // to define it. // define method find-aux-rule-set (name :: , aux-rule-sets :: ) => res :: false-or(); block (return) for (aux-rule-set in aux-rule-sets) if (aux-rule-set.rule-set-name == name) return(aux-rule-set); end if; end for; #f; end block; end method find-aux-rule-set; // find-intermediate-words-in -- internal. // // Find places in pattern where something follows a body-variable and call // find-intermediate-words-at-start on that something. // define generic find-intermediate-words-in (pattern :: , this-rule-set :: false-or(), aux-rule-sets :: , results :: ) => res :: ; // find-intermediate-words-in{} // // Catch-all method for patterns that either can't have two adjacent pattern // variables or a whole sub-pattern. We explicitly enumerate the exceptions // below. // define method find-intermediate-words-in (pattern :: , this-rule-set :: false-or(), aux-rule-sets :: , results :: ) => res :: ; results; end method find-intermediate-words-in; // find-intermediate-words-in{} // // Descend into the two halves. This method assumes that there is a separator // between the two halves so that it doesn't matter what the first half ends // with. The exception () is delt with below. // define method find-intermediate-words-in (pattern :: , this-rule-set :: false-or(), aux-rule-sets :: , results :: ) => res :: ; find-intermediate-words-in (pattern.pattern-right, this-rule-set, aux-rule-sets, find-intermediate-words-in (pattern.pattern-left, this-rule-set, aux-rule-sets, results)); end method find-intermediate-words-in; // find-intermediate-words-in{} // // The two sub-patterns are adjacent, so first check to see if the left ends // in a body variable. If so, find the intermediate words at the start of // the right sub-pattern. // // And in any case, descend into the two subpatterns just like the // method above. // define method find-intermediate-words-in (pattern :: , this-rule-set :: false-or(), aux-rule-sets :: , results :: ) => res :: ; let left = pattern.pattern-left; let right = pattern.pattern-right; find-intermediate-words-in (right, this-rule-set, aux-rule-sets, find-intermediate-words-in (left, this-rule-set, aux-rule-sets, if (ends-in-body-variable?(left, this-rule-set, aux-rule-sets)) find-intermediate-words-at-start (right, this-rule-set, aux-rule-sets, results); else results; end if)); end method find-intermediate-words-in; // find-intermediate-words-in{} // // Descend into the guts pattern. // define method find-intermediate-words-in (pattern :: , this-rule-set :: false-or(), aux-rule-sets :: , results :: ) => res :: ; find-intermediate-words-in (pattern.pattern-guts, this-rule-set, aux-rule-sets, results); end method find-intermediate-words-in; // find-intermediate-words-at-start -- internal. // // Called whenever we have determined that some pattern ``follows'' a body // variable. A pattern can follow a body variable by physically being // next to it in the pattern, or it can be the pattern for some rule in // an auxiliary rule set named by a pattern variable that follows a body // variable. // define generic find-intermediate-words-at-start (pattern :: , this-rule-set :: false-or(), aux-rule-sets :: , results :: ) => res :: ; // find-intermediate-words-at-start{} // // Catch all method for patterns that can't start with intermediate words // or variables. The exceptions are explicitly enumerated below. // define method find-intermediate-words-at-start (pattern :: , this-rule-set :: false-or(), aux-rule-sets :: , results :: ) => res :: ; results; end method find-intermediate-words-at-start; // find-intermediate-words-at-start{} // // Find the intermediate words at the start of the left half. // define method find-intermediate-words-at-start (pattern :: , this-rule-set :: false-or(), aux-rule-sets :: , results :: ) => res :: ; find-intermediate-words-at-start (pattern.pattern-left, this-rule-set, aux-rule-sets, results); end method find-intermediate-words-at-start; // find-intermediate-words-at-start{} // // Hey, we found one! If we don't already know about it, add it to the // results. // define method find-intermediate-words-at-start (pattern :: , this-rule-set :: false-or(), aux-rule-sets :: , results :: ) => res :: ; let sym = pattern.pattern-name.token-symbol; if (member?(sym, results)) results; else pair(sym, results); end if; end method find-intermediate-words-at-start; // find-intermediate-words-at-start{} // // Okay, we have an intermediate variable. Check the start of the patterns // for the corresponding auxiliary-rule-set (if there is one). // // We set a flag in the rule set before recursing so that we will eventually // terminate. Otherwise a right-recursive rule set would cause use to hang. // For as long as we had stack space, that is. // define method find-intermediate-words-at-start (pattern :: , this-rule-set :: false-or(), aux-rule-sets :: , results :: ) => res :: ; let name = pattern.patvar-name | this-rule-set; if (name) let aux-rule-set = find-aux-rule-set(name, aux-rule-sets); if (aux-rule-set & ~aux-rule-set.rule-set-processed-intermediate-words?) aux-rule-set.rule-set-processed-intermediate-words? := #t; for (rule in aux-rule-set.rule-set-rules) results := find-intermediate-words-at-start (rule.rule-pattern, this-rule-set, aux-rule-sets, results); end for; end if; end if; results; end method find-intermediate-words-at-start; // find-intermediate-words-at-start{} // // We have to deal with this case even though it is rather strange. Just // recurse on the name pattern. // define method find-intermediate-words-at-start (pattern :: , this-rule-set :: false-or(), aux-rule-sets :: , results :: ) => res :: ; find-intermediate-words-at-start (pattern.variable-name-pattern, this-rule-set, aux-rule-sets, results); end method find-intermediate-words-at-start; // find-intermediate-words-at-start{} // // We have to deal with this case even though it is rather strange. Just // recurse on the variable pattern. // define method find-intermediate-words-at-start (pattern :: , this-rule-set :: false-or(), aux-rule-sets :: , results :: ) => res :: ; find-intermediate-words-at-start (pattern.bindings-variables-pattern, this-rule-set, aux-rule-sets, results); end method find-intermediate-words-at-start; // annotate-variables -- internal. // // We make some annotations on variables when the macro is defined so that // we don't have to expensively recompute these values each time the macro // is used. The two annotations we currently make are ``at-end?'' for wildcard // variables that must consume the entire fragment they are matched against // and defaulting the constraint to wildcard for variables that name // auxiliary rule sets. // define method annotate-variables (defn :: ) => (); let aux-rule-sets = defn.macro-auxiliary-rule-sets; for (rule in defn.macro-main-rule-set.rule-set-rules) annotate-variables-rule(rule, aux-rule-sets); end for; for (rule-set in aux-rule-sets) for (rule in rule-set.rule-set-rules) annotate-variables-rule(rule, aux-rule-sets); end for; end for; end method annotate-variables; // annotate-variables-rule // // Annotate a rule. // define generic annotate-variables-rule (within :: , aux-rule-sets :: ) => (); // annotate-variables-rule{} // // By default, just annotate the rule's pattern. // define method annotate-variables-rule (rule :: , aux-rule-sets :: ) => (); annotate-variables-pattern(rule.rule-pattern, aux-rule-sets, #t); end method annotate-variables-rule; // annotate-variables-rule{} // // But for define-rules, we want to annotate the modifiers pattern also. // define method annotate-variables-rule (rule :: , aux-rule-sets :: ) => (); annotate-variables-pattern (rule.define-rule-modifiers-pattern, aux-rule-sets, #t); annotate-variables-pattern(rule.rule-pattern, aux-rule-sets, #t); end method annotate-variables-rule; // annotate-variables-pattern // // Annotate the variables in some pattern. // define generic annotate-variables-pattern (within :: , aux-rule-sets :: , at-end? :: ) => (); // annotate-variables-pattern{} // // Catch-all method for patterns that contain no variables. Exceptions below. // define method annotate-variables-pattern (pattern :: , aux-rule-sets :: , at-end? :: ) => (); end method annotate-variables-pattern; // annotate-variables-pattern{type-union(, )} // // Annotate the two sub-patterns. The ; or , form hard delimiters that // wildcards can't pass, pass at-end down as #t. // define method annotate-variables-pattern (pattern :: type-union(, ), aux-rule-sets :: , at-end? :: ) => (); assert(at-end?); annotate-variables-pattern(pattern.pattern-left, aux-rule-sets, #t); annotate-variables-pattern(pattern.pattern-right, aux-rule-sets, #t); end method annotate-variables-pattern; // annotate-variables-pattern{} // // Annotate the two sub-patterns. // define method annotate-variables-pattern (pattern :: , aux-rule-sets :: , at-end? :: ) => (); assert(at-end?); annotate-variables-pattern(pattern.pattern-left, aux-rule-sets, #f); annotate-variables-pattern(pattern.pattern-right, aux-rule-sets, #t); end method annotate-variables-pattern; // annotate-variables-pattern{} // // Annotate the guts. // define method annotate-variables-pattern (pattern :: , aux-rule-sets :: , at-end? :: ) => (); annotate-variables-pattern(pattern.pattern-guts, aux-rule-sets, #t); end method annotate-variables-pattern; // annotate-variables-pattern{} // // Annotate the two pattern variables. Note: the parser handles figuring // out how much this pattern should match, so any contained wildcards must // consume everything they are given. // define method annotate-variables-pattern (pattern :: , aux-rule-sets :: , at-end? :: ) => (); annotate-variables-pattern(pattern.variable-name-pattern, aux-rule-sets, #t); annotate-variables-pattern(pattern.variable-type-pattern, aux-rule-sets, #t); end method annotate-variables-pattern; // annotate-variables-pattern{} // // Annotate the two pattern variables. Note: the parser handles figuring // out how much this pattern should match, so any contained wildcards must // consume everything they are given. // define method annotate-variables-pattern (pattern :: , aux-rule-sets :: , at-end? :: ) => (); annotate-variables-pattern (pattern.bindings-variables-pattern, aux-rule-sets, #t); annotate-variables-pattern (pattern.bindings-value-pattern, aux-rule-sets, #t); end method annotate-variables-pattern; // annotate-variables-pattern{} // // Annotate the variable. Set the at-end flag and default the constraint // to wildcard if it names an auxiliary rule. // define method annotate-variables-pattern (pattern :: , aux-rule-sets :: , at-end? :: ) => (); pattern.patvar-at-end? := at-end?; if (pattern.patvar-constraint == #f & find-aux-rule-set(pattern.patvar-name, aux-rule-sets)) pattern.patvar-constraint := #"*"; end if; end method annotate-variables-pattern; // annotate-variables-pattern{} // // Annotate the pattern variables buried inside this property list pattern. // define method annotate-variables-pattern (pattern :: , aux-rule-sets :: , at-end? :: ) => (); if (pattern.plistpat-rest) annotate-variables-pattern(pattern.plistpat-rest, aux-rule-sets, #t); end if; if (pattern.plistpat-keys) for (key in pattern.plistpat-keys) annotate-variables-pattern(key, aux-rule-sets, #t); end for; end if; end method annotate-variables-pattern; // Identify variables referenced in expansion templates. // identify-variable-references -- exported. // // Scans all of the expansion templates in a macro definition to insure // that all of the variables referenced in the template are exported // into the dumped library. // define method identify-variable-references (defn :: ) => (); let aux-rule-sets = defn.macro-auxiliary-rule-sets; for (rule in defn.macro-main-rule-set.rule-set-rules) identify-variable-references-template(rule.rule-template, defn.defn-name); end for; for (rule-set in aux-rule-sets) for (rule in rule-set.rule-set-rules) identify-variable-references-template(rule.rule-template, defn.defn-name); end for; end for; end method identify-variable-references; // identify-variable-references-template -- internal // define generic identify-variable-references-template (template ::