summaryrefslogtreecommitdiff
path: root/src/pkg/strconv
diff options
context:
space:
mode:
Diffstat (limited to 'src/pkg/strconv')
-rw-r--r--src/pkg/strconv/atob.go4
-rw-r--r--src/pkg/strconv/atob_test.go2
-rw-r--r--src/pkg/strconv/decimal.go2
-rw-r--r--src/pkg/strconv/quote.go126
-rw-r--r--src/pkg/strconv/quote_test.go81
5 files changed, 152 insertions, 63 deletions
diff --git a/src/pkg/strconv/atob.go b/src/pkg/strconv/atob.go
index 69fa2292a..98ce75079 100644
--- a/src/pkg/strconv/atob.go
+++ b/src/pkg/strconv/atob.go
@@ -7,8 +7,8 @@ package strconv
import "os"
// Atob returns the boolean value represented by the string.
-// It accepts 1, t, T, TRUE, true, 0, f, F, FALSE, false. Any other value returns
-// an error.
+// It accepts 1, t, T, TRUE, true, True, 0, f, F, FALSE, false, False.
+// Any other value returns an error.
func Atob(str string) (value bool, err os.Error) {
switch str {
case "1", "t", "T", "true", "TRUE", "True":
diff --git a/src/pkg/strconv/atob_test.go b/src/pkg/strconv/atob_test.go
index 497df5b18..541e60d1e 100644
--- a/src/pkg/strconv/atob_test.go
+++ b/src/pkg/strconv/atob_test.go
@@ -24,11 +24,13 @@ var atobtests = []atobTest{
{"F", false, nil},
{"FALSE", false, nil},
{"false", false, nil},
+ {"False", false, nil},
{"1", true, nil},
{"t", true, nil},
{"T", true, nil},
{"TRUE", true, nil},
{"true", true, nil},
+ {"True", true, nil},
}
func TestAtob(t *testing.T) {
diff --git a/src/pkg/strconv/decimal.go b/src/pkg/strconv/decimal.go
index 3a5cf1ba6..783065bfb 100644
--- a/src/pkg/strconv/decimal.go
+++ b/src/pkg/strconv/decimal.go
@@ -108,7 +108,7 @@ func newDecimal(i uint64) *decimal {
}
// Maximum shift that we can do in one pass without overflow.
-// Signed int has 31 bits, and we have to be able to accomodate 9<<k.
+// Signed int has 31 bits, and we have to be able to accommodate 9<<k.
const maxShift = 27
// Binary shift right (* 2) by k bits. k <= maxShift to avoid overflow.
diff --git a/src/pkg/strconv/quote.go b/src/pkg/strconv/quote.go
index ed5889723..98b19d3a2 100644
--- a/src/pkg/strconv/quote.go
+++ b/src/pkg/strconv/quote.go
@@ -14,63 +14,107 @@ import (
const lowerhex = "0123456789abcdef"
-// Quote returns a double-quoted Go string literal
-// representing s. The returned string s uses Go escape
-// sequences (\t, \n, \xFF, \u0100) for control characters
-// and non-ASCII characters.
-func Quote(s string) string {
+func quoteWith(s string, quote byte, ASCIIonly bool) string {
var buf bytes.Buffer
- buf.WriteByte('"')
- for ; len(s) > 0; s = s[1:] {
- switch c := s[0]; {
- case c == '"':
- buf.WriteString(`\"`)
- case c == '\\':
- buf.WriteString(`\\`)
- case ' ' <= c && c <= '~':
- buf.WriteString(string(c))
- case c == '\a':
+ buf.WriteByte(quote)
+ for width := 0; len(s) > 0; s = s[width:] {
+ rune := int(s[0])
+ width = 1
+ if rune >= utf8.RuneSelf {
+ rune, width = utf8.DecodeRuneInString(s)
+ }
+ if width == 1 && rune == utf8.RuneError {
+ goto printEscX
+ }
+ if rune == int(quote) || rune == '\\' { // always backslashed
+ buf.WriteByte('\\')
+ buf.WriteByte(byte(rune))
+ continue
+ }
+ if ASCIIonly {
+ if rune <= unicode.MaxASCII && unicode.IsPrint(rune) {
+ buf.WriteRune(rune)
+ continue
+ }
+ } else if unicode.IsPrint(rune) {
+ buf.WriteRune(rune)
+ continue
+ }
+ switch rune {
+ case '\a':
buf.WriteString(`\a`)
- case c == '\b':
+ case '\b':
buf.WriteString(`\b`)
- case c == '\f':
+ case '\f':
buf.WriteString(`\f`)
- case c == '\n':
+ case '\n':
buf.WriteString(`\n`)
- case c == '\r':
+ case '\r':
buf.WriteString(`\r`)
- case c == '\t':
+ case '\t':
buf.WriteString(`\t`)
- case c == '\v':
+ case '\v':
buf.WriteString(`\v`)
-
- case c >= utf8.RuneSelf && utf8.FullRuneInString(s):
- r, size := utf8.DecodeRuneInString(s)
- if r == utf8.RuneError && size == 1 {
- goto EscX
- }
- s = s[size-1:] // next iteration will slice off 1 more
- if r < 0x10000 {
+ default:
+ switch {
+ case rune < ' ':
+ printEscX:
+ buf.WriteString(`\x`)
+ buf.WriteByte(lowerhex[s[0]>>4])
+ buf.WriteByte(lowerhex[s[0]&0xF])
+ case rune > unicode.MaxRune:
+ rune = 0xFFFD
+ fallthrough
+ case rune < 0x10000:
buf.WriteString(`\u`)
- for j := uint(0); j < 4; j++ {
- buf.WriteByte(lowerhex[(r>>(12-4*j))&0xF])
+ for s := 12; s >= 0; s -= 4 {
+ buf.WriteByte(lowerhex[rune>>uint(s)&0xF])
}
- } else {
+ default:
buf.WriteString(`\U`)
- for j := uint(0); j < 8; j++ {
- buf.WriteByte(lowerhex[(r>>(28-4*j))&0xF])
+ for s := 28; s >= 0; s -= 4 {
+ buf.WriteByte(lowerhex[rune>>uint(s)&0xF])
}
}
-
- default:
- EscX:
- buf.WriteString(`\x`)
- buf.WriteByte(lowerhex[c>>4])
- buf.WriteByte(lowerhex[c&0xF])
}
}
- buf.WriteByte('"')
+ buf.WriteByte(quote)
return buf.String()
+
+}
+
+// Quote returns a double-quoted Go string literal representing s. The
+// returned string uses Go escape sequences (\t, \n, \xFF, \u0100) for
+// control characters and non-printable characters as defined by
+// unicode.IsPrint.
+func Quote(s string) string {
+ return quoteWith(s, '"', false)
+}
+
+// QuoteToASCII returns a double-quoted Go string literal representing s.
+// The returned string uses Go escape sequences (\t, \n, \xFF, \u0100) for
+// non-ASCII characters and non-printable characters as defined by
+// unicode.IsPrint.
+func QuoteToASCII(s string) string {
+ return quoteWith(s, '"', true)
+}
+
+// QuoteRune returns a single-quoted Go character literal representing the
+// rune. The returned string uses Go escape sequences (\t, \n, \xFF, \u0100)
+// for control characters and non-printable characters as defined by
+// unicode.IsPrint.
+func QuoteRune(rune int) string {
+ // TODO: avoid the allocation here.
+ return quoteWith(string(rune), '\'', false)
+}
+
+// QuoteRuneToASCII returns a single-quoted Go character literal representing
+// the rune. The returned string uses Go escape sequences (\t, \n, \xFF,
+// \u0100) for non-ASCII characters and non-printable characters as defined
+// by unicode.IsPrint.
+func QuoteRuneToASCII(rune int) string {
+ // TODO: avoid the allocation here.
+ return quoteWith(string(rune), '\'', true)
}
// CanBackquote returns whether the string s would be
diff --git a/src/pkg/strconv/quote_test.go b/src/pkg/strconv/quote_test.go
index 1235fcb9a..4d615db44 100644
--- a/src/pkg/strconv/quote_test.go
+++ b/src/pkg/strconv/quote_test.go
@@ -11,28 +11,70 @@ import (
)
type quoteTest struct {
- in string
- out string
+ in string
+ out string
+ ascii string
}
var quotetests = []quoteTest{
- {"\a\b\f\r\n\t\v", `"\a\b\f\r\n\t\v"`},
- {"\\", `"\\"`},
- {"abc\xffdef", `"abc\xffdef"`},
- {"\u263a", `"\u263a"`},
- {"\U0010ffff", `"\U0010ffff"`},
- {"\x04", `"\x04"`},
+ {"\a\b\f\r\n\t\v", `"\a\b\f\r\n\t\v"`, `"\a\b\f\r\n\t\v"`},
+ {"\\", `"\\"`, `"\\"`},
+ {"abc\xffdef", `"abc\xffdef"`, `"abc\xffdef"`},
+ {"\u263a", `"☺"`, `"\u263a"`},
+ {"\U0010ffff", `"\U0010ffff"`, `"\U0010ffff"`},
+ {"\x04", `"\x04"`, `"\x04"`},
}
func TestQuote(t *testing.T) {
- for i := 0; i < len(quotetests); i++ {
- tt := quotetests[i]
+ for _, tt := range quotetests {
if out := Quote(tt.in); out != tt.out {
t.Errorf("Quote(%s) = %s, want %s", tt.in, out, tt.out)
}
}
}
+func TestQuoteToASCII(t *testing.T) {
+ for _, tt := range quotetests {
+ if out := QuoteToASCII(tt.in); out != tt.ascii {
+ t.Errorf("QuoteToASCII(%s) = %s, want %s", tt.in, out, tt.ascii)
+ }
+ }
+}
+
+type quoteRuneTest struct {
+ in int
+ out string
+ ascii string
+}
+
+var quoterunetests = []quoteRuneTest{
+ {'a', `'a'`, `'a'`},
+ {'\a', `'\a'`, `'\a'`},
+ {'\\', `'\\'`, `'\\'`},
+ {0xFF, `'ÿ'`, `'\u00ff'`},
+ {0x263a, `'☺'`, `'\u263a'`},
+ {0xfffd, `'�'`, `'\ufffd'`},
+ {0x0010ffff, `'\U0010ffff'`, `'\U0010ffff'`},
+ {0x0010ffff + 1, `'�'`, `'\ufffd'`},
+ {0x04, `'\x04'`, `'\x04'`},
+}
+
+func TestQuoteRune(t *testing.T) {
+ for _, tt := range quoterunetests {
+ if out := QuoteRune(tt.in); out != tt.out {
+ t.Errorf("QuoteRune(%U) = %s, want %s", tt.in, out, tt.out)
+ }
+ }
+}
+
+func TestQuoteRuneToASCII(t *testing.T) {
+ for _, tt := range quoterunetests {
+ if out := QuoteRuneToASCII(tt.in); out != tt.ascii {
+ t.Errorf("QuoteRuneToASCII(%U) = %s, want %s", tt.in, out, tt.ascii)
+ }
+ }
+}
+
type canBackquoteTest struct {
in string
out bool
@@ -80,15 +122,19 @@ var canbackquotetests = []canBackquoteTest{
}
func TestCanBackquote(t *testing.T) {
- for i := 0; i < len(canbackquotetests); i++ {
- tt := canbackquotetests[i]
+ for _, tt := range canbackquotetests {
if out := CanBackquote(tt.in); out != tt.out {
t.Errorf("CanBackquote(%q) = %v, want %v", tt.in, out, tt.out)
}
}
}
-var unquotetests = []quoteTest{
+type unQuoteTest struct {
+ in string
+ out string
+}
+
+var unquotetests = []unQuoteTest{
{`""`, ""},
{`"a"`, "a"},
{`"abc"`, "abc"},
@@ -146,23 +192,20 @@ var misquoted = []string{
}
func TestUnquote(t *testing.T) {
- for i := 0; i < len(unquotetests); i++ {
- tt := unquotetests[i]
+ for _, tt := range unquotetests {
if out, err := Unquote(tt.in); err != nil && out != tt.out {
t.Errorf("Unquote(%#q) = %q, %v want %q, nil", tt.in, out, err, tt.out)
}
}
// run the quote tests too, backward
- for i := 0; i < len(quotetests); i++ {
- tt := quotetests[i]
+ for _, tt := range quotetests {
if in, err := Unquote(tt.out); in != tt.in {
t.Errorf("Unquote(%#q) = %q, %v, want %q, nil", tt.out, in, err, tt.in)
}
}
- for i := 0; i < len(misquoted); i++ {
- s := misquoted[i]
+ for _, s := range misquoted {
if out, err := Unquote(s); out != "" || err != os.EINVAL {
t.Errorf("Unquote(%#q) = %q, %v want %q, %v", s, out, err, "", os.EINVAL)
}