From f9a48321d76f468825f20e9fd972401e3950cd58 Mon Sep 17 00:00:00 2001 From: Frederik Bosch Date: Mon, 30 Nov 2015 13:32:29 +0100 Subject: [PATCH] easy method to create for loops --- src/Xpath/Compiler.php | 41 ++++++++++--- src/Xpath/ForLoopConstructor.php | 61 +++++++++++++++++++ tests/Integration/Schema/XsSequenceTest.php | 12 ---- tests/Integration/Xpath/ForLoopTest.php | 18 ++++++ tests/Integration/Xpath/SequenceTest.php | 12 ++++ tests/Stubs/Xpath/ForLoop/for-each.xsl | 11 ++++ tests/Stubs/Xpath/ForLoop/simple.xsl | 10 +++ .../Xpath/Sequence/constructor-integer.xsl | 11 ++++ .../Sequence/constructor-string.xsl} | 0 tests/Stubs/Xpath/Sequence/reverse.xsl | 2 +- 10 files changed, 155 insertions(+), 23 deletions(-) create mode 100644 src/Xpath/ForLoopConstructor.php delete mode 100644 tests/Integration/Schema/XsSequenceTest.php create mode 100644 tests/Integration/Xpath/ForLoopTest.php create mode 100644 tests/Stubs/Xpath/ForLoop/for-each.xsl create mode 100644 tests/Stubs/Xpath/ForLoop/simple.xsl create mode 100644 tests/Stubs/Xpath/Sequence/constructor-integer.xsl rename tests/Stubs/{Schema/string-sequence.xsl => Xpath/Sequence/constructor-string.xsl} (100%) diff --git a/src/Xpath/Compiler.php b/src/Xpath/Compiler.php index 9ad2fde..1c28817 100644 --- a/src/Xpath/Compiler.php +++ b/src/Xpath/Compiler.php @@ -18,9 +18,13 @@ final class Compiler */ private $functions; /** - * @var SequenceConstructor + * @var ReturnXsSequenceFunction */ private $sequenceConstructor; + /** + * @var ReturnXsSequenceFunction + */ + private $forLoopConstructor; /** * @param FunctionMap $functions @@ -29,6 +33,7 @@ public function __construct(FunctionMap $functions) { $this->functions = $functions; $this->sequenceConstructor = new ReturnXsSequenceFunction(new SequenceConstructor()); + $this->forLoopConstructor = new ForLoopConstructor(); } /** @@ -43,7 +48,7 @@ public function compile($xpathExpression, DOMNode $currentElement) foreach ($lexer as $token) { $nextToken = $lexer->peek($lexer->key() + 1); if ($nextToken === '(') { - $resultTokens = array_merge($resultTokens, $this->createFunctionTokens($lexer, $token, $currentElement)); + $resultTokens = array_merge($resultTokens, $this->createFunctionTokens($lexer, $currentElement)); continue; } @@ -52,6 +57,13 @@ public function compile($xpathExpression, DOMNode $currentElement) continue; } + if ($this->isForLoop($lexer)) { + array_pop($resultTokens); + array_pop($resultTokens); + $resultTokens = array_merge($resultTokens, $this->createForLoop($lexer, $currentElement)); + continue; + } + $resultTokens[] = $token; } @@ -60,11 +72,11 @@ public function compile($xpathExpression, DOMNode $currentElement) /** * @param Lexer $lexer - * @param $token - * @param $currentElement + * @param DOMNode $currentElement * @return string[] */ - private function createFunctionTokens (Lexer $lexer, $token, $currentElement) { + private function createFunctionTokens (Lexer $lexer, DOMNode $currentElement) { + $token = $lexer->current(); $namespaces = FetchNamespacesFromNode::fetch($currentElement); $functionName = $this->convertTokenToFunctionName($token, $namespaces); @@ -109,18 +121,27 @@ private function isSequence (Lexer $lexer) { if ($lexer->key() === 0) { return true; } else { -// $prevToken = $lexer->peek($lexer->key() - 1); -// return preg_match('/[\i-[:]][\c-[:]]*/', $prevToken) === 0; - return false; + $prevToken = $lexer->peek($lexer->key() - 1); + return $prevToken === '(' || preg_match('/\s/', $prevToken) === 1; } } /** * @param Lexer $lexer - * @param $currentElement + * @param DOMNode $currentElement * @return string[] */ - private function createSequenceTokens (Lexer $lexer, $currentElement) { + private function createSequenceTokens (Lexer $lexer, DOMNode $currentElement) { return $this->sequenceConstructor->replace($lexer, $currentElement); } + + private function isForLoop(Lexer $lexer) + { + return ($lexer->current() === 'to' && preg_match('/\s/', $lexer->peek($lexer->key() - 1)) === 1); + } + + private function createForLoop(Lexer $lexer, DOMNode $currentElement) + { + return $this->forLoopConstructor->replace($lexer, $currentElement); + } } diff --git a/src/Xpath/ForLoopConstructor.php b/src/Xpath/ForLoopConstructor.php new file mode 100644 index 0000000..6ca087d --- /dev/null +++ b/src/Xpath/ForLoopConstructor.php @@ -0,0 +1,61 @@ +peek($lexer->key() - 2); + $resultTokens[] = ','; + $resultTokens[] = $lexer->peek($lexer->key() + 2); + $resultTokens[] = ')'; + $resultTokens[] = '/'; + $resultTokens[] = 'xs:sequence'; + $resultTokens[] = '/'; + $resultTokens[] = '*'; + + $lexer->seek($lexer->key() + 2); + + return $resultTokens; + } + + /** + * @param $first + * @param $last + * @return mixed + * @throws \Genkgo\Xsl\Schema\Exception\UnknownSequenceItemException + */ + public static function call($first, $last) + { + return XsSequence::fromArray(range($first, $last)); + } +} diff --git a/tests/Integration/Schema/XsSequenceTest.php b/tests/Integration/Schema/XsSequenceTest.php deleted file mode 100644 index 0263670..0000000 --- a/tests/Integration/Schema/XsSequenceTest.php +++ /dev/null @@ -1,12 +0,0 @@ -transformFile('Stubs/Schema/string-sequence.xsl'); - $this->assertContains('a b c', $result); - } - -} diff --git a/tests/Integration/Xpath/ForLoopTest.php b/tests/Integration/Xpath/ForLoopTest.php new file mode 100644 index 0000000..43402df --- /dev/null +++ b/tests/Integration/Xpath/ForLoopTest.php @@ -0,0 +1,18 @@ +transformFile('Stubs/Xpath/ForLoop/simple.xsl'); + $this->assertContains('1 2 3 4 5 6 7 8 9 10', $result); + } + + public function testForEach() + { + $result = $this->transformFile('Stubs/Xpath/ForLoop/for-each.xsl'); + $this->assertContains('12345678910', $result); + } + +} diff --git a/tests/Integration/Xpath/SequenceTest.php b/tests/Integration/Xpath/SequenceTest.php index b8664ef..744de41 100644 --- a/tests/Integration/Xpath/SequenceTest.php +++ b/tests/Integration/Xpath/SequenceTest.php @@ -3,6 +3,18 @@ class SequenceTest extends AbstractXpathTest { + public function testConstructorString() + { + $result = $this->transformFile('Stubs/Xpath/Sequence/constructor-string.xsl'); + $this->assertContains('a b c', $result); + } + + public function testConstructorInteger() + { + $result = $this->transformFile('Stubs/Xpath/Sequence/constructor-integer.xsl'); + $this->assertContains('1 2 3', $result); + } + public function testReverse() { $this->assertEquals( diff --git a/tests/Stubs/Xpath/ForLoop/for-each.xsl b/tests/Stubs/Xpath/ForLoop/for-each.xsl new file mode 100644 index 0000000..15dc499 --- /dev/null +++ b/tests/Stubs/Xpath/ForLoop/for-each.xsl @@ -0,0 +1,11 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/tests/Stubs/Xpath/ForLoop/simple.xsl b/tests/Stubs/Xpath/ForLoop/simple.xsl new file mode 100644 index 0000000..6d644ee --- /dev/null +++ b/tests/Stubs/Xpath/ForLoop/simple.xsl @@ -0,0 +1,10 @@ + + + + + + + + + + \ No newline at end of file diff --git a/tests/Stubs/Xpath/Sequence/constructor-integer.xsl b/tests/Stubs/Xpath/Sequence/constructor-integer.xsl new file mode 100644 index 0000000..0c0936f --- /dev/null +++ b/tests/Stubs/Xpath/Sequence/constructor-integer.xsl @@ -0,0 +1,11 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/tests/Stubs/Schema/string-sequence.xsl b/tests/Stubs/Xpath/Sequence/constructor-string.xsl similarity index 100% rename from tests/Stubs/Schema/string-sequence.xsl rename to tests/Stubs/Xpath/Sequence/constructor-string.xsl diff --git a/tests/Stubs/Xpath/Sequence/reverse.xsl b/tests/Stubs/Xpath/Sequence/reverse.xsl index c2e449a..96017dd 100644 --- a/tests/Stubs/Xpath/Sequence/reverse.xsl +++ b/tests/Stubs/Xpath/Sequence/reverse.xsl @@ -3,7 +3,7 @@ - + \ No newline at end of file