haml-more 0.4.0.a
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/README.md +79 -0
- data/lib/haml/more/coffee_script.rb +137 -0
- data/lib/haml/more/content_for.rb +25 -0
- data/lib/haml/more.rb +45 -0
- data/lib/haml-more.rb +1 -0
- data/lib/sass/more.rb +16 -0
- data/lib/sass-more.rb +1 -0
- data/spec/sass/more_spec.rb +21 -0
- data/vendor/coffee-script/Cakefile +57 -0
- data/vendor/coffee-script/LICENSE +22 -0
- data/vendor/coffee-script/README +41 -0
- data/vendor/coffee-script/Rakefile +20 -0
- data/vendor/coffee-script/bin/cake +7 -0
- data/vendor/coffee-script/bin/coffee +7 -0
- data/vendor/coffee-script/documentation/coffee/aliases.coffee +9 -0
- data/vendor/coffee-script/documentation/coffee/arguments.coffee +4 -0
- data/vendor/coffee-script/documentation/coffee/array_comprehensions.coffee +7 -0
- data/vendor/coffee-script/documentation/coffee/assignment.coffee +2 -0
- data/vendor/coffee-script/documentation/coffee/cake_tasks.coffee +5 -0
- data/vendor/coffee-script/documentation/coffee/comparisons.coffee +5 -0
- data/vendor/coffee-script/documentation/coffee/conditionals.coffee +9 -0
- data/vendor/coffee-script/documentation/coffee/embedded.coffee +5 -0
- data/vendor/coffee-script/documentation/coffee/existence.coffee +8 -0
- data/vendor/coffee-script/documentation/coffee/expressions.coffee +9 -0
- data/vendor/coffee-script/documentation/coffee/expressions_assignment.coffee +1 -0
- data/vendor/coffee-script/documentation/coffee/expressions_comprehension.coffee +3 -0
- data/vendor/coffee-script/documentation/coffee/expressions_try.coffee +6 -0
- data/vendor/coffee-script/documentation/coffee/fat_arrow.coffee +6 -0
- data/vendor/coffee-script/documentation/coffee/functions.coffee +2 -0
- data/vendor/coffee-script/documentation/coffee/heredocs.coffee +5 -0
- data/vendor/coffee-script/documentation/coffee/multiple_return_values.coffee +5 -0
- data/vendor/coffee-script/documentation/coffee/object_comprehensions.coffee +4 -0
- data/vendor/coffee-script/documentation/coffee/object_extraction.coffee +13 -0
- data/vendor/coffee-script/documentation/coffee/objects_and_arrays.coffee +13 -0
- data/vendor/coffee-script/documentation/coffee/overview.coffee +29 -0
- data/vendor/coffee-script/documentation/coffee/parallel_assignment.coffee +4 -0
- data/vendor/coffee-script/documentation/coffee/range_comprehensions.coffee +6 -0
- data/vendor/coffee-script/documentation/coffee/scope.coffee +5 -0
- data/vendor/coffee-script/documentation/coffee/slices.coffee +6 -0
- data/vendor/coffee-script/documentation/coffee/soaks.coffee +1 -0
- data/vendor/coffee-script/documentation/coffee/splats.coffee +25 -0
- data/vendor/coffee-script/documentation/coffee/splices.coffee +5 -0
- data/vendor/coffee-script/documentation/coffee/strings.coffee +8 -0
- data/vendor/coffee-script/documentation/coffee/super.coffee +34 -0
- data/vendor/coffee-script/documentation/coffee/switch.coffee +10 -0
- data/vendor/coffee-script/documentation/coffee/try.coffee +7 -0
- data/vendor/coffee-script/documentation/coffee/while.coffee +10 -0
- data/vendor/coffee-script/documentation/css/docs.css +213 -0
- data/vendor/coffee-script/documentation/css/idle.css +63 -0
- data/vendor/coffee-script/documentation/css/logo.png +0 -0
- data/vendor/coffee-script/documentation/index.html.erb +967 -0
- data/vendor/coffee-script/documentation/js/aliases.js +14 -0
- data/vendor/coffee-script/documentation/js/arguments.js +8 -0
- data/vendor/coffee-script/documentation/js/array_comprehensions.js +26 -0
- data/vendor/coffee-script/documentation/js/assignment.js +5 -0
- data/vendor/coffee-script/documentation/js/cake_tasks.js +14 -0
- data/vendor/coffee-script/documentation/js/comparisons.js +5 -0
- data/vendor/coffee-script/documentation/js/conditionals.js +12 -0
- data/vendor/coffee-script/documentation/js/embedded.js +6 -0
- data/vendor/coffee-script/documentation/js/existence.js +7 -0
- data/vendor/coffee-script/documentation/js/expressions.js +13 -0
- data/vendor/coffee-script/documentation/js/expressions_assignment.js +4 -0
- data/vendor/coffee-script/documentation/js/expressions_comprehension.js +12 -0
- data/vendor/coffee-script/documentation/js/expressions_try.js +9 -0
- data/vendor/coffee-script/documentation/js/fat_arrow.js +15 -0
- data/vendor/coffee-script/documentation/js/functions.js +9 -0
- data/vendor/coffee-script/documentation/js/heredocs.js +4 -0
- data/vendor/coffee-script/documentation/js/intro.js +7 -0
- data/vendor/coffee-script/documentation/js/multiple_return_values.js +11 -0
- data/vendor/coffee-script/documentation/js/object_comprehensions.js +17 -0
- data/vendor/coffee-script/documentation/js/object_extraction.js +17 -0
- data/vendor/coffee-script/documentation/js/objects_and_arrays.js +10 -0
- data/vendor/coffee-script/documentation/js/overview.js +43 -0
- data/vendor/coffee-script/documentation/js/parallel_assignment.js +8 -0
- data/vendor/coffee-script/documentation/js/punctuation.js +8 -0
- data/vendor/coffee-script/documentation/js/range_comprehensions.js +21 -0
- data/vendor/coffee-script/documentation/js/scope.js +10 -0
- data/vendor/coffee-script/documentation/js/slices.js +6 -0
- data/vendor/coffee-script/documentation/js/soaks.js +4 -0
- data/vendor/coffee-script/documentation/js/splats.js +16 -0
- data/vendor/coffee-script/documentation/js/splices.js +5 -0
- data/vendor/coffee-script/documentation/js/strings.js +9 -0
- data/vendor/coffee-script/documentation/js/super.js +37 -0
- data/vendor/coffee-script/documentation/js/switch.js +18 -0
- data/vendor/coffee-script/documentation/js/try.js +10 -0
- data/vendor/coffee-script/documentation/js/while.js +22 -0
- data/vendor/coffee-script/documentation/underscore.html +627 -0
- data/vendor/coffee-script/examples/beautiful_code/binary_search.coffee +16 -0
- data/vendor/coffee-script/examples/beautiful_code/quicksort_runtime.coffee +13 -0
- data/vendor/coffee-script/examples/beautiful_code/regular_expression_matcher.coffee +34 -0
- data/vendor/coffee-script/examples/blocks.coffee +57 -0
- data/vendor/coffee-script/examples/code.coffee +173 -0
- data/vendor/coffee-script/examples/computer_science/README +4 -0
- data/vendor/coffee-script/examples/computer_science/binary_search.coffee +25 -0
- data/vendor/coffee-script/examples/computer_science/bubble_sort.coffee +11 -0
- data/vendor/coffee-script/examples/computer_science/linked_list.coffee +106 -0
- data/vendor/coffee-script/examples/computer_science/luhn_algorithm.coffee +36 -0
- data/vendor/coffee-script/examples/computer_science/merge_sort.coffee +19 -0
- data/vendor/coffee-script/examples/computer_science/selection_sort.coffee +23 -0
- data/vendor/coffee-script/examples/poignant.coffee +186 -0
- data/vendor/coffee-script/examples/potion.coffee +205 -0
- data/vendor/coffee-script/examples/underscore.coffee +603 -0
- data/vendor/coffee-script/examples/web_server.coffee +12 -0
- data/vendor/coffee-script/extras/CoffeeScript.tmbundle/Preferences/CoffeeScript.tmPreferences +24 -0
- data/vendor/coffee-script/extras/CoffeeScript.tmbundle/Syntaxes/CoffeeScript.tmLanguage +361 -0
- data/vendor/coffee-script/extras/CoffeeScript.tmbundle/info.plist +10 -0
- data/vendor/coffee-script/extras/EXTRAS +20 -0
- data/vendor/coffee-script/extras/coffee.vim +117 -0
- data/vendor/coffee-script/index.html +1847 -0
- data/vendor/coffee-script/lib/bin/cake +7 -0
- data/vendor/coffee-script/lib/bin/coffee +7 -0
- data/vendor/coffee-script/lib/cake.js +80 -0
- data/vendor/coffee-script/lib/coffee-script.js +61 -0
- data/vendor/coffee-script/lib/command_line.js +201 -0
- data/vendor/coffee-script/lib/grammar.js +564 -0
- data/vendor/coffee-script/lib/lexer.js +405 -0
- data/vendor/coffee-script/lib/narwhal.js +44 -0
- data/vendor/coffee-script/lib/nodes.js +1328 -0
- data/vendor/coffee-script/lib/optparse.js +117 -0
- data/vendor/coffee-script/lib/parser.js +536 -0
- data/vendor/coffee-script/lib/repl.js +32 -0
- data/vendor/coffee-script/lib/rewriter.js +383 -0
- data/vendor/coffee-script/lib/scope.js +114 -0
- data/vendor/coffee-script/package.json +7 -0
- data/vendor/coffee-script/src/cake.coffee +45 -0
- data/vendor/coffee-script/src/coffee-script.coffee +45 -0
- data/vendor/coffee-script/src/command_line.coffee +130 -0
- data/vendor/coffee-script/src/grammar.coffee +456 -0
- data/vendor/coffee-script/src/lexer.coffee +327 -0
- data/vendor/coffee-script/src/narwhal.coffee +42 -0
- data/vendor/coffee-script/src/nodes.coffee +1045 -0
- data/vendor/coffee-script/src/optparse.coffee +79 -0
- data/vendor/coffee-script/src/repl.coffee +23 -0
- data/vendor/coffee-script/src/rewriter.coffee +253 -0
- data/vendor/coffee-script/src/scope.coffee +75 -0
- data/vendor/coffee-script/test/test_arguments.coffee +34 -0
- data/vendor/coffee-script/test/test_array_comprehension.coffee +42 -0
- data/vendor/coffee-script/test/test_assignment.coffee +26 -0
- data/vendor/coffee-script/test/test_blocks.coffee +4 -0
- data/vendor/coffee-script/test/test_calling_super.coffee +42 -0
- data/vendor/coffee-script/test/test_chained_calls.coffee +25 -0
- data/vendor/coffee-script/test/test_destructuring_assignment.coffee +62 -0
- data/vendor/coffee-script/test/test_everything.coffee +29 -0
- data/vendor/coffee-script/test/test_exceptions.coffee +2 -0
- data/vendor/coffee-script/test/test_existence.coffee +81 -0
- data/vendor/coffee-script/test/test_expressions.coffee +30 -0
- data/vendor/coffee-script/test/test_fancy_if_statement.coffee +26 -0
- data/vendor/coffee-script/test/test_functions.coffee +80 -0
- data/vendor/coffee-script/test/test_funky_comments.coffee +25 -0
- data/vendor/coffee-script/test/test_heredocs.coffee +46 -0
- data/vendor/coffee-script/test/test_lexical_scope.coffee +10 -0
- data/vendor/coffee-script/test/test_literals.coffee +56 -0
- data/vendor/coffee-script/test/test_nested_comprehensions.coffee +11 -0
- data/vendor/coffee-script/test/test_newline_escaping.coffee +6 -0
- data/vendor/coffee-script/test/test_operations.coffee +18 -0
- data/vendor/coffee-script/test/test_range_comprehension.coffee +20 -0
- data/vendor/coffee-script/test/test_ranges_and_slices.coffee +16 -0
- data/vendor/coffee-script/test/test_splats.coffee +47 -0
- data/vendor/coffee-script/test/test_splices.coffee +5 -0
- data/vendor/coffee-script/test/test_switch.coffee +64 -0
- data/vendor/coffee-script/test/test_while.coffee +30 -0
- data/vendor/coffee-script/vendor/jison/Jakefile +31 -0
- data/vendor/coffee-script/vendor/jison/README.md +347 -0
- data/vendor/coffee-script/vendor/jison/bin/jison +3 -0
- data/vendor/coffee-script/vendor/jison/bin/json2jison +3 -0
- data/vendor/coffee-script/vendor/jison/examples/ansic.jison +415 -0
- data/vendor/coffee-script/vendor/jison/examples/basic.json +8 -0
- data/vendor/coffee-script/vendor/jison/examples/basic2.json +9 -0
- data/vendor/coffee-script/vendor/jison/examples/basic2_lex.json +16 -0
- data/vendor/coffee-script/vendor/jison/examples/basic_lex.json +15 -0
- data/vendor/coffee-script/vendor/jison/examples/calculator.jison +38 -0
- data/vendor/coffee-script/vendor/jison/examples/calculator.jisonlex +14 -0
- data/vendor/coffee-script/vendor/jison/examples/calculator.json +42 -0
- data/vendor/coffee-script/vendor/jison/examples/classy.json +105 -0
- data/vendor/coffee-script/vendor/jison/examples/classy_ast.json +126 -0
- data/vendor/coffee-script/vendor/jison/examples/dism.json +25 -0
- data/vendor/coffee-script/vendor/jison/examples/dism_lr0.json +26 -0
- data/vendor/coffee-script/vendor/jison/examples/json.js +80 -0
- data/vendor/coffee-script/vendor/jison/examples/json_ast.js +83 -0
- data/vendor/coffee-script/vendor/jison/examples/precedence.json +26 -0
- data/vendor/coffee-script/vendor/jison/examples/reduce_conflict.json +13 -0
- data/vendor/coffee-script/vendor/jison/lib/jison/bnf.js +43 -0
- data/vendor/coffee-script/vendor/jison/lib/jison/jisonlex.js +18 -0
- data/vendor/coffee-script/vendor/jison/lib/jison/json2jison.js +146 -0
- data/vendor/coffee-script/vendor/jison/lib/jison/lexer.js +224 -0
- data/vendor/coffee-script/vendor/jison/lib/jison/util/bnf-parser.js +383 -0
- data/vendor/coffee-script/vendor/jison/lib/jison/util/lex-parser.js +407 -0
- data/vendor/coffee-script/vendor/jison/lib/jison/util/set.js +94 -0
- data/vendor/coffee-script/vendor/jison/lib/jison/util/typal.js +90 -0
- data/vendor/coffee-script/vendor/jison/lib/jison.js +1414 -0
- data/vendor/coffee-script/vendor/jison/package.json +14 -0
- data/vendor/coffee-script/vendor/jison/src/bnf.jison +110 -0
- data/vendor/coffee-script/vendor/jison/src/bnf.jisonlex +25 -0
- data/vendor/coffee-script/vendor/jison/src/bnf.lex.json +24 -0
- data/vendor/coffee-script/vendor/jison/src/jisonlex.jison +129 -0
- data/vendor/coffee-script/vendor/jison/src/jisonlex.jisonlex +31 -0
- data/vendor/coffee-script/vendor/jison/src/jisonlex.lex.json +30 -0
- data/vendor/coffee-script/vendor/jison/tests/all-tests.js +8 -0
- data/vendor/coffee-script/vendor/jison/tests/grammar/bnf.js +91 -0
- data/vendor/coffee-script/vendor/jison/tests/grammar/bnf_parse.js +65 -0
- data/vendor/coffee-script/vendor/jison/tests/grammar/grammar-tests.js +10 -0
- data/vendor/coffee-script/vendor/jison/tests/grammar/json2jison.js +24 -0
- data/vendor/coffee-script/vendor/jison/tests/grammar/lex/ansic.jisonlex +115 -0
- data/vendor/coffee-script/vendor/jison/tests/grammar/lex/bnf.jisonlex +25 -0
- data/vendor/coffee-script/vendor/jison/tests/grammar/lex/bnf.lex.json +24 -0
- data/vendor/coffee-script/vendor/jison/tests/grammar/lex/lex_grammar.jisonlex +31 -0
- data/vendor/coffee-script/vendor/jison/tests/grammar/lex/lex_grammar.lex.json +30 -0
- data/vendor/coffee-script/vendor/jison/tests/grammar/lex.jison +119 -0
- data/vendor/coffee-script/vendor/jison/tests/grammar/lex.js +58 -0
- data/vendor/coffee-script/vendor/jison/tests/grammar/lex_parse.js +117 -0
- data/vendor/coffee-script/vendor/jison/tests/lexer/lexer-tests.js +6 -0
- data/vendor/coffee-script/vendor/jison/tests/lexer/regexplexer.js +417 -0
- data/vendor/coffee-script/vendor/jison/tests/parser/actions.js +311 -0
- data/vendor/coffee-script/vendor/jison/tests/parser/api.js +236 -0
- data/vendor/coffee-script/vendor/jison/tests/parser/generator.js +196 -0
- data/vendor/coffee-script/vendor/jison/tests/parser/lalr.js +183 -0
- data/vendor/coffee-script/vendor/jison/tests/parser/lr0.js +72 -0
- data/vendor/coffee-script/vendor/jison/tests/parser/lr1.js +119 -0
- data/vendor/coffee-script/vendor/jison/tests/parser/parser-tests.js +14 -0
- data/vendor/coffee-script/vendor/jison/tests/parser/precedence.js +237 -0
- data/vendor/coffee-script/vendor/jison/tests/parser/slr.js +52 -0
- data/vendor/coffee-script/vendor/jison/tests/parser/tables.js +126 -0
- data/vendor/coffee-script/vendor/jison/tests/performance.js +110 -0
- data/vendor/coffee-script/vendor/jison/tests/setup.js +3 -0
- metadata +324 -0
|
@@ -0,0 +1,1045 @@
|
|
|
1
|
+
if process?
|
|
2
|
+
process.mixin require 'scope'
|
|
3
|
+
else
|
|
4
|
+
this.exports: this
|
|
5
|
+
|
|
6
|
+
# Some helper functions
|
|
7
|
+
|
|
8
|
+
# Tabs are two spaces for pretty printing.
|
|
9
|
+
TAB: ' '
|
|
10
|
+
TRAILING_WHITESPACE: /\s+$/gm
|
|
11
|
+
|
|
12
|
+
# Keep the identifier regex in sync with the Lexer.
|
|
13
|
+
IDENTIFIER: /^[a-zA-Z$_](\w|\$)*$/
|
|
14
|
+
|
|
15
|
+
# Merge objects.
|
|
16
|
+
merge: (options, overrides) ->
|
|
17
|
+
fresh: {}
|
|
18
|
+
(fresh[key]: val) for key, val of options
|
|
19
|
+
(fresh[key]: val) for key, val of overrides if overrides
|
|
20
|
+
fresh
|
|
21
|
+
|
|
22
|
+
# Trim out all falsy values from an array.
|
|
23
|
+
compact: (array) -> item for item in array when item
|
|
24
|
+
|
|
25
|
+
# Return a completely flattened version of an array.
|
|
26
|
+
flatten: (array) ->
|
|
27
|
+
memo: []
|
|
28
|
+
for item in array
|
|
29
|
+
if item instanceof Array then memo: memo.concat(item) else memo.push(item)
|
|
30
|
+
memo
|
|
31
|
+
|
|
32
|
+
# Delete a key from an object, returning the value.
|
|
33
|
+
del: (obj, key) ->
|
|
34
|
+
val: obj[key]
|
|
35
|
+
delete obj[key]
|
|
36
|
+
val
|
|
37
|
+
|
|
38
|
+
# Quickie inheritance convenience wrapper to reduce typing.
|
|
39
|
+
inherit: (parent, props) ->
|
|
40
|
+
klass: del(props, 'constructor')
|
|
41
|
+
klass extends parent
|
|
42
|
+
(klass.prototype[name]: prop) for name, prop of props
|
|
43
|
+
klass
|
|
44
|
+
|
|
45
|
+
# Mark a node as a statement, or a statement only.
|
|
46
|
+
statement: (klass, only) ->
|
|
47
|
+
klass::is_statement: -> true
|
|
48
|
+
(klass::is_statement_only: -> true) if only
|
|
49
|
+
|
|
50
|
+
# The abstract base class for all CoffeeScript nodes.
|
|
51
|
+
# All nodes are implement a "compile_node" method, which performs the
|
|
52
|
+
# code generation for that node. To compile a node, call the "compile"
|
|
53
|
+
# method, which wraps "compile_node" in some extra smarts, to know when the
|
|
54
|
+
# generated code should be wrapped up in a closure. An options hash is passed
|
|
55
|
+
# and cloned throughout, containing messages from higher in the AST,
|
|
56
|
+
# information about the current scope, and indentation level.
|
|
57
|
+
Node: exports.Node: ->
|
|
58
|
+
|
|
59
|
+
# This is extremely important -- we convert JS statements into expressions
|
|
60
|
+
# by wrapping them in a closure, only if it's possible, and we're not at
|
|
61
|
+
# the top level of a block (which would be unnecessary), and we haven't
|
|
62
|
+
# already been asked to return the result.
|
|
63
|
+
Node::compile: (o) ->
|
|
64
|
+
@options: merge o or {}
|
|
65
|
+
@indent: o.indent
|
|
66
|
+
del @options, 'operation' unless @operation_sensitive()
|
|
67
|
+
top: if @top_sensitive() then @options.top else del @options, 'top'
|
|
68
|
+
closure: @is_statement() and not @is_statement_only() and not top and
|
|
69
|
+
not @options.returns and not (this instanceof CommentNode) and
|
|
70
|
+
not @contains (node) -> node.is_statement_only()
|
|
71
|
+
if closure then @compile_closure(@options) else @compile_node(@options)
|
|
72
|
+
|
|
73
|
+
# Statements converted into expressions share scope with their parent
|
|
74
|
+
# closure, to preserve JavaScript-style lexical scope.
|
|
75
|
+
Node::compile_closure: (o) ->
|
|
76
|
+
@indent: o.indent
|
|
77
|
+
o.shared_scope: o.scope
|
|
78
|
+
ClosureNode.wrap(this).compile(o)
|
|
79
|
+
|
|
80
|
+
# If the code generation wishes to use the result of a complex expression
|
|
81
|
+
# in multiple places, ensure that the expression is only ever evaluated once.
|
|
82
|
+
Node::compile_reference: (o) ->
|
|
83
|
+
reference: new LiteralNode(o.scope.free_variable())
|
|
84
|
+
compiled: new AssignNode(reference, this)
|
|
85
|
+
[compiled, reference]
|
|
86
|
+
|
|
87
|
+
# Quick short method for the current indentation level, plus tabbing in.
|
|
88
|
+
Node::idt: (tabs) ->
|
|
89
|
+
idt: (@indent || '')
|
|
90
|
+
idt += TAB for i in [0...(tabs or 0)]
|
|
91
|
+
idt
|
|
92
|
+
|
|
93
|
+
# Does this node, or any of its children, contain a node of a certain kind?
|
|
94
|
+
Node::contains: (block) ->
|
|
95
|
+
for node in @children
|
|
96
|
+
return true if block(node)
|
|
97
|
+
return true if node instanceof Node and node.contains block
|
|
98
|
+
false
|
|
99
|
+
|
|
100
|
+
# toString representation of the node, for inspecting the parse tree.
|
|
101
|
+
Node::toString: (idt) ->
|
|
102
|
+
idt ||= ''
|
|
103
|
+
'\n' + idt + @type + (child.toString(idt + TAB) for child in @children).join('')
|
|
104
|
+
|
|
105
|
+
# Default implementations of the common node methods.
|
|
106
|
+
Node::unwrap: -> this
|
|
107
|
+
Node::children: []
|
|
108
|
+
Node::is_statement: -> false
|
|
109
|
+
Node::is_statement_only: -> false
|
|
110
|
+
Node::top_sensitive: -> false
|
|
111
|
+
Node::operation_sensitive: -> false
|
|
112
|
+
|
|
113
|
+
# A collection of nodes, each one representing an expression.
|
|
114
|
+
Expressions: exports.Expressions: inherit Node, {
|
|
115
|
+
type: 'Expressions'
|
|
116
|
+
|
|
117
|
+
constructor: (nodes) ->
|
|
118
|
+
@children: @expressions: compact flatten nodes or []
|
|
119
|
+
this
|
|
120
|
+
|
|
121
|
+
# Tack an expression on to the end of this expression list.
|
|
122
|
+
push: (node) ->
|
|
123
|
+
@expressions.push(node)
|
|
124
|
+
this
|
|
125
|
+
|
|
126
|
+
# Tack an expression on to the beginning of this expression list.
|
|
127
|
+
unshift: (node) ->
|
|
128
|
+
@expressions.unshift(node)
|
|
129
|
+
this
|
|
130
|
+
|
|
131
|
+
# If this Expressions consists of a single node, pull it back out.
|
|
132
|
+
unwrap: ->
|
|
133
|
+
if @expressions.length is 1 then @expressions[0] else this
|
|
134
|
+
|
|
135
|
+
# Is this an empty block of code?
|
|
136
|
+
empty: ->
|
|
137
|
+
@expressions.length is 0
|
|
138
|
+
|
|
139
|
+
# Is the node last in this block of expressions?
|
|
140
|
+
is_last: (node) ->
|
|
141
|
+
l: @expressions.length
|
|
142
|
+
last_index: if @expressions[l - 1] instanceof CommentNode then 2 else 1
|
|
143
|
+
node is @expressions[l - last_index]
|
|
144
|
+
|
|
145
|
+
compile: (o) ->
|
|
146
|
+
o ||= {}
|
|
147
|
+
if o.scope then Node::compile.call(this, o) else @compile_root(o)
|
|
148
|
+
|
|
149
|
+
# Compile each expression in the Expressions body.
|
|
150
|
+
compile_node: (o) ->
|
|
151
|
+
(@compile_expression(node, merge(o)) for node in @expressions).join("\n")
|
|
152
|
+
|
|
153
|
+
# If this is the top-level Expressions, wrap everything in a safety closure.
|
|
154
|
+
compile_root: (o) ->
|
|
155
|
+
o.indent: @indent: indent: if o.no_wrap then '' else TAB
|
|
156
|
+
o.scope: new Scope(null, this, null)
|
|
157
|
+
code: if o.globals then @compile_node(o) else @compile_with_declarations(o)
|
|
158
|
+
code: code.replace(TRAILING_WHITESPACE, '')
|
|
159
|
+
if o.no_wrap then code else "(function(){\n"+code+"\n})();"
|
|
160
|
+
|
|
161
|
+
# Compile the expressions body, with declarations of all inner variables
|
|
162
|
+
# pushed up to the top.
|
|
163
|
+
compile_with_declarations: (o) ->
|
|
164
|
+
code: @compile_node(o)
|
|
165
|
+
args: @contains (node) -> node instanceof ValueNode and node.is_arguments()
|
|
166
|
+
code: @idt() + "arguments = Array.prototype.slice.call(arguments, 0);\n" + code if args
|
|
167
|
+
code: @idt() + 'var ' + o.scope.compiled_assignments() + ";\n" + code if o.scope.has_assignments(this)
|
|
168
|
+
code: @idt() + 'var ' + o.scope.compiled_declarations() + ";\n" + code if o.scope.has_declarations(this)
|
|
169
|
+
code
|
|
170
|
+
|
|
171
|
+
# Compiles a single expression within the expressions body.
|
|
172
|
+
compile_expression: (node, o) ->
|
|
173
|
+
@indent: o.indent
|
|
174
|
+
stmt: node.is_statement()
|
|
175
|
+
# We need to return the result if this is the last node in the expressions body.
|
|
176
|
+
returns: del(o, 'returns') and @is_last(node) and not node.is_statement_only()
|
|
177
|
+
# Return the regular compile of the node, unless we need to return the result.
|
|
178
|
+
return (if stmt then '' else @idt()) + node.compile(merge(o, {top: true})) + (if stmt then '' else ';') unless returns
|
|
179
|
+
# If it's a statement, the node knows how to return itself.
|
|
180
|
+
return node.compile(merge(o, {returns: true})) if node.is_statement()
|
|
181
|
+
# Otherwise, we can just return the value of the expression.
|
|
182
|
+
return @idt() + 'return ' + node.compile(o) + ';'
|
|
183
|
+
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
# Wrap up a node as an Expressions, unless it already is one.
|
|
187
|
+
Expressions.wrap: (nodes) ->
|
|
188
|
+
return nodes[0] if nodes.length is 1 and nodes[0] instanceof Expressions
|
|
189
|
+
new Expressions(nodes)
|
|
190
|
+
|
|
191
|
+
statement Expressions
|
|
192
|
+
|
|
193
|
+
# Literals are static values that can be passed through directly into
|
|
194
|
+
# JavaScript without translation, eg.: strings, numbers, true, false, null...
|
|
195
|
+
LiteralNode: exports.LiteralNode: inherit Node, {
|
|
196
|
+
type: 'Literal'
|
|
197
|
+
|
|
198
|
+
constructor: (value) ->
|
|
199
|
+
@value: value
|
|
200
|
+
this
|
|
201
|
+
|
|
202
|
+
# Break and continue must be treated as statements -- they lose their meaning
|
|
203
|
+
# when wrapped in a closure.
|
|
204
|
+
is_statement: ->
|
|
205
|
+
@value is 'break' or @value is 'continue'
|
|
206
|
+
|
|
207
|
+
compile_node: (o) ->
|
|
208
|
+
idt: if @is_statement() then @idt() else ''
|
|
209
|
+
end: if @is_statement() then ';' else ''
|
|
210
|
+
idt + @value + end
|
|
211
|
+
|
|
212
|
+
toString: (idt) ->
|
|
213
|
+
' "' + @value + '"'
|
|
214
|
+
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
LiteralNode::is_statement_only: LiteralNode::is_statement
|
|
218
|
+
|
|
219
|
+
# Return an expression, or wrap it in a closure and return it.
|
|
220
|
+
ReturnNode: exports.ReturnNode: inherit Node, {
|
|
221
|
+
type: 'Return'
|
|
222
|
+
|
|
223
|
+
constructor: (expression) ->
|
|
224
|
+
@children: [@expression: expression]
|
|
225
|
+
this
|
|
226
|
+
|
|
227
|
+
compile_node: (o) ->
|
|
228
|
+
return @expression.compile(merge(o, {returns: true})) if @expression.is_statement()
|
|
229
|
+
@idt() + 'return ' + @expression.compile(o) + ';'
|
|
230
|
+
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
statement ReturnNode, true
|
|
234
|
+
|
|
235
|
+
# A value, indexed or dotted into, or vanilla.
|
|
236
|
+
ValueNode: exports.ValueNode: inherit Node, {
|
|
237
|
+
type: 'Value'
|
|
238
|
+
|
|
239
|
+
SOAK: " == undefined ? undefined : "
|
|
240
|
+
|
|
241
|
+
constructor: (base, properties) ->
|
|
242
|
+
@children: flatten [@base: base, @properties: (properties or [])]
|
|
243
|
+
this
|
|
244
|
+
|
|
245
|
+
push: (prop) ->
|
|
246
|
+
@properties.push(prop)
|
|
247
|
+
@children.push(prop)
|
|
248
|
+
this
|
|
249
|
+
|
|
250
|
+
operation_sensitive: ->
|
|
251
|
+
true
|
|
252
|
+
|
|
253
|
+
has_properties: ->
|
|
254
|
+
!!@properties.length
|
|
255
|
+
|
|
256
|
+
is_array: ->
|
|
257
|
+
@base instanceof ArrayNode and not @has_properties()
|
|
258
|
+
|
|
259
|
+
is_object: ->
|
|
260
|
+
@base instanceof ObjectNode and not @has_properties()
|
|
261
|
+
|
|
262
|
+
is_splice: ->
|
|
263
|
+
@has_properties() and @properties[@properties.length - 1] instanceof SliceNode
|
|
264
|
+
|
|
265
|
+
is_arguments: ->
|
|
266
|
+
@base.value is 'arguments'
|
|
267
|
+
|
|
268
|
+
unwrap: ->
|
|
269
|
+
if @properties.length then this else @base
|
|
270
|
+
|
|
271
|
+
# Values are statements if their base is a statement.
|
|
272
|
+
is_statement: ->
|
|
273
|
+
@base.is_statement and @base.is_statement() and not @has_properties()
|
|
274
|
+
|
|
275
|
+
compile_node: (o) ->
|
|
276
|
+
soaked: false
|
|
277
|
+
only: del(o, 'only_first')
|
|
278
|
+
op: del(o, 'operation')
|
|
279
|
+
props: if only then @properties[0...@properties.length - 1] else @properties
|
|
280
|
+
baseline: @base.compile o
|
|
281
|
+
baseline: '(' + baseline + ')' if @base instanceof ObjectNode and @has_properties()
|
|
282
|
+
complete: @last: baseline
|
|
283
|
+
|
|
284
|
+
for prop in props
|
|
285
|
+
@source: baseline
|
|
286
|
+
if prop.soak_node
|
|
287
|
+
soaked: true
|
|
288
|
+
if @base instanceof CallNode and prop is props[0]
|
|
289
|
+
temp: o.scope.free_variable()
|
|
290
|
+
complete: '(' + temp + ' = ' + complete + ')' + @SOAK + (baseline: temp + prop.compile(o))
|
|
291
|
+
else
|
|
292
|
+
complete: complete + @SOAK + (baseline += prop.compile(o))
|
|
293
|
+
else
|
|
294
|
+
part: prop.compile(o)
|
|
295
|
+
baseline += part
|
|
296
|
+
complete += part
|
|
297
|
+
@last: part
|
|
298
|
+
|
|
299
|
+
if op and soaked then '(' + complete + ')' else complete
|
|
300
|
+
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
# Pass through CoffeeScript comments into JavaScript comments at the
|
|
304
|
+
# same position.
|
|
305
|
+
CommentNode: exports.CommentNode: inherit Node, {
|
|
306
|
+
type: 'Comment'
|
|
307
|
+
|
|
308
|
+
constructor: (lines) ->
|
|
309
|
+
@lines: lines
|
|
310
|
+
this
|
|
311
|
+
|
|
312
|
+
compile_node: (o) ->
|
|
313
|
+
@idt() + '//' + @lines.join('\n' + @idt() + '//')
|
|
314
|
+
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
statement CommentNode
|
|
318
|
+
|
|
319
|
+
# Node for a function invocation. Takes care of converting super() calls into
|
|
320
|
+
# calls against the prototype's function of the same name.
|
|
321
|
+
CallNode: exports.CallNode: inherit Node, {
|
|
322
|
+
type: 'Call'
|
|
323
|
+
|
|
324
|
+
constructor: (variable, args) ->
|
|
325
|
+
@children: flatten [@variable: variable, @args: (args or [])]
|
|
326
|
+
@prefix: ''
|
|
327
|
+
this
|
|
328
|
+
|
|
329
|
+
new_instance: ->
|
|
330
|
+
@prefix: 'new '
|
|
331
|
+
this
|
|
332
|
+
|
|
333
|
+
push: (arg) ->
|
|
334
|
+
@args.push(arg)
|
|
335
|
+
@children.push(arg)
|
|
336
|
+
this
|
|
337
|
+
|
|
338
|
+
# Compile a vanilla function call.
|
|
339
|
+
compile_node: (o) ->
|
|
340
|
+
return @compile_splat(o) if @args[@args.length - 1] instanceof SplatNode
|
|
341
|
+
args: (arg.compile(o) for arg in @args).join(', ')
|
|
342
|
+
return @compile_super(args, o) if @variable is 'super'
|
|
343
|
+
@prefix + @variable.compile(o) + '(' + args + ')'
|
|
344
|
+
|
|
345
|
+
# Compile a call against the superclass's implementation of the current function.
|
|
346
|
+
compile_super: (args, o) ->
|
|
347
|
+
methname: o.scope.method.name
|
|
348
|
+
arg_part: if args.length then ', ' + args else ''
|
|
349
|
+
meth: if o.scope.method.proto
|
|
350
|
+
o.scope.method.proto + '.__superClass__.' + methname
|
|
351
|
+
else
|
|
352
|
+
methname + '.__superClass__.constructor'
|
|
353
|
+
meth + '.call(this' + arg_part + ')'
|
|
354
|
+
|
|
355
|
+
# Compile a function call being passed variable arguments.
|
|
356
|
+
compile_splat: (o) ->
|
|
357
|
+
meth: @variable.compile o
|
|
358
|
+
obj: @variable.source or 'this'
|
|
359
|
+
args: for arg, i in @args
|
|
360
|
+
code: arg.compile o
|
|
361
|
+
code: if arg instanceof SplatNode then code else '[' + code + ']'
|
|
362
|
+
if i is 0 then code else '.concat(' + code + ')'
|
|
363
|
+
@prefix + meth + '.apply(' + obj + ', ' + args.join('') + ')'
|
|
364
|
+
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
# Node to extend an object's prototype with an ancestor object.
|
|
368
|
+
# After goog.inherits from the Closure Library.
|
|
369
|
+
ExtendsNode: exports.ExtendsNode: inherit Node, {
|
|
370
|
+
type: 'Extends'
|
|
371
|
+
|
|
372
|
+
constructor: (child, parent) ->
|
|
373
|
+
@children: [@child: child, @parent: parent]
|
|
374
|
+
this
|
|
375
|
+
|
|
376
|
+
# Hooking one constructor into another's prototype chain.
|
|
377
|
+
compile_node: (o) ->
|
|
378
|
+
construct: o.scope.free_variable()
|
|
379
|
+
child: @child.compile(o)
|
|
380
|
+
parent: @parent.compile(o)
|
|
381
|
+
prefix: ''
|
|
382
|
+
if not (@child instanceof ValueNode) or @child.has_properties() or not (@child.unwrap() instanceof LiteralNode)
|
|
383
|
+
child_var: o.scope.free_variable()
|
|
384
|
+
prefix += @idt() + child_var + ' = ' + child + ';\n'
|
|
385
|
+
child: child_var
|
|
386
|
+
if not (@parent instanceof ValueNode) or @parent.has_properties() or not (@parent.unwrap() instanceof LiteralNode)
|
|
387
|
+
parent_var: o.scope.free_variable()
|
|
388
|
+
prefix += @idt() + parent_var + ' = ' + parent + ';\n'
|
|
389
|
+
parent: parent_var
|
|
390
|
+
prefix + @idt() + construct + ' = function(){};\n' + @idt() +
|
|
391
|
+
construct + '.prototype = ' + parent + ".prototype;\n" + @idt() +
|
|
392
|
+
child + '.__superClass__ = ' + parent + ".prototype;\n" + @idt() +
|
|
393
|
+
child + '.prototype = new ' + construct + "();\n" + @idt() +
|
|
394
|
+
child + '.prototype.constructor = ' + child + ';'
|
|
395
|
+
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
statement ExtendsNode
|
|
399
|
+
|
|
400
|
+
# A dotted accessor into a part of a value, or the :: shorthand for
|
|
401
|
+
# an accessor into the object's prototype.
|
|
402
|
+
AccessorNode: exports.AccessorNode: inherit Node, {
|
|
403
|
+
type: 'Accessor'
|
|
404
|
+
|
|
405
|
+
constructor: (name, tag) ->
|
|
406
|
+
@children: [@name: name]
|
|
407
|
+
@prototype: tag is 'prototype'
|
|
408
|
+
@soak_node: tag is 'soak'
|
|
409
|
+
this
|
|
410
|
+
|
|
411
|
+
compile_node: (o) ->
|
|
412
|
+
'.' + (if @prototype then 'prototype.' else '') + @name.compile(o)
|
|
413
|
+
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
# An indexed accessor into a part of an array or object.
|
|
417
|
+
IndexNode: exports.IndexNode: inherit Node, {
|
|
418
|
+
type: 'Index'
|
|
419
|
+
|
|
420
|
+
constructor: (index, tag) ->
|
|
421
|
+
@children: [@index: index]
|
|
422
|
+
@soak_node: tag is 'soak'
|
|
423
|
+
this
|
|
424
|
+
|
|
425
|
+
compile_node: (o) ->
|
|
426
|
+
'[' + @index.compile(o) + ']'
|
|
427
|
+
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
# A range literal. Ranges can be used to extract portions (slices) of arrays,
|
|
431
|
+
# or to specify a range for list comprehensions.
|
|
432
|
+
RangeNode: exports.RangeNode: inherit Node, {
|
|
433
|
+
type: 'Range'
|
|
434
|
+
|
|
435
|
+
constructor: (from, to, exclusive) ->
|
|
436
|
+
@children: [@from: from, @to: to]
|
|
437
|
+
@exclusive: !!exclusive
|
|
438
|
+
this
|
|
439
|
+
|
|
440
|
+
compile_variables: (o) ->
|
|
441
|
+
@indent: o.indent
|
|
442
|
+
@from_var: o.scope.free_variable()
|
|
443
|
+
@to_var: o.scope.free_variable()
|
|
444
|
+
@from_var + ' = ' + @from.compile(o) + '; ' + @to_var + ' = ' + @to.compile(o) + ";\n" + @idt()
|
|
445
|
+
|
|
446
|
+
compile_node: (o) ->
|
|
447
|
+
return @compile_array(o) unless o.index
|
|
448
|
+
idx: del o, 'index'
|
|
449
|
+
step: del o, 'step'
|
|
450
|
+
vars: idx + '=' + @from_var
|
|
451
|
+
step: if step then step.compile(o) else '1'
|
|
452
|
+
equals: if @exclusive then '' else '='
|
|
453
|
+
intro: '(' + @from_var + ' <= ' + @to_var + ' ? ' + idx
|
|
454
|
+
compare: intro + ' <' + equals + ' ' + @to_var + ' : ' + idx + ' >' + equals + ' ' + @to_var + ')'
|
|
455
|
+
incr: intro + ' += ' + step + ' : ' + idx + ' -= ' + step + ')'
|
|
456
|
+
vars + '; ' + compare + '; ' + incr
|
|
457
|
+
|
|
458
|
+
# Expand the range into the equivalent array, if it's not being used as
|
|
459
|
+
# part of a comprehension, slice, or splice.
|
|
460
|
+
# TODO: This generates pretty ugly code ... shrink it.
|
|
461
|
+
compile_array: (o) ->
|
|
462
|
+
name: o.scope.free_variable()
|
|
463
|
+
body: Expressions.wrap([new LiteralNode(name)])
|
|
464
|
+
arr: Expressions.wrap([new ForNode(body, {source: (new ValueNode(this))}, new LiteralNode(name))])
|
|
465
|
+
(new ParentheticalNode(new CallNode(new CodeNode([], arr)))).compile(o)
|
|
466
|
+
|
|
467
|
+
}
|
|
468
|
+
|
|
469
|
+
# An array slice literal. Unlike JavaScript's Array#slice, the second parameter
|
|
470
|
+
# specifies the index of the end of the slice (just like the first parameter)
|
|
471
|
+
# is the index of the beginning.
|
|
472
|
+
SliceNode: exports.SliceNode: inherit Node, {
|
|
473
|
+
type: 'Slice'
|
|
474
|
+
|
|
475
|
+
constructor: (range) ->
|
|
476
|
+
@children: [@range: range]
|
|
477
|
+
this
|
|
478
|
+
|
|
479
|
+
compile_node: (o) ->
|
|
480
|
+
from: @range.from.compile(o)
|
|
481
|
+
to: @range.to.compile(o)
|
|
482
|
+
plus_part: if @range.exclusive then '' else ' + 1'
|
|
483
|
+
".slice(" + from + ', ' + to + plus_part + ')'
|
|
484
|
+
|
|
485
|
+
}
|
|
486
|
+
|
|
487
|
+
# An object literal.
|
|
488
|
+
ObjectNode: exports.ObjectNode: inherit Node, {
|
|
489
|
+
type: 'Object'
|
|
490
|
+
|
|
491
|
+
constructor: (props) ->
|
|
492
|
+
@children: @objects: @properties: props or []
|
|
493
|
+
this
|
|
494
|
+
|
|
495
|
+
# All the mucking about with commas is to make sure that CommentNodes and
|
|
496
|
+
# AssignNodes get interleaved correctly, with no trailing commas or
|
|
497
|
+
# commas affixed to comments. TODO: Extract this and add it to ArrayNode.
|
|
498
|
+
compile_node: (o) ->
|
|
499
|
+
o.indent: @idt(1)
|
|
500
|
+
non_comments: prop for prop in @properties when not (prop instanceof CommentNode)
|
|
501
|
+
last_noncom: non_comments[non_comments.length - 1]
|
|
502
|
+
props: for prop, i in @properties
|
|
503
|
+
join: ",\n"
|
|
504
|
+
join: "\n" if (prop is last_noncom) or (prop instanceof CommentNode)
|
|
505
|
+
join: '' if i is @properties.length - 1
|
|
506
|
+
indent: if prop instanceof CommentNode then '' else @idt(1)
|
|
507
|
+
indent + prop.compile(o) + join
|
|
508
|
+
props: props.join('')
|
|
509
|
+
inner: if props then '\n' + props + '\n' + @idt() else ''
|
|
510
|
+
'{' + inner + '}'
|
|
511
|
+
|
|
512
|
+
}
|
|
513
|
+
|
|
514
|
+
# An array literal.
|
|
515
|
+
ArrayNode: exports.ArrayNode: inherit Node, {
|
|
516
|
+
type: 'Array'
|
|
517
|
+
|
|
518
|
+
constructor: (objects) ->
|
|
519
|
+
@children: @objects: objects or []
|
|
520
|
+
this
|
|
521
|
+
|
|
522
|
+
compile_node: (o) ->
|
|
523
|
+
o.indent: @idt(1)
|
|
524
|
+
objects: for obj, i in @objects
|
|
525
|
+
code: obj.compile(o)
|
|
526
|
+
if obj instanceof CommentNode
|
|
527
|
+
'\n' + code + '\n' + o.indent
|
|
528
|
+
else if i is @objects.length - 1
|
|
529
|
+
code
|
|
530
|
+
else
|
|
531
|
+
code + ', '
|
|
532
|
+
objects: objects.join('')
|
|
533
|
+
ending: if objects.indexOf('\n') >= 0 then "\n" + @idt() + ']' else ']'
|
|
534
|
+
'[' + objects + ending
|
|
535
|
+
|
|
536
|
+
}
|
|
537
|
+
|
|
538
|
+
# A faux-node that is never created by the grammar, but is used during
|
|
539
|
+
# code generation to generate a quick "array.push(value)" tree of nodes.
|
|
540
|
+
PushNode: exports.PushNode: {
|
|
541
|
+
|
|
542
|
+
wrap: (array, expressions) ->
|
|
543
|
+
expr: expressions.unwrap()
|
|
544
|
+
return expressions if expr.is_statement_only() or expr.contains (n) -> n.is_statement_only()
|
|
545
|
+
Expressions.wrap([new CallNode(
|
|
546
|
+
new ValueNode(new LiteralNode(array), [new AccessorNode(new LiteralNode('push'))]), [expr]
|
|
547
|
+
)])
|
|
548
|
+
|
|
549
|
+
}
|
|
550
|
+
|
|
551
|
+
# A faux-node used to wrap an expressions body in a closure.
|
|
552
|
+
ClosureNode: exports.ClosureNode: {
|
|
553
|
+
|
|
554
|
+
wrap: (expressions, statement) ->
|
|
555
|
+
func: new ParentheticalNode(new CodeNode([], Expressions.wrap([expressions])))
|
|
556
|
+
call: new CallNode(new ValueNode(func, [new AccessorNode(new LiteralNode('call'))]), [new LiteralNode('this')])
|
|
557
|
+
if statement then Expressions.wrap([call]) else call
|
|
558
|
+
|
|
559
|
+
}
|
|
560
|
+
|
|
561
|
+
# Setting the value of a local variable, or the value of an object property.
|
|
562
|
+
AssignNode: exports.AssignNode: inherit Node, {
|
|
563
|
+
type: 'Assign'
|
|
564
|
+
|
|
565
|
+
PROTO_ASSIGN: /^(\S+)\.prototype/
|
|
566
|
+
LEADING_DOT: /^\.(prototype\.)?/
|
|
567
|
+
|
|
568
|
+
constructor: (variable, value, context) ->
|
|
569
|
+
@children: [@variable: variable, @value: value]
|
|
570
|
+
@context: context
|
|
571
|
+
this
|
|
572
|
+
|
|
573
|
+
top_sensitive: ->
|
|
574
|
+
true
|
|
575
|
+
|
|
576
|
+
is_value: ->
|
|
577
|
+
@variable instanceof ValueNode
|
|
578
|
+
|
|
579
|
+
is_statement: ->
|
|
580
|
+
@is_value() and (@variable.is_array() or @variable.is_object())
|
|
581
|
+
|
|
582
|
+
compile_node: (o) ->
|
|
583
|
+
top: del o, 'top'
|
|
584
|
+
return @compile_pattern_match(o) if @is_statement()
|
|
585
|
+
return @compile_splice(o) if @is_value() and @variable.is_splice()
|
|
586
|
+
stmt: del o, 'as_statement'
|
|
587
|
+
name: @variable.compile(o)
|
|
588
|
+
last: if @is_value() then @variable.last.replace(@LEADING_DOT, '') else name
|
|
589
|
+
match: name.match(@PROTO_ASSIGN)
|
|
590
|
+
proto: match and match[1]
|
|
591
|
+
if @value instanceof CodeNode
|
|
592
|
+
@value.name: last if last.match(IDENTIFIER)
|
|
593
|
+
@value.proto: proto if proto
|
|
594
|
+
return name + ': ' + @value.compile(o) if @context is 'object'
|
|
595
|
+
o.scope.find(name) unless @is_value() and @variable.has_properties()
|
|
596
|
+
val: name + ' = ' + @value.compile(o)
|
|
597
|
+
return @idt() + val + ';' if stmt
|
|
598
|
+
val: '(' + val + ')' if not top or o.returns
|
|
599
|
+
val: @idt() + 'return ' + val if o.returns
|
|
600
|
+
val
|
|
601
|
+
|
|
602
|
+
# Implementation of recursive pattern matching, when assigning array or
|
|
603
|
+
# object literals to a value. Peeks at their properties to assign inner names.
|
|
604
|
+
# See: http://wiki.ecmascript.org/doku.php?id=harmony:destructuring
|
|
605
|
+
compile_pattern_match: (o) ->
|
|
606
|
+
val_var: o.scope.free_variable()
|
|
607
|
+
assigns: [@idt() + val_var + ' = ' + @value.compile(o) + ';']
|
|
608
|
+
o.top: true
|
|
609
|
+
o.as_statement: true
|
|
610
|
+
for obj, i in @variable.base.objects
|
|
611
|
+
idx: i
|
|
612
|
+
[obj, idx]: [obj.value, obj.variable.base] if @variable.is_object()
|
|
613
|
+
access_class: if @variable.is_array() then IndexNode else AccessorNode
|
|
614
|
+
if obj instanceof SplatNode
|
|
615
|
+
val: new LiteralNode(obj.compile_value(o, val_var, @variable.base.objects.indexOf(obj)))
|
|
616
|
+
else
|
|
617
|
+
idx: new LiteralNode(idx) unless typeof idx is 'object'
|
|
618
|
+
val: new ValueNode(new LiteralNode(val_var), [new access_class(idx)])
|
|
619
|
+
assigns.push(new AssignNode(obj, val).compile(o))
|
|
620
|
+
assigns.join("\n")
|
|
621
|
+
|
|
622
|
+
compile_splice: (o) ->
|
|
623
|
+
name: @variable.compile(merge(o, {only_first: true}))
|
|
624
|
+
l: @variable.properties.length
|
|
625
|
+
range: @variable.properties[l - 1].range
|
|
626
|
+
plus: if range.exclusive then '' else ' + 1'
|
|
627
|
+
from: range.from.compile(o)
|
|
628
|
+
to: range.to.compile(o) + ' - ' + from + plus
|
|
629
|
+
name + '.splice.apply(' + name + ', [' + from + ', ' + to + '].concat(' + @value.compile(o) + '))'
|
|
630
|
+
|
|
631
|
+
}
|
|
632
|
+
|
|
633
|
+
# A function definition. The only node that creates a new Scope.
|
|
634
|
+
# A CodeNode does not have any children -- they're within the new scope.
|
|
635
|
+
CodeNode: exports.CodeNode: inherit Node, {
|
|
636
|
+
type: 'Code'
|
|
637
|
+
|
|
638
|
+
constructor: (params, body, tag) ->
|
|
639
|
+
@params: params
|
|
640
|
+
@body: body
|
|
641
|
+
@bound: tag is 'boundfunc'
|
|
642
|
+
this
|
|
643
|
+
|
|
644
|
+
compile_node: (o) ->
|
|
645
|
+
shared_scope: del o, 'shared_scope'
|
|
646
|
+
top: del o, 'top'
|
|
647
|
+
o.scope: shared_scope or new Scope(o.scope, @body, this)
|
|
648
|
+
o.returns: true
|
|
649
|
+
o.top: true
|
|
650
|
+
o.indent: @idt(if @bound then 2 else 1)
|
|
651
|
+
del o, 'no_wrap'
|
|
652
|
+
del o, 'globals'
|
|
653
|
+
if @params[@params.length - 1] instanceof SplatNode
|
|
654
|
+
splat: @params.pop()
|
|
655
|
+
splat.index: @params.length
|
|
656
|
+
@body.unshift(splat)
|
|
657
|
+
params: (param.compile(o) for param in @params)
|
|
658
|
+
(o.scope.parameter(param)) for param in params
|
|
659
|
+
code: if @body.expressions.length then '\n' + @body.compile_with_declarations(o) + '\n' else ''
|
|
660
|
+
name_part: if @name then ' ' + @name else ''
|
|
661
|
+
func: 'function' + (if @bound then '' else name_part) + '(' + params.join(', ') + ') {' + code + @idt(if @bound then 1 else 0) + '}'
|
|
662
|
+
func: '(' + func + ')' if top and not @bound
|
|
663
|
+
return func unless @bound
|
|
664
|
+
inner: '(function' + name_part + '() {\n' + @idt(2) + 'return __func.apply(__this, arguments);\n' + @idt(1) + '});'
|
|
665
|
+
'(function(__this) {\n' + @idt(1) + 'var __func = ' + func + ';\n' + @idt(1) + 'return ' + inner + '\n' + @idt() + '})(this)'
|
|
666
|
+
|
|
667
|
+
top_sensitive: ->
|
|
668
|
+
true
|
|
669
|
+
|
|
670
|
+
toString: (idt) ->
|
|
671
|
+
idt ||= ''
|
|
672
|
+
children: flatten [@params, @body.expressions]
|
|
673
|
+
'\n' + idt + @type + (child.toString(idt + TAB) for child in children).join('')
|
|
674
|
+
|
|
675
|
+
}
|
|
676
|
+
|
|
677
|
+
# A splat, either as a parameter to a function, an argument to a call,
|
|
678
|
+
# or in a destructuring assignment.
|
|
679
|
+
SplatNode: exports.SplatNode: inherit Node, {
|
|
680
|
+
type: 'Splat'
|
|
681
|
+
|
|
682
|
+
constructor: (name) ->
|
|
683
|
+
name: new LiteralNode(name) unless name.compile
|
|
684
|
+
@children: [@name: name]
|
|
685
|
+
this
|
|
686
|
+
|
|
687
|
+
compile_node: (o) ->
|
|
688
|
+
if @index? then @compile_param(o) else @name.compile(o)
|
|
689
|
+
|
|
690
|
+
compile_param: (o) ->
|
|
691
|
+
name: @name.compile(o)
|
|
692
|
+
o.scope.find name
|
|
693
|
+
name + ' = Array.prototype.slice.call(arguments, ' + @index + ')'
|
|
694
|
+
|
|
695
|
+
compile_value: (o, name, index) ->
|
|
696
|
+
"Array.prototype.slice.call(" + name + ', ' + index + ')'
|
|
697
|
+
|
|
698
|
+
}
|
|
699
|
+
|
|
700
|
+
# A while loop, the only sort of low-level loop exposed by CoffeeScript. From
|
|
701
|
+
# it, all other loops can be manufactured.
|
|
702
|
+
WhileNode: exports.WhileNode: inherit Node, {
|
|
703
|
+
type: 'While'
|
|
704
|
+
|
|
705
|
+
constructor: (condition, opts) ->
|
|
706
|
+
@children:[@condition: condition]
|
|
707
|
+
@filter: opts and opts.filter
|
|
708
|
+
this
|
|
709
|
+
|
|
710
|
+
add_body: (body) ->
|
|
711
|
+
@children.push @body: body
|
|
712
|
+
this
|
|
713
|
+
|
|
714
|
+
top_sensitive: ->
|
|
715
|
+
true
|
|
716
|
+
|
|
717
|
+
compile_node: (o) ->
|
|
718
|
+
returns: del(o, 'returns')
|
|
719
|
+
top: del(o, 'top') and not returns
|
|
720
|
+
o.indent: @idt(1)
|
|
721
|
+
o.top: true
|
|
722
|
+
cond: @condition.compile(o)
|
|
723
|
+
set: ''
|
|
724
|
+
if not top
|
|
725
|
+
rvar: o.scope.free_variable()
|
|
726
|
+
set: @idt() + rvar + ' = [];\n'
|
|
727
|
+
@body: PushNode.wrap(rvar, @body) if @body
|
|
728
|
+
post: if returns then '\n' + @idt() + 'return ' + rvar + ';' else ''
|
|
729
|
+
pre: set + @idt() + 'while (' + cond + ')'
|
|
730
|
+
return pre + ' null;' + post if not @body
|
|
731
|
+
@body: Expressions.wrap([new IfNode(@filter, @body)]) if @filter
|
|
732
|
+
pre + ' {\n' + @body.compile(o) + '\n' + @idt() + '}' + post
|
|
733
|
+
|
|
734
|
+
}
|
|
735
|
+
|
|
736
|
+
statement WhileNode
|
|
737
|
+
|
|
738
|
+
# Simple Arithmetic and logical operations. Performs some conversion from
|
|
739
|
+
# CoffeeScript operations into their JavaScript equivalents.
|
|
740
|
+
OpNode: exports.OpNode: inherit Node, {
|
|
741
|
+
type: 'Op'
|
|
742
|
+
|
|
743
|
+
CONVERSIONS: {
|
|
744
|
+
'==': '==='
|
|
745
|
+
'!=': '!=='
|
|
746
|
+
'and': '&&'
|
|
747
|
+
'or': '||'
|
|
748
|
+
'is': '==='
|
|
749
|
+
'isnt': '!=='
|
|
750
|
+
'not': '!'
|
|
751
|
+
}
|
|
752
|
+
|
|
753
|
+
CHAINABLE: ['<', '>', '>=', '<=', '===', '!==']
|
|
754
|
+
ASSIGNMENT: ['||=', '&&=', '?=']
|
|
755
|
+
PREFIX_OPERATORS: ['typeof', 'delete']
|
|
756
|
+
|
|
757
|
+
constructor: (operator, first, second, flip) ->
|
|
758
|
+
@type += ' ' + operator
|
|
759
|
+
@children: compact [@first: first, @second: second]
|
|
760
|
+
@operator: @CONVERSIONS[operator] or operator
|
|
761
|
+
@flip: !!flip
|
|
762
|
+
this
|
|
763
|
+
|
|
764
|
+
is_unary: ->
|
|
765
|
+
not @second
|
|
766
|
+
|
|
767
|
+
is_chainable: ->
|
|
768
|
+
@CHAINABLE.indexOf(@operator) >= 0
|
|
769
|
+
|
|
770
|
+
compile_node: (o) ->
|
|
771
|
+
o.operation: true
|
|
772
|
+
return @compile_chain(o) if @is_chainable() and @first.unwrap() instanceof OpNode and @first.unwrap().is_chainable()
|
|
773
|
+
return @compile_assignment(o) if @ASSIGNMENT.indexOf(@operator) >= 0
|
|
774
|
+
return @compile_unary(o) if @is_unary()
|
|
775
|
+
return @compile_existence(o) if @operator is '?'
|
|
776
|
+
@first.compile(o) + ' ' + @operator + ' ' + @second.compile(o)
|
|
777
|
+
|
|
778
|
+
# Mimic Python's chained comparisons. See:
|
|
779
|
+
# http://docs.python.org/reference/expressions.html#notin
|
|
780
|
+
compile_chain: (o) ->
|
|
781
|
+
shared: @first.unwrap().second
|
|
782
|
+
[@first.second, shared]: shared.compile_reference(o) if shared instanceof CallNode
|
|
783
|
+
'(' + @first.compile(o) + ') && (' + shared.compile(o) + ' ' + @operator + ' ' + @second.compile(o) + ')'
|
|
784
|
+
|
|
785
|
+
compile_assignment: (o) ->
|
|
786
|
+
[first, second]: [@first.compile(o), @second.compile(o)]
|
|
787
|
+
o.scope.find(first) if first.match(IDENTIFIER)
|
|
788
|
+
return first + ' = ' + ExistenceNode.compile_test(o, @first) + ' ? ' + first + ' : ' + second if @operator is '?='
|
|
789
|
+
first + ' = ' + first + ' ' + @operator.substr(0, 2) + ' ' + second
|
|
790
|
+
|
|
791
|
+
compile_existence: (o) ->
|
|
792
|
+
[first, second]: [@first.compile(o), @second.compile(o)]
|
|
793
|
+
ExistenceNode.compile_test(o, @first) + ' ? ' + first + ' : ' + second
|
|
794
|
+
|
|
795
|
+
compile_unary: (o) ->
|
|
796
|
+
space: if @PREFIX_OPERATORS.indexOf(@operator) >= 0 then ' ' else ''
|
|
797
|
+
parts: [@operator, space, @first.compile(o)]
|
|
798
|
+
parts: parts.reverse() if @flip
|
|
799
|
+
parts.join('')
|
|
800
|
+
|
|
801
|
+
}
|
|
802
|
+
|
|
803
|
+
# A try/catch/finally block.
|
|
804
|
+
TryNode: exports.TryNode: inherit Node, {
|
|
805
|
+
type: 'Try'
|
|
806
|
+
|
|
807
|
+
constructor: (attempt, error, recovery, ensure) ->
|
|
808
|
+
@children: compact [@attempt: attempt, @recovery: recovery, @ensure: ensure]
|
|
809
|
+
@error: error
|
|
810
|
+
this
|
|
811
|
+
|
|
812
|
+
compile_node: (o) ->
|
|
813
|
+
o.indent: @idt(1)
|
|
814
|
+
o.top: true
|
|
815
|
+
error_part: if @error then ' (' + @error.compile(o) + ') ' else ' '
|
|
816
|
+
catch_part: (@recovery or '') and ' catch' + error_part + '{\n' + @recovery.compile(o) + '\n' + @idt() + '}'
|
|
817
|
+
finally_part: (@ensure or '') and ' finally {\n' + @ensure.compile(merge(o, {returns: null})) + '\n' + @idt() + '}'
|
|
818
|
+
@idt() + 'try {\n' + @attempt.compile(o) + '\n' + @idt() + '}' + catch_part + finally_part
|
|
819
|
+
|
|
820
|
+
}
|
|
821
|
+
|
|
822
|
+
statement TryNode
|
|
823
|
+
|
|
824
|
+
# Throw an exception.
|
|
825
|
+
ThrowNode: exports.ThrowNode: inherit Node, {
|
|
826
|
+
type: 'Throw'
|
|
827
|
+
|
|
828
|
+
constructor: (expression) ->
|
|
829
|
+
@children: [@expression: expression]
|
|
830
|
+
this
|
|
831
|
+
|
|
832
|
+
compile_node: (o) ->
|
|
833
|
+
@idt() + 'throw ' + @expression.compile(o) + ';'
|
|
834
|
+
|
|
835
|
+
}
|
|
836
|
+
|
|
837
|
+
statement ThrowNode, true
|
|
838
|
+
|
|
839
|
+
# Check an expression for existence (meaning not null or undefined).
|
|
840
|
+
ExistenceNode: exports.ExistenceNode: inherit Node, {
|
|
841
|
+
type: 'Existence'
|
|
842
|
+
|
|
843
|
+
constructor: (expression) ->
|
|
844
|
+
@children: [@expression: expression]
|
|
845
|
+
this
|
|
846
|
+
|
|
847
|
+
compile_node: (o) ->
|
|
848
|
+
ExistenceNode.compile_test(o, @expression)
|
|
849
|
+
|
|
850
|
+
}
|
|
851
|
+
|
|
852
|
+
ExistenceNode.compile_test: (o, variable) ->
|
|
853
|
+
[first, second]: [variable, variable]
|
|
854
|
+
if variable instanceof CallNode or (variable instanceof ValueNode and variable.has_properties())
|
|
855
|
+
[first, second]: variable.compile_reference(o)
|
|
856
|
+
'(typeof ' + first.compile(o) + ' !== "undefined" && ' + second.compile(o) + ' !== null)'
|
|
857
|
+
|
|
858
|
+
# An extra set of parentheses, specified explicitly in the source.
|
|
859
|
+
ParentheticalNode: exports.ParentheticalNode: inherit Node, {
|
|
860
|
+
type: 'Paren'
|
|
861
|
+
|
|
862
|
+
constructor: (expression) ->
|
|
863
|
+
@children: [@expression: expression]
|
|
864
|
+
this
|
|
865
|
+
|
|
866
|
+
is_statement: ->
|
|
867
|
+
@expression.is_statement()
|
|
868
|
+
|
|
869
|
+
compile_node: (o) ->
|
|
870
|
+
code: @expression.compile(o)
|
|
871
|
+
return code if @is_statement()
|
|
872
|
+
l: code.length
|
|
873
|
+
code: code.substr(o, l-1) if code.substr(l-1, 1) is ';'
|
|
874
|
+
'(' + code + ')'
|
|
875
|
+
|
|
876
|
+
}
|
|
877
|
+
|
|
878
|
+
# The replacement for the for loop is an array comprehension (that compiles)
|
|
879
|
+
# into a for loop. Also acts as an expression, able to return the result
|
|
880
|
+
# of the comprehenion. Unlike Python array comprehensions, it's able to pass
|
|
881
|
+
# the current index of the loop as a second parameter.
|
|
882
|
+
ForNode: exports.ForNode: inherit Node, {
|
|
883
|
+
type: 'For'
|
|
884
|
+
|
|
885
|
+
constructor: (body, source, name, index) ->
|
|
886
|
+
@body: body
|
|
887
|
+
@name: name
|
|
888
|
+
@index: index or null
|
|
889
|
+
@source: source.source
|
|
890
|
+
@filter: source.filter
|
|
891
|
+
@step: source.step
|
|
892
|
+
@object: !!source.object
|
|
893
|
+
[@name, @index]: [@index, @name] if @object
|
|
894
|
+
@children: compact [@body, @source, @filter]
|
|
895
|
+
this
|
|
896
|
+
|
|
897
|
+
top_sensitive: ->
|
|
898
|
+
true
|
|
899
|
+
|
|
900
|
+
compile_node: (o) ->
|
|
901
|
+
top_level: del(o, 'top') and not o.returns
|
|
902
|
+
range: @source instanceof ValueNode and @source.base instanceof RangeNode and not @source.properties.length
|
|
903
|
+
source: if range then @source.base else @source
|
|
904
|
+
scope: o.scope
|
|
905
|
+
name: @name and @name.compile(o)
|
|
906
|
+
index: @index and @index.compile(o)
|
|
907
|
+
name_found: name and scope.find(name)
|
|
908
|
+
index_found: index and scope.find(index)
|
|
909
|
+
body_dent: @idt(1)
|
|
910
|
+
rvar: scope.free_variable() unless top_level
|
|
911
|
+
svar: scope.free_variable()
|
|
912
|
+
ivar: if range then name else index or scope.free_variable()
|
|
913
|
+
var_part: ''
|
|
914
|
+
body: Expressions.wrap([@body])
|
|
915
|
+
if range
|
|
916
|
+
index_var: scope.free_variable()
|
|
917
|
+
source_part: source.compile_variables(o)
|
|
918
|
+
for_part: index_var + ' = 0, ' + source.compile(merge(o, {index: ivar, step: @step})) + ', ' + index_var + '++'
|
|
919
|
+
else
|
|
920
|
+
index_var: null
|
|
921
|
+
source_part: svar + ' = ' + @source.compile(o) + ';\n' + @idt()
|
|
922
|
+
step_part: if @step then ivar + ' += ' + @step.compile(o) else ivar + '++'
|
|
923
|
+
for_part: ivar + ' = 0; ' + ivar + ' < ' + svar + '.length; ' + step_part
|
|
924
|
+
var_part: body_dent + name + ' = ' + svar + '[' + ivar + '];\n' if name
|
|
925
|
+
set_result: if rvar then @idt() + rvar + ' = []; ' else @idt()
|
|
926
|
+
return_result: rvar or ''
|
|
927
|
+
body: ClosureNode.wrap(body, true) if top_level and @contains (n) -> n instanceof CodeNode
|
|
928
|
+
body: PushNode.wrap(rvar, body) unless top_level
|
|
929
|
+
if o.returns
|
|
930
|
+
return_result: 'return ' + return_result
|
|
931
|
+
del o, 'returns'
|
|
932
|
+
body: new IfNode(@filter, body, null, {statement: true}) if @filter
|
|
933
|
+
else if @filter
|
|
934
|
+
body: Expressions.wrap([new IfNode(@filter, body)])
|
|
935
|
+
if @object
|
|
936
|
+
o.scope.assign('__hasProp', 'Object.prototype.hasOwnProperty', true)
|
|
937
|
+
for_part: ivar + ' in ' + svar + ') { if (__hasProp.call(' + svar + ', ' + ivar + ')'
|
|
938
|
+
return_result: '\n' + @idt() + return_result + ';' unless top_level
|
|
939
|
+
body: body.compile(merge(o, {indent: body_dent, top: true}))
|
|
940
|
+
vars: if range then name else name + ', ' + ivar
|
|
941
|
+
close: if @object then '}}\n' else '}\n'
|
|
942
|
+
set_result + source_part + 'for (' + for_part + ') {\n' + var_part + body + '\n' + @idt() + close + @idt() + return_result
|
|
943
|
+
|
|
944
|
+
}
|
|
945
|
+
|
|
946
|
+
statement ForNode
|
|
947
|
+
|
|
948
|
+
# If/else statements. Switch/whens get compiled into these. Acts as an
|
|
949
|
+
# expression by pushing down requested returns to the expression bodies.
|
|
950
|
+
# Single-expression IfNodes are compiled into ternary operators if possible,
|
|
951
|
+
# because ternaries are first-class returnable assignable expressions.
|
|
952
|
+
IfNode: exports.IfNode: inherit Node, {
|
|
953
|
+
type: 'If'
|
|
954
|
+
|
|
955
|
+
constructor: (condition, body, else_body, tags) ->
|
|
956
|
+
@condition: condition
|
|
957
|
+
@body: body and body.unwrap()
|
|
958
|
+
@else_body: else_body and else_body.unwrap()
|
|
959
|
+
@children: compact [@condition, @body, @else_body]
|
|
960
|
+
@tags: tags or {}
|
|
961
|
+
@multiple: true if @condition instanceof Array
|
|
962
|
+
@condition: new OpNode('!', new ParentheticalNode(@condition)) if @tags.invert
|
|
963
|
+
this
|
|
964
|
+
|
|
965
|
+
push: (else_body) ->
|
|
966
|
+
eb: else_body.unwrap()
|
|
967
|
+
if @else_body then @else_body.push(eb) else @else_body: eb
|
|
968
|
+
this
|
|
969
|
+
|
|
970
|
+
force_statement: ->
|
|
971
|
+
@tags.statement: true
|
|
972
|
+
this
|
|
973
|
+
|
|
974
|
+
# Tag a chain of IfNodes with their switch condition for equality.
|
|
975
|
+
rewrite_condition: (expression) ->
|
|
976
|
+
@switcher: expression
|
|
977
|
+
this
|
|
978
|
+
|
|
979
|
+
# Rewrite a chain of IfNodes with their switch condition for equality.
|
|
980
|
+
rewrite_switch: (o) ->
|
|
981
|
+
assigner: @switcher
|
|
982
|
+
if not (@switcher.unwrap() instanceof LiteralNode)
|
|
983
|
+
variable: new LiteralNode(o.scope.free_variable())
|
|
984
|
+
assigner: new AssignNode(variable, @switcher)
|
|
985
|
+
@switcher: variable
|
|
986
|
+
@condition: if @multiple
|
|
987
|
+
for cond, i in @condition
|
|
988
|
+
new OpNode('is', (if i is 0 then assigner else @switcher), cond)
|
|
989
|
+
else
|
|
990
|
+
new OpNode('is', assigner, @condition)
|
|
991
|
+
@else_body.rewrite_condition(@switcher) if @is_chain()
|
|
992
|
+
this
|
|
993
|
+
|
|
994
|
+
# Rewrite a chain of IfNodes to add a default case as the final else.
|
|
995
|
+
add_else: (exprs, statement) ->
|
|
996
|
+
if @is_chain()
|
|
997
|
+
@else_body.add_else exprs, statement
|
|
998
|
+
else
|
|
999
|
+
exprs: exprs.unwrap() unless statement
|
|
1000
|
+
@children.push @else_body: exprs
|
|
1001
|
+
this
|
|
1002
|
+
|
|
1003
|
+
# If the else_body is an IfNode itself, then we've got an if-else chain.
|
|
1004
|
+
is_chain: ->
|
|
1005
|
+
@chain ||= @else_body and @else_body instanceof IfNode
|
|
1006
|
+
|
|
1007
|
+
# The IfNode only compiles into a statement if either of the bodies needs
|
|
1008
|
+
# to be a statement.
|
|
1009
|
+
is_statement: ->
|
|
1010
|
+
@statement ||= !!(@comment or @tags.statement or @body.is_statement() or (@else_body and @else_body.is_statement()))
|
|
1011
|
+
|
|
1012
|
+
compile_condition: (o) ->
|
|
1013
|
+
(cond.compile(o) for cond in flatten([@condition])).join(' || ')
|
|
1014
|
+
|
|
1015
|
+
compile_node: (o) ->
|
|
1016
|
+
if @is_statement() then @compile_statement(o) else @compile_ternary(o)
|
|
1017
|
+
|
|
1018
|
+
# Compile the IfNode as a regular if-else statement. Flattened chains
|
|
1019
|
+
# force sub-else bodies into statement form.
|
|
1020
|
+
compile_statement: (o) ->
|
|
1021
|
+
@rewrite_switch(o) if @switcher
|
|
1022
|
+
child: del o, 'chain_child'
|
|
1023
|
+
cond_o: merge o
|
|
1024
|
+
del cond_o, 'returns'
|
|
1025
|
+
o.indent: @idt(1)
|
|
1026
|
+
o.top: true
|
|
1027
|
+
if_dent: if child then '' else @idt()
|
|
1028
|
+
com_dent: if child then @idt() else ''
|
|
1029
|
+
prefix: if @comment then @comment.compile(cond_o) + '\n' + com_dent else ''
|
|
1030
|
+
body: Expressions.wrap([@body]).compile(o)
|
|
1031
|
+
if_part: prefix + if_dent + 'if (' + @compile_condition(cond_o) + ') {\n' + body + '\n' + @idt() + '}'
|
|
1032
|
+
return if_part unless @else_body
|
|
1033
|
+
else_part: if @is_chain()
|
|
1034
|
+
' else ' + @else_body.compile(merge(o, {indent: @idt(), chain_child: true}))
|
|
1035
|
+
else
|
|
1036
|
+
' else {\n' + Expressions.wrap([@else_body]).compile(o) + '\n' + @idt() + '}'
|
|
1037
|
+
if_part + else_part
|
|
1038
|
+
|
|
1039
|
+
# Compile the IfNode into a ternary operator.
|
|
1040
|
+
compile_ternary: (o) ->
|
|
1041
|
+
if_part: @condition.compile(o) + ' ? ' + @body.compile(o)
|
|
1042
|
+
else_part: if @else_body then @else_body.compile(o) else 'null'
|
|
1043
|
+
if_part + ' : ' + else_part
|
|
1044
|
+
|
|
1045
|
+
}
|