stylus-source 0.15.4

Sign up to get free protection for your applications and to get access to all the features.
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
+ });