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/print.go b/src/lib/fmt/print.go
new file mode 100644
index 0000000..db42c1e
--- /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')
+ }
+}