Skip to content
This repository was archived by the owner on Sep 9, 2019. It is now read-only.

Yield from #42

Open
wants to merge 13 commits into
base: master
Choose a base branch
from
Open
7 changes: 6 additions & 1 deletion src/main/jay/grammars/php.jay
Original file line number Diff line number Diff line change
Expand Up @@ -76,11 +76,12 @@ use xp\compiler\ast\DynamicInstanceOfNode;
use xp\compiler\ast\DynamicVariableReferenceNode;
use xp\compiler\ast\NoopNode;
use xp\compiler\ast\YieldNode;
use xp\compiler\ast\YieldFromNode;
%}

%left ','
%left '(' ')'
%right T_YIELD
%right T_YIELD T_YIELD_FROM
%left T_BOOLEAN_OR
%left T_BOOLEAN_AND
%right '=' T_ADD_EQUAL T_SUB_EQUAL T_MUL_EQUAL T_DIV_EQUAL T_CONCAT_EQUAL T_MOD_EQUAL T_SHR T_SHL T_AND_EQUAL T_OR_EQUAL T_XOR_EQUAL
Expand Down Expand Up @@ -147,6 +148,7 @@ use xp\compiler\ast\YieldNode;
%token T_BREAK 365
%token T_CONTINUE 366
%token T_YIELD 367
%token T_YIELD_FROM 368

%token T_IF 370
%token T_ELSE 371
Expand Down Expand Up @@ -615,6 +617,9 @@ expression:
| T_YIELD expression T_DOUBLE_ARROW expression {
$$= $yyLex->create(new YieldNode($4, $2));
}
| T_YIELD_FROM expression {
$$= $yyLex->create(new YieldFromNode($2));
}
| assignment {
$$= $yyLex->create(new AssignmentNode($1));
}
Expand Down
7 changes: 6 additions & 1 deletion src/main/jay/grammars/xp.jay
Original file line number Diff line number Diff line change
Expand Up @@ -71,11 +71,12 @@ use xp\compiler\ast\WithNode;
use xp\compiler\ast\ArmNode;
use xp\compiler\ast\BracedExpressionNode;
use xp\compiler\ast\YieldNode;
use xp\compiler\ast\YieldFromNode;
%}

%left ','
%nonassoc T_ARROW
%right T_YIELD
%right T_YIELD T_YIELD_FROM
%left T_BOOLEAN_OR
%left T_BOOLEAN_AND
%right '=' T_ADD_EQUAL T_SUB_EQUAL T_MUL_EQUAL T_DIV_EQUAL T_CONCAT_EQUAL T_MOD_EQUAL T_AND_EQUAL T_OR_EQUAL T_XOR_EQUAL
Expand Down Expand Up @@ -147,6 +148,7 @@ use xp\compiler\ast\YieldNode;
%token T_BREAK 365
%token T_CONTINUE 366
%token T_YIELD 367
%token T_YIELD_FROM 368

%token T_IF 370
%token T_ELSE 371
Expand Down Expand Up @@ -838,6 +840,9 @@ expression:
| T_YIELD expression ':' expression {
$$= $yyLex->create(new YieldNode($4, $2));
}
| T_YIELD_FROM expression {
$$= $yyLex->create(new YieldFromNode($2));
}
| expression T_AS paramtyperef {
$$= $yyLex->create(new CastNode(array_merge($3, array('expression' => $1))));
}
Expand Down
23 changes: 22 additions & 1 deletion src/main/php/xp/compiler/ast/Visitor.class.php
Original file line number Diff line number Diff line change
Expand Up @@ -712,7 +712,28 @@ protected function visitBracedExpression(BracedExpressionNode $node) {
$node->expression= $this->visitOne($node->expression);
return $node;
}


/**
* Visit a yield statement
*
* @param xp.compiler.ast.Node node
*/
protected function visitYield(YieldNode $node) {
$node->key && $node->key= $this->visitOne($node->key);
$node->value && $node->value= $this->visitOne($node->value);
return $node;
}

/**
* Visit a yield statement
*
* @param xp.compiler.ast.Node node
*/
protected function visitYieldFrom(YieldFromNode $node) {
$node->expr= $this->visitOne($node->expr);
return $node;
}

/**
* Visit a node. Delegates to visit*() methods
*
Expand Down
21 changes: 21 additions & 0 deletions src/main/php/xp/compiler/ast/YieldFromNode.class.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
<?php namespace xp\compiler\ast;

/**
* The "yield from" statement
*/
class YieldFromNode extends Node {
public $expr;

public function __construct($expr) {
$this->expr= $expr;
}

/**
* Returns a hashcode
*
* @return string
*/
public function hashCode() {
return 'yield from '.$this->expr->hashCode();
}
}
10 changes: 9 additions & 1 deletion src/main/php/xp/compiler/emit/Emitter.class.php
Original file line number Diff line number Diff line change
Expand Up @@ -697,7 +697,15 @@ protected abstract function emitSilenceOperator($b, $silenced);
* @param xp.compiler.ast.YieldNode yield
*/
protected abstract function emitYield($b, $yield);


/**
* Emit a yield node
*
* @param xp.compiler.emit.Buffer b
* @param xp.compiler.ast.YieldFromNode yield
*/
protected abstract function emitYieldFrom($b, $yield);

/**
* Emit a single node
*
Expand Down
12 changes: 12 additions & 0 deletions src/main/php/xp/compiler/emit/php/Emitter.class.php
Original file line number Diff line number Diff line change
Expand Up @@ -2327,6 +2327,16 @@ protected function emitYield($b, $yield) {
$this->error('V505', 'Yield not supported in '.$this->getClassName());
}

/**
* Emit a yield from node
*
* @param xp.compiler.emit.Buffer b
* @param xp.compiler.ast.YieldFromNode yield
*/
protected function emitYieldFrom($b, $yield) {
$this->error('V505', 'Yield not supported in '.$this->getClassName());
}

/**
* Emit all given nodes
*
Expand Down Expand Up @@ -2450,6 +2460,8 @@ protected function emitTry($b, $try) {
V54Emitter::emitTry($b, $try);
}
}');
} else if (version_compare(PHP_VERSION, '7.0.0', 'gt')) {
return new V70Emitter();
} else if (version_compare(PHP_VERSION, '5.5.0', 'gt')) {
return new V55Emitter();
} else {
Expand Down
46 changes: 46 additions & 0 deletions src/main/php/xp/compiler/emit/php/V55Emitter.class.php
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,52 @@ protected function emitYield($b, $yield) {
}
}

/**
* Emit a yield from node
*
* @param xp.compiler.emit.Buffer b
* @param xp.compiler.ast.YieldFromNode yield
*/
protected function emitYieldFrom($b, $yield) {
static $shim= '
if ($iter instanceof \\Generator) {
defined(\'HHVM_VERSION\') && $iter->next();
$send= eval(\'return function() use($iter) {
$recv= null;
$send= true;
while ($iter->valid()) {
$next= $iter->current();
$send ? $iter->send($recv) : $iter->throw($recv);
try {
$recv= \'.(defined(\'HHVM_VERSION\') ? \'yield $next\' : \'(yield $next)\').\';
$send= true;
} catch (\\Exception $e) {
$recv= $e;
$send= false;
}
}
};\');
foreach ($send() as $next) { yield $next; }
} else {
foreach ($iter as $next) { yield $next; }
}
';

$iter= $this->tempVar();
$b->append($iter)->append('=');
$this->emitOne($b, $yield->expr);
$b->append(';');

$b->append(strtr($shim, [
"\n" => '',
' ' => '',
'$iter' => $iter,
'$recv' => $this->tempVar(),
'$send' => $this->tempVar(),
'$next' => $this->tempVar()
]));
}

/**
* Emit a try / catch block
*
Expand Down
18 changes: 18 additions & 0 deletions src/main/php/xp/compiler/emit/php/V70Emitter.class.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
<?php namespace xp\compiler\emit\php;

/**
* Emits sourcecode using PHP 7.0 sourcecode
*/
class V70Emitter extends V55Emitter {

/**
* Emit a yield from node
*
* @param xp.compiler.emit.Buffer b
* @param xp.compiler.ast.YieldFromNode yield
*/
protected function emitYieldFrom($b, $yield) {
$b->append('yield from ');
$this->emitOne($b, $yield->expr);
}
}
38 changes: 24 additions & 14 deletions src/main/php/xp/compiler/syntax/php/Lexer.class.php
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,6 @@ class Lexer extends \text\parser\generic\AbstractLexer {
'while' => Parser::T_WHILE,
'break' => Parser::T_BREAK,
'continue' => Parser::T_CONTINUE,
'yield' => Parser::T_YIELD,

'if' => Parser::T_IF,
'else' => Parser::T_ELSE,
Expand All @@ -66,15 +65,15 @@ class Lexer extends \text\parser\generic\AbstractLexer {
'.' => array('.=' => Parser::T_CONCAT_EQUAL),
'+' => array('+=' => Parser::T_ADD_EQUAL, '++' => Parser::T_INC),
'*' => array('*=' => Parser::T_MUL_EQUAL),
'/' => array('/=' => Parser::T_DIV_EQUAL),
'%' => array('%=' => Parser::T_MOD_EQUAL),
'=' => array('==' => Parser::T_EQUALS, '=>' => Parser::T_DOUBLE_ARROW),
'!' => array('!=' => Parser::T_NOT_EQUALS),
':' => array('::' => Parser::T_DOUBLE_COLON),
'|' => array('||' => Parser::T_BOOLEAN_OR, '|=' => Parser::T_OR_EQUAL),
'&' => array('&&' => Parser::T_BOOLEAN_AND, '&=' => Parser::T_AND_EQUAL),
'^' => array('^=' => Parser::T_XOR_EQUAL),
'?' => array('?>' => -1)
'?' => array('?>' => -1),
'yield' => array('yield from' => Parser::T_YIELD_FROM, 'yield' => Parser::T_YIELD)
);

const
Expand Down Expand Up @@ -212,6 +211,28 @@ public function advance() {
} else if ('$' === $token{0}) {
$this->token= Parser::T_VARIABLE;
$this->value= substr($token, 1);
} else if (isset(self::$lookahead[$token])) {
$ahead= $token;
$p= true;
foreach (self::$lookahead[$token] as $candidate => $id) {
$l= strlen($candidate);
while (strlen($ahead) < $l && $this->tokenizer->hasMoreTokens()) {
$ahead.= $this->nextToken();
}
if (0 === strncmp($candidate, $ahead, $l)) {
if (0 === $id) break;
$this->token= $id;
$this->value= $candidate;
$this->pushBack(substr($ahead, $l));
$p= false;
break;
}
}
if ($p) {
$this->pushBack(substr($ahead, 1));
$this->token= ord($token);
$this->value= $token;
}
} else if (isset(self::$keywords[$token])) {
$this->token= self::$keywords[$token];
$this->value= $token;
Expand Down Expand Up @@ -241,17 +262,6 @@ public function advance() {
$this->value= $token;
$this->pushBack($ahead);
}
} else if (isset(self::$lookahead[$token])) {
$ahead= $this->nextToken();
$combined= $token.$ahead;
if (isset(self::$lookahead[$token][$combined])) {
$this->token= self::$lookahead[$token][$combined];
$this->value= $combined;
} else {
$this->token= ord($token);
$this->value= $token;
$this->pushBack($ahead);
}
} else if (false !== strpos(self::DELIMITERS, $token) && 1 == strlen($token)) {
$this->token= ord($token);
$this->value= $token;
Expand Down
Loading