stylus-source 0.15.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/.DS_Store +0 -0
- data/README.md +3 -0
- data/lib/.DS_Store +0 -0
- data/lib/node_modules/cssom/.idea/CSSOM.iml +9 -0
- data/lib/node_modules/cssom/.idea/dictionaries/nv.xml +3 -0
- data/lib/node_modules/cssom/.idea/encodings.xml +5 -0
- data/lib/node_modules/cssom/.idea/misc.xml +17 -0
- data/lib/node_modules/cssom/.idea/modules.xml +9 -0
- data/lib/node_modules/cssom/.idea/projectCodeStyle.xml +82 -0
- data/lib/node_modules/cssom/.idea/vcs.xml +8 -0
- data/lib/node_modules/cssom/.idea/workspace.xml +467 -0
- data/lib/node_modules/cssom/.livereload +19 -0
- data/lib/node_modules/cssom/Jakefile +37 -0
- data/lib/node_modules/cssom/README.mdown +33 -0
- data/lib/node_modules/cssom/Rakefile +23 -0
- data/lib/node_modules/cssom/docs/.livereload +19 -0
- data/lib/node_modules/cssom/docs/bar.css +3 -0
- data/lib/node_modules/cssom/docs/demo.css +0 -0
- data/lib/node_modules/cssom/docs/foo.css +4 -0
- data/lib/node_modules/cssom/docs/parse.html +170 -0
- data/lib/node_modules/cssom/docs/parse2.html +431 -0
- data/lib/node_modules/cssom/index.html +100 -0
- data/lib/node_modules/cssom/lib/CSSImportRule.js +34 -0
- data/lib/node_modules/cssom/lib/CSSMediaRule.js +38 -0
- data/lib/node_modules/cssom/lib/CSSOM.js +3 -0
- data/lib/node_modules/cssom/lib/CSSRule.js +38 -0
- data/lib/node_modules/cssom/lib/CSSStyleDeclaration.js +130 -0
- data/lib/node_modules/cssom/lib/CSSStyleRule.js +187 -0
- data/lib/node_modules/cssom/lib/CSSStyleSheet.js +85 -0
- data/lib/node_modules/cssom/lib/MediaList.js +61 -0
- data/lib/node_modules/cssom/lib/StyleSheet.js +15 -0
- data/lib/node_modules/cssom/lib/clone.js +69 -0
- data/lib/node_modules/cssom/lib/index.js +10 -0
- data/lib/node_modules/cssom/lib/parse.js +195 -0
- data/lib/node_modules/cssom/media.html +17 -0
- data/lib/node_modules/cssom/package.json +30 -0
- data/lib/node_modules/cssom/plugins/toHTML.js +32 -0
- data/lib/node_modules/cssom/server/index.html +22 -0
- data/lib/node_modules/cssom/server/index.js +21 -0
- data/lib/node_modules/cssom/shorthands.html +21 -0
- data/lib/node_modules/cssom/test/CSSStyleDeclaration.test.js +35 -0
- data/lib/node_modules/cssom/test/CSSStyleRule.test.js +12 -0
- data/lib/node_modules/cssom/test/CSSStyleSheet.test.js +16 -0
- data/lib/node_modules/cssom/test/MediaList.test.js +21 -0
- data/lib/node_modules/cssom/test/clone.test.js +38 -0
- data/lib/node_modules/cssom/test/fixtures/dummy.css +3 -0
- data/lib/node_modules/cssom/test/helper.js +97 -0
- data/lib/node_modules/cssom/test/index.html +42 -0
- data/lib/node_modules/cssom/test/parse.test.js +346 -0
- data/lib/node_modules/cssom/test/vendor/qunit.css +189 -0
- data/lib/node_modules/cssom/test/vendor/qunit.js +1341 -0
- data/lib/node_modules/growl/History.md +16 -0
- data/lib/node_modules/growl/Readme.md +74 -0
- data/lib/node_modules/growl/lib/growl.js +82 -0
- data/lib/node_modules/growl/package.json +6 -0
- data/lib/node_modules/growl/test.js +17 -0
- data/lib/stylus/colors.js +156 -0
- data/lib/stylus/convert/css.js +130 -0
- data/lib/stylus/errors.js +58 -0
- data/lib/stylus/functions/image.js +120 -0
- data/lib/stylus/functions/index.js +722 -0
- data/lib/stylus/functions/index.styl +123 -0
- data/lib/stylus/functions/url.js +98 -0
- data/lib/stylus/lexer.js +728 -0
- data/lib/stylus/middleware.js +223 -0
- data/lib/stylus/nodes/arguments.js +65 -0
- data/lib/stylus/nodes/binop.js +54 -0
- data/lib/stylus/nodes/block.js +99 -0
- data/lib/stylus/nodes/boolean.js +103 -0
- data/lib/stylus/nodes/call.js +57 -0
- data/lib/stylus/nodes/charset.js +42 -0
- data/lib/stylus/nodes/comment.js +32 -0
- data/lib/stylus/nodes/each.js +56 -0
- data/lib/stylus/nodes/expression.js +168 -0
- data/lib/stylus/nodes/fontface.js +55 -0
- data/lib/stylus/nodes/function.js +104 -0
- data/lib/stylus/nodes/group.js +79 -0
- data/lib/stylus/nodes/hsla.js +256 -0
- data/lib/stylus/nodes/ident.js +127 -0
- data/lib/stylus/nodes/if.js +55 -0
- data/lib/stylus/nodes/import.js +30 -0
- data/lib/stylus/nodes/index.js +52 -0
- data/lib/stylus/nodes/jsliteral.js +32 -0
- data/lib/stylus/nodes/keyframes.js +78 -0
- data/lib/stylus/nodes/literal.js +92 -0
- data/lib/stylus/nodes/media.js +42 -0
- data/lib/stylus/nodes/node.js +209 -0
- data/lib/stylus/nodes/null.js +72 -0
- data/lib/stylus/nodes/page.js +43 -0
- data/lib/stylus/nodes/params.js +72 -0
- data/lib/stylus/nodes/property.js +72 -0
- data/lib/stylus/nodes/return.js +44 -0
- data/lib/stylus/nodes/rgba.js +335 -0
- data/lib/stylus/nodes/root.js +50 -0
- data/lib/stylus/nodes/selector.js +57 -0
- data/lib/stylus/nodes/string.js +120 -0
- data/lib/stylus/nodes/ternary.js +51 -0
- data/lib/stylus/nodes/unaryop.js +46 -0
- data/lib/stylus/nodes/unit.js +207 -0
- data/lib/stylus/parser.js +1514 -0
- data/lib/stylus/renderer.js +157 -0
- data/lib/stylus/source.rb +7 -0
- data/lib/stylus/stack/frame.js +66 -0
- data/lib/stylus/stack/index.js +146 -0
- data/lib/stylus/stack/scope.js +53 -0
- data/lib/stylus/stylus.js +102 -0
- data/lib/stylus/token.js +53 -0
- data/lib/stylus/utils.js +237 -0
- data/lib/stylus/visitor/compiler.js +472 -0
- data/lib/stylus/visitor/evaluator.js +1070 -0
- data/lib/stylus/visitor/index.js +31 -0
- data/stylus-source.gemspec +15 -0
- metadata +158 -0
|
@@ -0,0 +1,1070 @@
|
|
|
1
|
+
|
|
2
|
+
/*!
|
|
3
|
+
* Stylus - Evaluator
|
|
4
|
+
* Copyright(c) 2010 LearnBoost <dev@learnboost.com>
|
|
5
|
+
* MIT Licensed
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Module dependencies.
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
var Visitor = require('./')
|
|
13
|
+
, nodes = require('../nodes')
|
|
14
|
+
, Stack = require('../stack')
|
|
15
|
+
, Frame = require('../stack/frame')
|
|
16
|
+
, Scope = require('../stack/scope')
|
|
17
|
+
, utils = require('../utils')
|
|
18
|
+
, bifs = require('../functions')
|
|
19
|
+
, dirname = require('path').dirname
|
|
20
|
+
, join = require('path').join
|
|
21
|
+
, colors = require('../colors')
|
|
22
|
+
, fs = require('fs');
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Initialize a new `Evaluator` with the given `root` Node
|
|
26
|
+
* and the following `options`.
|
|
27
|
+
*
|
|
28
|
+
* Options:
|
|
29
|
+
*
|
|
30
|
+
* - `compress` Compress the css output, defaults to false
|
|
31
|
+
* - `warn` Warn the user of duplicate function definitions etc
|
|
32
|
+
*
|
|
33
|
+
* @param {Node} root
|
|
34
|
+
* @api private
|
|
35
|
+
*/
|
|
36
|
+
|
|
37
|
+
var Evaluator = module.exports = function Evaluator(root, options) {
|
|
38
|
+
options = options || {};
|
|
39
|
+
Visitor.call(this, root);
|
|
40
|
+
this.stack = new Stack;
|
|
41
|
+
this.imports = options.imports || [];
|
|
42
|
+
this.functions = options.functions || {};
|
|
43
|
+
this.globals = options.globals || {};
|
|
44
|
+
this.paths = options.paths || [];
|
|
45
|
+
this.filename = options.filename;
|
|
46
|
+
this.paths.push(dirname(options.filename || '.'));
|
|
47
|
+
this.stack.push(this.global = new Frame(root));
|
|
48
|
+
this.warnings = options.warn;
|
|
49
|
+
this.options = options;
|
|
50
|
+
this.calling = []; // TODO: remove, use stack
|
|
51
|
+
this.importStack = [];
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Inherit from `Visitor.prototype`.
|
|
56
|
+
*/
|
|
57
|
+
|
|
58
|
+
Evaluator.prototype.__proto__ = Visitor.prototype;
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Proxy visit to expose node line numbers.
|
|
62
|
+
*
|
|
63
|
+
* @param {Node} node
|
|
64
|
+
* @return {Node}
|
|
65
|
+
* @api private
|
|
66
|
+
*/
|
|
67
|
+
|
|
68
|
+
var visit = Visitor.prototype.visit;
|
|
69
|
+
Evaluator.prototype.visit = function(node){
|
|
70
|
+
try {
|
|
71
|
+
return visit.call(this, node);
|
|
72
|
+
} catch (err) {
|
|
73
|
+
if (err.filename) throw err;
|
|
74
|
+
err.lineno = node.lineno;
|
|
75
|
+
err.filename = node.filename;
|
|
76
|
+
err.stylusStack = this.stack.toString();
|
|
77
|
+
try {
|
|
78
|
+
err.input = fs.readFileSync(err.filename, 'utf8');
|
|
79
|
+
} catch (err) {
|
|
80
|
+
// ignore
|
|
81
|
+
}
|
|
82
|
+
throw err;
|
|
83
|
+
}
|
|
84
|
+
};
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* Perform evaluation setup:
|
|
88
|
+
*
|
|
89
|
+
* - populate global scope
|
|
90
|
+
* - iterate imports
|
|
91
|
+
*
|
|
92
|
+
* @api private
|
|
93
|
+
*/
|
|
94
|
+
|
|
95
|
+
Evaluator.prototype.setup = function(){
|
|
96
|
+
this.populateGlobalScope();
|
|
97
|
+
this.imports.forEach(function(file){
|
|
98
|
+
var expr = new nodes.Expression;
|
|
99
|
+
expr.push(new nodes.String(file));
|
|
100
|
+
this.visit(new nodes.Import(expr));
|
|
101
|
+
}, this);
|
|
102
|
+
};
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* Populate the global scope with:
|
|
106
|
+
*
|
|
107
|
+
* - css colors
|
|
108
|
+
* - user-defined globals
|
|
109
|
+
*
|
|
110
|
+
* @api private
|
|
111
|
+
*/
|
|
112
|
+
|
|
113
|
+
Evaluator.prototype.populateGlobalScope = function(){
|
|
114
|
+
var scope = this.global.scope;
|
|
115
|
+
|
|
116
|
+
// colors
|
|
117
|
+
Object.keys(colors).forEach(function(name){
|
|
118
|
+
var rgb = colors[name]
|
|
119
|
+
, rgba = new nodes.RGBA(rgb[0], rgb[1], rgb[2], 1)
|
|
120
|
+
, node = new nodes.Ident(name, rgba);
|
|
121
|
+
scope.add(node);
|
|
122
|
+
});
|
|
123
|
+
|
|
124
|
+
// user-defined globals
|
|
125
|
+
var globals = this.globals;
|
|
126
|
+
Object.keys(globals).forEach(function(name){
|
|
127
|
+
scope.add(new nodes.Ident(name, globals[name]));
|
|
128
|
+
});
|
|
129
|
+
};
|
|
130
|
+
|
|
131
|
+
/**
|
|
132
|
+
* Evaluate the tree.
|
|
133
|
+
*
|
|
134
|
+
* @return {Node}
|
|
135
|
+
* @api private
|
|
136
|
+
*/
|
|
137
|
+
|
|
138
|
+
Evaluator.prototype.evaluate = function(){
|
|
139
|
+
this.setup();
|
|
140
|
+
return this.visit(this.root);
|
|
141
|
+
};
|
|
142
|
+
|
|
143
|
+
/**
|
|
144
|
+
* Visit Group.
|
|
145
|
+
*/
|
|
146
|
+
|
|
147
|
+
Evaluator.prototype.visitGroup = function(group){
|
|
148
|
+
var vendors = this.vendors;
|
|
149
|
+
|
|
150
|
+
group.nodes = group.nodes.map(function(selector){
|
|
151
|
+
selector.val = this.interpolate(selector);
|
|
152
|
+
return selector;
|
|
153
|
+
}, this);
|
|
154
|
+
|
|
155
|
+
group.block = this.visit(group.block);
|
|
156
|
+
return group;
|
|
157
|
+
};
|
|
158
|
+
|
|
159
|
+
/**
|
|
160
|
+
* Visit Charset.
|
|
161
|
+
*/
|
|
162
|
+
|
|
163
|
+
Evaluator.prototype.visitCharset = function(charset){
|
|
164
|
+
return charset;
|
|
165
|
+
};
|
|
166
|
+
|
|
167
|
+
/**
|
|
168
|
+
* Visit Return.
|
|
169
|
+
*/
|
|
170
|
+
|
|
171
|
+
Evaluator.prototype.visitReturn = function(ret){
|
|
172
|
+
ret.expr = this.visit(ret.expr);
|
|
173
|
+
throw ret;
|
|
174
|
+
};
|
|
175
|
+
|
|
176
|
+
/**
|
|
177
|
+
* Visit Media.
|
|
178
|
+
*/
|
|
179
|
+
|
|
180
|
+
Evaluator.prototype.visitMedia = function(media){
|
|
181
|
+
media.block = this.visit(media.block);
|
|
182
|
+
return media;
|
|
183
|
+
};
|
|
184
|
+
|
|
185
|
+
/**
|
|
186
|
+
* Visit Keyframes.
|
|
187
|
+
*/
|
|
188
|
+
|
|
189
|
+
Evaluator.prototype.visitKeyframes = function(keyframes){
|
|
190
|
+
if (keyframes.fabricated) return keyframes;
|
|
191
|
+
keyframes.name = this.visit(keyframes.name).first.name;
|
|
192
|
+
|
|
193
|
+
keyframes.frames = keyframes.frames.map(function(frame){
|
|
194
|
+
frame.block = this.visit(frame.block);
|
|
195
|
+
return frame;
|
|
196
|
+
}, this);
|
|
197
|
+
|
|
198
|
+
if ('official' != keyframes.prefix) return keyframes;
|
|
199
|
+
|
|
200
|
+
this.vendors.forEach(function(prefix){
|
|
201
|
+
var node = keyframes.clone();
|
|
202
|
+
node.prefix = prefix;
|
|
203
|
+
node.fabricated = true;
|
|
204
|
+
this.currentBlock.push(node);
|
|
205
|
+
}, this);
|
|
206
|
+
|
|
207
|
+
return nodes.null;
|
|
208
|
+
};
|
|
209
|
+
|
|
210
|
+
/**
|
|
211
|
+
* Visit Function.
|
|
212
|
+
*/
|
|
213
|
+
|
|
214
|
+
Evaluator.prototype.visitFunction = function(fn){
|
|
215
|
+
// check local
|
|
216
|
+
var local = this.stack.currentFrame.scope.lookup(fn.name);
|
|
217
|
+
if (local) this.warn('local ' + local.nodeName + ' "' + fn.name + '" previously defined in this scope');
|
|
218
|
+
|
|
219
|
+
// user-defined
|
|
220
|
+
var user = this.functions[fn.name];
|
|
221
|
+
if (user) this.warn('user-defined function "' + fn.name + '" is already defined');
|
|
222
|
+
|
|
223
|
+
// BIF
|
|
224
|
+
var bif = bifs[fn.name];
|
|
225
|
+
if (bif) this.warn('built-in function "' + fn.name + '" is already defined');
|
|
226
|
+
|
|
227
|
+
return fn;
|
|
228
|
+
};
|
|
229
|
+
|
|
230
|
+
/**
|
|
231
|
+
* Visit Each.
|
|
232
|
+
*/
|
|
233
|
+
|
|
234
|
+
Evaluator.prototype.visitEach = function(each){
|
|
235
|
+
var expr = utils.unwrap(this.visit(utils.unwrap(each.expr)))
|
|
236
|
+
, len = expr.nodes.length
|
|
237
|
+
, val = new nodes.Ident(each.val)
|
|
238
|
+
, key = new nodes.Ident(each.key || '__index__')
|
|
239
|
+
, scope = this.currentScope
|
|
240
|
+
, block = this.currentBlock
|
|
241
|
+
, vals = []
|
|
242
|
+
, body;
|
|
243
|
+
|
|
244
|
+
each.block.scope = false;
|
|
245
|
+
for (var i = 0; i < len; ++i) {
|
|
246
|
+
val.val = expr.nodes[i];
|
|
247
|
+
key.val = new nodes.Unit(i);
|
|
248
|
+
scope.add(val);
|
|
249
|
+
scope.add(key);
|
|
250
|
+
body = this.visit(each.block.clone());
|
|
251
|
+
vals = vals.concat(body.nodes);
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
this.mixin(vals, block);
|
|
255
|
+
return vals[vals.length - 1] || nodes.null;
|
|
256
|
+
};
|
|
257
|
+
|
|
258
|
+
/**
|
|
259
|
+
* Visit Call.
|
|
260
|
+
*/
|
|
261
|
+
|
|
262
|
+
Evaluator.prototype.visitCall = function(call){
|
|
263
|
+
var fn = this.lookup(call.name)
|
|
264
|
+
, ret;
|
|
265
|
+
|
|
266
|
+
// url()
|
|
267
|
+
this.ignoreColors = 'url' == call.name;
|
|
268
|
+
|
|
269
|
+
// Variable function
|
|
270
|
+
if (fn && 'expression' == fn.nodeName) {
|
|
271
|
+
fn = fn.nodes[0];
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
// Not a function? try user-defined or built-ins
|
|
275
|
+
if (fn && 'function' != fn.nodeName) {
|
|
276
|
+
fn = this.lookupFunction(call.name);
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
// Undefined function, render literal css
|
|
280
|
+
if (!fn || fn.nodeName != 'function') {
|
|
281
|
+
var ret = this.literalCall(call);
|
|
282
|
+
this.ignoreColors = false;
|
|
283
|
+
return ret;
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
this.calling.push(call.name);
|
|
287
|
+
|
|
288
|
+
// Massive stack
|
|
289
|
+
if (this.calling.length > 200) {
|
|
290
|
+
throw new RangeError('Maximum call stack size exceeded');
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
// First node in expression
|
|
294
|
+
if ('expression' == fn.nodeName) fn = fn.first;
|
|
295
|
+
|
|
296
|
+
// Evaluate arguments
|
|
297
|
+
var _ = this.return;
|
|
298
|
+
this.return = true;
|
|
299
|
+
var args = this.visit(call.args);
|
|
300
|
+
for (var key in call.args.map) {
|
|
301
|
+
call.args.map[key] = this.visit(call.args.map[key]);
|
|
302
|
+
}
|
|
303
|
+
this.return = _;
|
|
304
|
+
|
|
305
|
+
// Built-in
|
|
306
|
+
if (fn.fn) {
|
|
307
|
+
ret = this.invokeBuiltin(fn.fn, args);
|
|
308
|
+
// User-defined
|
|
309
|
+
} else if ('function' == fn.nodeName) {
|
|
310
|
+
ret = this.invokeFunction(fn, args);
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
this.calling.pop();
|
|
314
|
+
this.ignoreColors = false;
|
|
315
|
+
return ret;
|
|
316
|
+
};
|
|
317
|
+
|
|
318
|
+
/**
|
|
319
|
+
* Visit Ident.
|
|
320
|
+
*/
|
|
321
|
+
|
|
322
|
+
Evaluator.prototype.visitIdent = function(ident){
|
|
323
|
+
var prop;
|
|
324
|
+
// Property lookup
|
|
325
|
+
if (ident.property) {
|
|
326
|
+
if (prop = this.lookupProperty(ident.name)) {
|
|
327
|
+
return this.visit(prop.expr.clone());
|
|
328
|
+
}
|
|
329
|
+
return nodes.null;
|
|
330
|
+
// Lookup
|
|
331
|
+
} else if (ident.val.isNull) {
|
|
332
|
+
var val = this.lookup(ident.name);
|
|
333
|
+
return val ? this.visit(val) : ident;
|
|
334
|
+
// Assign
|
|
335
|
+
} else {
|
|
336
|
+
var _ = this.return;
|
|
337
|
+
this.return = true;
|
|
338
|
+
ident.val = this.visit(ident.val);
|
|
339
|
+
this.return = _;
|
|
340
|
+
this.currentScope.add(ident);
|
|
341
|
+
return ident.val;
|
|
342
|
+
}
|
|
343
|
+
};
|
|
344
|
+
|
|
345
|
+
/**
|
|
346
|
+
* Visit BinOp.
|
|
347
|
+
*/
|
|
348
|
+
|
|
349
|
+
Evaluator.prototype.visitBinOp = function(binop){
|
|
350
|
+
// Special-case "is defined" pseudo binop
|
|
351
|
+
if ('is defined' == binop.op) return this.isDefined(binop.left);
|
|
352
|
+
|
|
353
|
+
var _ = this.return;
|
|
354
|
+
this.return = true;
|
|
355
|
+
// Visit operands
|
|
356
|
+
var op = binop.op
|
|
357
|
+
, left = this.visit(binop.left)
|
|
358
|
+
, right = this.visit(binop.right);
|
|
359
|
+
this.return = _;
|
|
360
|
+
|
|
361
|
+
// HACK: ternary
|
|
362
|
+
var val = binop.val
|
|
363
|
+
? this.visit(binop.val)
|
|
364
|
+
: null;
|
|
365
|
+
|
|
366
|
+
// Operate
|
|
367
|
+
try {
|
|
368
|
+
return this.visit(left.operate(op, right, val));
|
|
369
|
+
} catch (err) {
|
|
370
|
+
// disregard coercion issues in equality
|
|
371
|
+
// checks, and simply return false
|
|
372
|
+
if ('CoercionError' == err.name) {
|
|
373
|
+
switch (op) {
|
|
374
|
+
case '==':
|
|
375
|
+
return nodes.false;
|
|
376
|
+
case '!=':
|
|
377
|
+
return nodes.true;
|
|
378
|
+
}
|
|
379
|
+
}
|
|
380
|
+
throw err;
|
|
381
|
+
}
|
|
382
|
+
};
|
|
383
|
+
|
|
384
|
+
/**
|
|
385
|
+
* Visit UnaryOp.
|
|
386
|
+
*/
|
|
387
|
+
|
|
388
|
+
Evaluator.prototype.visitUnaryOp = function(unary){
|
|
389
|
+
var op = unary.op
|
|
390
|
+
, node = this.visit(unary.expr).first.clone();
|
|
391
|
+
|
|
392
|
+
if ('!' != op) utils.assertType(node, 'unit');
|
|
393
|
+
|
|
394
|
+
switch (op) {
|
|
395
|
+
case '-':
|
|
396
|
+
node.val = -node.val;
|
|
397
|
+
break;
|
|
398
|
+
case '+':
|
|
399
|
+
node.val = +node.val;
|
|
400
|
+
break;
|
|
401
|
+
case '~':
|
|
402
|
+
node.val = ~node.val;
|
|
403
|
+
break;
|
|
404
|
+
case '!':
|
|
405
|
+
return node.toBoolean().negate();
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
return node;
|
|
409
|
+
};
|
|
410
|
+
|
|
411
|
+
/**
|
|
412
|
+
* Visit TernaryOp.
|
|
413
|
+
*/
|
|
414
|
+
|
|
415
|
+
Evaluator.prototype.visitTernary = function(ternary){
|
|
416
|
+
var ok = this.visit(ternary.cond).toBoolean();
|
|
417
|
+
return ok.isTrue
|
|
418
|
+
? this.visit(ternary.trueExpr)
|
|
419
|
+
: this.visit(ternary.falseExpr);
|
|
420
|
+
};
|
|
421
|
+
|
|
422
|
+
/**
|
|
423
|
+
* Visit Expression.
|
|
424
|
+
*/
|
|
425
|
+
|
|
426
|
+
Evaluator.prototype.visitExpression = function(expr){
|
|
427
|
+
for (var i = 0, len = expr.nodes.length; i < len; ++i) {
|
|
428
|
+
expr.nodes[i] = this.visit(expr.nodes[i]);
|
|
429
|
+
}
|
|
430
|
+
return expr;
|
|
431
|
+
};
|
|
432
|
+
|
|
433
|
+
/**
|
|
434
|
+
* Visit Arguments.
|
|
435
|
+
*/
|
|
436
|
+
|
|
437
|
+
Evaluator.prototype.visitArguments = Evaluator.prototype.visitExpression;
|
|
438
|
+
|
|
439
|
+
/**
|
|
440
|
+
* Visit Property.
|
|
441
|
+
*/
|
|
442
|
+
|
|
443
|
+
Evaluator.prototype.visitProperty = function(prop){
|
|
444
|
+
var name = this.interpolate(prop)
|
|
445
|
+
, fn = this.lookup(name)
|
|
446
|
+
, call = fn && 'function' == fn.nodeName
|
|
447
|
+
, literal = ~this.calling.indexOf(name);
|
|
448
|
+
|
|
449
|
+
// Function of the same name
|
|
450
|
+
if (call && !literal && !prop.literal) {
|
|
451
|
+
this.calling.push(name);
|
|
452
|
+
var args = nodes.Arguments.fromExpression(utils.unwrap(prop.expr));
|
|
453
|
+
var ret = this.visit(new nodes.Call(name, args));
|
|
454
|
+
this.calling.pop();
|
|
455
|
+
return ret;
|
|
456
|
+
// Regular property
|
|
457
|
+
} else {
|
|
458
|
+
var _ = this.return;
|
|
459
|
+
this.return = true;
|
|
460
|
+
prop.name = name;
|
|
461
|
+
prop.literal = true;
|
|
462
|
+
this.property = prop;
|
|
463
|
+
prop.expr = this.visit(prop.expr);
|
|
464
|
+
delete this.property;
|
|
465
|
+
this.return = _;
|
|
466
|
+
return prop;
|
|
467
|
+
}
|
|
468
|
+
};
|
|
469
|
+
|
|
470
|
+
/**
|
|
471
|
+
* Visit Root.
|
|
472
|
+
*/
|
|
473
|
+
|
|
474
|
+
Evaluator.prototype.visitRoot = function(block){
|
|
475
|
+
for (var i = 0; i < block.nodes.length; ++i) {
|
|
476
|
+
block.index = this.rootIndex = i;
|
|
477
|
+
block.nodes[i] = this.visit(block.nodes[i]);
|
|
478
|
+
}
|
|
479
|
+
return block;
|
|
480
|
+
};
|
|
481
|
+
|
|
482
|
+
/**
|
|
483
|
+
* Visit Block.
|
|
484
|
+
*/
|
|
485
|
+
|
|
486
|
+
Evaluator.prototype.visitBlock = function(block){
|
|
487
|
+
this.stack.push(new Frame(block));
|
|
488
|
+
for (block.index = 0; block.index < block.nodes.length; ++block.index) {
|
|
489
|
+
try {
|
|
490
|
+
block.nodes[block.index] = this.visit(block.nodes[block.index]);
|
|
491
|
+
} catch (err) {
|
|
492
|
+
if ('return' == err.nodeName) {
|
|
493
|
+
if (this.return) {
|
|
494
|
+
this.stack.pop();
|
|
495
|
+
throw err;
|
|
496
|
+
} else {
|
|
497
|
+
block.nodes[block.index] = err;
|
|
498
|
+
break;
|
|
499
|
+
}
|
|
500
|
+
} else {
|
|
501
|
+
throw err;
|
|
502
|
+
}
|
|
503
|
+
}
|
|
504
|
+
}
|
|
505
|
+
this.stack.pop();
|
|
506
|
+
return block;
|
|
507
|
+
};
|
|
508
|
+
|
|
509
|
+
/**
|
|
510
|
+
* Visit If.
|
|
511
|
+
*/
|
|
512
|
+
|
|
513
|
+
Evaluator.prototype.visitIf = function(node){
|
|
514
|
+
var ret
|
|
515
|
+
, _ = this.return
|
|
516
|
+
, block = this.currentBlock
|
|
517
|
+
, negate = node.negate;
|
|
518
|
+
|
|
519
|
+
this.return = true;
|
|
520
|
+
var ok = this.visit(node.cond).first.toBoolean();
|
|
521
|
+
this.return = _;
|
|
522
|
+
|
|
523
|
+
// Evaluate body
|
|
524
|
+
if (negate) {
|
|
525
|
+
// unless
|
|
526
|
+
if (ok.isFalse) {
|
|
527
|
+
ret = this.visit(node.block);
|
|
528
|
+
}
|
|
529
|
+
} else {
|
|
530
|
+
// if
|
|
531
|
+
if (ok.isTrue) {
|
|
532
|
+
ret = this.visit(node.block);
|
|
533
|
+
// else
|
|
534
|
+
} else if (node.elses.length) {
|
|
535
|
+
var elses = node.elses
|
|
536
|
+
, len = elses.length;
|
|
537
|
+
for (var i = 0; i < len; ++i) {
|
|
538
|
+
// else if
|
|
539
|
+
if (elses[i].cond) {
|
|
540
|
+
if (this.visit(elses[i].cond).first.toBoolean().isTrue) {
|
|
541
|
+
ret = this.visit(elses[i].block);
|
|
542
|
+
break;
|
|
543
|
+
}
|
|
544
|
+
// else
|
|
545
|
+
} else {
|
|
546
|
+
ret = this.visit(elses[i]);
|
|
547
|
+
}
|
|
548
|
+
}
|
|
549
|
+
}
|
|
550
|
+
}
|
|
551
|
+
|
|
552
|
+
// mixin conditional statements within a selector group
|
|
553
|
+
if (ret && !node.postfix && block.node && 'group' == block.node.nodeName) {
|
|
554
|
+
this.mixin(ret.nodes, block);
|
|
555
|
+
return nodes.null;
|
|
556
|
+
}
|
|
557
|
+
|
|
558
|
+
return ret || nodes.null;
|
|
559
|
+
};
|
|
560
|
+
|
|
561
|
+
/**
|
|
562
|
+
* Visit Import.
|
|
563
|
+
*/
|
|
564
|
+
|
|
565
|
+
Evaluator.prototype.visitImport = function(imported){
|
|
566
|
+
var found
|
|
567
|
+
, root = this.root
|
|
568
|
+
, Parser = require('../parser')
|
|
569
|
+
, path = this.visit(imported.path).first;
|
|
570
|
+
|
|
571
|
+
// Enusre string
|
|
572
|
+
if (!path.string) throw new Error('@import string expected');
|
|
573
|
+
var name = path = path.string;
|
|
574
|
+
|
|
575
|
+
// Literal
|
|
576
|
+
if (~path.indexOf('.css')) return imported;
|
|
577
|
+
if (!~path.indexOf('.styl')) path += '.styl';
|
|
578
|
+
|
|
579
|
+
// Lookup
|
|
580
|
+
found = utils.lookup(path, this.paths, this.filename);
|
|
581
|
+
found = found || utils.lookup(join(name, 'index.styl'), this.paths, this.filename);
|
|
582
|
+
|
|
583
|
+
// Expose imports
|
|
584
|
+
imported.path = found;
|
|
585
|
+
imported.dirname = dirname(found);
|
|
586
|
+
this.paths.push(imported.dirname);
|
|
587
|
+
if (this.options._imports) this.options._imports.push(imported);
|
|
588
|
+
|
|
589
|
+
// Throw if import failed
|
|
590
|
+
if (!found) throw new Error('failed to locate @import file ' + path);
|
|
591
|
+
|
|
592
|
+
// Parse the file
|
|
593
|
+
this.importStack.push(found);
|
|
594
|
+
nodes.filename = found;
|
|
595
|
+
|
|
596
|
+
var str = fs.readFileSync(found, 'utf8')
|
|
597
|
+
, block = new nodes.Block
|
|
598
|
+
, parser = new Parser(str, utils.merge({ root: block }, this.options));
|
|
599
|
+
|
|
600
|
+
try {
|
|
601
|
+
block = parser.parse();
|
|
602
|
+
} catch (err) {
|
|
603
|
+
err.filename = found;
|
|
604
|
+
err.lineno = parser.lexer.lineno;
|
|
605
|
+
err.input = str;
|
|
606
|
+
throw err;
|
|
607
|
+
}
|
|
608
|
+
|
|
609
|
+
// Store the modified time
|
|
610
|
+
fs.stat(found, function(err, stat){
|
|
611
|
+
if (err) return;
|
|
612
|
+
imported.mtime = stat.mtime;
|
|
613
|
+
});
|
|
614
|
+
|
|
615
|
+
// Evaluate imported "root"
|
|
616
|
+
block.parent = root;
|
|
617
|
+
block.scope = false;
|
|
618
|
+
var ret = this.visit(block);
|
|
619
|
+
this.paths.pop();
|
|
620
|
+
this.importStack.pop();
|
|
621
|
+
|
|
622
|
+
return ret;
|
|
623
|
+
};
|
|
624
|
+
|
|
625
|
+
/**
|
|
626
|
+
* Invoke `fn` with `args`.
|
|
627
|
+
*
|
|
628
|
+
* @param {Function} fn
|
|
629
|
+
* @param {Array} args
|
|
630
|
+
* @return {Node}
|
|
631
|
+
* @api private
|
|
632
|
+
*/
|
|
633
|
+
|
|
634
|
+
Evaluator.prototype.invokeFunction = function(fn, args){
|
|
635
|
+
var block = new nodes.Block(fn.block.parent);
|
|
636
|
+
fn.block.parent = block;
|
|
637
|
+
|
|
638
|
+
// Clone the function body
|
|
639
|
+
// to prevent mutation of subsequent calls
|
|
640
|
+
// inject argument scope
|
|
641
|
+
var body = fn.block.clone();
|
|
642
|
+
|
|
643
|
+
// mixin block
|
|
644
|
+
var mixinBlock = this.stack.currentFrame.block;
|
|
645
|
+
|
|
646
|
+
// new block scope
|
|
647
|
+
this.stack.push(new Frame(block));
|
|
648
|
+
var scope = this.currentScope;
|
|
649
|
+
|
|
650
|
+
// arguments local
|
|
651
|
+
scope.add(new nodes.Ident('arguments', args));
|
|
652
|
+
|
|
653
|
+
// mixin scope introspection
|
|
654
|
+
scope.add(new nodes.Ident('mixin', this.return
|
|
655
|
+
? nodes.false
|
|
656
|
+
: new nodes.String(mixinBlock.nodeName)));
|
|
657
|
+
|
|
658
|
+
// current property
|
|
659
|
+
if (this.property) {
|
|
660
|
+
var prop = this.propertyExpression(this.property, fn.name);
|
|
661
|
+
scope.add(new nodes.Ident('current-property', prop));
|
|
662
|
+
} else {
|
|
663
|
+
scope.add(new nodes.Ident('current-property', nodes.null));
|
|
664
|
+
}
|
|
665
|
+
|
|
666
|
+
// inject arguments as locals
|
|
667
|
+
var i = 0
|
|
668
|
+
, len = args.nodes.length;
|
|
669
|
+
fn.params.nodes.forEach(function(node){
|
|
670
|
+
// rest param support
|
|
671
|
+
if (node.rest) {
|
|
672
|
+
node.val = new nodes.Expression;
|
|
673
|
+
for (; i < len; ++i) node.val.push(args.nodes[i]);
|
|
674
|
+
node.val.preserve = true;
|
|
675
|
+
// argument default support
|
|
676
|
+
} else {
|
|
677
|
+
var arg = args.map[node.name] || args.nodes[i++];
|
|
678
|
+
node = node.clone();
|
|
679
|
+
if (arg) {
|
|
680
|
+
if (!arg.isEmpty) node.val = arg;
|
|
681
|
+
} else {
|
|
682
|
+
args.push(node.val);
|
|
683
|
+
}
|
|
684
|
+
|
|
685
|
+
// required argument not satisfied
|
|
686
|
+
if (node.val.isNull) {
|
|
687
|
+
throw new Error('argument "' + node + '" required for ' + fn);
|
|
688
|
+
}
|
|
689
|
+
}
|
|
690
|
+
|
|
691
|
+
scope.add(node);
|
|
692
|
+
});
|
|
693
|
+
|
|
694
|
+
// invoke
|
|
695
|
+
return this.invoke(body, true);
|
|
696
|
+
};
|
|
697
|
+
|
|
698
|
+
/**
|
|
699
|
+
* Invoke built-in `fn` with `args`.
|
|
700
|
+
*
|
|
701
|
+
* @param {Function} fn
|
|
702
|
+
* @param {Array} args
|
|
703
|
+
* @return {Node}
|
|
704
|
+
* @api private
|
|
705
|
+
*/
|
|
706
|
+
|
|
707
|
+
Evaluator.prototype.invokeBuiltin = function(fn, args){
|
|
708
|
+
// Map arguments to first node
|
|
709
|
+
// providing a nicer js api for
|
|
710
|
+
// BIFs. Functions may specify that
|
|
711
|
+
// they wish to accept full expressions
|
|
712
|
+
// via .raw
|
|
713
|
+
if (fn.raw) {
|
|
714
|
+
args = args.nodes;
|
|
715
|
+
} else {
|
|
716
|
+
args = utils.params(fn).reduce(function(ret, param){
|
|
717
|
+
var arg = args.map[param] || args.nodes.shift();
|
|
718
|
+
if (arg) ret.push(arg.first);
|
|
719
|
+
return ret;
|
|
720
|
+
}, []);
|
|
721
|
+
}
|
|
722
|
+
|
|
723
|
+
// Invoke the BIF
|
|
724
|
+
var body = fn.apply(this, args);
|
|
725
|
+
|
|
726
|
+
// Always wrapping allows js functions
|
|
727
|
+
// to return several values with a single
|
|
728
|
+
// Expression node
|
|
729
|
+
var expr = new nodes.Expression;
|
|
730
|
+
expr.push(body);
|
|
731
|
+
body = expr;
|
|
732
|
+
|
|
733
|
+
// Invoke
|
|
734
|
+
return this.invoke(body);
|
|
735
|
+
};
|
|
736
|
+
|
|
737
|
+
/**
|
|
738
|
+
* Invoke the given function `body`.
|
|
739
|
+
*
|
|
740
|
+
* @param {Block} body
|
|
741
|
+
* @return {Node}
|
|
742
|
+
* @api private
|
|
743
|
+
*/
|
|
744
|
+
|
|
745
|
+
Evaluator.prototype.invoke = function(body, stack){
|
|
746
|
+
var self = this
|
|
747
|
+
, ret;
|
|
748
|
+
|
|
749
|
+
// Return
|
|
750
|
+
if (this.return) {
|
|
751
|
+
ret = this.eval(body.nodes);
|
|
752
|
+
if (stack) this.stack.pop();
|
|
753
|
+
// Mixin
|
|
754
|
+
} else {
|
|
755
|
+
var targetFrame = this.stack[this.stack.length - 2];
|
|
756
|
+
if (targetFrame) this.targetBlock = targetFrame.block;
|
|
757
|
+
body = this.visit(body);
|
|
758
|
+
if (stack) this.stack.pop();
|
|
759
|
+
this.mixin(body.nodes, this.currentBlock);
|
|
760
|
+
ret = nodes.null;
|
|
761
|
+
}
|
|
762
|
+
|
|
763
|
+
return ret;
|
|
764
|
+
};
|
|
765
|
+
|
|
766
|
+
/**
|
|
767
|
+
* Mixin the given `nodes` to the given `block`.
|
|
768
|
+
*
|
|
769
|
+
* @param {Array} nodes
|
|
770
|
+
* @param {Block} block
|
|
771
|
+
* @api private
|
|
772
|
+
*/
|
|
773
|
+
|
|
774
|
+
Evaluator.prototype.mixin = function(nodes, block){
|
|
775
|
+
var len = block.nodes.length
|
|
776
|
+
, head = block.nodes.slice(0, block.index)
|
|
777
|
+
, tail = block.nodes.slice(block.index + 1, len);
|
|
778
|
+
this._mixin(nodes, head);
|
|
779
|
+
block.nodes = head.concat(tail);
|
|
780
|
+
};
|
|
781
|
+
|
|
782
|
+
/**
|
|
783
|
+
* Mixin the given `nodes` to the `dest` array.
|
|
784
|
+
*
|
|
785
|
+
* @param {Array} nodes
|
|
786
|
+
* @param {Array} dest
|
|
787
|
+
* @api private
|
|
788
|
+
*/
|
|
789
|
+
|
|
790
|
+
Evaluator.prototype._mixin = function(nodes, dest){
|
|
791
|
+
var node
|
|
792
|
+
, len = nodes.length;
|
|
793
|
+
for (var i = 0; i < len; ++i) {
|
|
794
|
+
switch ((node = nodes[i]).nodeName) {
|
|
795
|
+
case 'return':
|
|
796
|
+
return;
|
|
797
|
+
case 'block':
|
|
798
|
+
this._mixin(node.nodes, dest);
|
|
799
|
+
break;
|
|
800
|
+
default:
|
|
801
|
+
dest.push(node);
|
|
802
|
+
}
|
|
803
|
+
}
|
|
804
|
+
};
|
|
805
|
+
|
|
806
|
+
/**
|
|
807
|
+
* Evaluate the given `vals`.
|
|
808
|
+
*
|
|
809
|
+
* @param {Array} vals
|
|
810
|
+
* @return {Node}
|
|
811
|
+
* @api private
|
|
812
|
+
*/
|
|
813
|
+
|
|
814
|
+
Evaluator.prototype.eval = function(vals){
|
|
815
|
+
if (!vals) return nodes.null;
|
|
816
|
+
var len = vals.length
|
|
817
|
+
, node = nodes.null;
|
|
818
|
+
|
|
819
|
+
try {
|
|
820
|
+
for (var i = 0; i < len; ++i) {
|
|
821
|
+
node = vals[i];
|
|
822
|
+
switch (node.nodeName) {
|
|
823
|
+
case 'if':
|
|
824
|
+
if ('block' != node.block.nodeName) {
|
|
825
|
+
node = this.visit(node);
|
|
826
|
+
break;
|
|
827
|
+
}
|
|
828
|
+
case 'each':
|
|
829
|
+
case 'block':
|
|
830
|
+
node = this.visit(node);
|
|
831
|
+
if (node.nodes) node = this.eval(node.nodes);
|
|
832
|
+
break;
|
|
833
|
+
default:
|
|
834
|
+
node = this.visit(node);
|
|
835
|
+
}
|
|
836
|
+
}
|
|
837
|
+
} catch (err) {
|
|
838
|
+
if ('return' == err.nodeName) {
|
|
839
|
+
return err.expr;
|
|
840
|
+
} else {
|
|
841
|
+
throw err;
|
|
842
|
+
}
|
|
843
|
+
}
|
|
844
|
+
|
|
845
|
+
return node;
|
|
846
|
+
};
|
|
847
|
+
|
|
848
|
+
/**
|
|
849
|
+
* Literal function `call`.
|
|
850
|
+
*
|
|
851
|
+
* @param {Call} call
|
|
852
|
+
* @return {call}
|
|
853
|
+
* @api private
|
|
854
|
+
*/
|
|
855
|
+
|
|
856
|
+
Evaluator.prototype.literalCall = function(call){
|
|
857
|
+
call.args = this.visit(call.args);
|
|
858
|
+
return call;
|
|
859
|
+
};
|
|
860
|
+
|
|
861
|
+
/**
|
|
862
|
+
* Lookup property `name` in the in the current block.
|
|
863
|
+
*
|
|
864
|
+
* @param {String} name
|
|
865
|
+
* @return {Property}
|
|
866
|
+
* @api private
|
|
867
|
+
*/
|
|
868
|
+
|
|
869
|
+
Evaluator.prototype.lookupProperty = function(name){
|
|
870
|
+
var nodes = this.closestBlock.nodes
|
|
871
|
+
, target = (this.targetBlock && this.targetBlock.nodes) || []
|
|
872
|
+
, nodes = target.concat(nodes);
|
|
873
|
+
|
|
874
|
+
for (var i = 0, len = nodes.length; i < len; ++i) {
|
|
875
|
+
if ('property' != nodes[i].nodeName) continue;
|
|
876
|
+
if (name == this.interpolate(nodes[i])) {
|
|
877
|
+
return nodes[i].clone();
|
|
878
|
+
}
|
|
879
|
+
}
|
|
880
|
+
};
|
|
881
|
+
|
|
882
|
+
/**
|
|
883
|
+
* Lookup `name`, with support for JavaScript
|
|
884
|
+
* functions, and BIFs.
|
|
885
|
+
*
|
|
886
|
+
* @param {String} name
|
|
887
|
+
* @return {Node}
|
|
888
|
+
* @api private
|
|
889
|
+
*/
|
|
890
|
+
|
|
891
|
+
Evaluator.prototype.lookup = function(name){
|
|
892
|
+
var val;
|
|
893
|
+
if (this.ignoreColors && name in colors) return;
|
|
894
|
+
if (val = this.stack.lookup(name)) {
|
|
895
|
+
return utils.unwrap(val);
|
|
896
|
+
} else {
|
|
897
|
+
return this.lookupFunction(name);
|
|
898
|
+
}
|
|
899
|
+
};
|
|
900
|
+
|
|
901
|
+
/**
|
|
902
|
+
* Map segments in `node` returning a string.
|
|
903
|
+
*
|
|
904
|
+
* @param {Node} node
|
|
905
|
+
* @return {String}
|
|
906
|
+
* @api private
|
|
907
|
+
*/
|
|
908
|
+
|
|
909
|
+
Evaluator.prototype.interpolate = function(node){
|
|
910
|
+
var self = this;
|
|
911
|
+
return node.segments.map(function(node){
|
|
912
|
+
function toString(node) {
|
|
913
|
+
switch (node.nodeName) {
|
|
914
|
+
case 'function':
|
|
915
|
+
case 'ident':
|
|
916
|
+
return node.name;
|
|
917
|
+
case 'literal':
|
|
918
|
+
case 'string':
|
|
919
|
+
case 'unit':
|
|
920
|
+
return node.val;
|
|
921
|
+
case 'expression':
|
|
922
|
+
var _ = self.return;
|
|
923
|
+
self.return = true;
|
|
924
|
+
var ret = toString(self.visit(node).first);
|
|
925
|
+
self.return = _;
|
|
926
|
+
return ret;
|
|
927
|
+
}
|
|
928
|
+
}
|
|
929
|
+
return toString(node);
|
|
930
|
+
}).join('');
|
|
931
|
+
};
|
|
932
|
+
|
|
933
|
+
/**
|
|
934
|
+
* Lookup JavaScript user-defined or built-in function.
|
|
935
|
+
*
|
|
936
|
+
* @param {String} name
|
|
937
|
+
* @return {Function}
|
|
938
|
+
* @api private
|
|
939
|
+
*/
|
|
940
|
+
|
|
941
|
+
Evaluator.prototype.lookupFunction = function(name){
|
|
942
|
+
var fn = this.functions[name] || bifs[name];
|
|
943
|
+
if (fn) return new nodes.Function(name, fn);
|
|
944
|
+
};
|
|
945
|
+
|
|
946
|
+
/**
|
|
947
|
+
* Check if the given `node` is an ident, and if it is defined.
|
|
948
|
+
*
|
|
949
|
+
* @param {Node} node
|
|
950
|
+
* @return {Boolean}
|
|
951
|
+
* @api private
|
|
952
|
+
*/
|
|
953
|
+
|
|
954
|
+
Evaluator.prototype.isDefined = function(node){
|
|
955
|
+
if ('ident' == node.nodeName) {
|
|
956
|
+
return nodes.Boolean(this.lookup(node.name));
|
|
957
|
+
} else {
|
|
958
|
+
throw new Error('invalid "is defined" check on non-variable ' + node);
|
|
959
|
+
}
|
|
960
|
+
};
|
|
961
|
+
|
|
962
|
+
/**
|
|
963
|
+
* Return `Expression` based on the given `prop`,
|
|
964
|
+
* replacing cyclic calls to the given function `name`
|
|
965
|
+
* with "__CALL__".
|
|
966
|
+
*
|
|
967
|
+
* @param {Property} prop
|
|
968
|
+
* @param {String} name
|
|
969
|
+
* @return {Expression}
|
|
970
|
+
* @api private
|
|
971
|
+
*/
|
|
972
|
+
|
|
973
|
+
Evaluator.prototype.propertyExpression = function(prop, name){
|
|
974
|
+
var expr = new nodes.Expression
|
|
975
|
+
, val = prop.expr.clone();
|
|
976
|
+
|
|
977
|
+
// name
|
|
978
|
+
expr.push(new nodes.String(prop.name));
|
|
979
|
+
|
|
980
|
+
// replace cyclic call with __CALL__
|
|
981
|
+
val.nodes = val.nodes.map(function(node){
|
|
982
|
+
if ('call' == node.nodeName && name == node.name) {
|
|
983
|
+
return new nodes.Literal('__CALL__');
|
|
984
|
+
}
|
|
985
|
+
return node;
|
|
986
|
+
});
|
|
987
|
+
|
|
988
|
+
expr.push(val);
|
|
989
|
+
return expr;
|
|
990
|
+
};
|
|
991
|
+
|
|
992
|
+
/**
|
|
993
|
+
* Warn with the given `msg`.
|
|
994
|
+
*
|
|
995
|
+
* @param {String} msg
|
|
996
|
+
* @api private
|
|
997
|
+
*/
|
|
998
|
+
|
|
999
|
+
Evaluator.prototype.warn = function(msg){
|
|
1000
|
+
if (!this.warnings) return;
|
|
1001
|
+
console.warn('\033[33mWarning:\033[0m ' + msg);
|
|
1002
|
+
};
|
|
1003
|
+
|
|
1004
|
+
/**
|
|
1005
|
+
* Return the current `Block`.
|
|
1006
|
+
*
|
|
1007
|
+
* @return {Block}
|
|
1008
|
+
* @api private
|
|
1009
|
+
*/
|
|
1010
|
+
|
|
1011
|
+
Evaluator.prototype.__defineGetter__('currentBlock', function(){
|
|
1012
|
+
return this.stack.currentFrame.block;
|
|
1013
|
+
});
|
|
1014
|
+
|
|
1015
|
+
/**
|
|
1016
|
+
* Return the closest mixin-able `Block`.
|
|
1017
|
+
*
|
|
1018
|
+
* @return {Block}
|
|
1019
|
+
* @api private
|
|
1020
|
+
*/
|
|
1021
|
+
|
|
1022
|
+
Evaluator.prototype.__defineGetter__('closestBlock', function(){
|
|
1023
|
+
var i = this.stack.length
|
|
1024
|
+
, block;
|
|
1025
|
+
while (i--) {
|
|
1026
|
+
block = this.stack[i].block;
|
|
1027
|
+
if (block.node) {
|
|
1028
|
+
switch (block.node.nodeName) {
|
|
1029
|
+
case 'group':
|
|
1030
|
+
case 'function':
|
|
1031
|
+
return block;
|
|
1032
|
+
}
|
|
1033
|
+
}
|
|
1034
|
+
}
|
|
1035
|
+
});
|
|
1036
|
+
|
|
1037
|
+
/**
|
|
1038
|
+
* Return an array of vendor names.
|
|
1039
|
+
*
|
|
1040
|
+
* @return {Array}
|
|
1041
|
+
* @api private
|
|
1042
|
+
*/
|
|
1043
|
+
|
|
1044
|
+
Evaluator.prototype.__defineGetter__('vendors', function(){
|
|
1045
|
+
return this.lookup('vendors').nodes.map(function(node){
|
|
1046
|
+
return node.string;
|
|
1047
|
+
});
|
|
1048
|
+
});
|
|
1049
|
+
|
|
1050
|
+
/**
|
|
1051
|
+
* Return the current frame `Scope`.
|
|
1052
|
+
*
|
|
1053
|
+
* @return {Scope}
|
|
1054
|
+
* @api private
|
|
1055
|
+
*/
|
|
1056
|
+
|
|
1057
|
+
Evaluator.prototype.__defineGetter__('currentScope', function(){
|
|
1058
|
+
return this.stack.currentFrame.scope;
|
|
1059
|
+
});
|
|
1060
|
+
|
|
1061
|
+
/**
|
|
1062
|
+
* Return the current `Frame`.
|
|
1063
|
+
*
|
|
1064
|
+
* @return {Frame}
|
|
1065
|
+
* @api private
|
|
1066
|
+
*/
|
|
1067
|
+
|
|
1068
|
+
Evaluator.prototype.__defineGetter__('currentFrame', function(){
|
|
1069
|
+
return this.stack.currentFrame;
|
|
1070
|
+
});
|