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.
- data/.gitignore +5 -10
- data/LICENSE +75 -0
- data/README.md +55 -3
- data/Rakefile +62 -139
- data/bin/opal +7 -10
- data/gems/core/README.md +5 -0
- data/gems/core/Rakefile +7 -0
- data/gems/core/core.gemspec +13 -0
- data/gems/core/lib/core.rb +33 -0
- data/gems/core/lib/core/array.rb +1470 -0
- data/gems/core/lib/core/basic_object.rb +15 -0
- data/gems/core/lib/core/class.rb +31 -0
- data/gems/core/lib/core/dir.rb +26 -0
- data/gems/core/lib/core/error.rb +43 -0
- data/gems/core/lib/core/false_class.rb +21 -0
- data/gems/core/lib/core/file.rb +54 -0
- data/gems/core/lib/core/hash.rb +725 -0
- data/gems/core/lib/core/kernel.rb +240 -0
- data/gems/core/lib/core/module.rb +98 -0
- data/gems/core/lib/core/nil_class.rb +41 -0
- data/gems/core/lib/core/numeric.rb +370 -0
- data/gems/core/lib/core/proc.rb +11 -0
- data/gems/core/lib/core/range.rb +17 -0
- data/gems/core/lib/core/regexp.rb +18 -0
- data/gems/core/lib/core/string.rb +328 -0
- data/gems/core/lib/core/symbol.rb +15 -0
- data/gems/core/lib/core/top_self.rb +8 -0
- data/gems/core/lib/core/true_class.rb +20 -0
- data/gems/core/lib/core/vm.rb +16 -0
- data/{opals/opal/opal → gems/core}/spec/core/array/append_spec.rb +0 -0
- data/{opals/opal/opal → gems/core}/spec/core/array/assoc_spec.rb +0 -0
- data/{opals/opal/opal → gems/core}/spec/core/array/at_spec.rb +0 -0
- data/{opals/opal/opal → gems/core}/spec/core/array/clear_spec.rb +1 -1
- data/{opals/opal/opal → gems/core}/spec/core/array/collect_bang_spec.rb +0 -0
- data/{opals/opal/opal → gems/core}/spec/core/array/collect_spec.rb +0 -0
- data/gems/core/spec/core/array/compact_spec.rb +41 -0
- data/{opals/opal/opal → gems/core}/spec/core/array/concat_spec.rb +0 -0
- data/{opals/opal/opal → gems/core}/spec/core/array/constructor_spec.rb +0 -0
- data/{opals/opal/opal → gems/core}/spec/core/array/each_spec.rb +0 -0
- data/{opals/opal/opal → gems/core}/spec/core/array/element_reference_spec.rb +0 -0
- data/{opals/opal/opal → gems/core}/spec/core/array/first_spec.rb +1 -1
- data/{opals/opal/opal → gems/core}/spec/core/array/include_spec.rb +0 -0
- data/gems/core/spec/core/array/join_spec.rb +6 -0
- data/gems/core/spec/core/array/last_spec.rb +51 -0
- data/gems/core/spec/core/array/length_spec.rb +6 -0
- data/{opals/opal/opal → gems/core}/spec/core/array/map_spec.rb +2 -0
- data/gems/core/spec/core/array/reverse_spec.rb +6 -0
- data/{opals/opal/opal → gems/core}/spec/core/builtin_constants/builtin_constants_spec.rb +0 -0
- data/{opals/opal/opal → gems/core}/spec/core/false/and_spec.rb +0 -0
- data/{opals/opal/opal → gems/core}/spec/core/false/inspect_spec.rb +0 -0
- data/{opals/opal/opal → gems/core}/spec/core/false/or_spec.rb +0 -0
- data/{opals/opal/opal → gems/core}/spec/core/false/to_s_spec.rb +0 -0
- data/{opals/opal/opal → gems/core}/spec/core/false/xor_spec.rb +0 -0
- data/{opals/opal/opal → gems/core}/spec/core/file/join_spec.rb +1 -1
- data/gems/core/spec/core/hash/assoc_spec.rb +32 -0
- data/{opals/opal/opal → gems/core}/spec/core/kernel/instance_eval_spec.rb +0 -0
- data/{opals/opal/opal → gems/core}/spec/core/kernel/loop_spec.rb +0 -0
- data/{opals/opal/opal → gems/core}/spec/core/kernel/raise_spec.rb +0 -0
- data/{opals/opal/opal → gems/core}/spec/core/module/attr_accessor_spec.rb +0 -0
- data/{opals/opal/opal → gems/core}/spec/core/number/lt_spec.rb +0 -0
- data/gems/core/spec/core/string/sub_spec.rb +24 -0
- data/{opals/opal/opal → gems/core}/spec/core/true/and_spec.rb +0 -0
- data/{opals/opal/opal → gems/core}/spec/core/true/inspect_spec.rb +0 -0
- data/{opals/opal/opal → gems/core}/spec/core/true/or_spec.rb +0 -0
- data/{opals/opal/opal → gems/core}/spec/core/true/to_s_spec.rb +0 -0
- data/{opals/opal/opal → gems/core}/spec/core/true/xor_spec.rb +0 -0
- data/{opals/opal/opal → gems/core}/spec/language/and_spec.rb +2 -3
- data/{opals/opal/opal → gems/core}/spec/language/array_spec.rb +21 -5
- data/gems/core/spec/language/block_spec.rb +38 -0
- data/{opals/opal/opal → gems/core}/spec/language/break_spec.rb +0 -0
- data/gems/core/spec/language/case_spec.rb +103 -0
- data/{opals/opal/opal → gems/core}/spec/language/def_spec.rb +11 -1
- data/{opals/opal/opal → gems/core}/spec/language/eigenclass_spec.rb +0 -0
- data/gems/core/spec/language/file_spec.rb +13 -0
- data/gems/core/spec/language/fixtures/block.rb +21 -0
- data/gems/core/spec/language/fixtures/super.rb +293 -0
- data/{opals/opal/opal → gems/core}/spec/language/hash_spec.rb +0 -0
- data/{opals/opal/opal → gems/core}/spec/language/if_spec.rb +0 -0
- data/{opals/opal/opal → gems/core}/spec/language/loop_spec.rb +0 -0
- data/gems/core/spec/language/metaclass_spec.rb +21 -0
- data/{opals/opal/opal → gems/core}/spec/language/method_spec.rb +60 -0
- data/{opals/opal/opal → gems/core}/spec/language/next_spec.rb +0 -0
- data/{opals/opal/opal → gems/core}/spec/language/or_spec.rb +0 -0
- data/{opals/opal/opal → gems/core}/spec/language/redo_spec.rb +0 -0
- data/gems/core/spec/language/regexp_spec.rb +26 -0
- data/{opals/opal/opal → gems/core}/spec/language/rescue_spec.rb +0 -0
- data/{opals/opal/opal → gems/core}/spec/language/return_spec.rb +0 -0
- data/{opals/opal/opal → gems/core}/spec/language/string_spec.rb +0 -0
- data/gems/core/spec/language/super_spec.rb +32 -0
- data/{opals/opal/opal → gems/core}/spec/language/until_spec.rb +47 -47
- data/gems/core/spec/language/variables_spec.rb +155 -0
- data/{opals/opal/opal → gems/core}/spec/language/while_spec.rb +47 -47
- data/{opals/opal/opal → gems/core}/spec/spec_helper.rb +1 -1
- data/gems/core_fs/README.md +19 -0
- data/gems/dev/Rakefile +5 -0
- data/gems/dev/lib/dev.js +99 -0
- data/gems/dev/lib/dev/generator.js +1264 -0
- data/gems/dev/lib/dev/parser.js +979 -0
- data/gems/dev/lib/dev/ruby_parser.js +1088 -0
- data/gems/dev/lib/dev/ruby_parser.y +1267 -0
- data/gems/dev/lib/dev/string_scanner.js +38 -0
- data/gems/dev/tools/racc2js/README.md +39 -0
- data/gems/dev/tools/racc2js/math_parser.js +222 -0
- data/gems/dev/tools/racc2js/math_parser.rb +133 -0
- data/gems/dev/tools/racc2js/math_parser.y +28 -0
- data/gems/dev/tools/racc2js/parser.js +218 -0
- data/gems/dev/tools/racc2js/racc2js.rb +153 -0
- data/gems/json/README.md +4 -0
- data/gems/json/json.gemspec +14 -0
- data/gems/json/lib/json.rb +64 -0
- data/gems/json/lib/json/ext.rb +51 -0
- data/{opals/opal/browser/lib/browser/json_parse.js → gems/json/lib/json/json2.js} +197 -37
- data/gems/ospec/README.md +0 -0
- data/gems/ospec/lib/ospec.rb +6 -0
- data/gems/ospec/lib/ospec/autorun.rb +3 -0
- data/gems/ospec/lib/ospec/dsl.rb +15 -0
- data/gems/ospec/lib/ospec/example.rb +11 -0
- data/gems/ospec/lib/ospec/example/before_and_after_hooks.rb +56 -0
- data/gems/ospec/lib/ospec/example/errors.rb +17 -0
- data/gems/ospec/lib/ospec/example/example_group.rb +12 -0
- data/gems/ospec/lib/ospec/example/example_group_factory.rb +21 -0
- data/gems/ospec/lib/ospec/example/example_group_hierarchy.rb +20 -0
- data/{opals/opal/spec/lib/spec → gems/ospec/lib/ospec}/example/example_group_methods.rb +26 -68
- data/gems/ospec/lib/ospec/example/example_group_proxy.rb +14 -0
- data/gems/ospec/lib/ospec/example/example_methods.rb +46 -0
- data/gems/ospec/lib/ospec/example/example_proxy.rb +18 -0
- data/gems/ospec/lib/ospec/expectations.rb +19 -0
- data/gems/ospec/lib/ospec/expectations/errors.rb +8 -0
- data/gems/ospec/lib/ospec/expectations/fail_with.rb +8 -0
- data/gems/ospec/lib/ospec/expectations/handler.rb +27 -0
- data/gems/ospec/lib/ospec/matchers.rb +24 -0
- data/{opals/opal/Rakefile → gems/ospec/lib/ospec/matchers/be.rb} +0 -0
- data/gems/ospec/lib/ospec/matchers/generated_descriptions.rb +20 -0
- data/gems/ospec/lib/ospec/matchers/operator_matcher.rb +52 -0
- data/gems/ospec/lib/ospec/runner.rb +40 -0
- data/gems/ospec/lib/ospec/runner/example_group_runner.rb +44 -0
- data/{opals/opal/spec/lib/spec → gems/ospec/lib/ospec}/runner/formatter/html_formatter.rb +31 -40
- data/gems/ospec/lib/ospec/runner/formatter/terminal_formatter.rb +48 -0
- data/gems/ospec/lib/ospec/runner/options.rb +36 -0
- data/{opals/opal/spec/lib/spec → gems/ospec/lib/ospec}/runner/reporter.rb +23 -55
- data/gems/ospec/ospec.gemspec +0 -0
- data/gems/rquery/README.md +9 -0
- data/gems/rquery/lib/rquery.rb +10 -0
- data/gems/rquery/lib/rquery/ajax.rb +4 -0
- data/gems/rquery/lib/rquery/css.rb +96 -0
- data/gems/rquery/lib/rquery/document.rb +25 -0
- data/gems/rquery/lib/rquery/element.rb +292 -0
- data/gems/rquery/lib/rquery/event.rb +108 -0
- data/gems/rquery/lib/rquery/jquery.js +8177 -0
- data/gems/rquery/lib/rquery/request.rb +138 -0
- data/gems/rquery/lib/rquery/response.rb +49 -0
- data/gems/rquery/rquery.gemspec +16 -0
- data/lib/opal.js +1597 -0
- data/lib/opal.rb +6 -69
- data/lib/opal/builder.rb +115 -0
- data/lib/opal/bundle.rb +131 -0
- data/lib/opal/command.rb +11 -0
- data/lib/opal/context.rb +69 -0
- data/lib/opal/context/console.rb +12 -0
- data/lib/opal/context/file_system.rb +19 -0
- data/lib/opal/context/loader.rb +117 -0
- data/lib/opal/gem.rb +153 -0
- data/lib/opal/ruby/nodes.rb +1302 -0
- data/lib/opal/ruby/parser.rb +780 -0
- data/lib/opal/ruby/ruby_parser.rb +5170 -0
- data/lib/opal/ruby/ruby_parser.y +1298 -0
- data/opal.gemspec +11 -256
- metadata +179 -240
- data/.gitmodules +0 -6
- data/Opalfile +0 -384
- data/VERSION +0 -1
- data/demos/apps/browser_demo/index.html +0 -11
- data/demos/apps/browser_demo/lib/browser_demo.rb +0 -31
- data/demos/apps/simple_opal/Opalfile +0 -13
- data/demos/apps/simple_opal/index.html +0 -11
- data/demos/apps/simple_opal/lib/app_controller.rb +0 -62
- data/demos/apps/simple_opal/lib/main_window.rb +0 -146
- data/demos/browser/request/index.html +0 -52
- data/demos/browser/request/request.rb +0 -48
- data/gen/browser/__PROJECT_NAME__/Rakefile +0 -12
- data/gen/browser/__PROJECT_NAME__/index.html +0 -11
- data/gen/browser/__PROJECT_NAME__/javascripts/opal.debug.js +0 -4687
- data/gen/browser/__PROJECT_NAME__/javascripts/opal.min.js +0 -175
- data/gen/browser/__PROJECT_NAME__/lib/__PROJECT_NAME__.rb +0 -1
- data/lib/opal/builders/base.rb +0 -50
- data/lib/opal/builders/css.rb +0 -46
- data/lib/opal/builders/javascript.rb +0 -44
- data/lib/opal/builders/opal.rb +0 -79
- data/lib/opal/builders/ruby.rb +0 -50
- data/lib/opal/builders/ruby/generate.rb +0 -1851
- data/lib/opal/builders/ruby/nodes.rb +0 -210
- data/lib/opal/builders/ruby/ruby.rb +0 -916
- data/lib/opal/builders/ruby/ruby_parser.rb +0 -6008
- data/lib/opal/builders/ruby/ruby_parser.rb.y +0 -1451
- data/lib/opal/models/build_item.rb +0 -104
- data/lib/opal/models/hash_struct.rb +0 -40
- data/lib/opal/models/project.rb +0 -252
- data/lib/opal/models/struct_accessors.rb +0 -58
- data/lib/opal/models/target.rb +0 -176
- data/lib/opal/opal/build.rb +0 -169
- data/lib/opal/opal/env/console.rb +0 -66
- data/lib/opal/opal/env/fs.rb +0 -98
- data/lib/opal/opal/env/object.rb +0 -48
- data/lib/opal/opal/environment.rb +0 -139
- data/lib/opal/opal/gen.rb +0 -62
- data/lib/opal/opal/opal.rb +0 -75
- data/lib/opal/opal/repl.rb +0 -38
- data/lib/opal/opalfile/dsl.rb +0 -62
- data/lib/opal/opalfile/opalfile.rb +0 -133
- data/lib/opal/opalfile/task.rb +0 -96
- data/lib/opal/opalfile/task_manager.rb +0 -63
- data/lib/opal/opalfile/task_scope.rb +0 -52
- data/lib/opal/rack/app_server.rb +0 -119
- data/lib/opal/rake/opal_task.rb +0 -34
- data/opals/opal/README.md +0 -37
- data/opals/opal/browser/Opalfile +0 -11
- data/opals/opal/browser/README.md +0 -146
- data/opals/opal/browser/SIZZLE_LICESNSE.txt +0 -148
- data/opals/opal/browser/lib/browser.rb +0 -118
- data/opals/opal/browser/lib/browser/builder.rb +0 -41
- data/opals/opal/browser/lib/browser/canvas_context.rb +0 -115
- data/opals/opal/browser/lib/browser/dimensions.rb +0 -50
- data/opals/opal/browser/lib/browser/document.rb +0 -146
- data/opals/opal/browser/lib/browser/element.rb +0 -487
- data/opals/opal/browser/lib/browser/element/attributes.rb +0 -88
- data/opals/opal/browser/lib/browser/element/css.rb +0 -290
- data/opals/opal/browser/lib/browser/element/form.rb +0 -146
- data/opals/opal/browser/lib/browser/event/dom_events.rb +0 -81
- data/opals/opal/browser/lib/browser/event/event.rb +0 -177
- data/opals/opal/browser/lib/browser/event/trigger_events.rb +0 -53
- data/opals/opal/browser/lib/browser/geometry.rb +0 -97
- data/opals/opal/browser/lib/browser/json.rb +0 -32
- data/opals/opal/browser/lib/browser/request/request.rb +0 -201
- data/opals/opal/browser/lib/browser/sizzle.js +0 -1068
- data/opals/opal/browser/lib/browser/string.rb +0 -42
- data/opals/opal/browser/lib/browser/touch.rb +0 -37
- data/opals/opal/browser/lib/browser/vml_context.js +0 -33
- data/opals/opal/browser/lib/browser/window.rb +0 -36
- data/opals/opal/browser/spec/browser/browser_detection_spec.rb +0 -7
- data/opals/opal/browser/spec/document/aref_spec.rb +0 -110
- data/opals/opal/browser/spec/document/ready_spec.rb +0 -16
- data/opals/opal/browser/spec/element/body_spec.rb +0 -11
- data/opals/opal/browser/spec/element/clear_spec.rb +0 -26
- data/opals/opal/browser/spec/element/empty_spec.rb +0 -29
- data/opals/opal/browser/spec/element/has_class_spec.rb +0 -40
- data/opals/opal/browser/spec/element/hidden_spec.rb +0 -23
- data/opals/opal/browser/spec/element/hide_spec.rb +0 -31
- data/opals/opal/browser/spec/element/remove_spec.rb +0 -25
- data/opals/opal/browser/spec/element/show_spec.rb +0 -31
- data/opals/opal/browser/spec/element/style_spec.rb +0 -69
- data/opals/opal/browser/spec/element/toggle_spec.rb +0 -31
- data/opals/opal/browser/spec/element/visible_spec.rb +0 -23
- data/opals/opal/browser/spec/spec_helper.rb +0 -1
- data/opals/opal/opal/Opalfile +0 -14
- data/opals/opal/opal/spec/core/array/compact_spec.rb +0 -15
- data/opals/opal/opal/spec/fixtures/super.rb +0 -70
- data/opals/opal/opal/spec/language/____temp_remove_this.rb +0 -12
- data/opals/opal/opal/spec/language/block_spec.rb +0 -18
- data/opals/opal/opal/spec/language/case_spec.rb +0 -103
- data/opals/opal/opal/spec/language/metaclass_spec.rb +0 -21
- data/opals/opal/opal/spec/language/super_spec.rb +0 -26
- data/opals/opal/runtime/Opalfile +0 -78
- data/opals/opal/runtime/README.md +0 -12
- data/opals/opal/runtime/docs/debugging.md +0 -51
- data/opals/opal/runtime/lib/array.rb +0 -1516
- data/opals/opal/runtime/lib/basic_object.rb +0 -49
- data/opals/opal/runtime/lib/class.rb +0 -54
- data/opals/opal/runtime/lib/dir.rb +0 -36
- data/opals/opal/runtime/lib/error.rb +0 -49
- data/opals/opal/runtime/lib/false_class.rb +0 -52
- data/opals/opal/runtime/lib/file.rb +0 -79
- data/opals/opal/runtime/lib/hash.rb +0 -791
- data/opals/opal/runtime/lib/io.rb +0 -39
- data/opals/opal/runtime/lib/kernel.rb +0 -288
- data/opals/opal/runtime/lib/match_data.rb +0 -36
- data/opals/opal/runtime/lib/module.rb +0 -109
- data/opals/opal/runtime/lib/nil_class.rb +0 -69
- data/opals/opal/runtime/lib/number.rb +0 -398
- data/opals/opal/runtime/lib/proc.rb +0 -77
- data/opals/opal/runtime/lib/range.rb +0 -63
- data/opals/opal/runtime/lib/regexp.rb +0 -111
- data/opals/opal/runtime/lib/ruby.rb +0 -30
- data/opals/opal/runtime/lib/string.rb +0 -328
- data/opals/opal/runtime/lib/symbol.rb +0 -40
- data/opals/opal/runtime/lib/top_self.rb +0 -33
- data/opals/opal/runtime/lib/true_class.rb +0 -45
- data/opals/opal/runtime/runtime/browser.js +0 -287
- data/opals/opal/runtime/runtime/debug.js +0 -180
- data/opals/opal/runtime/runtime/opal.js +0 -1008
- data/opals/opal/runtime/runtime/post_opal.js +0 -1
- data/opals/opal/runtime/runtime/pre_opal.js +0 -2
- data/opals/opal/runtime/runtime/server_side.js +0 -50
- data/opals/opal/spec/LICENSE.txt +0 -26
- data/opals/opal/spec/Opalfile +0 -5
- data/opals/opal/spec/bin/spec.rb +0 -43
- data/opals/opal/spec/lib/spec.rb +0 -33
- data/opals/opal/spec/lib/spec/dsl.rb +0 -41
- data/opals/opal/spec/lib/spec/example.rb +0 -35
- data/opals/opal/spec/lib/spec/example/before_and_after_hooks.rb +0 -81
- data/opals/opal/spec/lib/spec/example/errors.rb +0 -42
- data/opals/opal/spec/lib/spec/example/example_group.rb +0 -37
- data/opals/opal/spec/lib/spec/example/example_group_factory.rb +0 -43
- data/opals/opal/spec/lib/spec/example/example_group_hierarchy.rb +0 -45
- data/opals/opal/spec/lib/spec/example/example_group_proxy.rb +0 -41
- data/opals/opal/spec/lib/spec/example/example_methods.rb +0 -73
- data/opals/opal/spec/lib/spec/example/example_proxy.rb +0 -48
- data/opals/opal/spec/lib/spec/expectations.rb +0 -46
- data/opals/opal/spec/lib/spec/expectations/errors.rb +0 -35
- data/opals/opal/spec/lib/spec/expectations/fail_with.rb +0 -37
- data/opals/opal/spec/lib/spec/expectations/handler.rb +0 -48
- data/opals/opal/spec/lib/spec/matchers.rb +0 -50
- data/opals/opal/spec/lib/spec/matchers/be.rb +0 -26
- data/opals/opal/spec/lib/spec/matchers/generated_descriptions.rb +0 -47
- data/opals/opal/spec/lib/spec/matchers/operator_matcher.rb +0 -66
- data/opals/opal/spec/lib/spec/runner.rb +0 -48
- data/opals/opal/spec/lib/spec/runner/example_group_runner.rb +0 -71
- data/opals/opal/spec/lib/spec/runner/formatter/terminal_formatter.rb +0 -82
- data/opals/opal/spec/lib/spec/runner/options.rb +0 -63
- data/opals/opal/spec/resources/index.html +0 -25
- data/opals/opal/spec/resources/spec.css +0 -132
- data/spec/cherry_kit/iseq_spec.rb +0 -38
- data/spec/spec_helper.rb +0 -16
- data/spec/vienna_spec.rb +0 -7
- data/yard/index.html +0 -43
- data/yard/style.css +0 -765
data/lib/opal/gem.rb
ADDED
|
@@ -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
|
+
|