Skip to content
This repository
Newer
Older
100644 335 lines (303 sloc) 13.104 kb
a89a9a62 »
2011-08-27 add Printf.cs
1 using Niecza;
2 using System;
3 using System.Collections.Generic;
4 using System.Diagnostics;
6bc1637e »
2011-08-28 [lib/Printf.cs] now passes 40/44 sprintf tests with 1 skip
5 using System.IO;
6
7
8 public partial class Builtins {
9
10 public static Variable sprintf(Variable[] args) {
11 char[] fmt = args[0].Fetch().mo.mro_raw_Str.Get(args[0]).ToCharArray();
12 List<PrintfFormat> fmtlist = ParseFormatString(fmt);
13 return RenderFormat(fmtlist, args);
14 }
15
16 private enum PrintfDirective {
17 LiteralText,
18 CodePoint,
19 String,
4b9c017c »
2012-07-05 Major refactor for sprintf. Now handles all floating point directives…
20 Int,
6bc1637e »
2011-08-28 [lib/Printf.cs] now passes 40/44 sprintf tests with 1 skip
21 FloatScientific,
22 FloatFixedDecimal,
23 FloatEF,
24 InvokeCode
25 }
26
27 private enum PrintfModifier {
28 None,
29 NativeShort,
30 NativeLong,
31 NativeLongLong,
32 NativeQuad,
33 Complex
34 }
35
36 private struct PrintfFormat {
37 internal int index; // 0 means "next one" (default), 1-indexed
38 internal bool nonNegativeSpacePrefix;
39 internal bool nonNegativePlusPrefix;
40 internal bool leftJustify;
41 internal bool rightJustifyZeroes;
42 internal bool leadingRadix;
43 internal bool vector;
44 internal bool vectorOverride;
45 internal int vectorOverrideIndex; // 1-indexed
46 internal int minimumWidth;
47 internal int minimumWidthIndex; // 1-indexed
48 internal int precision;
49 internal int precisionIndex; // 1-indexed
50 internal PrintfDirective directive;
51 internal PrintfModifier modifier;
4b9c017c »
2012-07-05 Major refactor for sprintf. Now handles all floating point directives…
52 internal uint radix;
53 internal bool upper;
6bc1637e »
2011-08-28 [lib/Printf.cs] now passes 40/44 sprintf tests with 1 skip
54 internal string literaltext;
55 }
56
57 private static List<PrintfFormat> ParseFormatString(char[] fmtstring) {
58 List<PrintfFormat> fmtlist = new List<PrintfFormat>();
59 for (int pos=0; pos < fmtstring.Length; ++pos) {
60 char c = fmtstring[pos];
61 PrintfFormat format = new PrintfFormat();
bb5a7c3e »
2011-09-23 [lib/Printf.cs] implement minimum width for %s, fixes issue #58
62 // We do not have to initialize all these fields, but it's
63 // handy to have them all listed together in one place.
6bc1637e »
2011-08-28 [lib/Printf.cs] now passes 40/44 sprintf tests with 1 skip
64 format.index = 0;
65 format.nonNegativeSpacePrefix = false;
66 format.nonNegativePlusPrefix = false;
67 format.leftJustify = false;
68 format.rightJustifyZeroes = false;
69 format.leadingRadix = false;
70 format.vector = false;
71 format.vectorOverride = false;
72 format.vectorOverrideIndex = 0; // 1-indexed
73 format.minimumWidth = 0;
74 format.minimumWidthIndex = 0; // 1-indexed
75 format.precision = 0;
76 format.precisionIndex = 0; // 1-indexed
77 format.directive = PrintfDirective.String;
78 format.modifier = PrintfModifier.None;
4b9c017c »
2012-07-05 Major refactor for sprintf. Now handles all floating point directives…
79 format.radix = 10;
80 format.upper = false;
6bc1637e »
2011-08-28 [lib/Printf.cs] now passes 40/44 sprintf tests with 1 skip
81 format.literaltext = "";
82 if (c == '%') { // begin format specifier
83 bool continue_parsing_specifier = true;
84 bool parse_minimum_width = true;
85 bool seen_first_digit = false;
86 while (continue_parsing_specifier && ++pos<fmtstring.Length) {
87 switch (c=fmtstring[pos]) {
88 case '0': case '1': case '2': case '3': case '4':
89 case '5': case '6': case '7': case '8': case '9':
90 int digitvalue = c - '0';
91 if (!seen_first_digit && c == '0') {
92 format.rightJustifyZeroes = true;
93 }
94 seen_first_digit = true;
95 if (parse_minimum_width)
96 format.minimumWidth = 10 * format.minimumWidth + digitvalue;
97 else
98 format.precision = 10 * format.precision + digitvalue;
99 break;
100 case '.':
101 parse_minimum_width = false;
102 break;
103 case '-': // negative width
104 format.leftJustify = true;
105 break;
106 case '$': // "%1$" arglist position
107 format.index = format.minimumWidth;
108 format.minimumWidth = 0;
109 break;
110 case '%': // "%%" a literal '%'
111 format.directive = PrintfDirective.LiteralText;
112 format.literaltext = "" + c;
113 continue_parsing_specifier = false;
114 break;
115 case 'b': // unsigned binary integer
4b9c017c »
2012-07-05 Major refactor for sprintf. Now handles all floating point directives…
116 format.directive = PrintfDirective.Int;
117 format.radix = 2;
6bc1637e »
2011-08-28 [lib/Printf.cs] now passes 40/44 sprintf tests with 1 skip
118 continue_parsing_specifier = false;
119 break;
120 case 'C': // Code
121 format.directive = PrintfDirective.InvokeCode;
122 continue_parsing_specifier = false;
123 break;
124 case 'c': // codepoint
125 format.directive = PrintfDirective.CodePoint;
126 continue_parsing_specifier = false;
127 break;
128 case 'd': // decimal
129 case 'i': // integer (alias for decimal)
4b9c017c »
2012-07-05 Major refactor for sprintf. Now handles all floating point directives…
130 case 'u': // integer (alias for decimal)
131 format.directive = PrintfDirective.Int;
132 format.radix = 10;
6bc1637e »
2011-08-28 [lib/Printf.cs] now passes 40/44 sprintf tests with 1 skip
133 continue_parsing_specifier = false;
134 break;
135 case 'E': // scientific uppercase E
4b9c017c »
2012-07-05 Major refactor for sprintf. Now handles all floating point directives…
136 format.directive = PrintfDirective.FloatScientific;
137 format.upper = true;
6bc1637e »
2011-08-28 [lib/Printf.cs] now passes 40/44 sprintf tests with 1 skip
138 continue_parsing_specifier = false;
139 break;
140 case 'e': // scientific lowercase e
141 format.directive = PrintfDirective.FloatScientific;
142 continue_parsing_specifier = false;
143 break;
144 case 'f': // fixed point
e862bf26 »
2012-07-04 Refactor sprintf internals a bit. Probably still needs work on edge c…
145 case 'F': // fixed point synonym
6bc1637e »
2011-08-28 [lib/Printf.cs] now passes 40/44 sprintf tests with 1 skip
146 format.directive = PrintfDirective.FloatFixedDecimal;
147 continue_parsing_specifier = false;
148 break;
e862bf26 »
2012-07-04 Refactor sprintf internals a bit. Probably still needs work on edge c…
149 case 'G': // e or f uppercase E
4b9c017c »
2012-07-05 Major refactor for sprintf. Now handles all floating point directives…
150 format.directive = PrintfDirective.FloatEF;
151 format.upper = true;
e862bf26 »
2012-07-04 Refactor sprintf internals a bit. Probably still needs work on edge c…
152 continue_parsing_specifier = false;
153 break;
154 case 'g': // e or f lowercase e
155 format.directive = PrintfDirective.FloatEF;
156 continue_parsing_specifier = false;
157 break;
6bc1637e »
2011-08-28 [lib/Printf.cs] now passes 40/44 sprintf tests with 1 skip
158 case 'o': // unsigned octal
4b9c017c »
2012-07-05 Major refactor for sprintf. Now handles all floating point directives…
159 format.directive = PrintfDirective.Int;
160 format.radix = 8;
6bc1637e »
2011-08-28 [lib/Printf.cs] now passes 40/44 sprintf tests with 1 skip
161 continue_parsing_specifier = false;
162 break;
163 case 's': // string
164 format.directive = PrintfDirective.String;
165 continue_parsing_specifier = false;
166 break;
167 case 'X': // hex uppercase
4b9c017c »
2012-07-05 Major refactor for sprintf. Now handles all floating point directives…
168 format.directive = PrintfDirective.Int;
169 format.radix = 16;
170 format.upper = true;
6bc1637e »
2011-08-28 [lib/Printf.cs] now passes 40/44 sprintf tests with 1 skip
171 continue_parsing_specifier = false;
172 break;
173 case 'x': // hex lowercase
4b9c017c »
2012-07-05 Major refactor for sprintf. Now handles all floating point directives…
174 format.directive = PrintfDirective.Int;
175 format.radix = 16;
176 format.upper = false;
6bc1637e »
2011-08-28 [lib/Printf.cs] now passes 40/44 sprintf tests with 1 skip
177 continue_parsing_specifier = false;
178 break;
179 default:
5c1cdb15 »
2011-08-28 [lib/Printf.cs] throw exceptions to pass S32-str/sprintf.t dies_ok() …
180 throw new NieczaException("invalid format specifier");
6bc1637e »
2011-08-28 [lib/Printf.cs] now passes 40/44 sprintf tests with 1 skip
181 }
182 }
183 }
184 else { // non '%' characters
185 format.directive = PrintfDirective.LiteralText;
186 format.literaltext = "" + c;
187 while (++pos < fmtstring.Length && (c=fmtstring[pos]) != '%') {
188 format.literaltext += c;
189 }
190 // pos has gone one char too far, but should come back
191 // only if there is a '%' before the end of fmtstring
192 if (pos < fmtstring.Length && (c=fmtstring[pos]) == '%')
193 --pos;
194 }
195 fmtlist.Add(format);
196 }
197 return fmtlist;
198 }
e862bf26 »
2012-07-04 Refactor sprintf internals a bit. Probably still needs work on edge c…
199
4b9c017c »
2012-07-05 Major refactor for sprintf. Now handles all floating point directives…
200 private static string RemoveInitialZero(String s) {
f36d743e »
2012-07-05 Don't delete the leading zero in a hex number if the hex number is ju…
201 if (s.Length > 1 && s[0] == '0') {
4b9c017c »
2012-07-05 Major refactor for sprintf. Now handles all floating point directives…
202 return s.Substring(1);
203 }
204 return s;
205 }
206
e862bf26 »
2012-07-04 Refactor sprintf internals a bit. Probably still needs work on edge c…
207 private static string RenderFormat(PrintfFormat format, Variable arg) {
208 int n;
4b9c017c »
2012-07-05 Major refactor for sprintf. Now handles all floating point directives…
209
210 if (format.directive == PrintfDirective.String) {
211 return arg.Fetch().mo.mro_raw_Str.Get(arg);
212 }
213
214 if (format.directive == PrintfDirective.CodePoint) {
215 n = (int) arg.Fetch().mo.mro_raw_Numeric.Get(arg);
216 return "" + (char) n;
217 }
218
219 bool add_minus_sign = false;
220 if (format.directive == PrintfDirective.Int) {
221
222 P6any o1 = arg.Fetch();
223 int r1;
224 P6any n1 = GetNumber(arg, o1, out r1);
225 Variable arg2;
226
227 if (r1 != NR_BIGINT && r1 != NR_FIXINT) {
228 arg2 = Builtins.InvokeMethod("Int", arg);
229 o1 = arg2.Fetch();
230 n1 = GetNumber(arg2, o1, out r1);
231 }
232
233 BigInteger value = PromoteToBigInt(r1, n1);
234 if (value < 0) {
235 add_minus_sign = true;
236 value = -value;
237 }
238
239 String number;
240 if (format.radix == 16) {
241 number = RemoveInitialZero(value.ToString(format.radix, null)).ToLower();
242 } else {
243 number = value.ToString(format.radix, null);
244 }
245
246 if (format.upper) {
247 number = number.ToUpper();
248 }
249
250 if (add_minus_sign) {
251 if (format.rightJustifyZeroes) {
252 return "-" + number.PadLeft(format.minimumWidth - 1, '0');
e862bf26 »
2012-07-04 Refactor sprintf internals a bit. Probably still needs work on edge c…
253 } else {
4b9c017c »
2012-07-05 Major refactor for sprintf. Now handles all floating point directives…
254 return "-" + number;
e862bf26 »
2012-07-04 Refactor sprintf internals a bit. Probably still needs work on edge c…
255 }
4b9c017c »
2012-07-05 Major refactor for sprintf. Now handles all floating point directives…
256 }
257
258 return number;
259
260 } else {
261
262 double f = arg.Fetch().mo.mro_raw_Numeric.Get(arg);
263
264 if (f < 0.0) {
265 add_minus_sign = true;
266 f = -f;
267 }
268
9c29580c »
2012-07-05 Get correct default precision of 6 for the sprintf floating point for…
269 int precision = format.precision > 0 ? format.precision : 6;
270
4b9c017c »
2012-07-05 Major refactor for sprintf. Now handles all floating point directives…
271 String number = "??";
272 switch (format.directive) {
273 case PrintfDirective.FloatFixedDecimal:
9c29580c »
2012-07-05 Get correct default precision of 6 for the sprintf floating point for…
274 number = f.ToString("F" + precision);
4b9c017c »
2012-07-05 Major refactor for sprintf. Now handles all floating point directives…
275 break;
276 case PrintfDirective.FloatScientific:
9c29580c »
2012-07-05 Get correct default precision of 6 for the sprintf floating point for…
277 number = f.ToString("e" + precision);
4b9c017c »
2012-07-05 Major refactor for sprintf. Now handles all floating point directives…
278 break;
279 case PrintfDirective.FloatEF:
9c29580c »
2012-07-05 Get correct default precision of 6 for the sprintf floating point for…
280 number = f.ToString("g" + precision);
4b9c017c »
2012-07-05 Major refactor for sprintf. Now handles all floating point directives…
281 break;
282 }
283
284 if (format.upper) {
285 number = number.ToUpper();
286 }
287
288 if (add_minus_sign) {
289 if (format.rightJustifyZeroes) {
290 return "-" + number.PadLeft(format.minimumWidth - 1, '0');
e862bf26 »
2012-07-04 Refactor sprintf internals a bit. Probably still needs work on edge c…
291 } else {
4b9c017c »
2012-07-05 Major refactor for sprintf. Now handles all floating point directives…
292 return "-" + number;
e862bf26 »
2012-07-04 Refactor sprintf internals a bit. Probably still needs work on edge c…
293 }
4b9c017c »
2012-07-05 Major refactor for sprintf. Now handles all floating point directives…
294 }
295
296 return number;
e862bf26 »
2012-07-04 Refactor sprintf internals a bit. Probably still needs work on edge c…
297 }
298 }
4b9c017c »
2012-07-05 Major refactor for sprintf. Now handles all floating point directives…
299
6bc1637e »
2011-08-28 [lib/Printf.cs] now passes 40/44 sprintf tests with 1 skip
300 private static Variable RenderFormat(List<PrintfFormat> formatlist, Variable[] args) {
e862bf26 »
2012-07-04 Refactor sprintf internals a bit. Probably still needs work on edge c…
301 string result = "";
302 int argi = 0;
6bc1637e »
2011-08-28 [lib/Printf.cs] now passes 40/44 sprintf tests with 1 skip
303 foreach (PrintfFormat format in formatlist) {
e862bf26 »
2012-07-04 Refactor sprintf internals a bit. Probably still needs work on edge c…
304 if (format.directive == PrintfDirective.LiteralText) {
305 result += format.literaltext;
306 continue;
307 }
308
309 if (format.directive == PrintfDirective.InvokeCode) {
310 continue; // Should this do something more?
311 }
312
313 int i = format.index > 0 ? format.index : ++argi;
314 if (i < args.Length) {
315 string s = RenderFormat(format, args[i]);
4b9c017c »
2012-07-05 Major refactor for sprintf. Now handles all floating point directives…
316 if (format.leftJustify) {
317 result += s.PadRight(format.minimumWidth, ' ');
318 } else {
319 if (format.rightJustifyZeroes) {
320 result += s.PadLeft(format.minimumWidth, '0');
321 }
322 else {
323 result += s.PadLeft(format.minimumWidth, ' ');
324 }
e862bf26 »
2012-07-04 Refactor sprintf internals a bit. Probably still needs work on edge c…
325 }
326 }
327 else {
328 throw new NieczaException("index out of range");
6bc1637e »
2011-08-28 [lib/Printf.cs] now passes 40/44 sprintf tests with 1 skip
329 }
330 }
d67e96f3 »
2012-09-14 Move all [CORESaved] statics to instance variables of Compartment
331 return Kernel.BoxAnyMO(result, Compartment.Top.StrMO);
6bc1637e »
2011-08-28 [lib/Printf.cs] now passes 40/44 sprintf tests with 1 skip
332 }
333 }
334
Something went wrong with that request. Please try again.