SmaCC__SmaCCParser.st
author vranyj1
Wed, 17 Nov 2010 21:57:55 +0000
changeset 20 4ea23addc2c4
parent 19 dde4420fb553
permissions -rw-r--r--
Makefile updated

"{ Package: 'stx:goodies/smaCC' }"

"{ NameSpace: SmaCC }"

Object subclass:#SmaCCParser
	instanceVariableNames:'scanner currentToken errorToken stateStack nodeStack'
	classVariableNames:''
	poolDictionaries:''
	category:'SmaCC-Runtime'
!

SmaCCParser comment:'SmaCCParser is an abstract class that defines most of the parsing actions. Subclasses will define methods that specify their transitions and reduction actions. These are normally defined automatically when compiling the parser.

Subclasses must implement the following messages:
	accessing
		emptySymbolTokenId
		reduceTable
		transitionTable

Instance Variables:
	currentToken	<SmaCCToken>	the token last returned by the scanner that has not been shifted (reduce actions leave the current token alone)
	nodeStack	<OrderedCollection>	collection of items on stack. These items are specific to the parser and can be any object. 
	scanner	<SmaCCScanner>	our scanner
	stateStack	<OrderedCollection of: Integer>	the stack of states for our parser (standard LR state stack)

'
!


!SmaCCParser class methodsFor:'instance creation'!

new
	^(super new)
		initialize;
		yourself
!

on: aStream 
	| parser scanner |
	scanner := self scannerClass on: aStream.
	parser := self new.
	parser scanner: scanner.
	^parser
! !

!SmaCCParser class methodsFor:'accessing'!

parse: aString 
	^self parse: aString startingAt: self defaultStartingState
!

parse: aString onError: aBlock
        ^[self parse: aString] on: SmaCCParserError
                do: [:ex | 
                    Smalltalk isSmalltalkX ifTrue:[
                        ex return: (aBlock value: ex description value: ex parameter position)
                    ].
                    ex return: (aBlock value: ex description value: ex tag position)
                ]
!

parse: aString startingAt: anInteger 
	^self parseStream: (ReadStream on: aString) startingAt: anInteger
!

parse: aString startingAt: anInteger onError: aBlock
        ^[self parse: aString startingAt: anInteger] on: SmaCCParserError
                do: [:ex | 
                    Smalltalk isSmalltalkX ifTrue:[
                        ex return: (aBlock value: ex description value: ex parameter position)
                    ].
                    ex return: (aBlock value: ex description value: ex tag position)]
!

parseStream: aStream 
	^self parseStream: aStream startingAt: self defaultStartingState
!

parseStream: aStream onError: aBlock
        ^[self parseStream: aStream] on: SmaCCParserError
                do: [:ex | 
                    Smalltalk isSmalltalkX ifTrue:[
                        ex return: (aBlock value: ex description value: ex parameter position)
                    ].
                    ex return: (aBlock value: ex description value: ex tag position)]
!

parseStream: aStream startingAt: anInteger 
	| parser |
	parser := self on: aStream.
	parser setStartingState: anInteger.
	^parser parse
!

parseStream: aStream startingAt: anInteger onError: aBlock
        ^[self parseStream: aStream startingAt: anInteger] on: SmaCCParserError
                do: [:ex | 
                    Smalltalk isSmalltalkX ifTrue:[
                        ex return: (aBlock value: ex description value: ex parameter position)
                    ].
                    ex return: (aBlock value: ex description value: ex tag position)]
! !

!SmaCCParser class methodsFor:'private'!

defaultStartingState
	^1
!

scannerClass
	^self subclassResponsibility
! !

!SmaCCParser methodsFor:'accessing'!

emptySymbolTokenId
	^scanner emptySymbolTokenId
!

errorTable
	^#()
!

errorTokenId
	^scanner errorTokenId
!

parse
	self setDefaultStartingStateIfNone.
	self performParsingLoop.
	^nodeStack last
!

position
	^currentToken isNil 
		ifTrue: [scanner position]
		ifFalse: [currentToken startPosition]
!

reduceTable
	^self subclassResponsibility
!

transitionTable
	^self subclassResponsibility
! !

!SmaCCParser methodsFor:'initialize-release'!

initialize
	nodeStack := OrderedCollection new
!

scanner: aScanner 
	scanner := aScanner
!

setStartingState: startingState 
	stateStack := OrderedCollection with: startingState
! !

!SmaCCParser methodsFor:'private'!

acceptAction
	^0
!

actionFor: aSymbolIndex 
	^self actionForState: self currentState and: aSymbolIndex
!

actionForCurrentToken
	^self actionFor: currentToken id first
!

actionForState: stateIndex and: aSymbolIndex 
	| index row |
	row := self transitionTable at: stateIndex.
	(row at: 1) == 0 
		ifTrue: 
			[index := self 
						binarySearchIn: row
						for: aSymbolIndex
						size: 2.
			index == 0 
				ifTrue: [^self errorAction]
				ifFalse: [^((row at: 2) bitShift: 8) + (row at: 3)]]
		ifFalse: 
			[index := self 
						binarySearchIn: row
						for: aSymbolIndex
						size: 4.
			index == 0 
				ifTrue: [^self errorAction]
				ifFalse: [^((row at: index - 2) bitShift: 8) + (row at: index - 1)]]
!

actionMask
	^2r11
!

binarySearchIn: aRow for: aSymbolIndex size: step 
	| start mid length high low midItem stop |
	high := aSymbolIndex bitShift: -8.
	low := aSymbolIndex bitAnd: 16rFF.
	start := 4.
	stop := aRow size - 1.
	length := (stop - start) // step.
	[length > 4] whileTrue: 
			[length := length bitShift: -1.
			mid := length * step + start.
			midItem := aRow at: mid.
			((midItem == high and: [(aRow at: mid + 1) <= low]) or: [midItem < high]) 
				ifTrue: [start := mid]
				ifFalse: [stop := mid]].
	[start <= stop] whileTrue: 
			[((aRow at: start) == high and: [(aRow at: start + 1) == low]) 
				ifTrue: [^start].
			start := start + step].
	^0
!

currentState
	^stateStack last
!

errorAction
	^3
!

findErrorHandlerIfNoneUseErrorNumber: anInteger 
	| handlerStates index startingErrorToken newStack |
	handlerStates := self errorHandlerStates reverse.
	startingErrorToken := currentToken.
	
	[index := (1 to: handlerStates size) detect: 
					[:each | 
					| state |
					state := handlerStates at: each.
					state ~= 0 and: 
							[newStack := stateStack copyFrom: 1 to: handlerStates size - each + 1.
							newStack add: state.
							self willShift: newStack]]
				ifNone: [nil].
	index isNil] 
			whileTrue: 
				[self dismissErrorToken.
				currentToken id first = self emptySymbolTokenId 
					ifTrue: 
						[currentToken := startingErrorToken.
						self reportError: anInteger]].
	index - 1 timesRepeat: [self dismissStackTopForErrorRecovery].
	stateStack addLast: (handlerStates at: index).
	nodeStack addLast: startingErrorToken
!

getNextToken
	currentToken isNil ifTrue: [currentToken := scanner next]
!

liftFirstValue: aCollection 
	^aCollection first
!

liftLastValue: aCollection 
	^aCollection last
!

liftSecondValue: aCollection 
	^aCollection at: 2
!

performParsingLoop
	| action actionType |
	
	[self getNextToken.
	action := self actionForCurrentToken.
	action = self acceptAction] 
			whileFalse: 
				[actionType := action bitAnd: self actionMask.
				action := action bitShift: -2.
				actionType == self shiftAction 
					ifTrue: [self shift: action]
					ifFalse: 
						[actionType == self reduceAction 
							ifTrue: [self reduce: action]
							ifFalse: [self handleError: action]]].
	self checkForErrors
!

performReduceMethod: aSymbol with: items 
	^aSymbol last == $: 
		ifTrue: [self perform: aSymbol with: items]
		ifFalse: [self perform: aSymbol]
!

reduce: anInteger 
	| reduceEntry items size |
	reduceEntry := self reduceTable at: anInteger.
	items := OrderedCollection new: (size := reduceEntry at: 2).
	size timesRepeat: 
			[items addFirst: nodeStack removeLast.
			stateStack removeLast].
	nodeStack add: (self performReduceMethod: (reduceEntry at: 3) with: items).
	stateStack add: ((self actionFor: (reduceEntry at: 1)) bitShift: -2)
!

reduceAction
	^2r10
!

reduceFor: aCollection 
	| newCollection item |
	(aCollection allSatisfy: [:each | each class ~~ OrderedCollection]) 
		ifTrue: [^aCollection].
	aCollection first class == OrderedCollection 
		ifTrue: 
			[newCollection := aCollection first.
			2 to: aCollection size
				do: 
					[:i | 
					item := aCollection at: i.
					item class = OrderedCollection 
						ifTrue: [newCollection addAll: item]
						ifFalse: [newCollection add: item]].
			^newCollection].
	newCollection := OrderedCollection new.
	aCollection do: 
			[:each | 
			each class == OrderedCollection 
				ifTrue: [newCollection addAll: each]
				ifFalse: [newCollection add: each]].
	^newCollection
!

setDefaultStartingStateIfNone
	stateStack isNil 
		ifTrue: [self setStartingState: self class defaultStartingState]
!

shift: stateIndex 
	stateStack add: stateIndex.
	nodeStack add: currentToken.
	currentToken := nil
!

shiftAction
	^2r01
! !

!SmaCCParser methodsFor:'private-error handling'!

checkForErrors
	"If we have an error correction installed, we might have handled the errors. If we did, we don't 
	want to return the result, so we raise a final exception that can't be proceeded."

	errorToken isNil ifTrue: [^self].
	currentToken := errorToken.
	self reportErrorMessage: 'Token not expected'
!

dismissErrorToken
	currentToken := nil.
	self getNextToken
!

dismissStackTopForErrorRecovery
	stateStack removeLast.
	^nodeStack removeLast
!

errorHandlerStates
	^stateStack collect: 
			[:each | 
			| action |
			action := self actionForState: each and: self errorTokenId.
			(action bitAnd: self actionMask) = 1 
				ifTrue: [action bitShift: -2]
				ifFalse: [0]]
!

handleError: anInteger 
	errorToken isNil ifTrue: [errorToken := currentToken].
	(currentToken id first = self emptySymbolTokenId 
		or: [self hasErrorHandler not]) ifTrue: [self reportError: anInteger].
	self findErrorHandlerIfNoneUseErrorNumber: anInteger
!

hasErrorHandler
	^self errorHandlerStates anySatisfy: [:each | each ~~ 0]
!

reportError: anInteger 
	self reportErrorMessage: (anInteger = 0 
				ifTrue: ['Token not expected']
				ifFalse: [self errorTable at: anInteger])
!

reportErrorMessage: aString
        Smalltalk isSmalltalkX ifTrue:[
            errorToken ifNotNil:[
                | errorMessage |
                errorMessage := ((errorToken value = '') and:[scanner stream atEnd])
                                    ifTrue:['Unexpected end of file']
                                    ifFalse:[aString].
                SmaCCParserError raiseSignal: errorMessage with: self
            ] ifNil:[
                SmaCCParserError raiseSignal: aString with:self
            ]
        ] ifFalse:[
            SmaCCParserError signal: aString with: self
        ]

    "Modified: / 05-01-2007 / 13:04:48 / janfrog"
    "Modified: / 29-03-2010 / 13:15:06 / Jan Vrany <jan.vrany@fit.cvut.cz>"
!

willShift: potentialStateStack 
	| action compoundAction reduceEntry size |
	compoundAction := self actionForState: potentialStateStack last
				and: currentToken id first.
	action := compoundAction bitAnd: self actionMask.
	action == self shiftAction ifTrue: [^true].
	action == self reduceAction 
		ifTrue: 
			[reduceEntry := self reduceTable at: (compoundAction bitShift: -2).
			size := reduceEntry at: 2.
			size timesRepeat: [potentialStateStack removeLast].
			potentialStateStack 
				add: ((self actionForState: potentialStateStack last
						and: (reduceEntry at: 1)) bitShift: -2).
			^self willShift: potentialStateStack].
	^false
! !

!SmaCCParser methodsFor:'public'!

isEOFToken
	^currentToken id first = self emptySymbolTokenId
! !

!SmaCCParser class methodsFor:'documentation'!

version
    ^ '$Header: /opt/data/cvs/stx/goodies/smaCC/SmaCC__SmaCCParser.st,v 1.4 2007-01-05 15:16:41 vranyj1 Exp $'
!

version_SVN
    ^ '$Id$'
! !