aboutsummaryrefslogtreecommitdiffstats
path: root/asspb.go
diff options
context:
space:
mode:
authorRose Hogenson <rosehogenson@posteo.net>2025-10-26 10:07:57 -0700
committerRose Hogenson <rosehogenson@posteo.net>2025-10-26 10:07:57 -0700
commita57d0891f3a2cb25cd7824d2e0a7d620f6288864 (patch)
treede943b13e4f71943da6c03f3c5cca7ab56d6be30 /asspb.go
parentRefine numeric literal syntax (diff)
downloadccl-a57d0891f3a2cb25cd7824d2e0a7d620f6288864.tar.zst
Strings must be valid UTF-8
Diffstat (limited to 'asspb.go')
-rw-r--r--asspb.go48
1 files changed, 33 insertions, 15 deletions
diff --git a/asspb.go b/asspb.go
index 624f3ca..35cdc51 100644
--- a/asspb.go
+++ b/asspb.go
@@ -34,13 +34,15 @@
// # Strings
//
// Strings are written with " or ' and any sequence of intermediate bytes (with
-// the exception of escape sequences which are described below). "any bytes"
-// means that strings can contain newline without needing an escape sequence
+// the exception of escape sequences which are described below). Strings must be
+// valid UTF-8 after escape sequences are expanded.
//
// 'asdf'
// "that's cool"
// "\tall\n\tyour\n\tfavorite\n\tescape\n\tsequences"
//
+// Note that strings can contain newline without needing an escape sequence
+//
// 'a multiline
// string'
//
@@ -64,7 +66,7 @@
// \nnn 3-digit octal value nnn
// \xnn 2-digit hex value nn
// \unnnn unicode code point U+nnnn
-// \Unnnnnnnn unicode code point U+nnnnnnnn
+// \Unnnnnnnn unicode code point U+nnnnnnnn (UTF8)
//
// As an extension to the C11 escapes, a backslash immediately before a newline
// character (0x0a) will remove the newline character from the resulting string
@@ -259,28 +261,44 @@ func unescape(idx int, rawStr []byte) ([]byte, error) {
case "\\\n", "\\\r\n":
return nil
}
- if bytes.HasPrefix(escape, []byte(`\x`)) || bytes.HasPrefix(escape, []byte(`\u`)) || bytes.HasPrefix(escape, []byte(`\U`)) {
+ switch {
+ case bytes.HasPrefix(escape, []byte(`\x`)):
+ var n uint64
+ if n, err = strconv.ParseUint(string(escape[2:]), 16, 8); err != nil {
+ err = fmt.Errorf("%d: syntax error: invalid hex escape %q: %s", idx, escape, err)
+ return nil
+ }
+ return []byte{byte(n)}
+ case bytes.HasPrefix(escape, []byte(`\u`)), bytes.HasPrefix(escape, []byte(`\U`)):
var n int64
if n, err = strconv.ParseInt(string(escape[2:]), 16, 32); err != nil {
- err = fmt.Errorf("%d: syntax error: invalid hex escape %q: %s", idx, escape, err)
+ err = fmt.Errorf("%d: syntax error: invalid unicode escape %q: %s", idx, escape, err)
return nil
}
return utf8.AppendRune(nil, rune(n))
+ default:
+ if len(escape) != 4 {
+ err = fmt.Errorf("%d: syntax error: invalid string escape %q", idx, escape)
+ return nil
+ }
+ var n int64
+ if n, err = strconv.ParseInt(string(escape[1:]), 8, 32); err != nil {
+ err = fmt.Errorf("%d: syntax error: invalid string escape %q", idx, escape)
+ return nil
+ }
+ if n > 255 {
+ err = fmt.Errorf("%d: invalid octal escape %q %d > 255", idx, escape, n)
+ return nil
+ }
+ return []byte{byte(n)}
}
- if len(escape) != 4 {
- err = fmt.Errorf("%d: syntax error: invalid string escape %q", idx, escape)
- return nil
- }
- var n int64
- if n, err = strconv.ParseInt(string(escape[1:]), 8, 32); err != nil {
- err = fmt.Errorf("%d: syntax error: invalid string escape %q", idx, escape)
- return nil
- }
- return utf8.AppendRune(nil, rune(n))
})
if err != nil {
return nil, err
}
+ if !utf8.Valid(escaped) {
+ return nil, fmt.Errorf("%d: syntax error: string %q is not UTF-8 encoded", idx, escaped)
+ }
return escaped, nil
}