diff options
author | Russ Cox <rsc@golang.org> | 2008-11-24 14:51:33 -0800 |
---|---|---|
committer | Russ Cox <rsc@golang.org> | 2008-11-24 14:51:33 -0800 |
commit | 2572141a63f90c4af6761d1c1dd17b5a624512f3 (patch) | |
tree | 34e2a5396894dca1141c0db81df5d7791807a8d8 /src | |
parent | 4add140fbba0262ddd13b79f7ad012c9f78b4128 (diff) | |
download | golang-2572141a63f90c4af6761d1c1dd17b5a624512f3.tar.gz |
replay CL 19916 and CL 19913 now that the build can handle them
TBR=r
OCL=19924
CL=19934
Diffstat (limited to 'src')
-rw-r--r-- | src/lib/bufio.go | 34 | ||||
-rw-r--r-- | src/lib/fmt/fmt_test.go | 164 | ||||
-rw-r--r-- | src/lib/fmt/format.go | 92 | ||||
-rw-r--r-- | src/lib/fmt/print.go | 72 | ||||
-rw-r--r-- | src/lib/reflect/all_test.go | 11 | ||||
-rw-r--r-- | src/lib/reflect/cast_amd64.s | 10 | ||||
-rwxr-xr-x | src/lib/reflect/gencast.sh | 1 | ||||
-rw-r--r-- | src/lib/reflect/value.go | 14 | ||||
-rw-r--r-- | src/lib/strconv/Makefile | 5 | ||||
-rw-r--r-- | src/lib/strconv/ftoa_test.go | 1 | ||||
-rw-r--r-- | src/lib/strconv/quote.go | 76 | ||||
-rw-r--r-- | src/lib/strconv/quote_test.go | 87 | ||||
-rw-r--r-- | src/lib/utf8.go | 98 | ||||
-rw-r--r-- | src/lib/utf8_test.go | 68 |
14 files changed, 667 insertions, 66 deletions
diff --git a/src/lib/bufio.go b/src/lib/bufio.go index f41c4cd3d..11813d6c6 100644 --- a/src/lib/bufio.go +++ b/src/lib/bufio.go @@ -3,8 +3,12 @@ // license that can be found in the LICENSE file. package bufio -import "os" -import "io" + +import ( + "os"; + "io"; + "utf8"; +) // TODO: @@ -65,7 +69,7 @@ func (b *BufRead) Fill() *os.Error { } // Slide existing data to beginning. - if b.w > b.r { + if b.w > b.r { CopySlice(b.buf[0:b.w-b.r], b.buf[b.r:b.w]); b.w -= b.r; } else { @@ -140,6 +144,30 @@ func (b *BufRead) UnreadByte() *os.Error { return nil } +// Read a single Unicode character; returns the rune and its size. +func (b *BufRead) ReadRune() (rune int, size int, err *os.Error) { + for b.r + utf8.UTFMax > b.w && !utf8.FullRune(b.buf[b.r:b.w]) { + n := b.w - b.r; + b.Fill(); + if b.err != nil { + return 0, 0, b.err + } + if b.w - b.r == n { + // no bytes read + if b.r == b.w { + return 0, 0, EndOfFile + } + break; + } + } + rune, size = int(b.buf[b.r]), 1; + if rune >= 0x80 { + rune, size = utf8.DecodeRune(b.buf[b.r:b.w]); + } + b.r += size; + return rune, size, nil +} + // Helper function: look for byte c in array p, // returning its index or -1. func FindByte(p *[]byte, c byte) int { diff --git a/src/lib/fmt/fmt_test.go b/src/lib/fmt/fmt_test.go new file mode 100644 index 000000000..ec1e9951b --- /dev/null +++ b/src/lib/fmt/fmt_test.go @@ -0,0 +1,164 @@ +// 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"; + "syscall"; + "testing"; +) + +export func TestFmtInterface(t *testing.T) { + var i1 interface{}; + i1 = "abc"; + s := fmt.sprintf("%s", i1); + if s != "abc" { + t.Errorf(`fmt.sprintf("%%s", empty("abc")) = %q want %q`, s, "abc"); + } +} + +type FmtTest struct { + fmt string; + val interface { }; + out string; +} + +func Bytes(s string) *[]byte { + b := new([]byte, len(s)+1); + syscall.StringToBytes(b, s); + return b[0:len(s)]; +} + +const B32 uint32 = 1<<32 - 1 +const B64 uint64 = 1<<64 - 1 + +var fmttests = []FmtTest{ + // basic string + FmtTest{ "%s", "abc", "abc" }, + FmtTest{ "%x", "abc", "616263" }, + FmtTest{ "%x", "xyz", "78797a" }, + FmtTest{ "%X", "xyz", "78797A" }, + FmtTest{ "%q", "abc", `"abc"` }, + + // basic bytes + FmtTest{ "%s", Bytes("abc"), "abc" }, + FmtTest{ "%x", Bytes("abc"), "616263" }, + FmtTest{ "%x", Bytes("xyz"), "78797a" }, + FmtTest{ "%X", Bytes("xyz"), "78797A" }, + FmtTest{ "%q", Bytes("abc"), `"abc"` }, + + // escaped strings + FmtTest{ "%#q", `abc`, "`abc`" }, + FmtTest{ "%#q", `"`, "`\"`" }, + FmtTest{ "1 %#q", `\n`, "1 `\\n`" }, + FmtTest{ "2 %#q", "\n", `2 "\n"` }, + FmtTest{ "%q", `"`, `"\""` }, + FmtTest{ "%q", "\a\b\f\r\n\t\v", `"\a\b\f\r\n\t\v"` }, + FmtTest{ "%q", "abc\xffdef", `"abc\xffdef"` }, + FmtTest{ "%q", "\u263a", `"\u263a"` }, + FmtTest{ "%q", "\U0010ffff", `"\U0010ffff"` }, + + // width + FmtTest{ "%5s", "abc", " abc" }, + FmtTest{ "%-5s", "abc", "abc " }, + FmtTest{ "%05s", "abc", "00abc" }, + + // integers + FmtTest{ "%d", 12345, "12345" }, + FmtTest{ "%d", -12345, "-12345" }, + FmtTest{ "%10d", 12345, " 12345" }, + FmtTest{ "%10d", -12345, " -12345" }, + FmtTest{ "%+10d", 12345, " +12345" }, + FmtTest{ "%010d", 12345, "0000012345" }, + FmtTest{ "%010d", -12345, "-000012345" }, + FmtTest{ "%-10d", 12345, "12345 " }, + FmtTest{ "%010.3d", 1, " 001" }, + FmtTest{ "%010.3d", -1, " -001" }, + FmtTest{ "%+d", 12345, "+12345" }, + FmtTest{ "%+d", -12345, "-12345" }, + FmtTest{ "% d", 12345, " 12345" }, + FmtTest{ "% d", -12345, "-12345" }, + + // old test/fmt_test.go + FmtTest{ "%d", 1234, "1234" }, + FmtTest{ "%d", -1234, "-1234" }, + FmtTest{ "%d", uint(1234), "1234" }, + FmtTest{ "%d", uint32(B32), "4294967295" }, + FmtTest{ "%d", uint64(B64), "18446744073709551615" }, + FmtTest{ "%o", 01234, "1234" }, + FmtTest{ "%o", uint32(B32), "37777777777" }, + FmtTest{ "%o", uint64(B64), "1777777777777777777777" }, + FmtTest{ "%x", 0x1234abcd, "1234abcd" }, + FmtTest{ "%x", B32-0x1234567, "fedcba98" }, + FmtTest{ "%X", 0x1234abcd, "1234ABCD" }, + FmtTest{ "%X", B32-0x1234567, "FEDCBA98" }, + FmtTest{ "%x", B64, "ffffffffffffffff" }, + FmtTest{ "%b", 7, "111" }, + FmtTest{ "%b", B64, "1111111111111111111111111111111111111111111111111111111111111111" }, + FmtTest{ "%e", float64(1), "1.000000e+00" }, + FmtTest{ "%e", float64(1234.5678e3), "1.234568e+06" }, + FmtTest{ "%e", float64(1234.5678e-8), "1.234568e-05" }, + FmtTest{ "%e", float64(-7), "-7.000000e+00" }, + FmtTest{ "%e", float64(-1e-9), "-1.000000e-09" }, + FmtTest{ "%f", float64(1234.5678e3), "1234567.800000" }, + FmtTest{ "%f", float64(1234.5678e-8), "0.000012" }, + FmtTest{ "%f", float64(-7), "-7.000000" }, + FmtTest{ "%f", float64(-1e-9), "-0.000000" }, + FmtTest{ "%g", float64(1234.5678e3), "1.2345678e+06" }, + FmtTest{ "%g", float32(1234.5678e3), "1.2345678e+06" }, + FmtTest{ "%g", float64(1234.5678e-8), "1.2345678e-05" }, + FmtTest{ "%g", float64(-7), "-7" }, + FmtTest{ "%g", float64(-1e-9), "-1e-09", }, + FmtTest{ "%g", float32(-1e-9), "-1e-09" }, + FmtTest{ "%c", 'x', "x" }, + FmtTest{ "%c", 0xe4, "ä" }, + FmtTest{ "%c", 0x672c, "本" }, + FmtTest{ "%c", '日', "日" }, + FmtTest{ "%20.8d", 1234, " 00001234" }, + FmtTest{ "%20.8d", -1234, " -00001234" }, + FmtTest{ "%20d", 1234, " 1234" }, + FmtTest{ "%-20.8d", 1234, "00001234 " }, + FmtTest{ "%-20.8d", -1234, "-00001234 " }, + FmtTest{ "%.20b", 7, "00000000000000000111" }, + FmtTest{ "%20.5s", "qwertyuiop", " qwert" }, + FmtTest{ "%.5s", "qwertyuiop", "qwert" }, + FmtTest{ "%-20.5s", "qwertyuiop", "qwert " }, + FmtTest{ "%20c", 'x', " x" }, + FmtTest{ "%-20c", 'x', "x " }, + FmtTest{ "%20.6e", 1.2345e3, " 1.234500e+03" }, + FmtTest{ "%20.6e", 1.2345e-3, " 1.234500e-03" }, + FmtTest{ "%20e", 1.2345e3, " 1.234500e+03" }, + FmtTest{ "%20e", 1.2345e-3, " 1.234500e-03" }, + FmtTest{ "%20.8e", 1.2345e3, " 1.23450000e+03" }, + FmtTest{ "%20f", float64(1.23456789e3), " 1234.567890" }, + FmtTest{ "%20f", float64(1.23456789e-3), " 0.001235" }, + FmtTest{ "%20f", float64(12345678901.23456789), " 12345678901.234568" }, + FmtTest{ "%-20f", float64(1.23456789e3), "1234.567890 " }, + FmtTest{ "%20.8f", float64(1.23456789e3), " 1234.56789000" }, + FmtTest{ "%20.8f", float64(1.23456789e-3), " 0.00123457" }, + FmtTest{ "%g", float64(1.23456789e3), "1234.56789" }, + FmtTest{ "%g", float64(1.23456789e-3), "0.00123456789" }, + FmtTest{ "%g", float64(1.23456789e20), "1.23456789e+20" }, + FmtTest{ "%20e", sys.Inf(1), " +Inf" }, + FmtTest{ "%-20f", sys.Inf(-1), "-Inf " }, + FmtTest{ "%20g", sys.NaN(), " NaN" }, +} + +export func TestSprintf(t *testing.T) { + for i := 0; i < len(fmttests); i++ { + tt := fmttests[i]; + s := fmt.sprintf(tt.fmt, tt.val); + if s != tt.out { + if ss, ok := tt.val.(string); ok { + // Don't requote the already-quoted strings. + // It's too confusing to read the errors. + t.Errorf("fmt.sprintf(%q, %q) = %s want %s", tt.fmt, tt.val, s, tt.out); + } else { + t.Errorf("fmt.sprintf(%q, %v) = %q want %q", tt.fmt, tt.val, s, tt.out); + } + } + } +} + diff --git a/src/lib/fmt/format.go b/src/lib/fmt/format.go index 058c61944..64d6c9bc9 100644 --- a/src/lib/fmt/format.go +++ b/src/lib/fmt/format.go @@ -4,7 +4,9 @@ package fmt -import "strconv" +import ( + "strconv"; +) /* Raw formatter. See print.go for a more palatable interface. @@ -39,11 +41,22 @@ export type Fmt struct { wid_present bool; prec int; prec_present bool; + // flags + minus bool; + plus bool; + sharp bool; + space bool; + zero bool; } func (f *Fmt) clearflags() { f.wid_present = false; f.prec_present = false; + f.minus = false; + f.plus = false; + f.sharp = false; + f.space = false; + f.zero = false; } func (f *Fmt) clearbuf() { @@ -101,24 +114,28 @@ func (f *Fmt) w(x int) *Fmt { return f; } -// append s to buf, padded on left (w > 0) or right (w < 0) +// append s to buf, padded on left (w > 0) or right (w < 0 or f.minus) // 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; + left := !f.minus; w := f.wid; if w < 0 { left = false; w = -w; } w -= len(s); + padchar := byte(' '); + if left && f.zero { + padchar = '0'; + } if w > 0 { if w > NByte { w = NByte; } buf := new([]byte, w); for i := 0; i < w; i++ { - buf[i] = ' '; + buf[i] = padchar; } if left { s = string(buf) + s; @@ -163,16 +180,35 @@ func (f *Fmt) integer(a int64, base uint, is_signed bool, digits *string) string if negative { a = -a; } - i := putint(&buf, NByte-1, uint64(base), uint64(a), digits); + + // two ways to ask for extra leading zero digits: %.3d or %03d. + // apparently the first cancels the second. + prec := 0; if f.prec_present { - for i > 0 && f.prec > (NByte-1-i) { - buf[i] = '0'; - i--; + prec = f.prec; + f.zero = false; + } else if f.zero && f.wid_present && !f.minus && f.wid > 0{ + prec = f.wid; + if negative || f.plus || f.space { + prec--; // leave room for sign } } + + i := putint(&buf, NByte-1, uint64(base), uint64(a), digits); + for i > 0 && prec > (NByte-1-i) { + buf[i] = '0'; + i--; + } + if negative { buf[i] = '-'; i--; + } else if f.plus { + buf[i] = '+'; + i--; + } else if f.space { + buf[i] = ' '; + i--; } return string(buf)[i+1:NByte]; } @@ -334,6 +370,44 @@ func (f *Fmt) s(s string) *Fmt { return f; } +// hexadecimal string +func (f *Fmt) sx(s string) *Fmt { + t := ""; + for i := 0; i < len(s); i++ { + v := s[i]; + t += string(ldigits[v>>4]); + t += string(ldigits[v&0xF]); + } + f.pad(t); + f.clearflags(); + return f; +} + +func (f *Fmt) sX(s string) *Fmt { + t := ""; + for i := 0; i < len(s); i++ { + v := s[i]; + t += string(udigits[v>>4]); + t += string(udigits[v&0xF]); + } + f.pad(t); + f.clearflags(); + return f; +} + +// quoted string +func (f *Fmt) q(s string) *Fmt { + var quoted string; + if f.sharp && strconv.CanBackquote(s) { + quoted = "`"+s+"`"; + } else { + quoted = strconv.Quote(s); + } + f.pad(quoted); + f.clearflags(); + return f; +} + // floating-point func Prec(f *Fmt, def int) int { @@ -370,7 +444,7 @@ func (f *Fmt) fb64(a float64) *Fmt { // cannot defer to float64 versions // because it will get rounding wrong in corner cases. func (f *Fmt) e32(a float32) *Fmt { - return FmtString(f, strconv.ftoa32(a, 'e', Prec(f, -1))); + return FmtString(f, strconv.ftoa32(a, 'e', Prec(f, 6))); } func (f *Fmt) f32(a float32) *Fmt { diff --git a/src/lib/fmt/print.go b/src/lib/fmt/print.go index ce7a4f2d3..5a2dc67e9 100644 --- a/src/lib/fmt/print.go +++ b/src/lib/fmt/print.go @@ -186,6 +186,19 @@ export func sprintln(a ...) string { return s; } + +// Get the i'th arg of the struct value. +// If the arg itself is an interface, return a value for +// the thing inside the interface, not the interface itself. +func getField(v reflect.StructValue, i int) reflect.Value { + val := v.Field(i); + if val.Kind() == reflect.InterfaceKind { + inter := val.(reflect.InterfaceValue).Get(); + return reflect.NewValue(inter); + } + return val; +} + // Getters for the fields of the argument structure. func getBool(v reflect.Value) (val bool, ok bool) { @@ -227,6 +240,9 @@ func getString(v reflect.Value) (val string, ok bool) { case reflect.StringKind: return v.(reflect.StringValue).Get(), true; } + if valb, okb := v.Interface().(*[]byte); okb { + return string(valb), true; + } return "", false; } @@ -280,12 +296,6 @@ 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' { @@ -371,10 +381,28 @@ func (p *P) doprintf(format string, v reflect.StructValue) { i += w; continue; } - // saw % - do we have %20 (width)? - p.wid, p.wid_ok, i = parsenum(format, i+1, end); + i++; + // flags + F: for ; i < end; i++ { + switch format[i] { + case '#': + p.fmt.sharp = true; + case '0': + p.fmt.zero = true; + case '+': + p.fmt.plus = true; + case '-': + p.fmt.minus = true; + case ' ': + p.fmt.space = true; + default: + break F; + } + } + // do we have 20 (width)? + p.wid, p.wid_ok, i = parsenum(format, i, end); p.prec_ok = false; - // do we have %.20 (precision)? + // do we have .20 (precision)? if i < end && format[i] == '.' { p.prec, p.prec_ok, i = parsenum(format, i+1, end); } @@ -391,7 +419,7 @@ func (p *P) doprintf(format string, v reflect.StructValue) { p.addstr("(missing)"); continue; } - field := v.Field(fieldnum); + field := getField(v, fieldnum); fieldnum++; if c != 'T' { // don't want thing to describe itself if we're asking for its type if formatter, ok := field.Interface().(Format); ok { @@ -463,6 +491,20 @@ func (p *P) doprintf(format string, v reflect.StructValue) { } else { s = p.fmt.ux64(uint64(v)).str() } + } else if v, ok := getString(field); ok { + s = p.fmt.sx(v).str(); + } else { + goto badtype + } + case 'X': + if v, signed, ok := getInt(field); ok { + if signed { + s = p.fmt.X64(v).str() + } else { + s = p.fmt.uX64(uint64(v)).str() + } + } else if v, ok := getString(field); ok { + s = p.fmt.sX(v).str(); } else { goto badtype } @@ -500,6 +542,12 @@ func (p *P) doprintf(format string, v reflect.StructValue) { } else { goto badtype } + case 'q': + if v, ok := getString(field); ok { + s = p.fmt.q(v).str() + } else { + goto badtype + } // pointer case 'p': @@ -530,7 +578,7 @@ func (p *P) doprintf(format string, v reflect.StructValue) { if fieldnum < v.Len() { p.addstr("?(extra "); for ; fieldnum < v.Len(); fieldnum++ { - p.addstr(v.Field(fieldnum).Type().String()); + p.addstr(getField(v, fieldnum).Type().String()); if fieldnum + 1 < v.Len() { p.addstr(", "); } @@ -543,7 +591,7 @@ func (p *P) doprint(v reflect.StructValue, addspace, addnewline bool) { prev_string := false; for fieldnum := 0; fieldnum < v.Len(); fieldnum++ { // always add spaces if we're doing println - field := v.Field(fieldnum); + field := getField(v, fieldnum); if fieldnum > 0 { if addspace { p.add(' ') diff --git a/src/lib/reflect/all_test.go b/src/lib/reflect/all_test.go index 203413e55..a6ac1a7c7 100644 --- a/src/lib/reflect/all_test.go +++ b/src/lib/reflect/all_test.go @@ -283,3 +283,14 @@ export func TestAll(tt *testing.T) { // TODO(r): wrap up better println(a[i]); } } + +export func TestInterfaceGet(t *testing.T) { + var inter struct { e interface{ } }; + inter.e = 123.456; + v1 := reflect.NewValue(&inter); + v2 := v1.(reflect.PtrValue).Sub().(reflect.StructValue).Field(0); + assert(v2.Type().String(), "interface { }"); + i2 := v2.(reflect.InterfaceValue).Get(); + v3 := reflect.NewValue(i2); + assert(v3.Type().String(), "float"); +} diff --git a/src/lib/reflect/cast_amd64.s b/src/lib/reflect/cast_amd64.s index a1363718c..d0e97a3c2 100644 --- a/src/lib/reflect/cast_amd64.s +++ b/src/lib/reflect/cast_amd64.s @@ -181,3 +181,13 @@ TEXT reflect·PtrRuntimeArrayToAddr(SB),7,$-8 MOVQ AX, 16(SP) RET +TEXT reflect·AddrToPtrInterface(SB),7,$-8 + MOVQ 8(SP), AX + MOVQ AX, 16(SP) + RET + +TEXT reflect·PtrInterfaceToAddr(SB),7,$-8 + MOVQ 8(SP), AX + MOVQ AX, 16(SP) + RET + diff --git a/src/lib/reflect/gencast.sh b/src/lib/reflect/gencast.sh index af90d8df2..afb60de1a 100755 --- a/src/lib/reflect/gencast.sh +++ b/src/lib/reflect/gencast.sh @@ -38,4 +38,5 @@ Float80 String Bool RuntimeArray +Interface ! diff --git a/src/lib/reflect/value.go b/src/lib/reflect/value.go index ef6ddce7a..65d4b5ca9 100644 --- a/src/lib/reflect/value.go +++ b/src/lib/reflect/value.go @@ -36,14 +36,13 @@ func AddrToPtrString(Addr) *string func AddrToPtrBool(Addr) *bool func AddrToPtrRuntimeArray(Addr) *RuntimeArray func PtrRuntimeArrayToAddr(*RuntimeArray) Addr - -export type Empty interface {} // TODO(r): Delete when no longer needed? +func AddrToPtrInterface(Addr) *interface{} export type Value interface { Kind() int; Type() Type; Addr() Addr; - Interface() Empty; + Interface() interface {}; } // Common fields and functionality for all values @@ -66,7 +65,7 @@ func (c *Common) Addr() Addr { return c.addr } -func (c *Common) Interface() Empty { +func (c *Common) Interface() interface {} { return sys.unreflect(*AddrToPtrAddr(c.addr), c.typ.String()); } @@ -714,12 +713,17 @@ func StructCreator(typ Type, addr Addr) Value { export type InterfaceValue interface { Kind() int; Type() Type; + Get() interface {}; } type InterfaceValueStruct struct { Common } +func (v *InterfaceValueStruct) Get() interface{} { + return *AddrToPtrInterface(v.addr); +} + func InterfaceCreator(typ Type, addr Addr) Value { return &InterfaceValueStruct{ Common{InterfaceKind, typ, addr} } } @@ -824,7 +828,7 @@ export func NewOpenArrayValue(typ ArrayType, len, cap int) ArrayValue { return NewValueAddr(typ, PtrRuntimeArrayToAddr(array)); } -export func NewValue(e Empty) Value { +export func NewValue(e interface {}) Value { value, typestring := sys.reflect(e); p, ok := typecache[typestring]; if !ok { diff --git a/src/lib/strconv/Makefile b/src/lib/strconv/Makefile index 399360a93..cf74d58f7 100644 --- a/src/lib/strconv/Makefile +++ b/src/lib/strconv/Makefile @@ -33,8 +33,9 @@ coverage: packages O1=\ atoi.$O\ - decimal.$O\ itoa.$O\ + decimal.$O\ + quote.$O\ O2=\ ftoa.$O\ @@ -45,7 +46,7 @@ O3=\ strconv.a: a1 a2 a3 a1: $(O1) - $(AR) grc strconv.a atoi.$O decimal.$O itoa.$O + $(AR) grc strconv.a atoi.$O itoa.$O decimal.$O quote.$O rm -f $(O1) a2: $(O2) diff --git a/src/lib/strconv/ftoa_test.go b/src/lib/strconv/ftoa_test.go index 914ecd9e3..643abb0dd 100644 --- a/src/lib/strconv/ftoa_test.go +++ b/src/lib/strconv/ftoa_test.go @@ -25,6 +25,7 @@ var ftests = []Test { Test{ 1, 'g', 5, "1" }, Test{ 1, 'g', -1, "1" }, Test{ 20, 'g', -1, "20" }, + Test{ 1234567.8, 'g', -1, "1.2345678e+06" }, Test{ 200000, 'g', -1, "200000" }, Test{ 2000000, 'g', -1, "2e+06" }, diff --git a/src/lib/strconv/quote.go b/src/lib/strconv/quote.go new file mode 100644 index 000000000..122af92d7 --- /dev/null +++ b/src/lib/strconv/quote.go @@ -0,0 +1,76 @@ +// 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 strconv + +import ( + "utf8"; +) + +const ldigits = "0123456789abcdef" +const udigits = "0123456789ABCDEF" + +export func Quote(s string) string { + t := `"`; + for i := 0; i < len(s); i++ { + switch { + case s[i] == '"': + t += `\"`; + case s[i] == '\\': + t += `\\`; + case ' ' <= s[i] && s[i] <= '~': + t += string(s[i]); + case s[i] == '\a': + t += `\a`; + case s[i] == '\b': + t += `\b`; + case s[i] == '\f': + t += `\f`; + case s[i] == '\n': + t += `\n`; + case s[i] == '\r': + t += `\r`; + case s[i] == '\t': + t += `\t`; + case s[i] == '\v': + t += `\v`; + + case utf8.FullRuneInString(s, i): + r, size := utf8.DecodeRuneInString(s, i); + if r == utf8.RuneError && size == 1 { + goto EscX; + } + i += size-1; // i++ on next iteration + if r < 0x10000 { + t += `\u`; + for j:=uint(0); j<4; j++ { + t += string(ldigits[(r>>(12-4*j))&0xF]); + } + } else { + t += `\U`; + for j:=uint(0); j<8; j++ { + t += string(ldigits[(r>>(28-4*j))&0xF]); + } + } + + default: + EscX: + t += `\x`; + t += string(ldigits[s[i]>>4]); + t += string(ldigits[s[i]&0xF]); + } + } + t += `"`; + return t; +} + +export func CanBackquote(s string) bool { + for i := 0; i < len(s); i++ { + if s[i] < ' ' || s[i] == '`' { + return false; + } + } + return true; +} + diff --git a/src/lib/strconv/quote_test.go b/src/lib/strconv/quote_test.go new file mode 100644 index 000000000..2c0e98ed5 --- /dev/null +++ b/src/lib/strconv/quote_test.go @@ -0,0 +1,87 @@ +// 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 strconv + +import ( + "strconv"; + "testing"; +) + +type QuoteTest struct { + in string; + out string; +} + +var quotetests = []QuoteTest { + QuoteTest{ "\a\b\f\r\n\t\v", `"\a\b\f\r\n\t\v"` }, + QuoteTest{ "\\", `"\\"` }, + QuoteTest{ "abc\xffdef", `"abc\xffdef"` }, + QuoteTest{ "\u263a", `"\u263a"` }, + QuoteTest{ "\U0010ffff", `"\U0010ffff"` }, +} + +export func TestQuote(t *testing.T) { + for i := 0; i < len(quotetests); i++ { + tt := quotetests[i]; + if out := Quote(tt.in); out != tt.out { + t.Errorf("Quote(%s) = %s, want %s", tt.in, out, tt.out); + } + } +} + +type CanBackquoteTest struct { + in string; + out bool; +} + +var canbackquotetests = []CanBackquoteTest { + CanBackquoteTest{ "`", false }, + CanBackquoteTest{ string(0), false }, + CanBackquoteTest{ string(1), false }, + CanBackquoteTest{ string(2), false }, + CanBackquoteTest{ string(3), false }, + CanBackquoteTest{ string(4), false }, + CanBackquoteTest{ string(5), false }, + CanBackquoteTest{ string(6), false }, + CanBackquoteTest{ string(7), false }, + CanBackquoteTest{ string(8), false }, + CanBackquoteTest{ string(9), false }, + CanBackquoteTest{ string(10), false }, + CanBackquoteTest{ string(11), false }, + CanBackquoteTest{ string(12), false }, + CanBackquoteTest{ string(13), false }, + CanBackquoteTest{ string(14), false }, + CanBackquoteTest{ string(15), false }, + CanBackquoteTest{ string(16), false }, + CanBackquoteTest{ string(17), false }, + CanBackquoteTest{ string(18), false }, + CanBackquoteTest{ string(19), false }, + CanBackquoteTest{ string(20), false }, + CanBackquoteTest{ string(21), false }, + CanBackquoteTest{ string(22), false }, + CanBackquoteTest{ string(23), false }, + CanBackquoteTest{ string(24), false }, + CanBackquoteTest{ string(25), false }, + CanBackquoteTest{ string(26), false }, + CanBackquoteTest{ string(27), false }, + CanBackquoteTest{ string(28), false }, + CanBackquoteTest{ string(29), false }, + CanBackquoteTest{ string(30), false }, + CanBackquoteTest{ string(31), false }, + CanBackquoteTest{ `' !"#$%&'()*+,-./:;<=>?@[\]^_{|}~`, true }, + CanBackquoteTest{ `0123456789`, true }, + CanBackquoteTest{ `ABCDEFGHIJKLMNOPQRSTUVWXYZ`, true }, + CanBackquoteTest{ `abcdefghijklmnopqrstuvwxyz`, true }, + CanBackquoteTest{ `☺`, true }, +} + +export func TestCanBackquote(t *testing.T) { + for i := 0; i < len(canbackquotetests); i++ { + tt := canbackquotetests[i]; + if out := CanBackquote(tt.in); out != tt.out { + t.Errorf("CanBackquote(%q) = %v, want %v", tt.in, out, tt.out); + } + } +} diff --git a/src/lib/utf8.go b/src/lib/utf8.go index 7b0f15d8f..9ece25f6a 100644 --- a/src/lib/utf8.go +++ b/src/lib/utf8.go @@ -9,7 +9,8 @@ package utf8 export const ( RuneError = 0xFFFD; RuneSelf = 0x80; - RuneMax = 1<<21 - 1; + RuneMax = 0x10FFFF; + UTFMax = 4; ) const ( @@ -32,7 +33,8 @@ const ( ) func DecodeRuneInternal(p *[]byte) (rune, size int, short bool) { - if len(p) < 1 { + n := len(p); + if n < 1 { return RuneError, 0, true; } c0 := p[0]; @@ -48,7 +50,7 @@ func DecodeRuneInternal(p *[]byte) (rune, size int, short bool) { } // need first continuation byte - if len(p) < 2 { + if n < 2 { return RuneError, 1, true } c1 := p[1]; @@ -66,7 +68,7 @@ func DecodeRuneInternal(p *[]byte) (rune, size int, short bool) { } // need second continuation byte - if len(p) < 3 { + if n < 3 { return RuneError, 1, true } c2 := p[2]; @@ -84,7 +86,7 @@ func DecodeRuneInternal(p *[]byte) (rune, size int, short bool) { } // need third continuation byte - if len(p) < 4 { + if n < 4 { return RuneError, 1, true } c3 := p[3]; @@ -105,17 +107,103 @@ func DecodeRuneInternal(p *[]byte) (rune, size int, short bool) { return RuneError, 1, false } +func DecodeRuneInStringInternal(s string, i int) (rune, size int, short bool) { + n := len(s) - i; + if n < 1 { + return RuneError, 0, true; + } + c0 := s[i]; + + // 1-byte, 7-bit sequence? + if c0 < Tx { + return int(c0), 1, false + } + + // unexpected continuation byte? + if c0 < T2 { + return RuneError, 1, false + } + + // need first continuation byte + if n < 2 { + return RuneError, 1, true + } + c1 := s[i+1]; + if c1 < Tx || T2 <= c1 { + return RuneError, 1, false + } + + // 2-byte, 11-bit sequence? + if c0 < T3 { + rune = int(c0&Mask2)<<6 | int(c1&Maskx); + if rune <= Rune1Max { + return RuneError, 1, false + } + return rune, 2, false + } + + // need second continuation byte + if n < 3 { + return RuneError, 1, true + } + c2 := s[i+2]; + if c2 < Tx || T2 <= c2 { + return RuneError, 1, false + } + + // 3-byte, 16-bit sequence? + if c0 < T4 { + rune = int(c0&Mask3)<<12 | int(c1&Maskx)<<6 | int(c2&Maskx); + if rune <= Rune2Max { + return RuneError, 1, false + } + return rune, 3, false + } + + // need third continuation byte + if n < 4 { + return RuneError, 1, true + } + c3 := s[i+3]; + if c3 < Tx || T2 <= c3 { + return RuneError, 1, false + } + + // 4-byte, 21-bit sequence? + if c0 < T5 { + rune = int(c0&Mask4)<<18 | int(c1&Maskx)<<12 | int(c2&Maskx)<<6 | int(c3&Maskx); + if rune <= Rune3Max { + return RuneError, 1, false + } + return rune, 4, false + } + + // error + return RuneError, 1, false +} + export func FullRune(p *[]byte) bool { rune, size, short := DecodeRuneInternal(p); return !short } +export func FullRuneInString(s string, i int) bool { + rune, size, short := DecodeRuneInStringInternal(s, i); + return !short +} + export func DecodeRune(p *[]byte) (rune, size int) { var short bool; rune, size, short = DecodeRuneInternal(p); return; } +export func DecodeRuneInString(s string, i int) (rune, size int) { + var short bool; + rune, size, short = DecodeRuneInStringInternal(s, i); + return; +} + export func RuneLen(rune int) int { switch { case rune <= Rune1Max: diff --git a/src/lib/utf8_test.go b/src/lib/utf8_test.go index 550f4ba14..18c06c2ce 100644 --- a/src/lib/utf8_test.go +++ b/src/lib/utf8_test.go @@ -44,27 +44,6 @@ var utf8map = []Utf8Map { Utf8Map{ 0x10ffff, "\xf4\x8f\xbf\xbf" }, } -func CEscape(s *[]byte) string { - t := "\""; - for i := 0; i < len(s); i++ { - switch { - case s[i] == '\\' || s[i] == '"': - t += `\`; - t += string(s[i]); - case s[i] == '\n': - t += `\n`; - case s[i] == '\t': - t += `\t`; - case ' ' <= s[i] && s[i] <= '~': - t += string(s[i]); - default: - t += fmt.sprintf(`\x%02x`, s[i]); - } - } - t += "\""; - return t; -} - func Bytes(s string) *[]byte { b := new([]byte, len(s)+1); if !syscall.StringToBytes(b, s) { @@ -78,10 +57,19 @@ export func TestFullRune(t *testing.T) { m := utf8map[i]; b := Bytes(m.str); if !utf8.FullRune(b) { - t.Errorf("FullRune(%s) (rune %04x) = false, want true", CEscape(b), m.rune); + t.Errorf("FullRune(%q) (rune %04x) = false, want true", b, m.rune); + } + s := "xx"+m.str; + if !utf8.FullRuneInString(s, 2) { + t.Errorf("FullRuneInString(%q, 2) (rune %04x) = false, want true", s, m.rune); } - if b1 := b[0:len(b)-1]; utf8.FullRune(b1) { - t.Errorf("FullRune(%s) = true, want false", CEscape(b1)); + b1 := b[0:len(b)-1]; + if utf8.FullRune(b1) { + t.Errorf("FullRune(%q) = true, want false", b1); + } + s1 := "xxx"+string(b1); + if utf8.FullRuneInString(s1, 3) { + t.Errorf("FullRune(%q, 3) = true, want false", s1); } } } @@ -106,7 +94,7 @@ export func TestEncodeRune(t *testing.T) { n := utf8.EncodeRune(m.rune, &buf); b1 := (&buf)[0:n]; if !EqualBytes(b, b1) { - t.Errorf("EncodeRune(0x%04x) = %s want %s", m.rune, CEscape(b1), CEscape(b)); + t.Errorf("EncodeRune(0x%04x) = %q want %q", m.rune, b1, b); } } } @@ -117,23 +105,38 @@ export func TestDecodeRune(t *testing.T) { b := Bytes(m.str); rune, size := utf8.DecodeRune(b); if rune != m.rune || size != len(b) { - t.Errorf("DecodeRune(%s) = 0x%04x, %d want 0x%04x, %d", CEscape(b), rune, size, m.rune, len(b)); + t.Errorf("DecodeRune(%q) = 0x%04x, %d want 0x%04x, %d", b, rune, size, m.rune, len(b)); + } + s := "xx"+m.str; + rune, size = utf8.DecodeRuneInString(s, 2); + if rune != m.rune || size != len(b) { + t.Errorf("DecodeRune(%q, 2) = 0x%04x, %d want 0x%04x, %d", s, rune, size, m.rune, len(b)); } // there's an extra byte that Bytes left behind - make sure trailing byte works rune, size = utf8.DecodeRune(b[0:cap(b)]); if rune != m.rune || size != len(b) { - t.Errorf("DecodeRune(%s) = 0x%04x, %d want 0x%04x, %d", CEscape(b), rune, size, m.rune, len(b)); + t.Errorf("DecodeRune(%q) = 0x%04x, %d want 0x%04x, %d", b, rune, size, m.rune, len(b)); + } + s = "x"+m.str+"\x00"; + rune, size = utf8.DecodeRuneInString(s, 1); + if rune != m.rune || size != len(b) { + t.Errorf("DecodeRuneInString(%q, 1) = 0x%04x, %d want 0x%04x, %d", s, rune, size, m.rune, len(b)); } // make sure missing bytes fail - rune, size = utf8.DecodeRune(b[0:len(b)-1]); wantsize := 1; if wantsize >= len(b) { wantsize = 0; } + rune, size = utf8.DecodeRune(b[0:len(b)-1]); + if rune != RuneError || size != wantsize { + t.Errorf("DecodeRune(%q) = 0x%04x, %d want 0x%04x, %d", b[0:len(b)-1], rune, size, RuneError, wantsize); + } + s = "xxx"+m.str[0:len(m.str)-1]; + rune, size = utf8.DecodeRuneInString(s, 3); if rune != RuneError || size != wantsize { - t.Errorf("DecodeRune(%s) = 0x%04x, %d want 0x%04x, %d", CEscape(b[0:len(b)-1]), rune, size, RuneError, wantsize); + t.Errorf("DecodeRuneInString(%q, 3) = 0x%04x, %d want 0x%04x, %d", s, rune, size, RuneError, wantsize); } // make sure bad sequences fail @@ -144,7 +147,12 @@ export func TestDecodeRune(t *testing.T) { } rune, size = utf8.DecodeRune(b); if rune != RuneError || size != 1 { - t.Errorf("DecodeRune(%s) = 0x%04x, %d want 0x%04x, %d", CEscape(b), rune, size, RuneError, 1); + t.Errorf("DecodeRune(%q) = 0x%04x, %d want 0x%04x, %d", b, rune, size, RuneError, 1); + } + s = "xxxx"+string(b); + rune, size = utf8.DecodeRune(b); + if rune != RuneError || size != 1 { + t.Errorf("DecodeRuneInString(%q, 4) = 0x%04x, %d want 0x%04x, %d", s, rune, size, RuneError, 1); } } } |