Pastakhov has uploaded a new change for review.

  https://gerrit.wikimedia.org/r/94118


Change subject: add operator T_LIST
......................................................................

add operator T_LIST

* define FOXWAY_ALLOW_SKIP_PARAMS for operator T_LIST
* add recursive usege private function fillList for operator T_LIST
* modify assign code in compiler
* fix strange bug with operator 'if'

Time: 462 ms, Memory: 24.25Mb
OK (470 tests, 476 assertions)

Change-Id: Iad81e099d562424e6c2a7f3396c6c5894f27108b
---
M includes/Compiler.php
M includes/Runtime.php
M tests/phpunit/includes/RuntimeTest.php
3 files changed, 146 insertions(+), 40 deletions(-)


  git pull ssh://gerrit.wikimedia.org:29418/mediawiki/extensions/Foxway 
refs/changes/18/94118/1

diff --git a/includes/Compiler.php b/includes/Compiler.php
index ee71fd3..fa1edc0 100644
--- a/includes/Compiler.php
+++ b/includes/Compiler.php
@@ -26,6 +26,7 @@
 define( 'FOXWAY_EXPECT_EQUAL_END', 1 << 20 );
 define( 'FOXWAY_EQUAL_HAVE_OPERATOR', 1 << 21 );
 define( 'FOXWAY_ALLOW_ONLY_VARIABLES', 1 << 22 );
+define( 'FOXWAY_ALLOW_SKIP_PARAMS', 1 << 23 ); // used in operator T_LIST
 
 define( 'FOXWAY_CLEAR_FLAG_FOR_SHIFT_BEFORE_PARENTHESES', 
FOXWAY_EXPECT_PARENTHESES_WITH_LIST_PARAMS );
 //define( 'FOXWAY_CLEAR_FLAG_FOR_SHIFT_AFTER_PARENTHESES', 
FOXWAY_EXPECT_PARENTHESES_WITH_LIST_PARAMS );
@@ -459,17 +460,23 @@
                                case T_XOR_EQUAL:               // ^=
                                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 || $parentFlags & FOXWAY_ALLOW_ONLY_VARIABLES ) { throw new 
ExceptionFoxway($id, FOXWAY_PHP_SYNTAX_ERROR_UNEXPECTED, $tokenLine); }
+                                       if ( !$needOperator || 
!isset($lastValue) || $rightOperators || $parentFlags & 
FOXWAY_ALLOW_ONLY_VARIABLES ) { throw new ExceptionFoxway($id, 
FOXWAY_PHP_SYNTAX_ERROR_UNEXPECTED, $tokenLine); }
                                        $needOperator = false;
 
                                        array_unshift( $needParams, 
array(FOXWAY_STACK_COMMAND=>$id, 
FOXWAY_STACK_RESULT=>&$lastValue[FOXWAY_STACK_RESULT], 
FOXWAY_STACK_PARAM=>&$lastValue, FOXWAY_STACK_TOKEN_LINE=>$tokenLine) );
-                                       if( $id == T_DOUBLE_ARROW ) {
+                                       if ( $id == T_DOUBLE_ARROW ) {
                                                if( $parentFlags & 
FOXWAY_ALLOW_DOUBLE_ARROW == 0 ) { throw new ExceptionFoxway($id, 
FOXWAY_PHP_SYNTAX_ERROR_UNEXPECTED, $tokenLine); }
-                                       }else{
+                                       } elseif ( $id == '=' ) {
+                                               if ( 
$lastValue[FOXWAY_STACK_COMMAND] == T_VARIABLE ) {
+                                                       array_pop( $values ); 
// remove T_VARIABLE from $values
+                                               } elseif ( 
$lastValue[FOXWAY_STACK_COMMAND] != T_LIST ) {
+                                                       throw new 
ExceptionFoxway($id, FOXWAY_PHP_SYNTAX_ERROR_UNEXPECTED, $tokenLine);
+                                               }
+                                               $stack[] = &$needParams[0];
+                                       } else {
+                                               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
                                                $stack[] = &$needParams[0];
                                        }
                                        $parentheses[] = $parentFlags;
@@ -514,6 +521,10 @@
                                        if( !$needOperator && !isset($operator) 
&& $parentFlags & FOXWAY_THIS_IS_FUNCTION ) { $needOperator = true; }
                                        // break is not necessary here
                                case ',':
+                                       if ( !$needOperator && 
!isset($lastValue) && $parentFlags & FOXWAY_ALLOW_SKIP_PARAMS ) {
+                                               $needOperator = true;
+                                               $lastValue = null;
+                                       }
                                case ';':
                                        if( !$needOperator || !$parentFlags & 
FOXWAY_EXPECT_TERNARY_MIDDLE || $rightOperators ) { throw new 
ExceptionFoxway($id, FOXWAY_PHP_SYNTAX_ERROR_UNEXPECTED, $tokenLine); }
 
@@ -615,7 +626,9 @@
                                                                        
unset($operator);
                                                                }
                                                                $operator = 
&$needParams[0]; // restore result of function as value, this will be set as 
$lastValue
-                                                               $stack[] = 
&$operator;
+                                                               if( 
$operator[FOXWAY_STACK_COMMAND] != T_LIST ) { // T_LIST doesn't need add to 
stack
+                                                                       
$stack[] = &$operator; // add function to stack
+                                                               }
                                                                
array_shift($needParams);
                                                                list( $s, $math 
) = array_pop($memory); // restore $stack, $math
                                                                if( $s ) {
@@ -980,8 +993,8 @@
                                                                        
$needParams[0][FOXWAY_STACK_DO_FALSE] = $tmp;
                                                                        
$parentFlags = array_pop($parentheses);
                                                                        
$ifOperators[] = &$needParams[0];
+                                                                       
array_shift($needParams);
                                                                }
-                                                               
array_shift($needParams);
                                                                break; 
/********** EXIT **********/
                                                        } else { // Example: 
if(1) { echo 2; }
                                                                $bytecode[] = 
$tmp;
@@ -1037,6 +1050,18 @@
                                        }while( ',' == self::getNextToken( 
$tokens, $index, $countTokens, $tokenLine, array(',', ';') ) );
                                        $bytecode[][] = $tmp;
                                        break;
+                               case T_LIST:
+                                       if ( $rightOperators ) { throw new 
ExceptionFoxway($id, FOXWAY_PHP_SYNTAX_ERROR_UNEXPECTED, $tokenLine); }
+                                       if ( $parentFlags & 
FOXWAY_ALLOW_ONLY_VARIABLES && $needParams[0][FOXWAY_STACK_COMMAND] == T_LIST ) 
{ // T_LIST inside T_LIST. Example: list($a, list
+                                               $parentheses[] = $parentFlags;
+                                               array_unshift( $needParams, 
array( FOXWAY_STACK_COMMAND=>$id, FOXWAY_STACK_RESULT=>null, 
FOXWAY_STACK_PARAM=>array(), FOXWAY_STACK_TOKEN_LINE=>$tokenLine ) );
+                                               $memory[] = array($stack, 
array()); // push stack for restore late.
+                                               $stack = array();
+                                               $parentLevel++;
+                                               self::getNextToken( $tokens, 
$index, $countTokens, $tokenLine, array('(') );
+                                               break; /**** EXIT ****/
+                                       }
+                                       // break is not necessary here
                                case T_PRINT:
                                case T_ISSET:
                                case T_UNSET:
@@ -1064,22 +1089,21 @@
                                        $stack = array();
                                        $math = array();
 
-                                       if( $id == T_PRINT ) {
-                                               $parentFlags |= 
FOXWAY_EXPECT_SEMICOLON;
-                                               
$needParams[0][FOXWAY_STACK_RESULT] = 1;
+                                       if ( $id == T_PRINT ) {
+                                                       $parentFlags |= 
FOXWAY_EXPECT_SEMICOLON;
+                                                       
$needParams[0][FOXWAY_STACK_RESULT] = 1;
                                                break; /**** EXIT ****/
-                                       }elseif( $id == T_EMPTY ) {
+                                       } elseif ( $id == T_LIST ) {
+                                               $parentFlags |= 
FOXWAY_EXPECT_PARENTHES_CLOSE|FOXWAY_EXPECT_LIST_PARAMS|FOXWAY_ALLOW_ONLY_VARIABLES|FOXWAY_ALLOW_SKIP_PARAMS;
+                                       } elseif ( $id == T_EMPTY ) {
                                                $parentFlags |= 
FOXWAY_EXPECT_PARENTHES_CLOSE|FOXWAY_EXPECT_LIST_PARAMS;
-                                       }else{ // T_UNSET, T_ISSET
+                                       } else { // T_UNSET, T_ISSET
                                                $parentFlags |= 
FOXWAY_EXPECT_PARENTHES_CLOSE|FOXWAY_EXPECT_LIST_PARAMS|FOXWAY_ALLOW_ONLY_VARIABLES;
                                        }
                                        $parentLevel++;
 
                                        self::getNextToken( $tokens, $index, 
$countTokens, $tokenLine, array('(') );
                                        break;
-                               case T_LIST:
-
-                                       // @todo
                                default :
                                        throw new ExceptionFoxway($id, 
FOXWAY_PHP_SYNTAX_ERROR_UNEXPECTED, $tokenLine);
                                        break;
diff --git a/includes/Runtime.php b/includes/Runtime.php
index 6d12dd4..f7c7b5a 100644
--- a/includes/Runtime.php
+++ b/includes/Runtime.php
@@ -500,18 +500,24 @@
                                                }
                                                $value[FOXWAY_STACK_RESULT] = 
true;
                                                break;
-                                       default:
-                                               if( !isset($thisVariables[ 
$value[FOXWAY_STACK_PARAM][FOXWAY_STACK_PARAM] ]) ) { // Use undefined variable
-                                                       if( 
isset($value[FOXWAY_STACK_ARRAY_INDEX]) ) { // Example: $foo[1]++
-                                                               $thisVariables[ 
$value[FOXWAY_STACK_PARAM][FOXWAY_STACK_PARAM] ] = array();
-                                                       }else{
-                                                               $thisVariables[ 
$value[FOXWAY_STACK_PARAM][FOXWAY_STACK_PARAM] ] = null;
-                                                       }
-                                                       // @todo E_NOTICE
+                                       default: // ++, --, =, +=, -=, *=, 
etc...
+                                               $param = 
&$value[FOXWAY_STACK_PARAM];
+                                               if ( 
$param[FOXWAY_STACK_COMMAND] == T_LIST ) { // this is T_LIST. Example: 
list($foo, $bar) = $array;
+                                                       self::fillList( 
$value[FOXWAY_STACK_PARAM_2], $param, $thisVariables );
+                                                       unset( $param );
+                                                       break; /**** EXIT ****/
                                                }
-                                               $ref = &$thisVariables[ 
$value[FOXWAY_STACK_PARAM][FOXWAY_STACK_PARAM] ];
-                                               if( 
isset($value[FOXWAY_STACK_PARAM][FOXWAY_STACK_ARRAY_INDEX]) ) { // Example: 
$foo[1]++
-                                                       foreach( 
$value[FOXWAY_STACK_PARAM][FOXWAY_STACK_ARRAY_INDEX] as $v ) {
+                                               if( !isset($thisVariables[ 
$param[FOXWAY_STACK_PARAM] ]) ) { // Use undefined variable
+                                                       if( 
isset($value[FOXWAY_STACK_ARRAY_INDEX]) ) { // Example: $foo[1]++
+                                                               $thisVariables[ 
$param[FOXWAY_STACK_PARAM] ] = array();
+                                                       }else{
+                                                               $thisVariables[ 
$param[FOXWAY_STACK_PARAM] ] = null;
+                                                       }
+                                                       // @todo E_NOTICE if 
need
+                                               }
+                                               $ref = &$thisVariables[ 
$param[FOXWAY_STACK_PARAM] ];
+                                               if ( 
isset($param[FOXWAY_STACK_ARRAY_INDEX]) ) { // Example: $foo[1]++
+                                                       foreach ( 
$param[FOXWAY_STACK_ARRAY_INDEX] as $v ) {
                                                                if( $v === null 
) { // Example: $foo[]
                                                                        $t = 
null;
                                                                        $ref[] 
= &$t;
@@ -526,7 +532,7 @@
                                                                }
                                                        }
                                                }
-                                               switch 
($value[FOXWAY_STACK_COMMAND]) {
+                                               switch ( 
$value[FOXWAY_STACK_COMMAND] ) {
                                                        case T_INC:
                                                                $ref++;
                                                                break;
@@ -535,48 +541,49 @@
                                                                break;
                                                        case '=':
                                                                // Save result 
in T_VARIABLE FOXWAY_STACK_RESULT, Save result in $thisVariables[variable name]
-                                                               
$value[FOXWAY_STACK_PARAM][FOXWAY_STACK_RESULT] = $ref = 
$value[FOXWAY_STACK_PARAM_2];
+                                                               
$param[FOXWAY_STACK_RESULT] = $ref = $value[FOXWAY_STACK_PARAM_2];
                                                                break;
                                                        case T_PLUS_EQUAL:      
        // +=
-                                                               
$value[FOXWAY_STACK_PARAM][FOXWAY_STACK_RESULT] = $ref += 
$value[FOXWAY_STACK_PARAM_2];
+                                                               
$param[FOXWAY_STACK_RESULT] = $ref += $value[FOXWAY_STACK_PARAM_2];
                                                                break;
                                                        case T_MINUS_EQUAL:     
        // -=
-                                                               
$value[FOXWAY_STACK_PARAM][FOXWAY_STACK_RESULT] = $ref -= 
$value[FOXWAY_STACK_PARAM_2];
+                                                               
$param[FOXWAY_STACK_RESULT] = $ref -= $value[FOXWAY_STACK_PARAM_2];
                                                                break;
                                                        case T_MUL_EQUAL:       
        // *=
-                                                               
$value[FOXWAY_STACK_PARAM][FOXWAY_STACK_RESULT] = $ref *= 
$value[FOXWAY_STACK_PARAM_2];
+                                                               
$param[FOXWAY_STACK_RESULT] = $ref *= $value[FOXWAY_STACK_PARAM_2];
                                                                break;
                                                        case T_DIV_EQUAL:       
        // /=
                                                                if( 
(int)$value[FOXWAY_STACK_PARAM_2] == 0 ) {
                                                                        throw 
new ExceptionFoxway(null, FOXWAY_PHP_WARNING_DIVISION_BY_ZERO, 
$value[FOXWAY_STACK_TOKEN_LINE]);
                                                                }
-                                                               
$value[FOXWAY_STACK_PARAM][FOXWAY_STACK_RESULT] = $ref /= 
$value[FOXWAY_STACK_PARAM_2];
+                                                               
$param[FOXWAY_STACK_RESULT] = $ref /= $value[FOXWAY_STACK_PARAM_2];
                                                                break;
                                                        case T_CONCAT_EQUAL:    
// .=
-                                                               
$value[FOXWAY_STACK_PARAM][FOXWAY_STACK_RESULT] = $ref .= 
$value[FOXWAY_STACK_PARAM_2];
+                                                               
$param[FOXWAY_STACK_RESULT] = $ref .= $value[FOXWAY_STACK_PARAM_2];
                                                                break;
                                                        case T_MOD_EQUAL:       
        // %=
                                                                if( 
(int)$value[FOXWAY_STACK_PARAM_2] == 0 ) {
                                                                        throw 
new ExceptionFoxway(null, FOXWAY_PHP_WARNING_DIVISION_BY_ZERO, 
$value[FOXWAY_STACK_TOKEN_LINE]);
                                                                }
-                                                               
$value[FOXWAY_STACK_PARAM][FOXWAY_STACK_RESULT] = $ref %= 
$value[FOXWAY_STACK_PARAM_2];
+                                                               
$param[FOXWAY_STACK_RESULT] = $ref %= $value[FOXWAY_STACK_PARAM_2];
                                                                break;
                                                        case T_AND_EQUAL:       
        // &=
-                                                               
$value[FOXWAY_STACK_PARAM][FOXWAY_STACK_RESULT] = $ref &= 
$value[FOXWAY_STACK_PARAM_2];
+                                                               
$param[FOXWAY_STACK_RESULT] = $ref &= $value[FOXWAY_STACK_PARAM_2];
                                                                break;
                                                        case T_OR_EQUAL:        
        // |=
-                                                               
$value[FOXWAY_STACK_PARAM][FOXWAY_STACK_RESULT] = $ref |= 
$value[FOXWAY_STACK_PARAM_2];
+                                                               
$param[FOXWAY_STACK_RESULT] = $ref |= $value[FOXWAY_STACK_PARAM_2];
                                                                break;
                                                        case T_XOR_EQUAL:       
        // ^=
-                                                               
$value[FOXWAY_STACK_PARAM][FOXWAY_STACK_RESULT] = $ref ^= 
$value[FOXWAY_STACK_PARAM_2];
+                                                               
$param[FOXWAY_STACK_RESULT] = $ref ^= $value[FOXWAY_STACK_PARAM_2];
                                                                break;
                                                        case T_SL_EQUAL:        
        // <<=
-                                                               
$value[FOXWAY_STACK_PARAM][FOXWAY_STACK_RESULT] = $ref <<= 
$value[FOXWAY_STACK_PARAM_2];
+                                                               
$param[FOXWAY_STACK_RESULT] = $ref <<= $value[FOXWAY_STACK_PARAM_2];
                                                                break;
                                                        case T_SR_EQUAL:        
        // >>=
-                                                               
$value[FOXWAY_STACK_PARAM][FOXWAY_STACK_RESULT] = $ref >>= 
$value[FOXWAY_STACK_PARAM_2];
+                                                               
$param[FOXWAY_STACK_RESULT] = $ref >>= $value[FOXWAY_STACK_PARAM_2];
                                                                break;
                                                }
+                                               unset($param);
                                                break;
                                }
                        }
@@ -585,4 +592,48 @@
                return $return;
        }
 
+       private static function fillList( &$values, &$param, &$thisVariables ) {
+               $return = array();
+               foreach ( $param[FOXWAY_STACK_PARAM] as $key => $val ) {
+                       if( $val !== null ) { // skip emty params. Example: 
list(, $bar) = $array;
+                               if( $val[FOXWAY_STACK_COMMAND] == T_LIST ) { // 
T_LIST inside other T_LIST. Example: list($a, list($b, $c)) = array(1, array(2, 
3));
+                                       if ( is_array($values) && 
isset($values[$key]) ) {
+                                               $return[$key] = 
self::fillList($values[$key], $val, $thisVariables);
+                                       } else {
+                                               static $a=array();
+                                               $return[$key] = 
self::fillList($a, $val, $thisVariables);
+                                       }
+                                       continue;
+                               }
+                               $ref = &$thisVariables[ 
$val[FOXWAY_STACK_PARAM] ];
+                               if ( isset($val[FOXWAY_STACK_ARRAY_INDEX]) ) { 
// Example: list($foo[0], $foo[1]) = $array;
+                                       foreach ( 
$val[FOXWAY_STACK_ARRAY_INDEX] as $v ) {
+                                               if (  $v === null ) { // 
Example: $foo[]
+                                                       $t = null;
+                                                       $ref[] = &$t;
+                                                       $ref = &$t;
+                                                       unset( $t );
+                                               } else {
+                                                       if ( !isset($ref[$v]) ) 
{
+                                                               $ref[$v] = null;
+                                                       }
+                                                       $ref = &$ref[$v];
+                                               }
+                                       }
+                               }
+                               if ( is_array($values) ) {
+                                       if ( isset($values[$key]) ) {
+                                               $ref = $values[$key];
+                                       } else {
+                                               $ref = null;
+                                               // @todo E_NOTICE
+                                       }
+                               } else { // list() work with array only
+                                       $ref = null;
+                               }
+                               $return[$key] = $ref;
+                       }
+               }
+       }
+
 }
diff --git a/tests/phpunit/includes/RuntimeTest.php 
b/tests/phpunit/includes/RuntimeTest.php
index 7d8796e..50f4a11 100644
--- a/tests/phpunit/includes/RuntimeTest.php
+++ b/tests/phpunit/includes/RuntimeTest.php
@@ -2995,4 +2995,35 @@
                                );
        }
 
+       public function testRun_echo_list_1() {
+               $this->assertEquals(
+                               Runtime::runSource('$info = array("coffee", 
"brown", "caffeine"); list($drink, $color, $power) = $info; echo "$drink is 
$color and $power makes it special.";', array('testList'), 1),
+                               array('coffee is brown and caffeine makes it 
special.')
+                               );
+       }
+       public function testRun_echo_list_2() {
+               $this->assertEquals(
+                               Runtime::runSource('$info = array("coffee", 
"brown", "caffeine"); list($drink, , $power) = $info; echo "$drink has 
$power.";', array('testList'), 2),
+                               array('coffee has caffeine.')
+                               );
+       }
+       public function testRun_echo_list_3() {
+               $this->assertEquals(
+                               Runtime::runSource('$info = array("coffee", 
"brown", "caffeine"); list( , , $power) = $info; echo "I need $power!";', 
array('testList'), 3),
+                               array('I need caffeine!')
+                               );
+       }
+       public function testRun_echo_list_4() {
+               $this->assertEquals(
+                               Runtime::runSource('list($bar) = "abcde"; echo 
print_r($bar,true);'),
+                               array('')
+                               );
+       }
+       public function testRun_echo_list_5() {
+               $this->assertEquals(
+                               Runtime::runSource('list($a, list($b, $c)) = 
array(1, array(2, 3)); echo $a, $b, $c;'),
+                               array(1, 2, 3)
+                               );
+       }
+
 }

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

Gerrit-MessageType: newchange
Gerrit-Change-Id: Iad81e099d562424e6c2a7f3396c6c5894f27108b
Gerrit-PatchSet: 1
Gerrit-Project: mediawiki/extensions/Foxway
Gerrit-Branch: develop
Gerrit-Owner: Pastakhov <pastak...@yandex.ru>

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

Reply via email to