Class { #name : 'PEGFsa', #superclass : 'Object', #instVars : [ 'states', 'startState', 'name', 'distances', 'priorities' ], #category : 'PetitCompiler-FSA' } { #category : 'instance creation' } PEGFsa class >> new [ "return an initialized instance" ^ self basicNew initialize. ] { #category : 'comparing' } PEGFsa >> = anotherFsa [ " Please note what the compare does. IMO nothing useful for now. For comparing if two FSA's are equivalent, use isIsomorphicTo: " (self == anotherFsa) ifTrue: [ ^ true ]. (self class == anotherFsa class) ifFalse: [ ^ false ]. (startState = anotherFsa startState) ifFalse: [ ^ false ]. (name = anotherFsa name) ifFalse: [ ^ false ]. (states size = anotherFsa states size) ifFalse: [ ^ false ]. states do: [:s | (anotherFsa states contains: [ :e | e = s ]) ifFalse: [ ^ false ]. ]. ^ true ] { #category : 'modifications' } PEGFsa >> addState: state [ self assert: (states includes: state) not. states add: state ] { #category : 'modifications' } PEGFsa >> addTransitionFrom: fromState to: toState [ | transition | self assert: (states includes: fromState). self assert: (states includes: toState). transition := PEGFsaEpsilonTransition new destination: toState; priority: 0; yourself. fromState addTransition: transition. ] { #category : 'modifications' } PEGFsa >> addTransitionFrom: fromState to: toState on: character [ self addTransitionFrom: fromState to: toState on: character priority: 0 ] { #category : 'modifications' } PEGFsa >> addTransitionFrom: fromState to: toState on: character priority: priority [ | transition | transition := PEGFsaCharacterTransition new addCharacter: character; destination: toState; priority: priority; yourself. fromState addTransition: transition ] { #category : 'modifications' } PEGFsa >> addTransitionFrom: fromState to: toState onCharacterSet: characterSet [ self addTransitionFrom: fromState to: toState onCharacterSet: characterSet priority: 0 ] { #category : 'modifications' } PEGFsa >> addTransitionFrom: fromState to: toState onCharacterSet: characterSet priority: priority [ | transition | transition := PEGFsaCharacterTransition new characterSet: characterSet; destination: toState; priority: priority; yourself. fromState addTransition: transition ] { #category : 'modifications' } PEGFsa >> addTransitionFrom: fromState to: toState onPredicate: block priority: priority [ | transition | transition := PEGFsaPredicateTransition new predicate: block; destination: toState; priority: priority; yourself. fromState addTransition: transition ] { #category : 'modifications' } PEGFsa >> addTransitionFrom: fromState to: toState priority: priority [ | transition | "should not use minus priority epsilons any more" self assert: (priority == 0). self assert: (states includes: fromState). self assert: (states includes: toState). transition := PEGFsaEpsilonTransition new destination: toState; priority: priority; yourself. fromState addTransition: transition. ] { #category : 'modifications' } PEGFsa >> adopt: fsa [ states addAll: fsa reachableStates. ] { #category : 'accessing' } PEGFsa >> allTransitions [ ^ self allTransitions: IdentitySet new ] { #category : 'accessing' } PEGFsa >> allTransitions: collection [ self states do: [ :s | collection addAll: s transitions ]. ^ collection ] { #category : 'printing' } PEGFsa >> asString [ | stream | stream := WriteStream on: ''. self topologicalOrder do: [ :state | state printOn: stream. stream nextPutAll: '> '. (self transitionsFor: state) do: [ :transition | stream nextPut: (Character codePoint: 13). stream nextPut: (Character codePoint: 9). transition printOn: stream. ]. stream nextPut: (Character codePoint: 13). ]. " stream nextPutAll: 'finals: '. (states select: [:s | s isFinal ]) do: [:e | e printOn: stream ]. stream nextPut: (Character codePoint: 13). " ^ stream contents. ] { #category : 'analysis' } PEGFsa >> backTransitions [ | transitionSet | transitionSet := IdentitySet new. self computeDistances. self backTransitionsFrom: startState openSet: IdentitySet new transitionSet: transitionSet. ^ transitionSet ] { #category : 'analysis' } PEGFsa >> backTransitionsFrom: state openSet: openSet transitionSet: transitionSet [ (openSet includes: state) ifTrue: [ ^ self ]. openSet add: state. state transitions do: [ :t | ((openSet includes: t destination) and: [self is: state furtherThan: t destination]) ifTrue: [ transitionSet add: t ]. self backTransitionsFrom: t destination openSet: openSet copy transitionSet: transitionSet ] ] { #category : 'testing' } PEGFsa >> canHavePPCId [ ^ true ] { #category : 'testing' } PEGFsa >> checkConsistency [ self assert: (states includes: startState). states do: [ :s | s transitions do: [ :t | self assert: (states includes: t destination). ] ]. ^ true ] { #category : 'testing' } PEGFsa >> checkFinalStatesPriorities [ self assert: (self finalStates allSatisfy: #hasPriority) ] { #category : 'testing' } PEGFsa >> checkSanity [ self checkConsistency. self checkTransitionsIdentity. self checkTransitionsPriority. self checkFinalStatesPriorities. ] { #category : 'testing' } PEGFsa >> checkTransitionsIdentity [ | bag set | bag := IdentityBag new. set := IdentitySet new. bag := self allTransitions: bag. set := self allTransitions: set. self assert: bag size == set size. ] { #category : 'testing' } PEGFsa >> checkTransitionsPriority [ self finalStates do: [ :fs | fs isMultivalue ifFalse: [ fs transitions allSatisfy: [ :t | fs priority >= t priority ] ] ] ] { #category : 'transformations' } PEGFsa >> compact [ self error: 'deprecated?' ] { #category : 'analysis' } PEGFsa >> computeDistances [ | queue openSet | distances := IdentityDictionary new. queue := OrderedCollection with: startState. openSet := IdentitySet new. distances at: startState put: 0. [ queue isEmpty not ] whileTrue: [ | state | state := queue removeFirst. openSet add: state. state transitions do: [ :t | (openSet includes: (t destination)) ifFalse: [ distances at: (t destination ) put: ((distances at: state) + 1). queue addLast: (t destination) ] ] ]. ^ distances ] { #category : 'modifications' } PEGFsa >> decreasePriority [ ^ self decreasePriorityBy: 1 ] { #category : 'modifications' } PEGFsa >> decreasePriorityBy: value [ self states select: [ :s | s hasPriority ] thenDo: [ :s | s decreasePriorityBy: value. ]. self allTransitions do: [ :t | t decreasePriorityBy: value ] ] { #category : 'ids' } PEGFsa >> defaultName [ ^ #fsa ] { #category : 'modifications - determinization' } PEGFsa >> determinize [ ^ PEGFsaSequenceDeterminizator new determinize: self. ] { #category : 'modifications - determinization' } PEGFsa >> determinize: joinDictionary [ self error: 'deprecated'. self removeEpsilons. self removeUnreachableStates. self removeLowPriorityTransitions. self mergeTransitions. states := self topologicalOrder asOrderedCollection. states do: [ :state | state determinize: joinDictionary. ]. states := startState reachableStates. self removeUnreachableStates. self removeLowPriorityTransitions. self mergeTransitions. ] { #category : 'modifications - determinization' } PEGFsa >> determinizeChoice [ ^ PEGFsaChoiceDeterminizator new determinize: self. ] { #category : 'modifications - determinization' } PEGFsa >> determinizeStandard [ ^ PEGFsaDeterminizator new determinize: self. ] { #category : 'analysis' } PEGFsa >> epsilonDestinationsFrom: state [ | openSet | openSet := IdentitySet new. self epsilonDestinationsFrom: state openSet: openSet. ^ openSet ] { #category : 'analysis' } PEGFsa >> epsilonDestinationsFrom: state openSet: openSet [ (openSet includes: state) ifTrue: [ ^ self ]. openSet add: state. ((self transitionsFor: state) select: [ :t | t isEpsilon ]) do: [ :t | self epsilonDestinationsFrom: t destination openSet: openSet ] ] { #category : 'modifications' } PEGFsa >> finalState: state [ self assert: state isFinal not. state final: true. state priority: 0. ] { #category : 'analysis' } PEGFsa >> finalStates [ ^ self states select: [ :s | s isFinal ] ] { #category : 'modifications' } PEGFsa >> fixFinalStatePriorities [ self finalStates do: [ :s | s hasPriority ifFalse: [ s priority: 0 ] ] ] { #category : '*PetitCompiler-GUI' } PEGFsa >> gtGraphViewIn: composite [ composite roassal2 title: 'Graph'; initializeView: [ RTMondrian new ]; painting: [ :view | self viewGraphOn: view. ]. ] { #category : '*PetitCompiler-GUI' } PEGFsa >> gtStringViewIn: composite [ composite text title: 'Textual Representation'; display: [ :fsa | fsa asString ] ] { #category : 'analysis' } PEGFsa >> hasDistinctRetvals [ | finalStates retvals | finalStates := self finalStates. (finalStates anySatisfy: [ :s | s isMultivalue ]) ifTrue: [ ^ false ]. retvals := finalStates collect: [:s | s retval]. (finalStates size == 1) ifTrue: [ ^ true ]. (retvals asSet size == 1) ifTrue: [ ^ true ]. "final states leads only to final states with the same retval" (finalStates allSatisfy: [ :s | (self statesReachableFrom: s) allSatisfy: [ :rs | rs retval value isNil or: [ rs retval value == s retval value ] ] ]) ifTrue: [ ^ true ]. ^ false ] { #category : 'ids' } PEGFsa >> hasName [ ^ name isNil not ] { #category : 'analysis' } PEGFsa >> hasNoRetvals [ ^ self finalStates isEmpty ] { #category : 'comparing' } PEGFsa >> hash [ ^ states hash bitXor: (startState hash bitXor: name hash) ] { #category : 'initialization' } PEGFsa >> initialize [ states := IdentitySet new. ] { #category : 'analysis' } PEGFsa >> is: state furtherThan: anotherState [ ^ (distances at: state) >= (distances at: anotherState) ] { #category : 'analysis' } PEGFsa >> isBackTransition: t [ ^ self backTransitions includes: t ] { #category : 'testing' } PEGFsa >> isDeterministic [ self reachableStates do: [ :state | state transitionPairs do: [ :pair | ((pair first intersection: pair second) includes: true) ifTrue: [ ^ false ] ] ]. ^ true ] { #category : 'comparing' } PEGFsa >> isIsomorphicTo: anotherFsa [ | topologicalOrder anotherTopologicalOrder | " Please not that this version of comparison is sensitive to the order in which the transitions in state are ordered. " topologicalOrder := self topologicalOrder. anotherTopologicalOrder := anotherFsa topologicalOrder. topologicalOrder size == anotherTopologicalOrder size ifFalse: [ ^ false ]. topologicalOrder with: anotherTopologicalOrder do: [ :s1 :s2 | (s1 canBeIsomorphicTo: s2) ifFalse: [ ^ false ] ]. ^ true " transitions := topologicalOrder flatCollect: [ :s | s transitions ]. anotherTransitions := anotherTopologicalOrder flatCollect: [ :s | s transitions ]. " ] { #category : 'testing' } PEGFsa >> isReachableState: state [ ^ self reachableStates includes: state ] { #category : 'testing' } PEGFsa >> isStartState: state [ ^ startState == state ] { #category : 'testing' } PEGFsa >> isWithoutEpsilons [ self reachableStates do: [ :state | state transitions do: [ :t | t isEpsilon ifTrue: [ ^ false ] ] ]. ^ true ] { #category : 'analysis' } PEGFsa >> isWithoutPriorities [ ^ self states allSatisfy: [ :s | s hasPriority not or: [ s stateInfos allSatisfy: [ :i | i priority == 0 ] ] ]. ] { #category : 'transformations' } PEGFsa >> mergeTransitions [ | | self reachableStates do: [ :state | state mergeTransitions. ] ] { #category : 'modifications' } PEGFsa >> minimize [ ^ PEGFsaMinimizator new minimize: self ] { #category : 'accessing' } PEGFsa >> name [ ^ name ] { #category : 'accessing' } PEGFsa >> name: anObject [ name := anObject ] { #category : 'analysis' } PEGFsa >> nonFinalStates [ ^ self states reject: [ :s | s isFinal ] ] { #category : 'copying' } PEGFsa >> postCopy [ | map | super postCopy. map := IdentityDictionary new. states do: [ :s | map at: s put: s copy. ]. states := map values asIdentitySet. startState isNil ifFalse: [ startState := map at: startState. ]. states do: [ :s | s transitions do: [:t | t destination: (map at: t destination) ] ] ] { #category : 'ids' } PEGFsa >> prefix [ ^ 'scan' ] { #category : 'analysis' } PEGFsa >> reachableStates [ ^ self statesReachableFrom: startState ] { #category : 'modifications - epsilons' } PEGFsa >> removeEpsilonTransition: transition source: state [ ^ self removeEpsilonTransition: transition source: state openSet: IdentitySet new ] { #category : 'modifications - epsilons' } PEGFsa >> removeEpsilonTransition: transition source: source openSet: openSet [ | destination | (openSet includes: transition) ifTrue: [ self error: 'loop in epsilons?!' ]. openSet add: transition. destination := transition destination. "First Remove Recursively" ((self transitionsFor: destination ) select: [ :t | t isEpsilon ]) do: [ :t | self removeEpsilonTransition: t source: destination openSet: openSet ]. self assert: transition isEpsilon. self assert: transition priority = 0. (destination transitions) do: [ :t | source addTransition: (t copy) ]. source mergeInfo: destination into: source. destination isFinal ifTrue: [ source final: true. source retval: destination retval. ]. source removeTransition: transition. ] { #category : 'modifications - epsilons' } PEGFsa >> removeEpsilons [ "First, remove the negative values from epsilons" self removeNegativeEpsilons. states do: [ :state | self removeEpsilonsFor: state ] ] { #category : 'modifications - epsilons' } PEGFsa >> removeEpsilonsFor: state [ (self transitionsFor: state) copy do: [ :t | (t isEpsilon and: [ t destination isStub not ]) ifTrue: [ self removeEpsilonTransition: t source: state ] ] ] { #category : 'modifications' } PEGFsa >> removeFinals [ self finalStates do: [ :s | s final: false ] ] { #category : 'transformations' } PEGFsa >> removeLowPriorityTransitions [ states do: [ :state | self removeLowPriorityTransitionsFor: state ] ] { #category : 'transformations' } PEGFsa >> removeLowPriorityTransitionsFor: state [ | transitions | state hasPriority ifFalse: [ ^ self ]. state isFinal ifFalse: [ ^ self ]. "TODO JK: I can probably cut some transitions from multivalu as well" state isMultivalue ifTrue: [ ^ self ]. transitions := state transitions copy. transitions do: [ :t | (t priority < state priority) ifTrue: [ state removeTransition: t ] ] ] { #category : 'modifications - epsilons' } PEGFsa >> removeNegativeEpsilonTransition: transition source: state [ ^ self removeNegativeEpsilonTransition: transition source: state openSet: IdentitySet new ] { #category : 'modifications - epsilons' } PEGFsa >> removeNegativeEpsilonTransition: transition source: source openSet: openSet [ | destination | (openSet includes: transition) ifTrue: [ self error: 'loop in epsilons?!' ]. openSet add: transition. destination := transition destination. "First Remove Recursively" ((self transitionsFor: destination ) select: [ :t | t isEpsilon ]) do: [ :t | self removeNegativeEpsilonTransition: t source: destination openSet: openSet ]. "JK: Problem alert: if two different epsilons point to the same state, it will decreas the state priority two times! I don't know how to handle this situation properly and I make sure during FSA generation that there are no two paths to one state (except for loops). " (self statesReachableFrom: destination) do: [ :s | s decreasePriorityBy: transition priority abs. s transitions do: [ :t | t decreasePriorityBy: transition priority abs ] ]. transition priority: 0. ] { #category : 'modifications - epsilons' } PEGFsa >> removeNegativeEpsilons [ " This will remove only negative values from epsilons, the epsilons itself will not be removed! " states do: [ :state | self removeNegativeEpsilonsFor: state ] ] { #category : 'modifications - epsilons' } PEGFsa >> removeNegativeEpsilonsFor: state [ (self transitionsFor: state) copy do: [ :t | t isEpsilon ifTrue: [ self removeNegativeEpsilonTransition: t source: state ] ] ] { #category : 'modifications' } PEGFsa >> removePriorities [ self states select: [ :s| s hasPriority ] thenDo: [ :s | s priority: 0 ]. self allTransitions do: [ :t | t priority: 0 ] ] { #category : 'modifications' } PEGFsa >> removeState: state [ self assert: (states includes: state). states remove: state. ] { #category : 'transformations' } PEGFsa >> removeUnreachableStates [ | reachable toRemove | reachable := self reachableStates. toRemove := OrderedCollection new. states do: [ :s | (reachable includes: s) ifFalse: [ toRemove add: s ] ]. toRemove do: [ :s | states remove: s ] ] { #category : 'modifications' } PEGFsa >> replace: state with: anotherState [ | transitions | self assert: (state isKindOf: PEGFsaState). self assert: (anotherState isKindOf: PEGFsaState). transitions := self allTransitions. transitions do: [ :t | (t destination == state) ifTrue: [ t destination: anotherState. ] ]. state == startState ifTrue: [ startState := anotherState ]. states remove: state. states add: anotherState. ] { #category : 'modifications' } PEGFsa >> retval: returnValue [ self finalStates do: [ :s | self assert: s retval isNil. s retval: returnValue ] ] { #category : 'accessing' } PEGFsa >> retvals [ ^ (self finalStates flatCollect: [ :e | e retvals collect: #value ]) asIdentitySet ] { #category : 'accessing' } PEGFsa >> startState [ self assert: (states includes: startState). ^ startState ] { #category : 'modifications' } PEGFsa >> startState: state [ self assert: (states includes: state). startState := state ] { #category : 'accessing' } PEGFsa >> stateNamed: stateName [ ^ states detect: [ :e | e name = stateName ] ] { #category : 'analysis' } PEGFsa >> statePairs [ | pairs ordered | pairs := OrderedCollection new. ordered := self states asOrderedCollection. 1 to: (ordered size - 1) do: [ :index1 | (index1 + 1) to: ordered size do: [ :index2 | pairs add: (PEGFsaPair with: (ordered at: index1) with: (ordered at: index2)) ] ]. self assert: (pairs allSatisfy: [ :e | e class == PEGFsaPair ]). ^ pairs ] { #category : 'accessing' } PEGFsa >> states [ ^ states ] { #category : 'accessing' } PEGFsa >> states: whatever [ states := whatever ] { #category : 'analysis' } PEGFsa >> statesReachableFrom: state [ | openSet | self assert: state isNil not. openSet := IdentitySet new. self statesReachableFrom: state openSet: openSet. ^ openSet ] { #category : 'analysis' } PEGFsa >> statesReachableFrom: state openSet: openSet [ (openSet contains: [:e | e == state]) ifTrue: [ ^ self ]. openSet add: state. (self transitionsFor: state) do: [ :t | self statesReachableFrom: t destination openSet: openSet ] ] { #category : 'ids' } PEGFsa >> suffix [ ^ nil ] { #category : 'analysis' } PEGFsa >> topologicalOrder [ | collection | collection := OrderedCollection new. self statesReachableFrom: startState openSet: collection. ^ collection ] { #category : 'accessing' } PEGFsa >> transitionFrom: from to: to [ ^ from transitions detect: [ :t | t destination = to ] ] { #category : 'accessing' } PEGFsa >> transitionsFor: state [ self assert: (states includes: state). ^ state transitions ] { #category : '*PetitCompiler-GUI' } PEGFsa >> viewGraphOn: b [ b shape circle size: 50. b shape color: Color gray muchLighter muchLighter. b shape withText: #gtName. b nodes: (self nonFinalStates). b shape circle size: 50. b shape color: Color gray muchLighter. b shape withText: #gtName. b nodes: (self finalStates). b shape arrowedLine. b edges connectToAll: [ :state | state transitions select: [:t | (self isBackTransition:t) not] thenCollect: #destination ] labelled: [ :t | (self transitionFrom: t key to: t value) gtName ]. b shape arrowedLine. b shape color: Color red. b edges connectToAll: [ :state | state transitions select: [:t | (self isBackTransition: t) ] thenCollect: #destination ] labelled: [ :t | (self transitionFrom: t key to: t value) gtName ]. b layout horizontalTree . b layout layout horizontalGap: 30. ^ b ]