From 321920a50de004ef88c3501bb0b97c00da8d4211 Mon Sep 17 00:00:00 2001 From: Alexis Sellier Date: Thu, 15 Dec 2011 23:38:53 +0100 Subject: [PATCH] mixin guards Allows functional-style guard expressions: .mixin (@a) ? @a > 0 {...} --- lib/less/index.js | 12 ++++++------ lib/less/parser.js | 21 +++++++++++++++++++-- lib/less/tree/condition.js | 28 ++++++++++++++++++++++++++++ lib/less/tree/dimension.js | 13 +++++++++++++ lib/less/tree/expression.js | 2 +- lib/less/tree/mixin.js | 17 +++++++++++++---- 6 files changed, 80 insertions(+), 13 deletions(-) create mode 100644 lib/less/tree/condition.js diff --git a/lib/less/index.js b/lib/less/index.js index 7d4c76fe..e75de587 100644 --- a/lib/less/index.js +++ b/lib/less/index.js @@ -73,12 +73,12 @@ var less = { } }; -['color', 'directive', 'operation', 'dimension', - 'keyword', 'variable', 'ruleset', 'element', - 'selector', 'quoted', 'expression', 'rule', - 'call', 'url', 'alpha', 'import', - 'mixin', 'comment', 'anonymous', 'value', - 'javascript', 'assignment' +['color', 'directive', 'operation', 'dimension', + 'keyword', 'variable', 'ruleset', 'element', + 'selector', 'quoted', 'expression', 'rule', + 'call', 'url', 'alpha', 'import', + 'mixin', 'comment', 'anonymous', 'value', + 'javascript', 'assignment', 'condition' ].forEach(function (n) { require('./tree/' + n); }); diff --git a/lib/less/parser.js b/lib/less/parser.js index 18247506..231f3a55 100644 --- a/lib/less/parser.js +++ b/lib/less/parser.js @@ -733,7 +733,7 @@ less.Parser = function Parser(env) { // the `{...}` block. // definition: function () { - var name, params = [], match, ruleset, param, value; + var name, params = [], match, ruleset, param, value, cond, memo; if ((input.charAt(i) !== '.' && input.charAt(i) !== '#') || peek(/^[^{]*(;|})/)) return; @@ -761,10 +761,17 @@ less.Parser = function Parser(env) { } if (! $(')')) throw new(Error)("Expected )"); + memo = i; + + if ($('?')) { // Guard + cond = $(this.condition); + if (! cond) { i = memo } + } + ruleset = $(this.block); if (ruleset) { - return new(tree.mixin.Definition)(name, params, ruleset); + return new(tree.mixin.Definition)(name, params, ruleset, cond); } } } @@ -1080,6 +1087,16 @@ less.Parser = function Parser(env) { return operation || m; } }, + condition: function () { + var a, op, index = i; + if (a = $(this.addition)) { + if (op = $(/^[<=>]/)) { + if (b = $(this.addition)) { + return new(tree.Condition)(op, a, b, index); + } + } + } + }, // // An operand is anything that can be part of an operation, diff --git a/lib/less/tree/condition.js b/lib/less/tree/condition.js new file mode 100644 index 00000000..e26feacb --- /dev/null +++ b/lib/less/tree/condition.js @@ -0,0 +1,28 @@ +(function (tree) { + +tree.Condition = function (op, l, r, i) { + this.op = op.trim(); + this.lvalue = l; + this.rvalue = r; + this.index = i; +}; +tree.Condition.prototype.eval = function (env) { + var a = this.lvalue.eval(env), + b = this.rvalue.eval(env); + + var i = this.index; + + if (a.compare) { + switch (a.compare(b)) { + case -1: return this.op === '<'; + case 0: return this.op === '='; + case 1: return this.op === '>'; + } + } else { + throw { type: "Type", + message: "Unable to perform comparison", + index: i }; + } +}; + +})(require('../tree')); diff --git a/lib/less/tree/dimension.js b/lib/less/tree/dimension.js index 33560d76..f897f0aa 100644 --- a/lib/less/tree/dimension.js +++ b/lib/less/tree/dimension.js @@ -28,6 +28,19 @@ tree.Dimension.prototype = { return new(tree.Dimension) (tree.operate(op, this.value, other.value), this.unit || other.unit); + }, + + // TODO: Perform unit conversion before comparing + compare: function (other) { + if (other instanceof tree.Dimension) { + if (other.value > this.value) { + return -1; + } else if (other.value < this.value) { + return 1; + } else { + return 0; + } + } } }; diff --git a/lib/less/tree/expression.js b/lib/less/tree/expression.js index 405a2ac6..fbfa9c5b 100644 --- a/lib/less/tree/expression.js +++ b/lib/less/tree/expression.js @@ -15,7 +15,7 @@ tree.Expression.prototype = { }, toCSS: function (env) { return this.value.map(function (e) { - return e.toCSS(env); + return e.toCSS ? e.toCSS(env) : ''; }).join(' '); } }; diff --git a/lib/less/tree/mixin.js b/lib/less/tree/mixin.js index 86f8a189..cddfcd7b 100644 --- a/lib/less/tree/mixin.js +++ b/lib/less/tree/mixin.js @@ -41,10 +41,11 @@ tree.mixin.Call.prototype = { } }; -tree.mixin.Definition = function (name, params, rules) { +tree.mixin.Definition = function (name, params, rules, condition) { this.name = name; this.selectors = [new(tree.Selector)([new(tree.Element)(null, name)])]; this.params = params; + this.condition = condition; this.arity = params.length; this.rules = rules; this._lookups = {}; @@ -62,8 +63,8 @@ tree.mixin.Definition.prototype = { find: function () { return this.parent.find.apply(this, arguments) }, rulesets: function () { return this.parent.rulesets.apply(this) }, - eval: function (env, args) { - var frame = new(tree.Ruleset)(null, []), context, _arguments = []; + evalParams: function (env, args) { + var frame = new(tree.Ruleset)(null, []); for (var i = 0, val; i < this.params.length; i++) { if (this.params[i].name) { @@ -75,6 +76,11 @@ tree.mixin.Definition.prototype = { } } } + return frame; + }, + eval: function (env, args) { + var frame = this.evalParams(env, args), context, _arguments = []; + for (var i = 0; i < Math.max(this.params.length, args && args.length); i++) { _arguments.push(args[i] || this.params[i].value); } @@ -85,10 +91,13 @@ tree.mixin.Definition.prototype = { }); }, match: function (args, env) { - var argsLength = (args && args.length) || 0, len; + var argsLength = (args && args.length) || 0, len, frame; if (argsLength < this.required) { return false } if ((this.required > 0) && (argsLength > this.params.length)) { return false } + if (this.condition && !this.condition.eval({ + frames: [this.evalParams(env, args)] + })) { return false } len = Math.min(argsLength, this.arity);