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.runtime.core;
|
|
11
|
|
12 import java.io.*;
|
|
13 import java.util.*;
|
|
14
|
|
15 import com.oracle.truffle.ruby.runtime.core.array.*;
|
|
16
|
|
17 public class StringFormatter {
|
|
18
|
|
19 public static String format(String format, List<Object> values) {
|
|
20 final ByteArrayOutputStream byteArray = new ByteArrayOutputStream();
|
|
21 final PrintStream printStream = new PrintStream(byteArray);
|
|
22
|
|
23 format(printStream, format, values);
|
|
24
|
|
25 return byteArray.toString();
|
|
26 }
|
|
27
|
|
28 public static void format(PrintStream stream, String format, List<Object> values) {
|
|
29 /*
|
|
30 * See http://www.ruby-doc.org/core-1.9.3/Kernel.html#method-i-sprintf.
|
|
31 *
|
|
32 * At the moment we just do the basics that we need. We will need a proper lexer later on.
|
|
33 * Or better than that we could compile to Truffle nodes if the format string is constant! I
|
|
34 * don't think we can easily translate to Java's format syntax, otherwise JRuby would do
|
|
35 * that and they don't.
|
|
36 */
|
|
37
|
|
38 // I'm not using a for loop, because Checkstyle won't let me modify the control variable
|
|
39
|
|
40 int n = 0;
|
|
41 int v = 0;
|
|
42
|
|
43 while (n < format.length()) {
|
|
44 final char c = format.charAt(n);
|
|
45 n++;
|
|
46
|
|
47 if (c == '%') {
|
|
48 // %[flags][width][.precision]type
|
|
49
|
|
50 final String flagChars = "0";
|
|
51
|
|
52 boolean zeroPad = false;
|
|
53
|
|
54 while (n < format.length() && flagChars.indexOf(format.charAt(n)) != -1) {
|
|
55 switch (format.charAt(n)) {
|
|
56 case '0':
|
|
57 zeroPad = true;
|
|
58 break;
|
|
59 }
|
|
60
|
|
61 n++;
|
|
62 }
|
|
63
|
|
64 int width;
|
|
65
|
|
66 if (n < format.length() && Character.isDigit(format.charAt(n))) {
|
|
67 final int widthStart = n;
|
|
68
|
|
69 while (Character.isDigit(format.charAt(n))) {
|
|
70 n++;
|
|
71 }
|
|
72
|
|
73 width = Integer.parseInt(format.substring(widthStart, n));
|
|
74 } else {
|
|
75 width = 0;
|
|
76 }
|
|
77
|
|
78 int precision;
|
|
79
|
|
80 if (format.charAt(n) == '.') {
|
|
81 n++;
|
|
82
|
|
83 final int precisionStart = n;
|
|
84
|
|
85 while (Character.isDigit(format.charAt(n))) {
|
|
86 n++;
|
|
87 }
|
|
88
|
|
89 precision = Integer.parseInt(format.substring(precisionStart, n));
|
|
90 } else {
|
|
91 precision = 5;
|
|
92 }
|
|
93
|
|
94 final char type = format.charAt(n);
|
|
95 n++;
|
|
96
|
|
97 final StringBuilder formatBuilder = new StringBuilder();
|
|
98
|
|
99 formatBuilder.append("%");
|
|
100
|
|
101 if (width > 0) {
|
|
102 if (zeroPad) {
|
|
103 formatBuilder.append("0");
|
|
104 }
|
|
105
|
|
106 formatBuilder.append(width);
|
|
107 }
|
|
108
|
|
109 switch (type) {
|
|
110 case 'd': {
|
|
111 formatBuilder.append("d");
|
|
112 final int value = GeneralConversions.toFixnum(values.get(v));
|
|
113 stream.printf(formatBuilder.toString(), value);
|
|
114 break;
|
|
115 }
|
|
116
|
|
117 case 'f': {
|
|
118 formatBuilder.append(".");
|
|
119 formatBuilder.append(precision);
|
|
120 formatBuilder.append("f");
|
|
121 final double value = GeneralConversions.toFloat(values.get(v));
|
|
122 stream.printf(formatBuilder.toString(), value);
|
|
123 break;
|
|
124 }
|
|
125
|
|
126 default:
|
|
127 throw new RuntimeException("Kernel#sprintf error");
|
|
128 }
|
|
129
|
|
130 v++;
|
|
131 } else {
|
|
132 stream.print(c);
|
|
133 }
|
|
134 }
|
|
135 }
|
|
136
|
|
137 public static void formatPuts(PrintStream stream, List<Object> args) {
|
|
138 if (args.size() > 0) {
|
|
139 formatPutsInner(stream, args);
|
|
140 } else {
|
|
141 stream.println();
|
|
142 }
|
|
143 }
|
|
144
|
|
145 public static void formatPutsInner(PrintStream stream, List<Object> args) {
|
|
146 if (args.size() > 0) {
|
|
147 for (Object arg : args) {
|
|
148 if (arg instanceof RubyArray) {
|
|
149 final RubyArray array = (RubyArray) arg;
|
|
150 formatPutsInner(stream, array.asList());
|
|
151 } else {
|
|
152 stream.println(arg);
|
|
153 }
|
|
154 }
|
|
155 }
|
|
156 }
|
|
157 }
|