analyzer/tests/PPAnalyzerTest.st
changeset 208 42c859858c78
child 259 0f1afe248885
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/analyzer/tests/PPAnalyzerTest.st	Tue Mar 04 16:42:53 2014 +0100
@@ -0,0 +1,526 @@
+"{ Package: 'stx:goodies/petitparser/analyzer/tests' }"
+
+PPAbstractParserTest subclass:#PPAnalyzerTest
+	instanceVariableNames:''
+	classVariableNames:''
+	poolDictionaries:''
+	category:'PetitAnalyzer-Tests'
+!
+
+
+!PPAnalyzerTest class methodsFor:'accessing'!
+
+packageNamesUnderTest
+	^ #('PetitAnalyzer')
+! !
+
+!PPAnalyzerTest methodsFor:'accessing'!
+
+grammarA
+	"Güting, Erwig, Übersetzerbau, Springer (p.63)"
+
+	| grammar |
+	grammar := Dictionary new.
+	
+	" terminals "
+	grammar at: #a put: $a asParser.
+	grammar at: #b put: $b asParser.
+	grammar at: #c put: $c asParser.
+	grammar at: #d put: $d asParser.
+	grammar at: #e put: nil asParser.
+	
+	" non terminals "
+	grammar at: #B put: (grammar at: #b) / (grammar at: #e).
+	grammar at: #A put: (grammar at: #a) / (grammar at: #B).
+	grammar at: #S put: (grammar at: #A) , (grammar at: #B) , (grammar at: #c) , (grammar at: #d).
+	
+	^ grammar	
+!
+
+grammarB
+	"The canonical grammar to exercise first- and follow-set calculation, probably originally from the dragon-book."
+
+	| grammar |
+	grammar := Dictionary new.
+	#(E Ep T Tp F) 		do: [ :each | grammar at: each put: (PPUnresolvedParser named: each) ].
+		
+	(grammar at: #E)		def: (grammar at: #T) , (grammar at: #Ep).
+	(grammar at: #Ep)	def: ($+ asParser , (grammar at: #T) , (grammar at: #Ep)) optional.
+	
+	(grammar at: #T)		def: (grammar at: #F) , (grammar at: #Tp).
+	(grammar at: #Tp)	def: ($* asParser , (grammar at: #F) , (grammar at: #Tp)) optional.
+	
+	(grammar at: #F)		def: ($( asParser , (grammar at: #E) , $) asParser) / $i asParser.
+			
+	#(E Ep T Tp F) 		do: [ :each | (grammar at: each) name: each ].
+	
+	^ grammar
+!
+
+grammarC
+	"A highly recrusive grammar."
+
+	| grammar |
+	grammar := PPUnresolvedParser new.
+	grammar def: (grammar , $+ asParser , grammar) / $1 asParser.
+	^ grammar
+!
+
+grammarD
+	"A highly ambiguous grammar from: Saichaitanya Jampana. Exploring the problem of ambiguity in context-free grammars. Masters thesis, Oklahoma State Un                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                    
+!
+
+grammarE
+	"The most stupid parser, it just references itself and never consumes anything. All algorithms should survive such an attack."
+
+	| parser |
+	parser := PPDelegateParser new.
+	parser setParser: parser.
+	^ parser
+! !
+
+!PPAnalyzerTest methodsFor:'testing'!
+
+testAllNamedParsers
+	| p1 p2 p3 |
+	p1 := (#digit asParser name: 'a').
+	p2 := (#digit asParser name: 'b') star.
+	p3 := (#digit asParser name: 'c') token end.
+	self assert: p1 allNamedParsers size = 1.
+	self assert: p1 allNamedParsers first name = 'a'.
+	self assert: p2 allNamedParsers size = 1.
+	self assert: p2 allNamedParsers first name = 'b'.
+	self assert: p3 allNamedParsers size = 1.
+	self assert: p3 allNamedParsers first name = 'c'
+!
+
+testAllParsers
+	| p1 p2 p3 |
+	p1 := #lowercase asParser.
+	p2 := p1 ==> #asUppercase.
+	p3 := PPUnresolvedParser new.
+	p3 def: p2 / p3.
+	self assert: p1 allParsers size = 1.
+	self assert: p2 allParsers size = 2.
+	self assert: p3 allParsers size = 3
+!
+
+testInnerChildren
+	| p1 p2 p3 |
+	p1 := (#digit asParser name: 'a').
+	p2 := (#digit asParser star name: 'b').
+	p3 := (#digit asParser name: 'c') token star end.
+	self assert: p1 innerChildren isEmpty.
+	self assert: p2 innerChildren size = 1.
+	self assert: (p2 innerChildren allSatisfy: [ :each | each name isNil ]).
+	self assert: p3 innerChildren size = 2.
+	self assert: (p3 innerChildren allSatisfy: [ :each | each name isNil ])
+!
+
+testIsNullable
+	self assert: $a asParser star isNullable.
+	self assert: nil asParser isNullable.
+
+	self deny: $a asParser plus isNullable.	
+	self deny: PPLiteralSequenceParser new isNullable.
+	self deny: PPLiteralObjectParser new isNullable.
+	self deny: PPPredicateParser new isNullable.
+	self deny: PPChoiceParser new isNullable.
+	self deny: PPSequenceParser new isNullable.
+	self deny: PPAndParser new isNullable.
+	self deny: PPTokenParser new isNullable
+!
+
+testIsTerminal
+	self assert: PPEpsilonParser new isTerminal.
+	self assert: PPFailingParser new isTerminal.
+	self assert: PPPluggableParser new isTerminal.
+	self assert: PPLiteralObjectParser new isTerminal.
+	self assert: PPLiteralSequenceParser new isTerminal.
+	self assert: PPPredicateObjectParser new isTerminal.
+	self assert: PPPredicateSequenceParser new isTerminal.
+	
+	self deny: ($a asParser / $b asParser) isTerminal.
+	self deny: ($a asParser , $b asParser) isTerminal.
+	self deny: ($a asParser and) isTerminal.
+	self deny: ($a asParser not) isTerminal
+!
+
+testNamedChildren
+	| p1 p2 p3 p4 |
+	p1 := (#digit asParser name: 'a').
+	p2 := (#digit asParser name: 'b') star.
+	p3 := (#digit asParser name: 'c') token end.
+	p4 := ((#digit asParser name: 'c') token name: 'd') end.
+	self assert: p1 namedChildren isEmpty.
+	self assert: p2 namedChildren size = 1.
+	self assert: p2 namedChildren first name = 'b'.
+	self assert: p3 namedChildren size = 1.
+	self assert: p3 namedChildren first name = 'c'.
+	self assert: p4 namedChildren size = 1.
+	self assert: p4 namedChildren first name = 'd'
+! !
+
+!PPAnalyzerTest methodsFor:'testing-cycleset'!
+
+testCycleSetGrammarA
+	self grammarA do: [ :each | self assert: each cycleSet isEmpty ]
+!
+
+testCycleSetGrammarB
+	self grammarB do: [ :each | self assert: each cycleSet isEmpty ]
+!
+
+testCycleSetGrammarC
+	| grammar cycleSet |
+	grammar := self grammarC.
+	cycleSet := grammar cycleSet.
+	self assert: (cycleSet size = 2).
+	self assert: (cycleSet includes: grammar)
+!
+
+testCycleSetGrammarD
+	| grammar cycleSet |
+	grammar := self grammarD.
+	
+	cycleSet := (grammar at: #S) cycleSet.
+	self assert: (cycleSet size = 4).
+	self assert: (cycleSet includes: (grammar at: #A)).
+	self assert: (cycleSet includes: (grammar at: #S)).
+	
+	cycleSet := (grammar at: #A) cycleSet.
+	self assert: (cycleSet size = 4).
+	self assert: (cycleSet includes: (grammar at: #A)).
+	self assert: (cycleSet includes: (grammar at: #S)).
+
+	cycleSet := (grammar at: #B) cycleSet.
+	self assert: (cycleSet size = 2).
+	self assert: (cycleSet includes: (grammar at: #B))
+!
+
+testCycleSetGrammarE
+	| grammar cycleSet |
+	grammar := self grammarE.
+	cycleSet := grammar cycleSet.
+	self assert: (cycleSet size = 1).
+	self assert: (cycleSet includes: grammar)
+!
+
+testCycleSetInChoice
+	| parser cycleSet |
+	parser := PPUnresolvedParser new.
+	parser def: parser / $a asParser.
+	cycleSet := parser cycleSet.
+	self assert: (cycleSet size = 1).
+	self assert: (cycleSet includes: parser).
+	
+	parser := PPUnresolvedParser new.
+	parser def: $a asParser / parser.
+	cycleSet := parser cycleSet.
+	self assert: (cycleSet size = 1).
+	self assert: (cycleSet includes: parser).
+!
+
+testCycleSetInSequence
+	| parser cycleSet |
+	parser := PPUnresolvedParser new.
+	parser def: parser , $a asParser.
+	cycleSet := parser cycleSet.
+	self assert: (cycleSet size = 1).
+	self assert: (cycleSet includes: parser).
+	
+	parser := PPUnresolvedParser new.
+	parser def: nil asParser , parser.
+	cycleSet := parser cycleSet.
+	self assert: (cycleSet size = 1).
+	self assert: (cycleSet includes: parser).
+	
+	parser := PPUnresolvedParser new.
+	parser def: $a asParser , parser.
+	cycleSet := parser cycleSet.
+	self assert: cycleSet isEmpty
+! !
+
+!PPAnalyzerTest methodsFor:'testing-firstset'!
+
+testFirstSetExpression
+	| grammar |
+	grammar := PPArithmeticParser new.
+	self assert: grammar start firstSet includes: '(-0123456789' epsilon: false.
+	self assert: grammar addition firstSet includes: '(-0123456789' epsilon: false.
+	self assert: grammar factors firstSet includes:  '(-0123456789' epsilon: false.
+	self assert: grammar multiplication firstSet includes:  '(-0123456789' epsilon: false.
+	self assert: grammar number firstSet includes: '-0123456789' epsilon: false.
+	self assert: grammar parentheses firstSet includes: '(' epsilon: false.
+	self assert: grammar power firstSet includes: '(-0123456789' epsilon: false.
+	self assert: grammar primary firstSet includes: '(-0123456789' epsilon: false.
+	self assert: grammar terms firstSet includes: '(-0123456789' epsilon: false
+!
+
+testFirstSetGrammarA
+	| grammar |
+	grammar := self grammarA.
+	self assert: (grammar at: #a) firstSet includes: 'a' epsilon: false.
+	self assert: (grammar at: #b) firstSet includes: 'b' epsilon: false.
+	self assert: (grammar at: #c) firstSet includes: 'c' epsilon: false.
+	self assert: (grammar at: #d) firstSet includes: 'd' epsilon: false.
+	self assert: (grammar at: #e) firstSet includes: '' epsilon: true.
+	self assert: (grammar at: #S) firstSet includes: 'abc' epsilon: false.
+	self assert: (grammar at: #A) firstSet includes: 'ab' epsilon: true.
+	self assert: (grammar at: #B) firstSet includes: 'b' epsilon: true
+!
+
+testFirstSetGrammarB
+	| grammar |
+	grammar := self grammarB.
+	self assert: (grammar at: #E) firstSet includes: '(i' epsilon: false.
+	self assert: (grammar at: #Ep) firstSet includes: '+' epsilon: true.
+	self assert: (grammar at: #T) firstSet includes: '(i' epsilon: false.
+	self assert: (grammar at: #Tp) firstSet includes: '*' epsilon: true.
+	self assert: (grammar at: #F) firstSet includes: '(i' epsilon: false
+!
+
+testFirstSetGrammarC
+	| grammar |
+	grammar := self grammarC.
+	self assert: grammar firstSet includes: '1' epsilon: false
+!
+
+testFirstSetGrammarD
+	| grammar |
+	grammar := self grammarD.
+	self assert: (grammar at: #S) firstSet includes: 'ab' epsilon: false.
+	self assert: (grammar at: #A) firstSet includes: 'ab' epsilon: false.
+	self assert: (grammar at: #B) firstSet includes: 'a' epsilon: false.
+	self assert: (grammar at: #a) firstSet includes: 'a' epsilon: false.
+	self assert: (grammar at: #b) firstSet includes: 'b' epsilon: false
+!
+
+testFirstSetGrammarE
+	self assert: self grammarE firstSet includes: '' epsilon: false
+!
+
+testFirstSetLambda
+	| grammar |
+	grammar := PPLambdaParser new.
+	self assert: grammar start firstSet includes: '(ABCDEFGHIJKLMNOPQRSTUVWXYZ\abcdefghijklmnopqrstuvwxyz' epsilon: false.
+	self assert: grammar abstraction firstSet includes: '\' epsilon: false.
+	self assert: grammar application firstSet includes: '(' epsilon: false.
+	self assert: grammar expression firstSet includes: '(ABCDEFGHIJKLMNOPQRSTUVWXYZ\abcdefghijklmnopqrstuvwxyz' epsilon: false.
+	self assert: grammar variable firstSet includes: 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz' epsilon: false
+! !
+
+!PPAnalyzerTest methodsFor:'testing-followset'!
+
+testFollowSetExampleA
+	| grammar followSets |
+	grammar := self grammarA.
+	followSets := (grammar at: #S) followSets.
+	self assert: (followSets at: (grammar at: #a)) includes: 'bc' epsilon: false.
+	self assert: (followSets at: (grammar at: #b)) includes: 'bc' epsilon: false.
+	self assert: (followSets at: (grammar at: #c)) includes: 'd' epsilon: false.
+	self assert: (followSets at: (grammar at: #d)) includes: '' epsilon: true.
+	self assert: (followSets at: (grammar at: #e)) includes: 'bc' epsilon: false.
+	self assert: (followSets at: (grammar at: #S)) includes: '' epsilon: true.
+	self assert: (followSets at: (grammar at: #A)) includes: 'bc' epsilon: false.
+	self assert: (followSets at: (grammar at: #B)) includes: 'bc' epsilon: false
+!
+
+testFollowSetExampleB
+	| grammar followSets |
+	grammar := self grammarB.
+	followSets := (grammar at: #E) followSets.
+	self assert: (followSets at: (grammar at: #E)) includes: ')' epsilon: true.
+	self assert: (followSets at: (grammar at: #Ep)) includes: ')' epsilon: true.
+	self assert: (followSets at: (grammar at: #T)) includes: ')+' epsilon: true.
+	self assert: (followSets at: (grammar at: #Tp)) includes: ')+' epsilon: true.
+	self assert: (followSets at: (grammar at: #F)) includes: ')*+' epsilon: true
+!
+
+testFollowSetExampleC
+	self assert: self grammarC followSet includes: '+' epsilon: true
+!
+
+testFollowSetExampleD
+	| grammar followSets |
+	grammar := self grammarD.
+	followSets := (grammar at: #S) followSets.
+	self assert: (followSets at: (grammar at: #S)) includes: 'a' epsilon: true.
+	self assert: (followSets at: (grammar at: #A)) includes: 'ab' epsilon: true.
+	self assert: (followSets at: (grammar at: #B)) includes: 'ab' epsilon: true.
+	self assert: (followSets at: (grammar at: #a)) includes: 'ab' epsilon: true.
+	self assert: (followSets at: (grammar at: #b)) includes: 'ab' epsilon: true
+!
+
+testFollowSetExampleE
+	self assert: self grammarE followSet includes: '' epsilon: true
+! !
+
+!PPAnalyzerTest methodsFor:'testing-transform'!
+
+testDelegateReplace
+	| one other delegate |
+	one := $a asParser.
+	other := $b asParser.
+	delegate := one token.
+	self assert: delegate children first == one.
+	self deny: delegate children first == other.
+	
+	delegate replace: other with: one.
+	self assert: delegate children first == one.
+	self deny: delegate children first == other.
+	
+	delegate replace: one with: other.
+	self deny: delegate children first == one.
+	self assert: delegate children first == other
+!
+
+testListReplace
+	| one other another list |
+	one := $a asParser.
+	other := $b asParser.
+	another := $c asParser.
+	list := one , another , one.
+	self assert: list children first == one.
+	self assert: list children second == another.
+	self assert: list children last == one.
+	
+	list replace: other with: one.
+	self assert: list children first == one.
+	self assert: list children second == another.
+	self assert: list children last == one.
+	
+	list replace: one with: other.
+	self assert: list children first == other.
+	self assert: list children second == another.
+	self assert: list children last == other.
+	
+	list replace: another with: one.
+	self assert: list children first == other.
+	self assert: list children second == one.
+	self assert: list children last == other
+!
+
+testRepetitionReplace
+	| one two otherone othertwo repetition |
+	one := $a asParser.
+	two := $b asParser.
+	otherone := $1 asParser.
+	othertwo := $2 asParser.
+	
+	repetition := one starLazy: two.
+	self assert: repetition children first == one.
+	self assert: repetition children second == two.
+	
+	repetition replace: one with: otherone.
+	self assert: repetition children first == otherone.
+	self assert: repetition children second == two.
+	
+	repetition replace: two with: othertwo.
+	self assert: repetition children first == otherone.
+	self assert: repetition children second == othertwo
+!
+
+testTransformIdentityGrammarC
+	| orig tran |
+	orig := self grammarC.
+	tran := orig transform: [ :each | each ].
+	self deny: orig == tran.
+	self deny: orig children first == tran children first.
+	self deny: orig children first children first == tran children first children first.
+	self deny: orig children first children last == tran children first children last.
+	self deny: orig children last == tran children last.
+	
+	self assert: orig class == PPChoiceParser.
+	self assert: orig children first class == PPSequenceParser.
+	self assert: orig children first children first == orig.
+	self assert: orig children first children last == orig.
+	self assert: orig children last class == PPLiteralObjectParser.
+
+	self assert: tran class == PPChoiceParser.
+	self assert: tran children first class == PPSequenceParser.
+	self assert: tran children first children first == tran.
+	self assert: tran children first children last == tran.
+	self assert: tran children last class == PPLiteralObjectParser
+!
+
+testTransformIdentityGrammarE
+	| orig tran |
+	orig := self grammarE.
+	tran := orig transform: [ :each | each ].
+	self deny: orig == tran.
+	self deny: orig children first = tran children first.
+	
+	self assert: orig class == PPDelegateParser.
+	self assert: orig children first == orig.
+	
+	self assert: tran class == PPDelegateParser.
+	self assert: tran children first == tran
+!
+
+testTransformWrapGrammarC
+	| orig tran |
+	orig := self grammarC.
+	tran := orig transform: [ :each | each memoized ].
+
+	self assert: orig class == PPChoiceParser.
+	self assert: orig children first class == PPSequenceParser.
+	self assert: orig children first children first == orig.
+	self assert: orig children first children last == orig.
+	self assert: orig children last class == PPLiteralObjectParser.
+	
+	self assert: tran class == PPMemoizedParser.
+	self assert: tran children first class == PPChoiceParser.
+	self assert: tran children first children first class == PPMemoizedParser.
+	self assert: tran children first children first children first class == PPSequenceParser.
+	self assert: tran children first children first children first children first == tran.
+	self assert: tran children first children first children first children last == tran.
+	self assert: tran children first children last class == PPMemoizedParser.
+	self assert: tran children first children last children first class == PPLiteralObjectParser
+!
+
+testTransformWrapGrammarE
+	| orig tran |
+	orig := self grammarE.
+	tran := orig transform: [ :each | each memoized ].
+	
+	self assert: orig class == PPDelegateParser.
+	self assert: orig children first == orig.
+	
+	self assert: tran class == PPMemoizedParser.
+	self assert: tran children first class == PPDelegateParser.
+	self assert: tran children first children first == tran
+! !
+
+!PPAnalyzerTest methodsFor:'utilities'!
+
+assert: aCollection includes: aString epsilon: aBoolean
+	| parsers checker stream |
+	parsers := aCollection
+		collect: [ :each | each end ].
+	checker := [ :string |
+		parsers anySatisfy: [ :parser |
+			(parser parse: string asPetitStream)
+				isPetitFailure not ] ].
+	stream := WriteStream on: String new.
+	32 to: 127 do: [ :index |
+		(checker value: (String with: (Character value: index)))
+			ifTrue: [ stream nextPut: (Character value: index) ] ].
+	self
+		assert: stream contents = aString
+		description: 'Expected ' , aString printString , ', but got ' , stream contents printString.
+	self
+		assert: (checker value: '') = aBoolean
+		description: 'Expected epsilon to ' , (aBoolean ifTrue: [ 'be' ] ifFalse: [ 'not be' ]) , '  included'
+! !
+
+!PPAnalyzerTest class methodsFor:'documentation'!
+
+version
+    ^ '$Header: /cvs/stx/stx/goodies/petitparser/analyzer/tests/PPAnalyzerTest.st,v 1.1 2014-03-04 15:42:53 cg Exp $'
+!
+
+version_CVS
+    ^ '$Header: /cvs/stx/stx/goodies/petitparser/analyzer/tests/PPAnalyzerTest.st,v 1.1 2014-03-04 15:42:53 cg Exp $'
+! !
+