| // Copyright 2009 The Go Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style |
| // license that can be found in the LICENSE file. |
| |
| package fmt |
| |
| /* |
| f := fmt.New(); |
| print f.d(1234).s("\n").str(); // create string, print it |
| f.d(-1234).s("\n").put(); // print string |
| f.ud(^0).putnl(); // print string with automatic newline |
| */ |
| |
| // import sys "sys" |
| |
| export Fmt, New; |
| |
| var ldigits string; |
| var udigits string; |
| var inited bool; |
| var pows10 [160] double; |
| |
| type Fmt struct { |
| buf string; |
| wid int; |
| wid_present bool; |
| prec int; |
| prec_present bool; |
| } |
| |
| func (f *Fmt) clearflags() { |
| f.wid_present = false; |
| f.prec_present = false; |
| } |
| |
| func (f *Fmt) clearbuf() { |
| f.buf = ""; |
| } |
| |
| func (f *Fmt) init() { |
| f.clearbuf(); |
| f.clearflags(); |
| if inited { |
| return; |
| } |
| ldigits = "0123456789abcdef"; // BUG: should be initialized const |
| udigits = "0123456789ABCDEF"; // BUG: should be initialized const |
| // BUG: should be done with initialization |
| var p double = 1.0; |
| for i := 0; i < 160; i++ { // BUG: len(pows10) |
| pows10[i] = p; |
| p *= 10.0; |
| } |
| inited = true; |
| } |
| |
| func New() *Fmt { |
| f := new(Fmt); |
| f.init(); |
| return f; |
| } |
| |
| func (f *Fmt) str() string { |
| s := f.buf; |
| f.clearbuf(); |
| f.clearflags(); |
| f.buf = ""; |
| return s; |
| } |
| |
| func (f *Fmt) put() { |
| print f.buf; |
| f.clearbuf(); |
| f.clearflags(); |
| } |
| |
| func (f *Fmt) putnl() { |
| print f.buf, "\n"; |
| f.clearbuf(); |
| f.clearflags(); |
| } |
| |
| func (f *Fmt) wp(w, p int) *Fmt { |
| f.wid_present = true; |
| f.wid = w; |
| f.prec_present = true; |
| f.prec = p; |
| return f; |
| } |
| |
| func (f *Fmt) p(p int) *Fmt { |
| f.prec_present = true; |
| f.prec = p; |
| return f; |
| } |
| |
| func (f *Fmt) w(x int) *Fmt { |
| f.wid_present = true; |
| f.wid = x; |
| return f; |
| } |
| |
| // append s to buf, padded on left (w > 0) or right (w < 0) |
| // padding is in bytes, not characters (agrees with ANSIC C, not Plan 9 C) |
| func (f *Fmt) pad(s string) { |
| if f.wid_present && f.wid != 0 { |
| left := true; |
| w := f.wid; |
| if w < 0 { |
| left = false; |
| w = -w; |
| } |
| w -= len(s); |
| if w > 0 { |
| if w > 64 { // BUG: should be able to use a const |
| w = 64; |
| } |
| var buf[64] byte; // BUG: should be able to allocate a size |
| for i := 0; i < w; i++ { |
| buf[i] = ' '; |
| } |
| if left { |
| s = string(buf)[0:w] + s; |
| } else { |
| s = s + string(buf)[0:w]; |
| } |
| } |
| } |
| f.buf += s; |
| } |
| |
| // format val into buf, ending at buf[i]. (printing is easier right-to-left; |
| // that's why the bidi languages are right-to-left except for numbers. wait, |
| // never mind.) val is known to be unsigned. we could make things maybe |
| // marginally faster by splitting the 32-bit case out into a separate function |
| // but it's not worth the duplication, so val has 64 bits. |
| func putint(buf *[64]byte, i int, base, val uint64, digits *string) int { |
| for val >= base { |
| buf[i] = (*digits)[val%base]; // BUG: shouldn't need indirect |
| i--; |
| val /= base; |
| } |
| buf[i] = (*digits)[val]; // BUG: shouldn't need indirect |
| return i-1; |
| } |
| |
| // boolean |
| func (f *Fmt) boolean(a bool) *Fmt { |
| if a { |
| f.pad("true"); |
| } else { |
| f.pad("false"); |
| } |
| f.clearflags(); |
| return f; |
| } |
| |
| // integer; interprets prec but not wid. |
| func (f *Fmt) integer(a int64, base uint, is_signed bool, digits *string) string { |
| var buf [64]byte; |
| negative := is_signed && a < 0; |
| if negative { |
| a = -a; |
| } |
| i := putint(&buf, 63, uint64(base), uint64(a), digits); |
| if f.prec_present { |
| for i > 0 && f.prec > (63-i) { |
| buf[i] = '0'; |
| i--; |
| } |
| } |
| if negative { |
| buf[i] = '-'; |
| i--; |
| } |
| return string(buf)[i+1:64]; |
| } |
| |
| // decimal |
| func (f *Fmt) d(a int32) *Fmt { |
| f.pad(f.integer(int64(a), 10, true, &ldigits)); |
| f.clearflags(); |
| return f; |
| } |
| |
| func (f *Fmt) D(a int64) *Fmt { |
| f.pad(f.integer(a, 10, true, &ldigits)); |
| f.clearflags(); |
| return f; |
| } |
| |
| // unsigned decimal |
| func (f *Fmt) ud(a int32) *Fmt { |
| f.pad(f.integer(int64(uint32(a)), 10, false, &ldigits)); |
| f.clearflags(); |
| return f; |
| } |
| |
| func (f *Fmt) uD(a int64) *Fmt { |
| f.pad(f.integer(a, 10, false, &ldigits)); |
| f.clearflags(); |
| return f; |
| } |
| |
| // hexdecimal |
| func (f *Fmt) x(a int32) *Fmt { |
| f.pad(f.integer(int64(a), 16, true, &ldigits)); |
| f.clearflags(); |
| return f; |
| } |
| |
| func (f *Fmt) X(a int64) *Fmt { |
| f.pad(f.integer(a, 16, true, &ldigits)); |
| f.clearflags(); |
| return f; |
| } |
| |
| // unsigned hexdecimal |
| func (f *Fmt) ux(a int32) *Fmt { |
| f.pad(f.integer(int64(uint32(a)), 16, false, &ldigits)); |
| f.clearflags(); |
| return f; |
| } |
| |
| func (f *Fmt) uX(a int64) *Fmt { |
| f.pad(f.integer(a, 16, false, &ldigits)); |
| f.clearflags(); |
| return f; |
| } |
| |
| // HEXADECIMAL |
| func (f *Fmt) Ux(a int32) *Fmt { |
| f.pad(f.integer(int64(a), 16, true, &udigits)); |
| f.clearflags(); |
| return f; |
| } |
| |
| func (f *Fmt) UX(a int64) *Fmt { |
| f.pad(f.integer(a, 16, true, &udigits)); |
| f.clearflags(); |
| return f; |
| } |
| |
| // unsigned HEXADECIMAL |
| func (f *Fmt) uUx(a int32) *Fmt { |
| f.pad(f.integer(int64(uint32(a)), 16, false, &udigits)); |
| f.clearflags(); |
| return f; |
| } |
| |
| func (f *Fmt) uUX(a int64) *Fmt { |
| f.pad(f.integer(a, 16, false, &udigits)); |
| f.clearflags(); |
| return f; |
| } |
| |
| // octal |
| func (f *Fmt) o(a int32) *Fmt { |
| f.pad(f.integer(int64(a), 8, true, &ldigits)); |
| f.clearflags(); |
| return f; |
| } |
| |
| func (f *Fmt) O(a int64) *Fmt { |
| f.pad(f.integer(a, 8, true, &ldigits)); |
| f.clearflags(); |
| return f; |
| } |
| |
| // unsigned octal |
| func (f *Fmt) uo(a int32) *Fmt { |
| f.pad(f.integer(int64(uint32(a)), 8, false, &ldigits)); |
| f.clearflags(); |
| return f; |
| } |
| |
| func (f *Fmt) uO(a int64) *Fmt { |
| f.pad(f.integer(a, 8, false, &ldigits)); |
| f.clearflags(); |
| return f; |
| } |
| |
| // binary |
| func (f *Fmt) b(a int32) *Fmt { |
| f.pad(f.integer(int64(uint32(a)), 2, false, &ldigits)); |
| f.clearflags(); |
| return f; |
| } |
| |
| func (f *Fmt) B(a int64) *Fmt { |
| f.pad(f.integer(a, 2, false, &ldigits)); |
| f.clearflags(); |
| return f; |
| } |
| |
| // character |
| func (f *Fmt) c(a int) *Fmt { |
| f.pad(string(a)); |
| f.clearflags(); |
| return f; |
| } |
| |
| // string |
| func (f *Fmt) s(s string) *Fmt { |
| if f.prec_present { |
| if f.prec < len(s) { |
| s = s[0:f.prec]; |
| } |
| } |
| f.pad(s); |
| f.clearflags(); |
| return f; |
| } |
| |
| func pow10(n int) double { |
| var d double; |
| npows10 := 160; // nelem(pows10); BUG: why not a const? |
| |
| neg := false; |
| if n < 0 { |
| if n < -307 { // DBL_MIN_10_EXP |
| return 0.; |
| } |
| neg = true; |
| n = -n; |
| }else if n > 308 { // DBL_MAX_10_EXP |
| return 1.79769e+308; // HUGE_VAL |
| } |
| |
| if n < npows10 { |
| d = pows10[n]; |
| } else { |
| d = pows10[npows10-1]; |
| for { |
| n -= npows10 - 1; |
| if n < npows10 { |
| d *= pows10[n]; |
| break; |
| } |
| d *= pows10[npows10 - 1]; |
| } |
| } |
| if neg { |
| return 1/d; |
| } |
| return d; |
| } |
| |
| func unpack(a double) (negative bool, exp int, num double) { |
| if a == 0 { |
| return false, 0, 0.0 |
| } |
| neg := a < 0; |
| if neg { |
| a = -a; |
| } |
| // find g,e such that a = g*10^e. |
| // guess 10-exponent using 2-exponent, then fine tune. |
| var g double; |
| var e2 int; |
| e2, g = sys.frexp(a); // BUG: should be able to say e2, g := sys.frexp(a); |
| e := int(e2 * .301029995663981); |
| g = a * pow10(-e); |
| for g < 1 { |
| e--; |
| g = a * pow10(-e); |
| } |
| for g >= 10 { |
| e++; |
| g = a * pow10(-e); |
| } |
| return neg, e, g; |
| } |
| |
| // double |
| func (f *Fmt) E(a double) *Fmt { |
| var negative bool; |
| var g double; |
| var exp int; |
| negative, exp, g = unpack(a); |
| prec := 6; |
| if f.prec_present { |
| prec = f.prec; |
| } |
| prec++; // one digit left of decimal |
| var s string; |
| // multiply by 10^prec to get decimal places; put decimal after first digit |
| if g == 0 { |
| // doesn't work for zero - fake it |
| s = "000000000000000000000000000000000000000000000000000000000000"; |
| if prec < len(s) { |
| s = s[0:prec]; |
| } else { |
| prec = len(s); |
| } |
| } else { |
| g *= pow10(prec); |
| s = f.integer(int64(g + .5), 10, true, &ldigits); // get the digits into a string |
| } |
| s = s[0:1] + "." + s[1:prec]; // insert a decimal point |
| // print exponent with leading 0 if appropriate. |
| es := New().p(2).integer(int64(exp), 10, true, &ldigits); |
| if exp > 0 { |
| es = "+" + es; // BUG: should do this with a fmt flag |
| } |
| s = s + "e" + es; |
| if negative { |
| s = "-" + s; |
| } |
| f.pad(s); |
| f.clearflags(); |
| return f; |
| } |
| |
| // double |
| func (f *Fmt) F(a double) *Fmt { |
| var negative bool; |
| var g double; |
| var exp int; |
| negative, exp, g = unpack(a); |
| if exp > 19 || exp < -19 { // too big for this sloppy code |
| return f.E(a); |
| } |
| prec := 6; |
| if f.prec_present { |
| prec = f.prec; |
| } |
| // prec is number of digits after decimal point |
| s := "NO"; |
| if exp >= 0 { |
| g *= pow10(exp); |
| gi := int64(g); |
| s = New().integer(gi, 10, true, &ldigits); |
| s = s + "."; |
| g -= double(gi); |
| s = s + New().p(prec).integer(int64(g*pow10(prec) + .5), 10, true, &ldigits); |
| } else { |
| g *= pow10(prec + exp); |
| s = "0." + New().p(prec).integer(int64(g + .5), 10, true, &ldigits); |
| } |
| if negative { |
| s = "-" + s; |
| } |
| f.pad(s); |
| f.clearflags(); |
| return f; |
| } |
| |
| // double |
| func (f *Fmt) G(a double) *Fmt { |
| f1 := New(); |
| f2 := New(); |
| if f.wid_present { |
| f1.w(f.wid); |
| f2.w(f.wid); |
| } |
| if f.prec_present { |
| f1.p(f.prec); |
| f2.p(f.prec); |
| } |
| efmt := f1.E(a).str(); |
| ffmt := f2.F(a).str(); |
| // ffmt can return e in my bogus world; don't trim trailing 0s if so. |
| f_is_e := false; |
| for i := 0; i < len(ffmt); i++ { |
| if ffmt[i] == 'e' { |
| f_is_e = true; |
| break; |
| } |
| } |
| if !f_is_e { |
| // strip trailing zeros |
| l := len(ffmt); |
| for ffmt[l-1]=='0' { |
| l--; |
| } |
| ffmt = ffmt[0:l]; |
| } |
| if len(efmt) < len(ffmt) { |
| f.pad(efmt); |
| } else { |
| f.pad(ffmt); |
| } |
| f.clearflags(); |
| return f; |
| } |
| |
| // float |
| func (x *Fmt) f(a float) *Fmt { |
| return x.F(double(a)) |
| } |
| |
| // float |
| func (x *Fmt) e(a float) *Fmt { |
| return x.E(double(a)) |
| } |
| |
| // float |
| func (x *Fmt) g(a float) *Fmt { |
| return x.G(double(a)) |
| } |