| ... | ... | @@ -14,6 +14,8 @@ const State = enum { // TODO put inside format function and make sure the name a |
| 14 | 14 | CloseBrace, |
| 15 | 15 | Integer, |
| 16 | 16 | IntegerWidth, |
| 17 | Float, |
| 18 | FloatWidth, |
| 17 | 19 | Character, |
| 18 | 20 | Buf, |
| 19 | 21 | BufWidth, |
| ... | ... | @@ -85,6 +87,8 @@ pub fn format(context: var, output: fn(@typeOf(context), []const u8)->%void, |
| 85 | 87 | }, |
| 86 | 88 | 's' => { |
| 87 | 89 | state = State.Buf; |
| 90 | },'.' => { |
| 91 | state = State.Float; |
| 88 | 92 | }, |
| 89 | 93 | else => @compileError("Unknown format character: " ++ []u8{c}), |
| 90 | 94 | }, |
| ... | ... | @@ -129,6 +133,30 @@ pub fn format(context: var, output: fn(@typeOf(context), []const u8)->%void, |
| 129 | 133 | '0' ... '9' => {}, |
| 130 | 134 | else => @compileError("Unexpected character in format string: " ++ []u8{c}), |
| 131 | 135 | }, |
| 136 | State.Float => switch (c) { |
| 137 | '}' => { |
| 138 | %return formatFloatDecimal(args[next_arg], 0, context, output); |
| 139 | next_arg += 1; |
| 140 | state = State.Start; |
| 141 | start_index = i + 1; |
| 142 | }, |
| 143 | '0' ... '9' => { |
| 144 | width_start = i; |
| 145 | state = State.FloatWidth; |
| 146 | }, |
| 147 | else => @compileError("Unexpected character in format string: " ++ []u8{c}), |
| 148 | }, |
| 149 | State.FloatWidth => switch (c) { |
| 150 | '}' => { |
| 151 | width = comptime %%parseUnsigned(usize, fmt[width_start..i], 10); |
| 152 | %return formatFloatDecimal(args[next_arg], width, context, output); |
| 153 | next_arg += 1; |
| 154 | state = State.Start; |
| 155 | start_index = i + 1; |
| 156 | }, |
| 157 | '0' ... '9' => {}, |
| 158 | else => @compileError("Unexpected character in format string: " ++ []u8{c}), |
| 159 | }, |
| 132 | 160 | State.BufWidth => switch (c) { |
| 133 | 161 | '}' => { |
| 134 | 162 | width = comptime %%parseUnsigned(usize, fmt[width_start..i], 10); |
| ... | ... | @@ -267,6 +295,47 @@ pub fn formatFloat(value: var, context: var, output: fn(@typeOf(context), []cons |
| 267 | 295 | } |
| 268 | 296 | } |
| 269 | 297 | |
| 298 | pub fn formatFloatDecimal(value: var, precision: usize, context: var, output: fn(@typeOf(context), []const u8)->%void) -> %void { |
| 299 | var x = f64(value); |
| 300 | |
| 301 | // Errol doesn't handle these special cases. |
| 302 | if (math.isNan(x)) { |
| 303 | return output(context, "NaN"); |
| 304 | } |
| 305 | if (math.signbit(x)) { |
| 306 | %return output(context, "-"); |
| 307 | x = -x; |
| 308 | } |
| 309 | if (math.isPositiveInf(x)) { |
| 310 | return output(context, "Infinity"); |
| 311 | } |
| 312 | if (x == 0.0) { |
| 313 | return output(context, "0.0"); |
| 314 | } |
| 315 | |
| 316 | var buffer: [32]u8 = undefined; |
| 317 | const float_decimal = errol3(x, buffer[0..]); |
| 318 | |
| 319 | const num_left_digits = if (float_decimal.exp > 0) usize(float_decimal.exp) else 1; |
| 320 | |
| 321 | %return output(context, float_decimal.digits[0 .. num_left_digits]); |
| 322 | %return output(context, "."); |
| 323 | if (float_decimal.digits.len > 1) { |
| 324 | const num_valid_digtis = if (@typeOf(value) == f32) math.min(usize(7), float_decimal.digits.len) |
| 325 | else |
| 326 | float_decimal.digits.len; |
| 327 | |
| 328 | const num_right_digits = if (precision != 0) |
| 329 | math.min(precision, (num_valid_digtis-num_left_digits)) |
| 330 | else |
| 331 | num_valid_digtis - num_left_digits; |
| 332 | %return output(context, float_decimal.digits[num_left_digits .. (num_left_digits + num_right_digits)]); |
| 333 | } else { |
| 334 | %return output(context, "0"); |
| 335 | } |
| 336 | } |
| 337 | |
| 338 | |
| 270 | 339 | pub fn formatInt(value: var, base: u8, uppercase: bool, width: usize, |
| 271 | 340 | context: var, output: fn(@typeOf(context), []const u8)->%void) -> %void |
| 272 | 341 | { |
| ... | ... | @@ -540,6 +609,39 @@ test "fmt.format" { |
| 540 | 609 | const result = %%bufPrint(buf1[0..], "f64: {}\n", -math.inf_f64); |
| 541 | 610 | assert(mem.eql(u8, result, "f64: -Infinity\n")); |
| 542 | 611 | } |
| 612 | { |
| 613 | var buf1: [32]u8 = undefined; |
| 614 | const value: f32 = 1.1234; |
| 615 | const result = %%bufPrint(buf1[0..], "f32: {.1}\n", value); |
| 616 | assert(mem.eql(u8, result, "f32: 1.1\n")); |
| 617 | } |
| 618 | { |
| 619 | var buf1: [32]u8 = undefined; |
| 620 | const value: f32 = 1234.567; |
| 621 | const result = %%bufPrint(buf1[0..], "f32: {.2}\n", value); |
| 622 | assert(mem.eql(u8, result, "f32: 1234.56\n")); |
| 623 | } |
| 624 | { |
| 625 | var buf1: [32]u8 = undefined; |
| 626 | const value: f32 = -11.1234; |
| 627 | const result = %%bufPrint(buf1[0..], "f32: {.4}\n", value); |
| 628 | // -11.1234 is converted to f64 -11.12339... internally (errol3() function takes f64). |
| 629 | // -11.12339... is truncated to -11.1233 |
| 630 | assert(mem.eql(u8, result, "f32: -11.1233\n")); |
| 631 | } |
| 632 | { |
| 633 | var buf1: [32]u8 = undefined; |
| 634 | const value: f32 = 91.12345; |
| 635 | const result = %%bufPrint(buf1[0..], "f32: {.}\n", value); |
| 636 | assert(mem.eql(u8, result, "f32: 91.12345\n")); |
| 637 | } |
| 638 | { |
| 639 | var buf1: [32]u8 = undefined; |
| 640 | const value: f64 = 91.12345678901235; |
| 641 | const result = %%bufPrint(buf1[0..], "f64: {.10}\n", value); |
| 642 | assert(mem.eql(u8, result, "f64: 91.1234567890\n")); |
| 643 | } |
| 644 | |
| 543 | 645 | } |
| 544 | 646 | } |
| 545 | 647 | |