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.
- checksums.yaml +4 -4
- data/.travis.yml +6 -1
- data/CHANGELOG.md +29 -0
- data/CONTRIBUTING.md +51 -4
- data/Gemfile +3 -0
- data/README.md +5 -5
- data/config.ru +1 -1
- data/examples/sinatra/Gemfile +1 -0
- data/examples/sinatra/config.ru +13 -3
- data/lib/mspec/opal/rake_task.rb +21 -30
- data/lib/mspec/opal/runner.rb +37 -0
- data/lib/mspec/opal/special_calls.rb +6 -0
- data/lib/opal/builder.rb +1 -0
- data/lib/opal/builder_processors.rb +5 -2
- data/lib/opal/cli_runners/phantom.js +10 -1
- data/lib/opal/compiler.rb +6 -3
- data/lib/opal/config.rb +48 -0
- data/lib/opal/nodes/call.rb +3 -2
- data/lib/opal/nodes/literal.rb +19 -2
- data/lib/opal/parser/grammar.rb +2224 -2196
- data/lib/opal/parser/grammar.y +25 -7
- data/lib/opal/parser/lexer.rb +12 -9
- data/lib/opal/path_reader.rb +1 -1
- data/lib/opal/sprockets/erb.rb +6 -20
- data/lib/opal/sprockets/path_reader.rb +4 -2
- data/lib/opal/sprockets/processor.rb +135 -80
- data/lib/opal/sprockets/server.rb +49 -78
- data/lib/opal/sprockets/source_map_header_patch.rb +41 -0
- data/lib/opal/sprockets/source_map_server.rb +115 -0
- data/lib/opal/version.rb +1 -1
- data/lib/tilt/opal.rb +48 -0
- data/opal.gemspec +1 -1
- data/opal/corelib/array.rb +179 -51
- data/opal/corelib/array/inheritance.rb +14 -0
- data/opal/corelib/boolean.rb +5 -0
- data/opal/corelib/hash.rb +1 -1
- data/opal/corelib/kernel.rb +660 -164
- data/opal/corelib/match_data.rb +44 -21
- data/opal/corelib/module.rb +83 -53
- data/opal/corelib/numeric.rb +15 -1
- data/opal/corelib/regexp.rb +31 -75
- data/opal/corelib/runtime.js +20 -8
- data/opal/corelib/string.rb +754 -243
- data/opal/corelib/string/inheritance.rb +20 -3
- data/opal/corelib/struct.rb +30 -6
- data/opal/corelib/variables.rb +2 -2
- data/spec/filters/bugs/array.rb +0 -39
- data/spec/filters/bugs/kernel.rb +10 -7
- data/spec/filters/bugs/module.rb +21 -0
- data/spec/filters/bugs/opal.rb +0 -5
- data/spec/filters/bugs/singleton.rb +0 -2
- data/spec/filters/bugs/string.rb +69 -315
- data/spec/filters/bugs/struct.rb +0 -16
- data/spec/filters/unsupported/encoding.rb +7 -0
- data/spec/filters/unsupported/float.rb +3 -0
- data/spec/filters/unsupported/integer_size.rb +52 -0
- data/spec/filters/unsupported/marshal.rb +4 -0
- data/spec/filters/unsupported/mutable_strings.rb +37 -0
- data/spec/filters/unsupported/private_methods.rb +11 -0
- data/spec/filters/unsupported/rational_numbers.rb +4 -0
- data/spec/filters/unsupported/regular_expressions.rb +47 -0
- data/spec/filters/unsupported/symbols.rb +7 -0
- data/spec/filters/unsupported/tainted.rb +23 -1
- data/spec/filters/unsupported/trusted.rb +5 -0
- data/spec/lib/fixtures/complex_sprockets.js.rb.erb +4 -0
- data/spec/lib/fixtures/jst_file.js.jst +1 -0
- data/spec/lib/parser/call_spec.rb +19 -0
- data/spec/lib/parser/def_spec.rb +6 -0
- data/spec/lib/sprockets/erb_spec.rb +17 -4
- data/spec/lib/sprockets/processor_spec.rb +50 -18
- data/spec/lib/sprockets/server_spec.rb +39 -11
- data/spec/lib/tilt/opal_spec.rb +19 -0
- data/spec/opal/core/kernel/format_spec.rb +10 -10
- data/spec/opal/core/language/predefined_spec.rb +10 -0
- data/spec/opal/core/object_id_spec.rb +56 -0
- data/spec/opal/core/runtime/bridged_classes_spec.rb +4 -4
- data/spec/opal/core/runtime_spec.rb +7 -0
- data/spec/opal/stdlib/native/native_class_spec.rb +1 -1
- data/spec/opal/stdlib/promise/always_spec.rb +30 -0
- data/spec/opal/stdlib/promise/then_spec.rb +8 -0
- data/spec/opal/stdlib/promise/trace_spec.rb +8 -0
- data/spec/rubyspecs +15 -104
- data/spec/spec_helper.rb +4 -0
- data/stdlib/native.rb +7 -18
- data/stdlib/nodejs/file.rb +1 -1
- data/stdlib/opal-parser.rb +1 -0
- data/stdlib/pp.rb +7 -5
- data/stdlib/promise.rb +53 -41
- data/tasks/testing.rake +8 -6
- metadata +28 -14
- data/spec/filters/bugs/match_data.rb +0 -13
- data/spec/filters/bugs/numeric.rb +0 -22
- data/spec/filters/bugs/regexp.rb +0 -9
- data/spec/filters/bugs/unknown.rb +0 -11
data/opal/corelib/runtime.js
CHANGED
@@ -29,12 +29,17 @@
|
|
29
29
|
var $hasOwn = Opal.hasOwnProperty;
|
30
30
|
var $slice = Opal.slice = Array.prototype.slice;
|
31
31
|
|
32
|
-
//
|
33
|
-
var
|
32
|
+
// Nil object id is always 4
|
33
|
+
var nil_id = 4;
|
34
|
+
|
35
|
+
// Generates even sequential numbers greater than 4
|
36
|
+
// (nil_id) to serve as unique ids for ruby objects
|
37
|
+
var unique_id = nil_id;
|
34
38
|
|
35
39
|
// Return next unique id
|
36
40
|
Opal.uid = function() {
|
37
|
-
|
41
|
+
unique_id += 2;
|
42
|
+
return unique_id;
|
38
43
|
};
|
39
44
|
|
40
45
|
// Table holds all class variables
|
@@ -238,7 +243,7 @@
|
|
238
243
|
function setup_module_or_class_object(module, constructor, superklass, prototype) {
|
239
244
|
// @property $$id Each class is assigned a unique `id` that helps
|
240
245
|
// comparation and implementation of `#object_id`
|
241
|
-
module.$$id =
|
246
|
+
module.$$id = Opal.uid();
|
242
247
|
|
243
248
|
// @property $$proto This is the prototype on which methods will be defined
|
244
249
|
module.$$proto = prototype;
|
@@ -1134,6 +1139,13 @@
|
|
1134
1139
|
}
|
1135
1140
|
}
|
1136
1141
|
|
1142
|
+
/*
|
1143
|
+
* Called to remove a method.
|
1144
|
+
*/
|
1145
|
+
Opal.undef = function(obj, jsid) {
|
1146
|
+
delete obj.$$proto[jsid];
|
1147
|
+
};
|
1148
|
+
|
1137
1149
|
Opal.hash = function() {
|
1138
1150
|
if (arguments.length == 1 && arguments[0].$$class == Opal.Hash) {
|
1139
1151
|
return arguments[0];
|
@@ -1182,7 +1194,7 @@
|
|
1182
1194
|
obj = arguments[0];
|
1183
1195
|
for (key in obj) {
|
1184
1196
|
khash = key.$hash();
|
1185
|
-
|
1197
|
+
smap[khash] = obj[khash];
|
1186
1198
|
keys.push(key);
|
1187
1199
|
}
|
1188
1200
|
}
|
@@ -1248,8 +1260,8 @@
|
|
1248
1260
|
// Require system
|
1249
1261
|
// --------------
|
1250
1262
|
(function(Opal) {
|
1251
|
-
var loaded_features = ['corelib/runtime
|
1252
|
-
require_table = {'corelib/runtime
|
1263
|
+
var loaded_features = ['corelib/runtime'],
|
1264
|
+
require_table = {'corelib/runtime': true},
|
1253
1265
|
modules = {};
|
1254
1266
|
|
1255
1267
|
var current_dir = '.';
|
@@ -1272,6 +1284,7 @@
|
|
1272
1284
|
path = current_dir.replace(/\/*$/, '/') + path;
|
1273
1285
|
}
|
1274
1286
|
|
1287
|
+
path = path.replace(/\.(rb|opal|js)$/, '');
|
1275
1288
|
parts = path.split(SEPARATOR);
|
1276
1289
|
|
1277
1290
|
for (var i = 0, ii = parts.length; i < ii; i++) {
|
@@ -1408,7 +1421,6 @@
|
|
1408
1421
|
Opal.top = new ObjectClass.$$alloc();
|
1409
1422
|
|
1410
1423
|
// Nil
|
1411
|
-
var nil_id = Opal.uid(); // nil id is traditionally 4
|
1412
1424
|
Opal.klass(ObjectClass, ObjectClass, 'NilClass', NilClass);
|
1413
1425
|
var nil = Opal.nil = new NilClass();
|
1414
1426
|
nil.$$id = nil_id;
|
data/opal/corelib/string.rb
CHANGED
@@ -5,16 +5,29 @@ class String
|
|
5
5
|
|
6
6
|
`def.$$is_string = true`
|
7
7
|
|
8
|
+
def __id__
|
9
|
+
`self.toString()`
|
10
|
+
end
|
11
|
+
alias object_id __id__
|
12
|
+
|
8
13
|
def self.try_convert(what)
|
9
|
-
what
|
10
|
-
rescue
|
11
|
-
nil
|
14
|
+
Opal.coerce_to?(what, String, :to_str)
|
12
15
|
end
|
13
16
|
|
14
17
|
def self.new(str = '')
|
18
|
+
str = Opal.coerce_to(str, String, :to_str)
|
15
19
|
`new String(str)`
|
16
20
|
end
|
17
21
|
|
22
|
+
def initialize(str = undefined)
|
23
|
+
%x{
|
24
|
+
if (str === undefined) {
|
25
|
+
return self;
|
26
|
+
}
|
27
|
+
}
|
28
|
+
raise NotImplementedError, 'Mutable strings are not supported in Opal.'
|
29
|
+
end
|
30
|
+
|
18
31
|
def %(data)
|
19
32
|
if Array === data
|
20
33
|
format(self, *data)
|
@@ -25,20 +38,36 @@ class String
|
|
25
38
|
|
26
39
|
def *(count)
|
27
40
|
%x{
|
28
|
-
|
41
|
+
count = #{Opal.coerce_to(`count`, Integer, :to_int)};
|
42
|
+
|
43
|
+
if (count < 0) {
|
44
|
+
#{raise ArgumentError, 'negative argument'}
|
45
|
+
}
|
46
|
+
|
47
|
+
if (count === 0) {
|
29
48
|
return '';
|
30
49
|
}
|
31
50
|
|
32
|
-
var result
|
33
|
-
|
51
|
+
var result = '',
|
52
|
+
string = self.toString();
|
34
53
|
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
}
|
54
|
+
// All credit for the bit-twiddling magic code below goes to Mozilla
|
55
|
+
// polyfill implementation of String.prototype.repeat() posted here:
|
56
|
+
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/repeat
|
39
57
|
|
40
|
-
|
41
|
-
|
58
|
+
if (string.length * count >= 1 << 28) {
|
59
|
+
#{raise RangeError, 'multiply count must not overflow maximum string size'}
|
60
|
+
}
|
61
|
+
|
62
|
+
for (;;) {
|
63
|
+
if ((count & 1) === 1) {
|
64
|
+
result += string;
|
65
|
+
}
|
66
|
+
count >>>= 1;
|
67
|
+
if (count === 0) {
|
68
|
+
break;
|
69
|
+
}
|
70
|
+
string += string;
|
42
71
|
}
|
43
72
|
|
44
73
|
return result;
|
@@ -75,9 +104,15 @@ class String
|
|
75
104
|
end
|
76
105
|
|
77
106
|
def ==(other)
|
78
|
-
|
79
|
-
|
80
|
-
|
107
|
+
%x{
|
108
|
+
if (other.$$is_string) {
|
109
|
+
return self.toString() === other.toString();
|
110
|
+
}
|
111
|
+
if (#{Opal.respond_to? `other`, :to_str}) {
|
112
|
+
return #{other == self};
|
113
|
+
}
|
114
|
+
return false;
|
115
|
+
}
|
81
116
|
end
|
82
117
|
|
83
118
|
alias eql? ==
|
@@ -99,8 +134,12 @@ class String
|
|
99
134
|
|
100
135
|
if (index.$$is_range) {
|
101
136
|
var exclude = index.exclude,
|
102
|
-
length = index.end,
|
103
|
-
index = index.begin;
|
137
|
+
length = #{Opal.coerce_to(`index.end`, Integer, :to_int)},
|
138
|
+
index = #{Opal.coerce_to(`index.begin`, Integer, :to_int)};
|
139
|
+
|
140
|
+
if (Math.abs(index) > size) {
|
141
|
+
return nil;
|
142
|
+
}
|
104
143
|
|
105
144
|
if (index < 0) {
|
106
145
|
index += size;
|
@@ -114,10 +153,6 @@ class String
|
|
114
153
|
length += 1;
|
115
154
|
}
|
116
155
|
|
117
|
-
if (index > size) {
|
118
|
-
return nil;
|
119
|
-
}
|
120
|
-
|
121
156
|
length = length - index;
|
122
157
|
|
123
158
|
if (length < 0) {
|
@@ -127,19 +162,63 @@ class String
|
|
127
162
|
return self.substr(index, length);
|
128
163
|
}
|
129
164
|
|
165
|
+
|
166
|
+
if (index.$$is_string) {
|
167
|
+
if (length != null) {
|
168
|
+
#{raise TypeError}
|
169
|
+
}
|
170
|
+
return self.indexOf(index) !== -1 ? index : nil;
|
171
|
+
}
|
172
|
+
|
173
|
+
|
174
|
+
if (index.$$is_regexp) {
|
175
|
+
var match = self.match(index);
|
176
|
+
|
177
|
+
if (match === null) {
|
178
|
+
#{$~ = nil}
|
179
|
+
return nil;
|
180
|
+
}
|
181
|
+
|
182
|
+
#{$~ = MatchData.new(`index`, `match`)}
|
183
|
+
|
184
|
+
if (length == null) {
|
185
|
+
return match[0];
|
186
|
+
}
|
187
|
+
|
188
|
+
length = #{Opal.coerce_to(`length`, Integer, :to_int)};
|
189
|
+
|
190
|
+
if (length < 0 && -length < match.length) {
|
191
|
+
return match[length += match.length];
|
192
|
+
}
|
193
|
+
|
194
|
+
if (length >= 0 && length < match.length) {
|
195
|
+
return match[length];
|
196
|
+
}
|
197
|
+
|
198
|
+
return nil;
|
199
|
+
}
|
200
|
+
|
201
|
+
|
202
|
+
index = #{Opal.coerce_to(`index`, Integer, :to_int)};
|
203
|
+
|
130
204
|
if (index < 0) {
|
131
|
-
index +=
|
205
|
+
index += size;
|
132
206
|
}
|
133
207
|
|
134
208
|
if (length == null) {
|
135
|
-
if (index >=
|
209
|
+
if (index >= size || index < 0) {
|
136
210
|
return nil;
|
137
211
|
}
|
138
|
-
|
139
212
|
return self.substr(index, 1);
|
140
213
|
}
|
141
214
|
|
142
|
-
|
215
|
+
length = #{Opal.coerce_to(`length`, Integer, :to_int)};
|
216
|
+
|
217
|
+
if (length < 0) {
|
218
|
+
return nil;
|
219
|
+
}
|
220
|
+
|
221
|
+
if (index > size || index < 0) {
|
143
222
|
return nil;
|
144
223
|
}
|
145
224
|
|
@@ -155,8 +234,14 @@ class String
|
|
155
234
|
|
156
235
|
def casecmp(other)
|
157
236
|
other = Opal.coerce_to(other, String, :to_str).to_s
|
158
|
-
|
159
|
-
|
237
|
+
%x{
|
238
|
+
var ascii_only = /^[\x00-\x7F]*$/;
|
239
|
+
if (ascii_only.test(self) && ascii_only.test(other)) {
|
240
|
+
self = self.toLowerCase();
|
241
|
+
other = other.toLowerCase();
|
242
|
+
}
|
243
|
+
}
|
244
|
+
self <=> other
|
160
245
|
end
|
161
246
|
|
162
247
|
def center(width, padstr = ' ')
|
@@ -244,8 +329,30 @@ class String
|
|
244
329
|
copy
|
245
330
|
end
|
246
331
|
|
247
|
-
def count(
|
248
|
-
|
332
|
+
def count(*sets)
|
333
|
+
%x{
|
334
|
+
if (sets.length === 0) {
|
335
|
+
#{raise ArgumentError, "ArgumentError: wrong number of arguments (0 for 1+)"}
|
336
|
+
}
|
337
|
+
var char_class = char_class_from_char_sets(sets);
|
338
|
+
if (char_class === null) {
|
339
|
+
return 0;
|
340
|
+
}
|
341
|
+
return self.length - self.replace(new RegExp(char_class, 'g'), '').length;
|
342
|
+
}
|
343
|
+
end
|
344
|
+
|
345
|
+
def delete(*sets)
|
346
|
+
%x{
|
347
|
+
if (sets.length === 0) {
|
348
|
+
#{raise ArgumentError, "ArgumentError: wrong number of arguments (0 for 1+)"}
|
349
|
+
}
|
350
|
+
var char_class = char_class_from_char_sets(sets);
|
351
|
+
if (char_class === null) {
|
352
|
+
return self;
|
353
|
+
}
|
354
|
+
return self.replace(new RegExp(char_class, 'g'), '');
|
355
|
+
}
|
249
356
|
end
|
250
357
|
|
251
358
|
alias dup clone
|
@@ -269,10 +376,26 @@ class String
|
|
269
376
|
end
|
270
377
|
|
271
378
|
def each_line(separator = $/)
|
272
|
-
return
|
379
|
+
return enum_for :each_line, separator unless block_given?
|
273
380
|
|
274
381
|
%x{
|
275
|
-
|
382
|
+
if (separator === nil) {
|
383
|
+
#{yield self};
|
384
|
+
return self;
|
385
|
+
}
|
386
|
+
|
387
|
+
separator = #{Opal.coerce_to(`separator`, String, :to_str)}
|
388
|
+
|
389
|
+
if (separator.length === 0) {
|
390
|
+
for (var a = self.split(/(\n{2,})/), i = 0, n = a.length; i < n; i += 2) {
|
391
|
+
if (a[i] || a[i + 1]) {
|
392
|
+
#{yield `(a[i] || "") + (a[i + 1] || "")`};
|
393
|
+
}
|
394
|
+
}
|
395
|
+
return self;
|
396
|
+
}
|
397
|
+
|
398
|
+
var chomped = #{chomp(separator)},
|
276
399
|
trailing = self.length != chomped.length,
|
277
400
|
splitted = chomped.split(separator);
|
278
401
|
|
@@ -311,22 +434,73 @@ class String
|
|
311
434
|
alias eql? ==
|
312
435
|
alias equal? ===
|
313
436
|
|
314
|
-
def gsub(pattern,
|
315
|
-
|
316
|
-
|
317
|
-
end
|
437
|
+
def gsub(pattern, replacement = undefined, &block)
|
438
|
+
%x{
|
439
|
+
var result = '', match_data = nil, index = 0, match, _replacement;
|
318
440
|
|
319
|
-
|
320
|
-
|
321
|
-
|
441
|
+
if (pattern.$$is_regexp) {
|
442
|
+
pattern = new RegExp(pattern.source, 'gm' + (pattern.ignoreCase ? 'i' : ''));
|
443
|
+
} else {
|
444
|
+
pattern = #{Opal.coerce_to(`pattern`, String, :to_str)};
|
445
|
+
pattern = new RegExp(pattern.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'), 'gm');
|
446
|
+
}
|
322
447
|
|
323
|
-
|
324
|
-
|
325
|
-
|
326
|
-
|
448
|
+
while (true) {
|
449
|
+
match = pattern.exec(self);
|
450
|
+
|
451
|
+
if (match === null) {
|
452
|
+
#{$~ = nil}
|
453
|
+
result += self.slice(index);
|
454
|
+
break;
|
455
|
+
}
|
456
|
+
|
457
|
+
match_data = #{MatchData.new `pattern`, `match`};
|
458
|
+
|
459
|
+
if (replacement === undefined) {
|
460
|
+
if (block === nil) {
|
461
|
+
#{raise ArgumentError, 'wrong number of arguments (1 for 2)'}
|
462
|
+
}
|
463
|
+
_replacement = block(match[0]);
|
464
|
+
}
|
465
|
+
else if (replacement.$$is_hash) {
|
466
|
+
_replacement = #{`replacement`[`match[0]`].to_s};
|
467
|
+
}
|
468
|
+
else {
|
469
|
+
if (!replacement.$$is_string) {
|
470
|
+
replacement = #{Opal.coerce_to(`replacement`, String, :to_str)};
|
471
|
+
}
|
472
|
+
_replacement = replacement.replace(/([\\]+)([0-9+&`'])/g, function (original, slashes, command) {
|
473
|
+
if (slashes.length % 2 === 0) {
|
474
|
+
return original;
|
475
|
+
}
|
476
|
+
switch (command) {
|
477
|
+
case "+":
|
478
|
+
for (var i = match.length - 1; i > 0; i--) {
|
479
|
+
if (match[i] !== undefined) {
|
480
|
+
return slashes.slice(1) + match[i];
|
481
|
+
}
|
482
|
+
}
|
483
|
+
return '';
|
484
|
+
case "&": return slashes.slice(1) + match[0];
|
485
|
+
case "`": return slashes.slice(1) + self.slice(0, match.index);
|
486
|
+
case "'": return slashes.slice(1) + self.slice(match.index + match[0].length);
|
487
|
+
default: return slashes.slice(1) + (match[command] || '');
|
488
|
+
}
|
489
|
+
}).replace(/\\\\/g, '\\');
|
490
|
+
}
|
491
|
+
|
492
|
+
if (pattern.lastIndex === match.index) {
|
493
|
+
result += (_replacement + self.slice(index, match.index + 1))
|
494
|
+
pattern.lastIndex += 1;
|
495
|
+
}
|
496
|
+
else {
|
497
|
+
result += (self.slice(index, match.index) + _replacement)
|
498
|
+
}
|
499
|
+
index = pattern.lastIndex;
|
500
|
+
}
|
327
501
|
|
328
|
-
|
329
|
-
return
|
502
|
+
#{$~ = `match_data`}
|
503
|
+
return result;
|
330
504
|
}
|
331
505
|
end
|
332
506
|
|
@@ -354,77 +528,72 @@ class String
|
|
354
528
|
`self.indexOf(#{other.to_str}) !== -1`
|
355
529
|
end
|
356
530
|
|
357
|
-
def index(
|
358
|
-
|
359
|
-
|
360
|
-
|
361
|
-
|
362
|
-
|
363
|
-
|
364
|
-
|
365
|
-
|
366
|
-
|
367
|
-
|
368
|
-
if offset
|
369
|
-
offset = Opal.coerce_to offset, Integer, :to_int
|
370
|
-
|
371
|
-
%x{
|
372
|
-
var size = self.length;
|
373
|
-
|
531
|
+
def index(search, offset = undefined)
|
532
|
+
%x{
|
533
|
+
var index,
|
534
|
+
match,
|
535
|
+
regex;
|
536
|
+
|
537
|
+
if (offset === undefined) {
|
538
|
+
offset = 0;
|
539
|
+
} else {
|
540
|
+
offset = #{Opal.coerce_to(`offset`, Integer, :to_int)};
|
374
541
|
if (offset < 0) {
|
375
|
-
offset
|
376
|
-
|
377
|
-
|
378
|
-
|
379
|
-
return nil;
|
542
|
+
offset += self.length;
|
543
|
+
if (offset < 0) {
|
544
|
+
return nil;
|
545
|
+
}
|
380
546
|
}
|
381
547
|
}
|
382
548
|
|
383
|
-
if
|
384
|
-
|
385
|
-
|
386
|
-
|
387
|
-
|
388
|
-
|
389
|
-
|
390
|
-
|
391
|
-
|
549
|
+
if (search.$$is_regexp) {
|
550
|
+
regex = new RegExp(search.source, 'gm' + (search.ignoreCase ? 'i' : ''));
|
551
|
+
while (true) {
|
552
|
+
match = regex.exec(self);
|
553
|
+
if (match === null) {
|
554
|
+
#{$~ = nil};
|
555
|
+
index = -1;
|
556
|
+
break;
|
557
|
+
}
|
558
|
+
if (match.index >= offset) {
|
559
|
+
#{$~ = MatchData.new(`regex`, `match`)}
|
560
|
+
index = match.index;
|
561
|
+
break;
|
562
|
+
}
|
563
|
+
regex.lastIndex = match.index + 1;
|
564
|
+
}
|
565
|
+
} else {
|
566
|
+
search = #{Opal.coerce_to(`search`, String, :to_str)};
|
567
|
+
if (search.length === 0 && offset > self.length) {
|
568
|
+
index = -1;
|
569
|
+
} else {
|
570
|
+
index = self.indexOf(search, offset);
|
392
571
|
}
|
393
572
|
}
|
394
|
-
else
|
395
|
-
if Regexp === what
|
396
|
-
result = (what =~ self) || -1
|
397
|
-
else
|
398
|
-
result = `self.indexOf(what)`
|
399
|
-
end
|
400
|
-
end
|
401
573
|
|
402
|
-
|
403
|
-
|
404
|
-
end
|
574
|
+
return index === -1 ? nil : index;
|
575
|
+
}
|
405
576
|
end
|
406
577
|
|
407
578
|
def inspect
|
408
579
|
%x{
|
409
|
-
var escapable = /[\\\"\x00-\x1f\x7f-\x9f\
|
410
|
-
meta
|
580
|
+
var escapable = /[\\\"\x00-\x1f\x7f-\x9f\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,
|
581
|
+
meta = {
|
582
|
+
'\u0007': '\\a',
|
583
|
+
'\u001b': '\\e',
|
411
584
|
'\b': '\\b',
|
412
585
|
'\t': '\\t',
|
413
586
|
'\n': '\\n',
|
414
587
|
'\f': '\\f',
|
415
588
|
'\r': '\\r',
|
589
|
+
'\v': '\\v',
|
416
590
|
'"' : '\\"',
|
417
591
|
'\\': '\\\\'
|
418
|
-
}
|
419
|
-
|
420
|
-
|
421
|
-
|
422
|
-
return
|
423
|
-
var c = meta[a];
|
424
|
-
|
425
|
-
return typeof c === 'string' ? c :
|
426
|
-
'\\u' + ('0000' + a.charCodeAt(0).toString(16)).slice(-4);
|
427
|
-
}) + '"' : '"' + self + '"';
|
592
|
+
},
|
593
|
+
escaped = self.replace(escapable, function (chr) {
|
594
|
+
return meta[chr] || '\\u' + ('0000' + chr.charCodeAt(0).toString(16).toUpperCase()).slice(-4);
|
595
|
+
});
|
596
|
+
return '"' + escaped.replace(/\#[\$\@\{]/g, '\\$&') + '"';
|
428
597
|
}
|
429
598
|
end
|
430
599
|
|
@@ -432,8 +601,9 @@ class String
|
|
432
601
|
self
|
433
602
|
end
|
434
603
|
|
435
|
-
def lines(separator =
|
436
|
-
each_line(separator)
|
604
|
+
def lines(separator = $/, &block)
|
605
|
+
e = each_line(separator, &block)
|
606
|
+
block ? self : e.to_a
|
437
607
|
end
|
438
608
|
|
439
609
|
def length
|
@@ -472,7 +642,7 @@ class String
|
|
472
642
|
|
473
643
|
def match(pattern, pos = undefined, &block)
|
474
644
|
if String === pattern || pattern.respond_to?(:to_str)
|
475
|
-
pattern =
|
645
|
+
pattern = Regexp.new(pattern.to_str)
|
476
646
|
end
|
477
647
|
|
478
648
|
unless Regexp === pattern
|
@@ -484,29 +654,150 @@ class String
|
|
484
654
|
|
485
655
|
def next
|
486
656
|
%x{
|
487
|
-
|
488
|
-
|
657
|
+
var i = self.length;
|
658
|
+
if (i === 0) {
|
659
|
+
return '';
|
489
660
|
}
|
490
|
-
|
491
|
-
var
|
492
|
-
var
|
493
|
-
|
494
|
-
|
661
|
+
var result = self;
|
662
|
+
var first_alphanum_char_index = self.search(/[a-zA-Z0-9]/);
|
663
|
+
var carry = false;
|
664
|
+
var code;
|
665
|
+
while (i--) {
|
666
|
+
code = self.charCodeAt(i);
|
667
|
+
if ((code >= 48 && code <= 57) ||
|
668
|
+
(code >= 65 && code <= 90) ||
|
669
|
+
(code >= 97 && code <= 122)) {
|
670
|
+
switch (code) {
|
671
|
+
case 57:
|
672
|
+
carry = true;
|
673
|
+
code = 48;
|
674
|
+
break;
|
675
|
+
case 90:
|
676
|
+
carry = true;
|
677
|
+
code = 65;
|
678
|
+
break;
|
679
|
+
case 122:
|
680
|
+
carry = true;
|
681
|
+
code = 97;
|
682
|
+
break;
|
683
|
+
default:
|
684
|
+
carry = false;
|
685
|
+
code += 1;
|
686
|
+
}
|
687
|
+
} else {
|
688
|
+
if (first_alphanum_char_index === -1) {
|
689
|
+
if (code === 255) {
|
690
|
+
carry = true;
|
691
|
+
code = 0;
|
692
|
+
} else {
|
693
|
+
carry = false;
|
694
|
+
code += 1;
|
695
|
+
}
|
696
|
+
} else {
|
697
|
+
carry = true;
|
698
|
+
}
|
699
|
+
}
|
700
|
+
result = result.slice(0, i) + String.fromCharCode(code) + result.slice(i + 1);
|
701
|
+
if (carry && (i === 0 || i === first_alphanum_char_index)) {
|
702
|
+
switch (code) {
|
703
|
+
case 65:
|
704
|
+
break;
|
705
|
+
case 97:
|
706
|
+
break;
|
707
|
+
default:
|
708
|
+
code += 1;
|
709
|
+
}
|
710
|
+
if (i === 0) {
|
711
|
+
result = String.fromCharCode(code) + result;
|
712
|
+
} else {
|
713
|
+
result = result.slice(0, i) + String.fromCharCode(code) + result.slice(i);
|
714
|
+
}
|
715
|
+
carry = false;
|
716
|
+
}
|
717
|
+
if (!carry) {
|
718
|
+
break;
|
719
|
+
}
|
720
|
+
}
|
721
|
+
return result;
|
495
722
|
}
|
496
723
|
end
|
497
724
|
|
498
725
|
alias next! <<
|
499
726
|
|
727
|
+
def oct
|
728
|
+
%x{
|
729
|
+
var result,
|
730
|
+
string = self,
|
731
|
+
radix = 8;
|
732
|
+
|
733
|
+
if (/^\s*_/.test(string)) {
|
734
|
+
return 0;
|
735
|
+
}
|
736
|
+
|
737
|
+
string = string.replace(/^(\s*[+-]?)(0[bodx]?)(.+)$/i, function (original, head, flag, tail) {
|
738
|
+
switch (tail.charAt(0)) {
|
739
|
+
case '+':
|
740
|
+
case '-':
|
741
|
+
return original;
|
742
|
+
case '0':
|
743
|
+
if (tail.charAt(1) === 'x' && flag === '0x') {
|
744
|
+
return original;
|
745
|
+
}
|
746
|
+
}
|
747
|
+
switch (flag) {
|
748
|
+
case '0b':
|
749
|
+
radix = 2;
|
750
|
+
break;
|
751
|
+
case '0':
|
752
|
+
case '0o':
|
753
|
+
radix = 8;
|
754
|
+
break;
|
755
|
+
case '0d':
|
756
|
+
radix = 10;
|
757
|
+
break;
|
758
|
+
case '0x':
|
759
|
+
radix = 16;
|
760
|
+
break;
|
761
|
+
}
|
762
|
+
return head + tail;
|
763
|
+
});
|
764
|
+
|
765
|
+
result = parseInt(string.replace(/_(?!_)/g, ''), radix);
|
766
|
+
return isNaN(result) ? 0 : result;
|
767
|
+
}
|
768
|
+
end
|
769
|
+
|
500
770
|
def ord
|
501
771
|
`self.charCodeAt(0)`
|
502
772
|
end
|
503
773
|
|
504
|
-
def partition(
|
774
|
+
def partition(sep)
|
505
775
|
%x{
|
506
|
-
var
|
507
|
-
|
776
|
+
var i, m;
|
777
|
+
|
778
|
+
if (sep.$$is_regexp) {
|
779
|
+
m = sep.exec(self);
|
780
|
+
if (m === null) {
|
781
|
+
i = -1;
|
782
|
+
} else {
|
783
|
+
#{MatchData.new `sep`, `m`};
|
784
|
+
sep = m[0];
|
785
|
+
i = m.index;
|
786
|
+
}
|
787
|
+
} else {
|
788
|
+
sep = #{Opal.coerce_to(`sep`, String, :to_str)};
|
789
|
+
i = self.indexOf(sep);
|
790
|
+
}
|
508
791
|
|
509
|
-
|
792
|
+
if (i === -1) {
|
793
|
+
return [self, '', ''];
|
794
|
+
}
|
795
|
+
|
796
|
+
return [
|
797
|
+
self.slice(0, i),
|
798
|
+
self.slice(i, i + sep.length),
|
799
|
+
self.slice(i + sep.length)
|
800
|
+
];
|
510
801
|
}
|
511
802
|
end
|
512
803
|
|
@@ -516,48 +807,46 @@ class String
|
|
516
807
|
|
517
808
|
alias reverse! <<
|
518
809
|
|
519
|
-
# TODO handle case where search is regexp
|
520
810
|
def rindex(search, offset = undefined)
|
521
811
|
%x{
|
522
|
-
var
|
523
|
-
if (search_type != String && search_type != RegExp) {
|
524
|
-
var msg = "type mismatch: " + search_type + " given";
|
525
|
-
#{raise TypeError.new(`msg`)};
|
526
|
-
}
|
527
|
-
|
528
|
-
if (self.length == 0) {
|
529
|
-
return search.length == 0 ? 0 : nil;
|
530
|
-
}
|
812
|
+
var i, m, r, _m;
|
531
813
|
|
532
|
-
|
533
|
-
|
814
|
+
if (offset === undefined) {
|
815
|
+
offset = self.length;
|
816
|
+
} else {
|
817
|
+
offset = #{Opal.coerce_to(`offset`, Integer, :to_int)};
|
534
818
|
if (offset < 0) {
|
535
|
-
offset
|
536
|
-
|
537
|
-
|
538
|
-
if (search_type == String) {
|
539
|
-
result = self.lastIndexOf(search, offset);
|
540
|
-
}
|
541
|
-
else {
|
542
|
-
result = self.substr(0, offset + 1).$reverse().search(search);
|
543
|
-
if (result !== -1) {
|
544
|
-
result = offset - result;
|
819
|
+
offset += self.length;
|
820
|
+
if (offset < 0) {
|
821
|
+
return nil;
|
545
822
|
}
|
546
823
|
}
|
547
824
|
}
|
548
|
-
|
549
|
-
|
550
|
-
|
551
|
-
|
552
|
-
|
553
|
-
|
554
|
-
if (
|
555
|
-
|
825
|
+
|
826
|
+
if (search.$$is_regexp) {
|
827
|
+
m = null;
|
828
|
+
r = new RegExp(search.source, 'gm' + (search.ignoreCase ? 'i' : ''));
|
829
|
+
while (true) {
|
830
|
+
_m = r.exec(self);
|
831
|
+
if (_m === null || _m.index > offset) {
|
832
|
+
break;
|
556
833
|
}
|
834
|
+
m = _m;
|
835
|
+
r.lastIndex = m.index + 1;
|
836
|
+
}
|
837
|
+
if (m === null) {
|
838
|
+
#{$~ = nil}
|
839
|
+
i = -1;
|
840
|
+
} else {
|
841
|
+
#{MatchData.new `r`, `m`};
|
842
|
+
i = m.index;
|
557
843
|
}
|
844
|
+
} else {
|
845
|
+
search = #{Opal.coerce_to(`search`, String, :to_str)};
|
846
|
+
i = self.lastIndexOf(search, offset);
|
558
847
|
}
|
559
848
|
|
560
|
-
return
|
849
|
+
return i === -1 ? nil : i;
|
561
850
|
}
|
562
851
|
end
|
563
852
|
|
@@ -581,34 +870,79 @@ class String
|
|
581
870
|
}
|
582
871
|
end
|
583
872
|
|
873
|
+
def rpartition(sep)
|
874
|
+
%x{
|
875
|
+
var i, m, r, _m;
|
876
|
+
|
877
|
+
if (sep.$$is_regexp) {
|
878
|
+
m = null;
|
879
|
+
r = new RegExp(sep.source, 'gm' + (sep.ignoreCase ? 'i' : ''));
|
880
|
+
|
881
|
+
while (true) {
|
882
|
+
_m = r.exec(self);
|
883
|
+
if (_m === null) {
|
884
|
+
break;
|
885
|
+
}
|
886
|
+
m = _m;
|
887
|
+
r.lastIndex = m.index + 1;
|
888
|
+
}
|
889
|
+
|
890
|
+
if (m === null) {
|
891
|
+
i = -1;
|
892
|
+
} else {
|
893
|
+
#{MatchData.new `r`, `m`};
|
894
|
+
sep = m[0];
|
895
|
+
i = m.index;
|
896
|
+
}
|
897
|
+
|
898
|
+
} else {
|
899
|
+
sep = #{Opal.coerce_to(`sep`, String, :to_str)};
|
900
|
+
i = self.lastIndexOf(sep);
|
901
|
+
}
|
902
|
+
|
903
|
+
if (i === -1) {
|
904
|
+
return ['', '', self];
|
905
|
+
}
|
906
|
+
|
907
|
+
return [
|
908
|
+
self.slice(0, i),
|
909
|
+
self.slice(i, i + sep.length),
|
910
|
+
self.slice(i + sep.length)
|
911
|
+
];
|
912
|
+
}
|
913
|
+
end
|
914
|
+
|
584
915
|
def rstrip
|
585
|
-
`self.replace(
|
916
|
+
`self.replace(/[\s\u0000]*$/, '')`
|
586
917
|
end
|
587
918
|
|
588
919
|
def scan(pattern, &block)
|
589
920
|
%x{
|
590
|
-
|
591
|
-
|
592
|
-
|
593
|
-
|
594
|
-
|
595
|
-
|
596
|
-
|
921
|
+
var result = [],
|
922
|
+
match_data = nil,
|
923
|
+
match;
|
924
|
+
|
925
|
+
if (pattern.$$is_regexp) {
|
926
|
+
pattern = new RegExp(pattern.source, 'gm' + (pattern.ignoreCase ? 'i' : ''));
|
927
|
+
} else {
|
928
|
+
pattern = #{Opal.coerce_to(`pattern`, String, :to_str)};
|
929
|
+
pattern = new RegExp(pattern.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'), 'gm');
|
597
930
|
}
|
598
931
|
|
599
|
-
var result = [];
|
600
|
-
var match;
|
601
|
-
|
602
932
|
while ((match = pattern.exec(self)) != null) {
|
603
|
-
|
933
|
+
match_data = #{MatchData.new `pattern`, `match`};
|
604
934
|
if (block === nil) {
|
605
|
-
match.length == 1 ? result.push(match[0]) : result.push(
|
935
|
+
match.length == 1 ? result.push(match[0]) : result.push(#{`match_data`.captures});
|
936
|
+
} else {
|
937
|
+
match.length == 1 ? block(match[0]) : block.call(self, #{`match_data`.captures});
|
606
938
|
}
|
607
|
-
|
608
|
-
|
939
|
+
if (pattern.lastIndex === match.index) {
|
940
|
+
pattern.lastIndex += 1;
|
609
941
|
}
|
610
942
|
}
|
611
943
|
|
944
|
+
#{$~ = `match_data`}
|
945
|
+
|
612
946
|
return (block !== nil ? self : result);
|
613
947
|
}
|
614
948
|
end
|
@@ -748,20 +1082,11 @@ class String
|
|
748
1082
|
if (sets.length === 0) {
|
749
1083
|
return self.replace(/(.)\1+/g, '$1');
|
750
1084
|
}
|
751
|
-
|
752
|
-
|
753
|
-
%x{
|
754
|
-
var set = #{Opal.coerce_to(`sets[0]`, String, :to_str).chars};
|
755
|
-
|
756
|
-
for (var i = 1, length = sets.length; i < length; i++) {
|
757
|
-
set = #{`set` & Opal.coerce_to(`sets[i]`, String, :to_str).chars};
|
758
|
-
}
|
759
|
-
|
760
|
-
if (set.length === 0) {
|
1085
|
+
var char_class = char_class_from_char_sets(sets);
|
1086
|
+
if (char_class === null) {
|
761
1087
|
return self;
|
762
1088
|
}
|
763
|
-
|
764
|
-
return self.replace(new RegExp("([" + #{Regexp.escape(`set`.join)} + "])\\1+", "g"), "$1");
|
1089
|
+
return self.replace(new RegExp('(' + char_class + ')\\1+', 'g'), '$1');
|
765
1090
|
}
|
766
1091
|
end
|
767
1092
|
|
@@ -782,78 +1107,60 @@ class String
|
|
782
1107
|
end
|
783
1108
|
|
784
1109
|
def strip
|
785
|
-
`self.replace(/^\s*/, '').replace(
|
1110
|
+
`self.replace(/^\s*/, '').replace(/[\s\u0000]*$/, '')`
|
786
1111
|
end
|
787
1112
|
|
788
1113
|
alias strip! <<
|
789
1114
|
|
790
|
-
|
791
|
-
// convert Ruby back reference to JavaScript back reference
|
792
|
-
function convertReplace(replace) {
|
793
|
-
return replace.replace(
|
794
|
-
/(^|[^\\])\\(\d)/g, function(a, b, c) { return b + '$' + c }
|
795
|
-
).replace(
|
796
|
-
/(^|[^\\])(\\\\)+\\\\(\d)/g, '$1$2\\$3'
|
797
|
-
).replace(
|
798
|
-
/(^|[^\\])(?:(\\)\\)+([^\\]|$)/g, '$1$2$3'
|
799
|
-
);
|
800
|
-
}
|
801
|
-
}
|
802
|
-
|
803
|
-
def sub(pattern, replace = undefined, &block)
|
1115
|
+
def sub(pattern, replacement = undefined, &block)
|
804
1116
|
%x{
|
805
|
-
if (
|
806
|
-
pattern = #{Opal.coerce_to
|
1117
|
+
if (!pattern.$$is_regexp) {
|
1118
|
+
pattern = #{Opal.coerce_to(`pattern`, String, :to_str)};
|
1119
|
+
pattern = new RegExp(pattern.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'));
|
807
1120
|
}
|
808
1121
|
|
809
|
-
|
810
|
-
if (#{replace.is_a?(Hash)}) {
|
811
|
-
return self.replace(pattern, function(str) {
|
812
|
-
var value = #{replace[str]};
|
1122
|
+
var result = pattern.exec(self);
|
813
1123
|
|
814
|
-
|
815
|
-
|
816
|
-
|
817
|
-
|
818
|
-
|
819
|
-
|
820
|
-
}
|
1124
|
+
if (result === null) {
|
1125
|
+
#{$~ = nil}
|
1126
|
+
return self.toString();
|
1127
|
+
}
|
1128
|
+
|
1129
|
+
#{MatchData.new `pattern`, `result`}
|
821
1130
|
|
822
|
-
|
823
|
-
|
1131
|
+
if (replacement === undefined) {
|
1132
|
+
if (block === nil) {
|
1133
|
+
#{raise ArgumentError, 'wrong number of arguments (1 for 2)'}
|
824
1134
|
}
|
1135
|
+
return self.slice(0, result.index) + block(result[0]) + self.slice(result.index + result[0].length);
|
1136
|
+
}
|
825
1137
|
|
1138
|
+
if (replacement.$$is_hash) {
|
1139
|
+
return self.slice(0, result.index) + #{`replacement`[`result[0]`].to_s} + self.slice(result.index + result[0].length);
|
826
1140
|
}
|
827
|
-
else if (block != null && block !== nil) {
|
828
|
-
return self.replace(pattern, function() {
|
829
|
-
// FIXME: this should be a formal MatchData object with all the goodies
|
830
|
-
var match_data = []
|
831
|
-
for (var i = 0, len = arguments.length; i < len; i++) {
|
832
|
-
var arg = arguments[i];
|
833
|
-
if (arg == undefined) {
|
834
|
-
match_data.push(nil);
|
835
|
-
}
|
836
|
-
else {
|
837
|
-
match_data.push(arg);
|
838
|
-
}
|
839
|
-
}
|
840
1141
|
|
841
|
-
|
842
|
-
var offset = match_data.pop();
|
843
|
-
var match_len = match_data.length;
|
1142
|
+
replacement = #{Opal.coerce_to(`replacement`, String, :to_str)};
|
844
1143
|
|
845
|
-
|
846
|
-
|
847
|
-
|
1144
|
+
replacement = replacement.replace(/([\\]+)([0-9+&`'])/g, function (original, slashes, command) {
|
1145
|
+
if (slashes.length % 2 === 0) {
|
1146
|
+
return original;
|
1147
|
+
}
|
1148
|
+
switch (command) {
|
1149
|
+
case "+":
|
1150
|
+
for (var i = result.length - 1; i > 0; i--) {
|
1151
|
+
if (result[i] !== undefined) {
|
1152
|
+
return slashes.slice(1) + result[i];
|
1153
|
+
}
|
848
1154
|
}
|
849
|
-
|
850
|
-
|
851
|
-
|
852
|
-
|
853
|
-
|
854
|
-
|
855
|
-
|
856
|
-
|
1155
|
+
return '';
|
1156
|
+
case "&": return slashes.slice(1) + result[0];
|
1157
|
+
case "`": return slashes.slice(1) + self.slice(0, result.index);
|
1158
|
+
case "'": return slashes.slice(1) + self.slice(result.index + result[0].length);
|
1159
|
+
default: return slashes.slice(1) + (result[command] || '');
|
1160
|
+
}
|
1161
|
+
}).replace(/\\\\/g, '\\');
|
1162
|
+
|
1163
|
+
return self.slice(0, result.index) + replacement + self.slice(result.index + result[0].length);
|
857
1164
|
}
|
858
1165
|
end
|
859
1166
|
|
@@ -864,13 +1171,21 @@ class String
|
|
864
1171
|
|
865
1172
|
def sum(n = 16)
|
866
1173
|
%x{
|
867
|
-
|
1174
|
+
n = #{Opal.coerce_to(`n`, Integer, :to_int)};
|
868
1175
|
|
869
|
-
|
870
|
-
|
1176
|
+
var result = 0,
|
1177
|
+
length = self.length,
|
1178
|
+
i = 0;
|
1179
|
+
|
1180
|
+
for (; i < length; i++) {
|
1181
|
+
result += self.charCodeAt(i);
|
871
1182
|
}
|
872
1183
|
|
873
|
-
|
1184
|
+
if (n <= 0) {
|
1185
|
+
return result;
|
1186
|
+
}
|
1187
|
+
|
1188
|
+
return result & (Math.pow(2, n) - 1);
|
874
1189
|
}
|
875
1190
|
end
|
876
1191
|
|
@@ -909,13 +1224,60 @@ class String
|
|
909
1224
|
|
910
1225
|
def to_i(base = 10)
|
911
1226
|
%x{
|
912
|
-
var result
|
1227
|
+
var result,
|
1228
|
+
string = self.toLowerCase(),
|
1229
|
+
radix = #{Opal.coerce_to(`base`, Integer, :to_int)};
|
1230
|
+
|
1231
|
+
if (radix === 1 || radix < 0 || radix > 36) {
|
1232
|
+
#{raise ArgumentError, "invalid radix #{`radix`}"}
|
1233
|
+
}
|
913
1234
|
|
914
|
-
if (
|
1235
|
+
if (/^\s*_/.test(string)) {
|
915
1236
|
return 0;
|
916
1237
|
}
|
917
1238
|
|
918
|
-
|
1239
|
+
string = string.replace(/^(\s*[+-]?)(0[bodx]?)(.+)$/, function (original, head, flag, tail) {
|
1240
|
+
switch (tail.charAt(0)) {
|
1241
|
+
case '+':
|
1242
|
+
case '-':
|
1243
|
+
return original;
|
1244
|
+
case '0':
|
1245
|
+
if (tail.charAt(1) === 'x' && flag === '0x' && (radix === 0 || radix === 16)) {
|
1246
|
+
return original;
|
1247
|
+
}
|
1248
|
+
}
|
1249
|
+
switch (flag) {
|
1250
|
+
case '0b':
|
1251
|
+
if (radix === 0 || radix === 2) {
|
1252
|
+
radix = 2;
|
1253
|
+
return head + tail;
|
1254
|
+
}
|
1255
|
+
break;
|
1256
|
+
case '0':
|
1257
|
+
case '0o':
|
1258
|
+
if (radix === 0 || radix === 8) {
|
1259
|
+
radix = 8;
|
1260
|
+
return head + tail;
|
1261
|
+
}
|
1262
|
+
break;
|
1263
|
+
case '0d':
|
1264
|
+
if (radix === 0 || radix === 10) {
|
1265
|
+
radix = 10;
|
1266
|
+
return head + tail;
|
1267
|
+
}
|
1268
|
+
break;
|
1269
|
+
case '0x':
|
1270
|
+
if (radix === 0 || radix === 16) {
|
1271
|
+
radix = 16;
|
1272
|
+
return head + tail;
|
1273
|
+
}
|
1274
|
+
break;
|
1275
|
+
}
|
1276
|
+
return original
|
1277
|
+
});
|
1278
|
+
|
1279
|
+
result = parseInt(string.replace(/_(?!_)/g, ''), radix);
|
1280
|
+
return isNaN(result) ? 0 : result;
|
919
1281
|
}
|
920
1282
|
end
|
921
1283
|
|
@@ -939,6 +1301,8 @@ class String
|
|
939
1301
|
alias to_sym intern
|
940
1302
|
|
941
1303
|
def tr(from, to)
|
1304
|
+
from = Opal.coerce_to(from, String, :to_str).to_s
|
1305
|
+
to = Opal.coerce_to(to, String, :to_str).to_s
|
942
1306
|
%x{
|
943
1307
|
if (from.length == 0 || from === to) {
|
944
1308
|
return self;
|
@@ -952,7 +1316,7 @@ class String
|
|
952
1316
|
|
953
1317
|
var inverse = false;
|
954
1318
|
var global_sub = null;
|
955
|
-
if (from_chars[0] === '^') {
|
1319
|
+
if (from_chars[0] === '^' && from_chars.length > 1) {
|
956
1320
|
inverse = true;
|
957
1321
|
from_chars.shift();
|
958
1322
|
global_sub = to_chars[to_length - 1]
|
@@ -981,9 +1345,12 @@ class String
|
|
981
1345
|
}
|
982
1346
|
}
|
983
1347
|
else if (in_range) {
|
984
|
-
var start = last_from.charCodeAt(0)
|
1348
|
+
var start = last_from.charCodeAt(0);
|
985
1349
|
var end = ch.charCodeAt(0);
|
986
|
-
|
1350
|
+
if (start > end) {
|
1351
|
+
#{raise ArgumentError, "invalid range \"#{`String.fromCharCode(start)`}-#{`String.fromCharCode(end)`}\" in string transliteration"}
|
1352
|
+
}
|
1353
|
+
for (var c = start + 1; c < end; c++) {
|
987
1354
|
from_chars_expanded.push(String.fromCharCode(c));
|
988
1355
|
}
|
989
1356
|
from_chars_expanded.push(ch);
|
@@ -1027,9 +1394,12 @@ class String
|
|
1027
1394
|
}
|
1028
1395
|
}
|
1029
1396
|
else if (in_range) {
|
1030
|
-
var start = last_from.charCodeAt(0)
|
1397
|
+
var start = last_from.charCodeAt(0);
|
1031
1398
|
var end = ch.charCodeAt(0);
|
1032
|
-
|
1399
|
+
if (start > end) {
|
1400
|
+
#{raise ArgumentError, "invalid range \"#{`String.fromCharCode(start)`}-#{`String.fromCharCode(end)`}\" in string transliteration"}
|
1401
|
+
}
|
1402
|
+
for (var c = start + 1; c < end; c++) {
|
1033
1403
|
to_chars_expanded.push(String.fromCharCode(c));
|
1034
1404
|
}
|
1035
1405
|
to_chars_expanded.push(ch);
|
@@ -1076,6 +1446,8 @@ class String
|
|
1076
1446
|
alias tr! <<
|
1077
1447
|
|
1078
1448
|
def tr_s(from, to)
|
1449
|
+
from = Opal.coerce_to(from, String, :to_str).to_s
|
1450
|
+
to = Opal.coerce_to(to, String, :to_str).to_s
|
1079
1451
|
%x{
|
1080
1452
|
if (from.length == 0) {
|
1081
1453
|
return self;
|
@@ -1089,7 +1461,7 @@ class String
|
|
1089
1461
|
|
1090
1462
|
var inverse = false;
|
1091
1463
|
var global_sub = null;
|
1092
|
-
if (from_chars[0] === '^') {
|
1464
|
+
if (from_chars[0] === '^' && from_chars.length > 1) {
|
1093
1465
|
inverse = true;
|
1094
1466
|
from_chars.shift();
|
1095
1467
|
global_sub = to_chars[to_length - 1]
|
@@ -1118,9 +1490,12 @@ class String
|
|
1118
1490
|
}
|
1119
1491
|
}
|
1120
1492
|
else if (in_range) {
|
1121
|
-
var start = last_from.charCodeAt(0)
|
1493
|
+
var start = last_from.charCodeAt(0);
|
1122
1494
|
var end = ch.charCodeAt(0);
|
1123
|
-
|
1495
|
+
if (start > end) {
|
1496
|
+
#{raise ArgumentError, "invalid range \"#{`String.fromCharCode(start)`}-#{`String.fromCharCode(end)`}\" in string transliteration"}
|
1497
|
+
}
|
1498
|
+
for (var c = start + 1; c < end; c++) {
|
1124
1499
|
from_chars_expanded.push(String.fromCharCode(c));
|
1125
1500
|
}
|
1126
1501
|
from_chars_expanded.push(ch);
|
@@ -1164,9 +1539,12 @@ class String
|
|
1164
1539
|
}
|
1165
1540
|
}
|
1166
1541
|
else if (in_range) {
|
1167
|
-
var start = last_from.charCodeAt(0)
|
1542
|
+
var start = last_from.charCodeAt(0);
|
1168
1543
|
var end = ch.charCodeAt(0);
|
1169
|
-
|
1544
|
+
if (start > end) {
|
1545
|
+
#{raise ArgumentError, "invalid range \"#{`String.fromCharCode(start)`}-#{`String.fromCharCode(end)`}\" in string transliteration"}
|
1546
|
+
}
|
1547
|
+
for (var c = start + 1; c < end; c++) {
|
1170
1548
|
to_chars_expanded.push(String.fromCharCode(c));
|
1171
1549
|
}
|
1172
1550
|
to_chars_expanded.push(ch);
|
@@ -1243,6 +1621,139 @@ class String
|
|
1243
1621
|
def frozen?
|
1244
1622
|
true
|
1245
1623
|
end
|
1624
|
+
|
1625
|
+
def upto(stop, excl = false, &block)
|
1626
|
+
return enum_for :upto, stop, excl unless block_given?
|
1627
|
+
stop = Opal.coerce_to(stop, String, :to_str)
|
1628
|
+
%x{
|
1629
|
+
var a, b, s = self.toString();
|
1630
|
+
|
1631
|
+
if (s.length === 1 && stop.length === 1) {
|
1632
|
+
|
1633
|
+
a = s.charCodeAt(0);
|
1634
|
+
b = stop.charCodeAt(0);
|
1635
|
+
|
1636
|
+
while (a <= b) {
|
1637
|
+
if (excl && a === b) {
|
1638
|
+
break;
|
1639
|
+
}
|
1640
|
+
block(String.fromCharCode(a));
|
1641
|
+
a += 1;
|
1642
|
+
}
|
1643
|
+
|
1644
|
+
} else if (parseInt(s).toString() === s && parseInt(stop).toString() === stop) {
|
1645
|
+
|
1646
|
+
a = parseInt(s);
|
1647
|
+
b = parseInt(stop);
|
1648
|
+
|
1649
|
+
while (a <= b) {
|
1650
|
+
if (excl && a === b) {
|
1651
|
+
break;
|
1652
|
+
}
|
1653
|
+
block(a.toString());
|
1654
|
+
a += 1;
|
1655
|
+
}
|
1656
|
+
|
1657
|
+
} else {
|
1658
|
+
|
1659
|
+
while (s.length <= stop.length && s <= stop) {
|
1660
|
+
if (excl && s === stop) {
|
1661
|
+
break;
|
1662
|
+
}
|
1663
|
+
block(s);
|
1664
|
+
s = #{`s`.succ};
|
1665
|
+
}
|
1666
|
+
|
1667
|
+
}
|
1668
|
+
return self;
|
1669
|
+
}
|
1670
|
+
end
|
1671
|
+
|
1672
|
+
%x{
|
1673
|
+
function char_class_from_char_sets(sets) {
|
1674
|
+
function explode_sequences_in_character_set(set) {
|
1675
|
+
var result = '',
|
1676
|
+
i, len = set.length,
|
1677
|
+
curr_char,
|
1678
|
+
skip_next_dash,
|
1679
|
+
char_code_from,
|
1680
|
+
char_code_upto,
|
1681
|
+
char_code;
|
1682
|
+
for (i = 0; i < len; i++) {
|
1683
|
+
curr_char = set.charAt(i);
|
1684
|
+
if (curr_char === '-' && i > 0 && i < (len - 1) && !skip_next_dash) {
|
1685
|
+
char_code_from = set.charCodeAt(i - 1);
|
1686
|
+
char_code_upto = set.charCodeAt(i + 1);
|
1687
|
+
if (char_code_from > char_code_upto) {
|
1688
|
+
#{raise ArgumentError, "invalid range \"#{`char_code_from`}-#{`char_code_upto`}\" in string transliteration"}
|
1689
|
+
}
|
1690
|
+
for (char_code = char_code_from + 1; char_code < char_code_upto + 1; char_code++) {
|
1691
|
+
result += String.fromCharCode(char_code);
|
1692
|
+
}
|
1693
|
+
skip_next_dash = true;
|
1694
|
+
i++;
|
1695
|
+
} else {
|
1696
|
+
skip_next_dash = (curr_char === '\\');
|
1697
|
+
result += curr_char;
|
1698
|
+
}
|
1699
|
+
}
|
1700
|
+
return result;
|
1701
|
+
}
|
1702
|
+
|
1703
|
+
function intersection(setA, setB) {
|
1704
|
+
if (setA.length === 0) {
|
1705
|
+
return setB;
|
1706
|
+
}
|
1707
|
+
var result = '',
|
1708
|
+
i, len = setA.length,
|
1709
|
+
chr;
|
1710
|
+
for (i = 0; i < len; i++) {
|
1711
|
+
chr = setA.charAt(i);
|
1712
|
+
if (setB.indexOf(chr) !== -1) {
|
1713
|
+
result += chr;
|
1714
|
+
}
|
1715
|
+
}
|
1716
|
+
return result;
|
1717
|
+
}
|
1718
|
+
|
1719
|
+
var i, len, set, neg, chr, tmp,
|
1720
|
+
pos_intersection = '',
|
1721
|
+
neg_intersection = '';
|
1722
|
+
|
1723
|
+
for (i = 0, len = sets.length; i < len; i++) {
|
1724
|
+
set = #{Opal.coerce_to(`sets[i]`, String, :to_str)};
|
1725
|
+
neg = (set.charAt(0) === '^' && set.length > 1);
|
1726
|
+
set = explode_sequences_in_character_set(neg ? set.slice(1) : set);
|
1727
|
+
if (neg) {
|
1728
|
+
neg_intersection = intersection(neg_intersection, set);
|
1729
|
+
} else {
|
1730
|
+
pos_intersection = intersection(pos_intersection, set);
|
1731
|
+
}
|
1732
|
+
}
|
1733
|
+
|
1734
|
+
if (pos_intersection.length > 0 && neg_intersection.length > 0) {
|
1735
|
+
tmp = '';
|
1736
|
+
for (i = 0, len = pos_intersection.length; i < len; i++) {
|
1737
|
+
chr = pos_intersection.charAt(i);
|
1738
|
+
if (neg_intersection.indexOf(chr) === -1) {
|
1739
|
+
tmp += chr;
|
1740
|
+
}
|
1741
|
+
}
|
1742
|
+
pos_intersection = tmp;
|
1743
|
+
neg_intersection = '';
|
1744
|
+
}
|
1745
|
+
|
1746
|
+
if (pos_intersection.length > 0) {
|
1747
|
+
return '[' + #{Regexp.escape(`pos_intersection`)} + ']';
|
1748
|
+
}
|
1749
|
+
|
1750
|
+
if (neg_intersection.length > 0) {
|
1751
|
+
return '[^' + #{Regexp.escape(`neg_intersection`)} + ']';
|
1752
|
+
}
|
1753
|
+
|
1754
|
+
return null;
|
1755
|
+
}
|
1756
|
+
}
|
1246
1757
|
end
|
1247
1758
|
|
1248
1759
|
Symbol = String
|