13514
|
1 /*
|
|
2 * Copyright (c) 2013 Oracle and/or its affiliates. All rights reserved. This
|
|
3 * code is released under a tri EPL/GPL/LGPL license. You can use it,
|
|
4 * redistribute it and/or modify it under the terms of the:
|
|
5 *
|
|
6 * Eclipse Public License version 1.0
|
|
7 * GNU General Public License version 2
|
|
8 * GNU Lesser General Public License version 2.1
|
|
9 */
|
|
10 package com.oracle.truffle.ruby.nodes.core;
|
|
11
|
|
12 import java.util.*;
|
|
13 import java.util.regex.*;
|
|
14
|
|
15 import com.oracle.truffle.api.*;
|
|
16 import com.oracle.truffle.api.dsl.*;
|
|
17 import com.oracle.truffle.api.frame.*;
|
|
18 import com.oracle.truffle.ruby.runtime.*;
|
|
19 import com.oracle.truffle.ruby.runtime.core.*;
|
|
20 import com.oracle.truffle.ruby.runtime.core.array.*;
|
|
21
|
|
22 @CoreClass(name = "String")
|
|
23 public abstract class StringNodes {
|
|
24
|
|
25 @CoreMethod(names = "+", minArgs = 1, maxArgs = 1)
|
|
26 public abstract static class AddNode extends CoreMethodNode {
|
|
27
|
|
28 public AddNode(RubyContext context, SourceSection sourceSection) {
|
|
29 super(context, sourceSection);
|
|
30 }
|
|
31
|
|
32 public AddNode(AddNode prev) {
|
|
33 super(prev);
|
|
34 }
|
|
35
|
|
36 @Specialization
|
|
37 public RubyString add(RubyString a, RubyString b) {
|
|
38 return new RubyString(a.getRubyClass().getContext().getCoreLibrary().getStringClass(), a.toString() + b.toString());
|
|
39 }
|
|
40 }
|
|
41
|
|
42 @CoreMethod(names = {"==", "==="}, minArgs = 1, maxArgs = 1)
|
|
43 public abstract static class EqualNode extends CoreMethodNode {
|
|
44
|
|
45 public EqualNode(RubyContext context, SourceSection sourceSection) {
|
|
46 super(context, sourceSection);
|
|
47 }
|
|
48
|
|
49 public EqualNode(EqualNode prev) {
|
|
50 super(prev);
|
|
51 }
|
|
52
|
|
53 @Specialization
|
|
54 public boolean equal(@SuppressWarnings("unused") RubyString a, @SuppressWarnings("unused") NilPlaceholder b) {
|
|
55 return false;
|
|
56 }
|
|
57
|
|
58 @Specialization
|
|
59 public boolean equal(RubyString a, RubyString b) {
|
|
60 return a.toString().equals(b.toString());
|
|
61 }
|
|
62 }
|
|
63
|
|
64 @CoreMethod(names = "!=", minArgs = 1, maxArgs = 1)
|
|
65 public abstract static class NotEqualNode extends CoreMethodNode {
|
|
66
|
|
67 public NotEqualNode(RubyContext context, SourceSection sourceSection) {
|
|
68 super(context, sourceSection);
|
|
69 }
|
|
70
|
|
71 public NotEqualNode(NotEqualNode prev) {
|
|
72 super(prev);
|
|
73 }
|
|
74
|
|
75 @Specialization
|
|
76 public boolean equal(@SuppressWarnings("unused") RubyString a, @SuppressWarnings("unused") NilPlaceholder b) {
|
|
77 return true;
|
|
78 }
|
|
79
|
|
80 @Specialization
|
|
81 public boolean notEqual(RubyString a, RubyString b) {
|
|
82 return !a.toString().equals(b.toString());
|
|
83 }
|
|
84
|
|
85 }
|
|
86
|
|
87 @CoreMethod(names = "<=>", minArgs = 1, maxArgs = 1)
|
|
88 public abstract static class CompareNode extends CoreMethodNode {
|
|
89
|
|
90 public CompareNode(RubyContext context, SourceSection sourceSection) {
|
|
91 super(context, sourceSection);
|
|
92 }
|
|
93
|
|
94 public CompareNode(CompareNode prev) {
|
|
95 super(prev);
|
|
96 }
|
|
97
|
|
98 @Specialization
|
|
99 public int compare(RubyString a, RubyString b) {
|
|
100 return a.toString().compareTo(b.toString());
|
|
101 }
|
|
102 }
|
|
103
|
|
104 @CoreMethod(names = "<<", minArgs = 1, maxArgs = 1)
|
|
105 public abstract static class ConcatNode extends CoreMethodNode {
|
|
106
|
|
107 public ConcatNode(RubyContext context, SourceSection sourceSection) {
|
|
108 super(context, sourceSection);
|
|
109 }
|
|
110
|
|
111 public ConcatNode(ConcatNode prev) {
|
|
112 super(prev);
|
|
113 }
|
|
114
|
|
115 @Specialization
|
|
116 public RubyString concat(RubyString string, RubyString other) {
|
|
117 string.replace(string.toString() + other.toString());
|
|
118 return string;
|
|
119 }
|
|
120 }
|
|
121
|
|
122 @CoreMethod(names = "%", minArgs = 1, maxArgs = 1, isSplatted = true)
|
|
123 public abstract static class FormatNode extends CoreMethodNode {
|
|
124
|
|
125 public FormatNode(RubyContext context, SourceSection sourceSection) {
|
|
126 super(context, sourceSection);
|
|
127 }
|
|
128
|
|
129 public FormatNode(FormatNode prev) {
|
|
130 super(prev);
|
|
131 }
|
|
132
|
|
133 @Specialization
|
|
134 public RubyString format(RubyString format, Object[] args) {
|
|
135 final RubyContext context = getContext();
|
|
136
|
|
137 if (args.length == 1 && args[0] instanceof RubyArray) {
|
|
138 return context.makeString(StringFormatter.format(format.toString(), ((RubyArray) args[0]).asList()));
|
|
139 } else {
|
|
140 return context.makeString(StringFormatter.format(format.toString(), Arrays.asList(args)));
|
|
141 }
|
|
142 }
|
|
143 }
|
|
144
|
|
145 @CoreMethod(names = "[]", minArgs = 1, maxArgs = 2, isSplatted = true)
|
|
146 public abstract static class GetIndexNode extends CoreMethodNode {
|
|
147
|
|
148 public GetIndexNode(RubyContext context, SourceSection sourceSection) {
|
|
149 super(context, sourceSection);
|
|
150 }
|
|
151
|
|
152 public GetIndexNode(GetIndexNode prev) {
|
|
153 super(prev);
|
|
154 }
|
|
155
|
|
156 @Specialization
|
|
157 public Object getIndex(RubyString string, Object[] args) {
|
|
158 return RubyString.getIndex(getContext(), string.toString(), args);
|
|
159 }
|
|
160 }
|
|
161
|
|
162 @CoreMethod(names = "=~", minArgs = 1, maxArgs = 1)
|
|
163 public abstract static class MatchOperatorNode extends CoreMethodNode {
|
|
164
|
|
165 public MatchOperatorNode(RubyContext context, SourceSection sourceSection) {
|
|
166 super(context, sourceSection);
|
|
167 }
|
|
168
|
|
169 public MatchOperatorNode(MatchOperatorNode prev) {
|
|
170 super(prev);
|
|
171 }
|
|
172
|
|
173 @Specialization
|
|
174 public Object match(VirtualFrame frame, RubyString string, RubyRegexp regexp) {
|
|
175 return regexp.matchOperator(frame, string.toString());
|
|
176 }
|
|
177 }
|
|
178
|
|
179 @CoreMethod(names = "chomp", maxArgs = 0)
|
|
180 public abstract static class ChompNode extends CoreMethodNode {
|
|
181
|
|
182 public ChompNode(RubyContext context, SourceSection sourceSection) {
|
|
183 super(context, sourceSection);
|
|
184 }
|
|
185
|
|
186 public ChompNode(ChompNode prev) {
|
|
187 super(prev);
|
|
188 }
|
|
189
|
|
190 @Specialization
|
|
191 public RubyString chomp(RubyString string) {
|
|
192 return string.getRubyClass().getContext().makeString(string.toString().trim());
|
|
193 }
|
|
194 }
|
|
195
|
|
196 @CoreMethod(names = "chomp!", maxArgs = 0)
|
|
197 public abstract static class ChompBangNode extends CoreMethodNode {
|
|
198
|
|
199 public ChompBangNode(RubyContext context, SourceSection sourceSection) {
|
|
200 super(context, sourceSection);
|
|
201 }
|
|
202
|
|
203 public ChompBangNode(ChompBangNode prev) {
|
|
204 super(prev);
|
|
205 }
|
|
206
|
|
207 @Specialization
|
|
208 public RubyString chompBang(RubyString string) {
|
|
209 string.replace(string.toString().trim());
|
|
210 return string;
|
|
211 }
|
|
212 }
|
|
213
|
|
214 @CoreMethod(names = "downcase", maxArgs = 0)
|
|
215 public abstract static class DowncaseNode extends CoreMethodNode {
|
|
216
|
|
217 public DowncaseNode(RubyContext context, SourceSection sourceSection) {
|
|
218 super(context, sourceSection);
|
|
219 }
|
|
220
|
|
221 public DowncaseNode(DowncaseNode prev) {
|
|
222 super(prev);
|
|
223 }
|
|
224
|
|
225 @Specialization
|
|
226 public RubyString downcase(RubyString string) {
|
|
227 return string.getRubyClass().getContext().makeString(string.toString().toLowerCase());
|
|
228 }
|
|
229 }
|
|
230
|
|
231 @CoreMethod(names = "downcase!", maxArgs = 0)
|
|
232 public abstract static class DowncaseBangNode extends CoreMethodNode {
|
|
233
|
|
234 public DowncaseBangNode(RubyContext context, SourceSection sourceSection) {
|
|
235 super(context, sourceSection);
|
|
236 }
|
|
237
|
|
238 public DowncaseBangNode(DowncaseBangNode prev) {
|
|
239 super(prev);
|
|
240 }
|
|
241
|
|
242 @Specialization
|
|
243 public RubyString downcase(RubyString string) {
|
|
244 string.replace(string.toString().toLowerCase());
|
|
245 return string;
|
|
246 }
|
|
247 }
|
|
248
|
|
249 @CoreMethod(names = "empty?", maxArgs = 0)
|
|
250 public abstract static class EmptyNode extends CoreMethodNode {
|
|
251
|
|
252 public EmptyNode(RubyContext context, SourceSection sourceSection) {
|
|
253 super(context, sourceSection);
|
|
254 }
|
|
255
|
|
256 public EmptyNode(EmptyNode prev) {
|
|
257 super(prev);
|
|
258 }
|
|
259
|
|
260 @Specialization
|
|
261 public boolean empty(RubyString string) {
|
|
262 return string.toString().isEmpty();
|
|
263 }
|
|
264 }
|
|
265
|
|
266 @CoreMethod(names = "end_with?", minArgs = 1, maxArgs = 1)
|
|
267 public abstract static class EndWithNode extends CoreMethodNode {
|
|
268
|
|
269 public EndWithNode(RubyContext context, SourceSection sourceSection) {
|
|
270 super(context, sourceSection);
|
|
271 }
|
|
272
|
|
273 public EndWithNode(EndWithNode prev) {
|
|
274 super(prev);
|
|
275 }
|
|
276
|
|
277 @Specialization
|
|
278 public boolean endWith(RubyString string, RubyString b) {
|
|
279 return string.toString().endsWith(b.toString());
|
|
280 }
|
|
281 }
|
|
282
|
|
283 @CoreMethod(names = "gsub", minArgs = 2, maxArgs = 2)
|
|
284 public abstract static class GsubNode extends CoreMethodNode {
|
|
285
|
|
286 public GsubNode(RubyContext context, SourceSection sourceSection) {
|
|
287 super(context, sourceSection);
|
|
288 }
|
|
289
|
|
290 public GsubNode(GsubNode prev) {
|
|
291 super(prev);
|
|
292 }
|
|
293
|
|
294 @Specialization
|
|
295 public RubyString gsub(RubyString string, RubyString regexpString, RubyString replacement) {
|
|
296 final RubyRegexp regexp = new RubyRegexp(getContext().getCoreLibrary().getRegexpClass(), regexpString.toString());
|
|
297 return gsub(string, regexp, replacement);
|
|
298 }
|
|
299
|
|
300 @Specialization
|
|
301 public RubyString gsub(RubyString string, RubyRegexp regexp, RubyString replacement) {
|
|
302 return getContext().makeString(regexp.getPattern().matcher(string.toString()).replaceAll(replacement.toString()));
|
|
303 }
|
|
304 }
|
|
305
|
|
306 @CoreMethod(names = "inspect", maxArgs = 0)
|
|
307 public abstract static class InpsectNode extends CoreMethodNode {
|
|
308
|
|
309 public InpsectNode(RubyContext context, SourceSection sourceSection) {
|
|
310 super(context, sourceSection);
|
|
311 }
|
|
312
|
|
313 public InpsectNode(InpsectNode prev) {
|
|
314 super(prev);
|
|
315 }
|
|
316
|
|
317 @Specialization
|
|
318 public RubyString inspect(RubyString string) {
|
|
319 return getContext().makeString("\"" + string.toString().replace("\\", "\\\\").replace("\"", "\\\"") + "\"");
|
|
320 }
|
|
321 }
|
|
322
|
|
323 @CoreMethod(names = "ljust", minArgs = 1, maxArgs = 2)
|
|
324 public abstract static class LjustNode extends CoreMethodNode {
|
|
325
|
|
326 public LjustNode(RubyContext context, SourceSection sourceSection) {
|
|
327 super(context, sourceSection);
|
|
328 }
|
|
329
|
|
330 public LjustNode(LjustNode prev) {
|
|
331 super(prev);
|
|
332 }
|
|
333
|
|
334 @Specialization
|
|
335 public RubyString ljust(RubyString string, int length, @SuppressWarnings("unused") UndefinedPlaceholder padding) {
|
|
336 return getContext().makeString(RubyString.ljust(string.toString(), length, " "));
|
|
337 }
|
|
338
|
|
339 @Specialization
|
|
340 public RubyString ljust(RubyString string, int length, RubyString padding) {
|
|
341 return getContext().makeString(RubyString.ljust(string.toString(), length, padding.toString()));
|
|
342 }
|
|
343
|
|
344 }
|
|
345
|
|
346 @CoreMethod(names = "size", maxArgs = 0)
|
|
347 public abstract static class SizeNode extends CoreMethodNode {
|
|
348
|
|
349 public SizeNode(RubyContext context, SourceSection sourceSection) {
|
|
350 super(context, sourceSection);
|
|
351 }
|
|
352
|
|
353 public SizeNode(SizeNode prev) {
|
|
354 super(prev);
|
|
355 }
|
|
356
|
|
357 @Specialization
|
|
358 public int size(RubyString string) {
|
|
359 return string.toString().length();
|
|
360 }
|
|
361 }
|
|
362
|
|
363 @CoreMethod(names = "match", minArgs = 1, maxArgs = 1)
|
|
364 public abstract static class MatchNode extends CoreMethodNode {
|
|
365
|
|
366 public MatchNode(RubyContext context, SourceSection sourceSection) {
|
|
367 super(context, sourceSection);
|
|
368 }
|
|
369
|
|
370 public MatchNode(MatchNode prev) {
|
|
371 super(prev);
|
|
372 }
|
|
373
|
|
374 @Specialization
|
|
375 public Object match(RubyString string, RubyString regexpString) {
|
|
376 final RubyRegexp regexp = new RubyRegexp(getContext().getCoreLibrary().getRegexpClass(), regexpString.toString());
|
|
377 return regexp.match(string.toString());
|
|
378 }
|
|
379
|
|
380 @Specialization
|
|
381 public Object match(RubyString string, RubyRegexp regexp) {
|
|
382 return regexp.match(string.toString());
|
|
383 }
|
|
384 }
|
|
385
|
|
386 @CoreMethod(names = "rjust", minArgs = 1, maxArgs = 2)
|
|
387 public abstract static class RjustNode extends CoreMethodNode {
|
|
388
|
|
389 public RjustNode(RubyContext context, SourceSection sourceSection) {
|
|
390 super(context, sourceSection);
|
|
391 }
|
|
392
|
|
393 public RjustNode(RjustNode prev) {
|
|
394 super(prev);
|
|
395 }
|
|
396
|
|
397 @Specialization
|
|
398 public RubyString rjust(RubyString string, int length, @SuppressWarnings("unused") UndefinedPlaceholder padding) {
|
|
399 return getContext().makeString(RubyString.rjust(string.toString(), length, " "));
|
|
400 }
|
|
401
|
|
402 @Specialization
|
|
403 public RubyString rjust(RubyString string, int length, RubyString padding) {
|
|
404 return getContext().makeString(RubyString.rjust(string.toString(), length, padding.toString()));
|
|
405 }
|
|
406
|
|
407 }
|
|
408
|
|
409 @CoreMethod(names = "scan", minArgs = 1, maxArgs = 1)
|
|
410 public abstract static class ScanNode extends CoreMethodNode {
|
|
411
|
|
412 public ScanNode(RubyContext context, SourceSection sourceSection) {
|
|
413 super(context, sourceSection);
|
|
414 }
|
|
415
|
|
416 public ScanNode(ScanNode prev) {
|
|
417 super(prev);
|
|
418 }
|
|
419
|
|
420 @Specialization
|
|
421 public RubyArray scan(RubyString string, RubyString regexp) {
|
|
422 return RubyString.scan(getContext(), string.toString(), Pattern.compile(regexp.toString()));
|
|
423 }
|
|
424
|
|
425 @Specialization
|
|
426 public RubyArray scan(RubyString string, RubyRegexp regexp) {
|
|
427 return RubyString.scan(getContext(), string.toString(), regexp.getPattern());
|
|
428 }
|
|
429
|
|
430 }
|
|
431
|
|
432 @CoreMethod(names = "split", minArgs = 1, maxArgs = 1)
|
|
433 public abstract static class SplitNode extends CoreMethodNode {
|
|
434
|
|
435 public SplitNode(RubyContext context, SourceSection sourceSection) {
|
|
436 super(context, sourceSection);
|
|
437 }
|
|
438
|
|
439 public SplitNode(SplitNode prev) {
|
|
440 super(prev);
|
|
441 }
|
|
442
|
|
443 @Specialization
|
|
444 public RubyArray split(RubyString string, RubyString sep) {
|
|
445 final RubyContext context = getContext();
|
|
446
|
|
447 final String[] components = string.toString().split(Pattern.quote(sep.toString()));
|
|
448
|
|
449 final Object[] objects = new Object[components.length];
|
|
450
|
|
451 for (int n = 0; n < objects.length; n++) {
|
|
452 objects[n] = context.makeString(components[n]);
|
|
453 }
|
|
454
|
|
455 return RubyArray.specializedFromObjects(context.getCoreLibrary().getArrayClass(), objects);
|
|
456 }
|
|
457
|
|
458 @Specialization
|
|
459 public RubyArray split(RubyString string, RubyRegexp sep) {
|
|
460 final RubyContext context = getContext();
|
|
461
|
|
462 final String[] components = string.toString().split(sep.getPattern().pattern());
|
|
463
|
|
464 final Object[] objects = new Object[components.length];
|
|
465
|
|
466 for (int n = 0; n < objects.length; n++) {
|
|
467 objects[n] = context.makeString(components[n]);
|
|
468 }
|
|
469
|
|
470 return RubyArray.specializedFromObjects(context.getCoreLibrary().getArrayClass(), objects);
|
|
471 }
|
|
472 }
|
|
473
|
|
474 @CoreMethod(names = "start_with?", minArgs = 1, maxArgs = 1)
|
|
475 public abstract static class StartWithNode extends CoreMethodNode {
|
|
476
|
|
477 public StartWithNode(RubyContext context, SourceSection sourceSection) {
|
|
478 super(context, sourceSection);
|
|
479 }
|
|
480
|
|
481 public StartWithNode(StartWithNode prev) {
|
|
482 super(prev);
|
|
483 }
|
|
484
|
|
485 @Specialization
|
|
486 public boolean endWith(RubyString string, RubyString b) {
|
|
487 return string.toString().startsWith(b.toString());
|
|
488 }
|
|
489 }
|
|
490
|
|
491 @CoreMethod(names = "to_f", maxArgs = 0)
|
|
492 public abstract static class ToFNode extends CoreMethodNode {
|
|
493
|
|
494 public ToFNode(RubyContext context, SourceSection sourceSection) {
|
|
495 super(context, sourceSection);
|
|
496 }
|
|
497
|
|
498 public ToFNode(ToFNode prev) {
|
|
499 super(prev);
|
|
500 }
|
|
501
|
|
502 @Specialization
|
|
503 public double toF(RubyString string) {
|
|
504 return Double.parseDouble(string.toString());
|
|
505 }
|
|
506 }
|
|
507
|
|
508 @CoreMethod(names = "to_i", maxArgs = 0)
|
|
509 public abstract static class ToINode extends CoreMethodNode {
|
|
510
|
|
511 public ToINode(RubyContext context, SourceSection sourceSection) {
|
|
512 super(context, sourceSection);
|
|
513 }
|
|
514
|
|
515 public ToINode(ToINode prev) {
|
|
516 super(prev);
|
|
517 }
|
|
518
|
|
519 @Specialization
|
|
520 public Object toI(RubyString string) {
|
|
521 return string.toInteger();
|
|
522 }
|
|
523 }
|
|
524
|
|
525 @CoreMethod(names = "to_s", maxArgs = 0)
|
|
526 public abstract static class ToSNode extends CoreMethodNode {
|
|
527
|
|
528 public ToSNode(RubyContext context, SourceSection sourceSection) {
|
|
529 super(context, sourceSection);
|
|
530 }
|
|
531
|
|
532 public ToSNode(ToSNode prev) {
|
|
533 super(prev);
|
|
534 }
|
|
535
|
|
536 @Specialization
|
|
537 public RubyString toF(RubyString string) {
|
|
538 return string;
|
|
539 }
|
|
540 }
|
|
541
|
|
542 @CoreMethod(names = {"to_sym", "intern"}, maxArgs = 0)
|
|
543 public abstract static class ToSymNode extends CoreMethodNode {
|
|
544
|
|
545 public ToSymNode(RubyContext context, SourceSection sourceSection) {
|
|
546 super(context, sourceSection);
|
|
547 }
|
|
548
|
|
549 public ToSymNode(ToSymNode prev) {
|
|
550 super(prev);
|
|
551 }
|
|
552
|
|
553 @Specialization
|
|
554 public RubySymbol toSym(RubyString string) {
|
|
555 return new RubySymbol(getContext().getCoreLibrary().getSymbolClass(), string.toString());
|
|
556 }
|
|
557 }
|
|
558
|
|
559 }
|