compiler/tests/PPCCompilerTest.st
author Jan Vrany <jan.vrany@fit.cvut.cz>
Wed, 05 Nov 2014 21:40:01 +0000
changeset 413 5389e6fbb3bc
parent 401 compiler/tests/PetitCompilerTest.st@538267cab6ec
child 414 0eaf09920532
permissions -rw-r--r--
Classes renamed to ease following merge wirh Pharo version. * PetitBenchmark renamed to PPCBenchmark * PetitBenchmarkSources renamed to PPCBenchmarkResources * PetitCompilerTest renamed to PPCCompilerTest

"{ Package: 'stx:goodies/petitparser/compiler/tests' }"

PPAbstractParserTest subclass:#PPCCompilerTest
	instanceVariableNames:'parser result context'
	classVariableNames:''
	poolDictionaries:''
	category:'PetitCompiler-Tests-Core'
!


!PPCCompilerTest methodsFor:'context'!

context	
	^ context := PPCProfilingContext new
! !

!PPCCompilerTest methodsFor:'running'!

tearDown
    | parserClass |

    parserClass := (Smalltalk at: #PPGeneratedParser).
    parserClass notNil ifTrue:[ 
        parserClass removeFromSystem
    ].

    "Created: / 30-10-2014 / 22:56:19 / Jan Vrany <jan.vrany@fit.cvut.cz>"
! !

!PPCCompilerTest 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.
! !

!PPCCompilerTest 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.
! !

!PPCCompilerTest 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 methodDictionary 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.

    "Modified: / 30-10-2014 / 23:20:57 / Jan Vrany <jan.vrany@fit.cvut.cz>"
!

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 methodDictionary 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.

    "Modified: / 30-10-2014 / 23:21:17 / Jan Vrany <jan.vrany@fit.cvut.cz>"
!

testTrimmingToken
        parser := (#letter asParser, (#digit asParser / #letter asParser) star) trimmingToken compileWithParameters: { #profile -> true }.

        self assert: parser class methodDictionary 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: ''.

    "Modified: / 30-10-2014 / 23:21:23 / Jan Vrany <jan.vrany@fit.cvut.cz>"
! !

!PPCCompilerTest 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.
! !

!PPCCompilerTest 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'.
! !

!PPCCompilerTest 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 methodsDo: [ :m |
                source := m sourceCode.
                self assert: (normalParser parse: source) 
                          equals: (compiledParser parse: source withContext: self context). 
        ].

    "Modified: / 30-10-2014 / 23:22:00 / Jan Vrany <jan.vrany@fit.cvut.cz>"
!

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).
! !

!PPCCompilerTest class methodsFor:'documentation'!

version_HG

    ^ '$Changeset: <not expanded> $'
! !