opal 0.7.2 → 0.8.0.beta1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (94) hide show
  1. checksums.yaml +4 -4
  2. data/.travis.yml +6 -1
  3. data/CHANGELOG.md +29 -0
  4. data/CONTRIBUTING.md +51 -4
  5. data/Gemfile +3 -0
  6. data/README.md +5 -5
  7. data/config.ru +1 -1
  8. data/examples/sinatra/Gemfile +1 -0
  9. data/examples/sinatra/config.ru +13 -3
  10. data/lib/mspec/opal/rake_task.rb +21 -30
  11. data/lib/mspec/opal/runner.rb +37 -0
  12. data/lib/mspec/opal/special_calls.rb +6 -0
  13. data/lib/opal/builder.rb +1 -0
  14. data/lib/opal/builder_processors.rb +5 -2
  15. data/lib/opal/cli_runners/phantom.js +10 -1
  16. data/lib/opal/compiler.rb +6 -3
  17. data/lib/opal/config.rb +48 -0
  18. data/lib/opal/nodes/call.rb +3 -2
  19. data/lib/opal/nodes/literal.rb +19 -2
  20. data/lib/opal/parser/grammar.rb +2224 -2196
  21. data/lib/opal/parser/grammar.y +25 -7
  22. data/lib/opal/parser/lexer.rb +12 -9
  23. data/lib/opal/path_reader.rb +1 -1
  24. data/lib/opal/sprockets/erb.rb +6 -20
  25. data/lib/opal/sprockets/path_reader.rb +4 -2
  26. data/lib/opal/sprockets/processor.rb +135 -80
  27. data/lib/opal/sprockets/server.rb +49 -78
  28. data/lib/opal/sprockets/source_map_header_patch.rb +41 -0
  29. data/lib/opal/sprockets/source_map_server.rb +115 -0
  30. data/lib/opal/version.rb +1 -1
  31. data/lib/tilt/opal.rb +48 -0
  32. data/opal.gemspec +1 -1
  33. data/opal/corelib/array.rb +179 -51
  34. data/opal/corelib/array/inheritance.rb +14 -0
  35. data/opal/corelib/boolean.rb +5 -0
  36. data/opal/corelib/hash.rb +1 -1
  37. data/opal/corelib/kernel.rb +660 -164
  38. data/opal/corelib/match_data.rb +44 -21
  39. data/opal/corelib/module.rb +83 -53
  40. data/opal/corelib/numeric.rb +15 -1
  41. data/opal/corelib/regexp.rb +31 -75
  42. data/opal/corelib/runtime.js +20 -8
  43. data/opal/corelib/string.rb +754 -243
  44. data/opal/corelib/string/inheritance.rb +20 -3
  45. data/opal/corelib/struct.rb +30 -6
  46. data/opal/corelib/variables.rb +2 -2
  47. data/spec/filters/bugs/array.rb +0 -39
  48. data/spec/filters/bugs/kernel.rb +10 -7
  49. data/spec/filters/bugs/module.rb +21 -0
  50. data/spec/filters/bugs/opal.rb +0 -5
  51. data/spec/filters/bugs/singleton.rb +0 -2
  52. data/spec/filters/bugs/string.rb +69 -315
  53. data/spec/filters/bugs/struct.rb +0 -16
  54. data/spec/filters/unsupported/encoding.rb +7 -0
  55. data/spec/filters/unsupported/float.rb +3 -0
  56. data/spec/filters/unsupported/integer_size.rb +52 -0
  57. data/spec/filters/unsupported/marshal.rb +4 -0
  58. data/spec/filters/unsupported/mutable_strings.rb +37 -0
  59. data/spec/filters/unsupported/private_methods.rb +11 -0
  60. data/spec/filters/unsupported/rational_numbers.rb +4 -0
  61. data/spec/filters/unsupported/regular_expressions.rb +47 -0
  62. data/spec/filters/unsupported/symbols.rb +7 -0
  63. data/spec/filters/unsupported/tainted.rb +23 -1
  64. data/spec/filters/unsupported/trusted.rb +5 -0
  65. data/spec/lib/fixtures/complex_sprockets.js.rb.erb +4 -0
  66. data/spec/lib/fixtures/jst_file.js.jst +1 -0
  67. data/spec/lib/parser/call_spec.rb +19 -0
  68. data/spec/lib/parser/def_spec.rb +6 -0
  69. data/spec/lib/sprockets/erb_spec.rb +17 -4
  70. data/spec/lib/sprockets/processor_spec.rb +50 -18
  71. data/spec/lib/sprockets/server_spec.rb +39 -11
  72. data/spec/lib/tilt/opal_spec.rb +19 -0
  73. data/spec/opal/core/kernel/format_spec.rb +10 -10
  74. data/spec/opal/core/language/predefined_spec.rb +10 -0
  75. data/spec/opal/core/object_id_spec.rb +56 -0
  76. data/spec/opal/core/runtime/bridged_classes_spec.rb +4 -4
  77. data/spec/opal/core/runtime_spec.rb +7 -0
  78. data/spec/opal/stdlib/native/native_class_spec.rb +1 -1
  79. data/spec/opal/stdlib/promise/always_spec.rb +30 -0
  80. data/spec/opal/stdlib/promise/then_spec.rb +8 -0
  81. data/spec/opal/stdlib/promise/trace_spec.rb +8 -0
  82. data/spec/rubyspecs +15 -104
  83. data/spec/spec_helper.rb +4 -0
  84. data/stdlib/native.rb +7 -18
  85. data/stdlib/nodejs/file.rb +1 -1
  86. data/stdlib/opal-parser.rb +1 -0
  87. data/stdlib/pp.rb +7 -5
  88. data/stdlib/promise.rb +53 -41
  89. data/tasks/testing.rake +8 -6
  90. metadata +28 -14
  91. data/spec/filters/bugs/match_data.rb +0 -13
  92. data/spec/filters/bugs/numeric.rb +0 -22
  93. data/spec/filters/bugs/regexp.rb +0 -9
  94. data/spec/filters/bugs/unknown.rb +0 -11
@@ -16,6 +16,8 @@ class Array
16
16
  end
17
17
 
18
18
  class Array::Wrapper
19
+ `def.$$is_array = true`
20
+
19
21
  def self.allocate(array = [])
20
22
  obj = super()
21
23
  `obj.literal = array`
@@ -74,6 +76,10 @@ class Array::Wrapper
74
76
  @literal.inspect
75
77
  end
76
78
 
79
+ def hash
80
+ @literal.hash
81
+ end
82
+
77
83
  # wrapped results
78
84
  def *(other)
79
85
  %x{
@@ -110,4 +116,12 @@ class Array::Wrapper
110
116
  def flatten(level = undefined)
111
117
  self.class.allocate(@literal.flatten(level))
112
118
  end
119
+
120
+ def -(other)
121
+ @literal - other
122
+ end
123
+
124
+ def +(other)
125
+ @literal + other
126
+ end
113
127
  end
@@ -1,6 +1,11 @@
1
1
  class Boolean
2
2
  `def.$$is_boolean = true`
3
3
 
4
+ def __id__
5
+ `self.valueOf() ? 2 : 0`
6
+ end
7
+ alias object_id __id__
8
+
4
9
  class << self
5
10
  undef_method :new
6
11
  end
data/opal/corelib/hash.rb CHANGED
@@ -423,7 +423,7 @@ class Hash
423
423
  return defaults;
424
424
  }
425
425
 
426
- #{ raise KeyError, "key not found" };
426
+ #{ raise KeyError, "key not found: #{key.inspect}" };
427
427
  }
428
428
  end
429
429
 
@@ -8,12 +8,14 @@ module Kernel
8
8
  end
9
9
 
10
10
  def ===(other)
11
- self == other
11
+ self.object_id == other.object_id || self == other
12
12
  end
13
13
 
14
14
  def <=>(other)
15
15
  %x{
16
- if (#{self == other}) {
16
+ var x = #{self == other};
17
+
18
+ if (x && x !== nil) {
17
19
  return 0;
18
20
  }
19
21
 
@@ -54,20 +56,25 @@ module Kernel
54
56
  }
55
57
  end
56
58
 
57
- def Array(object, *args, &block)
59
+ def Array(object)
58
60
  %x{
59
- if (object == null || object === nil) {
61
+ var coerced;
62
+
63
+ if (object === nil) {
60
64
  return [];
61
65
  }
62
- else if (#{object.respond_to? :to_ary}) {
63
- return #{object.to_ary};
64
- }
65
- else if (#{object.respond_to? :to_a}) {
66
- return #{object.to_a};
67
- }
68
- else {
69
- return [object];
66
+
67
+ if (object.$$is_array) {
68
+ return object;
70
69
  }
70
+
71
+ coerced = #{Opal.coerce_to?(object, Array, :to_ary)};
72
+ if (coerced !== nil) { return coerced; }
73
+
74
+ coerced = #{Opal.coerce_to?(object, Array, :to_a)};
75
+ if (coerced !== nil) { return coerced; }
76
+
77
+ return [object];
71
78
  }
72
79
  end
73
80
 
@@ -171,132 +178,544 @@ module Kernel
171
178
  self
172
179
  end
173
180
 
174
- def format(format, *args)
181
+ def format(format_string, *args)
182
+ if args.length == 1 && args[0].respond_to?(:to_ary)
183
+ args = args[0].to_ary
184
+ args = args.to_a
185
+ end
186
+
175
187
  %x{
176
- var idx = 0;
177
- return format.replace(/%(\d+\$)?([-+ 0]*)(\d*|\*(\d+\$)?)(?:\.(\d*|\*(\d+\$)?))?([cspdiubBoxXfgeEG])|(%%)/g, function(str, idx_str, flags, width_str, w_idx_str, prec_str, p_idx_str, spec, escaped) {
178
- if (escaped) {
179
- return '%';
188
+ var result = '',
189
+ //used for slicing:
190
+ begin_slice = 0,
191
+ end_slice,
192
+ //used for iterating over the format string:
193
+ i,
194
+ len = format_string.length,
195
+ //used for processing field values:
196
+ arg,
197
+ str,
198
+ //used for processing %g and %G fields:
199
+ exponent,
200
+ //used for keeping track of width and precision:
201
+ width,
202
+ precision,
203
+ //used for holding temporary values:
204
+ tmp_num,
205
+ //used for processing %{} and %<> fileds:
206
+ hash_parameter_key,
207
+ closing_brace_char,
208
+ //used for processing %b, %B, %o, %x, and %X fields:
209
+ base_number,
210
+ base_prefix,
211
+ base_neg_zero_regex,
212
+ base_neg_zero_digit,
213
+ //used for processing arguments:
214
+ next_arg,
215
+ seq_arg_num = 1,
216
+ pos_arg_num = 0,
217
+ //used for keeping track of flags:
218
+ flags,
219
+ FNONE = 0,
220
+ FSHARP = 1,
221
+ FMINUS = 2,
222
+ FPLUS = 4,
223
+ FZERO = 8,
224
+ FSPACE = 16,
225
+ FWIDTH = 32,
226
+ FPREC = 64,
227
+ FPREC0 = 128;
228
+
229
+ function CHECK_FOR_FLAGS() {
230
+ if (flags&FWIDTH) { #{raise ArgumentError, 'flag after width'} }
231
+ if (flags&FPREC0) { #{raise ArgumentError, 'flag after precision'} }
232
+ }
233
+
234
+ function CHECK_FOR_WIDTH() {
235
+ if (flags&FWIDTH) { #{raise ArgumentError, 'width given twice'} }
236
+ if (flags&FPREC0) { #{raise ArgumentError, 'width after precision'} }
237
+ }
238
+
239
+ function GET_NTH_ARG(num) {
240
+ if (num >= args.length) { #{raise ArgumentError, 'too few arguments'} }
241
+ return args[num];
242
+ }
243
+
244
+ function GET_NEXT_ARG() {
245
+ switch (pos_arg_num) {
246
+ case -1: #{raise ArgumentError, "unnumbered(#{`seq_arg_num`}) mixed with numbered"}
247
+ case -2: #{raise ArgumentError, "unnumbered(#{`seq_arg_num`}) mixed with named"}
180
248
  }
249
+ pos_arg_num = seq_arg_num++;
250
+ return GET_NTH_ARG(pos_arg_num - 1);
251
+ }
181
252
 
182
- var width,
183
- prec,
184
- is_integer_spec = ("diubBoxX".indexOf(spec) != -1),
185
- is_float_spec = ("eEfgG".indexOf(spec) != -1),
186
- prefix = '',
187
- obj;
188
-
189
- if (width_str === undefined) {
190
- width = undefined;
191
- } else if (width_str.charAt(0) == '*') {
192
- var w_idx = idx++;
193
- if (w_idx_str) {
194
- w_idx = parseInt(w_idx_str, 10) - 1;
195
- }
196
- width = #{`args[w_idx]`.to_i};
197
- } else {
198
- width = parseInt(width_str, 10);
253
+ function GET_POS_ARG(num) {
254
+ if (pos_arg_num > 0) {
255
+ #{raise ArgumentError, "numbered(#{`num`}) after unnumbered(#{`pos_arg_num`})"}
199
256
  }
200
- if (!prec_str) {
201
- prec = is_float_spec ? 6 : undefined;
202
- } else if (prec_str.charAt(0) == '*') {
203
- var p_idx = idx++;
204
- if (p_idx_str) {
205
- p_idx = parseInt(p_idx_str, 10) - 1;
206
- }
207
- prec = #{`args[p_idx]`.to_i};
208
- } else {
209
- prec = parseInt(prec_str, 10);
257
+ if (pos_arg_num === -2) {
258
+ #{raise ArgumentError, "numbered(#{`num`}) after named"}
210
259
  }
211
- if (idx_str) {
212
- idx = parseInt(idx_str, 10) - 1;
260
+ if (num < 1) {
261
+ #{raise ArgumentError, "invalid index - #{`num`}$"}
213
262
  }
214
- switch (spec) {
215
- case 'c':
216
- obj = args[idx];
217
- if (obj.$$is_string) {
218
- str = obj.charAt(0);
219
- } else {
220
- str = String.fromCharCode(#{`obj`.to_i});
221
- }
222
- break;
223
- case 's':
224
- str = #{`args[idx]`.to_s};
225
- if (prec !== undefined) {
226
- str = str.substr(0, prec);
227
- }
228
- break;
229
- case 'p':
230
- str = #{`args[idx]`.inspect};
231
- if (prec !== undefined) {
232
- str = str.substr(0, prec);
263
+ pos_arg_num = -1;
264
+ return GET_NTH_ARG(num - 1);
265
+ }
266
+
267
+ function GET_ARG() {
268
+ return (next_arg === undefined ? GET_NEXT_ARG() : next_arg);
269
+ }
270
+
271
+ function READ_NUM(label) {
272
+ var num, str = '';
273
+ for (;; i++) {
274
+ if (i === len) {
275
+ #{raise ArgumentError, 'malformed format string - %*[0-9]'}
233
276
  }
234
- break;
235
- case 'd':
236
- case 'i':
237
- case 'u':
238
- str = #{`args[idx]`.to_i}.toString();
239
- break;
240
- case 'b':
241
- case 'B':
242
- str = #{`args[idx]`.to_i}.toString(2);
243
- break;
244
- case 'o':
245
- str = #{`args[idx]`.to_i}.toString(8);
246
- break;
247
- case 'x':
248
- case 'X':
249
- str = #{`args[idx]`.to_i}.toString(16);
250
- break;
251
- case 'e':
252
- case 'E':
253
- str = #{`args[idx]`.to_f}.toExponential(prec);
254
- break;
255
- case 'f':
256
- str = #{`args[idx]`.to_f}.toFixed(prec);
257
- break;
258
- case 'g':
259
- case 'G':
260
- str = #{`args[idx]`.to_f}.toPrecision(prec);
261
- break;
262
- }
263
- idx++;
264
- if (is_integer_spec || is_float_spec) {
265
- if (str.charAt(0) == '-') {
266
- prefix = '-';
267
- str = str.substr(1);
268
- } else {
269
- if (flags.indexOf('+') != -1) {
270
- prefix = '+';
271
- } else if (flags.indexOf(' ') != -1) {
272
- prefix = ' ';
277
+ if (format_string.charCodeAt(i) < 48 || format_string.charCodeAt(i) > 57) {
278
+ i--;
279
+ num = parseInt(str) || 0;
280
+ if (num > 2147483647) {
281
+ #{raise ArgumentError, "#{`label`} too big"}
273
282
  }
283
+ return num;
274
284
  }
285
+ str += format_string.charAt(i);
275
286
  }
276
- if (is_integer_spec && prec !== undefined) {
277
- if (str.length < prec) {
278
- str = #{'0' * `prec - str.length`} + str;
279
- }
287
+ }
288
+
289
+ function READ_NUM_AFTER_ASTER(label) {
290
+ var arg, num = READ_NUM(label);
291
+ if (format_string.charAt(i + 1) === '$') {
292
+ i++;
293
+ arg = GET_POS_ARG(num);
294
+ } else {
295
+ arg = GET_NEXT_ARG();
280
296
  }
281
- var total_len = prefix.length + str.length;
282
- if (width !== undefined && total_len < width) {
283
- if (flags.indexOf('-') != -1) {
284
- str = str + #{' ' * `width - total_len`};
285
- } else {
286
- var pad_char = ' ';
287
- if (flags.indexOf('0') != -1) {
288
- str = #{'0' * `width - total_len`} + str;
297
+ return #{`arg`.to_int};
298
+ }
299
+
300
+ for (i = format_string.indexOf('%'); i !== -1; i = format_string.indexOf('%', i)) {
301
+ str = undefined;
302
+
303
+ flags = FNONE;
304
+ width = -1;
305
+ precision = -1;
306
+ next_arg = undefined;
307
+
308
+ end_slice = i;
309
+
310
+ i++;
311
+
312
+ switch (format_string.charAt(i)) {
313
+ case '%':
314
+ begin_slice = i;
315
+ case '':
316
+ case '\n':
317
+ case '\0':
318
+ i++;
319
+ continue;
320
+ }
321
+
322
+ format_sequence: for (; i < len; i++) {
323
+ switch (format_string.charAt(i)) {
324
+
325
+ case ' ':
326
+ CHECK_FOR_FLAGS();
327
+ flags |= FSPACE;
328
+ continue format_sequence;
329
+
330
+ case '#':
331
+ CHECK_FOR_FLAGS();
332
+ flags |= FSHARP;
333
+ continue format_sequence;
334
+
335
+ case '+':
336
+ CHECK_FOR_FLAGS();
337
+ flags |= FPLUS;
338
+ continue format_sequence;
339
+
340
+ case '-':
341
+ CHECK_FOR_FLAGS();
342
+ flags |= FMINUS;
343
+ continue format_sequence;
344
+
345
+ case '0':
346
+ CHECK_FOR_FLAGS();
347
+ flags |= FZERO;
348
+ continue format_sequence;
349
+
350
+ case '1':
351
+ case '2':
352
+ case '3':
353
+ case '4':
354
+ case '5':
355
+ case '6':
356
+ case '7':
357
+ case '8':
358
+ case '9':
359
+ tmp_num = READ_NUM('width');
360
+ if (format_string.charAt(i + 1) === '$') {
361
+ if (i + 2 === len) {
362
+ str = '%';
363
+ i++;
364
+ break format_sequence;
365
+ }
366
+ if (next_arg !== undefined) {
367
+ #{raise ArgumentError, "value given twice - %#{`tmp_num`}$"}
368
+ }
369
+ next_arg = GET_POS_ARG(tmp_num);
370
+ i++;
371
+ } else {
372
+ CHECK_FOR_WIDTH();
373
+ flags |= FWIDTH;
374
+ width = tmp_num;
375
+ }
376
+ continue format_sequence;
377
+
378
+ case '<':
379
+ case '\{':
380
+ closing_brace_char = (format_string.charAt(i) === '<' ? '>' : '\}');
381
+ hash_parameter_key = '';
382
+
383
+ i++;
384
+
385
+ for (;; i++) {
386
+ if (i === len) {
387
+ #{raise ArgumentError, 'malformed name - unmatched parenthesis'}
388
+ }
389
+ if (format_string.charAt(i) === closing_brace_char) {
390
+
391
+ if (pos_arg_num > 0) {
392
+ #{raise ArgumentError, "named #{`hash_parameter_key`} after unnumbered(#{`pos_arg_num`})"}
393
+ }
394
+ if (pos_arg_num === -1) {
395
+ #{raise ArgumentError, "named #{`hash_parameter_key`} after numbered"}
396
+ }
397
+ pos_arg_num = -2;
398
+
399
+ if (args[0] === undefined || !args[0].$$is_hash) {
400
+ #{raise ArgumentError, 'one hash required'}
401
+ }
402
+
403
+ next_arg = #{`args[0]`.fetch(`hash_parameter_key`)};
404
+
405
+ if (closing_brace_char === '>') {
406
+ continue format_sequence;
407
+ } else {
408
+ str = next_arg.toString();
409
+ if (precision !== -1) { str = str.slice(0, precision); }
410
+ if (flags&FMINUS) {
411
+ while (str.length < width) { str = str + ' '; }
412
+ } else {
413
+ while (str.length < width) { str = ' ' + str; }
414
+ }
415
+ break format_sequence;
416
+ }
417
+ }
418
+ hash_parameter_key += format_string.charAt(i);
419
+ }
420
+
421
+ case '*':
422
+ i++;
423
+ CHECK_FOR_WIDTH();
424
+ flags |= FWIDTH;
425
+ width = READ_NUM_AFTER_ASTER('width');
426
+ if (width < 0) {
427
+ flags |= FMINUS;
428
+ width = -width;
429
+ }
430
+ continue format_sequence;
431
+
432
+ case '.':
433
+ if (flags&FPREC0) {
434
+ #{raise ArgumentError, 'precision given twice'}
435
+ }
436
+ flags |= FPREC|FPREC0;
437
+ precision = 0;
438
+ i++;
439
+ if (format_string.charAt(i) === '*') {
440
+ i++;
441
+ precision = READ_NUM_AFTER_ASTER('precision');
442
+ if (precision < 0) {
443
+ flags &= ~FPREC;
444
+ }
445
+ continue format_sequence;
446
+ }
447
+ precision = READ_NUM('precision');
448
+ continue format_sequence;
449
+
450
+ case 'd':
451
+ case 'i':
452
+ case 'u':
453
+ arg = #{Integer(`GET_ARG()`)};
454
+ if (arg >= 0) {
455
+ str = arg.toString();
456
+ while (str.length < precision) { str = '0' + str; }
457
+ if (flags&FMINUS) {
458
+ if (flags&FPLUS || flags&FSPACE) { str = (flags&FPLUS ? '+' : ' ') + str; }
459
+ while (str.length < width) { str = str + ' '; }
460
+ } else {
461
+ if (flags&FZERO && precision === -1) {
462
+ while (str.length < width - ((flags&FPLUS || flags&FSPACE) ? 1 : 0)) { str = '0' + str; }
463
+ if (flags&FPLUS || flags&FSPACE) { str = (flags&FPLUS ? '+' : ' ') + str; }
464
+ } else {
465
+ if (flags&FPLUS || flags&FSPACE) { str = (flags&FPLUS ? '+' : ' ') + str; }
466
+ while (str.length < width) { str = ' ' + str; }
467
+ }
468
+ }
469
+ } else {
470
+ str = (-arg).toString();
471
+ while (str.length < precision) { str = '0' + str; }
472
+ if (flags&FMINUS) {
473
+ str = '-' + str;
474
+ while (str.length < width) { str = str + ' '; }
475
+ } else {
476
+ if (flags&FZERO && precision === -1) {
477
+ while (str.length < width - 1) { str = '0' + str; }
478
+ str = '-' + str;
479
+ } else {
480
+ str = '-' + str;
481
+ while (str.length < width) { str = ' ' + str; }
482
+ }
483
+ }
484
+ }
485
+ break format_sequence;
486
+
487
+ case 'b':
488
+ case 'B':
489
+ case 'o':
490
+ case 'x':
491
+ case 'X':
492
+ switch (format_string.charAt(i)) {
493
+ case 'b':
494
+ case 'B':
495
+ base_number = 2;
496
+ base_prefix = '0b';
497
+ base_neg_zero_regex = /^1+/;
498
+ base_neg_zero_digit = '1';
499
+ break;
500
+ case 'o':
501
+ base_number = 8;
502
+ base_prefix = '0';
503
+ base_neg_zero_regex = /^3?7+/;
504
+ base_neg_zero_digit = '7';
505
+ break;
506
+ case 'x':
507
+ case 'X':
508
+ base_number = 16;
509
+ base_prefix = '0x';
510
+ base_neg_zero_regex = /^f+/;
511
+ base_neg_zero_digit = 'f';
512
+ break;
513
+ }
514
+ arg = #{Integer(`GET_ARG()`)};
515
+ if (arg >= 0) {
516
+ str = arg.toString(base_number);
517
+ while (str.length < precision) { str = '0' + str; }
518
+ if (flags&FMINUS) {
519
+ if (flags&FPLUS || flags&FSPACE) { str = (flags&FPLUS ? '+' : ' ') + str; }
520
+ if (flags&FSHARP && arg !== 0) { str = base_prefix + str; }
521
+ while (str.length < width) { str = str + ' '; }
522
+ } else {
523
+ if (flags&FZERO && precision === -1) {
524
+ while (str.length < width - ((flags&FPLUS || flags&FSPACE) ? 1 : 0) - ((flags&FSHARP && arg !== 0) ? base_prefix.length : 0)) { str = '0' + str; }
525
+ if (flags&FSHARP && arg !== 0) { str = base_prefix + str; }
526
+ if (flags&FPLUS || flags&FSPACE) { str = (flags&FPLUS ? '+' : ' ') + str; }
527
+ } else {
528
+ if (flags&FSHARP && arg !== 0) { str = base_prefix + str; }
529
+ if (flags&FPLUS || flags&FSPACE) { str = (flags&FPLUS ? '+' : ' ') + str; }
530
+ while (str.length < width) { str = ' ' + str; }
531
+ }
532
+ }
533
+ } else {
534
+ if (flags&FPLUS || flags&FSPACE) {
535
+ str = (-arg).toString(base_number);
536
+ while (str.length < precision) { str = '0' + str; }
537
+ if (flags&FMINUS) {
538
+ if (flags&FSHARP) { str = base_prefix + str; }
539
+ str = '-' + str;
540
+ while (str.length < width) { str = str + ' '; }
541
+ } else {
542
+ if (flags&FZERO && precision === -1) {
543
+ while (str.length < width - 1 - (flags&FSHARP ? 2 : 0)) { str = '0' + str; }
544
+ if (flags&FSHARP) { str = base_prefix + str; }
545
+ str = '-' + str;
546
+ } else {
547
+ if (flags&FSHARP) { str = base_prefix + str; }
548
+ str = '-' + str;
549
+ while (str.length < width) { str = ' ' + str; }
550
+ }
551
+ }
552
+ } else {
553
+ str = (arg >>> 0).toString(base_number).replace(base_neg_zero_regex, base_neg_zero_digit);
554
+ while (str.length < precision - 2) { str = base_neg_zero_digit + str; }
555
+ if (flags&FMINUS) {
556
+ str = '..' + str;
557
+ if (flags&FSHARP) { str = base_prefix + str; }
558
+ while (str.length < width) { str = str + ' '; }
559
+ } else {
560
+ if (flags&FZERO && precision === -1) {
561
+ while (str.length < width - 2 - (flags&FSHARP ? base_prefix.length : 0)) { str = base_neg_zero_digit + str; }
562
+ str = '..' + str;
563
+ if (flags&FSHARP) { str = base_prefix + str; }
564
+ } else {
565
+ str = '..' + str;
566
+ if (flags&FSHARP) { str = base_prefix + str; }
567
+ while (str.length < width) { str = ' ' + str; }
568
+ }
569
+ }
570
+ }
571
+ }
572
+ if (format_string.charAt(i) === format_string.charAt(i).toUpperCase()) {
573
+ str = str.toUpperCase();
574
+ }
575
+ break format_sequence;
576
+
577
+ case 'f':
578
+ case 'e':
579
+ case 'E':
580
+ case 'g':
581
+ case 'G':
582
+ arg = #{Float(`GET_ARG()`)};
583
+ if (arg >= 0 || isNaN(arg)) {
584
+ if (arg === Infinity) {
585
+ str = 'Inf';
586
+ } else {
587
+ switch (format_string.charAt(i)) {
588
+ case 'f':
589
+ str = arg.toFixed(precision === -1 ? 6 : precision);
590
+ break;
591
+ case 'e':
592
+ case 'E':
593
+ str = arg.toExponential(precision === -1 ? 6 : precision);
594
+ break;
595
+ case 'g':
596
+ case 'G':
597
+ str = arg.toExponential();
598
+ exponent = parseInt(str.split('e')[1]);
599
+ if (!(exponent < -4 || exponent >= (precision === -1 ? 6 : precision))) {
600
+ str = arg.toPrecision(precision === -1 ? (flags&FSHARP ? 6 : undefined) : precision);
601
+ }
602
+ break;
603
+ }
604
+ }
605
+ if (flags&FMINUS) {
606
+ if (flags&FPLUS || flags&FSPACE) { str = (flags&FPLUS ? '+' : ' ') + str; }
607
+ while (str.length < width) { str = str + ' '; }
608
+ } else {
609
+ if (flags&FZERO && arg !== Infinity && !isNaN(arg)) {
610
+ while (str.length < width - ((flags&FPLUS || flags&FSPACE) ? 1 : 0)) { str = '0' + str; }
611
+ if (flags&FPLUS || flags&FSPACE) { str = (flags&FPLUS ? '+' : ' ') + str; }
612
+ } else {
613
+ if (flags&FPLUS || flags&FSPACE) { str = (flags&FPLUS ? '+' : ' ') + str; }
614
+ while (str.length < width) { str = ' ' + str; }
615
+ }
616
+ }
617
+ } else {
618
+ if (arg === -Infinity) {
619
+ str = 'Inf';
620
+ } else {
621
+ switch (format_string.charAt(i)) {
622
+ case 'f':
623
+ str = (-arg).toFixed(precision === -1 ? 6 : precision);
624
+ break;
625
+ case 'e':
626
+ case 'E':
627
+ str = (-arg).toExponential(precision === -1 ? 6 : precision);
628
+ break;
629
+ case 'g':
630
+ case 'G':
631
+ str = (-arg).toExponential();
632
+ exponent = parseInt(str.split('e')[1]);
633
+ if (!(exponent < -4 || exponent >= (precision === -1 ? 6 : precision))) {
634
+ str = (-arg).toPrecision(precision === -1 ? (flags&FSHARP ? 6 : undefined) : precision);
635
+ }
636
+ break;
637
+ }
638
+ }
639
+ if (flags&FMINUS) {
640
+ str = '-' + str;
641
+ while (str.length < width) { str = str + ' '; }
642
+ } else {
643
+ if (flags&FZERO && arg !== -Infinity) {
644
+ while (str.length < width - 1) { str = '0' + str; }
645
+ str = '-' + str;
646
+ } else {
647
+ str = '-' + str;
648
+ while (str.length < width) { str = ' ' + str; }
649
+ }
650
+ }
651
+ }
652
+ if (format_string.charAt(i) === format_string.charAt(i).toUpperCase() && arg !== Infinity && arg !== -Infinity && !isNaN(arg)) {
653
+ str = str.toUpperCase();
654
+ }
655
+ str = str.replace(/([eE][-+]?)([0-9])$/, '$10$2');
656
+ break format_sequence;
657
+
658
+ case 'a':
659
+ case 'A':
660
+ // Not implemented because there are no specs for this field type.
661
+ #{raise NotImplementedError, '`A` and `a` format field types are not implemented in Opal yet'}
662
+
663
+ case 'c':
664
+ arg = GET_ARG();
665
+ if (#{`arg`.respond_to?(:to_ary)}) { arg = #{`arg`.to_ary}[0]; }
666
+ if (#{`arg`.respond_to?(:to_str)}) {
667
+ str = #{`arg`.to_str};
668
+ } else {
669
+ str = String.fromCharCode(#{Opal.coerce_to(`arg`, Integer, :to_int)});
670
+ }
671
+ if (str.length !== 1) {
672
+ #{raise ArgumentError, '%c requires a character'}
673
+ }
674
+ if (flags&FMINUS) {
675
+ while (str.length < width) { str = str + ' '; }
676
+ } else {
677
+ while (str.length < width) { str = ' ' + str; }
678
+ }
679
+ break format_sequence;
680
+
681
+ case 'p':
682
+ str = #{`GET_ARG()`.inspect};
683
+ if (precision !== -1) { str = str.slice(0, precision); }
684
+ if (flags&FMINUS) {
685
+ while (str.length < width) { str = str + ' '; }
289
686
  } else {
290
- prefix = #{' ' * `width - total_len`} + prefix;
687
+ while (str.length < width) { str = ' ' + str; }
291
688
  }
689
+ break format_sequence;
690
+
691
+ case 's':
692
+ str = #{`GET_ARG()`.to_s};
693
+ if (precision !== -1) { str = str.slice(0, precision); }
694
+ if (flags&FMINUS) {
695
+ while (str.length < width) { str = str + ' '; }
696
+ } else {
697
+ while (str.length < width) { str = ' ' + str; }
698
+ }
699
+ break format_sequence;
700
+
701
+ default:
702
+ #{raise ArgumentError, "malformed format string - %#{`format_string.charAt(i)`}"}
292
703
  }
293
704
  }
294
- var result = prefix + str;
295
- if ('XEG'.indexOf(spec) != -1) {
296
- result = result.toUpperCase();
705
+
706
+ if (str === undefined) {
707
+ #{raise ArgumentError, 'malformed format string - %'}
297
708
  }
298
- return result;
299
- });
709
+
710
+ result += format_string.slice(begin_slice, end_slice) + str;
711
+ begin_slice = i + 1;
712
+ }
713
+
714
+ if (#{$DEBUG} && pos_arg_num >= 0 && seq_arg_num < args.length) {
715
+ #{raise ArgumentError, 'too many arguments for format string'}
716
+ }
717
+
718
+ return result + format_string.slice(begin_slice);
300
719
  }
301
720
  end
302
721
 
@@ -310,7 +729,7 @@ module Kernel
310
729
  end
311
730
 
312
731
  def hash
313
- `[self.$$class.$$name,#{`self.$$class`.__id__},#{__id__}].join(':')`
732
+ "#{self.class}:#{self.class.__id__}:#{__id__}"
314
733
  end
315
734
 
316
735
  def initialize_copy(other)
@@ -356,52 +775,123 @@ module Kernel
356
775
  }
357
776
  end
358
777
 
359
- def Integer(value, base = nil)
360
- if String === value
361
- if value.empty?
362
- raise ArgumentError, "invalid value for Integer: (empty string)"
363
- end
778
+ def Integer(value, base = undefined)
779
+ %x{
780
+ var i, str, base_digits;
364
781
 
365
- return `parseInt(#{value}, #{base || `undefined`})`
366
- end
782
+ if (!value.$$is_string) {
783
+ if (base !== undefined) {
784
+ #{raise ArgumentError, "base specified for non string value"}
785
+ }
786
+ if (value === nil) {
787
+ #{raise TypeError, "can't convert nil into Integer"}
788
+ }
789
+ if (value.$$is_number) {
790
+ if (value === Infinity || value === -Infinity || isNaN(value)) {
791
+ #{raise FloatDomainError, value}
792
+ }
793
+ return Math.floor(value);
794
+ }
795
+ if (#{value.respond_to?(:to_int)}) {
796
+ i = #{value.to_int};
797
+ if (i !== nil) {
798
+ return i;
799
+ }
800
+ }
801
+ return #{Opal.coerce_to!(value, Integer, :to_i)};
802
+ }
367
803
 
368
- if base
369
- raise ArgumentError "base is only valid for String values"
370
- end
804
+ if (base === undefined) {
805
+ base = 0;
806
+ } else {
807
+ base = #{Opal.coerce_to(`base`, Integer, :to_int)};
808
+ if (base === 1 || base < 0 || base > 36) {
809
+ #{raise ArgumentError, "invalid radix #{base}"}
810
+ }
811
+ }
812
+
813
+ str = value.toLowerCase();
814
+
815
+ str = str.replace(/(\d)_(?=\d)/g, '$1');
816
+
817
+ str = str.replace(/^(\s*[+-]?)(0[bodx]?)/, function (_, head, flag) {
818
+ switch (flag) {
819
+ case '0b':
820
+ if (base === 0 || base === 2) {
821
+ base = 2;
822
+ return head;
823
+ }
824
+ case '0':
825
+ case '0o':
826
+ if (base === 0 || base === 8) {
827
+ base = 8;
828
+ return head;
829
+ }
830
+ case '0d':
831
+ if (base === 0 || base === 10) {
832
+ base = 10;
833
+ return head;
834
+ }
835
+ case '0x':
836
+ if (base === 0 || base === 16) {
837
+ base = 16;
838
+ return head;
839
+ }
840
+ }
841
+ #{raise ArgumentError, "invalid value for Integer(): \"#{value}\""}
842
+ });
371
843
 
372
- case value
373
- when Integer
374
- value
844
+ base = (base === 0 ? 10 : base);
375
845
 
376
- when Float
377
- if value.nan? or value.infinite?
378
- raise FloatDomainError, "unable to coerce #{value} to Integer"
379
- end
846
+ base_digits = '0-' + (base <= 10 ? base - 1 : '9a-' + String.fromCharCode(97 + (base - 11)));
380
847
 
381
- value.to_int
848
+ if (!(new RegExp('^\\s*[+-]?[' + base_digits + ']+\\s*$')).test(str)) {
849
+ #{raise ArgumentError, "invalid value for Integer(): \"#{value}\""}
850
+ }
382
851
 
383
- when NilClass
384
- raise TypeError, "can't convert nil into Integer"
852
+ i = parseInt(str, base);
385
853
 
386
- else
387
- if value.respond_to? :to_int
388
- value.to_int
389
- elsif value.respond_to? :to_i
390
- value.to_i
391
- else
392
- raise TypeError, "can't convert #{value.class} into Integer"
393
- end
394
- end
854
+ if (isNaN(i)) {
855
+ #{raise ArgumentError, "invalid value for Integer(): \"#{value}\""}
856
+ }
857
+
858
+ return i;
859
+ }
395
860
  end
396
861
 
397
862
  def Float(value)
398
- if String === value
399
- `parseFloat(value)`
400
- elsif value.respond_to? :to_f
401
- value.to_f
402
- else
403
- raise TypeError, "can't convert #{value.class} into Float"
404
- end
863
+ %x{
864
+ var str;
865
+
866
+ if (value === nil) {
867
+ #{raise TypeError, "can't convert nil into Float"}
868
+ }
869
+
870
+ if (value.$$is_string) {
871
+ str = value.toString();
872
+
873
+ str = str.replace(/(\d)_(?=\d)/g, '$1');
874
+
875
+ //Special case for hex strings only:
876
+ if (/^\s*[-+]?0[xX][0-9a-fA-F]+\s*$/.test(str)) {
877
+ return #{Integer(`str`)};
878
+ }
879
+
880
+ if (!/^\s*[-+]?[0-9]*\.?[0-9]+([eE][-+]?[0-9]+)?\s*$/.test(str)) {
881
+ #{raise ArgumentError, "invalid value for Float(): \"#{value}\""}
882
+ }
883
+
884
+ return parseFloat(str);
885
+ }
886
+
887
+ return #{Opal.coerce_to!(value, Float, :to_f)};
888
+ }
889
+ end
890
+
891
+ def Hash(arg)
892
+ return {} if arg.nil? || arg == []
893
+ return arg if Hash === arg
894
+ return Opal.coerce_to!(arg, Hash, :to_hash)
405
895
  end
406
896
 
407
897
  def is_a?(klass)
@@ -482,16 +972,21 @@ module Kernel
482
972
  def raise(exception = undefined, string = undefined)
483
973
  %x{
484
974
  if (exception == null && #$!) {
485
- exception = #$!;
975
+ throw #$!;
976
+ }
977
+
978
+ if (exception == null) {
979
+ exception = #{RuntimeError.new};
486
980
  }
487
981
  else if (exception.$$is_string) {
488
982
  exception = #{RuntimeError.new exception};
489
983
  }
490
- else if (!#{exception.is_a? Exception}) {
984
+ else if (exception.$$is_class) {
491
985
  exception = #{exception.new string};
492
986
  }
493
987
 
494
- #{$! = exception};
988
+ #$! = exception;
989
+
495
990
  throw exception;
496
991
  }
497
992
  end
@@ -564,7 +1059,7 @@ module Kernel
564
1059
  alias public_send __send__
565
1060
 
566
1061
  def singleton_class
567
- %x{Opal.get_singleton_class(self)}
1062
+ `Opal.get_singleton_class(self)`
568
1063
  end
569
1064
 
570
1065
  alias sprintf format
@@ -572,7 +1067,8 @@ module Kernel
572
1067
  alias srand rand
573
1068
 
574
1069
  def String(str)
575
- `String(str)`
1070
+ Opal.coerce_to?(str, String, :to_str) ||
1071
+ Opal.coerce_to!(str, String, :to_s)
576
1072
  end
577
1073
 
578
1074
  def taint