jenkins-bot has submitted this change and it was merged.

Change subject: Add CONTINUE and BREAK operators
......................................................................


Add CONTINUE and BREAK operators

* remove $math in "case T_ECHO:"
* add function getNextToken
* add $ifOperators
* add operators &&, ||
* fix pass-by-reference

Time: 276 ms, Memory: 21.25Mb
OK (276 tests, 278 assertions)

Change-Id: I0a529031c6ac33d56d9f44621be79a9fb9a87bb2
---
M includes/Compiler.php
M includes/Runtime.php
M tests/phpunit/includes/RuntimeTest.php
3 files changed, 277 insertions(+), 94 deletions(-)

Approvals:
  Pastakhov: Looks good to me, approved
  jenkins-bot: Verified



diff --git a/includes/Compiler.php b/includes/Compiler.php
index 370ecd7..656fc19 100644
--- a/includes/Compiler.php
+++ b/includes/Compiler.php
@@ -113,6 +113,7 @@
                $needOperator = false;
                $incrementOperator = false;
                $rightOperators = array();
+               $ifOperators = array();
 
                $countTokens = count($tokens);
                for( $index = 0; $index < $countTokens; $index++ ){
@@ -258,15 +259,13 @@
                                case '-':
                                        if( !$needOperator ) { // This is 
negative statement of the next value: -$foo, -5, 5 + -5 ...
                                                if( $id == '-' ) { // ignore '+'
-                                                       $tmp = 
array(FOXWAY_STACK_COMMAND=>$id, FOXWAY_STACK_RESULT=>null, 
FOXWAY_STACK_PARAM=>0, FOXWAY_STACK_TOKEN_LINE=>$tokenLine);
-                                                       if( $rightOperators ) { 
// this is not first right operator. Example: echo (int)-
-                                                               
$rightOperators[0][FOXWAY_STACK_PARAM_2] = &$tmp[FOXWAY_STACK_RESULT];
+                                                       array_unshift( 
$rightOperators, array(FOXWAY_STACK_COMMAND=>$id, FOXWAY_STACK_RESULT=>null, 
FOXWAY_STACK_PARAM=>0, FOXWAY_STACK_TOKEN_LINE=>$tokenLine) );
+                                                       if( 
isset($rightOperators[1]) ) { // this is not first right operator. Example: 
echo (int)-
+                                                               
$rightOperators[1][FOXWAY_STACK_PARAM_2] = 
&$rightOperators[0][FOXWAY_STACK_RESULT];
                                                        }else{ // this is first 
right operator. Example: echo -
                                                                $parentFlags = 
$parentFlags & FOXWAY_CLEAR_FLAG_FOR_VALUE;
-                                                               $lastValue = 
&$tmp;
+                                                               $lastValue = 
&$rightOperators[0];
                                                        }
-                                                       array_unshift( 
$rightOperators, &$tmp );
-                                                       unset( $tmp );
                                                }
                                                break;
                                        }
@@ -280,6 +279,8 @@
                                case '^':
                                case T_SL:                      // <<
                                case T_SR:                      // >>
+                               case T_BOOLEAN_AND:     // &&
+                               case T_BOOLEAN_OR:      // ||
                                case T_LOGICAL_AND:     // and
                                case T_LOGICAL_XOR:     // xor
                                case T_LOGICAL_OR:      // or
@@ -357,7 +358,8 @@
                                                $memory[] = array($stack, 
$math); // Save $stack, $math for restore late
                                                $math = array();
                                                $stack = array();
-                                               array_unshift( $needParams, 
&$operator ); // Save operator '?'
+                                               $needParams = array_merge( 
array(&$operator), $needParams ); // Save operator '?'
+                                               //array_unshift( $needParams, 
&$operator ); // Save operator '?'
                                                unset($operator);
                                                //$operator = false;
                                                $parentFlags = 
FOXWAY_EXPECT_TERNARY_MIDDLE;
@@ -382,7 +384,8 @@
 
                                                $operator[FOXWAY_STACK_DO_TRUE] 
= $stack?:false; // Save stack in operator
                                                $stack = array();
-                                               array_unshift( $needParams, 
&$operator ); // Save operator ':'
+                                               $needParams = array_merge( 
array(&$operator), $needParams );
+                                               //array_unshift( $needParams, 
&$operator ); // Save operator ':'
                                                unset($operator);
                                        }
                                        unset($lastValue);
@@ -400,6 +403,7 @@
                                case T_SL_EQUAL:                // <<=
                                case T_SR_EQUAL:                // >>=
                                        if( $lastValue[FOXWAY_STACK_COMMAND] != 
T_VARIABLE ) { throw new ExceptionFoxway($id, 
FOXWAY_PHP_SYNTAX_ERROR_UNEXPECTED, $tokenLine); }
+                                       array_pop( $values ); // remove 
T_VARIABLE from $values
                                        // break is not necessary here
                                case T_DOUBLE_ARROW:    // =>
                                        if( !$needOperator || !$lastValue || 
$rightOperators ) { throw new ExceptionFoxway($id, 
FOXWAY_PHP_SYNTAX_ERROR_UNEXPECTED, $tokenLine); }
@@ -419,7 +423,7 @@
 
                                        if( isset($operator) ) { // This is not 
first operator
                                                $operator[FOXWAY_STACK_PARAM_2] 
= &$needParams[0][FOXWAY_STACK_RESULT];
-                                               array_unshift($memOperators, 
&$operator); // push $operator temporarily for restore late
+                                               $memOperators = array_merge( 
array(&$operator), $memOperators ); //array_unshift($memOperators, &$operator); 
// push $operator temporarily for restore late
                                                $parentFlags |= 
FOXWAY_EQUAL_HAVE_OPERATOR;
                                                $operPrec = 
self::$precedencesMatrix[$operator[FOXWAY_STACK_COMMAND]];
                                                if( $values ) {
@@ -568,7 +572,8 @@
                                                        if( $parentFlags & 
FOXWAY_NEED_RESTORE_RIGHT_OPERATORS ) { // Need restore right operators
                                                                $tmp = 
array_pop( $memOperators ); // restore right operators to $tmp
                                                                
$tmp[0][FOXWAY_STACK_PARAM_2] = &$operator[FOXWAY_STACK_RESULT]; // Set parents 
result as param to right operators
-                                                               $lk = 
array_pop( array_keys($tmp) ); // Get key of last right operator
+                                                               $k = 
array_keys($tmp);
+                                                               $lk = 
array_pop( $k ); // Get key of last right operator
                                                                $lastValue = 
&$tmp[$lk]; // Set $lastValue as link to last right operator
                                                                $stack = 
array_merge($stack, $tmp); // Push right operators to stack
                                                                unset($tmp);
@@ -619,29 +624,33 @@
                                                                
$needParams[0][FOXWAY_STACK_PARAM][] = &$operator[FOXWAY_STACK_RESULT];
                                                                $parentFlags = 
array_pop($parentheses);
 
-                                                               list( $tmp, 
$math ) = array_shift($memory); // restore $stack, $math
-                                                               $s = 
self::mergeStackAndMath($tmp, $math);
+                                                               //list( $tmp, 
$math ) = array_shift($memory); // restore $stack, $math
+                                                               //$s = 
self::mergeStackAndMath($tmp, $math);
+                                                               list( $s ) = 
array_shift( $memory ); // restore $stack
                                                                if( $s ) {
                                                                        $stack 
= array_merge( $s, $stack );
                                                                }
                                                                $stack[] = 
array_shift($needParams);
                                                        }
 
+                                                       //$ifOperators = 
array();
                                                        while(true) {
                                                                if( 
$parentFlags & FOXWAY_EXPECT_DO_TRUE_STACK ) { // Exsample: if(1) echo 2;
-                                                                       
$lastValue = &$needParams[0]; // Save link for operator 'else'
                                                                        if( 
$parentFlags & FOXWAY_EXPECT_CURLY_CLOSE ) { // if(1) { echo 2;
-                                                                               
$lastValue[FOXWAY_STACK_DO_TRUE] = array_merge( 
$lastValue[FOXWAY_STACK_DO_TRUE], $stack );
+                                                                               
$needParams[0][FOXWAY_STACK_DO_TRUE] = array_merge( 
$needParams[0][FOXWAY_STACK_DO_TRUE], $stack );
                                                                                
break; /********** EXIT **********/
                                                                        }else{ 
// if(1) echo 2;
-                                                                               
if( $lastValue[FOXWAY_STACK_COMMAND] == T_WHILE ) {
-                                                                               
        $stack[] = array( FOXWAY_STACK_COMMAND=>T_CONTINUE, 
FOXWAY_STACK_PARAM=>1 ); // Add operator T_CONTINUE to the end of the cycle
-                                                                               
        $lastValue[FOXWAY_STACK_DO_TRUE] = array_merge( 
$lastValue[FOXWAY_STACK_DO_TRUE], $stack );
-                                                                               
}else{
-                                                                               
        $lastValue[FOXWAY_STACK_DO_TRUE] = $stack;
+                                                                               
$link = &$needParams[0];
+                                                                               
if( $link[FOXWAY_STACK_COMMAND] == T_WHILE ) { // T_WHILE
+                                                                               
        $stack[] = array( FOXWAY_STACK_COMMAND=>T_CONTINUE, 
FOXWAY_STACK_RESULT=>1 ); // Add operator T_CONTINUE to the end of the cycle
+                                                                               
        $link[FOXWAY_STACK_DO_TRUE] = array_merge( $link[FOXWAY_STACK_DO_TRUE], 
$stack );
+                                                                               
        $stack = array( &$link );
+                                                                               
}else{ // T_IF
+                                                                               
        $link[FOXWAY_STACK_DO_TRUE] = $stack;
+                                                                               
        $stack = array_pop($memory); // Restore stack and ...
+                                                                               
        $stack[] = &$link; // ... add operator
+                                                                               
        $ifOperators[] = &$link; // Save operator T_IF for restore if will be 
used operator T_ELSE
                                                                                
}
-                                                                               
$stack = array_shift($memory); // Restore stack and ...
-                                                                               
$stack[] = &$lastValue; // ... add operator
                                                                                
array_shift($needParams);
                                                                                
$parentFlags = array_pop($parentheses);
                                                                        }
@@ -653,9 +662,7 @@
                                                                                
$needParams[0][FOXWAY_STACK_DO_FALSE] = $stack;
                                                                                
array_shift($needParams);
                                                                                
$parentFlags = array_pop($parentheses);
-                                                                               
if( $parentFlags & FOXWAY_KEEP_EXPECT_ELSE ) { // Exsample: if(1) if(2) echo 3; 
else echo 4;
-                                                                               
        $lastValue = &$needParams[0]; // Save link for operator 'else' of 
'if(1)'
-                                                                               
}else{ // Exsample: if(1) echo 2; else echo 3;
+                                                                               
if( $parentFlags & FOXWAY_KEEP_EXPECT_ELSE == 0 ) { // Exsample: if(1) echo 2; 
else echo 3;
                                                                                
        $parentFlags &= ~FOXWAY_EXPECT_ELSE;
                                                                                
}
                                                                                
break; /********** EXIT **********/
@@ -695,10 +702,11 @@
                                        if( $parentFlags & 
FOXWAY_EXPECT_START_COMMAND == 0 ) { throw new ExceptionFoxway($id, 
FOXWAY_PHP_SYNTAX_ERROR_UNEXPECTED, $tokenLine); }
 
                                        // for compatible with functions that 
use flag FOXWAY_EXPECT_LIST_PARAMS
-                                       ksort($math);
+                                       array_unshift( $memory, array($stack) );
+                                       /*ksort($math);
                                        array_unshift( $memory, array($stack, 
$math) );
                                        $stack = array();
-                                       $math = array();
+                                       $math = array();*/
 
                                        array_unshift( $needParams, array( 
FOXWAY_STACK_COMMAND=>$id, FOXWAY_STACK_RESULT=>null, 
FOXWAY_STACK_PARAM=>array(), FOXWAY_STACK_TOKEN_LINE=>$tokenLine ) );
 
@@ -720,30 +728,14 @@
                                        $parentheses[] = 
FOXWAY_EXPECT_START_COMMAND | FOXWAY_EXPECT_SEMICOLON | 
FOXWAY_EXPECT_DO_TRUE_STACK;
                                        $parentheses[] = 
FOXWAY_EXPECT_RESULT_FROM_PARENTHESES;
                                        $parentFlags = 
FOXWAY_EXPECT_PARENTHES_CLOSE;
-                                       for( $index++; $index < $countTokens; 
$index++ ){ // go to '('
-                                               $token = &$tokens[$index];
-                                               if ( is_string($token) ) {
-                                                       $id = $token;
-                                               } else {
-                                                       list($id, $text, 
$tokenLine) = $token;
-                                               }
-                                               switch ($id) {
-                                                       case T_COMMENT:
-                                                       case T_DOC_COMMENT:
-                                                       case T_WHITESPACE:
-                                                               break; // 
ignore it
-                                                       case '(':
-                                                               break 3; // if (
-                                                       default:
-                                                               throw new 
ExceptionFoxway($id, FOXWAY_PHP_SYNTAX_ERROR_UNEXPECTED, $tokenLine);
-                                               }
-                                       }
-                                       throw new ExceptionFoxway('$end', 
FOXWAY_PHP_SYNTAX_ERROR_UNEXPECTED, $tokenLine);
+
+                                       self::getNextToken( $tokens, $index, 
$countTokens, $tokenLine, array('(') );
                                        break;
                                case T_ELSE:            // else
                                        if( $parentFlags & FOXWAY_EXPECT_ELSE 
== 0 || $stack || isset($operator) || $values ) { throw new 
ExceptionFoxway($id, FOXWAY_PHP_SYNTAX_ERROR_UNEXPECTED, $tokenLine); }
 
-                                       array_unshift( $needParams, &$lastValue 
); // $lastValue is link to operator 'if'
+                                       $needParams = array_merge( 
$ifOperators, $needParams  );
+                                       $ifOperators = array();
                                        if( $parentFlags & 
FOXWAY_KEEP_EXPECT_ELSE == 0 ) { // Example: if (1) echo 2; else
                                                $parentFlags &= 
~FOXWAY_EXPECT_ELSE; // Skip for: if(1) if (2) echo 3; else
                                        }
@@ -753,7 +745,8 @@
                                case T_ELSEIF:          // elseif
                                        if( $parentFlags & FOXWAY_EXPECT_ELSE 
== 0 || $stack || isset($operator) || $values ) { throw new 
ExceptionFoxway($id, FOXWAY_PHP_SYNTAX_ERROR_UNEXPECTED, $tokenLine); }
 
-                                       array_unshift( $needParams, &$lastValue 
); // $lastValue is link to operator 'if'
+                                       $needParams = array_merge( 
$ifOperators, $needParams );
+                                       $ifOperators = array();
                                        array_unshift( $needParams, array( 
FOXWAY_STACK_COMMAND=>T_IF, FOXWAY_STACK_RESULT=>null, 
FOXWAY_STACK_PARAM=>null, FOXWAY_STACK_TOKEN_LINE=>$tokenLine ) );
                                        $parentheses[] = 
$parentFlags|FOXWAY_EXPECT_DO_FALSE_STACK;
                                        $parentheses[] = 
FOXWAY_EXPECT_START_COMMAND | FOXWAY_EXPECT_SEMICOLON | 
FOXWAY_EXPECT_DO_TRUE_STACK;
@@ -829,15 +822,13 @@
                                case T_UNSET_CAST:      // (unset)
                                        if( $needOperator ) { throw new 
ExceptionFoxway($id, FOXWAY_PHP_SYNTAX_ERROR_UNEXPECTED, $tokenLine); }
 
-                                       $tmp = array(FOXWAY_STACK_COMMAND=>$id, 
FOXWAY_STACK_RESULT=>null, FOXWAY_STACK_TOKEN_LINE=>$tokenLine);
-                                       if( $rightOperators ) { // this is not 
first right operator. Example: echo -(int)
-                                               
$rightOperators[0][FOXWAY_STACK_PARAM_2] = &$tmp[FOXWAY_STACK_RESULT];
-                                       }else{ // this is first right operator. 
Example: echo (int)
+                                       array_unshift( $rightOperators, 
array(FOXWAY_STACK_COMMAND=>$id, FOXWAY_STACK_RESULT=>null, 
FOXWAY_STACK_TOKEN_LINE=>$tokenLine) );
+                                       if( isset($rightOperators[1]) ) { // 
this is not first right operator. Example: echo (int)-
+                                               
$rightOperators[1][FOXWAY_STACK_PARAM_2] = 
&$rightOperators[0][FOXWAY_STACK_RESULT];
+                                       }else{ // this is first right operator. 
Example: echo -
                                                $parentFlags = $parentFlags & 
FOXWAY_CLEAR_FLAG_FOR_VALUE;
-                                               $lastValue = &$tmp;
+                                               $lastValue = 
&$rightOperators[0];
                                        }
-                                       array_unshift( $rightOperators, &$tmp );
-                                       unset( $tmp );
                                        break;
                                case '[':
                                        $memEncapsed[] = $stackEncapsed;
@@ -867,7 +858,7 @@
                                                if( 
$lastValue[FOXWAY_STACK_COMMAND] != T_VARIABLE ) {
                                                        throw new 
ExceptionFoxway($id, FOXWAY_PHP_SYNTAX_ERROR_UNEXPECTED, $tokenLine);
                                                }
-                                               array_unshift( $needParams, 
&$lastValue );
+                                               $needParams = array_merge( 
array(&$lastValue), $needParams ); //array_unshift( $needParams, &$lastValue );
                                                $values = array();
                                                unset($lastValue);
                                        }else{ // $foo = [
@@ -877,7 +868,7 @@
                                        break;
                                case '{':
                                        if( $parentFlags & 
FOXWAY_EXPECT_START_COMMAND == 0 || $stack || isset($operator) || $values ) { 
throw new ExceptionFoxway($id, FOXWAY_PHP_SYNTAX_ERROR_UNEXPECTED, $tokenLine); 
}
-                                               if( 
$needParams[0][FOXWAY_STACK_COMMAND] == T_IF ) {
+                                       if( 
$needParams[0][FOXWAY_STACK_COMMAND] == T_IF ) {
                                                if( $parentFlags & 
FOXWAY_EXPECT_DO_TRUE_STACK ) {
                                                        
$needParams[0][FOXWAY_STACK_DO_TRUE] = array();
                                                }elseif( $parentFlags & 
FOXWAY_EXPECT_DO_FALSE_STACK ) {
@@ -897,32 +888,41 @@
                                        if( $stackEncapsed ) { // Example: echo 
"hello {$foo}
                                                $parentFlags = 
array_pop($parentheses);
                                                break;
-                                       }
+                                       } // Example: if(1) { echo "hello"; }
 
-                                       // Example: if(1) { echo "hello"; }
                                        if( $parentFlags & 
FOXWAY_EXPECT_START_COMMAND == 0 || $stack || isset($operator) || $values ) { 
throw new ExceptionFoxway($id, FOXWAY_PHP_SYNTAX_ERROR_UNEXPECTED, $tokenLine); 
}
 
-                                       $lastValue = &$needParams[0];
-                                       if( $lastValue[FOXWAY_STACK_COMMAND] == 
T_WHILE ) { // Add operator T_CONTINUE to the end of the cycle
-                                               
$lastValue[FOXWAY_STACK_DO_TRUE][] = array( FOXWAY_STACK_COMMAND=>T_CONTINUE, 
FOXWAY_STACK_PARAM=>1 );
-                                       }
-                                       array_shift($needParams);
+                                       $link = &$needParams[0];
                                        array_pop($parentheses);
                                        $parentFlags = array_pop($parentheses);
-                                       if( 
!isset($lastValue[FOXWAY_STACK_DO_FALSE]) ) { // operator 'else' not used
-                                               //$tmp = array_merge( 
array(array(array(&$lastValue))), array_shift($memory) ); // Restore stack and 
add operator
-                                               $tmp = array_shift($memory); // 
Restore stack and ...
-                                               $tmp[] = &$lastValue; // ... 
add operator
+                                       if( 
!isset($link[FOXWAY_STACK_DO_FALSE]) ) { // operator 'else' not used
+                                               if( $link[FOXWAY_STACK_COMMAND] 
== T_WHILE ) {
+                                                       
$link[FOXWAY_STACK_DO_TRUE][] = array( FOXWAY_STACK_COMMAND=>T_CONTINUE, 
FOXWAY_STACK_RESULT=>1 );  // Add operator T_CONTINUE to the end of the cycle
+                                                       $tmp = array( &$link );
+                                                       $ifOperators = array();
+                                               }else{ // T_IF
+                                                       $tmp = 
array_pop($memory); // Restore stack and ...
+                                                       $tmp[] = &$link; // ... 
add operator
+                                                       $ifOperators = array( 
&$needParams[0] );
+                                               }
+                                               array_shift($needParams);
                                                while(true) {
-                                                       if( $parentFlags & 
FOXWAY_EXPECT_DO_TRUE_STACK ) { // Exsample: if(1) if(2) echo 3;
-                                                               if( 
$parentFlags & FOXWAY_EXPECT_CURLY_CLOSE ) { // Exsample: if(1) { if(2) echo 3; 
}
+                                                       if( $parentFlags & 
FOXWAY_EXPECT_DO_TRUE_STACK ) { // Exsample: if(1) if(2) { echo 3; }
+                                                               if( 
$parentFlags & FOXWAY_EXPECT_CURLY_CLOSE ) { // Exsample: if(1) { if(2) { echo 
3; }
                                                                        
$needParams[0][FOXWAY_STACK_DO_TRUE] = array_merge( 
$needParams[0][FOXWAY_STACK_DO_TRUE], $tmp );
                                                                        break; 
/********** EXIT **********/
                                                                }else{ // 
Exsample: if(1) if(2) { echo 3; }
-                                                                       
$needParams[0][FOXWAY_STACK_DO_TRUE] = $tmp;
-                                                                       //$tmp 
= array_merge( array(array(array(&$needParams[0]))), array_shift($memory) ); // 
Restore stack and add operator
-                                                                       $tmp = 
array_shift($memory); // Restore stack and ...
-                                                                       $tmp[] 
= &$needParams[0]; // ... add operator
+                                                                       $link = 
&$needParams[0];
+                                                                       if( 
$link[FOXWAY_STACK_COMMAND] == T_WHILE ) { // T_WHILE
+                                                                               
$link[FOXWAY_STACK_DO_TRUE] = array_merge( $link[FOXWAY_STACK_DO_TRUE], $tmp );
+                                                                               
$tmp = array( &$link );
+                                                                       }else{ 
// T_IF
+                                                                               
$link[FOXWAY_STACK_DO_TRUE] = $tmp;
+                                                                               
$tmp = array_pop($memory); // Restore stack and ...
+                                                                               
$tmp[] = &$link; // ... add operator
+                                                                               
$ifOperators[] = &$link;
+                                                                       }
+                                                                       
array_shift($needParams);
                                                                        
$parentFlags = array_pop($parentheses) | $parentFlags & FOXWAY_KEEP_EXPECT_ELSE;
                                                                }
                                                        } elseif ( $parentFlags 
& FOXWAY_EXPECT_DO_FALSE_STACK ) { // Exsample: if(1) echo 2; else if(3) echo 4;
@@ -931,7 +931,9 @@
                                                                }else{ // 
Exsample: if(1) echo 2; else if(3) echo 4;
                                                                        
$needParams[0][FOXWAY_STACK_DO_FALSE] = $tmp;
                                                                        
$parentFlags = array_pop($parentheses);
+                                                                       
$ifOperators[] = &$needParams[0];
                                                                }
+                                                               
array_shift($needParams);
                                                                break; 
/********** EXIT **********/
                                                        } else { // Example: 
if(1) { echo 2; }
                                                                $bytecode[] = 
$tmp;
@@ -940,10 +942,29 @@
                                                }
                                                //$stack = array();
                                                $parentLevel = 0;
-                                       }else{
-                                               $lastValue = &$needParams[0]; 
// Save link for operator 'else'
-                                               // @todo memory leak in 
$needParams for testRun_echo_if_double_13()
+                                       }else{ // operator 'else' was used. 
Example: if(1) { if(2) { echo 3; } else { echo 4; }
+                                               array_shift($needParams);
                                        }
+                                       break;
+                               case T_CONTINUE:
+                               case T_BREAK:
+                                       if( $parentFlags & 
FOXWAY_EXPECT_START_COMMAND == 0 ) { throw new ExceptionFoxway($id, 
FOXWAY_PHP_SYNTAX_ERROR_UNEXPECTED, $tokenLine); }
+
+                                       $tmp = self::getNextToken( $tokens, 
$index, $countTokens, $tokenLine, array(T_LNUMBER, ';') );
+                                       if( $tmp == ';' ) { // Example: break;
+                                               $text = 1;
+                                       }else{ // Example: break 1;
+                                               $text = 
self::getIntegerFromString($tmp);
+                                               if( $text == 0 ) {
+                                                       $text = 1; // todo 
throw new ExceptionFoxway, as in PHP 5.4
+                                               }
+                                               self::getNextToken( $tokens, 
$index, $countTokens, $tokenLine, array(';') );
+                                       }
+                                       $stack[] = array( 
FOXWAY_STACK_COMMAND=>$id, FOXWAY_STACK_RESULT=>$text, 
FOXWAY_STACK_TOKEN_LINE=>$tokenLine );
+                                       unset($lastValue);
+                                       $lastValue = null;
+                                       $needOperator = true;
+                                       $index--;
                                        break;
                                default :
                                        //throw new ExceptionFoxway($id, 
FOXWAY_PHP_SYNTAX_ERROR_UNEXPECTED, $tokenLine);
@@ -1059,4 +1080,25 @@
                return $s;
        }
 
+       private static function getNextToken( &$tokens, &$index, $countTokens, 
$tokenLine, $found ) {
+               static $ignore = array( T_COMMENT, T_DOC_COMMENT, T_WHITESPACE 
);
+
+               for( $index++; $index < $countTokens; $index++ ){ // go to '('
+                       $token = &$tokens[$index];
+                       if ( is_string($token) ) {
+                               $id = $token;
+                               $text = $token;
+                       } else {
+                               list($id, $text, $tokenLine) = $token;
+                       }
+                       if( in_array($id, $found) ) {
+                               return $text;
+                       }
+                       if( !in_array($id, $ignore) ) {
+                               throw new ExceptionFoxway($id, 
FOXWAY_PHP_SYNTAX_ERROR_UNEXPECTED, $tokenLine);
+                       }
+               }
+               throw new ExceptionFoxway('$end', 
FOXWAY_PHP_SYNTAX_ERROR_UNEXPECTED, $tokenLine);
+       }
+
 }
diff --git a/includes/Runtime.php b/includes/Runtime.php
index 10e290a..5e2d5e9 100644
--- a/includes/Runtime.php
+++ b/includes/Runtime.php
@@ -609,10 +609,25 @@
                $return = array();
                $break = 0; // used for T_BREAK
                $continue = false; // used for T_CONTINUE
+               $loopsOwner = null;
 
                $c=count($code);
                $i=-1;
                do {
+                       if( $break ) {
+                               if( $loopsOwner == T_WHILE ) {
+                                       $break--;
+                                       continue;
+                               }
+                               break;
+                       }elseif( $continue ) {
+                               if( $loopsOwner == T_WHILE ) {
+                                       $i = -1;
+                                       $continue = false;
+                               }else{
+                                       continue;
+                               }
+                       }
                        $i++;
                        for(; $i<$c; $i++ ) {
                                $value = &$code[$i];
@@ -662,14 +677,16 @@
                                        case T_SR:                      // >>
                                                $value[FOXWAY_STACK_RESULT] = 
$value[FOXWAY_STACK_PARAM] >> $value[FOXWAY_STACK_PARAM_2];
                                                break;
+                                       case T_BOOLEAN_AND:     // &&
                                        case T_LOGICAL_AND:     // and
-                                               $value[FOXWAY_STACK_RESULT] = 
$value[FOXWAY_STACK_PARAM] and $value[FOXWAY_STACK_PARAM_2];
+                                               $value[FOXWAY_STACK_RESULT] = 
$value[FOXWAY_STACK_PARAM] && $value[FOXWAY_STACK_PARAM_2];
                                                break;
                                        case T_LOGICAL_XOR:     // xor
                                                $value[FOXWAY_STACK_RESULT] = 
$value[FOXWAY_STACK_PARAM] xor $value[FOXWAY_STACK_PARAM_2];
                                                break;
+                                       case T_BOOLEAN_OR:      // ||
                                        case T_LOGICAL_OR:      // or
-                                               $value[FOXWAY_STACK_RESULT] = 
$value[FOXWAY_STACK_PARAM] or $value[FOXWAY_STACK_PARAM_2];
+                                               $value[FOXWAY_STACK_RESULT] = 
$value[FOXWAY_STACK_PARAM] || $value[FOXWAY_STACK_PARAM_2];
                                                break;
                                        case '<':
                                                $value[FOXWAY_STACK_RESULT] = 
$value[FOXWAY_STACK_PARAM] < $value[FOXWAY_STACK_PARAM_2];
@@ -727,19 +744,21 @@
                                        case '?':
                                                if( $value[FOXWAY_STACK_PARAM] 
) { // true ?
                                                        if( 
$value[FOXWAY_STACK_PARAM_2][FOXWAY_STACK_DO_TRUE] ) { // true ? 1+2 :
-                                                               $memory[] = 
array( &$value[FOXWAY_STACK_PARAM_2][FOXWAY_STACK_PARAM], $code, $i, $c );
+                                                               $memory[] = 
array( &$value[FOXWAY_STACK_PARAM_2][FOXWAY_STACK_PARAM], $code, $i, $c, 
$loopsOwner );
                                                                $code = 
$value[FOXWAY_STACK_PARAM_2][FOXWAY_STACK_DO_TRUE];
                                                                $i = -1;
                                                                $c = 
count($code);
+                                                               $loopsOwner = 
'?';
                                                        }else{ // true ? 1 :
                                                                
$value[FOXWAY_STACK_RESULT] = $value[FOXWAY_STACK_PARAM_2][FOXWAY_STACK_PARAM];
                                                        }
                                                }else{ // false ?
                                                        if( 
$value[FOXWAY_STACK_PARAM_2][FOXWAY_STACK_DO_FALSE] ) { // false ? ... : 1+2
-                                                               $memory[] = 
array( &$value[FOXWAY_STACK_PARAM_2][FOXWAY_STACK_PARAM_2], $code, $i, $c );
+                                                               $memory[] = 
array( &$value[FOXWAY_STACK_PARAM_2][FOXWAY_STACK_PARAM_2], $code, $i, $c, 
$loopsOwner );
                                                                $code = 
$value[FOXWAY_STACK_PARAM_2][FOXWAY_STACK_DO_FALSE];
                                                                $i = -1;
                                                                $c = 
count($code);
+                                                               $loopsOwner = 
'?';
                                                        }else{ // false ? ... : 
1
                                                                
$value[FOXWAY_STACK_RESULT] = 
$value[FOXWAY_STACK_PARAM_2][FOXWAY_STACK_PARAM_2];
                                                        }
@@ -748,46 +767,54 @@
                                        case T_IF:
                                                if( $value[FOXWAY_STACK_PARAM] 
) { // Example: if( true )
                                                        if( 
$value[FOXWAY_STACK_DO_TRUE] ) { // Stack not empty: if(true);
-                                                               $memory[] = 
array( null, $code, $i, $c );
+                                                               $memory[] = 
array( null, $code, $i, $c, $loopsOwner );
                                                                $code = 
$value[FOXWAY_STACK_DO_TRUE];
                                                                $i = -1;
                                                                $c = 
count($code);
+                                                               $loopsOwner = 
T_IF;
                                                        }
                                                }else{ // Example: if( false )
                                                        if( 
isset($value[FOXWAY_STACK_DO_FALSE]) ) { // Stack not empty: if(false) ; else ;
-                                                               $memory[] = 
array( null, $code, $i, $c );
+                                                               $memory[] = 
array( null, $code, $i, $c, $loopsOwner );
                                                                $code = 
$value[FOXWAY_STACK_DO_FALSE];
                                                                $i = -1;
                                                                $c = 
count($code);
+                                                               $loopsOwner = 
T_IF;
                                                        }
                                                }
                                                break;
                                        case T_WHILE: // PHP code "while($foo) 
{ ... }" doing as T_WHILE { T_DO($foo) ... }. If $foo == false, T_DO doing as 
T_BREAK
-                                               if( $break ) { // was used 
T_BREAK
+                                               /*if( $break ) { // was used 
T_BREAK
                                                        if( --$break ) { // 
T_BREAK is 2 or more
                                                                break 2; // go 
to one level down
                                                        }
                                                        break; // T_BREAK is 1, 
just go to next operator (T_WHILE will be skiped)
-                                               } // T_BREAK is not used
-                                               $memory[] = array( null, $code, 
$i, $c );
+                                               } */// T_BREAK is not used
+                                               $memory[] = array( null, $code, 
$i, $c, $loopsOwner );
                                                $code = 
$value[FOXWAY_STACK_DO_TRUE];
                                                $i = -1;
                                                $c = count($code);
+                                               $loopsOwner = T_WHILE;
                                                break;
                                        case T_DO:
                                                if( $value[FOXWAY_STACK_PARAM] 
) {
                                                        continue; // this is 
"while(true)", just go next
                                                }// while(false) doing as 
T_BREAK;
-                                               $break = 1;
                                                break 2; // go to one level down
                                        case T_BREAK:
-                                               // @todo T_BREAK is 2 or more
-                                               $break = 1;
+                                               $break = 
$value[FOXWAY_STACK_RESULT];
+                                               if( $loopsOwner == T_WHILE ) {
+                                                       $break--;
+                                               }
                                                break 2; // go to one level down
                                        case T_CONTINUE:
-                                               // @todo T_CONTINUE is 2 or more
-                                               $i = -1;
-                                               break;
+                                               $break = 
$value[FOXWAY_STACK_RESULT]-1;
+                                               if( $loopsOwner == T_WHILE && 
$break == 0 ) { // Example: while(true) continue;
+                                                       $i = -1;
+                                                       break;
+                                               }
+                                               $continue = true;
+                                               break 2; // go to one level down
                                        case T_ARRAY:                   // array
                                                $value[FOXWAY_STACK_RESULT] = 
array(); // init array
                                                foreach 
($value[FOXWAY_STACK_PARAM] as $v) {
@@ -896,7 +923,7 @@
                                                break;
                                }
                        }
-               } while( list($code[$i][FOXWAY_STACK_RESULT], $code, $i, $c) = 
array_pop($memory) );
+               } while( list($code[$i][FOXWAY_STACK_RESULT], $code, $i, $c, 
$loopsOwner) = array_pop($memory) );
 
                return $return;
        }
diff --git a/tests/phpunit/includes/RuntimeTest.php 
b/tests/phpunit/includes/RuntimeTest.php
index 2a802fd..81e05ad 100644
--- a/tests/phpunit/includes/RuntimeTest.php
+++ b/tests/phpunit/includes/RuntimeTest.php
@@ -332,13 +332,13 @@
                                Runtime::runSource('$foo=1; echo $foo++ + $foo 
= 40 + $foo = 400 + $foo, $foo;'), // $foo = 400 + 2; $foo = 40 + 402; echo 1 + 
442, 442
                                array('443', '442')
                                );
-       }*/
+       }
        public function testRun_echo_math_variables_short_circuit_1() {
                $this->assertEquals(
                                Runtime::runSource('$foo=10; echo $foo = 400 + 
$foo or $foo = 10000, $foo;'), // $foo = 400 + 10; echo 441 or ... , 410
                                array(true, '410')
                                );
-       }/*
+       }
        public function testRun_echo_math_variables_short_circuit_2() {
                $this->assertEquals(
                                Runtime::runSource('$foo=10; echo $foo = 10 - 
$foo or $foo = 10000, $foo;'), // $foo = 10 - 10; echo 0 or $foo=10000 , 10000
@@ -1338,6 +1338,24 @@
                                array('false second FALSE')
                                );
        }
+       public function testRun_echo_if_double_24() {
+               $this->assertEquals(
+                               Runtime::runSource('if( true ) if( true ) echo 
"true2"; else echo "false2"; else echo "false";'),
+                               array('true2')
+                               );
+       }
+       public function testRun_echo_if_double_25() {
+               $this->assertEquals(
+                               Runtime::runSource('if( true ) if( false ) echo 
"true2"; else echo "false2"; else echo "false";'),
+                               array('false2')
+                               );
+       }
+       public function testRun_echo_if_double_26() {
+               $this->assertEquals(
+                               Runtime::runSource('if( false ) if( false ) 
echo "true2"; else echo "false2"; else echo "false";'),
+                               array('false')
+                               );
+       }
        public function testRun_echo_elseif_1() {
                $this->assertEquals(
                                Runtime::runSource('if( true ) echo "one"; 
elseif( true ) echo "two"; else echo "three";'),
@@ -1630,5 +1648,101 @@
                                array('1', '2', '3')
                                );
        }
+       public function testRun_while_continue_1() {
+               $this->assertEquals(
+                               Runtime::runSource('$i=1; while( $i <= 3 ) { 
echo $i++; continue; $i++; }'),
+                               array('1', '2', '3')
+                               );
+       }
+       public function testRun_while_break_1() {
+               $this->assertEquals(
+                               Runtime::runSource('$i=1; while( $i <= 33 ) { 
echo $i++; break; $i++; }'),
+                               array('1')
+                               );
+       }
+       public function testRun_while_if_break_1() {
+               $this->assertEquals(
+                               Runtime::runSource('$i=1; while( $i <= 33 ) { 
echo $i++; if($i == 3) break; }'),
+                               array('1', '2')
+                               );
+       }
+       public function testRun_while_if_break_2() {
+               $this->assertEquals(
+                               Runtime::runSource('$i=1; while( $i <= 33 ) { 
echo $i++; if($i == 3){echo "The end"; break; echo "anything";} }'),
+                               array('1', '2', 'The end')
+                               );
+       }
+       public function testRun_while_if_continue_1() {
+               $this->assertEquals(
+                               Runtime::runSource('$i=0; while( $i <= 2 ) { 
$i++; if($i == 2) continue; echo $i; }'),
+                               array('1', '3')
+                               );
+       }
+       public function testRun_while_if_continue_2() {
+               $this->assertEquals(
+                               Runtime::runSource('$i=0; while( $i <= 2 ) { 
$i++; if($i == 2) { echo "Two"; continue; } echo $i; }'),
+                               array('1', 'Two', '3')
+                               );
+       }
+       public function testRun_while_while_1() {
+               $this->assertEquals(
+                               implode( Runtime::runSource('$i=1; while( $i<=3 
){ echo "|$i|"; $y=1; while( $y<=$i ){ echo "($y)"; $y++; } $i++; }') ),
+                               '|1|(1)|2|(1)(2)|3|(1)(2)(3)'
+                               );
+       }
+       public function testRun_while_while_continue_1() {
+               $this->assertEquals(
+                               implode( Runtime::runSource('$i=1; while( $i<=3 
){ echo "|$i|"; $y=0; while( $y<3 ){ $y++; if( $y==2) continue; echo "($y)";  } 
$i++; }') ),
+                               '|1|(1)(3)|2|(1)(3)|3|(1)(3)'
+                               );
+       }
+       public function testRun_while_while_continue_2() {
+               $this->assertEquals(
+                               implode( Runtime::runSource('$i=0; while( $i<5 
){ $i++; echo "|$i|"; $y=0; while( $y<3 ){ $y++; if( $y==$i ){ continue 2; } 
echo "($y)";  } }') ),
+                               '|1||2|(1)|3|(1)(2)|4|(1)(2)(3)|5|(1)(2)(3)'
+                               );
+       }
+       public function testRun_while_while_break_1() {
+               $this->assertEquals(
+                               implode( Runtime::runSource('$i=1; while( $i<=5 
){ echo "|$i|"; $y=0; while( $y<3 ){ $y++; if( $y==2) break; echo "($y)";  } 
$i++; }') ),
+                               '|1|(1)|2|(1)|3|(1)|4|(1)|5|(1)'
+                               );
+       }
+       public function testRun_while_while_break_2() {
+               $this->assertEquals(
+                               implode( Runtime::runSource('$i=1; $y=0; 
while($y<4&&$y<$i){$y++; if($y==3){break; echo "hohoho";} echo "($y)";}') ),
+                               '(1)'
+                               );
+       }
+       public function testRun_while_while_break_3() {
+               $this->assertEquals(
+                               implode( Runtime::runSource('$i=1; while( $i<=5 
){ echo "|$i|"; $y=0; while( $y<4 && $y<$i ){ $y++; if( $y==3) { break 2; echo 
"hohoho"; } echo "($y)";  } $i++; }') ),
+                               '|1|(1)|2|(1)(2)|3|(1)(2)'
+                               );
+       }
+       public function testRun_while_if_while_1() {
+               $this->assertEquals(
+                               implode( Runtime::runSource('$i=1; while( $i<=5 
){ echo "|$i|"; $y=0; if( $i==2 || $i == 4 ) while( $y<$i ){ $y++; echo "($y)"; 
} $i++; } ') ),
+                               '|1||2|(1)(2)|3||4|(1)(2)(3)(4)|5|'
+                               );
+       }
+       public function testRun_while_if_while_2() {
+               $this->assertEquals(
+                               implode( Runtime::runSource('$i=1; while( $i<=5 
){ echo "|$i|"; $y=0; if( $i==2 || $i == 4 ) { echo "."; while( $y<$i ){ $y++; 
echo "($y)"; } } $i++; } ') ),
+                               '|1||2|.(1)(2)|3||4|.(1)(2)(3)(4)|5|'
+                               );
+       }
+       public function testRun_while_if_while_if_1() {
+               $this->assertEquals(
+                               implode( Runtime::runSource('$i=2; $y=0; if( 
$i==2 || $i == 4 ) { echo "."; while( $y<$i ){ $y++; if($y< 3) echo "($y)"; 
else break 2;} }') ),
+                               '.(1)(2)'
+                               );
+       }
+       public function testRun_while_if_while_if_2() {
+               $this->assertEquals(
+                               implode( Runtime::runSource('$i=1; while( $i<=5 
){ echo "|$i|"; $y=0; if( $i==2 || $i == 4 ) { echo "."; while( $y<$i ){ $y++; 
if($y < 3) echo "($y)"; else break 2; } } $i++; } ') ),
+                               '|1||2|.(1)(2)|3||4|.(1)(2)'
+                               );
+       }
 
 }

-- 
To view, visit https://gerrit.wikimedia.org/r/90492
To unsubscribe, visit https://gerrit.wikimedia.org/r/settings

Gerrit-MessageType: merged
Gerrit-Change-Id: I0a529031c6ac33d56d9f44621be79a9fb9a87bb2
Gerrit-PatchSet: 6
Gerrit-Project: mediawiki/extensions/Foxway
Gerrit-Branch: develop
Gerrit-Owner: Pastakhov <pastak...@yandex.ru>
Gerrit-Reviewer: Pastakhov <pastak...@yandex.ru>
Gerrit-Reviewer: jenkins-bot

_______________________________________________
MediaWiki-commits mailing list
MediaWiki-commits@lists.wikimedia.org
https://lists.wikimedia.org/mailman/listinfo/mediawiki-commits

Reply via email to