spade-packager 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (316) hide show
  1. data/.gitignore +2 -0
  2. data/.gitmodules +6 -0
  3. data/bin/spadepkg +8 -0
  4. data/lib/libgems_ext.rb +8 -0
  5. data/lib/libgems_ext/config_file.rb +33 -0
  6. data/lib/libgems_ext/dependency_installer.rb +150 -0
  7. data/lib/libgems_ext/installer.rb +39 -0
  8. data/lib/libgems_ext/libgems.rb +39 -0
  9. data/lib/libgems_ext/spec_fetcher.rb +11 -0
  10. data/lib/spade-packager.rb +1 -0
  11. data/lib/spade/packager.rb +18 -0
  12. data/lib/spade/packager/cli.rb +9 -0
  13. data/lib/spade/packager/cli/base.rb +196 -0
  14. data/lib/spade/packager/cli/owner.rb +46 -0
  15. data/lib/spade/packager/cli/project_generator.rb +117 -0
  16. data/lib/spade/packager/credentials.rb +38 -0
  17. data/lib/spade/packager/local.rb +50 -0
  18. data/lib/spade/packager/package.rb +160 -0
  19. data/lib/spade/packager/remote.rb +98 -0
  20. data/lib/spade/packager/repository.rb +18 -0
  21. data/lib/spade/packager/version.rb +5 -0
  22. data/packages/coffee-script/.gitignore +8 -0
  23. data/packages/coffee-script/.npmignore +11 -0
  24. data/packages/coffee-script/Cakefile +229 -0
  25. data/packages/coffee-script/LICENSE +22 -0
  26. data/packages/coffee-script/README +47 -0
  27. data/packages/coffee-script/Rakefile +78 -0
  28. data/packages/coffee-script/bin/cake +7 -0
  29. data/packages/coffee-script/bin/coffee +7 -0
  30. data/packages/coffee-script/documentation/coffee/aliases.coffee +11 -0
  31. data/packages/coffee-script/documentation/coffee/array_comprehensions.coffee +2 -0
  32. data/packages/coffee-script/documentation/coffee/block_comment.coffee +6 -0
  33. data/packages/coffee-script/documentation/coffee/cake_tasks.coffee +9 -0
  34. data/packages/coffee-script/documentation/coffee/classes.coffee +25 -0
  35. data/packages/coffee-script/documentation/coffee/comparisons.coffee +5 -0
  36. data/packages/coffee-script/documentation/coffee/conditionals.coffee +13 -0
  37. data/packages/coffee-script/documentation/coffee/default_args.coffee +8 -0
  38. data/packages/coffee-script/documentation/coffee/do.coffee +4 -0
  39. data/packages/coffee-script/documentation/coffee/embedded.coffee +5 -0
  40. data/packages/coffee-script/documentation/coffee/existence.coffee +10 -0
  41. data/packages/coffee-script/documentation/coffee/expressions.coffee +9 -0
  42. data/packages/coffee-script/documentation/coffee/expressions_assignment.coffee +3 -0
  43. data/packages/coffee-script/documentation/coffee/expressions_comprehension.coffee +3 -0
  44. data/packages/coffee-script/documentation/coffee/expressions_try.coffee +7 -0
  45. data/packages/coffee-script/documentation/coffee/fat_arrow.coffee +6 -0
  46. data/packages/coffee-script/documentation/coffee/functions.coffee +2 -0
  47. data/packages/coffee-script/documentation/coffee/heredocs.coffee +7 -0
  48. data/packages/coffee-script/documentation/coffee/heregexes.coffee +11 -0
  49. data/packages/coffee-script/documentation/coffee/interpolation.coffee +6 -0
  50. data/packages/coffee-script/documentation/coffee/multiple_return_values.coffee +7 -0
  51. data/packages/coffee-script/documentation/coffee/object_comprehensions.coffee +4 -0
  52. data/packages/coffee-script/documentation/coffee/object_extraction.coffee +13 -0
  53. data/packages/coffee-script/documentation/coffee/objects_and_arrays.coffee +19 -0
  54. data/packages/coffee-script/documentation/coffee/objects_reserved.coffee +5 -0
  55. data/packages/coffee-script/documentation/coffee/overview.coffee +28 -0
  56. data/packages/coffee-script/documentation/coffee/parallel_assignment.coffee +6 -0
  57. data/packages/coffee-script/documentation/coffee/patterns_and_splats.coffee +7 -0
  58. data/packages/coffee-script/documentation/coffee/prototypes.coffee +3 -0
  59. data/packages/coffee-script/documentation/coffee/range_comprehensions.coffee +2 -0
  60. data/packages/coffee-script/documentation/coffee/scope.coffee +5 -0
  61. data/packages/coffee-script/documentation/coffee/slices.coffee +7 -0
  62. data/packages/coffee-script/documentation/coffee/soaks.coffee +1 -0
  63. data/packages/coffee-script/documentation/coffee/splats.coffee +27 -0
  64. data/packages/coffee-script/documentation/coffee/splices.coffee +5 -0
  65. data/packages/coffee-script/documentation/coffee/strings.coffee +8 -0
  66. data/packages/coffee-script/documentation/coffee/switch.coffee +10 -0
  67. data/packages/coffee-script/documentation/coffee/try.coffee +8 -0
  68. data/packages/coffee-script/documentation/coffee/while.coffee +10 -0
  69. data/packages/coffee-script/documentation/css/docs.css +374 -0
  70. data/packages/coffee-script/documentation/css/idle.css +64 -0
  71. data/packages/coffee-script/documentation/docs/browser.html +25 -0
  72. data/packages/coffee-script/documentation/docs/cake.html +43 -0
  73. data/packages/coffee-script/documentation/docs/coffee-script.html +51 -0
  74. data/packages/coffee-script/documentation/docs/command.html +161 -0
  75. data/packages/coffee-script/documentation/docs/docco.css +186 -0
  76. data/packages/coffee-script/documentation/docs/grammar.html +399 -0
  77. data/packages/coffee-script/documentation/docs/helpers.html +31 -0
  78. data/packages/coffee-script/documentation/docs/index.html +3 -0
  79. data/packages/coffee-script/documentation/docs/lexer.html +490 -0
  80. data/packages/coffee-script/documentation/docs/nodes.html +1338 -0
  81. data/packages/coffee-script/documentation/docs/optparse.html +78 -0
  82. data/packages/coffee-script/documentation/docs/repl.html +24 -0
  83. data/packages/coffee-script/documentation/docs/rewriter.html +251 -0
  84. data/packages/coffee-script/documentation/docs/scope.html +54 -0
  85. data/packages/coffee-script/documentation/docs/underscore.html +295 -0
  86. data/packages/coffee-script/documentation/images/background.png +0 -0
  87. data/packages/coffee-script/documentation/images/banding.png +0 -0
  88. data/packages/coffee-script/documentation/images/button_bg.png +0 -0
  89. data/packages/coffee-script/documentation/images/button_bg_dark.gif +0 -0
  90. data/packages/coffee-script/documentation/images/button_bg_green.gif +0 -0
  91. data/packages/coffee-script/documentation/images/favicon.ico +0 -0
  92. data/packages/coffee-script/documentation/images/logo.png +0 -0
  93. data/packages/coffee-script/documentation/images/screenshadow.png +0 -0
  94. data/packages/coffee-script/documentation/index.html.erb +1607 -0
  95. data/packages/coffee-script/documentation/js/aliases.js +17 -0
  96. data/packages/coffee-script/documentation/js/array_comprehensions.js +6 -0
  97. data/packages/coffee-script/documentation/js/block_comment.js +4 -0
  98. data/packages/coffee-script/documentation/js/cake_tasks.js +10 -0
  99. data/packages/coffee-script/documentation/js/classes.js +44 -0
  100. data/packages/coffee-script/documentation/js/comparisons.js +3 -0
  101. data/packages/coffee-script/documentation/js/conditionals.js +12 -0
  102. data/packages/coffee-script/documentation/js/default_args.js +7 -0
  103. data/packages/coffee-script/documentation/js/do.js +10 -0
  104. data/packages/coffee-script/documentation/js/embedded.js +4 -0
  105. data/packages/coffee-script/documentation/js/existence.js +6 -0
  106. data/packages/coffee-script/documentation/js/expressions.js +15 -0
  107. data/packages/coffee-script/documentation/js/expressions_assignment.js +2 -0
  108. data/packages/coffee-script/documentation/js/expressions_comprehension.js +9 -0
  109. data/packages/coffee-script/documentation/js/expressions_try.js +7 -0
  110. data/packages/coffee-script/documentation/js/fat_arrow.js +9 -0
  111. data/packages/coffee-script/documentation/js/functions.js +7 -0
  112. data/packages/coffee-script/documentation/js/heredocs.js +2 -0
  113. data/packages/coffee-script/documentation/js/heregexes.js +2 -0
  114. data/packages/coffee-script/documentation/js/interpolation.js +4 -0
  115. data/packages/coffee-script/documentation/js/multiple_return_values.js +5 -0
  116. data/packages/coffee-script/documentation/js/object_comprehensions.js +15 -0
  117. data/packages/coffee-script/documentation/js/object_extraction.js +10 -0
  118. data/packages/coffee-script/documentation/js/objects_and_arrays.js +17 -0
  119. data/packages/coffee-script/documentation/js/objects_reserved.js +4 -0
  120. data/packages/coffee-script/documentation/js/overview.js +35 -0
  121. data/packages/coffee-script/documentation/js/parallel_assignment.js +4 -0
  122. data/packages/coffee-script/documentation/js/patterns_and_splats.js +4 -0
  123. data/packages/coffee-script/documentation/js/prototypes.js +3 -0
  124. data/packages/coffee-script/documentation/js/range_comprehensions.js +9 -0
  125. data/packages/coffee-script/documentation/js/scope.js +8 -0
  126. data/packages/coffee-script/documentation/js/slices.js +4 -0
  127. data/packages/coffee-script/documentation/js/soaks.js +2 -0
  128. data/packages/coffee-script/documentation/js/splats.js +15 -0
  129. data/packages/coffee-script/documentation/js/splices.js +3 -0
  130. data/packages/coffee-script/documentation/js/strings.js +2 -0
  131. data/packages/coffee-script/documentation/js/switch.js +23 -0
  132. data/packages/coffee-script/documentation/js/try.js +8 -0
  133. data/packages/coffee-script/documentation/js/while.js +18 -0
  134. data/packages/coffee-script/documentation/vendor/jquery-1.4.2.js +6240 -0
  135. data/packages/coffee-script/examples/beautiful_code/binary_search.coffee +16 -0
  136. data/packages/coffee-script/examples/beautiful_code/quicksort_runtime.coffee +13 -0
  137. data/packages/coffee-script/examples/beautiful_code/regular_expression_matcher.coffee +34 -0
  138. data/packages/coffee-script/examples/blocks.coffee +54 -0
  139. data/packages/coffee-script/examples/code.coffee +167 -0
  140. data/packages/coffee-script/examples/computer_science/README +4 -0
  141. data/packages/coffee-script/examples/computer_science/binary_search.coffee +25 -0
  142. data/packages/coffee-script/examples/computer_science/bubble_sort.coffee +11 -0
  143. data/packages/coffee-script/examples/computer_science/linked_list.coffee +108 -0
  144. data/packages/coffee-script/examples/computer_science/luhn_algorithm.coffee +36 -0
  145. data/packages/coffee-script/examples/computer_science/merge_sort.coffee +19 -0
  146. data/packages/coffee-script/examples/computer_science/selection_sort.coffee +23 -0
  147. data/packages/coffee-script/examples/poignant.coffee +181 -0
  148. data/packages/coffee-script/examples/potion.coffee +206 -0
  149. data/packages/coffee-script/examples/underscore.coffee +682 -0
  150. data/packages/coffee-script/examples/web_server.coffee +12 -0
  151. data/packages/coffee-script/extras/EXTRAS +7 -0
  152. data/packages/coffee-script/extras/coffee-script.js +8 -0
  153. data/packages/coffee-script/extras/jsl.conf +44 -0
  154. data/packages/coffee-script/index.html +2515 -0
  155. data/packages/coffee-script/lib/browser.js +52 -0
  156. data/packages/coffee-script/lib/cake.js +76 -0
  157. data/packages/coffee-script/lib/coffee-script.js +82 -0
  158. data/packages/coffee-script/lib/command.js +263 -0
  159. data/packages/coffee-script/lib/grammar.js +581 -0
  160. data/packages/coffee-script/lib/helpers.js +66 -0
  161. data/packages/coffee-script/lib/index.js +8 -0
  162. data/packages/coffee-script/lib/lexer.js +633 -0
  163. data/packages/coffee-script/lib/nodes.js +2165 -0
  164. data/packages/coffee-script/lib/optparse.js +111 -0
  165. data/packages/coffee-script/lib/parser.js +649 -0
  166. data/packages/coffee-script/lib/repl.js +42 -0
  167. data/packages/coffee-script/lib/rewriter.js +353 -0
  168. data/packages/coffee-script/lib/scope.js +120 -0
  169. data/packages/coffee-script/lib/spade-format.js +45 -0
  170. data/packages/coffee-script/package.json +26 -0
  171. data/packages/coffee-script/src/browser.coffee +43 -0
  172. data/packages/coffee-script/src/cake.coffee +69 -0
  173. data/packages/coffee-script/src/coffee-script.coffee +92 -0
  174. data/packages/coffee-script/src/command.coffee +214 -0
  175. data/packages/coffee-script/src/grammar.coffee +590 -0
  176. data/packages/coffee-script/src/helpers.coffee +56 -0
  177. data/packages/coffee-script/src/index.coffee +2 -0
  178. data/packages/coffee-script/src/lexer.coffee +653 -0
  179. data/packages/coffee-script/src/nodes.coffee +1754 -0
  180. data/packages/coffee-script/src/optparse.coffee +99 -0
  181. data/packages/coffee-script/src/repl.coffee +42 -0
  182. data/packages/coffee-script/src/rewriter.coffee +326 -0
  183. data/packages/coffee-script/src/scope.coffee +94 -0
  184. data/packages/coffee-script/test/arguments.coffee +127 -0
  185. data/packages/coffee-script/test/assignment.coffee +98 -0
  186. data/packages/coffee-script/test/break.coffee +18 -0
  187. data/packages/coffee-script/test/comments.coffee +201 -0
  188. data/packages/coffee-script/test/conditionals.coffee +181 -0
  189. data/packages/coffee-script/test/exception_handling.coffee +90 -0
  190. data/packages/coffee-script/test/helpers.coffee +96 -0
  191. data/packages/coffee-script/test/importing.coffee +18 -0
  192. data/packages/coffee-script/test/operators.coffee +225 -0
  193. data/packages/coffee-script/test/ranges_slices_and_splices.coffee +186 -0
  194. data/packages/coffee-script/test/regular_expressions.coffee +56 -0
  195. data/packages/coffee-script/test/test.html +123 -0
  196. data/packages/coffee-script/test/test_chaining.coffee +77 -0
  197. data/packages/coffee-script/test/test_classes.coffee +372 -0
  198. data/packages/coffee-script/test/test_compilation.coffee +26 -0
  199. data/packages/coffee-script/test/test_comprehensions.coffee +318 -0
  200. data/packages/coffee-script/test/test_existence.coffee +165 -0
  201. data/packages/coffee-script/test/test_functions.coffee +379 -0
  202. data/packages/coffee-script/test/test_heredocs.coffee +111 -0
  203. data/packages/coffee-script/test/test_literals.coffee +270 -0
  204. data/packages/coffee-script/test/test_option_parser.coffee +27 -0
  205. data/packages/coffee-script/test/test_pattern_matching.coffee +162 -0
  206. data/packages/coffee-script/test/test_returns.coffee +63 -0
  207. data/packages/coffee-script/test/test_splats.coffee +102 -0
  208. data/packages/coffee-script/test/test_strings.coffee +118 -0
  209. data/packages/coffee-script/test/test_switch.coffee +103 -0
  210. data/packages/coffee-script/test/test_while.coffee +71 -0
  211. data/packages/ivory/LICENSE.txt +1 -0
  212. data/packages/ivory/README.md +19 -0
  213. data/packages/ivory/lib/buffer.js +111 -0
  214. data/packages/ivory/lib/events.js +137 -0
  215. data/packages/ivory/lib/fs.js +266 -0
  216. data/packages/ivory/lib/main.js +13 -0
  217. data/packages/ivory/lib/path.js +158 -0
  218. data/packages/ivory/lib/ruby/buffer.rb +145 -0
  219. data/packages/ivory/lib/ruby/constants.rb +585 -0
  220. data/packages/ivory/lib/ruby/events.rb +32 -0
  221. data/packages/ivory/lib/ruby/fs.rb +245 -0
  222. data/packages/ivory/lib/ruby/process.rb +28 -0
  223. data/packages/ivory/lib/stream.js +115 -0
  224. data/packages/ivory/lib/util.js +414 -0
  225. data/packages/ivory/package.json +11 -0
  226. data/packages/ivory/spade-boot.js +78 -0
  227. data/packages/jquery/main.js +7179 -0
  228. data/packages/jquery/package.json +10 -0
  229. data/packages/json/lib/main.js +14 -0
  230. data/packages/json/package.json +8 -0
  231. data/packages/lproj/README.md +77 -0
  232. data/packages/lproj/examples/demo-app/en.lproj/localized.strings +2 -0
  233. data/packages/lproj/examples/demo-app/fr.lproj/localized.strings +3 -0
  234. data/packages/lproj/examples/demo-app/index.html +8 -0
  235. data/packages/lproj/examples/demo-app/lib/main.js +7 -0
  236. data/packages/lproj/examples/demo-app/package.json +9 -0
  237. data/packages/lproj/lib/main.js +78 -0
  238. data/packages/lproj/lib/strings-format.js +6 -0
  239. data/packages/lproj/package.json +9 -0
  240. data/packages/optparse/README.md +161 -0
  241. data/packages/optparse/TODO +1 -0
  242. data/packages/optparse/examples/browser-test.html +75 -0
  243. data/packages/optparse/examples/nodejs-test.js +90 -0
  244. data/packages/optparse/lib/optparse.js +309 -0
  245. data/packages/optparse/package.json +13 -0
  246. data/packages/optparse/seed.yml +5 -0
  247. data/packages/text/lib/main.js +8 -0
  248. data/packages/text/package.json +9 -0
  249. data/packages/web-file/README.md +7 -0
  250. data/packages/web-file/lib/errors.js +32 -0
  251. data/packages/web-file/lib/file-reader.js +10 -0
  252. data/packages/web-file/lib/file-system.js +234 -0
  253. data/packages/web-file/lib/file-writer.js +10 -0
  254. data/packages/web-file/lib/file.js +9 -0
  255. data/packages/web-file/lib/main.js +34 -0
  256. data/packages/web-file/lib/platform.js +25 -0
  257. data/packages/web-file/lib/ruby/file.rb +252 -0
  258. data/packages/web-file/lib/ruby/file_reader.rb +69 -0
  259. data/packages/web-file/lib/ruby/file_system.rb +134 -0
  260. data/packages/web-file/lib/ruby/file_writer.rb +78 -0
  261. data/packages/web-file/package.json +12 -0
  262. data/packages/web-typed-array/README.md +7 -0
  263. data/packages/web-typed-array/lib/array-buffer-view.js +9 -0
  264. data/packages/web-typed-array/lib/array-buffer.js +7 -0
  265. data/packages/web-typed-array/lib/main.js +33 -0
  266. data/packages/web-typed-array/lib/platform.js +20 -0
  267. data/packages/web-typed-array/lib/ruby/array_buffer.rb +31 -0
  268. data/packages/web-typed-array/lib/ruby/array_buffer_view.rb +130 -0
  269. data/packages/web-typed-array/lib/ruby/typed_array.rb +133 -0
  270. data/packages/web-typed-array/lib/typed-array.js +26 -0
  271. data/packages/web-typed-array/package.json +9 -0
  272. data/spade-packager.gemspec +39 -0
  273. data/spec/cli/build_spec.rb +57 -0
  274. data/spec/cli/install_spec.rb +119 -0
  275. data/spec/cli/installed_spec.rb +55 -0
  276. data/spec/cli/list_spec.rb +74 -0
  277. data/spec/cli/login_spec.rb +75 -0
  278. data/spec/cli/new_spec.rb +5 -0
  279. data/spec/cli/owner_spec.rb +114 -0
  280. data/spec/cli/push_spec.rb +73 -0
  281. data/spec/cli/uninstall_spec.rb +58 -0
  282. data/spec/cli/unpack_spec.rb +72 -0
  283. data/spec/cli/unyank_spec.rb +73 -0
  284. data/spec/cli/yank_spec.rb +73 -0
  285. data/spec/credentials_spec.rb +23 -0
  286. data/spec/fixtures/badrake-0.8.7.spd +0 -0
  287. data/spec/fixtures/builder-3.0.0.spd +0 -0
  288. data/spec/fixtures/bundler-1.1.pre.spd +0 -0
  289. data/spec/fixtures/coffee-1.0.1.pre.spd +0 -0
  290. data/spec/fixtures/core-test-0.4.3.spd +0 -0
  291. data/spec/fixtures/core-test/bin/cot +3 -0
  292. data/spec/fixtures/core-test/lib/main.js +1 -0
  293. data/spec/fixtures/core-test/resources/runner.css +0 -0
  294. data/spec/fixtures/core-test/tests/test.js +1 -0
  295. data/spec/fixtures/highline-1.6.1.spd +0 -0
  296. data/spec/fixtures/ivory-0.0.1.spd +0 -0
  297. data/spec/fixtures/jquery-1.4.3.spd +0 -0
  298. data/spec/fixtures/optparse-1.0.1.spd +0 -0
  299. data/spec/fixtures/package.json +30 -0
  300. data/spec/fixtures/rake-0.8.6.spd +0 -0
  301. data/spec/fixtures/rake-0.8.7.spd +0 -0
  302. data/spec/gauntlet_spec.rb +27 -0
  303. data/spec/package_spec.rb +267 -0
  304. data/spec/spec_helper.rb +32 -0
  305. data/spec/support/cli.rb +103 -0
  306. data/spec/support/fake.rb +48 -0
  307. data/spec/support/fake_gem_server.rb +67 -0
  308. data/spec/support/fake_gemcutter.rb +50 -0
  309. data/spec/support/matchers.rb +32 -0
  310. data/spec/support/path.rb +61 -0
  311. data/templates/project/LICENSE +19 -0
  312. data/templates/project/README.md +21 -0
  313. data/templates/project/lib/main.js +9 -0
  314. data/templates/project/project.json +31 -0
  315. data/templates/project/tests/main-test.js +8 -0
  316. metadata +484 -0
@@ -0,0 +1,1754 @@
1
+ # `nodes.coffee` contains all of the node classes for the syntax tree. Most
2
+ # nodes are created as the result of actions in the [grammar](grammar.html),
3
+ # but some are created by other nodes as a method of code generation. To convert
4
+ # the syntax tree into a string of JavaScript code, call `compile()` on the root.
5
+
6
+ {Scope} = require './scope'
7
+
8
+ # Import the helpers we plan to use.
9
+ {compact, flatten, extend, merge, del, starts, ends, last} = require './helpers'
10
+
11
+ exports.extend = extend # for parser
12
+
13
+ # Constant functions for nodes that don't need customization.
14
+ YES = -> yes
15
+ NO = -> no
16
+ THIS = -> this
17
+ NEGATE = -> @negated = not @negated; this
18
+
19
+ #### Base
20
+
21
+ # The **Base** is the abstract base class for all nodes in the syntax tree.
22
+ # Each subclass implements the `compileNode` method, which performs the
23
+ # code generation for that node. To compile a node to JavaScript,
24
+ # call `compile` on it, which wraps `compileNode` in some generic extra smarts,
25
+ # to know when the generated code needs to be wrapped up in a closure.
26
+ # An options hash is passed and cloned throughout, containing information about
27
+ # the environment from higher in the tree (such as if a returned value is
28
+ # being requested by the surrounding function), information about the current
29
+ # scope, and indentation level.
30
+ exports.Base = class Base
31
+
32
+ # Common logic for determining whether to wrap this node in a closure before
33
+ # compiling it, or to compile directly. We need to wrap if this node is a
34
+ # *statement*, and it's not a *pureStatement*, and we're not at
35
+ # the top level of a block (which would be unnecessary), and we haven't
36
+ # already been asked to return the result (because statements know how to
37
+ # return results).
38
+ compile: (o, lvl) ->
39
+ o = extend {}, o
40
+ o.level = lvl if lvl
41
+ node = @unfoldSoak(o) or this
42
+ node.tab = o.indent
43
+ if o.level is LEVEL_TOP or not node.isStatement(o)
44
+ node.compileNode o
45
+ else
46
+ node.compileClosure o
47
+
48
+ # Statements converted into expressions via closure-wrapping share a scope
49
+ # object with their parent closure, to preserve the expected lexical scope.
50
+ compileClosure: (o) ->
51
+ if @jumps()
52
+ throw SyntaxError 'cannot use a pure statement in an expression.'
53
+ o.sharedScope = yes
54
+ Closure.wrap(this).compileNode o
55
+
56
+ # If the code generation wishes to use the result of a complex expression
57
+ # in multiple places, ensure that the expression is only ever evaluated once,
58
+ # by assigning it to a temporary variable. Pass a level to precompile.
59
+ cache: (o, level, reused) ->
60
+ unless @isComplex()
61
+ ref = if level then @compile o, level else this
62
+ [ref, ref]
63
+ else
64
+ ref = new Literal reused or o.scope.freeVariable 'ref'
65
+ sub = new Assign ref, this
66
+ if level then [sub.compile(o, level), ref.value] else [sub, ref]
67
+
68
+ # Compile to a source/variable pair suitable for looping.
69
+ compileLoopReference: (o, name) ->
70
+ src = tmp = @compile o, LEVEL_LIST
71
+ unless -Infinity < +src < Infinity or IDENTIFIER.test(src) and o.scope.check(src, yes)
72
+ src = "#{ tmp = o.scope.freeVariable name } = #{src}"
73
+ [src, tmp]
74
+
75
+ # Construct a node that returns the current node's result.
76
+ # Note that this is overridden for smarter behavior for
77
+ # many statement nodes (e.g. If, For)...
78
+ makeReturn: ->
79
+ new Return this
80
+
81
+ # Does this node, or any of its children, contain a node of a certain kind?
82
+ # Recursively traverses down the *children* of the nodes, yielding to a block
83
+ # and returning true when the block finds a match. `contains` does not cross
84
+ # scope boundaries.
85
+ contains: (pred) ->
86
+ contains = no
87
+ @traverseChildren no, (node) ->
88
+ if pred node
89
+ contains = yes
90
+ return no
91
+ contains
92
+
93
+ # Is this node of a certain type, or does it contain the type?
94
+ containsType: (type) ->
95
+ this instanceof type or @contains (node) -> node instanceof type
96
+
97
+ # Pull out the last non-comment node of a node list.
98
+ lastNonComment: (list) ->
99
+ i = list.length
100
+ return list[i] while i-- when list[i] not instanceof Comment
101
+ null
102
+
103
+ # `toString` representation of the node, for inspecting the parse tree.
104
+ # This is what `coffee --nodes` prints out.
105
+ toString: (idt = '', name = @constructor.name) ->
106
+ tree = '\n' + idt + name
107
+ tree += '?' if @soak
108
+ @eachChild (node) -> tree += node.toString idt + TAB
109
+ tree
110
+
111
+ # Passes each child to a function, breaking when the function returns `false`.
112
+ eachChild: (func) ->
113
+ return this unless @children
114
+ for attr in @children when @[attr]
115
+ for child in flatten [@[attr]]
116
+ return this if func(child) is false
117
+ this
118
+
119
+ traverseChildren: (crossScope, func) ->
120
+ @eachChild (child) ->
121
+ return false if func(child) is false
122
+ child.traverseChildren crossScope, func
123
+
124
+ invert: ->
125
+ new Op '!', this
126
+
127
+ unwrapAll: ->
128
+ node = this
129
+ continue until node is node = node.unwrap()
130
+ node
131
+
132
+ # Default implementations of the common node properties and methods. Nodes
133
+ # will override these with custom logic, if needed.
134
+ children: []
135
+
136
+ isStatement : NO
137
+ jumps : NO
138
+ isComplex : YES
139
+ isChainable : NO
140
+ isAssignable : NO
141
+
142
+ unwrap : THIS
143
+ unfoldSoak : NO
144
+
145
+ # Is this node used to assign a certain variable?
146
+ assigns: NO
147
+
148
+ #### Expressions
149
+
150
+ # The expressions body is the list of expressions that forms the body of an
151
+ # indented block of code -- the implementation of a function, a clause in an
152
+ # `if`, `switch`, or `try`, and so on...
153
+ exports.Expressions = class Expressions extends Base
154
+ constructor: (nodes) ->
155
+ @expressions = compact flatten nodes or []
156
+
157
+ children: ['expressions']
158
+
159
+ # Tack an expression on to the end of this expression list.
160
+ push: (node) ->
161
+ @expressions.push node
162
+ this
163
+
164
+ # Remove and return the last expression of this expression list.
165
+ pop: ->
166
+ @expressions.pop()
167
+
168
+ # Add an expression at the beginning of this expression list.
169
+ unshift: (node) ->
170
+ @expressions.unshift node
171
+ this
172
+
173
+ # If this Expressions consists of just a single node, unwrap it by pulling
174
+ # it back out.
175
+ unwrap: ->
176
+ if @expressions.length is 1 then @expressions[0] else this
177
+
178
+ # Is this an empty block of code?
179
+ isEmpty: ->
180
+ not @expressions.length
181
+
182
+ isStatement: (o) ->
183
+ for exp in @expressions when exp.isStatement o
184
+ return yes
185
+ no
186
+
187
+ jumps: (o) ->
188
+ for exp in @expressions
189
+ return exp if exp.jumps o
190
+
191
+ # An Expressions node does not return its entire body, rather it
192
+ # ensures that the final expression is returned.
193
+ makeReturn: ->
194
+ len = @expressions.length
195
+ while len--
196
+ expr = @expressions[len]
197
+ if expr not instanceof Comment
198
+ @expressions[len] = expr.makeReturn()
199
+ break
200
+ this
201
+
202
+ # An **Expressions** is the only node that can serve as the root.
203
+ compile: (o = {}, level) ->
204
+ if o.scope then super o, level else @compileRoot o
205
+
206
+ # Compile all expressions within the **Expressions** body. If we need to
207
+ # return the result, and it's an expression, simply return it. If it's a
208
+ # statement, ask the statement to do so.
209
+ compileNode: (o) ->
210
+ @tab = o.indent
211
+ top = o.level is LEVEL_TOP
212
+ codes = []
213
+ for node in @expressions
214
+ node = node.unwrapAll()
215
+ node = (node.unfoldSoak(o) or node)
216
+ if top
217
+ node.front = true
218
+ code = node.compile o
219
+ codes.push if node.isStatement o then code else @tab + code + ';'
220
+ else
221
+ codes.push node.compile o, LEVEL_LIST
222
+ return codes.join '\n' if top
223
+ code = codes.join(', ') or 'void 0'
224
+ if codes.length > 1 and o.level >= LEVEL_LIST then "(#{code})" else code
225
+
226
+ # If we happen to be the top-level **Expressions**, wrap everything in
227
+ # a safety closure, unless requested not to.
228
+ # It would be better not to generate them in the first place, but for now,
229
+ # clean up obvious double-parentheses.
230
+ compileRoot: (o) ->
231
+ o.indent = @tab = if o.bare then '' else TAB
232
+ o.scope = new Scope null, this, null
233
+ o.level = LEVEL_TOP
234
+ code = @compileWithDeclarations o
235
+ code = code.replace TRAILING_WHITESPACE, ''
236
+ if o.bare then code else "(function() {\n#{code}\n}).call(this);\n"
237
+
238
+ # Compile the expressions body for the contents of a function, with
239
+ # declarations of all inner variables pushed up to the top.
240
+ compileWithDeclarations: (o) ->
241
+ code = post = ''
242
+ for exp, i in @expressions
243
+ exp = exp.unwrap()
244
+ break unless exp instanceof Comment or exp instanceof Literal
245
+ o = merge(o, level: LEVEL_TOP)
246
+ if i
247
+ rest = @expressions.splice i, @expressions.length
248
+ code = @compileNode o
249
+ @expressions = rest
250
+ post = @compileNode o
251
+ {scope} = o
252
+ if scope.expressions is this
253
+ if not o.globals and o.scope.hasDeclarations()
254
+ code += "#{@tab}var #{ scope.declaredVariables().join(', ') };\n"
255
+ if scope.hasAssignments
256
+ code += "#{@tab}var #{ multident scope.assignedVariables().join(', '), @tab };\n"
257
+ code + post
258
+
259
+ # Wrap up the given nodes as an **Expressions**, unless it already happens
260
+ # to be one.
261
+ @wrap: (nodes) ->
262
+ return nodes[0] if nodes.length is 1 and nodes[0] instanceof Expressions
263
+ new Expressions nodes
264
+
265
+ #### Literal
266
+
267
+ # Literals are static values that can be passed through directly into
268
+ # JavaScript without translation, such as: strings, numbers,
269
+ # `true`, `false`, `null`...
270
+ exports.Literal = class Literal extends Base
271
+ constructor: (@value) ->
272
+
273
+ makeReturn: ->
274
+ if @isStatement() then this else new Return this
275
+
276
+ isAssignable: ->
277
+ IDENTIFIER.test @value
278
+
279
+ isStatement: ->
280
+ @value in ['break', 'continue', 'debugger']
281
+
282
+ isComplex: NO
283
+
284
+ assigns: (name) ->
285
+ name is @value
286
+
287
+ jumps: (o) ->
288
+ return no unless @isStatement()
289
+ if not (o and (o.loop or o.block and (@value isnt 'continue'))) then this else no
290
+
291
+ compileNode: (o) ->
292
+ code = if @value.reserved then "\"#{@value}\"" else @value
293
+ if @isStatement() then "#{@tab}#{code};" else code
294
+
295
+ toString: ->
296
+ ' "' + @value + '"'
297
+
298
+ #### Return
299
+
300
+ # A `return` is a *pureStatement* -- wrapping it in a closure wouldn't
301
+ # make sense.
302
+ exports.Return = class Return extends Base
303
+ constructor: (@expression) ->
304
+
305
+ children: ['expression']
306
+
307
+ isStatement: YES
308
+ makeReturn: THIS
309
+ jumps: THIS
310
+
311
+ compile: (o, level) ->
312
+ expr = @expression?.makeReturn()
313
+ if expr and expr not instanceof Return then expr.compile o, level else super o, level
314
+
315
+ compileNode: (o) ->
316
+ @tab + "return#{ if @expression then ' ' + @expression.compile(o, LEVEL_PAREN) else '' };"
317
+
318
+ #### Value
319
+
320
+ # A value, variable or literal or parenthesized, indexed or dotted into,
321
+ # or vanilla.
322
+ exports.Value = class Value extends Base
323
+ constructor: (base, props, tag) ->
324
+ return base if not props and base instanceof Value
325
+ @base = base
326
+ @properties = props or []
327
+ @[tag] = true if tag
328
+ return this
329
+
330
+ children: ['base', 'properties']
331
+
332
+ # Add a property access to the list.
333
+ push: (prop) ->
334
+ @properties.push prop
335
+ this
336
+
337
+ hasProperties: ->
338
+ !!@properties.length
339
+
340
+ # Some boolean checks for the benefit of other nodes.
341
+ isArray : -> not @properties.length and @base instanceof Arr
342
+ isComplex : -> @hasProperties() or @base.isComplex()
343
+ isAssignable : -> @hasProperties() or @base.isAssignable()
344
+ isSimpleNumber : -> @base instanceof Literal and SIMPLENUM.test @base.value
345
+ isAtomic : ->
346
+ for node in @properties.concat @base
347
+ return no if node.soak or node instanceof Call
348
+ yes
349
+
350
+ isStatement : (o) -> not @properties.length and @base.isStatement o
351
+ assigns : (name) -> not @properties.length and @base.assigns name
352
+ jumps : (o) -> not @properties.length and @base.jumps o
353
+
354
+ isObject: (onlyGenerated) ->
355
+ return no if @properties.length
356
+ (@base instanceof Obj) and (not onlyGenerated or @base.generated)
357
+
358
+ isSplice: ->
359
+ last(@properties) instanceof Slice
360
+
361
+ makeReturn: ->
362
+ if @properties.length then super() else @base.makeReturn()
363
+
364
+ # The value can be unwrapped as its inner node, if there are no attached
365
+ # properties.
366
+ unwrap: ->
367
+ if @properties.length then this else @base
368
+
369
+ # A reference has base part (`this` value) and name part.
370
+ # We cache them separately for compiling complex expressions.
371
+ # `a()[b()] ?= c` -> `(_base = a())[_name = b()] ? _base[_name] = c`
372
+ cacheReference: (o) ->
373
+ name = last @properties
374
+ if @properties.length < 2 and not @base.isComplex() and not name?.isComplex()
375
+ return [this, this] # `a` `a.b`
376
+ base = new Value @base, @properties.slice 0, -1
377
+ if base.isComplex() # `a().b`
378
+ bref = new Literal o.scope.freeVariable 'base'
379
+ base = new Value new Parens new Assign bref, base
380
+ return [base, bref] unless name # `a()`
381
+ if name.isComplex() # `a[b()]`
382
+ nref = new Literal o.scope.freeVariable 'name'
383
+ name = new Index new Assign nref, name.index
384
+ nref = new Index nref
385
+ [base.push(name), new Value(bref or base.base, [nref or name])]
386
+
387
+ # We compile a value to JavaScript by compiling and joining each property.
388
+ # Things get much more interesting if the chain of properties has *soak*
389
+ # operators `?.` interspersed. Then we have to take care not to accidentally
390
+ # evaluate anything twice when building the soak chain.
391
+ compileNode: (o) ->
392
+ @base.front = @front
393
+ props = @properties
394
+ code = @base.compile o, if props.length then LEVEL_ACCESS else null
395
+ code = "(#{code})" if props[0] instanceof Access and @isSimpleNumber()
396
+ code += prop.compile o for prop in props
397
+ code
398
+
399
+ # Unfold a soak into an `If`: `a?.b` -> `a.b if a?`
400
+ unfoldSoak: (o) ->
401
+ if ifn = @base.unfoldSoak o
402
+ Array::push.apply ifn.body.properties, @properties
403
+ return ifn
404
+ for prop, i in @properties when prop.soak
405
+ prop.soak = off
406
+ fst = new Value @base, @properties.slice 0, i
407
+ snd = new Value @base, @properties.slice i
408
+ if fst.isComplex()
409
+ ref = new Literal o.scope.freeVariable 'ref'
410
+ fst = new Parens new Assign ref, fst
411
+ snd.base = ref
412
+ return new If new Existence(fst), snd, soak: on
413
+ null
414
+
415
+ #### Comment
416
+
417
+ # CoffeeScript passes through block comments as JavaScript block comments
418
+ # at the same position.
419
+ exports.Comment = class Comment extends Base
420
+ constructor: (@comment) ->
421
+
422
+ isStatement: YES
423
+ makeReturn: THIS
424
+
425
+ compileNode: (o, level) ->
426
+ code = '/*' + multident(@comment, @tab) + '*/'
427
+ code = o.indent + code if (level or o.level) is LEVEL_TOP
428
+ code
429
+
430
+ #### Call
431
+
432
+ # Node for a function invocation. Takes care of converting `super()` calls into
433
+ # calls against the prototype's function of the same name.
434
+ exports.Call = class Call extends Base
435
+ constructor: (variable, @args = [], @soak) ->
436
+ @isNew = false
437
+ @isSuper = variable is 'super'
438
+ @variable = if @isSuper then null else variable
439
+
440
+ children: ['variable', 'args']
441
+
442
+ # Tag this invocation as creating a new instance.
443
+ newInstance: ->
444
+ base = @variable.base or @variable
445
+ if base instanceof Call
446
+ base.newInstance()
447
+ else
448
+ @isNew = true
449
+ this
450
+
451
+ # Grab the reference to the superclass's implementation of the current
452
+ # method.
453
+ superReference: (o) ->
454
+ {method} = o.scope
455
+ throw SyntaxError 'cannot call super outside of a function.' unless method
456
+ {name} = method
457
+ throw SyntaxError 'cannot call super on an anonymous function.' unless name
458
+ if method.klass
459
+ "#{method.klass}.__super__.#{name}"
460
+ else
461
+ "#{name}.__super__.constructor"
462
+
463
+ # Soaked chained invocations unfold into if/else ternary structures.
464
+ unfoldSoak: (o) ->
465
+ if @soak
466
+ if @variable
467
+ return ifn if ifn = unfoldSoak o, this, 'variable'
468
+ [left, rite] = new Value(@variable).cacheReference o
469
+ else
470
+ left = new Literal @superReference o
471
+ rite = new Value left
472
+ rite = new Call rite, @args
473
+ rite.isNew = @isNew
474
+ left = new Literal "typeof #{ left.compile o } === \"function\""
475
+ return new If left, new Value(rite), soak: yes
476
+ call = this
477
+ list = []
478
+ loop
479
+ if call.variable instanceof Call
480
+ list.push call
481
+ call = call.variable
482
+ continue
483
+ break unless call.variable instanceof Value
484
+ list.push call
485
+ break unless (call = call.variable.base) instanceof Call
486
+ for call in list.reverse()
487
+ if ifn
488
+ if call.variable instanceof Call
489
+ call.variable = ifn
490
+ else
491
+ call.variable.base = ifn
492
+ ifn = unfoldSoak o, call, 'variable'
493
+ ifn
494
+
495
+ # Compile a vanilla function call.
496
+ compileNode: (o) ->
497
+ @variable?.front = @front
498
+ if code = Splat.compileSplattedArray o, @args, true
499
+ return @compileSplat o, code
500
+ args = (arg.compile o, LEVEL_LIST for arg in @args).join ', '
501
+ if @isSuper
502
+ @superReference(o) + ".call(this#{ args and ', ' + args })"
503
+ else
504
+ (if @isNew then 'new ' else '') + @variable.compile(o, LEVEL_ACCESS) + "(#{args})"
505
+
506
+ # `super()` is converted into a call against the superclass's implementation
507
+ # of the current function.
508
+ compileSuper: (args, o) ->
509
+ "#{@superReference(o)}.call(this#{ if args.length then ', ' else '' }#{args})"
510
+
511
+ # If you call a function with a splat, it's converted into a JavaScript
512
+ # `.apply()` call to allow an array of arguments to be passed.
513
+ # If it's a constructor, then things get real tricky. We have to inject an
514
+ # inner constructor in order to be able to pass the varargs.
515
+ compileSplat: (o, splatArgs) ->
516
+ return "#{ @superReference o }.apply(this, #{splatArgs})" if @isSuper
517
+ if @isNew
518
+ idt = @tab + TAB
519
+ return """
520
+ (function(func, args, ctor) {
521
+ #{idt}ctor.prototype = func.prototype;
522
+ #{idt}var child = new ctor, result = func.apply(child, args);
523
+ #{idt}return typeof result === "object" ? result : child;
524
+ #{@tab}})(#{ @variable.compile o, LEVEL_LIST }, #{splatArgs}, function() {})
525
+ """
526
+ base = new Value @variable
527
+ if (name = base.properties.pop()) and base.isComplex()
528
+ ref = o.scope.freeVariable 'ref'
529
+ fun = "(#{ref} = #{ base.compile o, LEVEL_LIST })#{ name.compile o }"
530
+ else
531
+ fun = base.compile o, LEVEL_ACCESS
532
+ if name
533
+ ref = fun
534
+ fun += name.compile o
535
+ else
536
+ ref = 'null'
537
+ "#{fun}.apply(#{ref}, #{splatArgs})"
538
+
539
+ #### Extends
540
+
541
+ # Node to extend an object's prototype with an ancestor object.
542
+ # After `goog.inherits` from the
543
+ # [Closure Library](http://closure-library.googlecode.com/svn/docs/closureGoogBase.js.html).
544
+ exports.Extends = class Extends extends Base
545
+ constructor: (@child, @parent) ->
546
+
547
+ children: ['child', 'parent']
548
+
549
+ # Hooks one constructor into another's prototype chain.
550
+ compile: (o) ->
551
+ utility 'hasProp'
552
+ new Call(new Value(new Literal utility 'extends'), [@child, @parent]).compile o
553
+
554
+ #### Access
555
+
556
+ # A `.` access into a property of a value, or the `::` shorthand for
557
+ # an access into the object's prototype.
558
+ exports.Access = class Access extends Base
559
+ constructor: (@name, tag) ->
560
+ @name.asKey = yes
561
+ @proto = if tag is 'proto' then '.prototype' else ''
562
+ @soak = tag is 'soak'
563
+
564
+ children: ['name']
565
+
566
+ compile: (o) ->
567
+ name = @name.compile o
568
+ @proto + if IS_STRING.test name then "[#{name}]" else ".#{name}"
569
+
570
+ isComplex: NO
571
+
572
+ #### Index
573
+
574
+ # A `[ ... ]` indexed access into an array or object.
575
+ exports.Index = class Index extends Base
576
+ constructor: (@index) ->
577
+
578
+ children: ['index']
579
+
580
+ compile: (o) ->
581
+ (if @proto then '.prototype' else '') + "[#{ @index.compile o, LEVEL_PAREN }]"
582
+
583
+ isComplex: ->
584
+ @index.isComplex()
585
+
586
+ #### Range
587
+
588
+ # A range literal. Ranges can be used to extract portions (slices) of arrays,
589
+ # to specify a range for comprehensions, or as a value, to be expanded into the
590
+ # corresponding array of integers at runtime.
591
+ exports.Range = class Range extends Base
592
+
593
+ children: ['from', 'to']
594
+
595
+ constructor: (@from, @to, tag) ->
596
+ @exclusive = tag is 'exclusive'
597
+ @equals = if @exclusive then '' else '='
598
+
599
+ # Compiles the range's source variables -- where it starts and where it ends.
600
+ # But only if they need to be cached to avoid double evaluation.
601
+ compileVariables: (o) ->
602
+ o = merge(o, top: true)
603
+ [@from, @fromVar] = @from.cache o, LEVEL_LIST
604
+ [@to, @toVar] = @to.cache o, LEVEL_LIST
605
+ [@fromNum, @toNum] = [@fromVar.match(SIMPLENUM), @toVar.match(SIMPLENUM)]
606
+ parts = []
607
+ parts.push @from if @from isnt @fromVar
608
+ parts.push @to if @to isnt @toVar
609
+
610
+ # When compiled normally, the range returns the contents of the *for loop*
611
+ # needed to iterate over the values in the range. Used by comprehensions.
612
+ compileNode: (o) ->
613
+ @compileVariables o
614
+ return @compileArray(o) unless o.index
615
+ return @compileSimple(o) if @fromNum and @toNum
616
+ idx = del o, 'index'
617
+ step = del o, 'step'
618
+ vars = "#{idx} = #{@from}" + if @to isnt @toVar then ", #{@to}" else ''
619
+ intro = "(#{@fromVar} <= #{@toVar} ? #{idx}"
620
+ compare = "#{intro} <#{@equals} #{@toVar} : #{idx} >#{@equals} #{@toVar})"
621
+ stepPart = if step then step.compile(o) else '1'
622
+ incr = if step then "#{idx} += #{stepPart}" else "#{intro} += #{stepPart} : #{idx} -= #{stepPart})"
623
+ "#{vars}; #{compare}; #{incr}"
624
+
625
+ # Compile a simple range comprehension, with integers.
626
+ compileSimple: (o) ->
627
+ [from, to] = [+@fromNum, +@toNum]
628
+ idx = del o, 'index'
629
+ step = del o, 'step'
630
+ step and= "#{idx} += #{step.compile(o)}"
631
+ if from <= to
632
+ "#{idx} = #{from}; #{idx} <#{@equals} #{to}; #{step or "#{idx}++"}"
633
+ else
634
+ "#{idx} = #{from}; #{idx} >#{@equals} #{to}; #{step or "#{idx}--"}"
635
+
636
+ # When used as a value, expand the range into the equivalent array.
637
+ compileArray: (o) ->
638
+ if @fromNum and @toNum and Math.abs(@fromNum - @toNum) <= 20
639
+ range = [+@fromNum..+@toNum]
640
+ range.pop() if @exclusive
641
+ return "[#{ range.join(', ') }]"
642
+ idt = @tab + TAB
643
+ i = o.scope.freeVariable 'i'
644
+ result = o.scope.freeVariable 'results'
645
+ pre = "\n#{idt}#{result} = [];"
646
+ if @fromNum and @toNum
647
+ o.index = i
648
+ body = @compileSimple o
649
+ else
650
+ vars = "#{i} = #{@from}" + if @to isnt @toVar then ", #{@to}" else ''
651
+ clause = "#{@fromVar} <= #{@toVar} ?"
652
+ body = "var #{vars}; #{clause} #{i} <#{@equals} #{@toVar} : #{i} >#{@equals} #{@toVar}; #{clause} #{i} += 1 : #{i} -= 1"
653
+ post = "{ #{result}.push(#{i}); }\n#{idt}return #{result};\n#{o.indent}"
654
+ "(function() {#{pre}\n#{idt}for (#{body})#{post}}).call(this)"
655
+
656
+ #### Slice
657
+
658
+ # An array slice literal. Unlike JavaScript's `Array#slice`, the second parameter
659
+ # specifies the index of the end of the slice, just as the first parameter
660
+ # is the index of the beginning.
661
+ exports.Slice = class Slice extends Base
662
+
663
+ children: ['range']
664
+
665
+ constructor: (@range) ->
666
+ super()
667
+
668
+ # We have to be careful when trying to slice through the end of the array,
669
+ # `9e9` is used because not all implementations respect `undefined` or `1/0`.
670
+ # `9e9` should be safe because `9e9` > `2**32`, the max array length.
671
+ compileNode: (o) ->
672
+ {to, from} = @range
673
+ fromStr = from and from.compile(o, LEVEL_PAREN) or '0'
674
+ compiled = to and to.compile o, LEVEL_PAREN
675
+ if to and not (not @range.exclusive and +compiled is -1)
676
+ toStr = ', ' + if @range.exclusive
677
+ compiled
678
+ else if SIMPLENUM.test compiled
679
+ (+compiled + 1).toString()
680
+ else
681
+ "(#{compiled} + 1) || 9e9"
682
+ ".slice(#{ fromStr }#{ toStr or '' })"
683
+
684
+ #### Obj
685
+
686
+ # An object literal, nothing fancy.
687
+ exports.Obj = class Obj extends Base
688
+ constructor: (props, @generated = false) ->
689
+ @objects = @properties = props or []
690
+
691
+ children: ['properties']
692
+
693
+ compileNode: (o) ->
694
+ props = @properties
695
+ return (if @front then '({})' else '{}') unless props.length
696
+ idt = o.indent += TAB
697
+ lastNoncom = @lastNonComment @properties
698
+ props = for prop, i in props
699
+ join = if i is props.length - 1
700
+ ''
701
+ else if prop is lastNoncom or prop instanceof Comment
702
+ '\n'
703
+ else
704
+ ',\n'
705
+ indent = if prop instanceof Comment then '' else idt
706
+ if prop instanceof Value and prop.this
707
+ prop = new Assign prop.properties[0].name, prop, 'object'
708
+ if prop not instanceof Comment
709
+ if prop not instanceof Assign
710
+ prop = new Assign prop, prop, 'object'
711
+ (prop.variable.base or prop.variable).asKey = yes
712
+ indent + prop.compile(o, LEVEL_TOP) + join
713
+ props = props.join ''
714
+ obj = "{#{ props and '\n' + props + '\n' + @tab }}"
715
+ if @front then "(#{obj})" else obj
716
+
717
+ assigns: (name) ->
718
+ for prop in @properties when prop.assigns name then return yes
719
+ no
720
+
721
+ #### Arr
722
+
723
+ # An array literal.
724
+ exports.Arr = class Arr extends Base
725
+ constructor: (objs) ->
726
+ @objects = objs or []
727
+
728
+ children: ['objects']
729
+
730
+ compileNode: (o) ->
731
+ return '[]' unless @objects.length
732
+ o.indent += TAB
733
+ return code if code = Splat.compileSplattedArray o, @objects
734
+ code = (obj.compile o, LEVEL_LIST for obj in @objects).join ', '
735
+ if code.indexOf('\n') >= 0
736
+ "[\n#{o.indent}#{code}\n#{@tab}]"
737
+ else
738
+ "[#{code}]"
739
+
740
+ assigns: (name) ->
741
+ for obj in @objects when obj.assigns name then return yes
742
+ no
743
+
744
+ #### Class
745
+
746
+ # The CoffeeScript class definition.
747
+ # Initialize a **Class** with its name, an optional superclass, and a
748
+ # list of prototype property assignments.
749
+ exports.Class = class Class extends Base
750
+ constructor: (@variable, @parent, @body = new Expressions) ->
751
+ @boundFuncs = []
752
+ @body.classBody = yes
753
+
754
+ children: ['variable', 'parent', 'body']
755
+
756
+ # Figure out the appropriate name for the constructor function of this class.
757
+ determineName: ->
758
+ return null unless @variable
759
+ decl = if tail = last @variable.properties
760
+ tail instanceof Access and tail.name.value
761
+ else
762
+ @variable.base.value
763
+ decl and= IDENTIFIER.test(decl) and decl
764
+
765
+ # For all `this`-references and bound functions in the class definition,
766
+ # `this` is the Class being constructed.
767
+ setContext: (name) ->
768
+ @body.traverseChildren false, (node) ->
769
+ return false if node.classBody
770
+ if node instanceof Literal and node.value is 'this'
771
+ node.value = name
772
+ else if node instanceof Code
773
+ node.klass = name
774
+ node.context = name if node.bound
775
+
776
+ # Ensure that all functions bound to the instance are proxied in the
777
+ # constructor.
778
+ addBoundFunctions: (o) ->
779
+ if @boundFuncs.length
780
+ for bvar in @boundFuncs
781
+ bname = bvar.compile o
782
+ @ctor.body.unshift new Literal "this.#{bname} = #{utility 'bind'}(this.#{bname}, this);"
783
+
784
+ # Merge the properties from a top-level object as prototypal properties
785
+ # on the class.
786
+ addProperties: (node, name) ->
787
+ props = node.base.properties.slice 0
788
+ while assign = props.shift()
789
+ if assign instanceof Assign
790
+ base = assign.variable.base
791
+ delete assign.context
792
+ func = assign.value
793
+ if base.value is 'constructor'
794
+ if @ctor
795
+ throw new Error 'cannot define more than one constructor in a class'
796
+ if func.bound
797
+ throw new Error 'cannot define a constructor as a bound function'
798
+ if func instanceof Code
799
+ assign = @ctor = func
800
+ else
801
+ assign = @ctor = new Assign(new Value(new Literal name), func)
802
+ else
803
+ unless assign.variable.this
804
+ assign.variable = new Value(new Literal(name), [new Access(base, 'proto')])
805
+ if func instanceof Code and func.bound
806
+ @boundFuncs.push base
807
+ func.bound = no
808
+ assign
809
+
810
+ # Walk the body of the class, looking for prototype properties to be converted.
811
+ walkBody: (name) ->
812
+ @traverseChildren false, (child) =>
813
+ return false if child instanceof Class
814
+ if child instanceof Expressions
815
+ for node, i in exps = child.expressions
816
+ if node instanceof Value and node.isObject(true)
817
+ exps[i] = @addProperties node, name
818
+ child.expressions = exps = flatten exps
819
+
820
+ # Make sure that a constructor is defined for the class, and properly
821
+ # configured.
822
+ ensureConstructor: (name) ->
823
+ if not @ctor
824
+ @ctor = new Code
825
+ @ctor.body.push new Call 'super', [new Splat new Literal 'arguments'] if @parent
826
+ @body.expressions.unshift @ctor
827
+ @ctor.ctor = @ctor.name = name
828
+ @ctor.klass = null
829
+ @ctor.noReturn = yes
830
+
831
+ # Instead of generating the JavaScript string directly, we build up the
832
+ # equivalent syntax tree and compile that, in pieces. You can see the
833
+ # constructor, property assignments, and inheritance getting built out below.
834
+ compileNode: (o) ->
835
+ decl = @determineName()
836
+ name = decl or @name or '_Class'
837
+ lname = new Literal name
838
+
839
+ @setContext name
840
+ @walkBody name
841
+ @body.expressions.unshift new Extends lname, @parent if @parent
842
+ @ensureConstructor name
843
+ @body.expressions.push lname
844
+ @addBoundFunctions o
845
+
846
+ klass = new Parens Closure.wrap(@body), true
847
+ klass = new Assign @variable, klass if @variable
848
+ klass.compile o
849
+
850
+ #### Assign
851
+
852
+ # The **Assign** is used to assign a local variable to value, or to set the
853
+ # property of an object -- including within object literals.
854
+ exports.Assign = class Assign extends Base
855
+ constructor: (@variable, @value, @context, options) ->
856
+ @param = options and options.param
857
+
858
+ # Matchers for detecting class/method names
859
+ METHOD_DEF: /^(?:(\S+)\.prototype\.|\S+?)?\b([$A-Za-z_][$\w]*)$/
860
+
861
+ children: ['variable', 'value']
862
+
863
+ assigns: (name) ->
864
+ @[if @context is 'object' then 'value' else 'variable'].assigns name
865
+
866
+ unfoldSoak: (o) ->
867
+ unfoldSoak o, this, 'variable'
868
+
869
+ # Compile an assignment, delegating to `compilePatternMatch` or
870
+ # `compileSplice` if appropriate. Keep track of the name of the base object
871
+ # we've been assigned to, for correct internal references. If the variable
872
+ # has not been seen yet within the current scope, declare it.
873
+ compileNode: (o) ->
874
+ if isValue = @variable instanceof Value
875
+ return @compilePatternMatch o if @variable.isArray() or @variable.isObject()
876
+ return @compileSplice o if @variable.isSplice()
877
+ return @compileConditional o if @context in ['||=', '&&=', '?=']
878
+ name = @variable.compile o, LEVEL_LIST
879
+ if @value instanceof Code and match = @METHOD_DEF.exec name
880
+ @value.name = match[2]
881
+ @value.klass = match[1] if match[1]
882
+ val = @value.compile o, LEVEL_LIST
883
+ return "#{name}: #{val}" if @context is 'object'
884
+ unless @variable.isAssignable()
885
+ throw SyntaxError "\"#{ @variable.compile o }\" cannot be assigned."
886
+ unless @context or isValue and (@variable.namespaced or @variable.hasProperties())
887
+ if @param
888
+ o.scope.add name, 'var'
889
+ else
890
+ o.scope.find name
891
+ val = name + " #{ @context or '=' } " + val
892
+ if o.level <= LEVEL_LIST then val else "(#{val})"
893
+
894
+ # Brief implementation of recursive pattern matching, when assigning array or
895
+ # object literals to a value. Peeks at their properties to assign inner names.
896
+ # See the [ECMAScript Harmony Wiki](http://wiki.ecmascript.org/doku.php?id=harmony:destructuring)
897
+ # for details.
898
+ compilePatternMatch: (o) ->
899
+ top = o.level is LEVEL_TOP
900
+ {value} = this
901
+ {objects} = @variable.base
902
+ return value.compile o unless olen = objects.length
903
+ isObject = @variable.isObject()
904
+ if top and olen is 1 and (obj = objects[0]) not instanceof Splat
905
+ # Unroll simplest cases: `{v} = x` -> `v = x.v`
906
+ if obj instanceof Assign
907
+ {variable: {base: idx}, value: obj} = obj
908
+ else
909
+ if obj.base instanceof Parens
910
+ [obj, idx] = new Value(obj.unwrapAll()).cacheReference o
911
+ else
912
+ idx = if isObject
913
+ if obj.this then obj.properties[0].name else obj
914
+ else
915
+ new Literal 0
916
+ acc = IDENTIFIER.test idx.unwrap().value or 0
917
+ value = new Value value
918
+ value.properties.push new (if acc then Access else Index) idx
919
+ return new Assign(obj, value).compile o
920
+ vvar = value.compile o, LEVEL_LIST
921
+ assigns = []
922
+ splat = false
923
+ if not IDENTIFIER.test(vvar) or @variable.assigns(vvar)
924
+ assigns.push "#{ ref = o.scope.freeVariable 'ref' } = #{vvar}"
925
+ vvar = ref
926
+ for obj, i in objects
927
+ # A regular array pattern-match.
928
+ idx = i
929
+ if isObject
930
+ if obj instanceof Assign
931
+ # A regular object pattern-match.
932
+ {variable: {base: idx}, value: obj} = obj
933
+ else
934
+ # A shorthand `{a, b, @c} = val` pattern-match.
935
+ if obj.base instanceof Parens
936
+ [obj, idx] = new Value(obj.unwrapAll()).cacheReference o
937
+ else
938
+ idx = if obj.this then obj.properties[0].name else obj
939
+ if not splat and obj instanceof Splat
940
+ val = "#{olen} <= #{vvar}.length ? #{ utility 'slice' }.call(#{vvar}, #{i}"
941
+ if rest = olen - i - 1
942
+ ivar = o.scope.freeVariable 'i'
943
+ val += ", #{ivar} = #{vvar}.length - #{rest}) : (#{ivar} = #{i}, [])"
944
+ else
945
+ val += ") : []"
946
+ val = new Literal val
947
+ splat = "#{ivar}++"
948
+ else
949
+ if obj instanceof Splat
950
+ obj = obj.name.compile o
951
+ throw SyntaxError \
952
+ "multiple splats are disallowed in an assignment: #{obj} ..."
953
+ if typeof idx is 'number'
954
+ idx = new Literal splat or idx
955
+ acc = no
956
+ else
957
+ acc = isObject and IDENTIFIER.test idx.unwrap().value or 0
958
+ val = new Value new Literal(vvar), [new (if acc then Access else Index) idx]
959
+ assigns.push new Assign(obj, val, null, param: @param).compile o, LEVEL_TOP
960
+ assigns.push vvar unless top
961
+ code = assigns.join ', '
962
+ if o.level < LEVEL_LIST then code else "(#{code})"
963
+
964
+ # When compiling a conditional assignment, take care to ensure that the
965
+ # operands are only evaluated once, even though we have to reference them
966
+ # more than once.
967
+ compileConditional: (o) ->
968
+ [left, rite] = @variable.cacheReference o
969
+ new Op(@context.slice(0, -1), left, new Assign(rite, @value, '=')).compile o
970
+
971
+ # Compile the assignment from an array splice literal, using JavaScript's
972
+ # `Array#splice` method.
973
+ compileSplice: (o) ->
974
+ {range: {from, to, exclusive}} = @variable.properties.pop()
975
+ name = @variable.compile o
976
+ [fromDecl, fromRef] = from?.cache(o, LEVEL_OP) or ['0', '0']
977
+ if to
978
+ if from?.isSimpleNumber() and to.isSimpleNumber()
979
+ to = +to.compile(o) - +fromRef
980
+ to += 1 unless exclusive
981
+ else
982
+ to = to.compile(o) + ' - ' + fromRef
983
+ to += ' + 1' unless exclusive
984
+ else
985
+ to = "9e9"
986
+ [valDef, valRef] = @value.cache o, LEVEL_LIST
987
+ code = "[].splice.apply(#{name}, [#{fromDecl}, #{to}].concat(#{valDef})), #{valRef}"
988
+ if o.level > LEVEL_TOP then "(#{code})" else code
989
+
990
+ #### Code
991
+
992
+ # A function definition. This is the only node that creates a new Scope.
993
+ # When for the purposes of walking the contents of a function body, the Code
994
+ # has no *children* -- they're within the inner scope.
995
+ exports.Code = class Code extends Base
996
+ constructor: (params, body, tag) ->
997
+ @params = params or []
998
+ @body = body or new Expressions
999
+ @bound = tag is 'boundfunc'
1000
+ @context = 'this' if @bound
1001
+
1002
+ children: ['params', 'body']
1003
+
1004
+ isStatement: -> !!@ctor
1005
+
1006
+ jumps: NO
1007
+
1008
+ # Compilation creates a new scope unless explicitly asked to share with the
1009
+ # outer scope. Handles splat parameters in the parameter list by peeking at
1010
+ # the JavaScript `arguments` objects. If the function is bound with the `=>`
1011
+ # arrow, generates a wrapper that saves the current value of `this` through
1012
+ # a closure.
1013
+ compileNode: (o) ->
1014
+ o.scope = new Scope o.scope, @body, this
1015
+ o.scope.shared = del o, 'sharedScope'
1016
+ o.indent += TAB
1017
+ delete o.bare
1018
+ delete o.globals
1019
+ vars = []
1020
+ exprs = []
1021
+ for param in @params when param.splat
1022
+ splats = new Assign new Value(new Arr(p.asReference o for p in @params)),
1023
+ new Value new Literal 'arguments'
1024
+ break
1025
+ for param in @params
1026
+ if param.isComplex()
1027
+ val = ref = param.asReference o
1028
+ val = new Op '?', ref, param.value if param.value
1029
+ exprs.push new Assign new Value(param.name), val, '=', param: yes
1030
+ else
1031
+ ref = param
1032
+ if param.value
1033
+ lit = new Literal ref.name.value + ' == null'
1034
+ val = new Assign new Value(param.name), param.value, '='
1035
+ exprs.push new If lit, val
1036
+ vars.push ref unless splats
1037
+ wasEmpty = @body.isEmpty()
1038
+ exprs.unshift splats if splats
1039
+ @body.expressions.unshift exprs... if exprs.length
1040
+ o.scope.parameter vars[i] = v.compile o for v, i in vars unless splats
1041
+ @body.makeReturn() unless wasEmpty or @noReturn
1042
+ idt = o.indent
1043
+ code = 'function'
1044
+ code += ' ' + @name if @ctor
1045
+ code += '(' + vars.join(', ') + ') {'
1046
+ code += "\n#{ @body.compileWithDeclarations o }\n#{@tab}" unless @body.isEmpty()
1047
+ code += '}'
1048
+ return @tab + code if @ctor
1049
+ return utility('bind') + "(#{code}, #{@context})" if @bound
1050
+ if @front or (o.level >= LEVEL_ACCESS) then "(#{code})" else code
1051
+
1052
+ # Short-circuit `traverseChildren` method to prevent it from crossing scope boundaries
1053
+ # unless `crossScope` is `true`.
1054
+ traverseChildren: (crossScope, func) ->
1055
+ super(crossScope, func) if crossScope
1056
+
1057
+ #### Param
1058
+
1059
+ # A parameter in a function definition. Beyond a typical Javascript parameter,
1060
+ # these parameters can also attach themselves to the context of the function,
1061
+ # as well as be a splat, gathering up a group of parameters into an array.
1062
+ exports.Param = class Param extends Base
1063
+ constructor: (@name, @value, @splat) ->
1064
+
1065
+ children: ['name', 'value']
1066
+
1067
+ compile: (o) ->
1068
+ @name.compile o, LEVEL_LIST
1069
+
1070
+ asReference: (o) ->
1071
+ return @reference if @reference
1072
+ node = @name
1073
+ if node.this
1074
+ node = node.properties[0].name
1075
+ node = new Literal '_' + node.value if node.value.reserved
1076
+ else if node.isComplex()
1077
+ node = new Literal o.scope.freeVariable 'arg'
1078
+ node = new Value node
1079
+ node = new Splat node if @splat
1080
+ @reference = node
1081
+
1082
+ isComplex: ->
1083
+ @name.isComplex()
1084
+
1085
+ #### Splat
1086
+
1087
+ # A splat, either as a parameter to a function, an argument to a call,
1088
+ # or as part of a destructuring assignment.
1089
+ exports.Splat = class Splat extends Base
1090
+
1091
+ children: ['name']
1092
+
1093
+ isAssignable: YES
1094
+
1095
+ constructor: (name) ->
1096
+ @name = if name.compile then name else new Literal name
1097
+
1098
+ assigns: (name) ->
1099
+ @name.assigns name
1100
+
1101
+ compile: (o) ->
1102
+ if @index? then @compileParam o else @name.compile o
1103
+
1104
+ # Utility function that converts arbitrary number of elements, mixed with
1105
+ # splats, to a proper array.
1106
+ @compileSplattedArray: (o, list, apply) ->
1107
+ index = -1
1108
+ continue while (node = list[++index]) and node not instanceof Splat
1109
+ return '' if index >= list.length
1110
+ if list.length is 1
1111
+ code = list[0].compile o, LEVEL_LIST
1112
+ return code if apply
1113
+ return "#{ utility 'slice' }.call(#{code})"
1114
+ args = list.slice index
1115
+ for node, i in args
1116
+ code = node.compile o, LEVEL_LIST
1117
+ args[i] = if node instanceof Splat
1118
+ then "#{ utility 'slice' }.call(#{code})"
1119
+ else "[#{code}]"
1120
+ return args[0] + ".concat(#{ args.slice(1).join ', ' })" if index is 0
1121
+ base = (node.compile o, LEVEL_LIST for node in list.slice 0, index)
1122
+ "[#{ base.join ', ' }].concat(#{ args.join ', ' })"
1123
+
1124
+ #### While
1125
+
1126
+ # A while loop, the only sort of low-level loop exposed by CoffeeScript. From
1127
+ # it, all other loops can be manufactured. Useful in cases where you need more
1128
+ # flexibility or more speed than a comprehension can provide.
1129
+ exports.While = class While extends Base
1130
+ constructor: (condition, options) ->
1131
+ @condition = if options?.invert then condition.invert() else condition
1132
+ @guard = options?.guard
1133
+
1134
+ children: ['condition', 'guard', 'body']
1135
+
1136
+ isStatement: YES
1137
+
1138
+ makeReturn: ->
1139
+ @returns = yes
1140
+ this
1141
+
1142
+ addBody: (@body) ->
1143
+ this
1144
+
1145
+ jumps: ->
1146
+ {expressions} = @body
1147
+ return no unless expressions.length
1148
+ for node in expressions
1149
+ return node if node.jumps loop: yes
1150
+ no
1151
+
1152
+ # The main difference from a JavaScript *while* is that the CoffeeScript
1153
+ # *while* can be used as a part of a larger expression -- while loops may
1154
+ # return an array containing the computed result of each iteration.
1155
+ compileNode: (o) ->
1156
+ o.indent += TAB
1157
+ set = ''
1158
+ {body} = this
1159
+ if body.isEmpty()
1160
+ body = ''
1161
+ else
1162
+ if o.level > LEVEL_TOP or @returns
1163
+ rvar = o.scope.freeVariable 'results'
1164
+ set = "#{@tab}#{rvar} = [];\n"
1165
+ body = Push.wrap rvar, body if body
1166
+ body = Expressions.wrap [new If @guard, body] if @guard
1167
+ body = "\n#{ body.compile o, LEVEL_TOP }\n#{@tab}"
1168
+ code = set + @tab + "while (#{ @condition.compile o, LEVEL_PAREN }) {#{body}}"
1169
+ if @returns
1170
+ code += "\n#{@tab}return #{rvar};"
1171
+ code
1172
+
1173
+ #### Op
1174
+
1175
+ # Simple Arithmetic and logical operations. Performs some conversion from
1176
+ # CoffeeScript operations into their JavaScript equivalents.
1177
+ exports.Op = class Op extends Base
1178
+ constructor: (op, first, second, flip) ->
1179
+ return new In first, second if op is 'in'
1180
+ return new Call first, first.params or [] if op is 'do'
1181
+ if op is 'new'
1182
+ return first.newInstance() if first instanceof Call
1183
+ first = new Parens first if first instanceof Code and first.bound
1184
+ @operator = CONVERSIONS[op] or op
1185
+ @first = first
1186
+ @second = second
1187
+ @flip = !!flip
1188
+ return this
1189
+
1190
+ # The map of conversions from CoffeeScript to JavaScript symbols.
1191
+ CONVERSIONS =
1192
+ '==': '==='
1193
+ '!=': '!=='
1194
+ 'of': 'in'
1195
+
1196
+ # The map of invertible operators.
1197
+ INVERSIONS =
1198
+ '!==': '==='
1199
+ '===': '!=='
1200
+
1201
+ children: ['first', 'second']
1202
+
1203
+ isSimpleNumber: NO
1204
+
1205
+ isUnary: ->
1206
+ not @second
1207
+
1208
+ # Am I capable of
1209
+ # [Python-style comparison chaining](http://docs.python.org/reference/expressions.html#notin)?
1210
+ isChainable: ->
1211
+ @operator in ['<', '>', '>=', '<=', '===', '!==']
1212
+
1213
+ invert: ->
1214
+ if @isChainable() and @first.isChainable()
1215
+ allInvertable = yes
1216
+ curr = this
1217
+ while curr and curr.operator
1218
+ allInvertable and= (curr.operator of INVERSIONS)
1219
+ curr = curr.first
1220
+ return new Parens(this).invert() unless allInvertable
1221
+ curr = this
1222
+ while curr and curr.operator
1223
+ curr.invert = !curr.invert
1224
+ curr.operator = INVERSIONS[curr.operator]
1225
+ curr = curr.first
1226
+ this
1227
+ else if op = INVERSIONS[@operator]
1228
+ @operator = op
1229
+ if @first.unwrap() instanceof Op
1230
+ @first.invert()
1231
+ this
1232
+ else if @second
1233
+ new Parens(this).invert()
1234
+ else if @operator is '!' and (fst = @first.unwrap()) instanceof Op and
1235
+ fst.operator in ['!', 'in', 'instanceof']
1236
+ fst
1237
+ else
1238
+ new Op '!', this
1239
+
1240
+ unfoldSoak: (o) ->
1241
+ @operator in ['++', '--', 'delete'] and unfoldSoak o, this, 'first'
1242
+
1243
+ compileNode: (o) ->
1244
+ return @compileUnary o if @isUnary()
1245
+ return @compileChain o if @isChainable() and @first.isChainable()
1246
+ return @compileExistence o if @operator is '?'
1247
+ @first.front = @front
1248
+ code = @first.compile(o, LEVEL_OP) + ' ' + @operator + ' ' +
1249
+ @second.compile(o, LEVEL_OP)
1250
+ if o.level <= LEVEL_OP then code else "(#{code})"
1251
+
1252
+ # Mimic Python's chained comparisons when multiple comparison operators are
1253
+ # used sequentially. For example:
1254
+ #
1255
+ # bin/coffee -e 'console.log 50 < 65 > 10'
1256
+ # true
1257
+ compileChain: (o) ->
1258
+ [@first.second, shared] = @first.second.cache o
1259
+ fst = @first.compile o, LEVEL_OP
1260
+ code = "#{fst} #{if @invert then '&&' else '||'} #{ shared.compile o } #{@operator} #{ @second.compile o, LEVEL_OP }"
1261
+ "(#{code})"
1262
+
1263
+ compileExistence: (o) ->
1264
+ if @first.isComplex()
1265
+ ref = o.scope.freeVariable 'ref'
1266
+ fst = new Parens new Assign new Literal(ref), @first
1267
+ else
1268
+ fst = @first
1269
+ ref = fst.compile o
1270
+ new Existence(fst).compile(o) + " ? #{ref} : #{ @second.compile o, LEVEL_LIST }"
1271
+
1272
+ # Compile a unary **Op**.
1273
+ compileUnary: (o) ->
1274
+ parts = [op = @operator]
1275
+ parts.push ' ' if op in ['new', 'typeof', 'delete'] or
1276
+ op in ['+', '-'] and @first instanceof Op and @first.operator is op
1277
+ parts.push @first.compile o, LEVEL_OP
1278
+ parts.reverse() if @flip
1279
+ parts.join ''
1280
+
1281
+ toString: (idt) ->
1282
+ super idt, @constructor.name + ' ' + @operator
1283
+
1284
+ #### In
1285
+ exports.In = class In extends Base
1286
+ constructor: (@object, @array) ->
1287
+
1288
+ children: ['object', 'array']
1289
+
1290
+ invert: NEGATE
1291
+
1292
+ compileNode: (o) ->
1293
+ if @array instanceof Value and @array.isArray()
1294
+ @compileOrTest o
1295
+ else
1296
+ @compileLoopTest o
1297
+
1298
+ compileOrTest: (o) ->
1299
+ [sub, ref] = @object.cache o, LEVEL_OP
1300
+ [cmp, cnj] = if @negated then [' !== ', ' && '] else [' === ', ' || ']
1301
+ tests = for item, i in @array.base.objects
1302
+ (if i then ref else sub) + cmp + item.compile o, LEVEL_OP
1303
+ tests = tests.join cnj
1304
+ if o.level < LEVEL_OP then tests else "(#{tests})"
1305
+
1306
+ compileLoopTest: (o) ->
1307
+ [sub, ref] = @object.cache o, LEVEL_LIST
1308
+ code = utility('indexOf') + ".call(#{ @array.compile o, LEVEL_LIST }, #{ref}) " +
1309
+ if @negated then '< 0' else '>= 0'
1310
+ return code if sub is ref
1311
+ code = sub + ', ' + code
1312
+ if o.level < LEVEL_LIST then code else "(#{code})"
1313
+
1314
+ toString: (idt) ->
1315
+ super idt, @constructor.name + if @negated then '!' else ''
1316
+
1317
+ #### Try
1318
+
1319
+ # A classic *try/catch/finally* block.
1320
+ exports.Try = class Try extends Base
1321
+ constructor: (@attempt, @error, @recovery, @ensure) ->
1322
+
1323
+ children: ['attempt', 'recovery', 'ensure']
1324
+
1325
+ isStatement: YES
1326
+
1327
+ jumps: (o) -> @attempt.jumps(o) or @recovery?.jumps(o)
1328
+
1329
+ makeReturn: ->
1330
+ @attempt = @attempt .makeReturn() if @attempt
1331
+ @recovery = @recovery.makeReturn() if @recovery
1332
+ this
1333
+
1334
+ # Compilation is more or less as you would expect -- the *finally* clause
1335
+ # is optional, the *catch* is not.
1336
+ compileNode: (o) ->
1337
+ o.indent += TAB
1338
+ errorPart = if @error then " (#{ @error.compile o }) " else ' '
1339
+ catchPart = if @recovery
1340
+ " catch#{errorPart}{\n#{ @recovery.compile o, LEVEL_TOP }\n#{@tab}}"
1341
+ else unless @ensure or @recovery
1342
+ ' catch (_e) {}'
1343
+ """
1344
+ #{@tab}try {
1345
+ #{ @attempt.compile o, LEVEL_TOP }
1346
+ #{@tab}}#{ catchPart or '' }
1347
+ """ + if @ensure then " finally {\n#{ @ensure.compile o, LEVEL_TOP }\n#{@tab}}" else ''
1348
+
1349
+ #### Throw
1350
+
1351
+ # Simple node to throw an exception.
1352
+ exports.Throw = class Throw extends Base
1353
+ constructor: (@expression) ->
1354
+
1355
+ children: ['expression']
1356
+
1357
+ isStatement: YES
1358
+ jumps: NO
1359
+
1360
+ # A **Throw** is already a return, of sorts...
1361
+ makeReturn: THIS
1362
+
1363
+ compileNode: (o) ->
1364
+ @tab + "throw #{ @expression.compile o };"
1365
+
1366
+ #### Existence
1367
+
1368
+ # Checks a variable for existence -- not *null* and not *undefined*. This is
1369
+ # similar to `.nil?` in Ruby, and avoids having to consult a JavaScript truth
1370
+ # table.
1371
+ exports.Existence = class Existence extends Base
1372
+ constructor: (@expression) ->
1373
+
1374
+ children: ['expression']
1375
+
1376
+ invert: NEGATE
1377
+
1378
+ compileNode: (o) ->
1379
+ code = @expression.compile o, LEVEL_OP
1380
+ code = if IDENTIFIER.test(code) and not o.scope.check code
1381
+ if @negated
1382
+ "typeof #{code} == \"undefined\" || #{code} === null"
1383
+ else
1384
+ "typeof #{code} != \"undefined\" && #{code} !== null"
1385
+ else
1386
+ sym = if @negated then '==' else '!='
1387
+ "#{code} #{sym} null"
1388
+ if o.level <= LEVEL_COND then code else "(#{code})"
1389
+
1390
+ #### Parens
1391
+
1392
+ # An extra set of parentheses, specified explicitly in the source. At one time
1393
+ # we tried to clean up the results by detecting and removing redundant
1394
+ # parentheses, but no longer -- you can put in as many as you please.
1395
+ #
1396
+ # Parentheses are a good way to force any statement to become an expression.
1397
+ exports.Parens = class Parens extends Base
1398
+ constructor: (@body) ->
1399
+
1400
+ children: ['body']
1401
+
1402
+ unwrap : -> @body
1403
+ isComplex : -> @body.isComplex()
1404
+ makeReturn: -> @body.makeReturn()
1405
+
1406
+ compileNode: (o) ->
1407
+ expr = @body.unwrap()
1408
+ if expr instanceof Value and expr.isAtomic()
1409
+ expr.front = @front
1410
+ return expr.compile o
1411
+ code = expr.compile o, LEVEL_PAREN
1412
+ bare = o.level < LEVEL_OP and (expr instanceof Op or expr instanceof Call or
1413
+ (expr instanceof For and expr.returns))
1414
+ if bare then code else "(#{code})"
1415
+
1416
+ #### For
1417
+
1418
+ # CoffeeScript's replacement for the *for* loop is our array and object
1419
+ # comprehensions, that compile into *for* loops here. They also act as an
1420
+ # expression, able to return the result of each filtered iteration.
1421
+ #
1422
+ # Unlike Python array comprehensions, they can be multi-line, and you can pass
1423
+ # the current index of the loop as a second parameter. Unlike Ruby blocks,
1424
+ # you can map and filter in a single pass.
1425
+ exports.For = class For extends Base
1426
+ constructor: (body, source) ->
1427
+ {@source, @guard, @step, @name, @index} = source
1428
+ @body = Expressions.wrap [body]
1429
+ @own = !!source.own
1430
+ @object = !!source.object
1431
+ [@name, @index] = [@index, @name] if @object
1432
+ throw SyntaxError 'index cannot be a pattern matching expression' if @index instanceof Value
1433
+ @range = @source instanceof Value and @source.base instanceof Range and not @source.properties.length
1434
+ @pattern = @name instanceof Value
1435
+ throw SyntaxError 'indexes do not apply to range loops' if @range and @index
1436
+ throw SyntaxError 'cannot pattern match over range loops' if @range and @pattern
1437
+ @returns = false
1438
+
1439
+ children: ['body', 'source', 'guard', 'step']
1440
+
1441
+ isStatement: YES
1442
+
1443
+ jumps: While::jumps
1444
+
1445
+ makeReturn: ->
1446
+ @returns = yes
1447
+ this
1448
+
1449
+ # Welcome to the hairiest method in all of CoffeeScript. Handles the inner
1450
+ # loop, filtering, stepping, and result saving for array, object, and range
1451
+ # comprehensions. Some of the generated code can be shared in common, and
1452
+ # some cannot.
1453
+ compileNode: (o) ->
1454
+ body = Expressions.wrap [@body]
1455
+ lastJumps = last(body.expressions)?.jumps()
1456
+ @returns = no if lastJumps and lastJumps instanceof Return
1457
+ source = if @range then @source.base else @source
1458
+ scope = o.scope
1459
+ name = @name and @name.compile o, LEVEL_LIST
1460
+ index = @index and @index.compile o, LEVEL_LIST
1461
+ scope.find(name, immediate: yes) if name and not @pattern
1462
+ scope.find(index, immediate: yes) if index
1463
+ rvar = scope.freeVariable 'results' if @returns
1464
+ ivar = (if @range then name else index) or scope.freeVariable 'i'
1465
+ name = ivar if @pattern
1466
+ varPart = ''
1467
+ guardPart = ''
1468
+ defPart = ''
1469
+ idt1 = @tab + TAB
1470
+ if @range
1471
+ forPart = source.compile merge(o, {index: ivar, @step})
1472
+ else
1473
+ svar = @source.compile o, LEVEL_LIST
1474
+ if (name or @own) and not IDENTIFIER.test svar
1475
+ defPart = "#{@tab}#{ref = scope.freeVariable 'ref'} = #{svar};\n"
1476
+ svar = ref
1477
+ if name and not @pattern
1478
+ namePart = "#{name} = #{svar}[#{ivar}]"
1479
+ unless @object
1480
+ lvar = scope.freeVariable 'len'
1481
+ stepPart = if @step then "#{ivar} += #{ @step.compile(o, LEVEL_OP) }" else "#{ivar}++"
1482
+ forPart = "#{ivar} = 0, #{lvar} = #{svar}.length; #{ivar} < #{lvar}; #{stepPart}"
1483
+ if @returns
1484
+ resultPart = "#{@tab}#{rvar} = [];\n"
1485
+ returnResult = "\n#{@tab}return #{rvar};"
1486
+ body = Push.wrap rvar, body
1487
+ if @guard
1488
+ body = Expressions.wrap [new If @guard, body]
1489
+ if @pattern
1490
+ body.expressions.unshift new Assign @name, new Literal "#{svar}[#{ivar}]"
1491
+ defPart += @pluckDirectCall o, body
1492
+ varPart = "\n#{idt1}#{namePart};" if namePart
1493
+ if @object
1494
+ forPart = "#{ivar} in #{svar}"
1495
+ guardPart = "\n#{idt1}if (!#{utility('hasProp')}.call(#{svar}, #{ivar})) continue;" if @own
1496
+ body = body.compile merge(o, indent: idt1), LEVEL_TOP
1497
+ body = '\n' + body + '\n' if body
1498
+ """
1499
+ #{defPart}#{resultPart or ''}#{@tab}for (#{forPart}) {#{guardPart}#{varPart}#{body}#{@tab}}#{returnResult or ''}
1500
+ """
1501
+
1502
+ pluckDirectCall: (o, body) ->
1503
+ defs = ''
1504
+ for expr, idx in body.expressions
1505
+ expr = expr.unwrapAll()
1506
+ continue unless expr instanceof Call
1507
+ val = expr.variable.unwrapAll()
1508
+ continue unless (val instanceof Code) or
1509
+ (val instanceof Value and
1510
+ val.base?.unwrapAll() instanceof Code and
1511
+ val.properties.length is 1 and
1512
+ val.properties[0].name?.value in ['call', 'apply'])
1513
+ fn = val.base?.unwrapAll() or val
1514
+ ref = new Literal o.scope.freeVariable 'fn'
1515
+ base = new Value ref
1516
+ if val.base
1517
+ [val.base, base] = [base, val]
1518
+ args.unshift new Literal 'this'
1519
+ body.expressions[idx] = new Call base, expr.args
1520
+ defs += @tab + new Assign(ref, fn).compile(o, LEVEL_TOP) + ';\n'
1521
+ defs
1522
+
1523
+ #### Switch
1524
+
1525
+ # A JavaScript *switch* statement. Converts into a returnable expression on-demand.
1526
+ exports.Switch = class Switch extends Base
1527
+ constructor: (@subject, @cases, @otherwise) ->
1528
+
1529
+ children: ['subject', 'cases', 'otherwise']
1530
+
1531
+ isStatement: YES
1532
+
1533
+ jumps: (o = {block: yes}) ->
1534
+ for [conds, block] in @cases
1535
+ return block if block.jumps o
1536
+ @otherwise?.jumps o
1537
+
1538
+ makeReturn: ->
1539
+ pair[1].makeReturn() for pair in @cases
1540
+ @otherwise?.makeReturn()
1541
+ this
1542
+
1543
+ compileNode: (o) ->
1544
+ idt1 = o.indent + TAB
1545
+ idt2 = o.indent = idt1 + TAB
1546
+ code = @tab + "switch (#{ @subject?.compile(o, LEVEL_PAREN) or false }) {\n"
1547
+ for [conditions, block], i in @cases
1548
+ for cond in flatten [conditions]
1549
+ cond = cond.invert() unless @subject
1550
+ code += idt1 + "case #{ cond.compile o, LEVEL_PAREN }:\n"
1551
+ code += body + '\n' if body = block.compile o, LEVEL_TOP
1552
+ break if i is @cases.length - 1 and not @otherwise
1553
+ expr = @lastNonComment block.expressions
1554
+ jumper = expr.jumps()
1555
+ if not expr or not jumper or (jumper instanceof Literal and jumper.value is 'debugger')
1556
+ code += idt2 + 'break;\n'
1557
+ code += idt1 + "default:\n#{ @otherwise.compile o, LEVEL_TOP }\n" if @otherwise and @otherwise.expressions.length
1558
+ code + @tab + '}'
1559
+
1560
+ #### If
1561
+
1562
+ # *If/else* statements. Acts as an expression by pushing down requested returns
1563
+ # to the last line of each clause.
1564
+ #
1565
+ # Single-expression **Ifs** are compiled into conditional operators if possible,
1566
+ # because ternaries are already proper expressions, and don't need conversion.
1567
+ exports.If = class If extends Base
1568
+ constructor: (condition, @body, options = {}) ->
1569
+ @condition = if options.type is 'unless' then condition.invert() else condition
1570
+ @elseBody = null
1571
+ @isChain = false
1572
+ {@soak} = options
1573
+
1574
+ children: ['condition', 'body', 'elseBody']
1575
+
1576
+ bodyNode: -> @body?.unwrap()
1577
+ elseBodyNode: -> @elseBody?.unwrap()
1578
+
1579
+ # Rewrite a chain of **Ifs** to add a default case as the final *else*.
1580
+ addElse: (elseBody) ->
1581
+ if @isChain
1582
+ @elseBodyNode().addElse elseBody
1583
+ else
1584
+ @isChain = elseBody instanceof If
1585
+ @elseBody = @ensureExpressions elseBody
1586
+ this
1587
+
1588
+ # The **If** only compiles into a statement if either of its bodies needs
1589
+ # to be a statement. Otherwise a conditional operator is safe.
1590
+ isStatement: (o) ->
1591
+ o?.level is LEVEL_TOP or
1592
+ @bodyNode().isStatement(o) or @elseBodyNode()?.isStatement(o)
1593
+
1594
+ jumps: (o) -> @body.jumps(o) or @elseBody?.jumps(o)
1595
+
1596
+ compileNode: (o) ->
1597
+ if @isStatement o then @compileStatement o else @compileExpression o
1598
+
1599
+ makeReturn: ->
1600
+ @body and= new Expressions [@body.makeReturn()]
1601
+ @elseBody and= new Expressions [@elseBody.makeReturn()]
1602
+ this
1603
+
1604
+ ensureExpressions: (node) ->
1605
+ if node instanceof Expressions then node else new Expressions [node]
1606
+
1607
+ # Compile the **If** as a regular *if-else* statement. Flattened chains
1608
+ # force inner *else* bodies into statement form.
1609
+ compileStatement: (o) ->
1610
+ child = del o, 'chainChild'
1611
+ cond = @condition.compile o, LEVEL_PAREN
1612
+ o.indent += TAB
1613
+ body = @ensureExpressions(@body).compile o
1614
+ body = "\n#{body}\n#{@tab}" if body
1615
+ ifPart = "if (#{cond}) {#{body}}"
1616
+ ifPart = @tab + ifPart unless child
1617
+ return ifPart unless @elseBody
1618
+ ifPart + ' else ' + if @isChain
1619
+ o.indent = @tab
1620
+ o.chainChild = yes
1621
+ @elseBody.unwrap().compile o, LEVEL_TOP
1622
+ else
1623
+ "{\n#{ @elseBody.compile o, LEVEL_TOP }\n#{@tab}}"
1624
+
1625
+ # Compile the If as a conditional operator.
1626
+ compileExpression: (o) ->
1627
+ cond = @condition.compile o, LEVEL_COND
1628
+ body = @bodyNode().compile o, LEVEL_LIST
1629
+ alt = if @elseBodyNode() then @elseBodyNode().compile(o, LEVEL_LIST) else 'void 0'
1630
+ code = "#{cond} ? #{body} : #{alt}"
1631
+ if o.level >= LEVEL_COND then "(#{code})" else code
1632
+
1633
+ unfoldSoak: ->
1634
+ @soak and this
1635
+
1636
+ # Faux-Nodes
1637
+ # ----------
1638
+ # Faux-nodes are never created by the grammar, but are used during code
1639
+ # generation to generate other combinations of nodes.
1640
+
1641
+ #### Push
1642
+
1643
+ # The **Push** creates the tree for `array.push(value)`,
1644
+ # which is helpful for recording the result arrays from comprehensions.
1645
+ Push =
1646
+ wrap: (name, exps) ->
1647
+ return exps if exps.isEmpty() or last(exps.expressions).jumps()
1648
+ exps.push new Call new Value(new Literal(name), [new Access new Literal 'push']), [exps.pop()]
1649
+
1650
+ #### Closure
1651
+
1652
+ # A faux-node used to wrap an expressions body in a closure.
1653
+ Closure =
1654
+
1655
+ # Wrap the expressions body, unless it contains a pure statement,
1656
+ # in which case, no dice. If the body mentions `this` or `arguments`,
1657
+ # then make sure that the closure wrapper preserves the original values.
1658
+ wrap: (expressions, statement, noReturn) ->
1659
+ return expressions if expressions.jumps()
1660
+ func = new Code [], Expressions.wrap [expressions]
1661
+ args = []
1662
+ if (mentionsArgs = expressions.contains @literalArgs) or
1663
+ ( expressions.contains @literalThis)
1664
+ meth = new Literal if mentionsArgs then 'apply' else 'call'
1665
+ args = [new Literal 'this']
1666
+ args.push new Literal 'arguments' if mentionsArgs
1667
+ func = new Value func, [new Access meth]
1668
+ func.noReturn = noReturn
1669
+ call = new Call func, args
1670
+ if statement then Expressions.wrap [call] else call
1671
+
1672
+ literalArgs: (node) ->
1673
+ node instanceof Literal and node.value is 'arguments' and not node.asKey
1674
+ literalThis: (node) ->
1675
+ (node instanceof Literal and node.value is 'this' and not node.asKey) or
1676
+ (node instanceof Code and node.bound)
1677
+
1678
+ # Unfold a node's child if soak, then tuck the node under created `If`
1679
+ unfoldSoak = (o, parent, name) ->
1680
+ return unless ifn = parent[name].unfoldSoak o
1681
+ parent[name] = ifn.body
1682
+ ifn.body = new Value parent
1683
+ ifn
1684
+
1685
+ # Constants
1686
+ # ---------
1687
+
1688
+ UTILITIES =
1689
+
1690
+ # Correctly set up a prototype chain for inheritance, including a reference
1691
+ # to the superclass for `super()` calls, and copies of any static properties.
1692
+ extends: '''
1693
+ function(child, parent) {
1694
+ for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; }
1695
+ function ctor() { this.constructor = child; }
1696
+ ctor.prototype = parent.prototype;
1697
+ child.prototype = new ctor;
1698
+ child.__super__ = parent.prototype;
1699
+ return child;
1700
+ }
1701
+ '''
1702
+
1703
+ # Create a function bound to the current value of "this".
1704
+ bind: '''
1705
+ function(fn, me){ return function(){ return fn.apply(me, arguments); }; }
1706
+ '''
1707
+
1708
+ # Discover if an item is in an array.
1709
+ indexOf: '''
1710
+ Array.prototype.indexOf || function(item) {
1711
+ for (var i = 0, l = this.length; i < l; i++) {
1712
+ if (this[i] === item) return i;
1713
+ }
1714
+ return -1;
1715
+ }
1716
+ '''
1717
+
1718
+ # Shortcuts to speed up the lookup time for native functions.
1719
+ hasProp: 'Object.prototype.hasOwnProperty'
1720
+ slice : 'Array.prototype.slice'
1721
+
1722
+ # Levels indicates a node's position in the AST. Useful for knowing if
1723
+ # parens are necessary or superfluous.
1724
+ LEVEL_TOP = 1 # ...;
1725
+ LEVEL_PAREN = 2 # (...)
1726
+ LEVEL_LIST = 3 # [...]
1727
+ LEVEL_COND = 4 # ... ? x : y
1728
+ LEVEL_OP = 5 # !...
1729
+ LEVEL_ACCESS = 6 # ...[0]
1730
+
1731
+ # Tabs are two spaces for pretty printing.
1732
+ TAB = ' '
1733
+
1734
+ # Trim out all trailing whitespace, so that the generated code plays nice
1735
+ # with Git.
1736
+ TRAILING_WHITESPACE = /[ \t]+$/gm
1737
+
1738
+ IDENTIFIER = /^[$A-Za-z_][$\w]*$/
1739
+ SIMPLENUM = /^[+-]?\d+$/
1740
+
1741
+ # Is a literal value a string?
1742
+ IS_STRING = /^['"]/
1743
+
1744
+ # Utility Functions
1745
+ # -----------------
1746
+
1747
+ # Helper for ensuring that utility functions are assigned at the top level.
1748
+ utility = (name) ->
1749
+ ref = "__#{name}"
1750
+ Scope.root.assign ref, UTILITIES[name]
1751
+ ref
1752
+
1753
+ multident = (code, tab) ->
1754
+ code.replace /\n/g, '$&' + tab