aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorIvan Enderlin <ivan.enderlin@hoa-project.net>2016-08-14 17:40:38 +0200
committerIvan Enderlin <ivan.enderlin@hoa-project.net>2016-08-14 18:20:06 +0200
commit216cef959f1631c22c7794279d4eaaf242eb6980 (patch)
tree2602f4e6f190373d460167470be0e24b5034644a
parentab66e1e49794ddc6cd2841064e71a30126a04e7f (diff)
downloadCompiler-216cef959f1631c22c7794279d4eaaf242eb6980.zip
Compiler-216cef959f1631c22c7794279d4eaaf242eb6980.tar.gz
Compiler-216cef959f1631c22c7794279d4eaaf242eb6980.tar.bz2
Test: Write `Hoa\Compiler\Llk\Llk` test suite.
-rw-r--r--Test/Unit/Llk/Llk.php373
1 files changed, 373 insertions, 0 deletions
diff --git a/Test/Unit/Llk/Llk.php b/Test/Unit/Llk/Llk.php
new file mode 100644
index 0000000..416c96b
--- /dev/null
+++ b/Test/Unit/Llk/Llk.php
@@ -0,0 +1,373 @@
+<?php
+
+/**
+ * Hoa
+ *
+ *
+ * @license
+ *
+ * New BSD License
+ *
+ * Copyright © 2007-2016, Hoa community. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of the Hoa nor the names of its contributors may be
+ * used to endorse or promote products derived from this software without
+ * specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS AND CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+namespace Hoa\Compiler\Test\Unit\Llk;
+
+use Hoa\Compiler as LUT;
+use Hoa\Compiler\Llk\Llk as SUT;
+use Hoa\File;
+use Hoa\Test;
+
+/**
+ * Class \Hoa\Compiler\Test\Unit\Llk\Llk.
+ *
+ * Test suite of the LL(k) helper class.
+ *
+ * @copyright Copyright © 2007-2016 Hoa community
+ * @license New BSD License
+ */
+class Llk extends Test\Unit\Suite
+{
+ public function case_load_empty()
+ {
+ $this
+ ->given($stream = new File\ReadWrite('hoa://Test/Vfs/Empty.pp?type=file'))
+ ->exception(function () use ($stream) {
+ SUT::load($stream);
+ })
+ ->isInstanceOf(LUT\Exception::class)
+ ->hasMessage(
+ 'The grammar is empty: Nothing to read on the stream ' .
+ 'hoa://Test/Vfs/Empty.pp?type=file.'
+ );
+ }
+
+ public function case_load()
+ {
+ $this
+ ->given(
+ $stream = new File\ReadWrite('hoa://Test/Vfs/Grammar.pp?type=file'),
+ $stream->writeAll(
+ '%pragma hello world' . "\n" .
+ '%token foobar bazqux' . "\n" .
+ 'ruleA:' . "\n" .
+ ' <foobar>'
+ ),
+
+ $_ruleA = new LUT\Llk\Rule\Token('ruleA', 'foobar', null, -1, true),
+ $_ruleA->setPPRepresentation(' <foobar>')
+ )
+ ->when($result = SUT::load($stream))
+ ->then
+ ->object($result)
+ ->isInstanceOf(LUT\Llk\Parser::class)
+ ->array($result->getPragmas())
+ ->isEqualTo([
+ 'hello' => 'world'
+ ])
+ ->array($result->getTokens())
+ ->isEqualTo([
+ 'default' => [
+ 'foobar' => 'bazqux'
+ ]
+ ])
+ ->array($result->getRules())
+ ->isEqualTo([
+ 'ruleA' => $_ruleA
+ ]);
+ }
+
+ public function case_save()
+ {
+ $this
+ ->given(
+ $stream = new File\Read('hoa://Library/Compiler/Llk/Llk.pp'),
+ $parser = SUT::load($stream)
+ )
+ ->when($result = SUT::save($parser, 'Foobar'))
+ ->then
+ ->string($result)
+ ->isEqualTo(<<<'OUTPUT'
+class Foobar extends \Hoa\Compiler\Llk\Parser
+{
+ public function __construct()
+ {
+ parent::__construct(
+ [
+ 'default' => [
+ 'skip' => '\s',
+ 'or' => '\|',
+ 'zero_or_one' => '\?',
+ 'one_or_more' => '\+',
+ 'zero_or_more' => '\*',
+ 'n_to_m' => '\{[0-9]+,[0-9]+\}',
+ 'zero_to_m' => '\{,[0-9]+\}',
+ 'n_or_more' => '\{[0-9]+,\}',
+ 'exactly_n' => '\{[0-9]+\}',
+ 'token' => '[a-zA-Z_][a-zA-Z0-9_]*',
+ 'skipped' => '::',
+ 'kept_' => '<',
+ '_kept' => '>',
+ 'named' => '\(\)',
+ 'node' => '#[a-zA-Z_][a-zA-Z0-9_]*(:[mM])?',
+ 'capturing_' => '\(',
+ '_capturing' => '\)',
+ 'unification_' => '\[',
+ 'unification' => '[0-9]+',
+ '_unification' => '\]',
+ ],
+ ],
+ [
+ 0 => new \Hoa\Compiler\Llk\Rule\Concatenation(0, ['choice'], null),
+ 'rule' => new \Hoa\Compiler\Llk\Rule\Concatenation('rule', [0], '#rule'),
+ 2 => new \Hoa\Compiler\Llk\Rule\Token(2, 'or', null, -1, false),
+ 3 => new \Hoa\Compiler\Llk\Rule\Concatenation(3, [2, 'concatenation'], '#choice'),
+ 4 => new \Hoa\Compiler\Llk\Rule\Repetition(4, 0, -1, 3, null),
+ 'choice' => new \Hoa\Compiler\Llk\Rule\Concatenation('choice', ['concatenation', 4], null),
+ 6 => new \Hoa\Compiler\Llk\Rule\Concatenation(6, ['repetition'], '#concatenation'),
+ 7 => new \Hoa\Compiler\Llk\Rule\Repetition(7, 0, -1, 6, null),
+ 'concatenation' => new \Hoa\Compiler\Llk\Rule\Concatenation('concatenation', ['repetition', 7], null),
+ 9 => new \Hoa\Compiler\Llk\Rule\Concatenation(9, ['quantifier'], '#repetition'),
+ 10 => new \Hoa\Compiler\Llk\Rule\Repetition(10, 0, 1, 9, null),
+ 11 => new \Hoa\Compiler\Llk\Rule\Token(11, 'node', null, -1, true),
+ 12 => new \Hoa\Compiler\Llk\Rule\Repetition(12, 0, 1, 11, null),
+ 'repetition' => new \Hoa\Compiler\Llk\Rule\Concatenation('repetition', ['simple', 10, 12], null),
+ 14 => new \Hoa\Compiler\Llk\Rule\Token(14, 'capturing_', null, -1, false),
+ 15 => new \Hoa\Compiler\Llk\Rule\Token(15, '_capturing', null, -1, false),
+ 16 => new \Hoa\Compiler\Llk\Rule\Concatenation(16, [14, 'choice', 15], null),
+ 17 => new \Hoa\Compiler\Llk\Rule\Token(17, 'skipped', null, -1, false),
+ 18 => new \Hoa\Compiler\Llk\Rule\Token(18, 'token', null, -1, true),
+ 19 => new \Hoa\Compiler\Llk\Rule\Token(19, 'unification_', null, -1, false),
+ 20 => new \Hoa\Compiler\Llk\Rule\Token(20, 'unification', null, -1, true),
+ 21 => new \Hoa\Compiler\Llk\Rule\Token(21, '_unification', null, -1, false),
+ 22 => new \Hoa\Compiler\Llk\Rule\Concatenation(22, [19, 20, 21], null),
+ 23 => new \Hoa\Compiler\Llk\Rule\Repetition(23, 0, 1, 22, null),
+ 24 => new \Hoa\Compiler\Llk\Rule\Token(24, 'skipped', null, -1, false),
+ 25 => new \Hoa\Compiler\Llk\Rule\Concatenation(25, [17, 18, 23, 24], '#skipped'),
+ 26 => new \Hoa\Compiler\Llk\Rule\Token(26, 'kept_', null, -1, false),
+ 27 => new \Hoa\Compiler\Llk\Rule\Token(27, 'token', null, -1, true),
+ 28 => new \Hoa\Compiler\Llk\Rule\Token(28, 'unification_', null, -1, false),
+ 29 => new \Hoa\Compiler\Llk\Rule\Token(29, 'unification', null, -1, true),
+ 30 => new \Hoa\Compiler\Llk\Rule\Token(30, '_unification', null, -1, false),
+ 31 => new \Hoa\Compiler\Llk\Rule\Concatenation(31, [28, 29, 30], null),
+ 32 => new \Hoa\Compiler\Llk\Rule\Repetition(32, 0, 1, 31, null),
+ 33 => new \Hoa\Compiler\Llk\Rule\Token(33, '_kept', null, -1, false),
+ 34 => new \Hoa\Compiler\Llk\Rule\Concatenation(34, [26, 27, 32, 33], '#kept'),
+ 35 => new \Hoa\Compiler\Llk\Rule\Token(35, 'token', null, -1, true),
+ 36 => new \Hoa\Compiler\Llk\Rule\Token(36, 'named', null, -1, false),
+ 37 => new \Hoa\Compiler\Llk\Rule\Concatenation(37, [35, 36], null),
+ 'simple' => new \Hoa\Compiler\Llk\Rule\Choice('simple', [16, 25, 34, 37], null),
+ 39 => new \Hoa\Compiler\Llk\Rule\Token(39, 'zero_or_one', null, -1, true),
+ 40 => new \Hoa\Compiler\Llk\Rule\Token(40, 'one_or_more', null, -1, true),
+ 41 => new \Hoa\Compiler\Llk\Rule\Token(41, 'zero_or_more', null, -1, true),
+ 42 => new \Hoa\Compiler\Llk\Rule\Token(42, 'n_to_m', null, -1, true),
+ 43 => new \Hoa\Compiler\Llk\Rule\Token(43, 'n_or_more', null, -1, true),
+ 44 => new \Hoa\Compiler\Llk\Rule\Token(44, 'exactly_n', null, -1, true),
+ 'quantifier' => new \Hoa\Compiler\Llk\Rule\Choice('quantifier', [39, 40, 41, 42, 43, 44], null),
+ ],
+ [
+ ]
+ );
+
+ $this->getRule('rule')->setDefaultId('#rule');
+ $this->getRule('rule')->setPPRepresentation(' choice()');
+ $this->getRule('choice')->setPPRepresentation(' concatenation() ( ::or:: concatenation() #choice )*');
+ $this->getRule('concatenation')->setPPRepresentation(' repetition() ( repetition() #concatenation )*');
+ $this->getRule('repetition')->setPPRepresentation(' simple() ( quantifier() #repetition )? <node>?');
+ $this->getRule('simple')->setPPRepresentation(' ::capturing_:: choice() ::_capturing:: | ::skipped:: <token> ( ::unification_:: <unification> ::_unification:: )? ::skipped:: #skipped | ::kept_:: <token> ( ::unification_:: <unification> ::_unification:: )? ::_kept:: #kept | <token> ::named::');
+ $this->getRule('quantifier')->setPPRepresentation(' <zero_or_one> | <one_or_more> | <zero_or_more> | <n_to_m> | <n_or_more> | <exactly_n>');
+ }
+}
+
+OUTPUT
+);
+ }
+
+ public function case_parse_tokens()
+ {
+ $this
+ ->given(
+ $pp =
+ '%token foobar1 bazqux1' . "\n" .
+ '%token sourceNS1:foobar2 bazqux2' . "\n" .
+ '%token sourceNS2:foobar3 bazqux3 -> destinationNS' . "\n" .
+ '%token foobar4 barqux4 -> destinationNS'
+ )
+ ->when($result = SUT::parsePP($pp, $tokens, $rules, $pragmas, 'streamFoo'))
+ ->then
+ ->variable($result)
+ ->isNull()
+ ->array($tokens)
+ ->isEqualTo([
+ 'default' => [
+ 'foobar1' => 'bazqux1',
+ 'foobar4:destinationNS' => 'barqux4'
+ ],
+ 'sourceNS1' => [
+ 'foobar2' => 'bazqux2'
+ ],
+ 'sourceNS2' => [
+ 'foobar3:destinationNS' => 'bazqux3'
+ ]
+ ])
+ ->array($rules)
+ ->isEmpty()
+ ->array($pragmas)
+ ->isEmpty();
+ }
+
+ public function case_parse_skip_tokens()
+ {
+ $this
+ ->given(
+ $pp =
+ '%skip foobar1 bazqux1' . "\n" .
+ '%skip foobar2 bazqux2' . "\n" .
+ '%skip foobar3 bazqux3' . "\n" .
+ '%skip sourceNS1:foobar4 bazqux4' . "\n" .
+ '%skip sourceNS1:foobar5 bazqux5' . "\n" .
+ '%skip sourceNS2:foobar6 bazqux6' . "\n"
+ )
+ ->when($result = SUT::parsePP($pp, $tokens, $rules, $pragmas, 'streamFoo'))
+ ->then
+ ->variable($result)
+ ->isNull()
+ ->array($tokens)
+ ->isEqualTo([
+ 'default' => [
+ 'skip' => '(?:(?:bazqux1|bazqux2)|bazqux3)',
+ ],
+ 'sourceNS1' => [
+ 'skip' => '(?:bazqux4|bazqux5)'
+ ],
+ 'sourceNS2' => [
+ 'skip' => 'bazqux6'
+ ]
+ ])
+ ->array($rules)
+ ->isEmpty()
+ ->array($pragmas)
+ ->isEmpty();
+ }
+
+ public function case_parse_pragmas()
+ {
+ $this
+ ->given(
+ $pp =
+ '%pragma truly true' . "\n" .
+ '%pragma falsy false' . "\n" .
+ '%pragma numby 42' . "\n" .
+ '%pragma foobar hello' . "\n" .
+ '%pragma bazqux "world!" ' . "\n"
+ )
+ ->when($result = SUT::parsePP($pp, $tokens, $rules, $pragmas, 'streamFoo'))
+ ->then
+ ->variable($result)
+ ->isNull()
+ ->array($tokens)
+ ->isEqualTo(['default' => []])
+ ->array($rules)
+ ->isEmpty()
+ ->array($pragmas)
+ ->isIdenticalTo([
+ 'truly' => true,
+ 'falsy' => false,
+ 'numby' => 42,
+ 'foobar' => 'hello',
+ 'bazqux' => '"world!"'
+ ]);
+ }
+
+ public function case_unrecognized_instructions()
+ {
+ $this
+ ->given(
+ $pp =
+ '// shift line' . "\n" .
+ '%foobar baz qux' . "\n"
+ )
+ ->exception(function () use ($pp) {
+ SUT::parsePP($pp, $tokens, $rules, $pragmas, 'streamFoo');
+ })
+ ->isInstanceOf(LUT\Exception::class)
+ ->hasMessage(
+ 'Unrecognized instructions:' . "\n" .
+ ' %foobar baz qux' . "\n" .
+ 'in file streamFoo at line 2.'
+ );
+ }
+
+ public function case_parse_rules()
+ {
+ $this
+ ->given(
+ $pp =
+ 'ruleA:' . "\n" .
+ ' single space' . "\n" .
+ ' single space' . "\n" .
+ 'ruleB:' . "\n" .
+ ' many spaces'. "\n" .
+ "\t" . 'single tab'. "\n" .
+ 'ruleC:' . "\n" .
+ "\t\t" . 'many tabs' . "\n"
+ )
+ ->when($result = SUT::parsePP($pp, $tokens, $rules, $pragmas, 'streamFoo'))
+ ->then
+ ->variable($result)
+ ->isNull()
+ ->array($tokens)
+ ->isEqualTo(['default' => []])
+ ->array($rules)
+ ->isEqualTo([
+ 'ruleA' => ' single space single space',
+ 'ruleB' => ' many spaces single tab',
+ 'ruleC' => ' many tabs'
+ ])
+ ->array($pragmas)
+ ->isEmpty();
+ }
+
+ public function case_parse_skip_comments()
+ {
+ $this
+ ->given(
+ $pp =
+ '// Hello,' . "\n" .
+ '// World!'
+ )
+ ->when($result = SUT::parsePP($pp, $tokens, $rules, $pragmas, 'streamFoo'))
+ ->then
+ ->variable($result)
+ ->isNull()
+ ->array($tokens)
+ ->isEqualTo(['default' => []])
+ ->array($rules)
+ ->isEmpty()
+ ->array($pragmas)
+ ->isEmpty();
+ }
+}