Skip to content

Commit

Permalink
Less_Tree_Quoted: Ignore quote type when comparing values
Browse files Browse the repository at this point in the history
* Ported Quoted change from upstream
  less/less.js@5647d4d2761

* Sync other compare() implementations to move logic to nodeCompare()
  and acknowledge the null/undefined outcome.

Bug: T357160
Change-Id: I657e7a950ae16ba194d14e438d71e691d0c96f33
  • Loading branch information
Krinkle committed Feb 12, 2024
1 parent 99460b3 commit 6d652ed
Show file tree
Hide file tree
Showing 8 changed files with 131 additions and 59 deletions.
57 changes: 57 additions & 0 deletions lib/Less/Tree.php
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,63 @@ public static function outputRuleset( $output, $rules ) {
public function accept( $visitor ) {
}

/**
* @param Less_Tree $a
* @param Less_Tree $b
* @return int|null
* @see less-2.5.3.js#Node.compare
*/
public static function nodeCompare( $a, $b ) {
// Less_Tree subclasses that implement compare() are:
// Anonymous, Color, Dimension, Keyword, Quoted, Unit
if ( $b instanceof Less_Tree_Quoted || $b instanceof Less_Tree_Anonymous ) {
// for "symmetric results" force toCSS-based comparison via b.compare()
// of Quoted or Anonymous if either value is one of those
return -$b->compare( $a );
} elseif ( $a instanceof Less_Tree_Anonymous || $a instanceof Less_Tree_Color
|| $a instanceof Less_Tree_Dimension || $a instanceof Less_Tree_Keyword
|| $a instanceof Less_Tree_Quoted || $a instanceof Less_Tree_Unit
) {
return $a->compare( $b );
} elseif ( get_class( $a ) !== get_class( $b ) ) {
return null;
}

// Less_Tree subclasses that have an array value: Less_Tree_Expression, Less_Tree_Value
// @phan-suppress-next-line PhanUndeclaredProperty
$aval = $a->value ?? [];
// @phan-suppress-next-line PhanUndeclaredProperty
$bval = $b->value ?? [];
if ( !( $a instanceof Less_Tree_Expression || $a instanceof Less_Tree_Value ) ) {
return $aval === $bval ? 0 : null;
}
if ( count( $aval ) !== count( $bval ) ) {
return null;
}
foreach ( $aval as $i => $item ) {
if ( self::nodeCompare( $item, $bval[$i] ) !== 0 ) {
return null;
}
}
return 0;
}

/**
* @param string|float|int $a
* @param string|float|int $b
* @return int|null
* @see less-2.5.3.js#Node.numericCompare
*/
public static function numericCompare( $a, $b ) {
return $a < $b ? -1
: ( $a === $b ? 0
: ( $a > $b ? 1
// NAN is not greater, less, or equal
: null
)
);
}

public static function ReferencedArray( $rules ) {
foreach ( $rules as $rule ) {
if ( method_exists( $rule, 'markReferenced' ) ) {
Expand Down
18 changes: 6 additions & 12 deletions lib/Less/Tree/Anonymous.php
Original file line number Diff line number Diff line change
Expand Up @@ -26,19 +26,13 @@ public function compile( $env ) {
return new self( $this->value, $this->index, $this->currentFileInfo, $this->mapLines );
}

/**
* @param Less_Tree|mixed $x
* @return int|null
* @see less-2.5.3.js#Anonymous.prototype.compare
*/
public function compare( $x ) {
if ( !is_object( $x ) ) {
return -1;
}

$left = $this->toCSS();
$right = $x->toCSS();

if ( $left === $right ) {
return 0;
}

return $left < $right ? -1 : 1;
return ( is_object( $x ) && $this->toCSS() === $x->toCSS() ) ? 0 : null;
}

/**
Expand Down
9 changes: 7 additions & 2 deletions lib/Less/Tree/Color.php
Original file line number Diff line number Diff line change
Expand Up @@ -191,15 +191,20 @@ public function toARGB() {
return $this->toHex( $argb );
}

/**
* @param mixed $x
* @return int|null
* @see less-2.5.3.js#Color.prototype.compare
*/
public function compare( $x ) {
if ( !property_exists( $x, 'rgb' ) ) {
if ( !$x instanceof self ) {
return -1;
}

return ( $x->rgb[0] === $this->rgb[0] &&
$x->rgb[1] === $this->rgb[1] &&
$x->rgb[2] === $this->rgb[2] &&
$x->alpha === $this->alpha ) ? 0 : -1;
$x->alpha === $this->alpha ) ? 0 : null;
}

public function toHex( $v ) {
Expand Down
19 changes: 8 additions & 11 deletions lib/Less/Tree/Condition.php
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,11 @@ public function accept( $visitor ) {
$this->rvalue = $visitor->visitObj( $this->rvalue );
}

/**
* @param Less_Environment $env
* @return bool
* @see less-2.5.3.js#Condition.prototype.eval
*/
public function compile( $env ) {
$a = $this->lvalue->compile( $env );
$b = $this->rvalue->compile( $env );
Expand All @@ -37,26 +42,18 @@ public function compile( $env ) {
break;

default:
if ( Less_Parser::is_method( $a, 'compare' ) ) {
$result = $a->compare( $b );
} elseif ( Less_Parser::is_method( $b, 'compare' ) ) {
$result = $b->compare( $a );
} else {
throw new Less_Exception_Compiler( 'Unable to perform comparison', null, $this->index );
}

switch ( $result ) {
switch ( Less_Tree::nodeCompare( $a, $b ) ) {
case -1:
$result = $this->op === '<' || $this->op === '=<' || $this->op === '<=';
break;

case 0:
$result = $this->op === '=' || $this->op === '>=' || $this->op === '=<' || $this->op === '<=';
break;

case 1:
$result = $this->op === '>' || $this->op === '>=';
break;
default:
$result = false;
}
break;
}
Expand Down
40 changes: 18 additions & 22 deletions lib/Less/Tree/Dimension.php
Original file line number Diff line number Diff line change
Expand Up @@ -110,32 +110,28 @@ public function operate( $op, $other ) {
return new self( $value, $unit );
}

/**
* @param Less_Tree $other
* @return int|null
* @see less-2.5.3.js#Dimension.prototype.compare
*/
public function compare( $other ) {
if ( $other instanceof self ) {

if ( $this->unit->isEmpty() || $other->unit->isEmpty() ) {
$a = $this;
$b = $other;
} else {
$a = $this->unify();
$b = $other->unify();
if ( $a->unit->compare( $b->unit ) !== 0 ) {
return -1;
}
}
$aValue = $a->value;
$bValue = $b->value;
if ( !$other instanceof self ) {
return null;
}

if ( $bValue > $aValue ) {
return -1;
} elseif ( $bValue < $aValue ) {
return 1;
} else {
return 0;
}
if ( $this->unit->isEmpty() || $other->unit->isEmpty() ) {
$a = $this;
$b = $other;
} else {
return -1;
$a = $this->unify();
$b = $other->unify();
if ( $a->unit->compare( $b->unit ) !== 0 ) {
return null;
}
}

return Less_Tree::numericCompare( $a->value, $b->value );
}

public function unify() {
Expand Down
25 changes: 13 additions & 12 deletions lib/Less/Tree/Quoted.php
Original file line number Diff line number Diff line change
Expand Up @@ -61,18 +61,19 @@ public function compile( $env ) {
return new self( $this->quote . $r . $this->quote, $r, $this->escaped, $this->index, $this->currentFileInfo );
}

public function compare( $x ) {
if ( !Less_Parser::is_method( $x, 'toCSS' ) ) {
return -1;
}

$left = $this->toCSS();
$right = $x->toCSS();

if ( $left === $right ) {
return 0;
/**
* @param mixed $other
* @return int|null
* @see less-2.5.3.js#Quoted.prototype.compare
*/
public function compare( $other ) {
if ( $other instanceof self && !$this->escaped && !$other->escaped ) {
return Less_Tree::numericCompare( $this->value, $other->value );
} else {
return (
Less_Parser::is_method( $other, 'toCSS' )
&& $this->toCSS() === $other->toCSS()
) ? 0 : null;
}

return $left < $right ? -1 : 1;
}
}
8 changes: 8 additions & 0 deletions test/Fixtures/less.php/css/T357160-quoted-compare.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
double {
background: red;
background: blue;
}
single {
background: red;
background: blue;
}
14 changes: 14 additions & 0 deletions test/Fixtures/less.php/less/T357160-quoted-compare.less
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
.test-mixin(@value) when (@value = "test") {
background: red;
}

.test-mixin(@value) when (@value = 'test') {
background: blue;
}

double {
.test-mixin("test")
}
single {
.test-mixin('test')
}

0 comments on commit 6d652ed

Please sign in to comment.