BidirectionalEdgeLabeledDigraphNode variableSubclass: #LRParserState instanceVariableNames: 'reduceMap ' classVariableNames: '' poolDictionaries: '' category: 'T-gen-Scanning/Parsing'! LRParserState comment: '================================================= Copyright (c) 1992 by Justin O. Graver. All rights reserved (with exceptions). For complete information evaluate "Object tgenCopyright." ================================================= I represent a node in an LR parser characteristic finite state machine. Instance Variables: edgeLabelMap - overridden from EdgeLabeledDigraphNode for efficiency since only deterministic FSAs are constructed. reduceMap '! !LRParserState methodsFor: 'initialization'! init super init. self edgeLabelMap: Dictionary new. "overrides use of SetDictionary in superclass" self reduceMap: SetDictionary new! ! !LRParserState methodsFor: 'state accessing'! reduceMap ^reduceMap! reduceMap: argument reduceMap := argument! ! !LRParserState methodsFor: 'exception handling'! standardErrorString ^'unexpected token encountered: '! ! !LRParserState methodsFor: 'printing'! printOn: aStream super printOn: aStream. aStream cr. self reduceMap printOn: aStream! ! !LRParserState methodsFor: 'lalr analysis'! appendHashTo: sym "Answer a new nonterminal or terminal with my hash value appended." | newSym | newSym := sym , self symbolSuffixSeparatorString , self hash printString. ^sym isNonterminal ifTrue: [newSym asNonterminal] ifFalse: [newSym]! buildLalrGrammarWith: stateDict originalGrammar: aGrammar "Answer my corresponding LALR(1) grammar. The new productions will not be in any particular order so we must be sure to locate and explicitly specify the new start symbol." | productions startSymbol pattern startSyms | productions := OrderedCollection new. self collectLalrProductionsIn: productions andProdMapsIn: stateDict traversedStates: Set new. pattern := aGrammar startSymbol , self symbolSuffixSeparatorString , '*'. startSyms := Set new. productions do: [:prod | (pattern match: prod leftHandSide) ifTrue: [startSyms add: prod leftHandSide]]. startSyms size = 1 ifTrue: [startSymbol := startSyms first] ifFalse: [self error: 'multiple start symbols in LALR grammar']. ^self buildGrammarWithProductions: productions startSymbol: startSymbol! collectLalrProductionsIn: aCollection andProdMapsIn: stateDict traversedStates: aSet | newProds | (aSet includes: self) ifFalse: [aSet add: self. self isReduceState ifTrue: [self reductionsDo: [:prod | newProds := self makeLalrProductionFor: prod. (stateDict includesKey: self) ifTrue: ["only need to retain data for conflict states" newProds do: [:np | (stateDict at: self) at: prod add: np leftHandSide]]. aCollection addAll: newProds]]. self successorsExceptSelfDo: [:state | state collectLalrProductionsIn: aCollection andProdMapsIn: stateDict traversedStates: aSet]]! lalr1AnalyzeConflicts: stateSet originalGrammar: aGrammar | conflictStateMap newGrammar prodMap prod follows conflictStates | conflictStates := Set new. conflictStateMap := Dictionary new: stateSet size. stateSet do: [:state | conflictStateMap at: state put: SetDictionary new]. newGrammar := self buildLalrGrammarWith: conflictStateMap originalGrammar: aGrammar. "rebuild reduce maps for inconsistent states" stateSet do: [:state | state reduceMap: SetDictionary new. prodMap := conflictStateMap at: state. prodMap associationsDo: [:assoc | prod := assoc key. follows := Set new. assoc value do: [:nonterm | (newGrammar followSetOf: nonterm) do: [:term | follows add: (term copyUpToLast: self symbolSuffixSeparatorChar)]]. follows do: [:term | state reduceBy: prod on: term]]. state hasReduceReduceConflict | state hasShiftReduceConflict ifTrue: [conflictStates add: state]]. ^conflictStates isEmpty! makeLalrProductionFor: prod | stateSet rhs newProds lhs currState | stateSet := Set with: self. prod rightHandSide reverseDo: [:sym | stateSet := stateSet inject: Set new into: [:set :state | set union: (state predecessorLabelMap at: sym)]]. newProds := Set new. stateSet do: [:state | lhs := state appendHashTo: prod leftHandSide. currState := state. rhs := OrderedCollection new. prod rightHandSide do: [:sym | rhs add: (currState appendHashTo: sym). currState := currState transitionFor: sym]. newProds add: (self makeProductionWithLeftHandSide: lhs rightHandSide: rhs)]. ^newProds! ! !LRParserState methodsFor: 'building'! goto: aState on: transitionSymbol self addSuccessor: aState withEdgeLabeled: transitionSymbol. aState addPredecessor: self withEdgeLabeled: transitionSymbol! ! !LRParserState methodsFor: 'state transitions'! actionFor: aTerminal | action | (action := self reductionFor: aTerminal) isNil ifTrue: [(action := self transitionFor: aTerminal) isNil ifTrue: [action := self acceptSymbol]]. ^action! ! !LRParserState methodsFor: 'modifying'! addSuccessor: node withEdgeLabeled: label "overridden for Dictionary edgeLabelMap" (self edgeLabelMap includesKey: label) ifTrue: [self error: 'check it out']. self edgeLabelMap at: label put: node! ! !LRParserState methodsFor: 'accessing'! successors "overriden for Dictionary edgeLabelMap" ^self edgeLabelMap values! ! !LRParserState methodsFor: 'private'! acceptSymbol ^self class acceptSymbol! buildGrammarWithProductions: prods startSymbol: aSymbol ^self grammarClass buildGrammarWithProductions: prods startSymbol: aSymbol! grammarClass ^Grammar! grammarProductionClass ^GrammarProduction! makeProductionWithLeftHandSide: lhs rightHandSide: rhs ^self grammarProductionClass leftHandSide: lhs rightHandSide: rhs! symbolSuffixSeparatorChar ^self class symbolSuffixSeparatorChar! symbolSuffixSeparatorString ^String with: self symbolSuffixSeparatorChar! ! !LRParserState methodsFor: 'accessing reductions'! reduceBy: aProduction on: aTerminal self reduceMap at: aTerminal add: aProduction! reductionFor: aSymbol ^self reduceMap at: aSymbol ifAbsent: [nil] ifNotUnique: [self error: 'reduce/reduce conflict in parser']! ! !LRParserState methodsFor: 'testing'! hasReduceReduceConflict "Answer true if there is a reduce/reduce conflict in this state, and false otherwise." ^self reduceMap isDeterministic not! hasShiftReduceConflict "Answer true if there is a shift/reduce conflict in this state, and false otherwise." | reduceSyms shiftSyms | reduceSyms := self reduceMap keys. shiftSyms := self edgeLabelMap keys. ^reduceSyms size + shiftSyms size ~= (reduceSyms union: shiftSyms) size! isReduceState ^self reduceMap isEmpty not! ! !LRParserState methodsFor: 'enumerating'! reductionsDo: aBlock "Evaluate aBlock for each of my reduce productions." self reduceMap elementsDo: aBlock! ! !LRParserState methodsFor: 'converting'! spaceOptimizeMap "Predecessors are only needed for LALR(1) analysis." super spaceOptimizeMap. self predecessorLabelMap: nil! ! "-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- "! LRParserState class instanceVariableNames: ''! !LRParserState class methodsFor: 'constants'! acceptSymbol ^#accept! symbolSuffixSeparatorChar ^$.! ! !LRParserState class methodsFor: 'class initialization'! initialize "LRParserState initialize" self noTransitionSignal: (Signal new nameClass: self message: #noTransitionSymbol)! ! Object variableSubclass: #TokenClassification instanceVariableNames: 'tokenType action ' classVariableNames: '' poolDictionaries: '' category: 'T-gen-Scanning/Parsing'! TokenClassification comment: '================================================= Copyright (c) 1992 by Justin O. Graver. All rights reserved (with exceptions). For complete information evaluate "Object tgenCopyright." ================================================= I represent a class of tokens. Instance Variables: tokenType - name of this token class. action - a message selector that will be sent to the scanner (via #perform:) when a token with this type is recognized.'! !TokenClassification methodsFor: 'state accessing'! action ^action! action: argument action := argument! tokenType ^tokenType! tokenType: argument tokenType := argument! ! !TokenClassification methodsFor: 'testing'! isTokenClassification ^true! ! !TokenClassification methodsFor: 'printing'! printOn: aStream aStream nextPutAll: self tokenType. aStream nextPutAll: ' : {'. aStream nextPutAll: (self action isNil ifTrue: ['nil'] ifFalse: [self action]). aStream nextPutAll: '} ;'! ! !TokenClassification methodsFor: 'reconstructing'! reconstructOn: aStream "Emit #( tokenType action ) on aStream" aStream poundSign; leftParenthesis. self tokenType reconstructOn: aStream. aStream space. self action reconstructOn: aStream. aStream rightParenthesis! ! "-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- "! TokenClassification class instanceVariableNames: ''! !TokenClassification class methodsFor: 'instance creation'! tokenType: arg1 action: arg2 | newMe | newMe := self new. newMe tokenType: arg1. newMe action: arg2. ^newMe! ! Object variableSubclass: #GrammarProduction instanceVariableNames: 'leftHandSide rightHandSide ' classVariableNames: '' poolDictionaries: '' category: 'T-gen-Scanning/Parsing'! GrammarProduction comment: '================================================= Copyright (c) 1992 by Justin O. Graver. All rights reserved (with exceptions). For complete information evaluate "Object tgenCopyright." ================================================= I represent one production of a context-free grammar. I am responsible for some parts of the first/follow set computation algorithm and for converting myself between various related representations (e.g. LR(0) items). Instance Variables: leftHandSide rightHandSide '! !GrammarProduction methodsFor: 'state accessing'! leftHandSide ^leftHandSide! leftHandSide: argument leftHandSide := argument! rightHandSide ^rightHandSide! rightHandSide: argument rightHandSide := argument! ! !GrammarProduction methodsFor: 'copying'! postCopy super postCopy. self rightHandSide: self rightHandSide copy. ^self! ! !GrammarProduction methodsFor: 'parse tree building'! computeResultNodeFor: builder withArgNodes: nodes "Productions without translation symbols can only pass on a single argument node." nodes size = 1 ifFalse: [self error: 'Productions without translation symbols can only pass on results from a single right-hand side nonterminal']. ^builder answerArgument: nodes first! computeResultNodeFor: builder withTokenClassValue: value "See this method in class TransductionGrammarProduction." self error: 'No translation has been specified that would create a place to store the token class value.'! numberOfRhsNonterminals "Answer the number of nonterminals in my right-hand side." ^(self rightHandSide select: [:sym | sym isNonterminal]) size! ! !GrammarProduction methodsFor: 'testing'! hasSingleTokenClassRhs "Answer true if my right hand side consists solely of a single token class terminal symbol and false otherwise." ^self rightHandSide size = 1 and: [self rightHandSide first isTokenClassTerminal]! hasTranslation "See class TransductionGrammarProduction." ^false! isEpsilonProduction "Answer true if I am a production of the form S -> (i.e. if my right hand side is empty) and false otherwise." ^self rightHandSide isEmpty! isGrammarProduction ^true! rightHandSideComprisedOf: aSet "Answer true if all symbols in my right-hand side are included in aSet and false otherwise." self rightHandSide detect: [:sym | (aSet includes: sym) not] ifNone: [^true]. ^false! rightHandSideHasAllNontermsIn: aSet "Answer true if all nonterminals in my right-hand side are included in aSet and false otherwise." self rightHandSide detect: [:sym | sym isNonterminal and: [(aSet includes: sym) not]] ifNone: [^true]. ^false! ! !GrammarProduction methodsFor: 'printing'! printOn: aStream self printSymbol: self leftHandSide on: aStream. aStream tab; nextPut: $:; space. self rightHandSide do: [:sym | self printSymbol: sym on: aStream. aStream space]! ! !GrammarProduction methodsFor: 'private'! lr0ItemClass ^LR0Item! lr1ItemClass ^LR1Item! lrParserStateClass ^LRParserState! printSymbol: sym on: aStream "Render the given grammar symbol (terminal or nonterminal) on aStream. This is provided so that grammars are printed in T-gen specification form. Nonterminals and token class terminals are printed without #s or 's and terminals are printed as strings." (sym isNonterminal or: [sym isTokenClassTerminal]) ifTrue: [sym do: [:ch | aStream nextPut: ch]] ifFalse: [sym printOn: aStream]! symbolSuffixSeparatorChar ^self lrParserStateClass symbolSuffixSeparatorChar! ! !GrammarProduction methodsFor: 'conversion'! asInitialLR0Item ^self lr0ItemClass leftHandSide: self leftHandSide preDotSymbols: OrderedCollection new postDotSymbols: self rightHandSide copy! asInitialLR1ItemWithLookahead: terminal ^self lr1ItemClass leftHandSide: self leftHandSide preDotSymbols: OrderedCollection new postDotSymbols: self rightHandSide copy lookahead: terminal! asNonLalrSuffixedProduction "Assuming I am of the form 'A.* -> B.* C.*', answer the prefix production 'A -> B C'." | separator lhs rhs | separator := self symbolSuffixSeparatorChar. lhs := self leftHandSide copyUpTo: separator. rhs := self rightHandSide collect: [:sym | sym copyUpTo: separator]. ^self species leftHandSide: lhs rightHandSide: rhs! ! !GrammarProduction methodsFor: 'first/follow sets'! computeFirstIn: grammar using: graph "Build dependency graph for first sets and initialize first sets. Starting at the left end of my right hand side, symbols are processed until a terminal or non-nullable nonterminal is encountered. Any terminal encountered is added to the first set associated with my left hand side node in the graph. Any nonterminal encountered means that I must include its first set in mine. This accomplished (indirectly) by adding an edge in the graph from the nonterminal's node to my lhs node. The actual first set unioning will be done after the graph is complete (see sender)." self rightHandSide do: [:sym | sym isTerminal ifTrue: [graph addTerminal: sym toNodeLabeled: self leftHandSide. ^self]. graph addEdgeFromNodeLabeled: sym toNodeLabeled: self leftHandSide. (grammar isNullable: sym) ifFalse: [^self]]! computeFollowIn: grammar using: graph "Build dependency graph for follow sets and initialize follow sets. This method performs two distinct parts of the algorithm. First, each nonterminal in my right hand side is checked to what symbols can follow it. Those symbols are added to the follow set for the nonterminal's graph node. Second, starting at the right end of my right hand side, symbols are processed until a terminal or non-nullable nonterminal is encountered. Any nonterminal encountered means that my follow set should also be included in its follow set. This accomplished (indirectly) by adding an edge in the graph from my lhs node to the nonterminal's node. The actual follow set unioning will be done after the graph is complete (see sender)." | n currSym more j nextSym | n := self rightHandSide size. 1 to: n - 1 do: [:i | (currSym := self rightHandSide at: i) isNonterminal ifTrue: [more := true. j := i + 1. [j <= n & more] whileTrue: [nextSym := self rightHandSide at: j. (grammar firstSetOf: nextSym) do: [:sym | graph addTerminal: sym toNodeLabeled: currSym]. j := j + 1. more := grammar isNullable: nextSym]]]. self rightHandSide reverseDo: [:sym | sym isTerminal ifTrue: [^self]. graph addEdgeFromNodeLabeled: self leftHandSide toNodeLabeled: sym. (grammar isNullable: sym) ifFalse: [^self]]! ! !GrammarProduction methodsFor: 'comparing'! = aProd ^aProd isGrammarProduction ifTrue: [self leftHandSide = aProd leftHandSide and: [self rightHandSide = aProd rightHandSide]] ifFalse: [false]! hash "This is redefined because = is redefined." ^self leftHandSide hash bitXor: self rightHandSide hash! ! !GrammarProduction methodsFor: 'reconstructing'! constructItsContentOn: aStream using: tokenTable "Emit lhs and #( rhs ) on aStream" (tokenTable indexOf: self leftHandSide) reconstructOn: aStream. aStream space; poundSign; leftParenthesis. self rightHandSide do: [:ea | (tokenTable indexOf: ea) reconstructOn: aStream. aStream space]. aStream rightParenthesis! reconstructOn: aStream "Emit #( productions ) on aStream " aStream poundSign; leftParenthesis. (self symbolTable at: self leftHandSide) reconstructOn: aStream. aStream space; poundSign; leftParenthesis. self rightHandSide do: [:ea | (self symbolTable at: ea) reconstructOn: aStream. aStream space]. aStream rightParenthesis; rightParenthesis; space! reconstructOn: aStream using: tokenTable aStream poundSign; leftParenthesis. self constructItsContentOn: aStream using: tokenTable. aStream rightParenthesis; space! ! "-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- "! GrammarProduction class instanceVariableNames: ''! !GrammarProduction class methodsFor: 'instance creation'! leftHandSide: arg1 rightHandSide: arg2 | newMe | newMe := self new. newMe leftHandSide: arg1. newMe rightHandSide: arg2. ^newMe! ! Array variableSubclass: #Stack instanceVariableNames: 'topPtr ' classVariableNames: '' poolDictionaries: '' category: 'T-gen-Scanning/Parsing'! Stack comment: '================================================= Copyright (c) 1992 by Justin O. Graver. All rights reserved (with exceptions). For complete information evaluate "Object tgenCopyright." ================================================= This class provides a more traditional push/pop interface for Arrays.'! !Stack methodsFor: 'private'! copyEmpty: aSize "Answer a copy of the receiver that contains no elements. This method should be redefined in subclasses that add instance variables, so that the state of those variables is preserved" ^(super copyEmpty: aSize) topPtr: self topPtr.! ! !Stack methodsFor: 'state accessing'! topPtr ^topPtr! topPtr: arg topPtr := arg! ! !Stack methodsFor: 'testing'! isEmpty ^topPtr = 0! isFull ^ topPtr = self basicSize! ! !Stack methodsFor: 'accessing'! pop "Answer the object on top of the stack." | n | n := self at: topPtr. topPtr := topPtr - 1. ^n! pop: numElem "Pop and discard top numElems and answer receiver" topPtr := topPtr - numElem! push: anObject "Push anObject onto the top of the stack." self isFull ifTrue: [self grow]. topPtr := topPtr + 1. ^self at: topPtr put: anObject! size "Answer the number of objects on the stack." ^topPtr! top "Answer (without removing) the object on top of the stack." ^self at: topPtr! ! !Stack methodsFor: 'initialization'! init self topPtr: 0! ! !Stack methodsFor: 'enumerating'! do: aBlock "Evaluate aBlock for each object on the stack, from top to bottom." ^super reverseDo: aBlock! reverseDo: aBlock "Evaluate aBlock for each object on the stack, from bottom to top." ^super do: aBlock! ! "-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- "! Stack class instanceVariableNames: ''! !Stack class methodsFor: 'instance creation'! new ^self new: 100! new: arg ^( super new: arg ) init! ! Dictionary variableSubclass: #LLParserTable instanceVariableNames: '' classVariableNames: '' poolDictionaries: '' category: 'T-gen-Scanning/Parsing'! LLParserTable comment: '================================================= Copyright (c) 1992 by Justin O. Graver. All rights reserved (with exceptions). For complete information evaluate "Object tgenCopyright." ================================================= I implement a two dimensional LL(1) parser table with rows indexed by nonterminals, columns indexed by terminals, and with production table entries. At the top level I''m a Dictionary from nonterminals to rows; each row is a SetDictionary from terminals to productions. In deterministic tables (tables without multiple entries) the SetDictionaries can be (and are) converted into simple Dictionaries.'! !LLParserTable methodsFor: 'testing'! isDeterministic self detect: [:row | row isDeterministic not] ifNone: [^true]. ^false! ! !LLParserTable methodsFor: 'private'! newRow ^self rowClass new! rowClass ^SetDictionary! ! !LLParserTable methodsFor: 'accessing'! atNonterminal: nont andTerminal: term addProduction: prod | row | row := self at: nont ifAbsent: [self at: nont put: self newRow]. ^row at: term add: prod! productionAtNonterminal: nont andTerminal: term | row | row := self at: nont ifAbsent: [self raiseNoTransitionExceptionErrorString: 'illegal nonterminal symbol encountered: ' , nont]. ^row at: term ifAbsent: [self raiseNoTransitionExceptionErrorString: 'expecting one of ' , row keys printString , ' but encountered: ''' , term , '''']! ! !LLParserTable methodsFor: 'converting'! spaceOptimize "Assumes self isDeterministic." self associationsDo: [:assoc | self at: assoc key put: assoc value asDictionary]! ! !LLParserTable methodsFor: 'exception handling'! raiseNoTransitionExceptionErrorString: aString self class noTransitionSignal raiseErrorString: aString! ! "-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- "! LLParserTable class instanceVariableNames: 'noTransitionSignal '! !LLParserTable class methodsFor: 'state accessing'! noTransitionSignal ^noTransitionSignal! noTransitionSignal: argument noTransitionSignal := argument! ! !LLParserTable class methodsFor: 'class initialization'! initialize "LLParserTable initialize" self noTransitionSignal: (Signal new nameClass: self message: #noTransitionSymbol)! ! GrammarProduction variableSubclass: #TransductionGrammarProduction instanceVariableNames: 'translationSymbol ' classVariableNames: '' poolDictionaries: '' category: 'T-gen-Scanning/Parsing'! TransductionGrammarProduction comment: '================================================= Copyright (c) 1992 by Justin O. Graver. All rights reserved (with exceptions). For complete information evaluate "Object tgenCopyright." ================================================= I add a translation attribute to context-free grammar productions so that I can be used to build simple transduction grammars (or syntax-directed translation scheme). Transduction grammars are used to build abstract syntax trees rather than derivation trees during parsing. For more information, refer to Chapter 7. ("Syntax-Directed Translation") in {\em Compiler Construction: Theory and Practice} by Barrett, Bates, Gustafson, and Couch. Instance Variables: translationSymbol - used as basis for translation node when parsing.'! !TransductionGrammarProduction methodsFor: 'state accessing'! translationSymbol ^translationSymbol! translationSymbol: argument translationSymbol := argument! ! !TransductionGrammarProduction methodsFor: 'testing'! hasTranslation ^true! ! !TransductionGrammarProduction methodsFor: 'printing'! printOn: aStream super printOn: aStream. aStream nextPutAll: ' {'. self printSymbol: self translationSymbol asSymbol on: aStream. aStream nextPutAll: '} '! ! !TransductionGrammarProduction methodsFor: 'conversion'! asInitialLR0Item ^self lr0ItemClass leftHandSide: self leftHandSide preDotSymbols: OrderedCollection new postDotSymbols: self rightHandSide copy translationSymbol: self translationSymbol! asInitialLR1ItemWithLookahead: terminal ^self lr1ItemClass leftHandSide: self leftHandSide preDotSymbols: OrderedCollection new postDotSymbols: self rightHandSide copy lookahead: terminal translationSymbol: self translationSymbol! asNonLalrSuffixedProduction "Assuming I am of the form 'A.* -> B.* C.*', answer the prefix production 'A -> B C'." | separator lhs rhs | separator := self symbolSuffixSeparatorChar. lhs := self leftHandSide copyUpTo: separator. rhs := self rightHandSide collect: [:sym | sym copyUpTo: separator]. ^self species leftHandSide: lhs rightHandSide: rhs translationSymbol: self translationSymbol! ! !TransductionGrammarProduction methodsFor: 'private'! epsilonSymbol ^#nil! leftLiftSymbol ^#liftLeftChild! rightLiftSymbol ^#liftRightChild! ! !TransductionGrammarProduction methodsFor: 'parse tree building'! computeResultNodeFor: builder withArgNodes: nodes "Three kinds of translation symbols are currently supported: node names, special directives, and arbitrary message selectors. For a node name, a new instance of the specified node is created and given nodes, if any, as its children. The special directive 'nil' simply returns nil. The directive liftRightChild adds any nodes preceeding the right-most node as children to the right-most node, and returns the right-most node. The directive liftLeftChild works in an analogous fashion. Arbitrary message selectors must take the number of arguments in nodes and are invoked as a builder message, thus allowing users to define their own tree-building messages." | symbol node | symbol := self translationSymbol asSymbol. symbol first isUppercase ifTrue: [^nodes isEmpty ifTrue: [builder makeNewNode: symbol] ifFalse: [builder makeNewNode: symbol withChildren: nodes]]. symbol = self epsilonSymbol ifTrue: [^builder answerNil]. symbol = self rightLiftSymbol ifTrue: [nodes size < 2 ifTrue: [self error: 'Only use liftRightChild when there are at least two right-hand-side nonterminals.']. "special case for building lists ending with epsilon" (nodes size = 2 and: [nodes last isNil]) ifTrue: [^builder answerArgument: nodes first]. node := nodes removeLast. ^builder addChildrenFirst: nodes to: node]. symbol = self leftLiftSymbol ifTrue: [nodes size < 2 ifTrue: [self error: 'Only use liftLeftChild when there are at least two right-hand-side nonterminals.']. "special case for building lists beginning with epsilon" (nodes size = 2 and: [nodes first isNil]) ifTrue: [^builder answerArgument: nodes last]. node := nodes removeFirst. ^builder addChildrenLast: nodes to: node]. symbol numArgs = nodes size ifFalse: [self error: 'Translation message selectors must have the same number of arguments as right-hand-side nonterminals.']. nodes isEmpty ifTrue: [^builder perform: symbol]. "It may be more efficient to check the number of arguments and use perform:with:, etc., but probably not." ^builder perform: symbol withArguments: nodes asArray! computeResultNodeFor: builder withTokenClassValue: value "I am assumed to be a production of the form 'A -> => symbol'. The symbol can be either a node name or a one-argument message selector. If it is a node name then create a new instance of that node with the specified attribute value. If it is a message selector then invoke the corresponding operation on the builder with the specified value." | symbol | symbol := self translationSymbol asSymbol. symbol first isUppercase ifTrue: [^builder makeNewNode: symbol withAttribute: value] ifFalse: [symbol numArgs = 1 ifTrue: [^builder perform: symbol with: value] ifFalse: [self error: 'Expected either a node name or a one argument message selector as a translation symbol.']]! ! !TransductionGrammarProduction methodsFor: 'reconstructing'! constructItsContentOn: aStream using: tokenTable "Emit lhs , #( rhs ) and translationSymbol on aStream" super constructItsContentOn: aStream using: tokenTable. (tokenTable indexOf: self translationSymbol) reconstructOn: aStream! ! "-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- "! TransductionGrammarProduction class instanceVariableNames: ''! !TransductionGrammarProduction class methodsFor: 'instance creation'! leftHandSide: arg1 rightHandSide: arg2 translationSymbol: arg3 | newMe | newMe := self new. newMe leftHandSide: arg1. newMe rightHandSide: arg2. newMe translationSymbol: arg3. ^newMe! ! Object subclass: #TokenTypeActionHolder instanceVariableNames: 'type action ' classVariableNames: '' poolDictionaries: '' category: 'T-gen-Scanning/Parsing'! TokenTypeActionHolder comment: '================================================= Copyright (c) 1992 by Justin O. Graver. All rights reserved (with exceptions). For complete information evaluate "Object tgenCopyright." ================================================= I am used to package token type and actions together for transport between FSAFinalStates and the scanner. Instance Variables: type - token type. action - a message selector that will be sent to the scanner (via #perform:) when a token with this type is recognized.'! !TokenTypeActionHolder methodsFor: 'state accessing'! action ^action! action: argument action := argument! type ^type! type: argument type := argument! ! !TokenTypeActionHolder methodsFor: 'printing'! printOn: aStream aStream nextPutAll: self type. aStream nextPutAll: ' : {'. aStream nextPutAll: self action. aStream nextPutAll: '} ;'! ! "-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- "! TokenTypeActionHolder class instanceVariableNames: ''! !TokenTypeActionHolder class methodsFor: 'instance creation'! type: arg1 action: arg2 | newMe | newMe := self new. newMe type: arg1. newMe action: arg2. ^newMe! ! LRParserState initialize! LLParserTable initialize!