--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/compiler/tests/PetitCompilerTest.st Sun Oct 26 01:03:31 2014 +0000
@@ -0,0 +1,631 @@
+"{ Package: 'stx:goodies/petitparser/compiler/tests' }"
+
+PPAbstractParserTest subclass:#PetitCompilerTest
+ instanceVariableNames:'parser result context'
+ classVariableNames:''
+ poolDictionaries:''
+ category:'PetitCompiler-Tests-Core'
+!
+
+!PetitCompilerTest methodsFor:'context'!
+
+context
+ ^ context := PPCProfilingContext new
+! !
+
+!PetitCompilerTest methodsFor:'test support'!
+
+assert: p parse: whatever
+ ^ result := super assert: p parse: whatever.
+!
+
+compile: aPPParser
+ | compiler |
+ compiler := PPCCompiler new.
+ compiler profile: true.
+ ^ (compiler compile: aPPParser as: #PPGeneratedParser) new.
+!
+
+compile: aPPParser params: params
+ | compiler |
+ compiler := PPCCompiler new.
+ compiler profile: true.
+ ^ (compiler compile: aPPParser as: #PPGeneratedParser params: params) new.
+!
+
+compileInlining: aPPParser
+ | compiler |
+ compiler := PPCCompiler new.
+ compiler inlining: true.
+ compiler profile: true.
+ ^ (compiler compile: aPPParser as: #PPGeneratedParser) new.
+!
+
+compileTree: tree params: params
+ | compiler mock |
+ compiler := PPCCompiler new.
+ compiler profile: true.
+ mock := nil asParser.
+ ^ (compiler compileTree: tree as: #PPGeneratedParser parser: mock params: params) new.
+!
+
+parse: whatever
+ ^ result := super parse: whatever.
+! !
+
+!PetitCompilerTest methodsFor:'tests - compiling'!
+
+testCompileAnd
+ parser := #digit asParser and compile.
+
+ self assert: parser parse: '1' to: $1 end: 0.
+ self assert: parser fail: 'a'.
+ self assert: parser fail: ''.
+
+ parser := ('foo' asParser, ($: asParser and)) compile.
+ self assert: parser parse: 'foo:' to: { 'foo'. $: } end: 3.
+!
+
+testCompileAny
+ parser := #any asParser compile.
+
+ self assert: parser parse: 'a' to: $a.
+ self assert: parser parse: '_' to: $_.
+ self assert: parser parse: '
+' to: Character cr.
+!
+
+testCompileAnyStar
+ parser := #any asParser star compile.
+
+ self assert: parser parse: 'aaa' to: { $a. $a . $a }.
+ self assert: parser parse: '' to: { }.
+
+!
+
+testCompileBlock
+ parser := (#letter asParser) plus ==> [ :res | res collect: [:each | each asUppercase ]].
+ parser := parser compile.
+
+ self assert: parser parse: 'foo' to: { $F . $O . $O}.
+ self assert: parser parse: 'bar' to: { $B . $A . $R}.
+ self assert: parser fail: ''.
+!
+
+testCompileCharacter
+ parser := $a asParser compile.
+
+ self assert: parser parse: 'a' to: $a.
+ self assert: parser fail: 'b'.
+
+ parser := $# asParser compile.
+ self assert: parser parse: '#'.
+!
+
+testCompileChoice
+ parser := (#digit asParser / #letter asParser) compile.
+
+ self assert: parser parse: '1' to: $1.
+ self assert: parser parse: 'a' to: $a.
+ self assert: parser fail: '_'.
+
+!
+
+testCompileLiteral
+ parser := 'foo' asParser compile.
+
+ self assert: parser parse: 'foo' to: 'foo'.
+ self assert: parser parse: 'foobar' to: 'foo' end: 3.
+ self assert: parser fail: 'boo'.
+
+ parser := '#[' asParser compile.
+ self assert: parser parse: '#[1]' to: '#[' end: 2.
+!
+
+testCompileLiteral2
+ | quote |
+ quote := '''' asParser.
+ parser := (quote, $a asParser )compile: #PPCompilerTest.
+ self assert: parser parse: '''a' to: {'''' . $a}.
+!
+
+testCompileNegate
+ parser := #letter asParser negate star, #letter asParser.
+ parser := parser compile.
+
+ self assert: parser parse: '...a' to: { { $. . $. . $. } . $a }.
+ self assert: parser parse: 'aaa' to: { {} . $a } end: 1.
+ self assert: parser fail: '...'.
+!
+
+testCompileNil
+ parser := nil asParser compile.
+
+ self assert: parser parse: 'a' to: nil end: 0.
+ self assert: parser parse: '' to: nil end: 0.
+
+ parser := nil asParser, 'foo' asParser.
+ self assert: parser parse: 'foo' to: { nil . 'foo' }
+!
+
+testCompileNot
+ parser := #digit asParser not compile.
+
+ self assert: parser parse: 'a' to: nil end: 0.
+ self assert: parser fail: '1'.
+ self assert: parser parse: '' to: nil end: 0.
+
+ parser := 'foo' asParser, $: asParser not.
+ parser := parser compile: #PPCompilerTest.
+ self assert: parser parse: 'foo' to: { 'foo'. nil } end: 3.
+
+ parser := 'foo' asParser, $: asParser not, 'bar' asParser.
+ parser := parser compile: #PPCompilerTest.
+ self assert: parser parse: 'foobar' to: { 'foo'. nil . 'bar' } end: 6.
+!
+
+testCompileNot2
+ parser := ($a asParser, $b asParser) not compile.
+
+ self assert: parser parse: '' to: nil end: 0.
+ self assert: parser parse: 'a' to: nil end: 0.
+ self assert: parser parse: 'aa' to: nil end: 0.
+ self assert: parser fail: 'ab'.
+!
+
+testCompileNotLiteral
+ parser := 'foo' asParser not compile.
+ self assert: parser class methods size = 2.
+
+ self assert: parser parse: 'bar' to: nil end: 0.
+
+ self assert: parser fail: 'foo'.
+ self assert: parser parse: '' to: nil end: 0.
+
+ parser := '''' asParser not compile.
+ self assert: parser class methods size = 2.
+
+ self assert: parser parse: 'a' to: nil end: 0.
+ self assert: parser fail: ''''.
+ self assert: parser parse: '' to: nil end: 0.
+
+
+ parser := ('foo' asParser, 'bar' asParser not) compile.
+ self assert: parser parse: 'foofoo' to: { 'foo'. nil } end: 3.
+
+ parser := ('foo' asParser, 'foo' asParser not, #any asParser star) compile.
+ self assert: parser parse: 'foobar' to: { 'foo'. nil . #($b $a $r) } end: 6.
+ self assert: parser fail: 'foofoo'.
+!
+
+testCompileOptional
+ parser := #digit asParser optional compile.
+
+ self assert: parser parse: '1' to: $1.
+ self assert: parser parse: 'a' to: nil end: 0.
+ self assert: parser class parsers isEmpty.
+
+ parser := (#digit asParser optional, #letter asParser) compile.
+ self assert: parser parse: '1a' to: { $1 . $a }.
+ self assert: parser parse: 'a' to: { nil . $a }.
+ self assert: parser class parsers isEmpty.
+!
+
+testCompilePlus
+ parser := #letter asParser plus compile: #PPCompilerTest.
+
+ self assert: parser parse: 'lorem' to: {$l. $o. $r. $e. $m} .
+ self assert: parser parse: 'a123' to: {$a} end: 1.
+ self assert: parser parse: 'ab123' to: {$a . $b} end: 2.
+
+ self assert: parser fail: ''.
+ self assert: parser fail: '123'.
+!
+
+testCompilePredicate
+ parser := #digit asParser compile.
+
+ self assert: parser parse: '1' to: $1.
+ self assert: parser parse: '0' to: $0.
+ self assert: parser fail: 'a'.
+!
+
+testCompilePredicate2
+ parser := #space asParser compile.
+
+ self assert: parser parse: ' ' to: Character space.
+ self assert: parser fail: 'a'.
+!
+
+testCompileSequence
+ parser := (#digit asParser, #letter asParser) compile.
+
+ self assert: parser parse: '1a' to: {$1 .$a}.
+
+
+!
+
+testCompileSequence2
+ parser := (#digit asParser, #space asParser, #letter asParser) compile: #PPCompilerTest.
+
+ self assert: parser parse: '9 c' to: {$9 . Character space. $c }.
+ self assert: parser fail: '9c'.
+
+!
+
+testCompileSequence3
+ parser := (#any asParser, #any asParser, #any asParser) compile.
+
+ self assert: parser parse: 'foo' to: #($f $o $o).
+ self assert: parser fail: 'fo'.
+
+!
+
+testCompileStar
+ parser := #letter asParser star compile.
+
+ self assert: parser parse: 'lorem' to: {$l. $o. $r. $e. $m} .
+ self assert: parser parse: '' to: {}.
+ self assert: parser parse: '123' to: {} end: 0.
+ self assert: parser parse: 'ab123' to: {$a . $b} end: 2.
+!
+
+testCompileStarLiteral
+ parser := 'foo' asParser star compile.
+
+ self assert: parser parse: 'foo' to: #('foo' ) .
+ self assert: parser parse: 'foofoo' to: #('foo' 'foo') .
+ self assert: parser parse: 'foofoofoo' to: #('foo' 'foo' 'foo') .
+ self assert: parser parse: '' to: #().
+ self assert: parser parse: 'bar' to: #() end: 0.
+!
+
+testCompileStarPredicate
+ parser := #letter asParser star compile.
+
+ self assert: parser parse: 'foo' to: #($f $o $o ) .
+ self assert: parser parse: '' to: #().
+ self assert: parser parse: '123' to: #() end: 0.
+!
+
+testCompileSymbolBlock
+ parser := (#letter asParser) plus ==> #second.
+ parser := parser compile: #PPCompilerTest.
+
+ self assert: parser parse: 'foo' to: $o.
+ self assert: parser parse: 'bar' to: $a.
+ self assert: parser fail: ''.
+ self should: [ parser parse: 'f' ] raise: Error.
+!
+
+testTrim
+ parser := self compile: $a asParser trim.
+
+ self assert: parser fail: ''.
+ self assert: parser parse: 'a' to: $a.
+ self assert: parser parse: ' a' to: $a.
+ self assert: parser parse: 'a ' to: $a.
+ self assert: parser parse: ' a ' to: $a.
+! !
+
+!PetitCompilerTest methodsFor:'tests - extra'!
+
+testCompileSmalltalkToken
+ parser := (#letter asParser, ((#letter asParser / #digit asParser) star)) smalltalkToken compile.
+
+ self assert: parser parse: 'foo'.
+ self assert: result inputValue = 'foo'.
+ self assert: parser parse: 'a'.
+ self assert: result inputValue = 'a'.
+ self assert: parser parse: 'f123a'.
+ self assert: result inputValue = 'f123a'.
+
+ self assert: parser fail: ''.
+ self assert: parser fail: '12'.
+
+ self assert: parser parse: ' "comment" foo'.
+ self assert: result inputValue = 'foo'.
+
+ self assert: parser parse: ' "comment" bar "another comment" '.
+ self assert: result inputValue = 'bar'.
+ self assert: parser parse: '
+ "b"
+ "b"
+ foo
+ "and yet, another comment"
+
+ "one more to make sure :)"
+ '.
+ self assert: result inputValue = 'foo'.
+!
+
+testCycle
+ | p1 block |
+
+ p1 := PPDelegateParser new.
+ block := ${ asParser, p1, $} asParser / nil asParser.
+ p1 setParser: block.
+
+ parser := block compile: #PPCompilerTest.
+ self assert: parser parse: '{}' to: { ${. nil . $} }.
+ self assert: parser parse: '{{}}' to: { ${. { ${ . nil . $} } . $} }.
+
+!
+
+testGuardSmalltlakToken
+ | charSet |
+ charSet := PPCCompiler new guardCharSet: 'foo' asParser smalltalkToken.
+ self assert: (charSet equals: (PPCharSetPredicate on: [ :char | char = $f ])).
+
+ parser := (#letter asParser, #word asParser star) smalltalkToken compileWithParameters: { #profile -> true }.
+ self assert: parser parse: 'bar'.
+ self assert: (context invocations anySatisfy: [ :e | e beginsWith: 'seq' ]).
+
+ self assert: parser fail: '123'.
+ self assert: (context invocations noneSatisfy: [ :e | e beginsWith: 'seq' ]).
+!
+
+testSmalltalkToken
+ parser := (#letter asParser, (#digit asParser / #letter asParser) star) smalltalkToken compileWithParameters: {#profile -> true}.
+
+ self assert: parser class methods size = 6.
+ self assert: parser parse: 'foo'.
+ self assert: result inputValue = 'foo'.
+ self assert: context invocationCount = 9.
+ self assert: context rememberCount = 0.
+ self assert: context lwRememberCount = 1.
+ self assert: context lwRestoreCount = 0.
+!
+
+testSmalltalkToken2
+ |id|
+ id := (#letter asParser, (#digit asParser / #letter asParser) star)
+ name: 'identifier';
+ yourself.
+
+ parser := (id, $: asParser) smalltalkToken
+ name: 'kw';
+ yourself.
+
+ parser := parser compileWithParameters: {#profile -> true}.
+
+ self assert: parser parse: 'foo:'.
+ self assert: result inputValue = 'foo:'.
+!
+
+testToken
+ parser := (#letter asParser, (#digit asParser / #letter asParser) star) flatten compile.
+
+ self assert: parser parse: 'foo' to: 'foo'.
+ self assert: parser parse: 'a' to: 'a'.
+ self assert: parser parse: 'f123a' to: 'f123a'.
+ self assert: parser fail: ''.
+!
+
+testToken2
+ parser := (#letter asParser, (#digit asParser / #letter asParser) star) token compileWithParameters: {#profile -> true}.
+
+ self assert: parser class methods size = 5.
+ self assert: parser parse: 'foo'.
+ self assert: result inputValue = 'foo'.
+ self assert: context invocationCount = 7.
+ self assert: context rememberCount = 0.
+ self assert: context lwRememberCount = 1.
+ self assert: context lwRestoreCount = 0.
+!
+
+testTrimmingToken
+ parser := (#letter asParser, (#digit asParser / #letter asParser) star) trimmingToken compileWithParameters: { #profile -> true }.
+
+ self assert: parser class methods size = 6.
+
+ self assert: parser parse: 'foo'.
+ self assert: result inputValue = 'foo'.
+
+ self assert: context invocationCount = 9.
+ self assert: context rememberCount = 0.
+ self assert: context lwRememberCount = 1.
+ self assert: context lwRestoreCount = 0.
+
+ self assert: parser parse: ' foo '.
+ self assert: result inputValue = 'foo'.
+
+
+
+ self assert: parser fail: '123'.
+
+ self assert: context invocationCount = 3.
+ self assert: context rememberCount = 0.
+ self assert: context lwRememberCount = 0.
+ self assert: context lwRestoreCount = 0.
+
+
+ self assert: parser fail: ''.
+! !
+
+!PetitCompilerTest methodsFor:'tests - first set'!
+
+testFirstSetSuchThat
+ | a b fs at |
+ a := $a asParser.
+ at := a trim.
+ b := $b asParser.
+ parser := b optional, at.
+ fs := parser firstSetSuchThat: [ :e | (e isKindOf: PPTrimmingParser) or: [ e isTerminal ] ].
+ self assert: (fs anySatisfy: [ :e | e = at ]).
+ self assert: (fs anySatisfy: [ :e | e = b ]).
+ self assert: (fs noneSatisfy: [ :e | e = a ]).
+!
+
+testFirstSetSuchThat2
+ | a b fs at bt |
+ a := $a asParser optional.
+ at := a trim.
+ b := $b asParser.
+ bt := b trim.
+ parser := at, bt.
+ fs := parser firstSetSuchThat: [ :e | (e isKindOf: PPTrimmingParser) or: [ e isTerminal ] ].
+ self assert: (fs anySatisfy: [ :e | e = at ]).
+ self assert: (fs anySatisfy: [ :e | e = bt ]).
+ self assert: fs size = 2.
+!
+
+testFirstSetSuchThat3
+ | a b c fs at bt ct |
+ a := $a asParser optional.
+ at := a trim.
+ b := $b asParser.
+ bt := b trim.
+ c := $c asParser.
+ ct := c trim.
+
+ parser := (at, bt optional) wrapped, at, ct.
+ fs := parser firstSetSuchThat: [ :e | (e isKindOf: PPTrimmingParser) or: [ e isTerminal ] ].
+ self assert: (fs anySatisfy: [ :e | e = at ]).
+ self assert: (fs anySatisfy: [ :e | e = bt ]).
+ self assert: (fs anySatisfy: [ :e | e = ct ]).
+ self assert: fs size = 3.
+! !
+
+!PetitCompilerTest methodsFor:'tests - guard'!
+
+testChoiceGuard
+ parser := ('foo' asParser trimmingToken / 'bar' asParser trimmingToken / $d asParser trimmingToken plus)
+ compileWithParameters: {#profile -> true}.
+
+ self assert: parser parse: 'foo'.
+ self assert: result inputValue = 'foo'.
+ self assert: (context invocations anySatisfy: [ :e | e beginsWith: 'token' ]).
+
+ self assert: parser parse: 'bar'.
+ self assert: result inputValue = 'bar'.
+
+ self assert: parser parse: ' foo'.
+ self assert: result inputValue = 'foo'.
+
+ self assert: parser parse: ' d'.
+ self assert: result first inputValue = 'd'.
+
+ self assert: parser fail: ''.
+ self assert: (context invocations noneSatisfy: [ :e | e beginsWith: 'predicate' ]).
+ self assert: (context invocations noneSatisfy: [ :e | e beginsWith: 'token' ]).
+
+ self assert: parser fail: 'zorg'.
+ self assert: (context invocations noneSatisfy: [ :e | e beginsWith: 'token' ]).
+!
+
+testEmptyChoiceGuard
+ parser := ('foo' asParser trimmingToken / 'bar' asParser trimmingToken / $d asParser trimmingToken star)
+ compileWithParameters: {#profile -> true}.
+
+ self assert: parser parse: 'foo'.
+ self assert: result inputValue = 'foo'.
+
+ self assert: parser parse: 'bar'.
+ self assert: result inputValue = 'bar'.
+
+ self assert: parser parse: ' foo'.
+ self assert: result inputValue = 'foo'.
+
+ self assert: parser parse: ' d'.
+ self assert: result first inputValue = 'd'.
+
+ self assert: parser parse: ''.
+
+ self assert: parser parse: 'zorg' end: 0.
+!
+
+testGuard1
+ | charSet |
+ charSet := PPCCompiler new guardCharSet: $a asParser.
+ self assert: (charSet equals: (PPCharSetPredicate on: [ :char | char = $a ])).
+!
+
+testGuard2
+ | charSet |
+ charSet := PPCCompiler new guardCharSet: #letter asParser.
+ self assert: (charSet equals: (PPCharSetPredicate on: [ :char | char isLetter ])).
+!
+
+testGuard3
+ | charSet |
+ charSet := PPCCompiler new guardCharSet: #letter asParser not.
+ self assert: (charSet equals: (PPCharSetPredicate on: [ :char | char isLetter not ])).
+!
+
+testGuard4
+ | charSet |
+ charSet := PPCCompiler new guardCharSet: (#letter asParser, #word asParser star).
+ self assert: (charSet equals: (PPCharSetPredicate on: [ :char | char isLetter ])).
+!
+
+testGuard5
+ | charSet |
+ charSet := PPCCompiler new guardCharSet: 'foo' asParser.
+ self assert: (charSet equals: (PPCharSetPredicate on: [ :char | char = $f ])).
+!
+
+testGuard6
+ | charSet |
+ charSet := PPCCompiler new guardCharSet: ('foo' asParser trimmingToken asCompilerTree optimizeTree).
+ self assert: (charSet equals: (PPCharSetPredicate on: [ :char | (char = $f) ]))
+!
+
+testGuard7
+ | charSet |
+ charSet := PPCCompiler new guardCharSet: ('foo' asParser trimmingToken / 'bar' asParser trimmingToken) asCompilerTree optimizeTree.
+ self assert: (charSet equals: (PPCharSetPredicate on: [ :char | (char = $f) or: [ char = $b ]] )).
+!
+
+testSequenceGuard
+ parser := ((#any asParser, #any asParser) wrapped, (#any asParser, #any asParser)) compile.
+
+ self assert: parser parse: 'fooo' to: #(#($f $o) #($o $o)).
+ self assert: parser parse: 'fo oo' to: #(#($f $o) #($ $o)) end: 4.
+ self assert: parser fail: 'fo'.
+
+!
+
+testTrimmerGuard
+ parser := $a asParser trim, $b asParser compile: #PPGeneratedParser parameters: { #profile -> true }.
+
+ self assert: parser parse: 'ab'.
+ self assert: parser parse: ' ab'.
+! !
+
+!PetitCompilerTest methodsFor:'tests - verification'!
+
+testClass
+ | compiledParser normalParser source |
+ normalParser := PPSmalltalkGrammar new.
+ compiledParser := normalParser compile.
+
+ Class methods do: [ :m |
+ source := m sourceCode.
+ self assert: (normalParser parse: source)
+ equals: (compiledParser parse: source withContext: self context).
+ ].
+!
+
+testObject
+ | compiledParser normalParser source |
+ normalParser := PPSmalltalkGrammar new.
+ compiledParser := normalParser compile.
+
+ Object methods do: [ :m |
+ source := m sourceCode.
+ self assert: (normalParser parse: source)
+ equals: (compiledParser parse: source withContext: self context).
+ ].
+!
+
+testWhitespace
+ | compiledParser normalParser source |
+ normalParser := PPSmalltalkGrammar new.
+ compiledParser := normalParser compile.
+
+ source := ' foo ^ 1'.
+ self assert: (normalParser parse: source)
+ equals: (compiledParser parse: source withContext: self context).
+! !
+