opal 0.2.2 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (325) hide show
  1. data/.gitignore +5 -10
  2. data/LICENSE +75 -0
  3. data/README.md +55 -3
  4. data/Rakefile +62 -139
  5. data/bin/opal +7 -10
  6. data/gems/core/README.md +5 -0
  7. data/gems/core/Rakefile +7 -0
  8. data/gems/core/core.gemspec +13 -0
  9. data/gems/core/lib/core.rb +33 -0
  10. data/gems/core/lib/core/array.rb +1470 -0
  11. data/gems/core/lib/core/basic_object.rb +15 -0
  12. data/gems/core/lib/core/class.rb +31 -0
  13. data/gems/core/lib/core/dir.rb +26 -0
  14. data/gems/core/lib/core/error.rb +43 -0
  15. data/gems/core/lib/core/false_class.rb +21 -0
  16. data/gems/core/lib/core/file.rb +54 -0
  17. data/gems/core/lib/core/hash.rb +725 -0
  18. data/gems/core/lib/core/kernel.rb +240 -0
  19. data/gems/core/lib/core/module.rb +98 -0
  20. data/gems/core/lib/core/nil_class.rb +41 -0
  21. data/gems/core/lib/core/numeric.rb +370 -0
  22. data/gems/core/lib/core/proc.rb +11 -0
  23. data/gems/core/lib/core/range.rb +17 -0
  24. data/gems/core/lib/core/regexp.rb +18 -0
  25. data/gems/core/lib/core/string.rb +328 -0
  26. data/gems/core/lib/core/symbol.rb +15 -0
  27. data/gems/core/lib/core/top_self.rb +8 -0
  28. data/gems/core/lib/core/true_class.rb +20 -0
  29. data/gems/core/lib/core/vm.rb +16 -0
  30. data/{opals/opal/opal → gems/core}/spec/core/array/append_spec.rb +0 -0
  31. data/{opals/opal/opal → gems/core}/spec/core/array/assoc_spec.rb +0 -0
  32. data/{opals/opal/opal → gems/core}/spec/core/array/at_spec.rb +0 -0
  33. data/{opals/opal/opal → gems/core}/spec/core/array/clear_spec.rb +1 -1
  34. data/{opals/opal/opal → gems/core}/spec/core/array/collect_bang_spec.rb +0 -0
  35. data/{opals/opal/opal → gems/core}/spec/core/array/collect_spec.rb +0 -0
  36. data/gems/core/spec/core/array/compact_spec.rb +41 -0
  37. data/{opals/opal/opal → gems/core}/spec/core/array/concat_spec.rb +0 -0
  38. data/{opals/opal/opal → gems/core}/spec/core/array/constructor_spec.rb +0 -0
  39. data/{opals/opal/opal → gems/core}/spec/core/array/each_spec.rb +0 -0
  40. data/{opals/opal/opal → gems/core}/spec/core/array/element_reference_spec.rb +0 -0
  41. data/{opals/opal/opal → gems/core}/spec/core/array/first_spec.rb +1 -1
  42. data/{opals/opal/opal → gems/core}/spec/core/array/include_spec.rb +0 -0
  43. data/gems/core/spec/core/array/join_spec.rb +6 -0
  44. data/gems/core/spec/core/array/last_spec.rb +51 -0
  45. data/gems/core/spec/core/array/length_spec.rb +6 -0
  46. data/{opals/opal/opal → gems/core}/spec/core/array/map_spec.rb +2 -0
  47. data/gems/core/spec/core/array/reverse_spec.rb +6 -0
  48. data/{opals/opal/opal → gems/core}/spec/core/builtin_constants/builtin_constants_spec.rb +0 -0
  49. data/{opals/opal/opal → gems/core}/spec/core/false/and_spec.rb +0 -0
  50. data/{opals/opal/opal → gems/core}/spec/core/false/inspect_spec.rb +0 -0
  51. data/{opals/opal/opal → gems/core}/spec/core/false/or_spec.rb +0 -0
  52. data/{opals/opal/opal → gems/core}/spec/core/false/to_s_spec.rb +0 -0
  53. data/{opals/opal/opal → gems/core}/spec/core/false/xor_spec.rb +0 -0
  54. data/{opals/opal/opal → gems/core}/spec/core/file/join_spec.rb +1 -1
  55. data/gems/core/spec/core/hash/assoc_spec.rb +32 -0
  56. data/{opals/opal/opal → gems/core}/spec/core/kernel/instance_eval_spec.rb +0 -0
  57. data/{opals/opal/opal → gems/core}/spec/core/kernel/loop_spec.rb +0 -0
  58. data/{opals/opal/opal → gems/core}/spec/core/kernel/raise_spec.rb +0 -0
  59. data/{opals/opal/opal → gems/core}/spec/core/module/attr_accessor_spec.rb +0 -0
  60. data/{opals/opal/opal → gems/core}/spec/core/number/lt_spec.rb +0 -0
  61. data/gems/core/spec/core/string/sub_spec.rb +24 -0
  62. data/{opals/opal/opal → gems/core}/spec/core/true/and_spec.rb +0 -0
  63. data/{opals/opal/opal → gems/core}/spec/core/true/inspect_spec.rb +0 -0
  64. data/{opals/opal/opal → gems/core}/spec/core/true/or_spec.rb +0 -0
  65. data/{opals/opal/opal → gems/core}/spec/core/true/to_s_spec.rb +0 -0
  66. data/{opals/opal/opal → gems/core}/spec/core/true/xor_spec.rb +0 -0
  67. data/{opals/opal/opal → gems/core}/spec/language/and_spec.rb +2 -3
  68. data/{opals/opal/opal → gems/core}/spec/language/array_spec.rb +21 -5
  69. data/gems/core/spec/language/block_spec.rb +38 -0
  70. data/{opals/opal/opal → gems/core}/spec/language/break_spec.rb +0 -0
  71. data/gems/core/spec/language/case_spec.rb +103 -0
  72. data/{opals/opal/opal → gems/core}/spec/language/def_spec.rb +11 -1
  73. data/{opals/opal/opal → gems/core}/spec/language/eigenclass_spec.rb +0 -0
  74. data/gems/core/spec/language/file_spec.rb +13 -0
  75. data/gems/core/spec/language/fixtures/block.rb +21 -0
  76. data/gems/core/spec/language/fixtures/super.rb +293 -0
  77. data/{opals/opal/opal → gems/core}/spec/language/hash_spec.rb +0 -0
  78. data/{opals/opal/opal → gems/core}/spec/language/if_spec.rb +0 -0
  79. data/{opals/opal/opal → gems/core}/spec/language/loop_spec.rb +0 -0
  80. data/gems/core/spec/language/metaclass_spec.rb +21 -0
  81. data/{opals/opal/opal → gems/core}/spec/language/method_spec.rb +60 -0
  82. data/{opals/opal/opal → gems/core}/spec/language/next_spec.rb +0 -0
  83. data/{opals/opal/opal → gems/core}/spec/language/or_spec.rb +0 -0
  84. data/{opals/opal/opal → gems/core}/spec/language/redo_spec.rb +0 -0
  85. data/gems/core/spec/language/regexp_spec.rb +26 -0
  86. data/{opals/opal/opal → gems/core}/spec/language/rescue_spec.rb +0 -0
  87. data/{opals/opal/opal → gems/core}/spec/language/return_spec.rb +0 -0
  88. data/{opals/opal/opal → gems/core}/spec/language/string_spec.rb +0 -0
  89. data/gems/core/spec/language/super_spec.rb +32 -0
  90. data/{opals/opal/opal → gems/core}/spec/language/until_spec.rb +47 -47
  91. data/gems/core/spec/language/variables_spec.rb +155 -0
  92. data/{opals/opal/opal → gems/core}/spec/language/while_spec.rb +47 -47
  93. data/{opals/opal/opal → gems/core}/spec/spec_helper.rb +1 -1
  94. data/gems/core_fs/README.md +19 -0
  95. data/gems/dev/Rakefile +5 -0
  96. data/gems/dev/lib/dev.js +99 -0
  97. data/gems/dev/lib/dev/generator.js +1264 -0
  98. data/gems/dev/lib/dev/parser.js +979 -0
  99. data/gems/dev/lib/dev/ruby_parser.js +1088 -0
  100. data/gems/dev/lib/dev/ruby_parser.y +1267 -0
  101. data/gems/dev/lib/dev/string_scanner.js +38 -0
  102. data/gems/dev/tools/racc2js/README.md +39 -0
  103. data/gems/dev/tools/racc2js/math_parser.js +222 -0
  104. data/gems/dev/tools/racc2js/math_parser.rb +133 -0
  105. data/gems/dev/tools/racc2js/math_parser.y +28 -0
  106. data/gems/dev/tools/racc2js/parser.js +218 -0
  107. data/gems/dev/tools/racc2js/racc2js.rb +153 -0
  108. data/gems/json/README.md +4 -0
  109. data/gems/json/json.gemspec +14 -0
  110. data/gems/json/lib/json.rb +64 -0
  111. data/gems/json/lib/json/ext.rb +51 -0
  112. data/{opals/opal/browser/lib/browser/json_parse.js → gems/json/lib/json/json2.js} +197 -37
  113. data/gems/ospec/README.md +0 -0
  114. data/gems/ospec/lib/ospec.rb +6 -0
  115. data/gems/ospec/lib/ospec/autorun.rb +3 -0
  116. data/gems/ospec/lib/ospec/dsl.rb +15 -0
  117. data/gems/ospec/lib/ospec/example.rb +11 -0
  118. data/gems/ospec/lib/ospec/example/before_and_after_hooks.rb +56 -0
  119. data/gems/ospec/lib/ospec/example/errors.rb +17 -0
  120. data/gems/ospec/lib/ospec/example/example_group.rb +12 -0
  121. data/gems/ospec/lib/ospec/example/example_group_factory.rb +21 -0
  122. data/gems/ospec/lib/ospec/example/example_group_hierarchy.rb +20 -0
  123. data/{opals/opal/spec/lib/spec → gems/ospec/lib/ospec}/example/example_group_methods.rb +26 -68
  124. data/gems/ospec/lib/ospec/example/example_group_proxy.rb +14 -0
  125. data/gems/ospec/lib/ospec/example/example_methods.rb +46 -0
  126. data/gems/ospec/lib/ospec/example/example_proxy.rb +18 -0
  127. data/gems/ospec/lib/ospec/expectations.rb +19 -0
  128. data/gems/ospec/lib/ospec/expectations/errors.rb +8 -0
  129. data/gems/ospec/lib/ospec/expectations/fail_with.rb +8 -0
  130. data/gems/ospec/lib/ospec/expectations/handler.rb +27 -0
  131. data/gems/ospec/lib/ospec/matchers.rb +24 -0
  132. data/{opals/opal/Rakefile → gems/ospec/lib/ospec/matchers/be.rb} +0 -0
  133. data/gems/ospec/lib/ospec/matchers/generated_descriptions.rb +20 -0
  134. data/gems/ospec/lib/ospec/matchers/operator_matcher.rb +52 -0
  135. data/gems/ospec/lib/ospec/runner.rb +40 -0
  136. data/gems/ospec/lib/ospec/runner/example_group_runner.rb +44 -0
  137. data/{opals/opal/spec/lib/spec → gems/ospec/lib/ospec}/runner/formatter/html_formatter.rb +31 -40
  138. data/gems/ospec/lib/ospec/runner/formatter/terminal_formatter.rb +48 -0
  139. data/gems/ospec/lib/ospec/runner/options.rb +36 -0
  140. data/{opals/opal/spec/lib/spec → gems/ospec/lib/ospec}/runner/reporter.rb +23 -55
  141. data/gems/ospec/ospec.gemspec +0 -0
  142. data/gems/rquery/README.md +9 -0
  143. data/gems/rquery/lib/rquery.rb +10 -0
  144. data/gems/rquery/lib/rquery/ajax.rb +4 -0
  145. data/gems/rquery/lib/rquery/css.rb +96 -0
  146. data/gems/rquery/lib/rquery/document.rb +25 -0
  147. data/gems/rquery/lib/rquery/element.rb +292 -0
  148. data/gems/rquery/lib/rquery/event.rb +108 -0
  149. data/gems/rquery/lib/rquery/jquery.js +8177 -0
  150. data/gems/rquery/lib/rquery/request.rb +138 -0
  151. data/gems/rquery/lib/rquery/response.rb +49 -0
  152. data/gems/rquery/rquery.gemspec +16 -0
  153. data/lib/opal.js +1597 -0
  154. data/lib/opal.rb +6 -69
  155. data/lib/opal/builder.rb +115 -0
  156. data/lib/opal/bundle.rb +131 -0
  157. data/lib/opal/command.rb +11 -0
  158. data/lib/opal/context.rb +69 -0
  159. data/lib/opal/context/console.rb +12 -0
  160. data/lib/opal/context/file_system.rb +19 -0
  161. data/lib/opal/context/loader.rb +117 -0
  162. data/lib/opal/gem.rb +153 -0
  163. data/lib/opal/ruby/nodes.rb +1302 -0
  164. data/lib/opal/ruby/parser.rb +780 -0
  165. data/lib/opal/ruby/ruby_parser.rb +5170 -0
  166. data/lib/opal/ruby/ruby_parser.y +1298 -0
  167. data/opal.gemspec +11 -256
  168. metadata +179 -240
  169. data/.gitmodules +0 -6
  170. data/Opalfile +0 -384
  171. data/VERSION +0 -1
  172. data/demos/apps/browser_demo/index.html +0 -11
  173. data/demos/apps/browser_demo/lib/browser_demo.rb +0 -31
  174. data/demos/apps/simple_opal/Opalfile +0 -13
  175. data/demos/apps/simple_opal/index.html +0 -11
  176. data/demos/apps/simple_opal/lib/app_controller.rb +0 -62
  177. data/demos/apps/simple_opal/lib/main_window.rb +0 -146
  178. data/demos/browser/request/index.html +0 -52
  179. data/demos/browser/request/request.rb +0 -48
  180. data/gen/browser/__PROJECT_NAME__/Rakefile +0 -12
  181. data/gen/browser/__PROJECT_NAME__/index.html +0 -11
  182. data/gen/browser/__PROJECT_NAME__/javascripts/opal.debug.js +0 -4687
  183. data/gen/browser/__PROJECT_NAME__/javascripts/opal.min.js +0 -175
  184. data/gen/browser/__PROJECT_NAME__/lib/__PROJECT_NAME__.rb +0 -1
  185. data/lib/opal/builders/base.rb +0 -50
  186. data/lib/opal/builders/css.rb +0 -46
  187. data/lib/opal/builders/javascript.rb +0 -44
  188. data/lib/opal/builders/opal.rb +0 -79
  189. data/lib/opal/builders/ruby.rb +0 -50
  190. data/lib/opal/builders/ruby/generate.rb +0 -1851
  191. data/lib/opal/builders/ruby/nodes.rb +0 -210
  192. data/lib/opal/builders/ruby/ruby.rb +0 -916
  193. data/lib/opal/builders/ruby/ruby_parser.rb +0 -6008
  194. data/lib/opal/builders/ruby/ruby_parser.rb.y +0 -1451
  195. data/lib/opal/models/build_item.rb +0 -104
  196. data/lib/opal/models/hash_struct.rb +0 -40
  197. data/lib/opal/models/project.rb +0 -252
  198. data/lib/opal/models/struct_accessors.rb +0 -58
  199. data/lib/opal/models/target.rb +0 -176
  200. data/lib/opal/opal/build.rb +0 -169
  201. data/lib/opal/opal/env/console.rb +0 -66
  202. data/lib/opal/opal/env/fs.rb +0 -98
  203. data/lib/opal/opal/env/object.rb +0 -48
  204. data/lib/opal/opal/environment.rb +0 -139
  205. data/lib/opal/opal/gen.rb +0 -62
  206. data/lib/opal/opal/opal.rb +0 -75
  207. data/lib/opal/opal/repl.rb +0 -38
  208. data/lib/opal/opalfile/dsl.rb +0 -62
  209. data/lib/opal/opalfile/opalfile.rb +0 -133
  210. data/lib/opal/opalfile/task.rb +0 -96
  211. data/lib/opal/opalfile/task_manager.rb +0 -63
  212. data/lib/opal/opalfile/task_scope.rb +0 -52
  213. data/lib/opal/rack/app_server.rb +0 -119
  214. data/lib/opal/rake/opal_task.rb +0 -34
  215. data/opals/opal/README.md +0 -37
  216. data/opals/opal/browser/Opalfile +0 -11
  217. data/opals/opal/browser/README.md +0 -146
  218. data/opals/opal/browser/SIZZLE_LICESNSE.txt +0 -148
  219. data/opals/opal/browser/lib/browser.rb +0 -118
  220. data/opals/opal/browser/lib/browser/builder.rb +0 -41
  221. data/opals/opal/browser/lib/browser/canvas_context.rb +0 -115
  222. data/opals/opal/browser/lib/browser/dimensions.rb +0 -50
  223. data/opals/opal/browser/lib/browser/document.rb +0 -146
  224. data/opals/opal/browser/lib/browser/element.rb +0 -487
  225. data/opals/opal/browser/lib/browser/element/attributes.rb +0 -88
  226. data/opals/opal/browser/lib/browser/element/css.rb +0 -290
  227. data/opals/opal/browser/lib/browser/element/form.rb +0 -146
  228. data/opals/opal/browser/lib/browser/event/dom_events.rb +0 -81
  229. data/opals/opal/browser/lib/browser/event/event.rb +0 -177
  230. data/opals/opal/browser/lib/browser/event/trigger_events.rb +0 -53
  231. data/opals/opal/browser/lib/browser/geometry.rb +0 -97
  232. data/opals/opal/browser/lib/browser/json.rb +0 -32
  233. data/opals/opal/browser/lib/browser/request/request.rb +0 -201
  234. data/opals/opal/browser/lib/browser/sizzle.js +0 -1068
  235. data/opals/opal/browser/lib/browser/string.rb +0 -42
  236. data/opals/opal/browser/lib/browser/touch.rb +0 -37
  237. data/opals/opal/browser/lib/browser/vml_context.js +0 -33
  238. data/opals/opal/browser/lib/browser/window.rb +0 -36
  239. data/opals/opal/browser/spec/browser/browser_detection_spec.rb +0 -7
  240. data/opals/opal/browser/spec/document/aref_spec.rb +0 -110
  241. data/opals/opal/browser/spec/document/ready_spec.rb +0 -16
  242. data/opals/opal/browser/spec/element/body_spec.rb +0 -11
  243. data/opals/opal/browser/spec/element/clear_spec.rb +0 -26
  244. data/opals/opal/browser/spec/element/empty_spec.rb +0 -29
  245. data/opals/opal/browser/spec/element/has_class_spec.rb +0 -40
  246. data/opals/opal/browser/spec/element/hidden_spec.rb +0 -23
  247. data/opals/opal/browser/spec/element/hide_spec.rb +0 -31
  248. data/opals/opal/browser/spec/element/remove_spec.rb +0 -25
  249. data/opals/opal/browser/spec/element/show_spec.rb +0 -31
  250. data/opals/opal/browser/spec/element/style_spec.rb +0 -69
  251. data/opals/opal/browser/spec/element/toggle_spec.rb +0 -31
  252. data/opals/opal/browser/spec/element/visible_spec.rb +0 -23
  253. data/opals/opal/browser/spec/spec_helper.rb +0 -1
  254. data/opals/opal/opal/Opalfile +0 -14
  255. data/opals/opal/opal/spec/core/array/compact_spec.rb +0 -15
  256. data/opals/opal/opal/spec/fixtures/super.rb +0 -70
  257. data/opals/opal/opal/spec/language/____temp_remove_this.rb +0 -12
  258. data/opals/opal/opal/spec/language/block_spec.rb +0 -18
  259. data/opals/opal/opal/spec/language/case_spec.rb +0 -103
  260. data/opals/opal/opal/spec/language/metaclass_spec.rb +0 -21
  261. data/opals/opal/opal/spec/language/super_spec.rb +0 -26
  262. data/opals/opal/runtime/Opalfile +0 -78
  263. data/opals/opal/runtime/README.md +0 -12
  264. data/opals/opal/runtime/docs/debugging.md +0 -51
  265. data/opals/opal/runtime/lib/array.rb +0 -1516
  266. data/opals/opal/runtime/lib/basic_object.rb +0 -49
  267. data/opals/opal/runtime/lib/class.rb +0 -54
  268. data/opals/opal/runtime/lib/dir.rb +0 -36
  269. data/opals/opal/runtime/lib/error.rb +0 -49
  270. data/opals/opal/runtime/lib/false_class.rb +0 -52
  271. data/opals/opal/runtime/lib/file.rb +0 -79
  272. data/opals/opal/runtime/lib/hash.rb +0 -791
  273. data/opals/opal/runtime/lib/io.rb +0 -39
  274. data/opals/opal/runtime/lib/kernel.rb +0 -288
  275. data/opals/opal/runtime/lib/match_data.rb +0 -36
  276. data/opals/opal/runtime/lib/module.rb +0 -109
  277. data/opals/opal/runtime/lib/nil_class.rb +0 -69
  278. data/opals/opal/runtime/lib/number.rb +0 -398
  279. data/opals/opal/runtime/lib/proc.rb +0 -77
  280. data/opals/opal/runtime/lib/range.rb +0 -63
  281. data/opals/opal/runtime/lib/regexp.rb +0 -111
  282. data/opals/opal/runtime/lib/ruby.rb +0 -30
  283. data/opals/opal/runtime/lib/string.rb +0 -328
  284. data/opals/opal/runtime/lib/symbol.rb +0 -40
  285. data/opals/opal/runtime/lib/top_self.rb +0 -33
  286. data/opals/opal/runtime/lib/true_class.rb +0 -45
  287. data/opals/opal/runtime/runtime/browser.js +0 -287
  288. data/opals/opal/runtime/runtime/debug.js +0 -180
  289. data/opals/opal/runtime/runtime/opal.js +0 -1008
  290. data/opals/opal/runtime/runtime/post_opal.js +0 -1
  291. data/opals/opal/runtime/runtime/pre_opal.js +0 -2
  292. data/opals/opal/runtime/runtime/server_side.js +0 -50
  293. data/opals/opal/spec/LICENSE.txt +0 -26
  294. data/opals/opal/spec/Opalfile +0 -5
  295. data/opals/opal/spec/bin/spec.rb +0 -43
  296. data/opals/opal/spec/lib/spec.rb +0 -33
  297. data/opals/opal/spec/lib/spec/dsl.rb +0 -41
  298. data/opals/opal/spec/lib/spec/example.rb +0 -35
  299. data/opals/opal/spec/lib/spec/example/before_and_after_hooks.rb +0 -81
  300. data/opals/opal/spec/lib/spec/example/errors.rb +0 -42
  301. data/opals/opal/spec/lib/spec/example/example_group.rb +0 -37
  302. data/opals/opal/spec/lib/spec/example/example_group_factory.rb +0 -43
  303. data/opals/opal/spec/lib/spec/example/example_group_hierarchy.rb +0 -45
  304. data/opals/opal/spec/lib/spec/example/example_group_proxy.rb +0 -41
  305. data/opals/opal/spec/lib/spec/example/example_methods.rb +0 -73
  306. data/opals/opal/spec/lib/spec/example/example_proxy.rb +0 -48
  307. data/opals/opal/spec/lib/spec/expectations.rb +0 -46
  308. data/opals/opal/spec/lib/spec/expectations/errors.rb +0 -35
  309. data/opals/opal/spec/lib/spec/expectations/fail_with.rb +0 -37
  310. data/opals/opal/spec/lib/spec/expectations/handler.rb +0 -48
  311. data/opals/opal/spec/lib/spec/matchers.rb +0 -50
  312. data/opals/opal/spec/lib/spec/matchers/be.rb +0 -26
  313. data/opals/opal/spec/lib/spec/matchers/generated_descriptions.rb +0 -47
  314. data/opals/opal/spec/lib/spec/matchers/operator_matcher.rb +0 -66
  315. data/opals/opal/spec/lib/spec/runner.rb +0 -48
  316. data/opals/opal/spec/lib/spec/runner/example_group_runner.rb +0 -71
  317. data/opals/opal/spec/lib/spec/runner/formatter/terminal_formatter.rb +0 -82
  318. data/opals/opal/spec/lib/spec/runner/options.rb +0 -63
  319. data/opals/opal/spec/resources/index.html +0 -25
  320. data/opals/opal/spec/resources/spec.css +0 -132
  321. data/spec/cherry_kit/iseq_spec.rb +0 -38
  322. data/spec/spec_helper.rb +0 -16
  323. data/spec/vienna_spec.rb +0 -7
  324. data/yard/index.html +0 -43
  325. data/yard/style.css +0 -765
@@ -0,0 +1,153 @@
1
+
2
+ module Opal
3
+
4
+ # The gem class wraps around RubyGem's Gem::Specification class to basically add
5
+ # support for building a gem ready for the browser. The actual gem object can be
6
+ # accessed through {#spec}.
7
+ class Gem
8
+
9
+ # The real Gem::Specification object, which is used for accessing gem info
10
+ attr_reader :spec
11
+
12
+ # Root directory for the package
13
+ attr_reader :root_dir
14
+
15
+ def initialize(rootdir)
16
+ require 'rubygems'
17
+ @root_dir = rootdir
18
+ path = File.expand_path File.join(@root_dir, File.basename(@root_dir) + '.gemspec')
19
+
20
+ # we MUST chdir here to make sure files globs etc in the gemspec use the basedir
21
+ # as the working directory, otherwise opal, or the main gem working dir will
22
+ # accidentally be the cwd.
23
+ Dir.chdir(File.dirname(path)) do
24
+ @spec = ::Gem::Specification.load path
25
+ end
26
+
27
+ raise "Bad/missing gemspec in #{rootdir}" unless @spec
28
+ end
29
+
30
+ def name
31
+ @spec.name
32
+ end
33
+
34
+ def version
35
+ @spec.version.to_s
36
+ end
37
+
38
+ def require_paths
39
+ @spec.require_paths
40
+ end
41
+
42
+ def files
43
+ @spec.files
44
+ end
45
+
46
+ def to_s
47
+ "#<Gem '#{name}'>"
48
+ end
49
+
50
+ # Given the dependency name (required by this package), resolve its
51
+ # location on disk, or nil if it cannot be found. For now this will only
52
+ # check the built in locations, but in future it should search within the
53
+ # package first etc.
54
+ #
55
+ # @param {String} dep The name of the dependency
56
+ # @return {String, nil} Path to package if found
57
+ def find_dependency(dep)
58
+ search_dir = File.expand_path File.join(__FILE__, '..', '..', '..', 'gems')
59
+ # FIXME we should not rely on dirname being same as package name. We should
60
+ # really go throgh all search dirs in order, and check each directory in that,
61
+ # and check its json file if it exists, then check the jsons name property
62
+ package_dir = File.join search_dir, dep
63
+ return nil unless File.exists? package_dir
64
+
65
+ json_file = File.join package_dir, dep + '.gemspec'
66
+ return nil unless File.exists? json_file
67
+
68
+ package_dir
69
+ end
70
+
71
+ # Returns an array of dependencies for this package. Version requirements
72
+ # are ignored for now. This simply returns the array of package names that
73
+ # are needed. If the package.json does not declare any, then an empty
74
+ # array is returned.
75
+ #
76
+ # @return {Array<String>}
77
+ def dependencies
78
+ dependencies = @spec.dependencies
79
+
80
+ dependencies.map { |d| d.name }
81
+ end
82
+
83
+ # Returns the contents of the given file, relative to the root_dir,
84
+ # wrapped ready for use in the browser. Javascript sources will simply
85
+ # be wrapped in a function with its free variables, and ruby sources will
86
+ # be compiled, then wrapped.
87
+ #
88
+ # @return {String}
89
+ def wrap_source(path)
90
+ full_path = File.expand_path File.join(root_dir, path)
91
+ ext_name = File.extname path
92
+
93
+ content = case ext_name
94
+ when '.js'
95
+ source = File.read full_path
96
+ "function(VM, self, __FILE__) { #{source} }"
97
+
98
+ when '.rb'
99
+ source = Opal::RubyParser.new(File.read(full_path)).parse!.generate_top
100
+ # compiled ruby is now javascript, so dont try and fool opal.js!
101
+ path = path.sub(/\.rb/, '.js')
102
+ "function(VM, self, __FILE__) { #{source} }"
103
+
104
+ else
105
+ raise "Bad file type for wrapping. Must be ruby or javascript"
106
+ end
107
+
108
+ path.inspect + ": " + content
109
+ end
110
+
111
+ # Returns the files from the .files property of the gem that are only
112
+ # available from the require_paths array. Basically, these are the files
113
+ # that are our 'lib' files. They are returned as relative to the root.
114
+ #
115
+ # @return [Array<String>]
116
+ def lib_files
117
+ paths = require_paths
118
+ ext = %w[.rb .js]
119
+
120
+ files.select do |f|
121
+ f.start_with?(*paths) && ext.include?(File.extname(f))
122
+ end
123
+ end
124
+
125
+ # Returns a string that can be written to a file to be used with opal in
126
+ # the browser.
127
+ #
128
+ # @return {String}
129
+ def to_bundle
130
+ %[
131
+ opal.register("#{self.name}", {
132
+ "name": #{self.name.inspect},
133
+ "version": #{self.version.inspect},
134
+ "require_paths": #{self.require_paths.inspect},
135
+ "files": {
136
+ #{lib_files.map { |f| wrap_source f }.join(",\n ") }
137
+ }
138
+ });
139
+ ]
140
+ end
141
+
142
+ # This will return a simple string. For now, resources etc are not
143
+ # supported, and will be added back later. This simply calls
144
+ # to_bundle on itself, and all of its dependencies..
145
+ #
146
+ # @return {String}
147
+ def bundle(opts = {})
148
+ b = Bundle.new(self, opts)
149
+ b.build
150
+ end
151
+ end
152
+ end
153
+
@@ -0,0 +1,1302 @@
1
+ class Opal::RubyParser < Racc::Parser
2
+
3
+ # Indent for generated code scopes; 2 spaces, never use tabs
4
+ INDENT = ' '
5
+
6
+ LEVEL_TOP = 0 # normal top level statements
7
+ LEVEL_TOP_CLOSURE = 1 # normal top level, but wrapped in js closure
8
+ LEVEL_LIST = 2
9
+ LEVEL_EXPR = 3
10
+
11
+ # Base node for generators. All other nodes inherit from this
12
+ class BaseNode
13
+
14
+ attr_reader :line
15
+
16
+ # Generate the code for this node. This MUST be overriden in subclasses.
17
+ def generate(opts, level)
18
+ ''
19
+ end
20
+
21
+ # Makes the node return its value. Overriden by various subclasses. This is
22
+ # not for use with the ruby return statement, this just means that the
23
+ # generated scope requires us to return within a javascript function. The
24
+ # return statement in ruby uses another method for returning.
25
+ def returns
26
+ FuncReturnNode.new self
27
+ end
28
+
29
+ # By default, all nodes are expressions (';' to finish them). Statements
30
+ # override this to be false.
31
+ def expression?
32
+ true
33
+ end
34
+
35
+ # Processes the node, which generates it. By default, process will also fix
36
+ # the line number etc. Some nodes override this as they need a slightly
37
+ # different approach. This will also set the level for indentation?? To
38
+ # generate, but not indent or fix the line number, you may call {#generate}
39
+ # directly. Note that this relies on level. If the level is {LEVEL_LIST},
40
+ # for example, then a node will not correct its line number or indentation.
41
+ def process(opts, level)
42
+ if level <= LEVEL_LIST
43
+ fix_line_number(opts) + generate(opts, level)
44
+ else
45
+ generate opts, level
46
+ end
47
+ end
48
+
49
+ # Fix line numbers for nodes that need to. This returns code that is used
50
+ # inside {#process}. Basically, this returns a string of new line chars
51
+ # which will be prepended to the generated code for this node. This will
52
+ # use the {@line} local ivar, if present, or you may pass a direct line
53
+ # number into the {line} parameter.
54
+ def fix_line_number(opts, line = nil)
55
+ code = ''
56
+ # make sure we are on the right line
57
+ target = line || @line
58
+ current = opts[:top].line
59
+
60
+ if current < target
61
+ (target - current).times {
62
+ opts[:top].line += 1
63
+ code += "\n"
64
+ }
65
+
66
+ code += opts[:indent]
67
+ end
68
+
69
+ code
70
+ end
71
+ end
72
+
73
+ # Scope nodes. All scope nodes inherit from this node, including: method,
74
+ # class, def, etc.
75
+ class ScopeNode < BaseNode
76
+
77
+ attr_reader :variables
78
+
79
+ attr_reader :parent
80
+
81
+ def initialize(parent, statements)
82
+ @parent = parent
83
+ @statements = statements
84
+ # all variables - arg, tempts, params etc
85
+ @variables = []
86
+ # all vars for scope and temp
87
+ @scope_vars = []
88
+ # temps
89
+ @temp_current = 'a'
90
+ @temp_queue = []
91
+ # ivars..we need to make sure these exist (make sure they are nil if new)
92
+ @ivars = []
93
+ end
94
+
95
+ def ensure_ivar(name)
96
+ @ivars << name unless @ivars.include? name
97
+ end
98
+
99
+ def param_variable(name)
100
+ @variables << name
101
+ end
102
+
103
+ def ensure_variable(name)
104
+ variable = find_variable name
105
+ return variable if variable
106
+
107
+ # does not exist in scope
108
+ @scope_vars << name
109
+ @variables << name
110
+ end
111
+
112
+ def find_variable(name)
113
+ scope = self
114
+
115
+ while scope
116
+ return name if scope.variables.include? name
117
+
118
+ if scope.is_a?(BlockNode) && scope.parent
119
+ scope = scope.parent
120
+ else
121
+ break
122
+ end
123
+ end
124
+
125
+ nil
126
+ end
127
+
128
+ def temp_local
129
+ return @temp_queue.pop if @temp_queue.last
130
+
131
+ name = '__' + @temp_current
132
+ @scope_vars << name
133
+ @temp_current = @temp_current.succ
134
+ name
135
+ end
136
+
137
+ def queue_temp(temp)
138
+ @temp_queue << temp
139
+ end
140
+
141
+ def set_uses_block
142
+ return @block_arg_name if @block_arg_name
143
+
144
+ @block_arg_name = '__block__'
145
+ end
146
+
147
+ def generate(opts, level)
148
+ stmts = @statements.generate opts, level
149
+ vars = ''
150
+
151
+ vars + stmts
152
+ end
153
+ end
154
+
155
+ # Top level scope. This also manages things like line numbers etc. All opts
156
+ # will be passed a :top key, that references this root scope (instead of
157
+ # needing to manually traverse it each time).
158
+ class TopScopeNode < ScopeNode
159
+
160
+ # helpers we need to add to top of file
161
+ attr_reader :file_helpers
162
+
163
+ # keep track of the current line number in the generator
164
+ attr_accessor :line
165
+
166
+ def initialize(statements)
167
+ super nil, statements
168
+ @file_helpers = []
169
+ @line = 1
170
+ @mm_ids = []
171
+ end
172
+
173
+ # Register a method name that needs to be called to $opal.mm. This method
174
+ # will remove duplicates.
175
+ def register_mm_id(mid)
176
+ @mm_ids << mid unless @mm_ids.include? mid
177
+ end
178
+
179
+ def generate(opts, level)
180
+ code = []
181
+ code << super(opts, level)
182
+
183
+ pre = ''
184
+ pre += 'var nil = VM.Qnil, '
185
+ pre += '$class = VM.dc, $def = VM.dm, $symbol = VM.Y, '
186
+ pre += '$hash = VM.H, $block = VM.P, Qtrue = VM.Qtrue, Qfalse = VM.Qfalse;'
187
+
188
+ # add method missing setup
189
+ if @mm_ids.length > 0
190
+ mm_ids = "VM.mm(['#{@mm_ids.join "', '"}']);"
191
+ pre += mm_ids
192
+ end
193
+
194
+ # local vars... only if we used any..
195
+ unless @scope_vars.empty?
196
+ pre = pre + "var #{@scope_vars.join ', '};"
197
+ end
198
+
199
+ # ivars
200
+ @ivars.each do |ivar|
201
+ pre += "if (self['#{ivar}'] == undefined) { self['#{ivar}'] = nil; }"
202
+ end
203
+
204
+ pre + code.join('')
205
+ end
206
+ end
207
+
208
+ # Statements - represents any chain of statements
209
+ class StatementsNode < BaseNode
210
+
211
+ attr_reader :nodes
212
+
213
+ def initialize(nodes = [])
214
+ @line = 0
215
+ @nodes = nodes
216
+ end
217
+
218
+ def returns
219
+ if @nodes.length > 0
220
+ @nodes[-1] = @nodes[-1].returns
221
+ else
222
+ @nodes << FuncReturnNode.new(NilNode.new)
223
+ end
224
+ end
225
+
226
+ def generate(opts, level)
227
+ code = []
228
+
229
+ return NilNode.new.generate(opts, level) if @nodes.empty?
230
+
231
+ @nodes.each do |node|
232
+ node_code = node.process opts, LEVEL_TOP
233
+
234
+ if level <= LEVEL_TOP_CLOSURE
235
+ # to prevent lots of trailing whitespace when we generate statements
236
+ # on new lines, we only insert indent if we dont have a newline
237
+ # marker straight away
238
+ if node_code[0] == "\n"
239
+ code << node_code
240
+ else
241
+ code << (opts[:indent] + node_code)
242
+ end
243
+
244
+ # if expression, add ';' .. statements don't need ';'. We MUST call
245
+ # this after we generate it, as some statements may determine
246
+ # themselves during compilation. For example, IfNode does this
247
+ # depending on whether it needs to generate as a LEVEL_TOP, or as a
248
+ # LEVEL_TOP_CLOSURE.
249
+ code << ';' if node.expression?
250
+
251
+ else
252
+ code << node_code
253
+ end
254
+ end
255
+
256
+ code.join ''
257
+ end
258
+
259
+ # Push more statements onto end.
260
+ def <<(node)
261
+ @nodes << node
262
+ self
263
+ end
264
+
265
+ # Generate statements for top level. Generally used for files
266
+ def generate_top(opts = {})
267
+ scope = TopScopeNode.new self
268
+ opts[:scope] = scope
269
+ opts[:indent] = ''
270
+ opts[:top] = scope
271
+ scope.generate opts, LEVEL_TOP
272
+ end
273
+ end
274
+
275
+ class NumericNode < BaseNode
276
+
277
+ def initialize(val)
278
+ @line = val[:line]
279
+ @value = val[:value]
280
+ end
281
+
282
+ def generate(opts, level)
283
+ @value.to_s
284
+ end
285
+ end
286
+
287
+ class SymbolNode < BaseNode
288
+
289
+ def initialize(val)
290
+ @line = val[:line]
291
+ @value = val[:value]
292
+ end
293
+
294
+ def generate(opts, level)
295
+ "$symbol('#{@value}')"
296
+ end
297
+ end
298
+
299
+ class CallNode < BaseNode
300
+
301
+ # any call may have a block assigned to it
302
+ attr_writer :block
303
+
304
+ attr_reader :recv
305
+
306
+ attr_reader :mid
307
+
308
+ def initialize(recv, mid, args)
309
+ @recv = recv
310
+ @mid = mid[:value]
311
+ @args = args
312
+ @line = recv ? recv.line : mid[:line]
313
+ end
314
+
315
+ def mid_to_jsid(id)
316
+ return ".$m['#{id}']" if /[\!\=\?\+\-\*\/\^\&\%\@\|\[\]\<\>\~]/ =~ id
317
+
318
+ # FIXME: if our id is a reserved word in js, we need to also wrap it in
319
+ # brackets.
320
+ return ".$m['#{id}']" if js_reserved_words.include? id
321
+
322
+ # default we just do .method_name
323
+ '.$m.' + id
324
+ end
325
+
326
+ # Reserved js words - we cannot just generate properties with these names
327
+ # as they will cause a parse error, so we need to wrap them in brackets.
328
+ def js_reserved_words
329
+ %w[break case catch continue debugger default delete do else finally
330
+ for function if in instanceof new return switch this throw try typeof
331
+ var void while with class enum export extends import super]
332
+ end
333
+
334
+ def generate(opts, level)
335
+ code, arg_res, recv, mid = '', [], nil, nil
336
+
337
+ # we need a temp var for the receiver, which we add to the front of
338
+ # the args to send.
339
+ tmp_recv = opts[:scope].temp_local
340
+
341
+ # receiver
342
+ if @recv.is_a? NumericNode
343
+ recv = "(#{@recv.process opts, LEVEL_EXPR})"
344
+ elsif @recv
345
+ recv = @recv.process opts, LEVEL_EXPR
346
+ else
347
+ recv = SelfNode.new.generate opts, LEVEL_EXPR
348
+ end
349
+
350
+ # method id
351
+ mid = mid_to_jsid @mid
352
+
353
+ # Register our method_id to ensure $opal.mm gets it
354
+ opts[:top].register_mm_id @mid
355
+
356
+ args = @args
357
+ # normal args
358
+ if args[0]
359
+ args[0].each do |arg|
360
+ arg_res << arg.generate(opts, LEVEL_EXPR)
361
+ end
362
+ end
363
+
364
+ # hash assoc args
365
+ if args[2]
366
+ arg_res << HashNode.new(args[2], { :line => 0 }, { :line => 0 }).generate(opts, LEVEL_EXPR)
367
+ end
368
+
369
+ if @block
370
+ # tmp_self = opts[:scope].temp_local
371
+ tmp_args = opts[:scope].temp_local
372
+ block = @block.generate opts, LEVEL_TOP
373
+ arg_res.unshift tmp_recv
374
+
375
+ code = "(#{tmp_recv} = #{recv}, #{tmp_args} = [#{arg_res.join ', '}]"
376
+ code += ", ($block.p = #{block}).$self = #{SelfNode.new.generate opts, level}"
377
+ code += ", ($block.f = #{tmp_recv}#{mid}).apply(#{tmp_recv}, #{tmp_args}))"
378
+
379
+ opts[:scope].queue_temp tmp_recv
380
+ opts[:scope].queue_temp tmp_args
381
+
382
+ code
383
+
384
+ # &to_proc. Note, this must not reassign the $self for the proc.. we are
385
+ # just passing on an existing block.
386
+ #
387
+ # FIXME need to actually call to_proc.
388
+ elsif args[3]
389
+ tmp_args = opts[:scope].temp_local
390
+
391
+ arg_res.unshift tmp_recv
392
+
393
+ code = "(#{tmp_recv} = #{recv}, #{tmp_args} = [#{arg_res.join ', '}]"
394
+ code += ", ($block.p = #{args[3].process opts, LEVEL_LIST})"
395
+ code += ", ($block.f = #{tmp_recv}#{mid}).apply(#{tmp_recv}, #{tmp_args}))"
396
+
397
+ opts[:scope].queue_temp tmp_recv
398
+ opts[:scope].queue_temp tmp_args
399
+
400
+ code
401
+
402
+ # no block
403
+ else
404
+ # splat args
405
+ if args[1]
406
+ arg_res.unshift tmp_recv
407
+ splat = args[1].generate(opts, LEVEL_EXPR)
408
+ splat_args = arg_res.empty? ? splat : "[#{arg_res.join ', '}].concat(#{splat})"
409
+ # when using splat, our this val for apply may need a tmp var
410
+ # to save just outputting it twice (have to follow recv path twice)
411
+ splat_recv = recv
412
+ result = "(#{tmp_recv} = #{recv})" + mid + ".apply(nil, #{splat_args})"
413
+
414
+ opts[:scope].queue_temp tmp_recv
415
+ result
416
+ else
417
+ arg_res.unshift tmp_recv
418
+
419
+ result = "(#{tmp_recv} = #{recv})" + mid + '(' + arg_res.join(', ') + ')'
420
+
421
+ # requeue the tmp receiver as we are done with it and return
422
+ opts[:scope].queue_temp tmp_recv
423
+ result
424
+ end
425
+ end
426
+ end
427
+ end
428
+
429
+ class SelfNode < BaseNode
430
+ # We often use a fake SelfNode for filling in gaps, so it takes a default
431
+ # hash to save us manually making one every time.
432
+ def initialize(val = { :line => 0 })
433
+ @line = val[:line]
434
+ end
435
+
436
+ def generate(opts, level)
437
+ 'self'
438
+ end
439
+ end
440
+
441
+ class NilNode < BaseNode
442
+ # Default val hash to save us passing them into fake nodes
443
+ def initialize(val = { :line => 0 })
444
+ @line = val[:line]
445
+ end
446
+
447
+ def generate(opts, level)
448
+ 'nil'
449
+ end
450
+ end
451
+
452
+ class ModuleNode < ScopeNode
453
+
454
+ def initialize(mod, path, body, _end)
455
+ super nil, body
456
+ @line = mod[:line]
457
+ @base = path[0]
458
+ @class_name = path[1][:value]
459
+ @end_line = _end[:line]
460
+ end
461
+
462
+ def generate(opts, level)
463
+ code = '$class('
464
+
465
+ # base
466
+ if @base.nil?
467
+ code += SelfNode.new.generate(opts, level)
468
+ else
469
+ code += 'w'
470
+ end
471
+
472
+ code += ', '
473
+
474
+ # superclass
475
+ code += (NilNode.new.generate(opts, level) + ', ')
476
+
477
+ # module name
478
+ code += "'#{@class_name}', "
479
+
480
+ # scope
481
+ scope = { :indent => opts[:indent] + INDENT, :top => opts[:top], :scope => self }
482
+ stmt = @statements.generate scope, LEVEL_TOP
483
+
484
+ code += ('function() { var self = this; ' + stmt)
485
+
486
+ # fix line ending
487
+ code += fix_line_number opts, @end_line
488
+ code += '}, 2)'
489
+
490
+ code
491
+ end
492
+ end
493
+
494
+ class ClassNode < ScopeNode
495
+
496
+ def initialize(cls, path, sup, body, _end)
497
+ super nil, body
498
+ @scope_vars << 'self = this'
499
+ @line = cls[:line]
500
+ @base = path[0]
501
+ @cls_name = path[1]
502
+ @super = sup
503
+ @end_line = _end[:line]
504
+ end
505
+
506
+ def generate(opts, level)
507
+ code = '$class('
508
+
509
+ # base
510
+ code += (@base.nil? ? SelfNode.new.generate(opts, level) : 'w')
511
+ code += ', '
512
+
513
+ # superclass
514
+ code += (@super ? @super.generate(opts, level) : NilNode.new.generate(opts, level))
515
+ code += ', '
516
+
517
+ # class name
518
+ code += "'#{@cls_name[:value]}', "
519
+
520
+ # scope
521
+ scope = { :indent => opts[:indent] + INDENT, :top => opts[:top], :scope => self }
522
+ stmt = @statements.generate scope, level
523
+ code += "function() { var #{@scope_vars.join ', '};#{stmt}"
524
+
525
+ # fix trailing line number
526
+ code += fix_line_number opts, @end_line
527
+
528
+ code += opts[:indent] + '}, 0)'
529
+ code
530
+ end
531
+ end
532
+
533
+ class DefNode < ScopeNode
534
+
535
+ def initialize(defn, singleton, fname, args, body, endn)
536
+ super nil, body
537
+ # do this early
538
+ @line = defn[:line]
539
+ @singleton = singleton
540
+ @fname = fname
541
+ @args = args
542
+ @body = body
543
+ @end_line = endn[:line]
544
+ end
545
+
546
+ def generate(opts, level)
547
+ code = '$def('
548
+
549
+ # singleton
550
+ code += (@singleton ? @singleton.generate(opts, level) : SelfNode.new.generate(opts, level))
551
+ code += ', '
552
+
553
+ # method id
554
+ code += "'#{@fname[:value]}', "
555
+
556
+ # all method arg names need to be places in function arg list
557
+ method_args = []
558
+
559
+ pre_code = ''
560
+
561
+ # scope
562
+ scope = { :indent => opts[:indent] + INDENT, :top => opts[:top], :scope => self }
563
+
564
+ args = @args
565
+
566
+ # normal args
567
+ if args[0]
568
+ args[0].each do |arg|
569
+ param_variable arg[:value]
570
+ method_args << arg[:value]
571
+ end
572
+ end
573
+
574
+ # optional args
575
+ if args[1]
576
+ args[1].each do |arg|
577
+ param_variable arg[0][:value]
578
+ method_args << arg[0][:value]
579
+ pre_code += "if (#{arg[0][:value]} == undefined) {#{arg[0][:value]} = #{arg[1].generate(opts, LEVEL_EXPR)};}"
580
+ end
581
+ end
582
+
583
+ # rest args
584
+ if args[2]
585
+ param_variable args[2][:value]
586
+ method_args << args[2][:value]
587
+ pre_code += "#{args[2][:value]} = [].slice.call(arguments, #{method_args.length});"
588
+ end
589
+
590
+ # block arg
591
+ if args[3]
592
+ param_variable args[3][:value]
593
+ @block_arg_name = args[3][:value]
594
+ end
595
+
596
+ @body.returns
597
+ stmt = @body.generate scope, LEVEL_TOP
598
+
599
+ method_args.unshift 'self'
600
+
601
+ code += "function(#{method_args.join ', '}) { "
602
+
603
+ # local vars... only if we used any..
604
+ unless @scope_vars.empty?
605
+ pre_code = "var #{@scope_vars.join ', '};" + pre_code
606
+ end
607
+
608
+ # ivars
609
+ @ivars.each do |ivar|
610
+ pre_code += "if (self['#{ivar}'] == undefined) { self['#{ivar}'] = nil; }"
611
+ end
612
+
613
+ # block arg
614
+ if @block_arg_name
615
+ pre_code += " var #{@block_arg_name} = ($block.f == arguments.callee)"
616
+ pre_code += " ? $block.p : nil; $block.p = $block.f = nil;"
617
+ end
618
+
619
+ code += (pre_code + stmt)
620
+
621
+ # fix trailing end and 0/1 for normal/singleton
622
+ code += (fix_line_number(opts, @end_line) + "}, #{@singleton ? '1' : '0'})")
623
+
624
+ code
625
+ end
626
+ end
627
+
628
+ class BodyStatementsNode < BaseNode
629
+
630
+ attr_reader :opt_rescue
631
+
632
+ def initialize(stmt, optrescue, optelse, optensure)
633
+ @statements = stmt
634
+ @opt_rescue = optrescue
635
+ @opt_else = optelse
636
+ @opt_ensure = optensure
637
+ @line = stmt.line
638
+ end
639
+
640
+ def returns
641
+ @statements.returns
642
+ end
643
+
644
+ def generate(opts, level)
645
+ @statements.generate opts, level
646
+ end
647
+ end
648
+
649
+ class OrNode < BaseNode
650
+
651
+ def initialize(node, lhs, rhs)
652
+ @line = node[:line]
653
+ @lhs = lhs
654
+ @rhs = rhs
655
+ end
656
+
657
+ def generate(opts, level)
658
+ res = '(('
659
+ tmp = opts[:scope].temp_local
660
+ res += "#{tmp} = #{@lhs.generate opts, LEVEL_LIST}).$r ? "
661
+ res += "#{tmp} : #{@rhs.generate opts, LEVEL_LIST})"
662
+ opts[:scope].queue_temp tmp
663
+ res
664
+ end
665
+ end
666
+
667
+ class AndNode < BaseNode
668
+
669
+ def initialize(node, lhs, rhs)
670
+ @line = node[:line]
671
+ @lhs = lhs
672
+ @rhs = rhs
673
+ end
674
+
675
+ def generate(opts, level)
676
+ res = '(('
677
+ tmp = opts[:scope].temp_local
678
+ res += "#{tmp} = #{@lhs.generate opts, LEVEL_LIST}).$r ? "
679
+ res += "#{@rhs.generate opts, LEVEL_LIST} : #{tmp})"
680
+ opts[:scope].queue_temp tmp
681
+ res
682
+ end
683
+ end
684
+
685
+ class ArrayNode < BaseNode
686
+
687
+ def initialize(parts, begn, endn)
688
+ @line = begn[:line]
689
+ @end_line = endn[:line]
690
+ @args = parts
691
+ end
692
+
693
+ # We should really alter opts[:indent] to temp increase it so that args
694
+ # on a new line are indented to that of the array beg/end
695
+ def generate(opts, level)
696
+ parts = @args[0].map { |arg| arg.process opts, LEVEL_LIST }
697
+ "[#{parts.join ', '}#{fix_line_number opts, @end_line}]"
698
+ end
699
+ end
700
+
701
+ class HashNode < BaseNode
702
+
703
+ def initialize(parts, begn, endn)
704
+ @line = begn[:line]
705
+ @end_line = endn[:line]
706
+ @parts = parts
707
+ end
708
+
709
+ def generate(opts, level)
710
+ parts = @parts.flatten.map { |part| part.process opts, LEVEL_LIST }
711
+ "$hash(#{parts.join ', '}#{fix_line_number opts, @end_line})"
712
+ end
713
+ end
714
+
715
+ class IfNode < BaseNode
716
+
717
+ def initialize(begn, expr, stmt, tail, endn)
718
+ @line = begn[:line]
719
+ @end_line = endn[:line]
720
+ @expr = expr
721
+ @stmt = stmt
722
+ @tail = tail
723
+ end
724
+
725
+ def returns
726
+ @stmt.returns
727
+ # need to apply to each tail item
728
+ @tail.each do |tail|
729
+ if tail[0][:value] == 'elsif'
730
+ tail[2].returns
731
+ else
732
+ tail[1].returns
733
+ end
734
+ end
735
+ self
736
+ end
737
+
738
+ def expression?
739
+ @expr_level
740
+ end
741
+
742
+ def generate(opts, level)
743
+ code, done_else, tail, old_indent = '', false, nil, opts[:indent]
744
+
745
+ opts[:indent] += INDENT
746
+
747
+ # stmt_level is level_top, unless we are an expression.. then it is level_top_closure..
748
+ stmt_level = (level == LEVEL_EXPR ? LEVEL_TOP_CLOSURE : LEVEL_TOP)
749
+
750
+ if stmt_level == LEVEL_TOP_CLOSURE
751
+ returns
752
+ @level_expr = true
753
+ end
754
+
755
+ code += "if ((#{@expr.generate opts, LEVEL_EXPR}).$r) {#{@stmt.process opts, stmt_level}"
756
+
757
+ @tail.each do |tail|
758
+ opts[:indent] = old_indent
759
+ code += fix_line_number opts, tail[0][:line]
760
+
761
+ if tail[0][:value] == 'elsif'
762
+ code += "} else if ((#{tail[1].generate opts, LEVEL_EXPR}).$r) {"
763
+ opts[:indent] += INDENT
764
+ code += tail[2].process(opts, stmt_level)
765
+ else
766
+ done_else = true
767
+ code += '} else {'
768
+ opts[:indent] += INDENT
769
+ code += tail[1].process(opts, stmt_level)
770
+ end
771
+ end
772
+
773
+ if @force_else
774
+ # generate an else statement if we MUST have one. If, for example, we
775
+ # set the result of ourself as a variable, we must have an else part
776
+ # which simply returns nil.
777
+ end
778
+
779
+ opts[:indent] = old_indent
780
+ code += (fix_line_number(opts, @end_line) + '}')
781
+
782
+ # if we were an expression, we need to wrap ourself as closure
783
+ code = "(function() {#{code})()" if level == LEVEL_EXPR
784
+ code
785
+ end
786
+ end
787
+
788
+ class ConstantNode < BaseNode
789
+
790
+ def initialize(name)
791
+ @line = name[:line]
792
+ @name = name[:value]
793
+ end
794
+
795
+ def value
796
+ @name
797
+ end
798
+
799
+ def generate(opts, level)
800
+ "rb_vm_cg(#{SelfNode.new.generate opts, level}, '#{@name}')"
801
+ end
802
+ end
803
+
804
+ class Colon2Node < BaseNode
805
+
806
+ def initialize(lhs, name)
807
+ @lhs = lhs
808
+ @line = name[:line]
809
+ @name = name[:value]
810
+ end
811
+
812
+ def generate(opts, level)
813
+ # FIXME This should really be 'const at'.. const_get will relook all the way up chain
814
+ "rb_vm_cg(#{@lhs.generate opts, level}, '#{@name}')"
815
+ end
816
+ end
817
+
818
+ class Colon3Node < BaseNode
819
+
820
+ def initialize(name)
821
+ @line = name[:line]
822
+ @name = name[:value]
823
+ end
824
+
825
+ def generate(opts, level)
826
+ "rm_vm_cg($opal.Object, '#{@name}')"
827
+ end
828
+ end
829
+
830
+ class AssignNode < BaseNode
831
+
832
+ def initialize(lhs, rhs, assign = {})
833
+ @line = lhs.line
834
+ @lhs = lhs
835
+ @rhs = rhs
836
+ end
837
+
838
+ def generate(opts, level)
839
+ if @lhs.is_a? IvarNode
840
+ return "#{SelfNode.new.generate(opts, level)}['#{@lhs.value}'] = #{@rhs.generate(opts, LEVEL_EXPR)}"
841
+
842
+ elsif @lhs.is_a? GvarNode
843
+ return "VM.gs('#{@lhs.value}', #{@rhs.generate(opts, LEVEL_EXPR)})"
844
+
845
+ elsif @lhs.is_a? IdentifierNode
846
+ opts[:scope].ensure_variable @lhs.value
847
+ return @lhs.value + " = " + @rhs.generate(opts, LEVEL_EXPR)
848
+
849
+ elsif @lhs.is_a? ArefNode
850
+ return AsetNode.new(@lhs.recv, @lhs.arefs, @rhs).process(opts, level)
851
+
852
+ elsif @lhs.is_a? ConstantNode
853
+ return "rb_vm_cs(self, '#{@lhs.value}', #{@rhs.generate(opts, LEVEL_EXPR)})"
854
+
855
+ elsif @lhs.is_a? CallNode
856
+ return CallNode.new(@lhs.recv, { :value => @lhs.mid + '=', :line => @line }, [[@rhs]]).generate(opts, level);
857
+
858
+ else
859
+ raise "Bad lhs for assign on #{@line}"
860
+ end
861
+ end
862
+ end
863
+
864
+ class OpAsgnNode < BaseNode
865
+
866
+ def initialize(asgn, lhs, rhs)
867
+ @line = asgn[:line]
868
+ @lhs = lhs
869
+ @rhs = rhs
870
+ @asgn = asgn[:value]
871
+ end
872
+
873
+ def generate(opts, level)
874
+ assign = nil
875
+
876
+ if @asgn == '||'
877
+ assign = OrNode.new({:value => '||', :line => @line }, @lhs, AssignNode.new(@lhs, @rhs))
878
+ elsif %w[+ - / *].include? @asgn
879
+ assign = AssignNode.new @lhs, CallNode.new(@lhs, {:value => @asgn, :line => @line}, [[@rhs]])
880
+ else
881
+ raise "Bas op asgn type: #{@asgn}"
882
+ end
883
+ assign.generate(opts, level)
884
+ end
885
+ end
886
+
887
+ class IvarNode < BaseNode
888
+
889
+ attr_reader :value
890
+
891
+ def initialize(val)
892
+ @line = val[:line]
893
+ @value = val[:value]
894
+ end
895
+
896
+ def generate(opts, level)
897
+ opts[:scope].ensure_ivar @value
898
+ "#{SelfNode.new.generate(opts, level)}['#{@value}']"
899
+ end
900
+ end
901
+
902
+ class IdentifierNode < BaseNode
903
+
904
+ attr_reader :value
905
+
906
+ def initialize(val)
907
+ @line = val[:line]
908
+ @value = val[:value]
909
+ end
910
+
911
+ def generate(opts, level)
912
+ if opts[:scope].find_variable @value
913
+ @value
914
+ else
915
+ CallNode.new(nil, { :value => @value, :line => @line }, [[]]).generate(opts, level)
916
+ end
917
+ end
918
+ end
919
+
920
+ class FuncReturnNode < BaseNode
921
+
922
+ def initialize(val)
923
+ @value = val
924
+ @line = val.line
925
+ end
926
+
927
+ def generate(opts, level)
928
+ "return #{@value.generate opts, level}"
929
+ end
930
+ end
931
+
932
+ class StringNode < BaseNode
933
+
934
+ def initialize(parts, endn)
935
+ @line = endn[:line]
936
+ @parts = parts
937
+ @join = endn[:value]
938
+ end
939
+
940
+ def generate(opts, level)
941
+ if @parts.length == 0
942
+ "''"
943
+ elsif @parts.length == 1
944
+ if @parts[0][0] == 'string_content'
945
+ @join + @parts[0][1][:value] + @join
946
+ end
947
+
948
+ else
949
+ parts = @parts.map do |part|
950
+ if part[0] == 'string_content'
951
+ @join + part[1][:value] + @join
952
+ elsif part[0] == 'string_dbegin'
953
+ CallNode.new(part[1], { :value => 'to_s', :line => 0 }, [[]]).generate(opts, level)
954
+ end
955
+ end
956
+
957
+ '(' + parts.join(' + ') + ')'
958
+ end
959
+ end
960
+ end
961
+
962
+ class TrueNode < BaseNode
963
+
964
+ def initialize(val)
965
+ @line = val[:line]
966
+ end
967
+
968
+ def generate(opts, level)
969
+ "Qtrue"
970
+ end
971
+ end
972
+
973
+ class FalseNode < BaseNode
974
+
975
+ def initialize(val)
976
+ @line = val[:line]
977
+ end
978
+
979
+ def generate(opts, level)
980
+ "Qfalse"
981
+ end
982
+ end
983
+
984
+ class BlockNode < ScopeNode
985
+
986
+ def initialize(start, vars, stmt, endn)
987
+ super nil, stmt
988
+ @line = start[:line]
989
+ @args = vars
990
+ @stmt = stmt
991
+ @end_line = endn[:line]
992
+ end
993
+
994
+ def generate(opts, level)
995
+ @parent = opts[:scope]
996
+ pre_code = ''
997
+ code = ''
998
+
999
+ scope = { :scope => self, :top => opts[:top], :indent => opts[:indent] + INDENT }
1000
+ args = @args[0]
1001
+ method_args = []
1002
+
1003
+ if args
1004
+ # normal args
1005
+ if args[0]
1006
+ args[0].each do |arg|
1007
+ param_variable arg[:value]
1008
+ method_args << arg[:value]
1009
+ end
1010
+ end
1011
+
1012
+ # optional args
1013
+ if args[1]
1014
+ args[1].each do |arg|
1015
+ opt_arg_name = arg[0][:value]
1016
+ param_variable opt_arg_name
1017
+ method_args << arg[0][:value]
1018
+ pre_code += "if (#{opt_arg_name} == undefined) { #{opt_arg_name} = #{arg[1].generate(opts, level)};}"
1019
+ end
1020
+ end
1021
+
1022
+ # rest args
1023
+ if args[2]
1024
+ rest_arg_name = args[2][:value]
1025
+ # FIXME if we just pass '*', then we make a tmp variable name for it..
1026
+ param_variable rest_arg_name
1027
+ method_args << rest_arg_name
1028
+ pre_code += "#{rest_arg_name} = [].slice.call(arguments, #{method_args.length});"
1029
+ end
1030
+ end
1031
+
1032
+ @stmt.returns
1033
+ stmt = @stmt.process scope, LEVEL_TOP
1034
+ method_args.unshift 'self'
1035
+
1036
+ code += "function(#{method_args.join ', '}) {"
1037
+
1038
+ unless @scope_vars.empty?
1039
+ code += " var #{@scope_vars.join ', '};"
1040
+ end
1041
+
1042
+ code += (pre_code + stmt + fix_line_number(opts, @end_line) + "}")
1043
+ code
1044
+ end
1045
+ end
1046
+
1047
+ class XStringNode < BaseNode
1048
+
1049
+ def initialize(begn, parts, endn)
1050
+ @line = begn[:line]
1051
+ @parts = parts
1052
+ @end_line = endn[:line]
1053
+ end
1054
+
1055
+ # we dont want return for xstring.. or do we? no..
1056
+ def returns
1057
+ self
1058
+ end
1059
+
1060
+ # Treat ourself like an expression. All xstrings should add their own
1061
+ # semi-colons etc, so we can allow if, return, etc.
1062
+ def expression?
1063
+ false
1064
+ end
1065
+
1066
+ def generate(opts, level)
1067
+ parts = @parts.map do |part|
1068
+ if part[0] == 'string_content'
1069
+ part[1][:value]
1070
+ elsif part[0] == 'string_dbegin'
1071
+ part[1].generate opts, LEVEL_EXPR
1072
+ end
1073
+ end
1074
+
1075
+ parts.join ''
1076
+ end
1077
+ end
1078
+
1079
+ class ParenNode < BaseNode
1080
+
1081
+ def initialize(opening, parts, closing)
1082
+ @line = opening[:line]
1083
+ @parts = parts
1084
+ @end_line = closing[:line]
1085
+ end
1086
+
1087
+ def generate(opts, level)
1088
+ parts = @parts.nodes.map do |part|
1089
+ part.generate opts, LEVEL_EXPR
1090
+ end
1091
+
1092
+ # if no parens, then we need to eval to nil
1093
+ parts << 'nil' if parts.empty?
1094
+
1095
+ "(#{parts.join ', '})"
1096
+ end
1097
+ end
1098
+
1099
+ class ArefNode < BaseNode
1100
+
1101
+ attr_reader :recv
1102
+
1103
+ attr_reader :arefs
1104
+
1105
+ def initialize(recv, arefs)
1106
+ @line = recv.line
1107
+ @recv = recv
1108
+ @arefs = arefs
1109
+ end
1110
+
1111
+ def generate(opts, level)
1112
+ CallNode.new(@recv, { :line => @line, :value => '[]'}, @arefs).generate opts, level
1113
+ end
1114
+ end
1115
+
1116
+ class AsetNode < BaseNode
1117
+
1118
+ def initialize(recv, arefs, val)
1119
+ @line = recv.line
1120
+ @recv = recv
1121
+ @arefs = arefs
1122
+ @val = val
1123
+ end
1124
+
1125
+ def generate(opts, level)
1126
+ (@arefs[0] ||= []) << @val
1127
+ CallNode.new(@recv, { :line => @line, :value => '[]='}, @arefs ).generate(opts, level)
1128
+ end
1129
+ end
1130
+
1131
+ # Used for post form of IF and UNLESS statements
1132
+ class IfModNode < BaseNode
1133
+
1134
+ def initialize(type, expr, stmt)
1135
+ @line = type[:line]
1136
+ @type = type[:value]
1137
+ @expr = expr
1138
+ @stmt = stmt
1139
+ end
1140
+
1141
+ # If we return, that means our "else" result - which is not generated by
1142
+ # default, needs to return nil (as it might be needed if our if statement
1143
+ # does not evaluate truthy
1144
+ def returns
1145
+ @returns = true
1146
+ @stmt = @stmt.returns
1147
+ self
1148
+ end
1149
+
1150
+ def generate(opts, level)
1151
+ # if we return, make sure our stmt does
1152
+ @stmt.returns if @returns
1153
+
1154
+ r = "if(#{@type == 'if' ? '' : '!'}(#{@expr.generate(opts, LEVEL_EXPR)}"
1155
+ r += ").$r) {#{@stmt.process(opts, LEVEL_TOP)}}"
1156
+
1157
+ # also, if we return, we need to ensure we have an else conditional
1158
+ r += " else { return nil; }" if @returns
1159
+ r
1160
+ end
1161
+ end
1162
+
1163
+ class BlockGivenNode < BaseNode
1164
+
1165
+ def initialize(given)
1166
+ @line = given[:line]
1167
+ end
1168
+
1169
+ def generate(opts, level)
1170
+ name = opts[:scope].set_uses_block
1171
+ "(#{name} !== nil ? Qtrue : Qfalse)"
1172
+ end
1173
+ end
1174
+
1175
+ class YieldNode < BaseNode
1176
+
1177
+ def initialize(start, args)
1178
+ @line = start[:line]
1179
+ @args = args
1180
+ end
1181
+
1182
+ def generate(opts, level)
1183
+ # need to get block from nearest method
1184
+ block = opts[:scope].set_uses_block
1185
+
1186
+ parts = ["#{block}.$self"]
1187
+
1188
+ if @args[0]
1189
+ @args[0].each { |arg| parts << arg.generate(opts, LEVEL_EXPR) }
1190
+ end
1191
+
1192
+ "#{block}(#{parts.join ', '})"
1193
+ end
1194
+ end
1195
+
1196
+ class ReturnNode < BaseNode
1197
+
1198
+ def initialize(ret, val)
1199
+ @line = ret[:line]
1200
+ @args = val
1201
+ end
1202
+
1203
+ def returns
1204
+ self
1205
+ end
1206
+
1207
+ def generate(opts, level)
1208
+ args = @args
1209
+
1210
+ if args[0].nil?
1211
+ code = NilNode.new.generate opts, level
1212
+ elsif args[0].length == 1
1213
+ code = args[0][0].generate opts, level
1214
+ else
1215
+ # this really should return array of return vals
1216
+ code = NilNode.new.generate opts, level
1217
+ end
1218
+
1219
+ # if we are in a block, we need to throw return to nearest mthod
1220
+ if !opts[:scope].is_a?(DefNode)
1221
+ return_func = '__return_func'
1222
+ return "$return(#{code}, #{return_func})"
1223
+
1224
+ # level top, we are running full stmts, so just return normally
1225
+ elsif level == LEVEL_TOP
1226
+ return "return #{code}"
1227
+ else
1228
+ "$return(#{code})"
1229
+ end
1230
+ end
1231
+ end
1232
+
1233
+ class BeginNode < BaseNode
1234
+
1235
+ def initialize(beginn, body, endn)
1236
+ @line = beginn[:line]
1237
+ @body = body
1238
+ @end_line = endn[:line]
1239
+ end
1240
+
1241
+ def generate(opts, level)
1242
+ code = "try {"
1243
+ old_indent = opts[:indent]
1244
+ opts[:indent] += INDENT
1245
+
1246
+ code += @body.process opts, LEVEL_TOP
1247
+ code += "} catch (__err__) {"
1248
+
1249
+ @body.opt_rescue.each do |res|
1250
+ code += "#{fix_line_number opts, res[0][:line]}if (true){"
1251
+ opts[:indent] += INDENT
1252
+ opts[:scope].ensure_variable res[2].value
1253
+ code += (res[2].value + " = __err__;")
1254
+ code += "#{res[3].process opts, LEVEL_TOP}}"
1255
+ opts[:indent] = old_indent + INDENT
1256
+ end
1257
+
1258
+
1259
+ opts[:indent] = old_indent
1260
+ code += (fix_line_number(opts, @end_line) + "}")
1261
+ code
1262
+ end
1263
+ end
1264
+
1265
+ class GvarNode < BaseNode
1266
+
1267
+ attr_reader :value
1268
+
1269
+ def initialize(val)
1270
+ @line = val[:line]
1271
+ @value = val[:value]
1272
+ end
1273
+
1274
+ def generate(opts, level)
1275
+ "VM.gg('#{@value}')"
1276
+ end
1277
+ end
1278
+
1279
+ class FileNode < BaseNode
1280
+
1281
+ def initialize(val)
1282
+ @line = val[:line]
1283
+ end
1284
+
1285
+ def generate(opts, level)
1286
+ "__FILE__"
1287
+ end
1288
+ end
1289
+
1290
+ class LineNode < BaseNode
1291
+
1292
+ def initialize(val)
1293
+ @line = val[:line]
1294
+ @val = val[:value]
1295
+ end
1296
+
1297
+ def generate(opts, level)
1298
+ @val
1299
+ end
1300
+ end
1301
+ end
1302
+