opal 0.3.6 → 0.3.9

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 (98) hide show
  1. data/README.md +69 -97
  2. data/bin/opal +2 -2
  3. data/{lib/core → corelib}/array.rb +85 -56
  4. data/corelib/boolean.rb +20 -0
  5. data/corelib/class.rb +58 -0
  6. data/{lib → corelib}/core.rb +2 -50
  7. data/corelib/dir.rb +22 -0
  8. data/{lib/core → corelib}/enumerable.rb +0 -0
  9. data/corelib/error.rb +19 -0
  10. data/{lib/core → corelib}/file.rb +7 -9
  11. data/{lib/core → corelib}/hash.rb +104 -144
  12. data/{lib/core → corelib}/kernel.rb +38 -44
  13. data/corelib/load_order +21 -0
  14. data/{lib/core → corelib}/match_data.rb +0 -0
  15. data/{lib/core → corelib}/module.rb +12 -8
  16. data/{lib/core → corelib}/nil_class.rb +2 -2
  17. data/{lib/core → corelib}/numeric.rb +37 -100
  18. data/corelib/object.rb +37 -0
  19. data/{lib/core → corelib}/proc.rb +3 -3
  20. data/corelib/range.rb +27 -0
  21. data/{lib/core → corelib}/regexp.rb +1 -1
  22. data/{lib/core → corelib}/string.rb +16 -107
  23. data/{lib/core → corelib}/top_self.rb +0 -0
  24. data/lib/opal.rb +7 -0
  25. data/lib/opal/browserify.rb +34 -0
  26. data/{opal_lib → lib}/opal/builder.rb +70 -24
  27. data/lib/opal/command.rb +52 -0
  28. data/lib/opal/context.rb +197 -0
  29. data/{opal_lib/opal/ruby/parser.rb → lib/opal/lexer.rb} +20 -4
  30. data/{opal_lib/opal/ruby → lib/opal}/nodes.rb +238 -127
  31. data/lib/opal/parser.rb +4894 -0
  32. data/{opal_lib/opal/ruby/ruby_parser.y → lib/opal/parser.y} +38 -18
  33. data/lib/rbp.rb +2 -0
  34. data/lib/rbp/package.rb +49 -0
  35. data/runtime/class.js +216 -189
  36. data/runtime/fs.js +2 -2
  37. data/runtime/init.js +242 -244
  38. data/runtime/loader.js +78 -99
  39. data/runtime/module.js +34 -40
  40. data/runtime/post.js +2 -2
  41. data/runtime/pre.js +1 -1
  42. data/runtime/runtime.js +129 -135
  43. data/{lib → stdlib}/dev.rb +10 -10
  44. data/{lib → stdlib}/racc/parser.rb +0 -6
  45. data/{lib → stdlib}/strscan.rb +4 -4
  46. metadata +57 -105
  47. data/lib/core/basic_object.rb +0 -51
  48. data/lib/core/class.rb +0 -38
  49. data/lib/core/dir.rb +0 -26
  50. data/lib/core/error.rb +0 -75
  51. data/lib/core/false_class.rb +0 -81
  52. data/lib/core/object.rb +0 -6
  53. data/lib/core/range.rb +0 -27
  54. data/lib/core/symbol.rb +0 -42
  55. data/lib/core/true_class.rb +0 -41
  56. data/lib/ospec.rb +0 -7
  57. data/lib/ospec/autorun.rb +0 -8
  58. data/lib/ospec/dsl.rb +0 -15
  59. data/lib/ospec/example.rb +0 -11
  60. data/lib/ospec/example/before_and_after_hooks.rb +0 -56
  61. data/lib/ospec/example/errors.rb +0 -17
  62. data/lib/ospec/example/example_group.rb +0 -12
  63. data/lib/ospec/example/example_group_factory.rb +0 -18
  64. data/lib/ospec/example/example_group_hierarchy.rb +0 -21
  65. data/lib/ospec/example/example_group_methods.rb +0 -100
  66. data/lib/ospec/example/example_group_proxy.rb +0 -15
  67. data/lib/ospec/example/example_methods.rb +0 -46
  68. data/lib/ospec/example/example_proxy.rb +0 -18
  69. data/lib/ospec/expectations.rb +0 -19
  70. data/lib/ospec/expectations/errors.rb +0 -8
  71. data/lib/ospec/expectations/fail_with.rb +0 -9
  72. data/lib/ospec/expectations/handler.rb +0 -33
  73. data/lib/ospec/helpers/scratch.rb +0 -18
  74. data/lib/ospec/matchers.rb +0 -24
  75. data/lib/ospec/matchers/be.rb +0 -1
  76. data/lib/ospec/matchers/generated_descriptions.rb +0 -20
  77. data/lib/ospec/matchers/operator_matcher.rb +0 -54
  78. data/lib/ospec/matchers/raise_error.rb +0 -38
  79. data/lib/ospec/runner.rb +0 -90
  80. data/lib/ospec/runner/example_group_runner.rb +0 -41
  81. data/lib/ospec/runner/formatter/html_formatter.rb +0 -139
  82. data/lib/ospec/runner/formatter/terminal_formatter.rb +0 -48
  83. data/lib/ospec/runner/options.rb +0 -34
  84. data/lib/ospec/runner/reporter.rb +0 -82
  85. data/opal_lib/opal.rb +0 -16
  86. data/opal_lib/opal/build_methods.rb +0 -51
  87. data/opal_lib/opal/bundle.rb +0 -70
  88. data/opal_lib/opal/command.rb +0 -68
  89. data/opal_lib/opal/context.rb +0 -81
  90. data/opal_lib/opal/context/console.rb +0 -10
  91. data/opal_lib/opal/context/file_system.rb +0 -34
  92. data/opal_lib/opal/context/loader.rb +0 -135
  93. data/opal_lib/opal/gem.rb +0 -84
  94. data/opal_lib/opal/rake/builder_task.rb +0 -44
  95. data/opal_lib/opal/rake/spec_task.rb +0 -32
  96. data/opal_lib/opal/ruby/ruby_parser.rb +0 -4862
  97. data/opal_lib/opal/version.rb +0 -4
  98. data/runtime/debug.js +0 -84
@@ -0,0 +1,52 @@
1
+ module Opal
2
+
3
+ class Command
4
+
5
+ # Valid command line arguments
6
+ COMMANDS = [:help, :irb, :compile, :bundle, :exec, :eval]
7
+
8
+ def initialize(args)
9
+ command = args.shift
10
+
11
+ if command and COMMANDS.include?(command.to_sym)
12
+ __send__ command.to_sym, *args
13
+ elsif command and File.exists? command
14
+ eval command
15
+ else
16
+ help
17
+ end
18
+ end
19
+
20
+ def help
21
+ puts "need to print help"
22
+ end
23
+
24
+ # desc "irb", "Opens interactive opal/ruby repl"
25
+ def irb
26
+ ctx = Opal::Context.new
27
+ ctx.start_repl
28
+ end
29
+
30
+ def eval(path = nil)
31
+ return "no path given for eval" unless path
32
+
33
+ abort "path does not exist `#{path}'" unless File.exist? path
34
+
35
+ ctx = Opal::Context.new
36
+ ctx.require_file File.expand_path(path)
37
+ end
38
+
39
+ def compile(path)
40
+ puts Opal::Parser.new(File.read(path)).parse!.generate_top
41
+ end
42
+
43
+ # desc "bundle", "Bundle the gem in the given directory ready for browser"
44
+ # method_options :out => :string
45
+ def bundle
46
+ opts = options
47
+ bundle = Opal::Bundle.new(Opal::Gem.new(Dir.getwd))
48
+ bundle.build opts
49
+ end
50
+ end
51
+ end
52
+
@@ -0,0 +1,197 @@
1
+ module Opal
2
+ class Context
3
+
4
+ def initialize(root_dir = Dir.getwd)
5
+ @root_dir = root_dir
6
+ @builder = Opal::Builder.new
7
+
8
+ @load_paths = resolve_load_paths
9
+ end
10
+
11
+ # Looks through vendor/ directory and adds all relevant load paths
12
+ def resolve_load_paths
13
+ Dir['vendor/*/package.yml'].map do |package|
14
+ File.expand_path File.join(File.dirname(package), 'lib')
15
+ end
16
+ end
17
+
18
+ # Setup the context. This basically loads opal.js into our context, and
19
+ # replace the loader etc with our custom loader for a Ruby environment. The
20
+ # default "browser" loader cannot access files from disk.
21
+ def setup_v8
22
+ return if @v8
23
+
24
+ begin
25
+ require 'v8'
26
+ rescue LoadError => e
27
+ abort "therubyracer is required for running javascript. Install it with `gem install therubyracer`"
28
+ end
29
+
30
+ @v8 = V8::Context.new
31
+ @v8['console'] = Console.new
32
+
33
+ eval @builder.build_core, '(opal)'
34
+ opal = @v8['opal']
35
+ opal['fs'] = FileSystem.new self
36
+
37
+ # FIXME: we cant use a ruby array as a js array :(
38
+ opal['loader'] = Loader.new self, eval("[]")
39
+
40
+ @load_paths.each do |path|
41
+ eval "opal.loader.paths.push('#{path}')"
42
+ end
43
+
44
+ end
45
+
46
+ def eval(code, file = nil)
47
+ @v8.eval code, file
48
+ end
49
+
50
+ # Require the given id as if it was required in the context. This simply
51
+ # passes the require through to the underlying context.
52
+ def require_file(path)
53
+ setup_v8
54
+ eval "opal.run(function() {opal.require('#{path}');});", path
55
+ finish
56
+ end
57
+
58
+ # Set ARGV for the context
59
+ def argv=(args)
60
+ puts "setting argv to #{args.inspect}"
61
+ eval "opal.runtime.cs(opal.runtime.Object, 'ARGV', #{args.inspect});"
62
+ end
63
+
64
+ # Start normal js repl
65
+ def start_repl
66
+ require 'readline'
67
+ setup_v8
68
+
69
+ loop do
70
+ # on SIGINT lets just return from the loop..
71
+ trap("SIGINT") { finish; return }
72
+ line = Readline.readline '>> ', true
73
+
74
+ # if we type exit, then we need to close down context
75
+ if line == "exit"
76
+ break
77
+ end
78
+
79
+ puts "=> #{eval_ruby line, '(opal)'}"
80
+ end
81
+
82
+ finish
83
+ end
84
+
85
+ def eval_ruby(content, line = "")
86
+ begin
87
+ code = Opal::Parser.new(content).parse!.generate_top
88
+ code = "opal.run(function() {var $rb = opal.runtime, self = $rb.top, __FILE__ = '(opal)';" + code + "});"
89
+ # puts code
90
+ @v8['$opal_irb_result'] = eval code, line
91
+ eval "!($opal_irb_result == null || !$opal_irb_result.m$inspect) ? $opal_irb_result.m$inspect() : '(Object does not support #inspect)'"
92
+ rescue => e
93
+ puts e
94
+ puts("\t" + e.backtrace.join("\n\t"))
95
+ end
96
+ end
97
+
98
+ # Finishes the context, i.e. tidy everything up. This will cause
99
+ # the opal runtime to do it's at_exit() calls (if applicable) and
100
+ # then the v8 context will de removed. It can be reset by calling
101
+ # #setup_v8
102
+ def finish
103
+ return unless @v8
104
+ eval "opal.runtime.do_at_exit()", "(opal)"
105
+
106
+ @v8 = nil
107
+ end
108
+
109
+ # Console class is used to mimic the console object in web browsers
110
+ # to allow simple debugging to the stdout.
111
+ class Console
112
+ def log(*str)
113
+ puts str.join("\n")
114
+ nil
115
+ end
116
+ end
117
+
118
+ # FileSystem is used to interact with the file system from the ruby
119
+ # version of opal. The methods on this class replace the default ones
120
+ # made available in the web browser.
121
+ class FileSystem
122
+
123
+ def initialize(context)
124
+ @context = context
125
+ end
126
+
127
+ def cwd
128
+ Dir.getwd
129
+ end
130
+
131
+ def glob(*arr)
132
+ Dir.glob arr
133
+ end
134
+
135
+ def exist_p(path)
136
+ File.exist? path
137
+ end
138
+
139
+ def expand_path(filename, dir_string = nil)
140
+ File.expand_path filename, dir_string
141
+ end
142
+
143
+ def dirname(file)
144
+ File.dirname file
145
+ end
146
+
147
+ def join(*parts)
148
+ File.join *parts
149
+ end
150
+ end
151
+
152
+ # Loader for v8 context
153
+ class Loader
154
+
155
+ attr_reader :paths
156
+
157
+ def initialize(context, paths)
158
+ @context = context
159
+ @paths = paths
160
+ end
161
+
162
+ def resolve_lib(id)
163
+ resolved = find_lib id
164
+ raise "Cannot find lib `#{id}'" unless resolved
165
+
166
+ resolved
167
+ end
168
+
169
+ def find_lib(id)
170
+ @paths.each do |path|
171
+ candidate = File.join path, "#{id}.rb"
172
+ return candidate if File.exists? candidate
173
+
174
+ candidate = File.join path, id
175
+ return candidate if File.exists? candidate
176
+ end
177
+
178
+ return File.expand_path id if File.exists? id
179
+ return File.expand_path(id + '.rb') if File.exists?(id + '.rb')
180
+
181
+ nil
182
+ end
183
+
184
+ def ruby_file_contents(filename)
185
+ Opal::Parser.new(File.read(filename)).parse!.generate_top
186
+ end
187
+
188
+ def wrap(content, filename)
189
+ code = "(function($rb, self, __FILE__) { #{content} });"
190
+ @context.eval code, filename
191
+ # code
192
+ end
193
+ end
194
+
195
+ end
196
+ end
197
+
@@ -1,11 +1,12 @@
1
1
 
2
- require 'opal/ruby/ruby_parser'
3
- require 'opal/ruby/nodes'
2
+ require 'opal/parser'
3
+ require 'opal/nodes'
4
+
4
5
 
5
6
  require 'strscan'
6
7
 
7
8
  module Opal
8
- class RubyParser < Racc::Parser
9
+ class Parser < Racc::Parser
9
10
 
10
11
  class RubyLexingError < StandardError
11
12
 
@@ -29,7 +30,6 @@ module Opal
29
30
 
30
31
  def next_token
31
32
  t = get_next_token
32
- # puts "returning token #{t.inspect}"
33
33
  t[1] = { :value => t[1], :line => @line_number }
34
34
  t
35
35
  end
@@ -725,6 +725,14 @@ module Opal
725
725
  @lex_state = :expr_end
726
726
  return :NIL, scanner.matched
727
727
 
728
+ when 'undefined'
729
+ @lex_state = :expr_end
730
+ return :UNDEFINED, scanner.matched
731
+
732
+ when 'null'
733
+ @lex_state = :expr_end
734
+ return :NULL, scanner.matched
735
+
728
736
  when '__LINE__'
729
737
  @lex_state = :expr_end
730
738
  return :LINE, @line_number.to_s
@@ -809,6 +817,14 @@ module Opal
809
817
  @lex_state = :expr_beg
810
818
  return :WHILE_MOD, scanner.matched
811
819
 
820
+ when 'for'
821
+ @lex_state = :expr_beg
822
+ return :FOR, scanner.matched
823
+
824
+ when 'in'
825
+ @lex_state = :expr_beg
826
+ return :IN, scanner.matched
827
+
812
828
  when 'until'
813
829
  return :WHILE, scanner.matched if @lex_state == :expr_beg
814
830
  @lex_state = :expr_beg
@@ -1,5 +1,5 @@
1
1
  module Opal
2
- class RubyParser < Racc::Parser
2
+ class Parser < Racc::Parser
3
3
 
4
4
  # Indent for generated code scopes; 2 spaces, never use tabs
5
5
  INDENT = ' '
@@ -8,6 +8,7 @@ module Opal
8
8
  LEVEL_TOP_CLOSURE = 1 # normal top level, but wrapped in js closure
9
9
  LEVEL_LIST = 2
10
10
  LEVEL_EXPR = 3
11
+ LEVEL_COMPARE = 4 # comparison of if statement etc
11
12
 
12
13
  # Base node for generators. All other nodes inherit from this
13
14
  class BaseNode
@@ -69,6 +70,27 @@ module Opal
69
70
 
70
71
  code
71
72
  end
73
+
74
+ # Reserved js words - we cannot just generate properties with these names
75
+ # as they will cause a parse error, so we need to wrap them in brackets.
76
+ def js_reserved_words
77
+ %w[break case catch continue debugger default delete do else finally
78
+ for function if in instanceof new return switch this throw try typeof
79
+ var void while with class enum export extends import super true false]
80
+ end
81
+
82
+ def generate_truthy_test(expr, opts)
83
+ if expr.is_a? ComparisonNode
84
+ expr.generate opts, LEVEL_EXPR
85
+ else
86
+ tmp = opts[:scope].temp_local
87
+ code = expr.generate opts, LEVEL_EXPR
88
+ res = "(#{tmp} = #{code}, #{tmp} !== false && #{tmp} !== nil)"
89
+ opts[:scope].queue_temp tmp
90
+ res
91
+ end
92
+ end
93
+
72
94
  end
73
95
 
74
96
  # Scope nodes. All scope nodes inherit from this node, including: method,
@@ -194,26 +216,6 @@ module Opal
194
216
  super nil, statements
195
217
  @file_helpers = []
196
218
  @line = 1
197
- @mm_ids = []
198
-
199
- @symbol_refs = {}
200
- @symbol_count = 1
201
-
202
- @regexp_refs = []
203
- end
204
-
205
- def register_mm_id(mid)
206
- @mm_ids << mid unless @mm_ids.include? mid
207
- end
208
-
209
- def register_symbol(sym)
210
- if ref = @symbol_refs[sym]
211
- ref
212
- else
213
- ref = @symbol_refs[sym] = "$symbol_#{@symbol_count}"
214
- @symbol_count += 1
215
- ref
216
- end
217
219
  end
218
220
 
219
221
  def generate(opts, level)
@@ -225,32 +227,26 @@ module Opal
225
227
 
226
228
  pre = 'function $$(){'
227
229
  post = "\n}\n"
228
- # post = "\n\n}\nvar nil, $ac, $super, $break, $class, $def, $symbol, $range, "
229
- # post += '$hash, $B, Qtrue, Qfalse, $cg;'
230
- # local vars... only if we used any..
230
+
231
231
  unless @scope_vars.empty?
232
232
  post += "var #{@scope_vars.join ', '};"
233
233
  end
234
234
 
235
- post += 'var nil = $rb.Qnil, $ac = $rb.ac, $super = $rb.S, $break = $rb.B, '
236
- post += '$class = $rb.dc, $defn = $rb.dm, $defs = $rb.ds, $symbol = $rb.Y, '
237
- post += '$hash = $rb.H, $B = $rb.P, Qtrue = $rb.Qtrue, Qfalse = $rb.Qfalse, '
238
- post += '$cg = $rb.cg, $range = $rb.G'
239
-
240
- # symbols
241
- @symbol_refs.each do |val, sym|
242
- post += ", #{sym} = $symbol('#{val}')"
243
- end
235
+ post += 'var nil = $rb.Qnil, $super = $rb.S, $break = $rb.B, '
236
+ post += '$class = $rb.dc, $defn = $rb.dm, $defs = $rb.ds, $cg = $rb.cg, '
237
+ post += '$range = $rb.G, $hash = $rb.H, $B = $rb.P'
244
238
 
245
239
  post += ';'
246
240
 
247
- if @mm_ids.length > 0
248
- post += "$rb.mm(['#{ @mm_ids.join "', '" }']);"
249
- end
250
-
251
241
  # ivars
252
242
  @ivars.each do |ivar|
253
- post += "if (self['#{ivar}'] == undefined) { self['#{ivar}'] = nil; }"
243
+ if js_reserved_words.include? ivar
244
+ ivar_name = "self['#{ivar}']"
245
+ else
246
+ ivar_name = "self.#{ivar}"
247
+ end
248
+
249
+ post += "#{ivar_name}===undefined&&(#{ivar_name}=nil);"
254
250
  end
255
251
 
256
252
  post += "return $$();\n"
@@ -348,7 +344,7 @@ module Opal
348
344
  end
349
345
 
350
346
  def generate(opts, level)
351
- opts[:top].register_symbol @value
347
+ "'#{@value}'"
352
348
  end
353
349
  end
354
350
 
@@ -369,20 +365,10 @@ module Opal
369
365
  end
370
366
 
371
367
  def mid_to_jsid(id)
372
- return ".$m['#{id}']" if /[\!\=\?\+\-\*\/\^\&\%\@\|\[\]\<\>\~]/ =~ id
373
-
374
- return ".$m['#{id}']" if js_reserved_words.include? id
368
+ return "['#{id}']" if /[\!\=\?\+\-\*\/\^\&\%\@\|\[\]\<\>\~]/ =~ id
375
369
 
376
370
  # default we just do .method_name
377
- '.$m.' + id
378
- end
379
-
380
- # Reserved js words - we cannot just generate properties with these names
381
- # as they will cause a parse error, so we need to wrap them in brackets.
382
- def js_reserved_words
383
- %w[break case catch continue debugger default delete do else finally
384
- for function if in instanceof new return switch this throw try typeof
385
- var void while with class enum export extends import super]
371
+ '.' + id
386
372
  end
387
373
 
388
374
  def generate(opts, level)
@@ -393,16 +379,13 @@ module Opal
393
379
 
394
380
  elsif @mid == "block_given?"
395
381
  # name = opts[:scope].set_uses_block
396
- return "($yy == $y.y ? Qfalse : Qtrue)"
382
+ return "($yy !== $y.y)"
397
383
  end
398
384
 
399
385
  code = ''
400
386
  arg_res = []
401
387
  recv = nil
402
- mid = @mid
403
- tmp_recv = opts[:scope].temp_local
404
-
405
- opts[:top].register_mm_id @mid
388
+ mid = 'm$' + @mid
406
389
 
407
390
  # receiver
408
391
  if @recv.is_a? NumericNode
@@ -410,25 +393,11 @@ module Opal
410
393
  elsif @recv
411
394
  recv = @recv.process opts, LEVEL_EXPR
412
395
  else
413
- @recv = SelfNode.new
414
396
  recv = "self"
415
- mid = '$' + mid
416
397
  end
417
398
 
418
399
  mid = mid_to_jsid(mid)
419
400
 
420
- if @recv.is_a? SelfNode
421
- recv_code = recv
422
- recv_arg = recv
423
- elsif @recv.is_a?(IdentifierNode) and @recv.local_variable?(opts)
424
- recv_code = recv
425
- recv_arg = recv
426
- else
427
- recv_code = "(#{tmp_recv} = #{recv})"
428
- recv_arg = "#{tmp_recv}"
429
- end
430
-
431
-
432
401
  args = @args
433
402
  # normal args
434
403
  if args[0]
@@ -443,11 +412,12 @@ module Opal
443
412
  end
444
413
 
445
414
  if @block
415
+ tmp_recv = opts[:scope].temp_local
446
416
  block = @block.generate opts, LEVEL_TOP
447
- arg_res.unshift recv_arg
417
+ arg_res.unshift tmp_recv
448
418
 
449
- code = "($B.f = #{recv_code}#{mid}, ($B.p ="
450
- code += "#{block}).$proc =[self], $B.f)(#{arg_res.join ', '})"
419
+ code = "(#{tmp_recv} = #{recv}, $B.f = #{tmp_recv}#{mid}, ($B.p ="
420
+ code += "#{block}).$self=self, $B.f).call(#{arg_res.join ', '})"
451
421
 
452
422
  opts[:scope].queue_temp tmp_recv
453
423
  code
@@ -457,10 +427,11 @@ module Opal
457
427
  #
458
428
  # FIXME need to actually call to_proc.
459
429
  elsif args[3]
460
- arg_res.unshift recv_arg
430
+ tmp_recv = opts[:scope].temp_local
431
+ arg_res.unshift tmp_recv
461
432
 
462
433
  code = "($B.p = #{args[3].process opts, LEVEL_LIST}, "
463
- code += "$B.f = #{recv_code}#{mid})(#{arg_res.join ', '})"
434
+ code += "$B.f = (#{tmp_recv} = #{recv})#{mid}).call(#{arg_res.join ', '})"
464
435
 
465
436
  opts[:scope].queue_temp tmp_recv
466
437
 
@@ -470,21 +441,18 @@ module Opal
470
441
  else
471
442
  # splat args
472
443
  if args[1]
473
- arg_res.unshift recv_arg
444
+ tmp_recv = opts[:scope].temp_local
474
445
  splat = args[1].generate(opts, LEVEL_EXPR)
475
446
  splat_args = arg_res.empty? ? "#{splat}" : "[#{arg_res.join ', '}].concat(#{splat})"
476
447
  # when using splat, our this val for apply may need a tmp var
477
448
  # to save just outputting it twice (have to follow recv path twice)
478
449
  splat_recv = recv
479
- result = "#{recv_code}" + mid + ".apply(nil, #{splat_args})"
450
+ result = "(#{tmp_recv} = #{recv})" + mid + ".apply(#{tmp_recv}, #{splat_args})"
480
451
 
481
452
  opts[:scope].queue_temp tmp_recv
482
453
  result
483
454
  else
484
- arg_res.unshift recv_arg
485
-
486
- result = "#{recv_code}#{mid}(#{arg_res.join(', ')})"
487
- opts[:scope].queue_temp tmp_recv
455
+ result = "#{recv}#{mid}(#{arg_res.join(', ')})"
488
456
  result
489
457
  end
490
458
  end
@@ -514,6 +482,28 @@ module Opal
514
482
  end
515
483
  end
516
484
 
485
+ class UndefinedNode < BaseNode
486
+
487
+ def initialize(val)
488
+ @line = val[:line]
489
+ end
490
+
491
+ def generate(opts, level)
492
+ 'undefined'
493
+ end
494
+ end
495
+
496
+ class NullNode < BaseNode
497
+
498
+ def initialize(val)
499
+ @line = val[:line]
500
+ end
501
+
502
+ def generate(opts, level)
503
+ 'null'
504
+ end
505
+ end
506
+
517
507
  class ModuleNode < ScopeNode
518
508
 
519
509
  def initialize(mod, path, body, _end)
@@ -688,7 +678,13 @@ module Opal
688
678
  args[1].each do |arg|
689
679
  param_variable arg[0][:value]
690
680
  method_args << arg[0][:value]
691
- pre_code += "if (#{arg[0][:value]} == undefined) {#{arg[0][:value]} = #{arg[1].generate(opts, LEVEL_EXPR)};}"
681
+
682
+ # undefined is a special case... we use it to make core libs have
683
+ # right arity, but we dont want overhead of checking for no arg.
684
+ # i.e. non given arg will be undefined, not nil..
685
+ unless arg[1].is_a? UndefinedNode
686
+ pre_code += "if (#{arg[0][:value]} == undefined) {#{arg[0][:value]} = #{arg[1].generate(opts, LEVEL_EXPR)};}"
687
+ end
692
688
  end
693
689
  end
694
690
 
@@ -697,10 +693,11 @@ module Opal
697
693
  if args[2][:value] != "*"
698
694
  param_variable args[2][:value]
699
695
  method_args << args[2][:value]
700
- pre_code += "#{args[2][:value]} = [].slice.call(arguments, #{method_args.length});"
696
+ pre_code += "#{args[2][:value]} = [].slice.call(arguments, #{method_args.length - 1});"
701
697
  end
702
698
  end
703
699
 
700
+ # arity not currently used..could use it for debug mode?
704
701
  arity = (-arity) - 1 if args[1] or args[2]
705
702
 
706
703
  # block arg
@@ -712,9 +709,8 @@ module Opal
712
709
 
713
710
  @body.returns
714
711
  stmt = @body.generate scope, LEVEL_TOP
715
- method_args.unshift 'self'
716
712
 
717
- code += "function(#{method_args.join ', '}) {"
713
+ code += "function(#{method_args.join ', '}) { var self = this;"
718
714
 
719
715
  # local vars... only if we used any..
720
716
  unless @scope_vars.empty?
@@ -723,7 +719,13 @@ module Opal
723
719
 
724
720
  # ivars
725
721
  @ivars.each do |ivar|
726
- pre_code += "self['#{ivar}']==undefined&&(self['#{ivar}']=nil);"
722
+ if js_reserved_words.include? ivar
723
+ ivar_name = "self['#{ivar}']"
724
+ else
725
+ ivar_name = "self.#{ivar}"
726
+ end
727
+
728
+ pre_code += "if (#{ivar_name} == undefined) { #{ivar_name} = nil; }"
727
729
  end
728
730
 
729
731
  # block support
@@ -732,14 +734,14 @@ module Opal
732
734
  block_code = "var $y = $B, $yy, $ys, $yb = $y.b;"
733
735
  block_code += "if ($y.f == arguments.callee) { $yy = $y.p; }"
734
736
  block_code += "else { $yy = $y.y; }"
735
- block_code += "$y.f = nil ;$ys = $yy.$proc[0];"
737
+ block_code += "$y.f = nil ;$ys = $yy.$self;"
736
738
  pre_code = block_code + pre_code
737
739
  end
738
740
 
739
741
  code += (pre_code + stmt)
740
742
 
741
743
  # fix trailing end and 0/1 for normal/singleton
742
- code += (fix_line_number(opts, @end_line) + "}, #{arity})")
744
+ code += (fix_line_number(opts, @end_line) + "})")
743
745
 
744
746
  code
745
747
  end
@@ -748,6 +750,7 @@ module Opal
748
750
  class BodyStatementsNode < BaseNode
749
751
 
750
752
  attr_reader :opt_rescue
753
+ attr_reader :opt_ensure
751
754
 
752
755
  def initialize(stmt, optrescue, optelse, optensure)
753
756
  @statements = stmt
@@ -777,7 +780,7 @@ module Opal
777
780
  def generate(opts, level)
778
781
  res = '(('
779
782
  tmp = opts[:scope].temp_local
780
- res += "#{tmp} = #{@lhs.generate opts, LEVEL_LIST}).$r ? "
783
+ res += "(#{tmp} = #{@lhs.generate opts, LEVEL_LIST}), #{tmp} != false && #{tmp} != nil) ? "
781
784
  res += "#{tmp} : #{@rhs.generate opts, LEVEL_LIST})"
782
785
  opts[:scope].queue_temp tmp
783
786
  res
@@ -795,7 +798,7 @@ module Opal
795
798
  def generate(opts, level)
796
799
  res = '(('
797
800
  tmp = opts[:scope].temp_local
798
- res += "#{tmp} = #{@lhs.generate opts, LEVEL_LIST}).$r ? "
801
+ res += "(#{tmp} = #{@lhs.generate opts, LEVEL_LIST}), #{tmp} != false && #{tmp} != nil) ? "
799
802
  res += "#{@rhs.generate opts, LEVEL_LIST} : #{tmp})"
800
803
  opts[:scope].queue_temp tmp
801
804
  res
@@ -822,7 +825,7 @@ module Opal
822
825
  res = code
823
826
  end
824
827
 
825
- res
828
+ "#{res}"
826
829
  end
827
830
  end
828
831
 
@@ -884,20 +887,18 @@ module Opal
884
887
  @level_expr = true
885
888
  end
886
889
 
887
- expr = @expr.generate opts, LEVEL_EXPR
888
- expr = "(#{expr})" if @expr.is_a? NumericNode
889
-
890
+ expr = generate_truthy_test @expr, opts
890
891
  # code += "if ((#{@expr.generate opts, LEVEL_EXPR}).$r) {#{@stmt.process opts, stmt_level}"
891
- code += "if (#{@type == 'if' ? '' : '!'}#{expr}.$r) {#{@stmt.process opts, stmt_level}"
892
+ code += "if (#{@type == 'if' ? '' : '!'}#{expr}) {#{@stmt.process opts, stmt_level}"
892
893
 
893
894
  @tail.each do |tail|
894
895
  opts[:indent] = old_indent
895
896
  code = code + fix_line_number(opts, tail[0][:line])
896
897
 
897
898
  if tail[0][:value] == 'elsif'
898
- expr = tail[1].generate opts, LEVEL_EXPR
899
- expr = "(#{expr})" if tail[1].is_a? NumericNode
900
- code += "} else if (#{expr}.$r) {"
899
+ expr = generate_truthy_test tail[1], opts
900
+
901
+ code += "} else if (#{expr}) {"
901
902
  # code += "} else if ((#{tail[1].generate opts, LEVEL_EXPR}).$r) {"
902
903
  opts[:indent] = opts[:indent] + INDENT
903
904
  code = code + tail[2].process(opts, stmt_level)
@@ -971,10 +972,10 @@ module Opal
971
972
  if part[0][:value] == 'when'
972
973
  code += (idx == 0 ? "if" : "} else if")
973
974
  parts = part[1].map do |expr|
974
- CallNode.new(expr,
975
+ generate_truthy_test CallNode.new(expr,
975
976
  {:value => '===' },
976
977
  [[TempNode.new(case_ref)]]
977
- ).generate(opts, LEVEL_EXPR) + '.$r'
978
+ ), opts
978
979
  end
979
980
  opts[:indent] = opts[:indent] + INDENT
980
981
  code += " (#{parts.join ' || '}) {#{part[2].process opts, stmt_level}"
@@ -988,7 +989,7 @@ module Opal
988
989
  opts[:scope].queue_temp case_ref
989
990
  code += (fix_line_number(opts, @end_line) + '}')
990
991
 
991
- code = "(function() {#{code})()" if level == LEVEL_EXPR
992
+ code = "(function() {#{code}})()" if level == LEVEL_EXPR
992
993
  code
993
994
  end
994
995
  end
@@ -1057,7 +1058,14 @@ module Opal
1057
1058
 
1058
1059
  def generate(opts, level)
1059
1060
  if @lhs.is_a? IvarNode
1060
- return "#{SelfNode.new.generate(opts, level)}['#{@lhs.value}'] = #{@rhs.generate(opts, LEVEL_EXPR)}"
1061
+ ivar_name = @lhs.value.slice 1, @lhs.value.length
1062
+ ivar_rhs = @rhs.generate opts, LEVEL_EXPR
1063
+
1064
+ return "self['#{ivar_name}'] = #{ivar_rhs}" if js_reserved_words.include? ivar_name
1065
+ return "self.#{ivar_name} = #{ivar_rhs}"
1066
+
1067
+ elsif @lhs.is_a? CvarNode
1068
+ return "$rb.cvs('#{@lhs.value}', #{@rhs.generate opts, LEVEL_EXPR})"
1061
1069
 
1062
1070
  elsif @lhs.is_a? GvarNode
1063
1071
  return "$rb.gs('#{@lhs.value}', #{@rhs.generate(opts, LEVEL_EXPR)})"
@@ -1167,8 +1175,25 @@ module Opal
1167
1175
  end
1168
1176
 
1169
1177
  def generate(opts, level)
1170
- opts[:scope].ensure_ivar @value
1171
- "#{SelfNode.new.generate(opts, level)}['#{@value}']"
1178
+ var_name = @value.slice 1, @value.length
1179
+ opts[:scope].ensure_ivar var_name
1180
+
1181
+ return "self['#{var_name}']" if js_reserved_words.include? var_name
1182
+ "self.#{var_name}"
1183
+ end
1184
+ end
1185
+
1186
+ class CvarNode < BaseNode
1187
+
1188
+ attr_reader :value
1189
+
1190
+ def initialize(val)
1191
+ @line = val[:line]
1192
+ @value = val[:value]
1193
+ end
1194
+
1195
+ def generate(opts, level)
1196
+ "$rb.cvg('#{@value}')"
1172
1197
  end
1173
1198
  end
1174
1199
 
@@ -1228,7 +1253,8 @@ module Opal
1228
1253
  if part[0] == 'string_content'
1229
1254
  @join + part[1][:value] + @join
1230
1255
  elsif part[0] == 'string_dbegin'
1231
- CallNode.new(part[1], { :value => 'to_s', :line => 0 }, [[]]).generate(opts, level)
1256
+ "(" + part[1].generate(opts, LEVEL_EXPR) + ").m$to_s()"
1257
+ # CallNode.new(part[1], { :value => 'to_s', :line => 0 }, [[]]).generate(opts, level)
1232
1258
  end
1233
1259
  end
1234
1260
 
@@ -1237,6 +1263,49 @@ module Opal
1237
1263
  end
1238
1264
  end
1239
1265
 
1266
+ class ComparisonNode < BaseNode
1267
+
1268
+ def initialize(op, lhs, rhs)
1269
+ @line = op[:line]
1270
+ @op = op[:value]
1271
+ @lhs = lhs
1272
+ @rhs = rhs
1273
+ end
1274
+
1275
+ def generate(opts, level)
1276
+ lhs = @lhs.generate opts, LEVEL_EXPR
1277
+ lhs = "(#{lhs})" if @lhs.is_a? NumericNode
1278
+ rhs = @rhs.generate opts, LEVEL_EXPR
1279
+
1280
+ if @op == '!='
1281
+ "!#{lhs}['m$=='](#{rhs})"
1282
+ else
1283
+ "#{lhs}['m$#{@op}'](#{rhs})"
1284
+ end
1285
+ end
1286
+ end
1287
+
1288
+ class UnaryNode < BaseNode
1289
+
1290
+ def initialize(op, val)
1291
+ @line = op[:line]
1292
+ @op = op[:value]
1293
+ @val = val
1294
+ end
1295
+
1296
+ def generate(opts, level)
1297
+ if @op == '!'
1298
+ tmp = opts[:scope].temp_local
1299
+ expr = @val.generate opts, LEVEL_EXPR
1300
+ res = "(#{tmp} = #{expr}, #{tmp} === false || #{tmp} === nil)"
1301
+ opts[:scope].queue_temp tmp
1302
+ res
1303
+ else
1304
+ "#{@op}#{@val.generate opts, level}"
1305
+ end
1306
+ end
1307
+ end
1308
+
1240
1309
  class TrueNode < BaseNode
1241
1310
 
1242
1311
  def initialize(val)
@@ -1244,7 +1313,7 @@ module Opal
1244
1313
  end
1245
1314
 
1246
1315
  def generate(opts, level)
1247
- "Qtrue"
1316
+ "true"
1248
1317
  end
1249
1318
  end
1250
1319
 
@@ -1255,7 +1324,7 @@ module Opal
1255
1324
  end
1256
1325
 
1257
1326
  def generate(opts, level)
1258
- "Qfalse"
1327
+ "false"
1259
1328
  end
1260
1329
  end
1261
1330
 
@@ -1293,7 +1362,7 @@ module Opal
1293
1362
  #
1294
1363
  # Also, this is optional, and can be turned on/off for
1295
1364
  # performance gains.
1296
- if true
1365
+ if false
1297
1366
  pre_code += "if (#{arg[:value]} === undefined) { #{arg[:value]} = nil; }"
1298
1367
  end
1299
1368
  end
@@ -1317,7 +1386,7 @@ module Opal
1317
1386
  # FIXME if we just pass '*', then we make a tmp variable name for it..
1318
1387
  param_variable rest_arg_name
1319
1388
  method_args << rest_arg_name
1320
- pre_code += "#{rest_arg_name} = [].slice.call($A, #{method_args.length});"
1389
+ pre_code += "#{rest_arg_name} = [].slice.call(arguments, #{method_args.length - 1});"
1321
1390
  end
1322
1391
  end
1323
1392
 
@@ -1325,17 +1394,17 @@ module Opal
1325
1394
  if args[3]
1326
1395
  param_variable args[3][:value]
1327
1396
  @block_arg_name = args[3][:value]
1397
+ pre_code += "var #{args[3][:value]} = (($yy == $y.y) ? nil: $yy);"
1328
1398
  end
1329
1399
  end
1330
1400
 
1331
1401
  @stmt.returns
1332
1402
  stmt = @stmt.process scope, LEVEL_TOP
1333
- method_args.unshift 'self'
1334
1403
 
1335
1404
  block_var = opts[:scope].temp_local
1336
1405
  # code += "(#{block_var} = "
1337
1406
 
1338
- code += "function(#{method_args.join ', '}) {"
1407
+ code += "function(#{method_args.join ', '}) { var self = this;"
1339
1408
 
1340
1409
  unless @scope_vars.empty?
1341
1410
  code += " var #{@scope_vars.join ', '};"
@@ -1343,15 +1412,11 @@ module Opal
1343
1412
 
1344
1413
  # block arg
1345
1414
  if @block_arg_name
1346
- pre_code += "var $yield, #@block_arg_name; if ($B.f == arguments.callee && $B.p != nil) { #@block_arg_name = "
1347
- pre_code += "$yield = $B.p; } else { #@block_arg_name = nil; "
1348
- pre_code += "$yield = $B.y; } $B.p = $B.f = nil;"
1349
- pre_code += "var $yself = $yield.$proc[0];"
1350
-
1351
- stmt = "try{" + stmt
1352
-
1353
- # catch break statements
1354
- stmt += "} catch (__err__) {if(__err__.$keyword == 2) {return __err__.$value;} throw __err__;}"
1415
+ block_code = "var $y = $B, $yy, $ys, $yb = $y.b;"
1416
+ block_code += "if ($y.f == arguments.callee) { $yy = $y.p; }"
1417
+ block_code += "else { $yy = $y.y; }"
1418
+ block_code += "$y.f = nil ;$ys = $yy.o$s;"
1419
+ pre_code = block_code + pre_code
1355
1420
  end
1356
1421
 
1357
1422
  code += (pre_code + stmt + fix_line_number(opts, @end_line) + "}")
@@ -1471,8 +1536,10 @@ module Opal
1471
1536
  # if we return, make sure our stmt does
1472
1537
  @stmt.returns if @returns
1473
1538
 
1474
- r = "if(#{@type == 'if' ? '' : '!'}(#{@expr.generate(opts, LEVEL_EXPR)}"
1475
- r += ").$r) {#{@stmt.process(opts, LEVEL_TOP)}}"
1539
+ expr = generate_truthy_test @expr, opts
1540
+
1541
+ r = "if(#{@type == 'if' ? '' : '!'}(#{expr}"
1542
+ r += ")) {#{@stmt.process(opts, LEVEL_TOP)}}"
1476
1543
 
1477
1544
  # also, if we return, we need to ensure we have an else conditional
1478
1545
  r += " else { return nil; }" if @returns
@@ -1499,10 +1566,10 @@ module Opal
1499
1566
 
1500
1567
  if @args[1]
1501
1568
  parts.unshift '$ys'
1502
- code = "#{block_code}(null, [#{parts.join ', '}].concat(#{@args[1].generate(opts, LEVEL_EXPR)}))"
1569
+ code = "#{block_code}.apply($ys, [#{parts.join ', '}].concat(#{@args[1].generate(opts, LEVEL_EXPR)}))"
1503
1570
  else
1504
1571
  parts.unshift '$ys'
1505
- code = "#{block_code}(#{parts.join ', '})"
1572
+ code = "#{block_code}.call(#{parts.join ', '})"
1506
1573
  end
1507
1574
 
1508
1575
  code
@@ -1671,8 +1738,8 @@ module Opal
1671
1738
 
1672
1739
  @redo_var = eval_expr = opts[:scope].temp_local
1673
1740
  code = "#{eval_expr} = false; while (#{eval_expr} || #{truthy}("
1674
- code += @expr.generate opts, LEVEL_EXPR
1675
- code += ").$r) {#{eval_expr} = false;"
1741
+ code += generate_truthy_test @expr, opts
1742
+ code += ")) {#{eval_expr} = false;"
1676
1743
 
1677
1744
  opts[:scope].push_while_scope self
1678
1745
 
@@ -1698,6 +1765,43 @@ module Opal
1698
1765
  end
1699
1766
  end
1700
1767
 
1768
+ class ForNode < BaseNode
1769
+
1770
+ def initialize(begn, vars, expr, compstmt, endn)
1771
+ @line = begn[:line]
1772
+ @vars = vars
1773
+ @expr = expr
1774
+ @stmt = compstmt
1775
+ @end_line = endn[:line]
1776
+ end
1777
+
1778
+ def returns
1779
+ @returns = true
1780
+ self
1781
+ end
1782
+
1783
+ def generate(opts, level)
1784
+ @current_scope = opts[:scope]
1785
+ stmt_level = (level == LEVEL_EXPR ? LEVEL_TOP_CLOSURE : LEVEL_TOP)
1786
+
1787
+ if stmt_level == LEVEL_TOP_CLOSURE
1788
+ returns
1789
+ @level_expr = true
1790
+ end
1791
+
1792
+ idx = opts[:scope].temp_local
1793
+ ref = opts[:scope].temp_local
1794
+ len = opts[:scope].temp_local
1795
+ code = "for (#{idx} = 0, #{ref} = #{@expr.generate opts, LEVEL_EXPR}"
1796
+ code += ", #{len} = #{ref}.length; #{idx} < #{len}; #{idx}++) {"
1797
+
1798
+ code += @stmt.process opts, LEVEL_TOP
1799
+
1800
+ code += fix_line_number opts, @end_line
1801
+ code += "}"
1802
+ end
1803
+ end
1804
+
1701
1805
  class SuperNode < BaseNode
1702
1806
 
1703
1807
  def initialize(start, args)
@@ -1781,6 +1885,12 @@ module Opal
1781
1885
  opts[:indent] = old_indent + INDENT
1782
1886
  end
1783
1887
 
1888
+ if opt_ensure = @body.opt_ensure
1889
+ # puts "optional ensure!"
1890
+ code += "} finally {"
1891
+ code += opt_ensure.process opts, LEVEL_TOP
1892
+ end
1893
+
1784
1894
 
1785
1895
  opts[:indent] = old_indent
1786
1896
  code += (fix_line_number(opts, @end_line) + "}")
@@ -1798,7 +1908,8 @@ module Opal
1798
1908
  end
1799
1909
 
1800
1910
  def generate(opts, level)
1801
- "(#{@expr.generate opts, LEVEL_EXPR}.$r ? #{@true.generate opts, LEVEL_EXPR} : #{@false.generate opts, LEVEL_EXPR})"
1911
+ test = generate_truthy_test @expr, opts
1912
+ "(#{test} ? #{@true.generate opts, LEVEL_EXPR} : #{@false.generate opts, LEVEL_EXPR})"
1802
1913
  end
1803
1914
  end
1804
1915