'From Squeak6.0alpha of 3 April 2021 [latest update: #20329] on 7 April 2021 at 7:20:21 pm'!

!RxMatchOptimizer methodsFor: 'private' stamp: 'ct 4/7/2021 19:10'!
addPrefix: aCharacter

	^(prefixes ifNil: [ prefixes := "CharacterSet"Set new ]) add: aCharacter! !


!RxMatcher methodsFor: 'match enumeration' stamp: 'ct 4/7/2021 19:20'!
copy: aString replacingMatchesWith: replacementString
	"Copy <aString>, except for the matches. Replace each match with <aString>."

	^aString species new: (aString size min: 1000) streamContents: [ :stream |
		self
			copyStream: aString readStream
			to: stream
			replacingMatchesWith: replacementString ]! !

!RxMatcher methodsFor: 'match enumeration' stamp: 'ct 4/7/2021 19:19'!
copy: aString translatingMatchesUsing: aBlock
	"Copy <aString>, except for the matches. For each match, evaluate <aBlock> passing the matched substring as the argument.  Expect the block to answer a String, and replace the match with the answer."

	| answer |
	answer := (aString species new: 40) writeStream.
	self copyStream: aString readStream to: answer translatingMatchesUsing: aBlock.
	^answer contents! !

!RxMatcher methodsFor: 'match enumeration' stamp: 'ct 4/7/2021 19:17'!
copyStream: aStream to: writeStream translatingMatchesUsing: aBlock
	"Copy the contents of <aStream> on the <writeStream>, except for the matches. For each match, evaluate <aBlock> passing the matched substring as the argument.  Expect the block to answer a String, and write the answer to <writeStream> in place of the match."

	| searchStart matchStart matchEnd match |
	stream := aStream.	
	firstTryMatch := true.
	[searchStart := aStream position.
	self proceedSearchingStream: aStream] whileTrue:
		[matchStart := (self subBeginning: 1) last.
		matchEnd := (self subEnd: 1) last.
		aStream position: searchStart.
		searchStart to: matchStart - 1 do:
			[:ignoredPos | writeStream nextPut: aStream next].
		match := (writeStream collectionSpecies new: matchEnd - matchStart + 1) writeStream.
		matchStart to: matchEnd - 1 do:
			[:ignoredPos | match nextPut: aStream next].
		writeStream nextPutAll: (aBlock value: match contents).
		"Be extra careful about successful matches which consume no input.
		After those, make sure to advance or finish if already at end."
		matchEnd = searchStart ifTrue: 
			[aStream atEnd
				ifTrue:	[^self "rest after end of whileTrue: block is a no-op if atEnd"]
				ifFalse:	[writeStream nextPut: aStream next]]].
	aStream position: searchStart.
	[aStream atEnd] whileFalse: [writeStream nextPut: aStream next]! !

!RxMatcher methodsFor: 'double dispatch' stamp: 'ct 4/7/2021 19:10'!
syntaxBranch: branchNode
	"Double dispatch from the syntax tree. 
	Branch node is a link in a chain of concatenated pieces.
	First build the matcher for the rest of the chain, then make 
	it for the current piece and hook the rest to it."

	| piece branch |
	piece := branchNode piece.
	branch := branchNode branch ifNil: [ ^piece dispatchTo: self ].
	"Optimization: glue a sequence of individual characters into a single string to match."
	piece isAtomic ifTrue: [
		| result next stream |
		stream := ("String"Array new: 40) writeStream.
		next := branchNode tryMergingInto: stream.
		result := stream contents.
		result size > 1 ifTrue: [
			"worth merging"
			^(RxmSubstring new substring: result ignoreCase: ignoreCase)
				pointTailTo: (next ifNotNil: [ next dispatchTo: self ]);
				yourself ] ].
	"No optimization possible or worth it, just concatenate all. "
	^(piece dispatchTo: self)
		pointTailTo: (branch dispatchTo: self);
		yourself! !


!RxsCharSet methodsFor: 'privileged' stamp: 'ct 4/7/2021 19:10'!
enumerableSetIgnoringCase: aBoolean
	"Answer a collection of characters that make up the portion of me that can be enumerated, or nil if there are no such characters. The case check is only used to determine the type of set to be used. The returned set won't contain characters of both cases, because this way the senders of this method can create more efficient checks."

	| highestCharacterCode set |
	highestCharacterCode := elements inject: -1 into: [ :max :each |
		(each maximumCharacterCodeIgnoringCase: aBoolean) max: max ].
	highestCharacterCode = -1 ifTrue: [ ^nil ].
	set := "(highestCharacterCode <= 255
		ifTrue: [ CharacterSet ]
		ifFalse: [ WideCharacterSet ])"Set new.
	elements do: [ :each | each enumerateTo: set ].
	^set! !

