Clarify -{ => {{ transition
authorArlo Breault <abreault@wikimedia.org>
Sat, 10 Mar 2018 00:40:36 +0000 (19:40 -0500)
committerArlo Breault <abreault@wikimedia.org>
Thu, 15 Mar 2018 17:04:59 +0000 (13:04 -0400)
Ensure we have the correct rule on the stack.

Change-Id: Ie814df7b759a2381be0b815eeefdb5d1f7adcde0

includes/parser/Preprocessor_DOM.php
includes/parser/Preprocessor_Hash.php
tests/parser/parserTests.txt

index 09ca38a..64edbb2 100644 (file)
@@ -274,13 +274,6 @@ class Preprocessor_DOM extends Preprocessor {
                                $search = $searchBase;
                                if ( $stack->top === false ) {
                                        $currentClosing = '';
-                               } elseif (
-                                       $stack->top->close === '}-' &&
-                                       $stack->top->count > 2
-                               ) {
-                                       # adjust closing for -{{{...{{
-                                       $currentClosing = '}';
-                                       $search .= $currentClosing;
                                } else {
                                        $currentClosing = $stack->top->close;
                                        $search .= $currentClosing;
@@ -643,14 +636,27 @@ class Preprocessor_DOM extends Preprocessor {
                                        strspn( $text, $curChar[$curLen - 1], $i + 1 ) + 1 :
                                        strspn( $text, $curChar, $i );
 
+                               $savedPrefix = '';
+                               $lineStart = ( $i > 0 && $text[$i - 1] == "\n" );
+
+                               if ( $curChar === "-{" && $count > $curLen ) {
+                                       // -{ => {{ transition because rightmost wins
+                                       $savedPrefix = '-';
+                                       $i++;
+                                       $curChar = '{';
+                                       $count--;
+                                       $rule = $this->rules[$curChar];
+                               }
+
                                # we need to add to stack only if opening brace count is enough for one of the rules
                                if ( $count >= $rule['min'] ) {
                                        # Add it to the stack
                                        $piece = [
                                                'open' => $curChar,
                                                'close' => $rule['end'],
+                                               'savedPrefix' => $savedPrefix,
                                                'count' => $count,
-                                               'lineStart' => ( $i > 0 && $text[$i - 1] == "\n" ),
+                                               'lineStart' => $lineStart,
                                        ];
 
                                        $stack->push( $piece );
@@ -667,7 +673,7 @@ class Preprocessor_DOM extends Preprocessor {
                                        }
                                } else {
                                        # Add literal brace(s)
-                                       $accum .= htmlspecialchars( str_repeat( $curChar, $count ) );
+                                       $accum .= htmlspecialchars( $savedPrefix . str_repeat( $curChar, $count ) );
                                }
                                $i += $count;
                        } elseif ( $found == 'close' ) {
@@ -684,10 +690,6 @@ class Preprocessor_DOM extends Preprocessor {
                                # check for maximum matching characters (if there are 5 closing
                                # characters, we will probably need only 3 - depending on the rules)
                                $rule = $this->rules[$piece->open];
-                               if ( $piece->close === '}-' && $piece->count > 2 ) {
-                                       # tweak for -{..{{ }}..}-
-                                       $rule = $this->rules['{'];
-                               }
                                if ( $count > $rule['max'] ) {
                                        # The specified maximum exists in the callback array, unless the caller
                                        # has made an error
@@ -724,7 +726,9 @@ class Preprocessor_DOM extends Preprocessor {
 
                                        # The invocation is at the start of the line if lineStart is set in
                                        # the stack, and all opening brackets are used up.
-                                       if ( $maxCount == $matchingCount && !empty( $piece->lineStart ) ) {
+                                       if ( $maxCount == $matchingCount &&
+                                                       !empty( $piece->lineStart ) &&
+                                                       strlen( $piece->savedPrefix ) == 0 ) {
                                                $attr = ' lineStart="1"';
                                        } else {
                                                $attr = '';
@@ -762,15 +766,25 @@ class Preprocessor_DOM extends Preprocessor {
                                        if ( $piece->count >= $min ) {
                                                $stack->push( $piece );
                                                $accum =& $stack->getAccum();
+                                       } elseif ( $piece->count == 1 && $piece->open === '{' && $piece->savedPrefix === '-' ) {
+                                               $piece->savedPrefix = '';
+                                               $piece->open = '-{';
+                                               $piece->count = 2;
+                                               $piece->close = $this->rules[$piece->open]['end'];
+                                               $stack->push( $piece );
+                                               $accum =& $stack->getAccum();
                                        } else {
                                                $s = substr( $piece->open, 0, -1 );
                                                $s .= str_repeat(
                                                        substr( $piece->open, -1 ),
                                                        $piece->count - strlen( $s )
                                                );
-                                               $accum .= $s;
+                                               $accum .= $piece->savedPrefix . $s;
                                        }
+                               } elseif ( $piece->savedPrefix !== '' ) {
+                                       $accum .= $piece->savedPrefix;
                                }
+
                                $stackFlags = $stack->getFlags();
                                if ( isset( $stackFlags['findEquals'] ) ) {
                                        $findEquals = $stackFlags['findEquals'];
@@ -912,6 +926,12 @@ class PPDStackElement {
         */
        public $close;
 
+       /**
+        * @var string Saved prefix that may affect later processing,
+        *  e.g. to differentiate `-{{{{` and `{{{{` after later seeing `}}}`.
+        */
+       public $savedPrefix = '';
+
        /**
         * @var int Number of opening characters found (number of "=" for heading)
         */
@@ -973,7 +993,7 @@ class PPDStackElement {
         */
        public function breakSyntax( $openingCount = false ) {
                if ( $this->open == "\n" ) {
-                       $s = $this->parts[0]->out;
+                       $s = $this->savedPrefix . $this->parts[0]->out;
                } else {
                        if ( $openingCount === false ) {
                                $openingCount = $this->count;
@@ -983,6 +1003,7 @@ class PPDStackElement {
                                substr( $this->open, -1 ),
                                $openingCount - strlen( $s )
                        );
+                       $s = $this->savedPrefix . $s;
                        $first = true;
                        foreach ( $this->parts as $part ) {
                                if ( $first ) {
index 7dbf9b1..c7f630d 100644 (file)
@@ -206,13 +206,6 @@ class Preprocessor_Hash extends Preprocessor {
                                $search = $searchBase;
                                if ( $stack->top === false ) {
                                        $currentClosing = '';
-                               } elseif (
-                                       $stack->top->close === '}-' &&
-                                       $stack->top->count > 2
-                               ) {
-                                       # adjust closing for -{{{...{{
-                                       $currentClosing = '}';
-                                       $search .= $currentClosing;
                                } else {
                                        $currentClosing = $stack->top->close;
                                        $search .= $currentClosing;
@@ -589,14 +582,27 @@ class Preprocessor_Hash extends Preprocessor {
                                        strspn( $text, $curChar[$curLen - 1], $i + 1 ) + 1 :
                                        strspn( $text, $curChar, $i );
 
+                               $savedPrefix = '';
+                               $lineStart = ( $i > 0 && $text[$i - 1] == "\n" );
+
+                               if ( $curChar === "-{" && $count > $curLen ) {
+                                       // -{ => {{ transition because rightmost wins
+                                       $savedPrefix = '-';
+                                       $i++;
+                                       $curChar = '{';
+                                       $count--;
+                                       $rule = $this->rules[$curChar];
+                               }
+
                                # we need to add to stack only if opening brace count is enough for one of the rules
                                if ( $count >= $rule['min'] ) {
                                        # Add it to the stack
                                        $piece = [
                                                'open' => $curChar,
                                                'close' => $rule['end'],
+                                               'savedPrefix' => $savedPrefix,
                                                'count' => $count,
-                                               'lineStart' => ( $i > 0 && $text[$i - 1] == "\n" ),
+                                               'lineStart' => $lineStart,
                                        ];
 
                                        $stack->push( $piece );
@@ -613,7 +619,7 @@ class Preprocessor_Hash extends Preprocessor {
                                        }
                                } else {
                                        # Add literal brace(s)
-                                       self::addLiteral( $accum, str_repeat( $curChar, $count ) );
+                                       self::addLiteral( $accum, $savedPrefix . str_repeat( $curChar, $count ) );
                                }
                                $i += $count;
                        } elseif ( $found == 'close' ) {
@@ -630,10 +636,6 @@ class Preprocessor_Hash extends Preprocessor {
                                # check for maximum matching characters (if there are 5 closing
                                # characters, we will probably need only 3 - depending on the rules)
                                $rule = $this->rules[$piece->open];
-                               if ( $piece->close === '}-' && $piece->count > 2 ) {
-                                       # tweak for -{..{{ }}..}-
-                                       $rule = $this->rules['{'];
-                               }
                                if ( $count > $rule['max'] ) {
                                        # The specified maximum exists in the callback array, unless the caller
                                        # has made an error
@@ -672,7 +674,9 @@ class Preprocessor_Hash extends Preprocessor {
 
                                        # The invocation is at the start of the line if lineStart is set in
                                        # the stack, and all opening brackets are used up.
-                                       if ( $maxCount == $matchingCount && !empty( $piece->lineStart ) ) {
+                                       if ( $maxCount == $matchingCount &&
+                                                       !empty( $piece->lineStart ) &&
+                                                       strlen( $piece->savedPrefix ) == 0 ) {
                                                $children[] = [ '@lineStart', [ 1 ] ];
                                        }
                                        $titleNode = [ 'title', $titleAccum ];
@@ -711,14 +715,23 @@ class Preprocessor_Hash extends Preprocessor {
                                        if ( $piece->count >= $min ) {
                                                $stack->push( $piece );
                                                $accum =& $stack->getAccum();
+                                       } elseif ( $piece->count == 1 && $piece->open === '{' && $piece->savedPrefix === '-' ) {
+                                               $piece->savedPrefix = '';
+                                               $piece->open = '-{';
+                                               $piece->count = 2;
+                                               $piece->close = $this->rules[$piece->open]['end'];
+                                               $stack->push( $piece );
+                                               $accum =& $stack->getAccum();
                                        } else {
                                                $s = substr( $piece->open, 0, -1 );
                                                $s .= str_repeat(
                                                        substr( $piece->open, -1 ),
                                                        $piece->count - strlen( $s )
                                                );
-                                               self::addLiteral( $accum, $s );
+                                               self::addLiteral( $accum, $piece->savedPrefix . $s );
                                        }
+                               } elseif ( $piece->savedPrefix !== '' ) {
+                                       self::addLiteral( $accum, $piece->savedPrefix );
                                }
 
                                $stackFlags = $stack->getFlags();
@@ -814,7 +827,7 @@ class PPDStackElement_Hash extends PPDStackElement {
         */
        public function breakSyntax( $openingCount = false ) {
                if ( $this->open == "\n" ) {
-                       $accum = $this->parts[0]->out;
+                       $accum = array_merge( [ $this->savedPrefix ], $this->parts[0]->out );
                } else {
                        if ( $openingCount === false ) {
                                $openingCount = $this->count;
@@ -824,7 +837,7 @@ class PPDStackElement_Hash extends PPDStackElement {
                                substr( $this->open, -1 ),
                                $openingCount - strlen( $s )
                        );
-                       $accum = [ $s ];
+                       $accum = [ $this->savedPrefix . $s ];
                        $lastIndex = 0;
                        $first = true;
                        foreach ( $this->parts as $part ) {
index f98044b..c3616ed 100644 (file)
@@ -12444,6 +12444,32 @@ parsoid=wt2html
 <p about="#mwt1" typeof="mw:Transclusion" data-mw='{"parts":[{"template":{"target":{"wt":"echo","href":"./Template:Echo"},"params":{"1":{"wt":"hi {{}}"}},"i":0}}]}'>hi {{}}</p>
 !! end
 
+!! test
+Preprocessor precedence 18: another rightmost wins scenario
+!! options
+parsoid=wt2html
+!! wikitext
+{{ -{{{{1|tplarg}}} }} }-
+!! html/php
+<p>{{ -{tplarg }} }-
+</p>
+!! html/parsoid
+<p>{{ -{<span about="#mwt1" typeof="mw:Param" data-mw='{"parts":[{"templatearg":{"target":{"wt":"1"},"params":{"1":{"wt":"tplarg"}},"i":0}}]}'>tplarg</span> }} }-</p>
+!! end
+
+!! test
+Preprocessor precedence 19: break syntax
+!! options
+parsoid=wt2html
+!! wikitext
+-{{
+!! html/php
+<p>-{{
+</p>
+!! html/parsoid
+<p>-{{</p>
+!! end
+
 ###
 ### Token Stream Patcher tests
 ###