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