]> Cypherpunks repositories - gostls13.git/commitdiff
Polymorphic printing.
authorRob Pike <r@golang.org>
Thu, 6 Nov 2008 18:40:57 +0000 (10:40 -0800)
committerRob Pike <r@golang.org>
Thu, 6 Nov 2008 18:40:57 +0000 (10:40 -0800)
R=rsc
DELTA=100  (70 added, 10 deleted, 20 changed)
OCL=18663
CL=18669

src/lib/fmt/print.go

index 730b497ff82780cd4c26ca3fe58bfa57c59aecdf..3c237f5e32e0838fa27aa4666079bab7aaa47a03 100644 (file)
@@ -15,6 +15,23 @@ import (
        "os";
 )
 
+export type Writer interface {
+       Write(b *[]byte) (ret int, err *os.Error);
+}
+
+// Representation of printer state passed to custom formatters.
+// Provides access to the Writer interface plus information about
+// the active formatting verb.
+export type FormatHelper interface {
+       Write(b *[]byte) (ret int, err *os.Error);
+       Width() (wid int, ok bool);
+       Precision()     (prec int, ok bool);
+}
+
+export type Formatter interface {
+       Format(f FormatHelper, c int);
+}
+
 const Runeself = 0x80
 const AllocSize = 32
 
@@ -22,6 +39,10 @@ type P struct {
        n       int;
        buf     *[]byte;
        fmt     *Fmt;
+       wid     int;
+       wid_ok  bool;
+       prec    int;
+       prec_ok bool;
 }
 
 func Printer() *P {
@@ -30,6 +51,14 @@ func Printer() *P {
        return p;
 }
 
+func (p *P) Width() (wid int, ok bool) {
+       return p.wid, p.wid_ok
+}
+
+func (p *P) Precision() (prec int, ok bool) {
+       return p.prec, p.prec_ok
+}
+
 func (p *P) ensure(n int) {
        if p.buf == nil || len(p.buf) < n {
                newn := AllocSize;
@@ -81,10 +110,6 @@ func (p *P) Write(b *[]byte) (ret int, err *os.Error) {
        return len(b), nil;
 }
 
-export type Writer interface {
-       Write(b *[]byte) (ret int, err *os.Error);
-}
-
 func (p *P) doprintf(format string, v reflect.StructValue);
 func (p *P) doprint(v reflect.StructValue, addspace, addnewline bool);
 
@@ -159,6 +184,14 @@ export func sprintln(v ...) string {
 
 // Getters for the fields of the argument structure.
 
+func getBool(v reflect.Value) (val bool, ok bool) {
+       switch v.Kind() {
+       case reflect.BoolKind:
+               return v.(reflect.BoolValue).Get(), true;
+       }
+       return false, false
+}
+
 func getInt(v reflect.Value) (val int64, signed, ok bool) {
        switch v.Kind() {
        case reflect.IntKind:
@@ -215,7 +248,7 @@ func getPtr(v reflect.Value) (val uint64, ok bool) {
        return 0, false;
 }
 
-// Convert ASCII to integer.
+// Convert ASCII to integer.  n is 0 (and got is false) if no number present.
 
 func parsenum(s string, start, end int) (n int, got bool, newi int) {
        if start >= end {
@@ -248,18 +281,12 @@ func (p *P) doprintf(format string, v reflect.StructValue) {
                        i += w;
                        continue;
                }
-               var got bool;
                // saw % - do we have %20 (width)?
-               w, got, i = parsenum(format, i+1, end);
-               if got {
-                       p.fmt.w(w);
-               }
+               p.wid, p.wid_ok, i = parsenum(format, i+1, end);
+               p.prec_ok = false;
                // do we have %.20 (precision)?
                if i < end && format[i] == '.' {
-                       w, got, i = parsenum(format, i+1, end);
-                       if got {
-                               p.fmt.p(w);
-                       }
+                       p.prec, p.prec_ok, i = parsenum(format, i+1, end);
                }
                c, w = sys.stringtorune(format, i);
                i += w;
@@ -269,19 +296,35 @@ func (p *P) doprintf(format string, v reflect.StructValue) {
                        continue;
                }
                if fieldnum >= v.Len() {        // out of operands
-                       p.addstr("???");
+                       p.add('%');
+                       p.add(c);
+                       p.addstr("(missing)");
                        continue;
                }
                field := v.Field(fieldnum);
                fieldnum++;
+               if formatter, ok := field.Interface().(Formatter); ok {
+                       formatter.Format(p, c);
+                       continue;
+               }
                s := "";
+               if p.wid_ok {
+                       p.fmt.w(p.wid);
+               }
+               if p.prec_ok {
+                       p.fmt.p(p.prec);
+               }
                switch c {
                        // bool
                        case 't':
-                               if field.(reflect.BoolValue).Get() {
-                                       s = "true";
+                               if v, ok := getBool(field); ok {
+                                       if v {
+                                               s = "true";
+                                       } else {
+                                               s = "false";
+                                       }
                                } else {
-                                       s = "false";
+                                       goto badtype;
                                }
 
                        // int
@@ -289,7 +332,13 @@ func (p *P) doprintf(format string, v reflect.StructValue) {
                                if v, signed, ok := getInt(field); ok {
                                        s = p.fmt.b64(uint64(v)).str()  // always unsigned
                                } else {
-                                       s = "%b%"
+                                       goto badtype
+                               }
+                       case 'c':
+                               if v, signed, ok := getInt(field); ok {
+                                       s = p.fmt.c(int(v)).str()
+                               } else {
+                                       goto badtype
                                }
                        case 'd':
                                if v, signed, ok := getInt(field); ok {
@@ -299,7 +348,7 @@ func (p *P) doprintf(format string, v reflect.StructValue) {
                                                s = p.fmt.ud64(uint64(v)).str()
                                        }
                                } else {
-                                       s = "%d%"
+                                       goto badtype
                                }
                        case 'o':
                                if v, signed, ok := getInt(field); ok {
@@ -309,7 +358,7 @@ func (p *P) doprintf(format string, v reflect.StructValue) {
                                                s = p.fmt.uo64(uint64(v)).str()
                                        }
                                } else {
-                                       s= "%o%"
+                                       goto badtype
                                }
                        case 'x':
                                if v, signed, ok := getInt(field); ok {
@@ -319,7 +368,7 @@ func (p *P) doprintf(format string, v reflect.StructValue) {
                                                s = p.fmt.ux64(uint64(v)).str()
                                        }
                                } else {
-                                       s = "%x%"
+                                       goto badtype
                                }
 
                        // float
@@ -327,19 +376,19 @@ func (p *P) doprintf(format string, v reflect.StructValue) {
                                if v, ok := getFloat(field); ok {
                                        s = p.fmt.e64(v).str()
                                } else {
-                                       s = "%e%"
+                                       goto badtype
                                }
                        case 'f':
                                if v, ok := getFloat(field); ok {
                                        s = p.fmt.f64(v).str()
                                } else {
-                                       s = "%f%";
+                                       goto badtype
                                }
                        case 'g':
                                if v, ok := getFloat(field); ok {
                                        s = p.fmt.g64(v).str()
                                } else {
-                                       s = "%g%"
+                                       goto badtype
                                }
 
                        // string
@@ -347,7 +396,7 @@ func (p *P) doprintf(format string, v reflect.StructValue) {
                                if v, ok := getString(field); ok {
                                        s = p.fmt.s(v).str()
                                } else {
-                                       s = "%s%"
+                                       goto badtype
                                }
 
                        // pointer
@@ -355,14 +404,25 @@ func (p *P) doprintf(format string, v reflect.StructValue) {
                                if v, ok := getPtr(field); ok {
                                        s = "0x" + p.fmt.uX64(v).str()
                                } else {
-                                       s = "%p%"
+                                       goto badtype
                                }
 
                        default:
-                               s = "?" + string(c) + "?";
+                       badtype:
+                               s = "%" + string(c) + "(" + field.Type().String() + ")%";
                }
                p.addstr(s);
        }
+       if fieldnum < v.Len() {
+               p.addstr("?(extra ");
+               for ; fieldnum < v.Len(); fieldnum++ {
+                       p.addstr(v.Field(fieldnum).Type().String());
+                       if fieldnum + 1 < v.Len() {
+                               p.addstr(", ");
+                       }
+               }
+               p.addstr(")");
+       }
 }
 
 func (p *P) doprint(v reflect.StructValue, addspace, addnewline bool) {
@@ -404,7 +464,7 @@ func (p *P) doprint(v reflect.StructValue, addspace, addnewline bool) {
                        p.doprint(field, true, false);
                        p.add('}');
                default:
-                       s = "???";
+                       s = "?" + field.Type().String() + "?";
                }
                p.addstr(s);
                prev_string = field.Kind() == reflect.StringKind;