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.
Files changed (113) hide show
  1. data/.DS_Store +0 -0
  2. data/README.md +3 -0
  3. data/lib/.DS_Store +0 -0
  4. data/lib/node_modules/cssom/.idea/CSSOM.iml +9 -0
  5. data/lib/node_modules/cssom/.idea/dictionaries/nv.xml +3 -0
  6. data/lib/node_modules/cssom/.idea/encodings.xml +5 -0
  7. data/lib/node_modules/cssom/.idea/misc.xml +17 -0
  8. data/lib/node_modules/cssom/.idea/modules.xml +9 -0
  9. data/lib/node_modules/cssom/.idea/projectCodeStyle.xml +82 -0
  10. data/lib/node_modules/cssom/.idea/vcs.xml +8 -0
  11. data/lib/node_modules/cssom/.idea/workspace.xml +467 -0
  12. data/lib/node_modules/cssom/.livereload +19 -0
  13. data/lib/node_modules/cssom/Jakefile +37 -0
  14. data/lib/node_modules/cssom/README.mdown +33 -0
  15. data/lib/node_modules/cssom/Rakefile +23 -0
  16. data/lib/node_modules/cssom/docs/.livereload +19 -0
  17. data/lib/node_modules/cssom/docs/bar.css +3 -0
  18. data/lib/node_modules/cssom/docs/demo.css +0 -0
  19. data/lib/node_modules/cssom/docs/foo.css +4 -0
  20. data/lib/node_modules/cssom/docs/parse.html +170 -0
  21. data/lib/node_modules/cssom/docs/parse2.html +431 -0
  22. data/lib/node_modules/cssom/index.html +100 -0
  23. data/lib/node_modules/cssom/lib/CSSImportRule.js +34 -0
  24. data/lib/node_modules/cssom/lib/CSSMediaRule.js +38 -0
  25. data/lib/node_modules/cssom/lib/CSSOM.js +3 -0
  26. data/lib/node_modules/cssom/lib/CSSRule.js +38 -0
  27. data/lib/node_modules/cssom/lib/CSSStyleDeclaration.js +130 -0
  28. data/lib/node_modules/cssom/lib/CSSStyleRule.js +187 -0
  29. data/lib/node_modules/cssom/lib/CSSStyleSheet.js +85 -0
  30. data/lib/node_modules/cssom/lib/MediaList.js +61 -0
  31. data/lib/node_modules/cssom/lib/StyleSheet.js +15 -0
  32. data/lib/node_modules/cssom/lib/clone.js +69 -0
  33. data/lib/node_modules/cssom/lib/index.js +10 -0
  34. data/lib/node_modules/cssom/lib/parse.js +195 -0
  35. data/lib/node_modules/cssom/media.html +17 -0
  36. data/lib/node_modules/cssom/package.json +30 -0
  37. data/lib/node_modules/cssom/plugins/toHTML.js +32 -0
  38. data/lib/node_modules/cssom/server/index.html +22 -0
  39. data/lib/node_modules/cssom/server/index.js +21 -0
  40. data/lib/node_modules/cssom/shorthands.html +21 -0
  41. data/lib/node_modules/cssom/test/CSSStyleDeclaration.test.js +35 -0
  42. data/lib/node_modules/cssom/test/CSSStyleRule.test.js +12 -0
  43. data/lib/node_modules/cssom/test/CSSStyleSheet.test.js +16 -0
  44. data/lib/node_modules/cssom/test/MediaList.test.js +21 -0
  45. data/lib/node_modules/cssom/test/clone.test.js +38 -0
  46. data/lib/node_modules/cssom/test/fixtures/dummy.css +3 -0
  47. data/lib/node_modules/cssom/test/helper.js +97 -0
  48. data/lib/node_modules/cssom/test/index.html +42 -0
  49. data/lib/node_modules/cssom/test/parse.test.js +346 -0
  50. data/lib/node_modules/cssom/test/vendor/qunit.css +189 -0
  51. data/lib/node_modules/cssom/test/vendor/qunit.js +1341 -0
  52. data/lib/node_modules/growl/History.md +16 -0
  53. data/lib/node_modules/growl/Readme.md +74 -0
  54. data/lib/node_modules/growl/lib/growl.js +82 -0
  55. data/lib/node_modules/growl/package.json +6 -0
  56. data/lib/node_modules/growl/test.js +17 -0
  57. data/lib/stylus/colors.js +156 -0
  58. data/lib/stylus/convert/css.js +130 -0
  59. data/lib/stylus/errors.js +58 -0
  60. data/lib/stylus/functions/image.js +120 -0
  61. data/lib/stylus/functions/index.js +722 -0
  62. data/lib/stylus/functions/index.styl +123 -0
  63. data/lib/stylus/functions/url.js +98 -0
  64. data/lib/stylus/lexer.js +728 -0
  65. data/lib/stylus/middleware.js +223 -0
  66. data/lib/stylus/nodes/arguments.js +65 -0
  67. data/lib/stylus/nodes/binop.js +54 -0
  68. data/lib/stylus/nodes/block.js +99 -0
  69. data/lib/stylus/nodes/boolean.js +103 -0
  70. data/lib/stylus/nodes/call.js +57 -0
  71. data/lib/stylus/nodes/charset.js +42 -0
  72. data/lib/stylus/nodes/comment.js +32 -0
  73. data/lib/stylus/nodes/each.js +56 -0
  74. data/lib/stylus/nodes/expression.js +168 -0
  75. data/lib/stylus/nodes/fontface.js +55 -0
  76. data/lib/stylus/nodes/function.js +104 -0
  77. data/lib/stylus/nodes/group.js +79 -0
  78. data/lib/stylus/nodes/hsla.js +256 -0
  79. data/lib/stylus/nodes/ident.js +127 -0
  80. data/lib/stylus/nodes/if.js +55 -0
  81. data/lib/stylus/nodes/import.js +30 -0
  82. data/lib/stylus/nodes/index.js +52 -0
  83. data/lib/stylus/nodes/jsliteral.js +32 -0
  84. data/lib/stylus/nodes/keyframes.js +78 -0
  85. data/lib/stylus/nodes/literal.js +92 -0
  86. data/lib/stylus/nodes/media.js +42 -0
  87. data/lib/stylus/nodes/node.js +209 -0
  88. data/lib/stylus/nodes/null.js +72 -0
  89. data/lib/stylus/nodes/page.js +43 -0
  90. data/lib/stylus/nodes/params.js +72 -0
  91. data/lib/stylus/nodes/property.js +72 -0
  92. data/lib/stylus/nodes/return.js +44 -0
  93. data/lib/stylus/nodes/rgba.js +335 -0
  94. data/lib/stylus/nodes/root.js +50 -0
  95. data/lib/stylus/nodes/selector.js +57 -0
  96. data/lib/stylus/nodes/string.js +120 -0
  97. data/lib/stylus/nodes/ternary.js +51 -0
  98. data/lib/stylus/nodes/unaryop.js +46 -0
  99. data/lib/stylus/nodes/unit.js +207 -0
  100. data/lib/stylus/parser.js +1514 -0
  101. data/lib/stylus/renderer.js +157 -0
  102. data/lib/stylus/source.rb +7 -0
  103. data/lib/stylus/stack/frame.js +66 -0
  104. data/lib/stylus/stack/index.js +146 -0
  105. data/lib/stylus/stack/scope.js +53 -0
  106. data/lib/stylus/stylus.js +102 -0
  107. data/lib/stylus/token.js +53 -0
  108. data/lib/stylus/utils.js +237 -0
  109. data/lib/stylus/visitor/compiler.js +472 -0
  110. data/lib/stylus/visitor/evaluator.js +1070 -0
  111. data/lib/stylus/visitor/index.js +31 -0
  112. data/stylus-source.gemspec +15 -0
  113. 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
+ });