From: Rob Pike Date: Fri, 24 Oct 2008 23:33:29 +0000 (-0700) Subject: add printf to fmt. X-Git-Tag: weekly.2009-11-06~2895 X-Git-Url: http://www.git.cypherpunks.su/?a=commitdiff_plain;h=418b97c67013c892d88443949ed913ff96baf25e;p=gostls13.git add printf to fmt. uses reflection to determine arguments. for now, the arguments must be provided as a struct; the compiler will soon do the packaging automatically for "..." parameters. R=rsc DELTA=1436 (909 added, 520 deleted, 7 changed) OCL=17823 CL=17831 --- diff --git a/src/lib/fmt/Makefile b/src/lib/fmt/Makefile index 70ea703f19..64ca60c91f 100644 --- a/src/lib/fmt/Makefile +++ b/src/lib/fmt/Makefile @@ -3,7 +3,7 @@ # license that can be found in the LICENSE file. # DO NOT EDIT. Automatically generated by gobuild. -# gobuild -m fmt fmt.go +# gobuild -m fmt format.go print.go O=6 GC=$(O)g CC=$(O)c -w @@ -31,11 +31,17 @@ clean: O1=\ - fmt.$O\ + format.$O\ -$(PKG): a1 +O2=\ + print.$O\ + +$(PKG): a1 a2 a1: $(O1) $(AR) grc $(PKG) $(O1) +a2: $(O2) + $(AR) grc $(PKG) $(O2) $(O1): nuke +$(O2): a1 diff --git a/src/lib/fmt/fmt.go b/src/lib/fmt/format.go similarity index 100% rename from src/lib/fmt/fmt.go rename to src/lib/fmt/format.go diff --git a/src/lib/fmt/print.go b/src/lib/fmt/print.go new file mode 100644 index 0000000000..db42c1e0d5 --- /dev/null +++ b/src/lib/fmt/print.go @@ -0,0 +1,387 @@ +// 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 + +import ( + "fmt"; + "reflect"; + "os"; +) + +const Runeself = 0x80 +const AllocSize = 32 + +export type P struct { + n int; + buf *[]byte; + fmt *Fmt; +} + +export func Printer() *P { + p := new(P); + p.fmt = fmt.New(); + return p; +} + +func (p *P) ensure(n int) { + if p.buf == nil || len(p.buf) < n { + newn := AllocSize; + if p.buf != nil { + newn += len(p.buf); + } + if newn < n { + newn = n + AllocSize + } + b := new([]byte, newn); + for i := 0; i < p.n; i++ { + b[i] = p.buf[i]; + } + p.buf = b; + } +} + +func (p *P) addstr(s string) { + n := len(s); + p.ensure(p.n + n); + for i := 0; i < n; i++ { + p.buf[p.n] = s[i]; + p.n++; + } +} + +func (p *P) addbytes(b *[]byte, start, end int) { + p.ensure(p.n + end-start); + for i := start; i < end; i++ { + p.buf[p.n] = b[i]; + p.n++; + } +} + +func (p *P) add(c int) { + p.ensure(p.n + 1); + if c < Runeself { + p.buf[p.n] = byte(c); + p.n++; + } else { + p.addstr(string(c)); + } +} + +func (p *P) reset() { + p.n = 0; +} + +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 bool); + +// These routines end in 'f' and take a format string. + +func (p *P) fprintf(w Writer, format string, a reflect.Empty) (n int, error *os.Error) { + v := reflect.NewValue(a).(reflect.PtrValue).Sub().(reflect.StructValue); + p.doprintf(format, v); + n, error = w.Write(p.buf[0:p.n]); + p.reset(); + return n, error; +} + +func (p *P) printf(format string, v reflect.Empty) (n int, errno *os.Error) { + n, errno = p.fprintf(os.Stdout, format, v); + return n, errno; +} + +func (p *P) sprintf(format string, v reflect.Empty) string { + p.doprintf(format, reflect.NewValue(v).(reflect.StructValue)); + s := string(p.buf)[0 : p.n]; + p.reset(); + return s; +} + +// These routines do not take a format string and add spaces only +// when the operand on neither side is a string. + +func (p *P) fprint(w Writer, a reflect.Empty) (n int, error *os.Error) { + v := reflect.NewValue(a).(reflect.PtrValue).Sub().(reflect.StructValue); + p.doprint(v, false); + n, error = w.Write(p.buf[0:p.n]); + p.reset(); + return n, error; +} + +func (p *P) print(v reflect.Empty) (n int, errno *os.Error) { + n, errno = p.fprint(os.Stdout, v); + return n, errno; +} + +func (p *P) sprint(v reflect.Empty) string { + p.doprint(reflect.NewValue(v).(reflect.StructValue), false); + s := string(p.buf)[0 : p.n]; + p.reset(); + return s; +} + +// These routines end in 'ln', do not take a format string, +// always add spaces between operands, and add a newline +// after the last operand. + +func (p *P) fprintln(w Writer, a reflect.Empty) (n int, error *os.Error) { + v := reflect.NewValue(a).(reflect.PtrValue).Sub().(reflect.StructValue); + p.doprint(v, true); + n, error = w.Write(p.buf[0:p.n]); + p.reset(); + return n, error; +} + +func (p *P) println(v reflect.Empty) (n int, errno *os.Error) { + n, errno = p.fprintln(os.Stdout, v); + return n, errno; +} + +func (p *P) sprintln(v reflect.Empty) string { + p.doprint(reflect.NewValue(v).(reflect.StructValue), true); + s := string(p.buf)[0 : p.n]; + p.reset(); + return s; +} + +// Getters for the fields of the argument structure. + +func getInt(v reflect.Value) (val int64, signed, ok bool) { + switch v.Kind() { + case reflect.Int8Kind: + return int64(v.(reflect.Int8Value).Get()), true, true; + case reflect.Int16Kind: + return int64(v.(reflect.Int16Value).Get()), true, true; + case reflect.Int32Kind: + return int64(v.(reflect.Int32Value).Get()), true, true; + case reflect.Int64Kind: + return int64(v.(reflect.Int64Value).Get()), true, true; + case reflect.Uint8Kind: + return int64(v.(reflect.Uint8Value).Get()), false, true; + case reflect.Uint16Kind: + return int64(v.(reflect.Uint16Value).Get()), false, true; + case reflect.Uint32Kind: + return int64(v.(reflect.Uint32Value).Get()), false, true; + case reflect.Uint64Kind: + return int64(v.(reflect.Uint64Value).Get()), false, true; + } + return 0, false, false; +} + +func getString(v reflect.Value) (val string, ok bool) { + switch v.Kind() { + case reflect.StringKind: + return v.(reflect.StringValue).Get(), true; + } + return "", false; +} + +func getFloat(v reflect.Value) (val float64, ok bool) { + switch v.Kind() { + case reflect.Float32Kind: + return float64(v.(reflect.Float32Value).Get()), true; + case reflect.Float64Kind: + return float64(v.(reflect.Float32Value).Get()), true; + case reflect.Float80Kind: + break; // TODO: what to do here? + } + return 0.0, false; +} + +func getPtr(v reflect.Value) (val uint64, ok bool) { + switch v.Kind() { + case reflect.PtrKind: + return v.(reflect.PtrValue).Get(), true; + } + return 0, false; +} + +// Convert ASCII to integer. + +func parsenum(s string, start, end int) (n int, got bool, newi int) { + if start >= end { + return 0, false, end + } + if s[start] == '-' { + a, b, c := parsenum(s, start+1, end); + if b { + return -a, b, c; + } + } + isnum := false; + num := 0; + for '0' <= s[start] && s[start] <= '9' { + num = num*10 + int(s[start] - '0'); + start++; + isnum = true; + } + return num, isnum, start; +} + +func (p *P) doprintf(format string, v reflect.StructValue) { + p.ensure(len(format)); // a good starting size + end := len(format) - 1; + fieldnum := 0; // we process one field per non-trivial format + for i := 0; i <= end; { + c, w := sys.stringtorune(format, i); + if c != '%' || i == end { + p.add(c); + 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); + } + // do we have %.20 (precision)? + if i < end && format[i] == '.' { + w, got, i = parsenum(format, i+1, end); + if got { + p.fmt.p(w); + } + } + c, w = sys.stringtorune(format, i); + i += w; + // percent is special - absorbs no operand + if c == '%' { + p.add('%'); // TODO: should we bother with width & prec? + continue; + } + if fieldnum >= v.Len() { // out of operands + p.addstr("???"); + continue; + } + field := v.Field(fieldnum); + fieldnum++; + s := ""; + switch c { + // int + case 'b': + if v, signed, ok := getInt(field); ok { + s = p.fmt.B(v).str() // always unsigned + } else { + s = "%b%" + } + case 'd': + if v, signed, ok := getInt(field); ok { + if signed { + s = p.fmt.D(v).str() + } else { + s = p.fmt.uD(v).str() + } + } else { + s = "%d%" + } + case 'o': + if v, signed, ok := getInt(field); ok { + if signed { + s = p.fmt.O(v).str() + } else { + s = p.fmt.uO(v).str() + } + } else { + s= "%o%" + } + case 'x': + if v, signed, ok := getInt(field); ok { + if signed { + s = p.fmt.X(v).str() + } else { + s = p.fmt.uX(v).str() + } + } else { + s = "%x%" + } + + // float + case 'e': + if v, ok := getFloat(field); ok { + s = p.fmt.E(v).str() + } else { + s = "%e%" + } + case 'f': + if v, ok := getFloat(field); ok { + s = p.fmt.F(v).str() + } else { + s = "%f%"; + } + case 'g': + if v, ok := getFloat(field); ok { + s = p.fmt.G(v).str() + } else { + s = "%g%" + } + + // string + case 's': + if v, ok := getString(field); ok { + s = p.fmt.s(v).str() + } else { + s = "%s%" + } + + // pointer + case 'p': + if v, ok := getPtr(field); ok { + s = "0x" + p.fmt.uX(int64(v)).str() + } else { + s = "%p%" + } + + default: + s = "?" + string(c) + "?"; + } + p.addstr(s); + } +} + +func (p *P) doprint(v reflect.StructValue, is_println bool) { + prev_string := false; + for fieldnum := 0; fieldnum < v.Len(); fieldnum++ { + // always add spaces if we're doing println + field := v.Field(fieldnum); + s := ""; + if is_println { + if fieldnum > 0 { + p.add(' ') + } + } else if field.Kind() != reflect.StringKind && !prev_string{ + // if not doing println, add spaces if neither side is a string + p.add(' ') + } + switch field.Kind() { + case reflect.Int8Kind, reflect.Int16Kind, reflect.Int32Kind, reflect.Int64Kind: + v, signed, ok := getInt(field); + s = p.fmt.D(v).str(); + case reflect.Uint8Kind, reflect.Uint16Kind, reflect.Uint32Kind, reflect.Uint64Kind: + v, signed, ok := getInt(field); + s = p.fmt.uD(v).str(); + case reflect.Float32Kind, reflect.Float64Kind, reflect.Float80Kind: + v, ok := getFloat(field); + s = p.fmt.G(v).str(); + case reflect.StringKind: + v, ok := getString(field); + s = p.fmt.s(v).str(); + case reflect.PtrKind: + v, ok := getPtr(field); + p.add('0'); + p.add('x'); + s = p.fmt.uX(int64(v)).str(); + default: + s = "???"; + } + p.addstr(s); + prev_string = field.Kind() == reflect.StringKind; + } + if is_println { + p.add('\n') + } +} diff --git a/src/lib/reflect/tostring.go b/src/lib/reflect/tostring.go index ca3ada9fdd..4b3ff78ca4 100644 --- a/src/lib/reflect/tostring.go +++ b/src/lib/reflect/tostring.go @@ -151,7 +151,7 @@ func ValueToString(val Value) string { return val.(StringValue).Get(); case PtrKind: v := val.(PtrValue); - return TypeToString(typ, false) + "(" + integer(int64(v.Indirect())) + ")"; + return TypeToString(typ, false) + "(" + integer(int64(v.Get())) + ")"; case ArrayKind: t := typ.(ArrayType); v := val.(ArrayValue); diff --git a/src/lib/reflect/value.go b/src/lib/reflect/value.go index 7d6172dada..80ad46e6d8 100644 --- a/src/lib/reflect/value.go +++ b/src/lib/reflect/value.go @@ -444,9 +444,9 @@ func (v *StringValueStruct) Put(s string) { export type PtrValue interface { Kind() int; - Sub() Value; Type() Type; - Indirect() Addr; + Sub() Value; + Get() Addr; } type PtrValueStruct struct { @@ -462,12 +462,12 @@ func (v *PtrValueStruct) Type() Type { return v.typ } -func (v *PtrValueStruct) Indirect() Addr { +func (v *PtrValueStruct) Get() Addr { return *AddrToPtrAddr(v.addr) } func (v *PtrValueStruct) Sub() Value { - return NewValueAddr(v.typ.(PtrType).Sub(), v.Indirect()); + return NewValueAddr(v.typ.(PtrType).Sub(), v.Get()); } func PtrCreator(typ Type, addr Addr) Value {