Skip to content

Commit

Permalink
Less_Parser: Port faster matching with matchStr() method
Browse files Browse the repository at this point in the history
There are various parse methods in our codebase still using `matchReg`
(equivalent to `parserInput.$re` in Less.js), whereas the reference
implementation now uses `parserInput.$str` for various simple
sequences. This was done upstream in commit
less/less.js@1a33bc69f8 as part of
less/less.js#1615, which we did not
port until now.

For us it does not appear to provide a consistent speed-up, via
test/bench.php. It makes less than 1% difference and varies in which
direction. The motivation for this change is primarily code parity.

I suspect the reason upstream saw a bigger difference is that they
combined it with various other optimizations, which we have already
ported, such as the Visitor cache, and optimised parseEntitiesQuoted
method.

Change-Id: I9553c46f0fb3cc6f985d18b577c3948efd48805b
  • Loading branch information
Krinkle committed Mar 21, 2024
1 parent ba3d74c commit 7d50b1d
Showing 1 changed file with 35 additions and 8 deletions.
43 changes: 35 additions & 8 deletions lib/Less/Parser.php
Original file line number Diff line number Diff line change
Expand Up @@ -822,6 +822,24 @@ private function matchReg( $tok ) {
}
}

/**
* Match an exact string of characters.
*
* @param string $tok
* @return string|null
* @see less-2.5.3.js#parserInput.$str
*/
private function matchStr( $tok ) {
$tokLength = strlen( $tok );
if (
( $this->pos < $this->input_len ) &&
substr( $this->input, $this->pos, $tokLength ) === $tok
) {
$this->skipWhitespace( $tokLength );
return $tok;
}
}

/**
* Same as match(), but don't change the state of the parser,
* just return the match.
Expand Down Expand Up @@ -1351,11 +1369,12 @@ private function parseRulesetCall() {
//
// extend syntax - used to extend selectors
//
// @see less-2.5.3.js#parsers.extend
private function parseExtend( $isRule = false ) {
$index = $this->pos;
$extendList = [];

if ( !$this->MatchReg( $isRule ? '/\\G&:extend\(/' : '/\\G:extend\(/' ) ) {
if ( !$this->matchStr( $isRule ? '&:extend(' : ':extend(' ) ) {
return;
}

Expand Down Expand Up @@ -1453,6 +1472,7 @@ private function parseMixinCallElements() {

/**
* @param bool $isCall
* @see less-2.5.3.js#parsers.mixin.args
*/
private function parseMixinArgs( $isCall ) {
$expressions = [];
Expand All @@ -1470,7 +1490,7 @@ private function parseMixinArgs( $isCall ) {
$arg = $this->parseDetachedRuleset() ?? $this->parseExpression();
} else {
$this->parseComments();
if ( $this->input[ $this->pos ] === '.' && $this->MatchReg( '/\\G\.{3}/' ) ) {
if ( $this->input[ $this->pos ] === '.' && $this->matchStr( '...' ) ) {
$returner['variadic'] = true;
if ( $this->matchChar( ";" ) && !$isSemiColonSeperated ) {
$isSemiColonSeperated = true;
Expand Down Expand Up @@ -1532,7 +1552,7 @@ private function parseMixinArgs( $isCall ) {
}

$nameLoop = ( $name = $val->name );
} elseif ( !$isCall && $this->MatchReg( '/\\G\.{3}/' ) ) {
} elseif ( !$isCall && $this->matchStr( '...' ) ) {
$returner['variadic'] = true;
if ( $this->matchChar( ";" ) && !$isSemiColonSeperated ) {
$isSemiColonSeperated = true;
Expand Down Expand Up @@ -1602,6 +1622,7 @@ private function parseMixinArgs( $isCall ) {
// Once we've got our params list, and a closing `)`, we parse
// the `{...}` block.
//
// @see less-2.5.3.js#parsers.mixin.definition
private function parseMixinDefinition() {
$cond = null;

Expand Down Expand Up @@ -1633,7 +1654,7 @@ private function parseMixinDefinition() {

$this->parseComments();

if ( $this->MatchReg( '/\\Gwhen/' ) ) { // Guard
if ( $this->matchStr( 'when' ) ) { // Guard
$cond = $this->expect( 'parseConditions', 'Expected conditions' );
}

Expand Down Expand Up @@ -1821,7 +1842,7 @@ private function parseSelector( $isLess = false ) {
$c = null;
$index = $this->pos;

while ( ( $isLess && ( $extend = $this->parseExtend() ) ) || ( $isLess && ( $when = $this->matchReg( '/\\Gwhen/' ) ) ) || ( $e = $this->parseElement() ) ) {
while ( ( $isLess && ( $extend = $this->parseExtend() ) ) || ( $isLess && ( $when = $this->matchStr( 'when' ) ) ) || ( $e = $this->parseElement() ) ) {
if ( $when ) {
$condition = $this->expect( 'parseConditions', 'expected condition' );
} elseif ( $condition ) {
Expand Down Expand Up @@ -2184,8 +2205,11 @@ private function parseMediaFeatures() {
return $features ?: null;
}

/**
* @see less-2.5.3.js#parsers.media
*/
private function parseMedia() {
if ( $this->matchReg( '/\\G@media/' ) ) {
if ( $this->matchStr( '@media' ) ) {
$this->save();

$features = $this->parseMediaFeatures();
Expand Down Expand Up @@ -2464,12 +2488,15 @@ private function parseConditions() {
}
}

/**
* @see less-2.5.3.js#parsers.condition
*/
private function parseCondition() {
$index = $this->pos;
$negate = false;
$c = null;

if ( $this->matchReg( '/\\Gnot/' ) ) {
if ( $this->matchStr( 'not' ) ) {
$negate = true;
}
$this->expectChar( '(' );
Expand All @@ -2490,7 +2517,7 @@ private function parseCondition() {
}
$this->expectChar( ')' );
// @phan-suppress-next-line PhanPossiblyInfiniteRecursionSameParams
return $this->matchReg( '/\\Gand/' ) ? new Less_Tree_Condition( 'and', $c, $this->parseCondition() ) : $c;
return $this->matchStr( 'and' ) ? new Less_Tree_Condition( 'and', $c, $this->parseCondition() ) : $c;
}
}

Expand Down

0 comments on commit 7d50b1d

Please sign in to comment.