opal 0.3.10 → 0.3.11

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/lib/opal/context.rb CHANGED
@@ -1,38 +1,15 @@
1
1
  module Opal
2
2
  class Context
3
-
4
- def initialize(root_dir = Dir.getwd)
5
- @root_dir = root_dir
6
- @builder = Opal::Builder.new
3
+ # Options are mainly just passed onto the builder/parser.
4
+ def initialize(options = {})
5
+ @options = options
6
+ @root_dir = options[:dir] || Dir.getwd
7
+ @builder = Opal::Builder.new
7
8
  @loaded_paths = false
8
9
 
9
- # special case: if we are running in opal root, then we dont want
10
- # setup.rb to load the opal lib itself, so we do some "magic"
11
- if @root_dir == OPAL_DIR
12
- def self.setup_load_paths
13
- return if @loaded_paths
14
- Dir['packages/*/package.yml'].map do |package|
15
- path = File.expand_path File.join(File.dirname(package), 'lib')
16
- @v8.eval "opal.loader.paths.push('#{path}')"
17
- end
18
- end
19
- end
20
-
21
10
  setup_v8
22
11
  end
23
12
 
24
- ##
25
- # Looks through vendor/ directory and adds all relevant load paths
26
-
27
- def setup_load_paths
28
- return if @loaded_paths
29
-
30
- setup = File.join @root_dir, 'packages', 'init.rb'
31
- return [] unless File.exists? setup
32
-
33
- @v8.eval "opal.run(function() {opal.require('#{setup}');});", setup
34
- end
35
-
36
13
  ##
37
14
  # Require the given id as if it was required in the context. This simply
38
15
  # passes the require through to the underlying context.
@@ -43,17 +20,14 @@ module Opal
43
20
  finish
44
21
  end
45
22
 
46
- ##
47
- # Set ARGV for the context
48
-
23
+ # Set ARGV for the context.
24
+ # @param [Array<String>] args
49
25
  def argv=(args)
50
26
  puts "setting argv to #{args.inspect}"
51
27
  @v8.eval "opal.runtime.cs(opal.runtime.Object, 'ARGV', #{args.inspect});"
52
28
  end
53
29
 
54
- ##
55
30
  # Start normal js repl
56
-
57
31
  def start_repl
58
32
  require 'readline'
59
33
  setup_v8
@@ -74,26 +48,20 @@ module Opal
74
48
  finish
75
49
  end
76
50
 
77
- def eval(content, file = nil, line = "")
78
- begin
79
- js = @builder.parse content
80
- code = "opal.run(function() { var $rb = opal.runtime, self = $rb.top"
81
- code += ", __FILE__ = '(opal)'; return (#{js})($rb, self, __FILE__); })"
82
- # puts code
83
- @v8['$opal_irb_result'] = @v8.eval(code, file)
84
- @v8.eval "!($opal_irb_result == null || !$opal_irb_result.m$inspect) ? $opal_irb_result.m$inspect() : '(Object does not support #inspect)'"
85
- rescue => e
86
- puts e
87
- puts("\t" + e.backtrace.join("\n\t"))
88
- end
51
+ def eval(content, file = "(opal)", line = "")
52
+ js = @builder.parse content, @options
53
+ code = "opal.run(function() { var result = (#{js})(opal.runtime, "
54
+ code += "opal.runtime.top, '(opal)'); if (result == null || !result"
55
+ code += ".m$inspect) { return '(Object does not support #inspect)'; }"
56
+ code += "else { return result.m$inspect() } });"
57
+
58
+ @v8.eval code, file
89
59
  end
90
60
 
91
- ##
92
61
  # Finishes the context, i.e. tidy everything up. This will cause
93
62
  # the opal runtime to do it's at_exit() calls (if applicable) and
94
63
  # then the v8 context will de removed. It can be reset by calling
95
64
  # #setup_v8
96
-
97
65
  def finish
98
66
  return unless @v8
99
67
  @v8.eval "opal.runtime.do_at_exit()", "(opal)"
@@ -116,14 +84,12 @@ module Opal
116
84
  @v8 = V8::Context.new
117
85
  @v8['console'] = Console.new
118
86
 
119
- @v8.eval @builder.build_core, '(opal)'
87
+ @v8.eval File.read(OPAL_JS_PATH), "(opal)"
120
88
  opal = @v8['opal']
121
89
  opal['fs'] = FileSystem.new self
122
90
 
123
91
  # FIXME: we cant use a ruby array as a js array :(
124
92
  opal['loader'] = Loader.new self, @v8.eval("[]")
125
-
126
- setup_load_paths
127
93
  end
128
94
 
129
95
  ##
data/lib/opal/lexer.rb CHANGED
@@ -12,7 +12,7 @@ module Opal
12
12
  # parser = Opal::Parser.new
13
13
  #
14
14
  # parser.parse "self.do_something 1, 2, 3"
15
- # # => "some ruby content"
15
+ # # => "compiled javascript"
16
16
  class Parser < Racc::Parser
17
17
 
18
18
  # Thrown on parsing error
@@ -34,6 +34,25 @@ module Opal
34
34
  # in future bindings. `false` means its just a normal scope,
35
35
  # i.e. top. This is only needed in [Context] for irb.
36
36
  #
37
+ # `:overload_arithmetic` - whether to overload arithmetic operators
38
+ # or not. This defaults to `false` so that all method calls for
39
+ # `+`, `-`, `*`, `/`, `%`, `-@`, `-@` are just compiled directly
40
+ # into their javascript ops. Set to `true` to use ruby style
41
+ # operator overloading.
42
+ #
43
+ # `:overload_bitwise` - whether to overload bitwise operators. This
44
+ # applies to `&`, `|`, `^` and `~`. Defaults to `false`.
45
+ #
46
+ # `:overload_shift` - whether to overload shift bitwise operators.
47
+ # This applies to `<<` and `>>`. `<<` is generally useful for
48
+ # arrays so we keep it seperate. Defaults to `true`.
49
+ #
50
+ # `:overload_equal` - whether to overload `==` and `!==` operators.
51
+ # This defaults to `false`.
52
+ #
53
+ # `:overload_comparison` - whether to overload `<`, `<=`, `>` and
54
+ # `>=` operators. Defaults to `false`.
55
+ #
37
56
  # @param [String] source ruby source code to parse
38
57
  # @param [Hash] options parsing options to use
39
58
  def parse(source, options = {})
@@ -45,7 +64,7 @@ module Opal
45
64
  @scanner = StringScanner.new source
46
65
  nodes = do_parse
47
66
 
48
- return nodes.generate_top
67
+ return nodes.generate_top(options)
49
68
  end
50
69
 
51
70
  def next_token
data/lib/opal/nodes.rb CHANGED
@@ -80,7 +80,7 @@ module Opal
80
80
  end
81
81
 
82
82
  def generate_truthy_test(expr, opts)
83
- if expr.is_a? ComparisonNode
83
+ if expr.is_a?(EqualNode) || expr.is_a?(ComparisonNode)
84
84
  expr.generate opts, LEVEL_EXPR
85
85
  else
86
86
  tmp = opts[:scope].temp_local
@@ -218,6 +218,28 @@ module Opal
218
218
  @line = 1
219
219
  end
220
220
 
221
+ # [Parser] options
222
+ def options=(opts)
223
+ @overload_arithmetic = opts[:overload_arithmetic] || false
224
+ @overload_comparison = opts[:overload_comparison] || false
225
+ @overload_bitwise = opts[:overload_bitwise] || false
226
+ @overload_shift = opts[:overload_shift] || true
227
+ @overload_equal = opts[:overload_equal] || false
228
+ @method_missing = opts[:method_missing] || false
229
+ end
230
+
231
+ def overload_arithmetic?; @overload_arithmetic; end
232
+
233
+ def overload_comparison?; @overload_comparison; end
234
+
235
+ def overload_bitwise?; @overload_bitwise; end
236
+
237
+ def overload_shift?; @overload_shift; end
238
+
239
+ def overload_equal?; @overload_equal; end
240
+
241
+ def method_missing?; @method_missing; end
242
+
221
243
  def generate(opts, level)
222
244
  @opts = opts
223
245
  code = []
@@ -235,7 +257,7 @@ module Opal
235
257
 
236
258
  post += 'var nil = $rb.Qnil, $super = $rb.S, $break = $rb.B, '
237
259
  post += '$class = $rb.dc, $defn = $rb.dm, $defs = $rb.ds, $cg = $rb.cg, '
238
- post += '$range = $rb.G, $hash = $rb.H, $B = $rb.P'
260
+ post += '$range = $rb.G, $hash = $rb.H, $B = $rb.P, $rb_send = $rb.sm'
239
261
 
240
262
  post += ';'
241
263
 
@@ -315,12 +337,17 @@ module Opal
315
337
  end
316
338
 
317
339
  # Generate statements for top level. Generally used for files
318
- def generate_top(opts = {})
340
+ def generate_top(parser_options = {})
319
341
  scope = TopScopeNode.new self
320
- opts[:scope] = scope
321
- opts[:indent] = ''
322
- opts[:top] = scope
323
- scope.generate opts, LEVEL_TOP
342
+ opts = {}
343
+
344
+ scope.options = parser_options
345
+
346
+ opts[:scope] = scope
347
+ opts[:indent] = ''
348
+ opts[:top] = scope
349
+
350
+ return scope.generate opts, LEVEL_TOP
324
351
  end
325
352
  end
326
353
 
@@ -398,9 +425,16 @@ module Opal
398
425
  recv = "self"
399
426
  end
400
427
 
401
- mid = mid_to_jsid(mid)
428
+ # dispatch is true if we use the dispatch method (i.e. we want
429
+ # to support method missing).
430
+ dispatch = opts[:top].method_missing?
431
+
432
+ unless dispatch
433
+ mid = mid_to_jsid mid
434
+ end
402
435
 
403
436
  args = @args
437
+
404
438
  # normal args
405
439
  if args[0]
406
440
  args[0].each do |arg|
@@ -443,19 +477,34 @@ module Opal
443
477
  else
444
478
  # splat args
445
479
  if args[1]
446
- tmp_recv = opts[:scope].temp_local
447
480
  splat = args[1].generate(opts, LEVEL_EXPR)
448
- splat_args = arg_res.empty? ? "#{splat}" : "[#{arg_res.join ', '}].concat(#{splat})"
449
- # when using splat, our this val for apply may need a tmp var
450
- # to save just outputting it twice (have to follow recv path twice)
451
- splat_recv = recv
452
- result = "(#{tmp_recv} = #{recv})" + mid + ".apply(#{tmp_recv}, #{splat_args})"
453
481
 
482
+ if dispatch
483
+ arg_res.unshift recv, "'#{mid}'"
484
+ splat_args = "[#{arg_res.join ', '}].concat(#{splat})"
485
+
486
+ return "$rb_send.apply(null, #{splat_args})"
487
+ end
488
+
489
+ # non-disptach
490
+ tmp_recv = opts[:scope].temp_local
491
+ splat_args = arg_res.empty? ? "#{splat}" :
492
+ "[#{arg_res.join ', '}].concat(#{splat})"
493
+
494
+ result = "(#{tmp_recv} = #{recv})" + mid + ".apply("
495
+ result += "#{tmp_recv}, #{splat_args})"
454
496
  opts[:scope].queue_temp tmp_recv
455
- result
497
+
498
+ return result
499
+
500
+ # not a block call, and not a &to_proc call
456
501
  else
457
- result = "#{recv}#{mid}(#{arg_res.join(', ')})"
458
- result
502
+ if dispatch
503
+ arg_res.unshift recv, "'#{mid}'"
504
+ "$rb_send(#{arg_res.join ', '})"
505
+ else
506
+ "#{recv}#{mid}(#{arg_res.join(', ')})"
507
+ end
459
508
  end
460
509
  end
461
510
  end
@@ -1265,6 +1314,59 @@ module Opal
1265
1314
  end
1266
1315
  end
1267
1316
 
1317
+ class ArithmeticNode < BaseNode
1318
+ def initialize(lhs, op, rhs)
1319
+ @lhs = lhs
1320
+ @op = op[:value]
1321
+ @line = op[:line]
1322
+ @rhs = rhs
1323
+ end
1324
+
1325
+ def generate(opts, level)
1326
+ lhs = @lhs.generate opts, LEVEL_EXPR
1327
+ rhs = @rhs.generate opts, LEVEL_EXPR
1328
+
1329
+ if opts[:top].overload_arithmetic?
1330
+ if opts[:top].method_missing?
1331
+ return "$rb_send(#{lhs}, 'm$#{@op}', #{rhs})"
1332
+ else
1333
+ lhs = "(#{lhs})" if @lhs.is_a? NumericNode
1334
+ return "#{lhs}['m$#{@op}'](#{rhs})"
1335
+ end
1336
+ else
1337
+ return "#{lhs} #{@op} #{rhs}"
1338
+ end
1339
+ end
1340
+ end
1341
+
1342
+ class EqualNode < BaseNode
1343
+ def initialize(lhs, op, rhs)
1344
+ @line = op[:line]
1345
+ @op = op[:value]
1346
+ @lhs = lhs
1347
+ @rhs = rhs
1348
+ end
1349
+
1350
+ def generate(opts, level)
1351
+ lhs = @lhs.generate opts, LEVEL_EXPR
1352
+ rhs = @rhs.generate opts, LEVEL_EXPR
1353
+
1354
+ if opts[:top].overload_equal?
1355
+ if opts[:top].method_missing?
1356
+ return "$rb_send(#{lhs}, 'm$#{@op}', #{rhs})"
1357
+ else
1358
+ lhs = "(#{lhs})" if @lhs.is_a? NumericNode
1359
+ return "#{lhs}['m$#{@op}'](#{rhs})"
1360
+ end
1361
+ else
1362
+ op = "#{@op}="
1363
+ lhs = "(#{lhs})" if @lhs.is_a? NumericNode
1364
+ rhs = "(#{rhs})" if @rhs.is_a? NumericNode
1365
+ return "#{lhs}.valueOf() #{op} #{rhs}.valueOf()"
1366
+ end
1367
+ end
1368
+ end
1369
+
1268
1370
  class ComparisonNode < BaseNode
1269
1371
 
1270
1372
  def initialize(op, lhs, rhs)
@@ -1279,10 +1381,22 @@ module Opal
1279
1381
  lhs = "(#{lhs})" if @lhs.is_a? NumericNode
1280
1382
  rhs = @rhs.generate opts, LEVEL_EXPR
1281
1383
 
1282
- if @op == '!='
1283
- "!#{lhs}['m$=='](#{rhs})"
1384
+ if opts[:top].overload_equal?
1385
+ if @op == '!='
1386
+ "!#{lhs}['m$=='](#{rhs})"
1387
+ else
1388
+ "#{lhs}['m$#{@op}'](#{rhs})"
1389
+ end
1284
1390
  else
1285
- "#{lhs}['m$#{@op}'](#{rhs})"
1391
+ if @op == '!='
1392
+ rhs = "(#{rhs})" if @rhs.is_a? NumericNode
1393
+ "#{lhs}.valueOf() !== #{rhs}.valueOf()"
1394
+ elsif @op == '=='
1395
+ rhs = "(#{rhs})" if @rhs.is_a? NumericNode
1396
+ "#{lhs}.valueOf() === #{rhs}.valueOf()"
1397
+ else
1398
+ "#{lhs} #{@op} #{rhs}"
1399
+ end
1286
1400
  end
1287
1401
  end
1288
1402
  end
data/lib/opal/parser.rb CHANGED
@@ -3451,31 +3451,31 @@ def _reduce_178(val, _values, result)
3451
3451
  end
3452
3452
 
3453
3453
  def _reduce_179(val, _values, result)
3454
- result = CallNode.new val[0], val[1], [[val[2]]]
3454
+ result = ArithmeticNode.new val[0], val[1], val[2]
3455
3455
 
3456
3456
  result
3457
3457
  end
3458
3458
 
3459
3459
  def _reduce_180(val, _values, result)
3460
- result = CallNode.new val[0], val[1], [[val[2]]]
3460
+ result = ArithmeticNode.new val[0], val[1], val[2]
3461
3461
 
3462
3462
  result
3463
3463
  end
3464
3464
 
3465
3465
  def _reduce_181(val, _values, result)
3466
- result = CallNode.new val[0], val[1], [[val[2]]]
3466
+ result = ArithmeticNode.new val[0], val[1], val[2]
3467
3467
 
3468
3468
  result
3469
3469
  end
3470
3470
 
3471
3471
  def _reduce_182(val, _values, result)
3472
- result = CallNode.new val[0], val[1], [[val[2]]]
3472
+ result = ArithmeticNode.new val[0], val[1], val[2]
3473
3473
 
3474
3474
  result
3475
3475
  end
3476
3476
 
3477
3477
  def _reduce_183(val, _values, result)
3478
- result = CallNode.new val[0], val[1], [[val[2]]]
3478
+ result = ArithmeticNode.new val[0], val[1], val[2]
3479
3479
 
3480
3480
  result
3481
3481
  end
@@ -3547,7 +3547,7 @@ def _reduce_194(val, _values, result)
3547
3547
  end
3548
3548
 
3549
3549
  def _reduce_195(val, _values, result)
3550
- result = ComparisonNode.new val[1], val[0], val[2]
3550
+ result = EqualNode.new val[0], val[1], val[2]
3551
3551
 
3552
3552
  result
3553
3553
  end
@@ -3559,7 +3559,7 @@ def _reduce_196(val, _values, result)
3559
3559
  end
3560
3560
 
3561
3561
  def _reduce_197(val, _values, result)
3562
- result = ComparisonNode.new val[1], val[0], val[2]
3562
+ result = EqualNode.new val[0], val[1], val[2]
3563
3563
 
3564
3564
  result
3565
3565
  end
data/lib/opal/parser.y CHANGED
@@ -377,23 +377,23 @@ arg:
377
377
  }
378
378
  | arg '+' arg
379
379
  {
380
- result = CallNode.new val[0], val[1], [[val[2]]]
380
+ result = ArithmeticNode.new val[0], val[1], val[2]
381
381
  }
382
382
  | arg '-' arg
383
383
  {
384
- result = CallNode.new val[0], val[1], [[val[2]]]
384
+ result = ArithmeticNode.new val[0], val[1], val[2]
385
385
  }
386
386
  | arg '*' arg
387
387
  {
388
- result = CallNode.new val[0], val[1], [[val[2]]]
388
+ result = ArithmeticNode.new val[0], val[1], val[2]
389
389
  }
390
390
  | arg '/' arg
391
391
  {
392
- result = CallNode.new val[0], val[1], [[val[2]]]
392
+ result = ArithmeticNode.new val[0], val[1], val[2]
393
393
  }
394
394
  | arg '%' arg
395
395
  {
396
- result = CallNode.new val[0], val[1], [[val[2]]]
396
+ result = ArithmeticNode.new val[0], val[1], val[2]
397
397
  }
398
398
  | arg '**' arg
399
399
  {
@@ -441,7 +441,7 @@ arg:
441
441
  }
442
442
  | arg '==' arg
443
443
  {
444
- result = ComparisonNode.new val[1], val[0], val[2]
444
+ result = EqualNode.new val[0], val[1], val[2]
445
445
  }
446
446
  | arg '===' arg
447
447
  {
@@ -449,7 +449,7 @@ arg:
449
449
  }
450
450
  | arg '!=' arg
451
451
  {
452
- result = ComparisonNode.new val[1], val[0], val[2]
452
+ result = EqualNode.new val[0], val[1], val[2]
453
453
  }
454
454
  | arg '=~' arg
455
455
  {