1
|
1 |
"{ Package: 'stx:goodies/smaCC' }"
|
|
2 |
|
|
3 |
"{ NameSpace: SmaCC }"
|
|
4 |
|
|
5 |
Object subclass:#SmaCCNode
|
|
6 |
instanceVariableNames:'transitions action id'
|
|
7 |
classVariableNames:'NextId'
|
|
8 |
poolDictionaries:''
|
|
9 |
category:'SmaCC-Scanner Generator'
|
|
10 |
!
|
|
11 |
|
|
12 |
SmaCCNode comment:'SmaCCNode is a node in a directed graph.
|
|
13 |
|
|
14 |
Instance Variables:
|
|
15 |
action <SequenceableCollection> a collection of integers or a symbol. This contains the action to be performed when we match and can''t find a longer match.
|
|
16 |
id <Integer> a unique number that allows us to sort the nodes
|
|
17 |
transitions <Collection of: SmaCCEdge> our transitions
|
|
18 |
|
|
19 |
'
|
|
20 |
!
|
|
21 |
|
|
22 |
|
|
23 |
!SmaCCNode class methodsFor:'instance creation'!
|
|
24 |
|
|
25 |
new
|
|
26 |
^(super new)
|
|
27 |
initialize;
|
|
28 |
yourself
|
|
29 |
! !
|
|
30 |
|
|
31 |
!SmaCCNode class methodsFor:'class initialization'!
|
|
32 |
|
|
33 |
initialize
|
|
34 |
NextId := 0
|
|
35 |
! !
|
|
36 |
|
|
37 |
!SmaCCNode methodsFor:'compiling'!
|
|
38 |
|
|
39 |
asStatement: methodMap usingSelectorMap: aDictionary forClass: aClass
|
|
40 |
| stream |
|
|
41 |
stream := WriteStream on: (String new: 128).
|
|
42 |
self hasSimpleLoop ifTrue: [stream nextPut: $[].
|
|
43 |
self writeMatchingCodeOn: stream usingSelectorMap: aDictionary.
|
|
44 |
(self sortedTransitionsFor: aClass) do:
|
|
45 |
[:each |
|
|
46 |
each to == self
|
|
47 |
ifTrue:
|
|
48 |
[stream
|
|
49 |
nextPutAll: each expression;
|
|
50 |
nextPut: $];
|
|
51 |
nextPutAll: ' whileTrue.';
|
|
52 |
cr]
|
|
53 |
ifFalse:
|
|
54 |
[stream
|
|
55 |
nextPutAll: each expression;
|
|
56 |
nextPutAll: ' ifTrue: ['.
|
|
57 |
stream
|
|
58 |
nextPutAll: (methodMap at: each to
|
|
59 |
ifAbsentPut:
|
|
60 |
[each to
|
|
61 |
asStatement: methodMap
|
|
62 |
usingSelectorMap: aDictionary
|
|
63 |
forClass: aClass]);
|
|
64 |
nextPutAll: '].';
|
|
65 |
cr]].
|
|
66 |
(transitions notEmpty or: [action isNil])
|
|
67 |
ifTrue:
|
|
68 |
[stream
|
|
69 |
nextPutAll: '^self reportLastMatch';
|
|
70 |
cr].
|
|
71 |
^stream contents
|
|
72 |
!
|
|
73 |
|
|
74 |
compileInto: aClass usingSelectorMap: aDictionary
|
|
75 |
| methodNodes methodMap index |
|
|
76 |
methodNodes := self statesToMakeIntoMethods.
|
|
77 |
methodMap := self methodNameMapFor: methodNodes.
|
|
78 |
index := 0.
|
|
79 |
methodNodes do:
|
|
80 |
[:each |
|
|
81 |
| stream |
|
|
82 |
stream := WriteStream on: (String new: 1000).
|
|
83 |
stream
|
|
84 |
nextPutAll: (each == self
|
|
85 |
ifTrue: ['scanForToken']
|
|
86 |
ifFalse: ['scan' , (index := index + 1) printString]);
|
|
87 |
cr.
|
|
88 |
stream nextPutAll: (each
|
|
89 |
asStatement: methodMap
|
|
90 |
usingSelectorMap: aDictionary
|
|
91 |
forClass: aClass).
|
|
92 |
aClass
|
|
93 |
compile: (self optimizedParseTreeFor: stream contents) formattedCode
|
|
94 |
classified: #'generated-scanner']
|
|
95 |
!
|
|
96 |
|
|
97 |
methodNameMapFor: methodNodes
|
|
98 |
| index methodMap |
|
|
99 |
methodMap := IdentityDictionary new.
|
|
100 |
index := 0.
|
|
101 |
methodNodes do:
|
|
102 |
[:value |
|
|
103 |
methodMap at: value
|
|
104 |
put: (value == self
|
|
105 |
ifTrue: ['^self scanForToken']
|
|
106 |
ifFalse: ['^self scan' , (index := index + 1) printString])].
|
|
107 |
^methodMap
|
|
108 |
!
|
|
109 |
|
|
110 |
needsSeparateMethod
|
|
111 |
^self allStates size > 20
|
|
112 |
!
|
|
113 |
|
|
114 |
optimizationRewriter
|
|
115 |
| rewriter |
|
|
116 |
rewriter := ParseTreeRewriter new.
|
|
117 |
rewriter
|
|
118 |
replace: 'Core.Character' with: 'Character';
|
|
119 |
replace: '`@.Stmts1.
|
|
120 |
[`@.Stmts2.
|
|
121 |
currentCharacter ~~ `#l] whileTrue.
|
|
122 |
currentCharacter == `#l ifTrue: [`@.Stmts3].
|
|
123 |
`@.Stmts4'
|
|
124 |
with: '`@.Stmts1.
|
|
125 |
[`@.Stmts2.
|
|
126 |
currentCharacter ~~ `#l] whileTrue.
|
|
127 |
`@.Stmts3';
|
|
128 |
replaceMethod: '`name
|
|
129 |
`@.Stmts1.
|
|
130 |
`@a ifTrue: [`@.Stmts2.
|
|
131 |
^self `name].
|
|
132 |
`@.Stmts3'
|
|
133 |
with: '`name
|
|
134 |
[`@.Stmts1.
|
|
135 |
`@a] whileTrue: [`@.Stmts2].
|
|
136 |
`@.Stmts3';
|
|
137 |
replace: '`@.Stmts1.
|
|
138 |
currentCharacter isLiteral ifTrue: [`@.Stmts2].
|
|
139 |
`@.Stmts3'
|
|
140 |
with: '`@.Stmts1.
|
|
141 |
`@.Stmts2';
|
|
142 |
" replace: '``@.Stmts1.
|
|
143 |
(`@a ifTrue: [``@.Stmts2]) `{:node :dictionary | | index myStatements |
|
|
144 |
index := node parent statements indexOf: node.
|
|
145 |
myStatements := node parent statements.
|
|
146 |
dictionary at: #size put: ``@.Stmts2 size - (myStatements size - index).
|
|
147 |
index ~~ myStatements size and: [``@.Stmts2 size >= (myStatements size - index) and: [
|
|
148 |
(index + 1 to: myStatements size) allSatisfy: [:each |
|
|
149 |
(myStatements at: each) = (``@.Stmts2 at: ``@.Stmts2 size - (myStatements size - each))]]]
|
|
150 |
}.
|
|
151 |
``@.Stmts3'
|
|
152 |
with: '``@.Stmts1.
|
|
153 |
`{:dictionary | RBMessageNode receiver: `@a selector: #ifTrue: arguments: (Array with: (RBBlockNode body: (RBSequenceNode statements: (``@.Stmts2 copyFrom: 1 to: (dictionary at: #size)))))}.
|
|
154 |
``@.Stmts3';"
|
|
155 |
replace: '`@.Stmts1.
|
|
156 |
`.Stmt.
|
|
157 |
`@.Stmts.
|
|
158 |
`@a ifTrue: [self step. `.Stmt. `@.Stmts].
|
|
159 |
`@.Stmts2'
|
|
160 |
with: '`@.Stmts1.
|
|
161 |
`@a ifTrue: [self step].
|
|
162 |
`.Stmt.
|
|
163 |
`@.Stmts.
|
|
164 |
`@.Stmts2'.
|
|
165 |
^rewriter
|
|
166 |
!
|
|
167 |
|
|
168 |
optimizedParseTreeFor: aString
|
|
169 |
| tree rewriter |
|
|
170 |
tree := RBParser parseMethod: aString.
|
|
171 |
rewriter := self optimizationRewriter.
|
|
172 |
[rewriter executeTree: tree] whileTrue: [tree := rewriter tree].
|
|
173 |
^tree
|
|
174 |
!
|
|
175 |
|
|
176 |
sortedTransitionsFor: aClass
|
|
177 |
| frequencies |
|
|
178 |
frequencies := (aClass realClass ifNil: [SmaCCScanner]) frequencyTable.
|
|
179 |
^transitions asSortedCollection:
|
|
180 |
[:a :b |
|
|
181 |
| aFrequency bFrequency |
|
|
182 |
aFrequency := a characters inject: 0
|
|
183 |
into: [:sum :each | sum + (frequencies at: each asInteger \\ frequencies size + 1)].
|
|
184 |
bFrequency := b characters inject: 0
|
|
185 |
into: [:sum :each | sum + (frequencies at: each asInteger \\ frequencies size + 1)].
|
|
186 |
aFrequency > bFrequency
|
|
187 |
or: [aFrequency = bFrequency and: [a characters first < b characters first]]]
|
|
188 |
!
|
|
189 |
|
|
190 |
statesToMakeIntoMethods
|
|
191 |
| allStates incoming |
|
|
192 |
allStates := self allStates.
|
|
193 |
incoming := Dictionary new.
|
|
194 |
allStates do:
|
|
195 |
[:each |
|
|
196 |
each transitions do:
|
|
197 |
[:edge |
|
|
198 |
each ~~ edge to
|
|
199 |
ifTrue: [(incoming at: edge to ifAbsentPut: [Set new]) add: each]]].
|
|
200 |
^(allStates asOrderedCollection select:
|
|
201 |
[:each |
|
|
202 |
self == each or:
|
|
203 |
[each isTerminalNode not and:
|
|
204 |
[(incoming at: each ifAbsent: [#()]) size > 1
|
|
205 |
or: [each needsSeparateMethod]]]])
|
|
206 |
asSortedCollection: [:a :b | a id < b id]
|
|
207 |
!
|
|
208 |
|
|
209 |
writeMatchingCodeOn: aStream usingSelectorMap: aDictionary
|
|
210 |
| matchedItem |
|
|
211 |
action size > 0
|
|
212 |
ifTrue:
|
|
213 |
[matchedItem := aDictionary at: action first ifAbsent: [action asArray].
|
|
214 |
aStream nextPutAll: (transitions isEmpty
|
|
215 |
ifTrue: ['^self recordAndReportMatch:']
|
|
216 |
ifFalse: ['self recordMatch: ']).
|
|
217 |
matchedItem isSymbol
|
|
218 |
ifTrue: [aStream nextPutAll: matchedItem storeString]
|
|
219 |
ifFalse:
|
|
220 |
[aStream nextPutAll: '#('.
|
|
221 |
matchedItem do: [:each | aStream nextPutAll: each storeString]
|
|
222 |
separatedBy: [aStream nextPut: $ ].
|
|
223 |
aStream nextPut: $)].
|
|
224 |
aStream
|
|
225 |
nextPut: $.;
|
|
226 |
cr].
|
|
227 |
transitions notEmpty
|
|
228 |
ifTrue:
|
|
229 |
[aStream
|
|
230 |
nextPutAll: 'self step.';
|
|
231 |
cr]
|
|
232 |
! !
|
|
233 |
|
|
234 |
!SmaCCNode methodsFor:'converting'!
|
|
235 |
|
|
236 |
asDFA
|
|
237 |
self asDFA: IdentitySet new merged: Dictionary new.
|
|
238 |
self removeDuplicateNodes.
|
|
239 |
^self
|
|
240 |
! !
|
|
241 |
|
|
242 |
!SmaCCNode methodsFor:'edges'!
|
|
243 |
|
|
244 |
addEdgeTo: endNode
|
|
245 |
transitions add: (SmaCCEdge to: endNode on: nil)
|
|
246 |
!
|
|
247 |
|
|
248 |
addEdgeTo: endNode on: characterCollection
|
|
249 |
transitions add: (SmaCCEdge to: endNode on: characterCollection)
|
|
250 |
! !
|
|
251 |
|
|
252 |
!SmaCCNode methodsFor:'initialize-release'!
|
|
253 |
|
|
254 |
action: anObject
|
|
255 |
anObject isNil ifTrue: [^self].
|
|
256 |
action := anObject isSymbol
|
|
257 |
ifTrue: [anObject]
|
|
258 |
ifFalse: [SortedCollection with: anObject]
|
|
259 |
!
|
|
260 |
|
|
261 |
initialize
|
|
262 |
id := NextId := NextId + 1.
|
|
263 |
transitions := SortedCollection new
|
|
264 |
! !
|
|
265 |
|
|
266 |
!SmaCCNode methodsFor:'printing'!
|
|
267 |
|
|
268 |
printOn: aStream
|
|
269 |
aStream
|
|
270 |
nextPutAll: self class name;
|
|
271 |
nextPut: $(;
|
|
272 |
nextPutAll: id printString;
|
|
273 |
nextPut: $)
|
|
274 |
! !
|
|
275 |
|
|
276 |
!SmaCCNode methodsFor:'private'!
|
|
277 |
|
|
278 |
action
|
|
279 |
^action
|
|
280 |
!
|
|
281 |
|
|
282 |
addActions: aCollection
|
|
283 |
aCollection isNil ifTrue: [^self].
|
|
284 |
action isNil
|
|
285 |
ifTrue:
|
|
286 |
[action := aCollection copy.
|
|
287 |
^self].
|
|
288 |
action isSymbol ifTrue: [^self].
|
|
289 |
aCollection isSymbol ifTrue: [^action := aCollection].
|
|
290 |
aCollection
|
|
291 |
do: [:each | (action includes: each) ifFalse: [action add: each]]
|
|
292 |
!
|
|
293 |
|
|
294 |
allCharacterTransitions: aSet into: aNode
|
|
295 |
| index each |
|
|
296 |
(aSet includes: self) ifTrue: [^#()].
|
|
297 |
aSet add: self.
|
|
298 |
aNode addActions: action.
|
|
299 |
index := 1.
|
|
300 |
[index <= transitions size] whileTrue:
|
|
301 |
[each := transitions at: index.
|
|
302 |
index := index + 1.
|
|
303 |
each isEpsilonTransition
|
|
304 |
ifTrue: [each to allCharacterTransitions: aSet into: aNode]
|
|
305 |
ifFalse:
|
|
306 |
[(aNode transitions includes: each) ifFalse: [aNode transitions add: each]]]
|
|
307 |
!
|
|
308 |
|
|
309 |
allCharacterTransitionsAndActionsInto: aNode
|
|
310 |
| seen |
|
|
311 |
seen := IdentitySet new.
|
|
312 |
self allCharacterTransitions: seen into: aNode
|
|
313 |
!
|
|
314 |
|
|
315 |
allStates
|
|
316 |
| nodes |
|
|
317 |
nodes := Set new.
|
|
318 |
self allStatesInto: nodes.
|
|
319 |
^nodes
|
|
320 |
!
|
|
321 |
|
|
322 |
allStatesInto: aSet
|
|
323 |
(aSet includes: self) ifTrue: [^self].
|
|
324 |
aSet add: self.
|
|
325 |
transitions do: [:each | each to allStatesInto: aSet]
|
|
326 |
!
|
|
327 |
|
|
328 |
asDFA: aSet merged: aDictionary
|
|
329 |
| newTransitions |
|
|
330 |
(aSet includes: self) ifTrue: [^self].
|
|
331 |
aSet add: self.
|
|
332 |
newTransitions := OrderedCollection new: transitions size.
|
|
333 |
transitions do:
|
|
334 |
[:each |
|
|
335 |
| existingEdges new |
|
|
336 |
new := each copy.
|
|
337 |
existingEdges := newTransitions select: [:edge | edge conflictsWith: each].
|
|
338 |
existingEdges do:
|
|
339 |
[:existing |
|
|
340 |
| node newChars existingChars chars |
|
|
341 |
node := aDictionary
|
|
342 |
at: (SortedCollection with: existing to id with: new to id) asArray
|
|
343 |
ifAbsentPut:
|
|
344 |
[node := self class new.
|
|
345 |
node transitions: ((Set
|
|
346 |
withAll: (existing to transitions collect: [:edge | edge copy]))
|
|
347 |
addAll: (new to transitions collect: [:edge | edge copy]);
|
|
348 |
yourself).
|
|
349 |
node mergeCharacterTransitions.
|
|
350 |
node addActions: existing to action.
|
|
351 |
node addActions: new to action.
|
|
352 |
node].
|
|
353 |
newChars := new characters.
|
|
354 |
chars := newChars select: [:char | existing includesCharacter: char].
|
|
355 |
chars notEmpty
|
|
356 |
ifTrue: [newTransitions addFirst: (SmaCCEdge to: node on: chars)].
|
|
357 |
existingChars := existing characters.
|
|
358 |
existing removeCharacters: newChars.
|
|
359 |
new removeCharacters: existingChars.
|
|
360 |
existing isEmpty ifTrue: [newTransitions remove: existing]].
|
|
361 |
new isEmpty ifFalse: [newTransitions add: new]].
|
|
362 |
transitions := newTransitions.
|
|
363 |
transitions do: [:each | each to asDFA: aSet merged: aDictionary]
|
|
364 |
!
|
|
365 |
|
|
366 |
asNFAWithoutEpsilonTransitions
|
|
367 |
| seen node |
|
|
368 |
node := self class new.
|
|
369 |
self allCharacterTransitionsAndActionsInto: node.
|
|
370 |
seen := IdentitySet new.
|
|
371 |
node asNFAWithoutEpsilonTransitions: seen.
|
|
372 |
seen := IdentitySet new.
|
|
373 |
node removeEpsilonTransitions: seen.
|
|
374 |
node cleanUp.
|
|
375 |
^node
|
|
376 |
!
|
|
377 |
|
|
378 |
asNFAWithoutEpsilonTransitions: aSet
|
|
379 |
| index each |
|
|
380 |
(aSet includes: self) ifTrue: [^self].
|
|
381 |
aSet add: self.
|
|
382 |
index := 1.
|
|
383 |
[index <= transitions size] whileTrue:
|
|
384 |
[each := transitions at: index.
|
|
385 |
index := index + 1.
|
|
386 |
each isEpsilonTransition
|
|
387 |
ifTrue: [each to allCharacterTransitionsAndActionsInto: self]
|
|
388 |
ifFalse: [each to asNFAWithoutEpsilonTransitions: aSet]]
|
|
389 |
!
|
|
390 |
|
|
391 |
cleanUp
|
|
392 |
self removeDuplicateNodes.
|
|
393 |
self mergeAllTransitions
|
|
394 |
!
|
|
395 |
|
|
396 |
id
|
|
397 |
^id
|
|
398 |
!
|
|
399 |
|
|
400 |
mergeAllTransitions
|
|
401 |
| nodes |
|
|
402 |
nodes := self allStates.
|
|
403 |
nodes do: [:each | each mergeCharacterTransitions]
|
|
404 |
!
|
|
405 |
|
|
406 |
mergeCharacterTransitions
|
|
407 |
| toMap |
|
|
408 |
toMap := IdentityDictionary new.
|
|
409 |
transitions copy do:
|
|
410 |
[:each |
|
|
411 |
(toMap includesKey: each to)
|
|
412 |
ifTrue:
|
|
413 |
[(toMap at: each to) mergeWith: each.
|
15
|
414 |
transitions removeIdentical: (transitions detect:[:e|e = each])
|
|
415 |
]
|
1
|
416 |
ifFalse: [toMap at: each to put: each]]
|
|
417 |
!
|
|
418 |
|
|
419 |
removeDuplicateEdges
|
|
420 |
transitions := transitions asSet asSortedCollection
|
|
421 |
!
|
|
422 |
|
|
423 |
removeDuplicateNodes
|
|
424 |
| nodes |
|
|
425 |
|
|
426 |
[nodes := self allStates.
|
|
427 |
nodes do:
|
|
428 |
[:each |
|
|
429 |
each
|
|
430 |
mergeCharacterTransitions;
|
|
431 |
removeDuplicateEdges].
|
|
432 |
self removeDuplicateNodes: nodes]
|
|
433 |
whileTrue
|
|
434 |
!
|
|
435 |
|
|
436 |
removeDuplicateNodes: aCollection
|
|
437 |
| merged nodePartition |
|
|
438 |
merged := false.
|
|
439 |
nodePartition := Dictionary new.
|
|
440 |
aCollection do:
|
|
441 |
[:each |
|
|
442 |
(nodePartition at: (Array with: each transitions size with: each action)
|
|
443 |
ifAbsentPut: [OrderedCollection new]) add: each].
|
|
444 |
nodePartition do:
|
|
445 |
[:each |
|
|
446 |
| seen |
|
|
447 |
seen := OrderedCollection new.
|
|
448 |
each do:
|
|
449 |
[:node |
|
|
450 |
| existingNode |
|
|
451 |
existingNode := seen
|
|
452 |
detect: [:otherNode | otherNode transitionsMatch: node]
|
|
453 |
ifNone: [nil].
|
|
454 |
existingNode isNil
|
|
455 |
ifTrue: [seen add: node]
|
|
456 |
ifFalse:
|
|
457 |
[merged := true.
|
|
458 |
node oneWayBecome: existingNode]]].
|
|
459 |
^merged
|
|
460 |
!
|
|
461 |
|
|
462 |
removeEpsilonTransitions: aSet
|
|
463 |
(aSet includes: self) ifTrue: [^self].
|
|
464 |
aSet add: self.
|
|
465 |
transitions copy
|
|
466 |
do: [:each | each isEpsilonTransition ifTrue: [transitions remove: each]].
|
|
467 |
transitions do: [:each | each to removeEpsilonTransitions: aSet]
|
|
468 |
!
|
|
469 |
|
|
470 |
transitions
|
|
471 |
^transitions
|
|
472 |
!
|
|
473 |
|
|
474 |
transitions: aCollection
|
|
475 |
transitions := aCollection asSortedCollection
|
|
476 |
!
|
|
477 |
|
|
478 |
transitionsMatch: aNode
|
|
479 |
^aNode transitions allSatisfy:
|
|
480 |
[:each |
|
|
481 |
(transitions includes: each) or:
|
|
482 |
[each to = aNode and:
|
|
483 |
[each characters
|
|
484 |
= (transitions detect: [:edge | edge to = self] ifNone: [^false])
|
|
485 |
characters]]]
|
|
486 |
! !
|
|
487 |
|
|
488 |
!SmaCCNode methodsFor:'public'!
|
|
489 |
|
|
490 |
hasSimpleLoop
|
|
491 |
^transitions anySatisfy: [:each | each to == self]
|
|
492 |
!
|
|
493 |
|
|
494 |
isTerminalNode
|
|
495 |
^transitions isEmpty or: [transitions size = 1 and: [self hasSimpleLoop]]
|
|
496 |
!
|
|
497 |
|
|
498 |
simulate: aStream
|
|
499 |
| char |
|
|
500 |
aStream atEnd ifTrue: [^action].
|
|
501 |
char := aStream next.
|
|
502 |
transitions
|
|
503 |
do: [:each | (each characters includes: char) ifTrue: [^each to simulate: aStream]].
|
|
504 |
^action
|
|
505 |
! !
|
|
506 |
|
|
507 |
!SmaCCNode class methodsFor:'documentation'!
|
|
508 |
|
|
509 |
version
|
|
510 |
^ '$Header: /opt/data/cvs/stx/goodies/smaCC/SmaCC__SmaCCNode.st,v 1.1 2006-02-09 21:17:46 vranyj1 Exp $'
|
15
|
511 |
!
|
|
512 |
|
|
513 |
version_SVN
|
|
514 |
^ '$Id$'
|
1
|
515 |
! !
|
|
516 |
|
|
517 |
SmaCCNode initialize!
|