diff options
author | Damian Gryski <[email protected]> | 2023-03-24 13:48:00 -0700 |
---|---|---|
committer | Ron Evans <[email protected]> | 2023-03-30 21:10:54 +0200 |
commit | 5faff2e13a52475e02cfbe9c0f0bbe54088ecdc7 (patch) | |
tree | adf45db8d4184a4fce528f64d5f5bfbd69ac986c /src/reflect | |
parent | 195de23d3b793f5e9188d4f0c0543cdb6bbf01dd (diff) | |
download | tinygo-5faff2e13a52475e02cfbe9c0f0bbe54088ecdc7.tar.gz tinygo-5faff2e13a52475e02cfbe9c0f0bbe54088ecdc7.zip |
reflect: add sipmlified strconv.Quote() implementation for struct tags
Diffstat (limited to 'src/reflect')
-rw-r--r-- | src/reflect/all_test.go | 2 | ||||
-rw-r--r-- | src/reflect/strconv.go | 99 | ||||
-rw-r--r-- | src/reflect/type.go | 6 |
3 files changed, 101 insertions, 6 deletions
diff --git a/src/reflect/all_test.go b/src/reflect/all_test.go index ce1abef7f..dea02077f 100644 --- a/src/reflect/all_test.go +++ b/src/reflect/all_test.go @@ -188,7 +188,6 @@ var typeTests = []pair{ }{}, "struct { a int8; b int8; c int8; d int8; e int8; f int32 }", }, - /* // TODO(tinygo): Reflects tags aren't properly quoted {struct { x struct { a int8 `reflect:"hi there"` @@ -203,6 +202,7 @@ var typeTests = []pair{ }{}, `struct { a int8 "reflect:\"hi \\x00there\\t\\n\\\"\\\\\"" }`, }, + /* // TODO(tinygo): Functions not supported {struct { x struct { f func(args ...int) diff --git a/src/reflect/strconv.go b/src/reflect/strconv.go index b75d51341..2d8131c9d 100644 --- a/src/reflect/strconv.go +++ b/src/reflect/strconv.go @@ -30,6 +30,10 @@ func unhex(b byte) (v rune, ok bool) { return } +const ( + lowerhex = "0123456789abcef" +) + // unquoteChar decodes the first character or byte in the escaped string // or character literal represented by the string s. // It returns four values: @@ -231,6 +235,101 @@ func unquote(s string) (string, error) { return string(buf), nil } +func quote(s string) string { + buf := make([]byte, 0, 3*len(s)/2) + const quote = '"' + + buf = append(buf, quote) + for width := 0; len(s) > 0; s = s[width:] { + r := rune(s[0]) + width = 1 + if r >= utf8.RuneSelf { + r, width = utf8.DecodeRuneInString(s) + } + if width == 1 && r == utf8.RuneError { + buf = append(buf, `\x`...) + buf = append(buf, lowerhex[s[0]>>4]) + buf = append(buf, lowerhex[s[0]&0xF]) + continue + } + buf = appendEscapedRune(buf, r) + } + buf = append(buf, quote) + return string(buf) +} + +func appendEscapedRune(buf []byte, r rune) []byte { + + const quote = '"' + + var runeTmp [utf8.UTFMax]byte + if r == rune(quote) || r == '\\' { // always backslashed + buf = append(buf, '\\') + buf = append(buf, byte(r)) + return buf + } + if isPrint(r) { + n := utf8.EncodeRune(runeTmp[:], r) + buf = append(buf, runeTmp[:n]...) + return buf + } + switch r { + case '\a': + buf = append(buf, `\a`...) + case '\b': + buf = append(buf, `\b`...) + case '\f': + buf = append(buf, `\f`...) + case '\n': + buf = append(buf, `\n`...) + case '\r': + buf = append(buf, `\r`...) + case '\t': + buf = append(buf, `\t`...) + case '\v': + buf = append(buf, `\v`...) + default: + switch { + case r < ' ' || r == 0x7f: + buf = append(buf, `\x`...) + buf = append(buf, lowerhex[byte(r)>>4]) + buf = append(buf, lowerhex[byte(r)&0xF]) + case !utf8.ValidRune(r): + r = 0xFFFD + fallthrough + case r < 0x10000: + buf = append(buf, `\u`...) + for s := 12; s >= 0; s -= 4 { + buf = append(buf, lowerhex[r>>uint(s)&0xF]) + } + default: + buf = append(buf, `\U`...) + for s := 28; s >= 0; s -= 4 { + buf = append(buf, lowerhex[r>>uint(s)&0xF]) + } + } + } + return buf +} + +// This is only used for struct tags. Assume +func isPrint(r rune) bool { + if r <= 0xFF { + if 0x20 <= r && r <= 0x7E { + // All the ASCII is printable from space through DEL-1. + return true + } + if 0xA1 <= r && r <= 0xFF { + // Similarly for ¡ through ÿ... + return r != 0xAD // ...except for the bizarre soft hyphen. + } + return false + } + + // TinyGo: Skip all other unicode processing + return false +} + // contains reports whether the string contains the byte c. func contains(s string, c byte) bool { return indexByteString(s, c) != -1 diff --git a/src/reflect/type.go b/src/reflect/type.go index 1c1a17bb9..814cf39a4 100644 --- a/src/reflect/type.go +++ b/src/reflect/type.go @@ -554,11 +554,7 @@ func (t *rawType) String() string { f := t.rawField(i) s += " " + f.Name + " " + f.Type.String() if f.Tag != "" { - // TODO(dgryski): The tag should be - // double-quoted and escaped; that requires - // strconv and reflectlite or our own Quote() - // implementation - s += " " + string(f.Tag) + s += " " + quote(string(f.Tag)) } // every field except the last needs a semicolon if i < numField-1 { |