21.7 Auxiliary macros
One difficulty with the aircraft macro that we defined in Section 21.5 is this: suppose that we want each flight object to know the type of equipment used, rather than our having to look up the type in the aircraft registry. What looks like the obvious approach does not work:
define macro aircraft-definer
{ define aircraft ?identifier:name (?type:name) ?flights end }
=> { register-aircraft(make("<" ## ?type ## ">", id: ?#"identifier"));
register-flights(?#"identifier", ?flights) }
flights:
{ }
=> { }
{ ?flight; ... }
=> { ?flight, ... }
flight:
{ }
=> { }
{ flight ?id:name, #rest ?options:expression }
=> { make(<flight>, equipment: ?"type", id: ?#"id", ?options) }
end macro aircraft-definer;
When we are processing the flight auxiliary rules, we would like to be able to reference the pattern variable ?type (coercing it to a string) from the main rules, but it is not in scope — it is inaccessible to the auxiliary rules. We could have register-flights set the equipment slot after the flight is created, but we would prefer to initialize the slot at the time we create the <flight> object. There is a workaround, an auxiliary macro:
define macro aircraft-definer
{ define aircraft ?identifier:name (?type:name) ?flights:* end }
=> { register-aircraft (make("<" ## ?type ## ">", id: ?#"identifier"));
define flights (?#"identifier", ?"type")
?flights
end }
end macro aircraft-definer;
define macro flights-definer
{ define flights (?craft:name, ?equipment:name) end }
=> { }
{ define flights (?craft:name, ?equipment:name) ?flight ; ?more:* end }
=> { register-flights
(?craft, make(<flight>, equipment: ?equipment, ?flight)) ;
define flights (?craft, ?equipment) ?more end }
flight:
{ }
=> { }
{ flight ?id:name, #rest ?options:expression }
=> { id: ?#"id", ?options }
end macro flights-definer;
Here, we have essentially broken out the work that used to be done by the auxiliary rule flights into a separate definition macro. Where flights used points of ellipsis to walk over each flight, the definition macro uses a wildcard constraint ?more:*, explicitly calling itself again (that is, the macro appears in the substitution, and will be expanded again), as long as there are more flights to be processed.
Here is an example use of the flights-definer macro:
define aircraft UA4906H (DC10) flight UA11 from: #"BOS", to: #"SFO"; flight UA12 from: #"SFO", to: #"BOS"; end aircraft UA4906H;
Expanding that code would result in the following:
register-aircraft (make(<DC10>, #"UA4096H"));
register-flights (#"UA4096H",
make(<flight>, equipment: "DC10",
id: #"UA11" from: #"BOS", to: #"SFO");
register-flights (#"UA4096H",
make(<flight>, equipment: "DC10",
id: #"UA12" from: #"SFO", to: #"BOS");
(Note that this example is a hypothetical one used to illustrate macro expansion. The define aircraft statement cannot be compiled in the airport example.)




