# HG changeset patch # User Claus Gittinger # Date 1393947773 -3600 # Node ID 42c859858c78cd7bce294a9d00be6b5fb0a4aa15 # Parent 7db766b0a3e74069feca9345f6c858b7aa265a27 initial checkin diff -r 7db766b0a3e7 -r 42c859858c78 analyzer/tests/PPAnalyzerTest.st --- /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 $' +! ! +