2 n@(nodes Node traits) disable
3 "Expands a textual program element into nothing, effectively commenting it out.
4 Of course it does nothing to actual run-time configuration of the thing
6 [n surroundings Literal Nil].
8 n@(nodes Literal traits) er
9 "Takes a piece of Symbol/String literal syntax and expands into a block which
10 can apply it to the number of arguments the Symbol can take as a selector."
13 (selector is: Symbol) ifFalse: [error: '`er may only use a Symbol literal.'].
14 block: n surroundings Block new.
15 block statements: {n surroundings Message sending: selector to:
16 ([n surroundings LoadVariable from: block addInputVariable] reader next: selector arity)}.
20 "This is the tricky moment at which we activate the special macros.
21 Above here, they are unavailable, and after here, these definitions are not sound."
22 n@(nodes Node traits) := val &environment: env [n setTo: val &environment: env].
23 n@(nodes Node traits) ::= val &environment: env [n bindTo: val &environment: env].
25 n@(nodes Pattern traits) er &environment: env
27 block := n surroundings Block new.
28 message := n message copy.
29 message arguments infect:
31 ({n surroundings Placeholder. n surroundings ImplicitArgument} includes: arg)
32 ifTrue: [n surroundings LoadVariable from: block addInputVariable]
34 block statements := {message}.
38 expr@(nodes Node traits) er &environment: method
39 [(expr surroundings Literal for: (expr evaluateIn: method)) er].
41 n@(nodes Literal traits) erOnKey: attribute@(nodes Literal traits)
42 "Variant on the `er macro which calls the selector on the results of sending
43 the attribute selector to each argument."
44 [| selector attribSelector block |
46 attribSelector := attribute value.
47 (selector is: Symbol) /\ [attribSelector is: Symbol]
48 ifFalse: [error: '`erOnKey: may only use a Symbol literal.'].
49 n surroundings Block new `>>
51 statements := {n surroundings Message sending: selector to:
52 ([n surroundings UnaryMessage sending: attribSelector to:
53 {n surroundings LoadVariable from: block addInputVariable}]
54 reader next: selector arity)}. ]
57 def@(nodes MethodDefinition traits) commutatively
58 "Expands into a sequence of the original meethod and another having the
59 same body with the roles reversed."
61 def roles = def roles reversed
67 roles := newDef roles reversed.
68 inputVariables := newDef inputVariables reversed.
72 def@(nodes MethodDefinition traits) alias: newName
74 {def. def deepCopy `>> [selector := newName evaluate. ]} parenthesize
77 expr@(nodes Node traits) suspend
78 "Expands into a suspension-creating expression for a Block."
79 [`((nodes Block newFor: expr) `unquote suspend)].
81 expr@(nodes Node traits) cache &environment: method
82 "An environment-altering macro that performs no transformation on the argument:
83 It creates a new local binding with the result of evaluating the expression
84 when the surrounding method is run. All other uses of an equivalent (=)
85 expression in the same or deeper lexical context are replaced with an access
87 [| newVar loadExpr macroCall |
88 "Do nothing outside of a method."
89 method ifNil: [warn: 'Cannot cache outside of a method'. ^ expr].
90 "Make the new local, uniquely named."
91 newVar := method addVariable.
92 "Replace any equal expression (except this one) with a load of the variable."
93 loadExpr := newVar load.
94 macroCall := expr `cache `quote.
96 [| :node | node = expr \/ [node = macroCall]
97 ifTrue: [loadExpr] ifFalse: [node]].
98 "Expand into a store expression."
102 expr@(nodes Node traits) cacheAs: varName &environment: method
103 "An environment-altering macro that performs no transformation on the argument:
104 It creates a new local binding with the result of evaluating the expression
105 when the surrounding method is run. All other uses of an equivalent (=)
106 expression in the same or deeper lexical context are replaced with an access
108 [| newVar expressionsToReplace loadExpr |
109 "Do nothing outside of a method."
110 method ifNil: [warn: 'Cannot cache outside of a method'. ^ expr].
111 "Make the new local, uniquely named."
112 newVar := method addVariableNamed: varName value intern.
113 "Replace any equal expression (except this one) with a load of the variable."
114 loadExpr := newVar load.
115 expressionsToReplace := {
118 nodes UnaryMessage sending: varName value intern to: {nodes ImplicitArgument}
121 [| :node | (expressionsToReplace includes: node)
122 ifTrue: [loadExpr] ifFalse: [node]].
123 "Expand into a store expression."
127 expr@(nodes Node traits) rememberAs: varName &environment: method
128 "An environment-altering macro that performs no transformation on the argument:
129 It creates a new local binding with the result of evaluating the expression
130 when the surrounding method is run. All other uses of an equivalent (=)
131 expression in the same or deeper lexical context are replaced with an access
133 [| newVar expressionsToReplace loadExpr |
134 "Do nothing outside of a method."
135 method ifNil: [warn: 'Cannot cache outside of a method'. ^ expr].
136 "Make the new local, uniquely named."
137 newVar := method addVariableNamed: varName value intern.
138 "Replace any equal expression (except this one) with a load of the variable."
139 loadExpr := newVar load.
140 expressionsToReplace := {
141 nodes UnaryMessage sending: varName value intern to: {nodes ImplicitArgument}
144 [| :node | (expressionsToReplace includes: node)
145 ifTrue: [loadExpr] ifFalse: [node]].
146 "Expand into a store expression."
150 x@(Root traits) compareAndHashUsingSlots: relevantSlotNames
151 "Defines methods for #= and #hash for the simple case of just recursively
152 calling them on each value of the slots with the given names.
153 NOTE: This is not a macro but relies on the macro facilities."
156 isEmpty ifTrue: [error: 'Some slot names must be specified.'].
158 xLiteral := nodes Literal for: x.
161 {nodes MethodDefinition of: #= on: {xLiteral. xLiteral}
162 from: #{#obj1. #obj2} to:
164 "relevantSlotNames collect:
166 (obj1 `load atSlotNamed: slotName `unquote)
167 = (obj2 `load atSlotNamed: slotName `unquote)]"
168 relevantSlotNames allButFirst
170 `((obj1 `load atSlotNamed: relevantSlotNames first `unquote)
171 = (obj2 `load atSlotNamed: relevantSlotNames first `unquote))
172 into: [| :expr :slotName |
174 [(obj1 `load atSlotNamed: slotName `unquote)
175 = (obj2 `load atSlotNamed: slotName `unquote)])]]
176 &linkVariables: True.
177 nodes MethodDefinition of: #hash on: {xLiteral}
180 relevantSlotNames allButFirst
182 `((obj `load atSlotNamed: relevantSlotNames first `unquote) hash)
183 into: [| :hash :slotName |
184 `(hash `unquote bitXor:
185 (obj `load atSlotNamed: slotName `unquote) hash)]]
186 &linkVariables: True}.
190 x@(Root traits) extendCompareAndHashUsingSlots: relevantSlotNames
191 "Defines methods for #= and #hash for the simple case of just recursively
192 calling them on each value of the slots with the given names; uses a resend
193 to re-use the more abstract definition.
194 NOTE: This is not a macro but relies on the macro facilities."
195 "FIXME: The resend results in a compilation error."
198 isEmpty ifTrue: [error: 'Some slot names must be specified.'].
200 xLiteral := nodes Literal for: x.
203 {nodes MethodDefinition of: #= on: {xLiteral. xLiteral}
204 from: #{#obj1. #obj2} to:
206 relevantSlotNames inject: nodes Resend into:
209 [(obj1 `load atSlotNamed: slotName `unquote)
210 = (obj2 `load atSlotNamed: slotName `unquote)])]]
211 &linkVariables: True.
212 nodes MethodDefinition of: #hash on: {xLiteral}
215 relevantSlotNames inject: nodes Resend into:
217 `(hash `unquote bitXor:
218 (obj `load atSlotNamed: slotName `unquote) hash)]]
219 &linkVariables: True}.
223 "Bootstrap-time fix-ups:" (
224 ExternalResource traits compareAndHashUsingSlots: #{#locator}.
225 File Locator traits compareAndHashUsingSlots: #{#name. #path. #version. #fileType}.
226 File RelativeLocator traits compareAndHashUsingSlots: #{#basePath. #path. #name. #fileType. #version}.
227 Directory traits compareAndHashUsingSlots: #{#locator}.
228 nodes Annotation traits compareAndHashUsingSlots: #{#value}.
229 nodes Comment traits compareAndHashUsingSlots: #{#comment. #value}.
230 nodes OptionalKeywords traits compareAndHashUsingSlots: #{#message. #keywords. #arguments}.
231 nodes Literal traits compareAndHashUsingSlots: #{#value}.
232 nodes Namespace traits compareAndHashUsingSlots: #{#namespace}.
233 nodes Variable traits compareAndHashUsingSlots: #{#name. #scope}.
234 nodes LoadVariable traits compareAndHashUsingSlots: #{#variable}.
235 nodes StoreVariable traits compareAndHashUsingSlots: #{#variable. #value}.
236 CompiledMethod traits compareAndHashUsingSlots: #{#literals. #selectors. #code}.
237 Types Not traits compareAndHashUsingSlots: #{#argument}.
238 Types Member traits compareAndHashUsingSlots: #{#elements}.
239 Types Block traits compareAndHashUsingSlots: #{#resultType. #argumentTypes}.
240 Types Range traits compareAndHashUsingSlots: #{#type. #start. #end}.
241 Types Array traits compareAndHashUsingSlots: #{#type}.
244 x@(nodes Node traits) swapWith: y@(nodes Node traits) &environment: env
246 TODO: 'Determine how to perform a generic variable swap outside of a Method context without a global.'
249 x@(nodes LoadVariable traits) swapWith: y@(nodes LoadVariable traits) &environment: env
250 "A macro that expands into simple code swapping the values of two variables
251 in the current scope."
253 env ifNil: [error: 'Cannot swap variables outside of a method'].
254 tmpVar := env addVariable.
255 {tmpVar store: x variable load.
256 x variable store: y variable load.
257 y variable store: tmpVar load} parenthesize
260 _@(nodes Node traits) matchAppend: keyword with: val
261 "Encapsulates the idiom of taking a message and adding a keyword to denote an
262 additional yet non-optional parameter.
263 E.g. foo -> fooWith: -> fooWith:with:, fooWith:with:with: ..."
264 "TODO: Provide a way to call methods with syntactic *rest: parameters instead?"
267 message@(nodes UnaryMessage traits) matchAppend: keyword with: val
269 nodes KeywordMessage sending: (intern: message selector name ; keyword name capitalized)
270 to: {message arguments first. val}
273 message@(nodes KeywordMessage traits) matchAppend: keyword with: val
275 nodes KeywordMessage sending: (intern: message selector name ; keyword name)
276 to: message arguments ; {val}
279 paren@(nodes Parenthesis traits) matchAppend: keyword with: val
280 [paren statements first matchAppend: keyword with: val].
282 collection@(nodes Node traits) match: elements@(nodes Node traits) with: message@(nodes Node traits)
283 &otherwise: failBlock &append: keyword &environment: env
285 env ifNil: [error: 'Cannot match a collection outside of a method'].
288 ifNotNil: [keyword evaluate].
289 elements := elements evaluate sort.
290 tmpVar := env addVariable.
292 ((elements first upTo: elements last) collect:
294 `((size + 1) `literal ->
295 (elements inject: message into: [| :message :element |
298 [message matchAppend: keyword with: `(tmpVar `load at: element `literal)]
300 [message]]) `block)]).
303 [`((tmpVar `store: collection) size caseOf: cases `array)]
305 [`((tmpVar `store: collection) size caseOf: cases `array otherwise: failBlock `unquote)]