diff --git a/lib/less/tree.js b/lib/less/tree.js index d7ccd974..5814059b 100644 --- a/lib/less/tree.js +++ b/lib/less/tree.js @@ -94,4 +94,44 @@ tree.outputRuleset = function (env, output, rules) { env.tabLevel--; }; +tree.compare = function (a, b) { + /* returns: + -1: a < b + 0: a = b + 1: a > b + and *any* other value for a != b (e.g. undefined, NaN, -2 etc.) */ + + if ((a.compare) && + // for "symmetric results" force toCSS-based comparison + // of Quoted or Anonymous if either value is one of those + !(b.type === "Quoted" || b.type === "Anonymous")) { + return a.compare(b); + } else if (b.compare) { + return -b.compare(a); + } else if (a.type !== b.type) { + return undefined; + } + + a = a.value; + b = b.value; + if (!Array.isArray(a)) { + return a === b ? 0 : undefined; + } + if (a.length !== b.length) { + return undefined; + } + for (var i = 0; i < a.length; i++) { + if (tree.compare(a[i], b[i]) !== 0) { + return undefined; + } + } + return 0; +}; + +tree.numericCompare = function (a, b) { + return a < b ? -1 + : a === b ? 0 + : a > b ? 1 : undefined; +}; + })(require('./tree')); diff --git a/lib/less/tree/anonymous.js b/lib/less/tree/anonymous.js index 45840613..a960de62 100644 --- a/lib/less/tree/anonymous.js +++ b/lib/less/tree/anonymous.js @@ -12,19 +12,8 @@ tree.Anonymous.prototype = { eval: function () { return new tree.Anonymous(this.value, this.index, this.currentFileInfo, this.mapLines, this.rulesetLike); }, - compare: function (x) { - if (!x.toCSS) { - return -1; - } - - var left = this.toCSS(), - right = x.toCSS(); - - if (left === right) { - return 0; - } - - return left < right ? -1 : 1; + compare: function (other) { + return other.toCSS && this.toCSS() === other.toCSS() ? 0 : undefined; }, isRulesetLike: function() { return this.rulesetLike; diff --git a/lib/less/tree/color.js b/lib/less/tree/color.js index eac71f7e..15303bea 100644 --- a/lib/less/tree/color.js +++ b/lib/less/tree/color.js @@ -150,14 +150,11 @@ tree.Color.prototype = { return toHex([this.alpha * 255].concat(this.rgb)); }, compare: function (x) { - if (!x.rgb) { - return -1; - } - - return (x.rgb[0] === this.rgb[0] && + return (x.rgb && + 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 : undefined; } }; diff --git a/lib/less/tree/condition.js b/lib/less/tree/condition.js index 56d221ef..c9ef56c6 100644 --- a/lib/less/tree/condition.js +++ b/lib/less/tree/condition.js @@ -14,34 +14,20 @@ tree.Condition.prototype = { this.rvalue = visitor.visit(this.rvalue); }, eval: function (env) { - var a = this.lvalue.eval(env), - b = this.rvalue.eval(env); - - var i = this.index, result; - - result = (function (op) { + var result = (function (op, a, b) { switch (op) { - case 'and': - return a && b; - case 'or': - return a || b; + case 'and': return a && b; + case 'or': return a || b; default: - if (a.compare) { - result = a.compare(b); - } else if (b.compare) { - result = b.compare(a); - } else { - throw { type: "Type", - message: "Unable to perform comparison", - index: i }; - } - switch (result) { + switch (tree.compare(a, b)) { case -1: return op === '<' || op === '=<' || op === '<='; case 0: return op === '=' || op === '>=' || op === '=<' || op === '<='; case 1: return op === '>' || op === '>='; + default: return false; } } - })(this.op); + }) (this.op, this.lvalue.eval(env), this.rvalue.eval(env)); + return this.negate ? !result : result; } }; diff --git a/lib/less/tree/dimension.js b/lib/less/tree/dimension.js index aecec136..aabf3374 100644 --- a/lib/less/tree/dimension.js +++ b/lib/less/tree/dimension.js @@ -88,33 +88,24 @@ tree.Dimension.prototype = { }, compare: function (other) { - if (other instanceof tree.Dimension) { - var a, b, - aValue, bValue; - - 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 (bValue > aValue) { - return -1; - } else if (bValue < aValue) { - return 1; - } else { - return 0; - } - } else { - return -1; + var a, b; + + if (!(other instanceof tree.Dimension)) { + return undefined; } + + 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 undefined; + } + } + + return tree.numericCompare(a.value, b.value); }, unify: function () { @@ -220,7 +211,7 @@ tree.Unit.prototype = { }, compare: function (other) { - return this.is(other.toString()) ? 0 : -1; + return this.is(other.toString()) ? 0 : undefined; }, is: function (unitString) { diff --git a/lib/less/tree/keyword.js b/lib/less/tree/keyword.js index 830594d8..4b633b70 100644 --- a/lib/less/tree/keyword.js +++ b/lib/less/tree/keyword.js @@ -8,14 +8,7 @@ tree.Keyword.prototype = { if (this.value === '%') { throw { type: "Syntax", message: "Invalid % without number" }; } output.add(this.value); }, - toCSS: tree.toCSS, - compare: function (other) { - if (other instanceof tree.Keyword) { - return other.value === this.value ? 0 : 1; - } else { - return -1; - } - } + toCSS: tree.toCSS }; tree.True = new(tree.Keyword)('true'); diff --git a/lib/less/tree/quoted.js b/lib/less/tree/quoted.js index b8f63b9c..d20939e1 100644 --- a/lib/less/tree/quoted.js +++ b/lib/less/tree/quoted.js @@ -29,27 +29,13 @@ tree.Quoted.prototype = { }); return new(tree.Quoted)(this.quote + value + this.quote, value, this.escaped, this.index, this.currentFileInfo); }, - compare: function (x) { - if (!x.toCSS) { - return -1; - } - - var left, right; - + compare: function (other) { // when comparing quoted strings allow the quote to differ - if (x.type === "Quoted" && !this.escaped && !x.escaped) { - left = x.value; - right = this.value; + if (other.type === "Quoted" && !this.escaped && !other.escaped) { + return tree.numericCompare(this.value, other.value); } else { - left = this.toCSS(); - right = x.toCSS(); + return other.toCSS && this.toCSS() === other.toCSS() ? 0 : undefined; } - - if (left === right) { - return 0; - } - - return left < right ? -1 : 1; } }; diff --git a/test/css/mixins-guards.css b/test/css/mixins-guards.css index 59b6f932..c10760b9 100644 --- a/test/css/mixins-guards.css +++ b/test/css/mixins-guards.css @@ -79,6 +79,80 @@ content: theme1 is not 'theme2'; content: theme1 is theme1; } +.variouse-types-comparision { + /**/ + content: true is not equal to false; + content: false is not equal to true too; + /**/ + content: 1 is not equal to true; + content: true is not equal to 1 too; + /**/ + content: 2 is equal to 2px; + content: 2px is equal to 2 too; + /**/ + content: 3 is equal to 3; + content: 3 is equal to 3 too; + /**/ + content: 5 is not equal to 4; + content: 4 is not equal to 5 too; + /**/ + content: abc is equal to abc; + content: abc is equal to abc too; + /**/ + content: abc is not equal to "abc"; + content: "abc" is not equal to abc too; + /**/ + content: 'abc' is less than "abd"; + content: "abd" is greater than 'abc' too; + content: 'abc' is not equal to "abd"; + content: "abd" is not equal to 'abc' too; + /**/ + content: 6 is equal to 6; + content: 6 is equal to 6 too; + /**/ + content: 8 is less than 9 too; + content: 9 is greater than 8; + content: 9 is not equal to 8; + content: 8 is not equal to 9 too; + /**/ + content: a is not equal to b; + content: b is not equal to a too; + /**/ + content: 1 2 is not equal to 3; + content: 3 is not equal to 1 2 too; +} +.list-comparision { + /**/ + content: a b c is equal to a b c; + content: a b c is equal to a b c too; + /**/ + content: a b c is not equal to a b d; + content: a b d is not equal to a b c too; + /**/ + content: a, b, c is equal to a, b, c; + content: a, b, c is equal to a, b, c too; + /**/ + content: a, b, c is not equal to a, b, d; + content: a, b, d is not equal to a, b, c too; + /**/ + content: 1 2px 300ms is equal to 1em 2 0.3s; + content: 1em 2 0.3s is equal to 1 2px 300ms too; + /**/ + content: 1 2 3 is not equal to 1, 2, 3; + content: 1, 2, 3 is not equal to 1 2 3 too; + /**/ + content: 1, 2, 3 is equal to 1, 2, 3; + content: 1, 2, 3 is equal to 1, 2, 3 too; + /**/ + content: 1 2 3 1, 2, 3 is equal to 1 2 3 1, 2, 3; + content: 1 2 3 1, 2, 3 is equal to 1 2 3 1, 2, 3 too; + /**/ + content: 1 2 3 1, 2, 3 is not equal to 1, 2, 3 1 2 3; + content: 1, 2, 3 1 2 3 is not equal to 1 2 3 1, 2, 3 too; + /**/ + content: 1 2 3 1, 2, 3 4 is equal to 1 2 3 1, 2, 3 4; + content: 1 2 3 1, 2, 3 4 is equal to 1 2 3 1, 2, 3 4 too; +} #tryNumberPx { catch: all; declare: 4; diff --git a/test/less/mixins-guards.less b/test/less/mixins-guards.less index 5aec2e51..8d1ad0e6 100644 --- a/test/less/mixins-guards.less +++ b/test/less/mixins-guards.less @@ -130,6 +130,49 @@ .stringguard(theme1); } +.generic(@a, @b) {/**/} +.generic(@a, @b) when (@a = @b) {content: @a is equal to @b} +.generic(@a, @b) when (@b = @a) {content: @b is equal to @a too} +.generic(@a, @b) when (@a < @b) {content: @a is less than @b} +.generic(@a, @b) when (@b < @a) {content: @b is less than @a too} +.generic(@a, @b) when (@a > @b) {content: @a is greater than @b} +.generic(@a, @b) when (@b > @a) {content: @b is greater than @a too} +.generic(@a, @b) when not(@a = @b) {content: @a is not equal to @b} +.generic(@a, @b) when not(@b = @a) {content: @b is not equal to @a too} + +.variouse-types-comparision { + .generic(true, false); + .generic(1, true); + .generic(2, 2px); + .generic(3, ~"3"); + .generic(5, ~"4"); + .generic(abc, ~"abc"); + .generic(abc, "abc"); + .generic('abc', "abd"); + .generic(6, e("6")); + .generic(`9`, 8); + .generic(a, b); + .generic(1 2, 3); +} + +.list-comparision { + .generic(a b c, a b c); + .generic(a b c, a b d); + .generic(a, b, c; a, b, c); + .generic(a, b, c; a, b, d); + .generic(1 2px 300ms, 1em 2 .3s); + + @space-list: 1 2 3; + @comma-list: 1, 2, 3; + @compound: @space-list @comma-list; + + .generic(@space-list, @comma-list); + .generic(@comma-list, ~"1, 2, 3"); + .generic(@compound, @space-list @comma-list); + .generic(@compound, @comma-list @space-list); + .generic(@compound 4, ~"1 2 3 1, 2, 3 4"); +} + .mixin(...) { catch:all; }