opal 0.3.43 → 0.3.44

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/CHANGELOG.md +19 -0
  2. data/README.md +1 -0
  3. data/Rakefile +29 -19
  4. data/examples/native/Gemfile +3 -0
  5. data/examples/native/README.md +17 -0
  6. data/examples/native/app/app.rb +38 -0
  7. data/examples/native/config.ru +8 -0
  8. data/examples/native/index.html.erb +12 -0
  9. data/lib/opal/lexer.rb +5 -1
  10. data/lib/opal/parser.rb +36 -10
  11. data/lib/opal/processor.rb +10 -9
  12. data/lib/opal/server.rb +17 -7
  13. data/lib/opal/target_scope.rb +2 -2
  14. data/lib/opal/version.rb +1 -1
  15. data/opal/opal-browser/script_loader.rb +8 -13
  16. data/opal/opal.rb +23 -5
  17. data/opal/opal/array.rb +128 -14
  18. data/opal/opal/boolean.rb +1 -1
  19. data/opal/opal/class.rb +92 -18
  20. data/opal/opal/enumerable.rb +90 -0
  21. data/opal/opal/error.rb +1 -1
  22. data/opal/opal/hash.rb +2 -2
  23. data/opal/opal/kernel.rb +3 -3
  24. data/opal/opal/numeric.rb +1 -1
  25. data/opal/opal/proc.rb +1 -1
  26. data/opal/opal/regexp.rb +31 -31
  27. data/opal/opal/runtime.js +181 -69
  28. data/opal/opal/string.rb +561 -40
  29. data/opal/opal/time.rb +1 -1
  30. data/opal/rbconfig.rb +17 -3
  31. data/opal/strscan.rb +41 -5
  32. data/spec/opal/class/new_spec.rb +27 -0
  33. data/spec/opal/native_spec.rb +127 -0
  34. data/spec/ospec/runner.rb +0 -2
  35. data/spec/parser/strscan/get_byte_spec.rb +29 -0
  36. data/spec/parser/strscan/skip_spec.rb +40 -0
  37. data/spec/rubyspec/core/array/each_spec.rb +1 -1
  38. data/spec/rubyspec/core/array/intersection_spec.rb +5 -0
  39. data/spec/rubyspec/core/array/max_spec.rb +32 -0
  40. data/spec/rubyspec/core/array/min_spec.rb +32 -0
  41. data/spec/rubyspec/core/array/minus_spec.rb +7 -5
  42. data/spec/rubyspec/core/array/multiply_spec.rb +1 -1
  43. data/spec/rubyspec/core/array/plus_spec.rb +1 -1
  44. data/spec/rubyspec/core/array/pop_spec.rb +1 -1
  45. data/spec/rubyspec/core/array/push_spec.rb +1 -1
  46. data/spec/rubyspec/core/array/rassoc_spec.rb +1 -1
  47. data/spec/rubyspec/core/array/reject_spec.rb +2 -2
  48. data/spec/rubyspec/core/array/reverse_each_spec.rb +2 -2
  49. data/spec/rubyspec/core/array/rindex_spec.rb +2 -2
  50. data/spec/rubyspec/core/array/select_spec.rb +1 -1
  51. data/spec/rubyspec/core/array/shift_spec.rb +1 -1
  52. data/spec/rubyspec/core/array/slice_spec.rb +1 -1
  53. data/spec/rubyspec/core/array/sort_spec.rb +15 -15
  54. data/spec/rubyspec/core/array/to_a_spec.rb +1 -1
  55. data/spec/rubyspec/core/array/to_ary_spec.rb +1 -1
  56. data/spec/rubyspec/core/array/uniq_spec.rb +1 -1
  57. data/spec/rubyspec/core/array/unshift_spec.rb +1 -1
  58. data/spec/rubyspec/core/array/zip_spec.rb +1 -1
  59. data/spec/rubyspec/core/enumerable/select_spec.rb +4 -1
  60. data/spec/rubyspec/core/module/const_defined_spec.rb +86 -0
  61. data/spec/rubyspec/core/module/const_get_spec.rb +55 -3
  62. data/spec/rubyspec/core/module/const_set_spec.rb +2 -2
  63. data/spec/rubyspec/core/module/constants_spec.rb +49 -0
  64. data/spec/rubyspec/core/regexp/match_spec.rb +66 -1
  65. data/spec/rubyspec/core/string/center_spec.rb +71 -0
  66. data/spec/rubyspec/core/string/chomp_spec.rb +6 -1
  67. data/spec/rubyspec/core/string/clone_spec.rb +8 -0
  68. data/spec/rubyspec/core/string/dup_spec.rb +8 -0
  69. data/spec/rubyspec/core/string/end_with_spec.rb +5 -1
  70. data/spec/rubyspec/core/string/gsub_spec.rb +15 -1
  71. data/spec/rubyspec/core/string/lines_spec.rb +9 -0
  72. data/spec/rubyspec/core/string/ljust_spec.rb +17 -0
  73. data/spec/rubyspec/core/string/match_spec.rb +25 -3
  74. data/spec/rubyspec/core/string/rindex_spec.rb +50 -0
  75. data/spec/rubyspec/core/string/rjust_spec.rb +17 -0
  76. data/spec/rubyspec/core/string/scan_spec.rb +66 -0
  77. data/spec/rubyspec/core/string/sub_spec.rb +17 -1
  78. data/spec/rubyspec/core/string/tr_s_spec.rb +31 -0
  79. data/spec/rubyspec/core/string/tr_spec.rb +31 -0
  80. data/spec/rubyspec/fixtures/constants.rb +6 -0
  81. data/spec/rubyspec/language/class_spec.rb +4 -8
  82. data/spec/rubyspec/language/numbers_spec.rb +10 -4
  83. data/spec/rubyspec/language/predefined_spec.rb +69 -2
  84. data/spec/rubyspec/library/rbconfig/config_spec.rb +47 -0
  85. data/spec/rubyspec/spec_helper.rb +6 -0
  86. metadata +46 -25
  87. data/opal/opal-eventable.rb +0 -26
  88. data/opal/opal/native.rb +0 -115
  89. data/spec/opal/eventable_spec.rb +0 -75
  90. data/spec/opal/native/element_reference_spec.rb +0 -40
  91. data/spec/opal/native/equal_spec.rb +0 -17
  92. data/spec/opal/native/fixtures/classes.rb +0 -27
  93. data/spec/opal/native/global_spec.rb +0 -12
  94. data/spec/opal/native/initialize_spec.rb +0 -8
  95. data/spec/opal/native/method_missing_spec.rb +0 -53
  96. data/spec/opal/native/to_native_spec.rb +0 -8
  97. data/spec/rubyspec/core/string/demodulize_spec.rb +0 -10
  98. data/spec/rubyspec/core/string/underscore_spec.rb +0 -17
data/opal/opal/string.rb CHANGED
@@ -1,4 +1,4 @@
1
- class String < `String`
1
+ class String
2
2
  include Comparable
3
3
 
4
4
  `def._isString = true`
@@ -165,6 +165,19 @@ class String < `String`
165
165
  }
166
166
  end
167
167
 
168
+ def center(width, padstr = ' ')
169
+ %x{
170
+ if (width <= #{self}.length) {
171
+ return #{self};
172
+ }
173
+ else {
174
+ var ljustified = #{self.ljust( ((width + self.size)/2).floor, padstr)};
175
+ var rjustified = #{self.rjust( ((width + self.size)/2).ceil, padstr)};
176
+ return ljustified + rjustified.slice(#{self}.length);
177
+ }
178
+ }
179
+ end
180
+
168
181
  def chars
169
182
  %x{
170
183
  for (var i = 0, length = #{self}.length; i < length; i++) {
@@ -175,13 +188,30 @@ class String < `String`
175
188
 
176
189
  def chomp(separator = $/)
177
190
  %x{
178
- if (separator === "\\n") {
179
- return #{self}.replace(/(\\n|\\r|\\r\\n)$/, '');
180
- }
181
- else if (separator === "") {
182
- return #{self}.replace(/(\\n|\\r\\n)+$/, '');
191
+ var strlen = #{self}.length;
192
+ var seplen = separator.length;
193
+ if (strlen > 0) {
194
+ if (separator === "\\n") {
195
+ var last = #{self}.charAt(strlen - 1);
196
+ if (last === "\\n" || last == "\\r") {
197
+ var result = #{self}.substr(0, strlen - 1);
198
+ if (strlen > 1 && #{self}.charAt(strlen - 2) === "\\r") {
199
+ result = #{self}.substr(0, strlen - 2);
200
+ }
201
+ return result;
202
+ }
203
+ }
204
+ else if (separator === "") {
205
+ return #{self}.replace(/(?:\\n|\\r\\n)+$/, '');
206
+ }
207
+ else if (strlen >= seplen) {
208
+ var tail = #{self}.substr(-1 * seplen);
209
+ if (tail === separator) {
210
+ return #{self}.substr(0, strlen - seplen);
211
+ }
212
+ }
183
213
  }
184
- return #{self}.replace(new RegExp(separator + '$'), '');
214
+ return #{self}
185
215
  }
186
216
  end
187
217
 
@@ -193,39 +223,49 @@ class String < `String`
193
223
  `#{self}.charAt(0)`
194
224
  end
195
225
 
196
- def count(str)
197
- `(#{self}.length - #{self}.replace(new RegExp(str,"g"), '').length) / str.length`
226
+ def clone
227
+ `#{self}.slice()`
198
228
  end
199
229
 
200
- def dasherize
201
- `#{self}.replace(/[-\\s]+/g, '-')
202
- .replace(/([A-Z\\d]+)([A-Z][a-z])/g, '$1-$2')
203
- .replace(/([a-z\\d])([A-Z])/g, '$1-$2')
204
- .toLowerCase()`
230
+ def count(str)
231
+ `(#{self}.length - #{self}.replace(new RegExp(str,"g"), '').length) / str.length`
205
232
  end
206
233
 
207
- def demodulize
208
- %x{
209
- var idx = #{self}.lastIndexOf('::');
210
-
211
- if (idx > -1) {
212
- return #{self}.substr(idx + 2);
213
- }
214
-
215
- return #{self};
216
- }
217
- end
234
+ alias dup clone
218
235
 
219
236
  alias_native :downcase, :toLowerCase
220
237
 
221
238
  alias each_char chars
222
239
 
223
240
  def each_line (separator = $/)
241
+ return self.split(separator).each unless block_given?
242
+
224
243
  %x{
225
- var splitted = #{self}.split(separator);
244
+ var chomped = #{self.chomp};
245
+ var trailing_separator = #{self}.length != chomped.length
246
+ var splitted = chomped.split(separator);
247
+
248
+ if (!#{block_given?}) {
249
+ result = []
250
+ for (var i = 0, length = splitted.length; i < length; i++) {
251
+ if (i < length - 1 || trailing_separator) {
252
+ result.push(splitted[i] + separator);
253
+ }
254
+ else {
255
+ result.push(splitted[i]);
256
+ }
257
+ }
258
+
259
+ return #{`result`.each};
260
+ }
226
261
 
227
262
  for (var i = 0, length = splitted.length; i < length; i++) {
228
- #{yield `splitted[i] + separator`}
263
+ if (i < length - 1 || trailing_separator) {
264
+ #{yield `splitted[i] + separator`}
265
+ }
266
+ else {
267
+ #{yield `splitted[i]`}
268
+ }
229
269
  }
230
270
  }
231
271
  end
@@ -239,7 +279,7 @@ class String < `String`
239
279
  for (var i = 0, length = suffixes.length; i < length; i++) {
240
280
  var suffix = suffixes[i];
241
281
 
242
- if (#{self}.lastIndexOf(suffix) === #{self}.length - suffix.length) {
282
+ if (#{self}.length >= suffix.length && #{self}.substr(0 - suffix.length) === suffix) {
243
283
  return true;
244
284
  }
245
285
  }
@@ -352,8 +392,19 @@ class String < `String`
352
392
  `#{self}.length`
353
393
  end
354
394
 
355
- def ljust(integer, padstr = ' ')
356
- raise NotImplementedError
395
+ def ljust(width, padstr = ' ')
396
+ %x{
397
+ if (width <= #{self}.length) {
398
+ return #{self};
399
+ }
400
+ else {
401
+ var n_chars = Math.floor(width - #{self}.length)
402
+ var n_patterns = Math.floor(n_chars/padstr.length);
403
+ var result = Array(n_patterns + 1).join(padstr);
404
+ var remaining = n_chars - result.length;
405
+ return result + padstr.slice(0, remaining) + #{self};
406
+ }
407
+ }
357
408
  end
358
409
 
359
410
  def lstrip
@@ -394,10 +445,95 @@ class String < `String`
394
445
  `#{self}.split('').reverse().join('')`
395
446
  end
396
447
 
448
+ # TODO handle case where search is regexp
449
+ def rindex(search, offset = undefined)
450
+ %x{
451
+ var search_type = (search == null ? Opal.NilClass : search.$class());
452
+ if (search_type != String && search_type != RegExp) {
453
+ var msg = "type mismatch: " + search_type + " given";
454
+ #{raise TypeError.new(`msg`)};
455
+ }
456
+
457
+ if (#{self}.length == 0) {
458
+ return search.length == 0 ? 0 : nil;
459
+ }
460
+
461
+ var result = -1;
462
+ if (offset != null) {
463
+ if (offset < 0) {
464
+ offset = #{self}.length + offset;
465
+ }
466
+
467
+ if (search_type == String) {
468
+ result = #{self}.lastIndexOf(search, offset);
469
+ }
470
+ else {
471
+ result = #{self}.substr(0, offset + 1).$reverse().search(search);
472
+ if (result !== -1) {
473
+ result = offset - result;
474
+ }
475
+ }
476
+ }
477
+ else {
478
+ if (search_type == String) {
479
+ result = #{self}.lastIndexOf(search);
480
+ }
481
+ else {
482
+ result = #{self}.$reverse().search(search);
483
+ if (result !== -1) {
484
+ result = #{self}.length - 1 - result;
485
+ }
486
+ }
487
+ }
488
+
489
+ return result === -1 ? nil : result;
490
+ }
491
+ end
492
+
493
+ def rjust(width, padstr = ' ')
494
+ %x{
495
+ if (width <= #{self}.length) {
496
+ return #{self};
497
+ }
498
+ else {
499
+ var ljustified = #{ self.ljust(width, padstr) };
500
+ return #{self} + ljustified.slice(0, -#{self}.length);
501
+ }
502
+ }
503
+ end
504
+
397
505
  def rstrip
398
506
  `#{self}.replace(/\\s*$/, '')`
399
507
  end
400
508
 
509
+ def scan(pattern, &block)
510
+ %x{
511
+ if (pattern.global) {
512
+ // should we clear it afterwards too?
513
+ pattern.lastIndex = 0;
514
+ }
515
+ else {
516
+ // rewrite regular expression to add the global flag to capture pre/post match
517
+ pattern = new RegExp(pattern.source, 'g' + (pattern.multiline ? 'm' : '') + (pattern.ignoreCase ? 'i' : ''));
518
+ }
519
+
520
+ var result = [];
521
+ var match;
522
+
523
+ while ((match = pattern.exec(#{self})) != null) {
524
+ var match_data = #{MatchData.new `pattern`, `match`};
525
+ if (block === nil) {
526
+ match.length == 1 ? result.push(match[0]) : result.push(match.slice(1));
527
+ }
528
+ else {
529
+ match.length == 1 ? block(match[0]) : block.apply(#{self}, match.slice(1));
530
+ }
531
+ }
532
+
533
+ return (block !== nil ? #{self} : result);
534
+ }
535
+ end
536
+
401
537
  alias size length
402
538
 
403
539
  alias slice []
@@ -425,12 +561,35 @@ class String < `String`
425
561
  def sub(pattern, replace = undefined, &block)
426
562
  %x{
427
563
  if (typeof(replace) === 'string') {
564
+ // convert Ruby back reference to JavaScript back reference
565
+ replace = replace.replace(/\\\\([1-9])/g, '$$$1')
428
566
  return #{self}.replace(pattern, replace);
429
567
  }
430
568
  if (block !== nil) {
431
- return #{self}.replace(pattern, function(str, a) {
432
- #{ $1 = `a` };
433
- return block(str);
569
+ return #{self}.replace(pattern, function() {
570
+ // FIXME: this should be a formal MatchData object with all the goodies
571
+ var match_data = []
572
+ for (var i = 0, len = arguments.length; i < len; i++) {
573
+ var arg = arguments[i];
574
+ if (arg == undefined) {
575
+ match_data.push(nil);
576
+ }
577
+ else {
578
+ match_data.push(arg);
579
+ }
580
+ }
581
+
582
+ var str = match_data.pop();
583
+ var offset = match_data.pop();
584
+ var match_len = match_data.length;
585
+
586
+ // $1, $2, $3 not being parsed correctly in Ruby code
587
+ //for (var i = 1; i < match_len; i++) {
588
+ // __gvars[String(i)] = match_data[i];
589
+ //}
590
+ #{$& = `match_data[0]`};
591
+ #{$~ = `match_data`};
592
+ return block(match_data[0]);
434
593
  });
435
594
  }
436
595
  else if (replace !== undefined) {
@@ -452,7 +611,9 @@ class String < `String`
452
611
  }
453
612
  }
454
613
  else {
455
- return #{self}.replace(pattern, replace.toString());
614
+ // convert Ruby back reference to JavaScript back reference
615
+ replace = replace.toString().replace(/\\\\([1-9])/g, '$$$1')
616
+ return #{self}.replace(pattern, replace);
456
617
  }
457
618
  }
458
619
  end
@@ -533,15 +694,375 @@ class String < `String`
533
694
  alias to_str to_s
534
695
 
535
696
  alias to_sym intern
536
-
537
- def underscore
538
- `#{self}.replace(/[-\\s]+/g, '_')
539
- .replace(/([A-Z\\d]+)([A-Z][a-z])/g, '$1_$2')
540
- .replace(/([a-z\\d])([A-Z])/g, '$1_$2')
541
- .toLowerCase()`
697
+
698
+ def tr(from, to)
699
+ %x{
700
+ if (from.length == 0 || from === to) {
701
+ return #{self};
702
+ }
703
+
704
+ var subs = {};
705
+ var from_chars = from.split('');
706
+ var from_length = from_chars.length;
707
+ var to_chars = to.split('');
708
+ var to_length = to_chars.length;
709
+
710
+ var inverse = false;
711
+ var global_sub = null;
712
+ if (from_chars[0] === '^') {
713
+ inverse = true;
714
+ from_chars.shift();
715
+ global_sub = to_chars[to_length - 1]
716
+ from_length -= 1;
717
+ }
718
+
719
+ var from_chars_expanded = [];
720
+ var last_from = null;
721
+ var in_range = false;
722
+ for (var i = 0; i < from_length; i++) {
723
+ var char = from_chars[i];
724
+ if (last_from == null) {
725
+ last_from = char;
726
+ from_chars_expanded.push(char);
727
+ }
728
+ else if (char === '-') {
729
+ if (last_from === '-') {
730
+ from_chars_expanded.push('-');
731
+ from_chars_expanded.push('-');
732
+ }
733
+ else if (i == from_length - 1) {
734
+ from_chars_expanded.push('-');
735
+ }
736
+ else {
737
+ in_range = true;
738
+ }
739
+ }
740
+ else if (in_range) {
741
+ var start = last_from.charCodeAt(0) + 1;
742
+ var end = char.charCodeAt(0);
743
+ for (var c = start; c < end; c++) {
744
+ from_chars_expanded.push(String.fromCharCode(c));
745
+ }
746
+ from_chars_expanded.push(char);
747
+ in_range = null;
748
+ last_from = null;
749
+ }
750
+ else {
751
+ from_chars_expanded.push(char);
752
+ }
753
+ }
754
+
755
+ from_chars = from_chars_expanded;
756
+ from_length = from_chars.length;
757
+
758
+ if (inverse) {
759
+ for (var i = 0; i < from_length; i++) {
760
+ subs[from_chars[i]] = true;
761
+ }
762
+ }
763
+ else {
764
+ if (to_length > 0) {
765
+ var to_chars_expanded = [];
766
+ var last_to = null;
767
+ var in_range = false;
768
+ for (var i = 0; i < to_length; i++) {
769
+ var char = to_chars[i];
770
+ if (last_from == null) {
771
+ last_from = char;
772
+ to_chars_expanded.push(char);
773
+ }
774
+ else if (char === '-') {
775
+ if (last_to === '-') {
776
+ to_chars_expanded.push('-');
777
+ to_chars_expanded.push('-');
778
+ }
779
+ else if (i == to_length - 1) {
780
+ to_chars_expanded.push('-');
781
+ }
782
+ else {
783
+ in_range = true;
784
+ }
785
+ }
786
+ else if (in_range) {
787
+ var start = last_from.charCodeAt(0) + 1;
788
+ var end = char.charCodeAt(0);
789
+ for (var c = start; c < end; c++) {
790
+ to_chars_expanded.push(String.fromCharCode(c));
791
+ }
792
+ to_chars_expanded.push(char);
793
+ in_range = null;
794
+ last_from = null;
795
+ }
796
+ else {
797
+ to_chars_expanded.push(char);
798
+ }
799
+ }
800
+
801
+ to_chars = to_chars_expanded;
802
+ to_length = to_chars.length;
803
+ }
804
+
805
+ var length_diff = from_length - to_length;
806
+ if (length_diff > 0) {
807
+ var pad_char = (to_length > 0 ? to_chars[to_length - 1] : '');
808
+ for (var i = 0; i < length_diff; i++) {
809
+ to_chars.push(pad_char);
810
+ }
811
+ }
812
+
813
+ for (var i = 0; i < from_length; i++) {
814
+ subs[from_chars[i]] = to_chars[i];
815
+ }
816
+ }
817
+
818
+ var new_str = ''
819
+ for (var i = 0, length = #{self}.length; i < length; i++) {
820
+ var char = #{self}.charAt(i);
821
+ var sub = subs[char];
822
+ if (inverse) {
823
+ new_str += (sub == null ? global_sub : char);
824
+ }
825
+ else {
826
+ new_str += (sub != null ? sub : char);
827
+ }
828
+ }
829
+ return new_str;
830
+ }
831
+ end
832
+
833
+ def tr_s(from, to)
834
+ %x{
835
+ if (from.length == 0) {
836
+ return #{self};
837
+ }
838
+
839
+ var subs = {};
840
+ var from_chars = from.split('');
841
+ var from_length = from_chars.length;
842
+ var to_chars = to.split('');
843
+ var to_length = to_chars.length;
844
+
845
+ var inverse = false;
846
+ var global_sub = null;
847
+ if (from_chars[0] === '^') {
848
+ inverse = true;
849
+ from_chars.shift();
850
+ global_sub = to_chars[to_length - 1]
851
+ from_length -= 1;
852
+ }
853
+
854
+ var from_chars_expanded = [];
855
+ var last_from = null;
856
+ var in_range = false;
857
+ for (var i = 0; i < from_length; i++) {
858
+ var char = from_chars[i];
859
+ if (last_from == null) {
860
+ last_from = char;
861
+ from_chars_expanded.push(char);
862
+ }
863
+ else if (char === '-') {
864
+ if (last_from === '-') {
865
+ from_chars_expanded.push('-');
866
+ from_chars_expanded.push('-');
867
+ }
868
+ else if (i == from_length - 1) {
869
+ from_chars_expanded.push('-');
870
+ }
871
+ else {
872
+ in_range = true;
873
+ }
874
+ }
875
+ else if (in_range) {
876
+ var start = last_from.charCodeAt(0) + 1;
877
+ var end = char.charCodeAt(0);
878
+ for (var c = start; c < end; c++) {
879
+ from_chars_expanded.push(String.fromCharCode(c));
880
+ }
881
+ from_chars_expanded.push(char);
882
+ in_range = null;
883
+ last_from = null;
884
+ }
885
+ else {
886
+ from_chars_expanded.push(char);
887
+ }
888
+ }
889
+
890
+ from_chars = from_chars_expanded;
891
+ from_length = from_chars.length;
892
+
893
+ if (inverse) {
894
+ for (var i = 0; i < from_length; i++) {
895
+ subs[from_chars[i]] = true;
896
+ }
897
+ }
898
+ else {
899
+ if (to_length > 0) {
900
+ var to_chars_expanded = [];
901
+ var last_to = null;
902
+ var in_range = false;
903
+ for (var i = 0; i < to_length; i++) {
904
+ var char = to_chars[i];
905
+ if (last_from == null) {
906
+ last_from = char;
907
+ to_chars_expanded.push(char);
908
+ }
909
+ else if (char === '-') {
910
+ if (last_to === '-') {
911
+ to_chars_expanded.push('-');
912
+ to_chars_expanded.push('-');
913
+ }
914
+ else if (i == to_length - 1) {
915
+ to_chars_expanded.push('-');
916
+ }
917
+ else {
918
+ in_range = true;
919
+ }
920
+ }
921
+ else if (in_range) {
922
+ var start = last_from.charCodeAt(0) + 1;
923
+ var end = char.charCodeAt(0);
924
+ for (var c = start; c < end; c++) {
925
+ to_chars_expanded.push(String.fromCharCode(c));
926
+ }
927
+ to_chars_expanded.push(char);
928
+ in_range = null;
929
+ last_from = null;
930
+ }
931
+ else {
932
+ to_chars_expanded.push(char);
933
+ }
934
+ }
935
+
936
+ to_chars = to_chars_expanded;
937
+ to_length = to_chars.length;
938
+ }
939
+
940
+ var length_diff = from_length - to_length;
941
+ if (length_diff > 0) {
942
+ var pad_char = (to_length > 0 ? to_chars[to_length - 1] : '');
943
+ for (var i = 0; i < length_diff; i++) {
944
+ to_chars.push(pad_char);
945
+ }
946
+ }
947
+
948
+ for (var i = 0; i < from_length; i++) {
949
+ subs[from_chars[i]] = to_chars[i];
950
+ }
951
+ }
952
+ var new_str = ''
953
+ var last_substitute = null
954
+ for (var i = 0, length = #{self}.length; i < length; i++) {
955
+ var char = #{self}.charAt(i);
956
+ var sub = subs[char]
957
+ if (inverse) {
958
+ if (sub == null) {
959
+ if (last_substitute == null) {
960
+ new_str += global_sub;
961
+ last_substitute = true;
962
+ }
963
+ }
964
+ else {
965
+ new_str += char;
966
+ last_substitute = null;
967
+ }
968
+ }
969
+ else {
970
+ if (sub != null) {
971
+ if (last_substitute == null || last_substitute !== sub) {
972
+ new_str += sub;
973
+ last_substitute = sub;
974
+ }
975
+ }
976
+ else {
977
+ new_str += char;
978
+ last_substitute = null;
979
+ }
980
+ }
981
+ }
982
+ return new_str;
983
+ }
542
984
  end
543
985
 
544
986
  alias_native :upcase, :toUpperCase
545
987
  end
546
988
 
547
989
  Symbol = String
990
+
991
+ class MatchData < Array
992
+ attr_reader :post_match, :pre_match, :regexp, :string
993
+
994
+ def self.new(regexp, match_groups)
995
+ %x{
996
+ var instance = new Opal.MatchData;
997
+ for (var i = 0, len = match_groups.length; i < len; i++) {
998
+ var group = match_groups[i];
999
+ if (group == undefined) {
1000
+ instance.push(nil);
1001
+ }
1002
+ else {
1003
+ instance.push(group);
1004
+ }
1005
+ }
1006
+ instance._begin = match_groups.index;
1007
+ instance.regexp = regexp;
1008
+ instance.string = match_groups.input;
1009
+ instance.pre_match = #{$` = `instance.string.substr(0, regexp.lastIndex - instance[0].length)`};
1010
+ instance.post_match = #{$' = `instance.string.substr(regexp.lastIndex)`};
1011
+ return #{$~ = `instance`};
1012
+ }
1013
+ end
1014
+
1015
+ def begin(pos)
1016
+ %x{
1017
+ if (pos == 0 || pos == 1) {
1018
+ return #{self}._begin;
1019
+ }
1020
+ else {
1021
+ #{raise ArgumentError, 'MatchData#begin only supports 0th element'};
1022
+ }
1023
+ }
1024
+ end
1025
+
1026
+ def captures
1027
+ `#{self}.slice(1)`
1028
+ end
1029
+
1030
+ def inspect
1031
+ %x{
1032
+ var str = "<#MatchData " + #{self}[0].$inspect()
1033
+ for (var i = 1, len = #{self}.length; i < len; i++) {
1034
+ str += " " + i + ":" + #{self}[i].$inspect();
1035
+ }
1036
+ str += ">";
1037
+ return str;
1038
+ }
1039
+ end
1040
+
1041
+ def to_s
1042
+ `#{self}[0]`
1043
+ end
1044
+
1045
+ def values_at(*indexes)
1046
+ %x{
1047
+ var vals = [];
1048
+ var match_length = #{self}.length;
1049
+ for (var i = 0, length = indexes.length; i < length; i++) {
1050
+ var pos = indexes[i];
1051
+ if (pos >= 0) {
1052
+ vals.push(#{self}[pos]);
1053
+ }
1054
+ else {
1055
+ pos = match_length + pos;
1056
+ if (pos > 0) {
1057
+ vals.push(#{self}[pos]);
1058
+ }
1059
+ else {
1060
+ vals.push(nil);
1061
+ }
1062
+ }
1063
+ }
1064
+
1065
+ return vals;
1066
+ }
1067
+ end
1068
+ end