Skip to content

Commit

Permalink
easy method to create for loops
Browse files Browse the repository at this point in the history
  • Loading branch information
frederikbosch committed Nov 30, 2015
1 parent ff18c9e commit f9a4832
Show file tree
Hide file tree
Showing 10 changed files with 155 additions and 23 deletions.
41 changes: 31 additions & 10 deletions src/Xpath/Compiler.php
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,13 @@ final class Compiler
*/
private $functions;
/**
* @var SequenceConstructor
* @var ReturnXsSequenceFunction
*/
private $sequenceConstructor;
/**
* @var ReturnXsSequenceFunction
*/
private $forLoopConstructor;

/**
* @param FunctionMap $functions
Expand All @@ -29,6 +33,7 @@ public function __construct(FunctionMap $functions)
{
$this->functions = $functions;
$this->sequenceConstructor = new ReturnXsSequenceFunction(new SequenceConstructor());
$this->forLoopConstructor = new ForLoopConstructor();
}

/**
Expand All @@ -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;
}

Expand All @@ -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;
}

Expand All @@ -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);

Expand Down Expand Up @@ -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);
}
}
61 changes: 61 additions & 0 deletions src/Xpath/ForLoopConstructor.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
<?php
namespace Genkgo\Xsl\Xpath;

use DOMNode;
use Genkgo\Xsl\Callback\PhpCallback;
use Genkgo\Xsl\Callback\ReplaceFunctionInterface;
use Genkgo\Xsl\Schema\XsSequence;

/**
* Class Sequence
* @package Genkgo\Xsl\Xpath
*/
final class ForLoopConstructor implements ReplaceFunctionInterface
{
/**
* @param Lexer $lexer
* @param DOMNode $currentElement
* @return array|\string[]
*/
public function replace(Lexer $lexer, DOMNode $currentElement)
{
$resultTokens = [];
$resultTokens[] = 'php:function';
$resultTokens[] = '(';
$resultTokens[] = '\'';
$resultTokens[] = PhpCallback::class.'::callStatic';
$resultTokens[] = '\'';
$resultTokens[] = ',';
$resultTokens[] = '\'';
$resultTokens[] = static::class;
$resultTokens[] = '\'';
$resultTokens[] = ',';
$resultTokens[] = '\'';
$resultTokens[] = 'call';
$resultTokens[] = '\'';
$resultTokens[] = ',';
$resultTokens[] = $lexer->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));
}
}
12 changes: 0 additions & 12 deletions tests/Integration/Schema/XsSequenceTest.php

This file was deleted.

18 changes: 18 additions & 0 deletions tests/Integration/Xpath/ForLoopTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
<?php
namespace Genkgo\Xsl\Integration\Xpath;

class ForLoopTest extends AbstractXpathTest
{
public function testSimple()
{
$result = $this->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);
}

}
12 changes: 12 additions & 0 deletions tests/Integration/Xpath/SequenceTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -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(
Expand Down
11 changes: 11 additions & 0 deletions tests/Stubs/Xpath/ForLoop/for-each.xsl
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

<xsl:output omit-xml-declaration="yes" />

<xsl:template match="collection">
<xsl:for-each select="1 to 10">
<xsl:value-of select="." />
</xsl:for-each>
</xsl:template>

</xsl:stylesheet>
10 changes: 10 additions & 0 deletions tests/Stubs/Xpath/ForLoop/simple.xsl
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

<xsl:output omit-xml-declaration="yes" />

<xsl:template match="collection">
<xsl:variable name="list" select="1 to 10" />
<xsl:value-of select="$list" />
</xsl:template>

</xsl:stylesheet>
11 changes: 11 additions & 0 deletions tests/Stubs/Xpath/Sequence/constructor-integer.xsl
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

<xsl:output omit-xml-declaration="yes" />

<xsl:template match="collection">

<xsl:value-of select="(1, 2, 3)" />

</xsl:template>

</xsl:stylesheet>
File renamed without changes.
2 changes: 1 addition & 1 deletion tests/Stubs/Xpath/Sequence/reverse.xsl
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
<xsl:output omit-xml-declaration="yes" />

<xsl:template match="/">
<xsl:value-of select="reverse(tokenize('xsl2 is transpiled by genkgo/xsl', '\s'))" />
<xsl:value-of select="reverse(('xsl2','is','transpiled','by','genkgo/xsl'))" />
</xsl:template>

</xsl:stylesheet>

0 comments on commit f9a4832

Please sign in to comment.