compiler: implement range over a string

This commit is contained in:
Ayke van Laethem
2018-09-22 19:19:53 +02:00
parent 18b5ab290c
commit 473e71b573
2 changed files with 71 additions and 24 deletions
+46
View File
@@ -12,6 +12,12 @@ type _string struct {
length lenType
}
// The iterator state for a range over a string.
type stringIterator struct {
byteindex lenType
rangeindex lenType
}
// Return true iff the strings match.
//go:nobounds
func stringEqual(x, y string) bool {
@@ -75,6 +81,18 @@ func stringFromUnicode(x rune) _string {
return _string{ptr: (*byte)(unsafe.Pointer(&array)), length: length}
}
// Iterate over a string.
// Returns (ok, key, value).
func stringNext(s string, it *stringIterator) (bool, int, rune) {
if len(s) <= int(it.byteindex) {
return false, 0, 0
}
r, length := decodeUTF8(s, it.byteindex)
it.byteindex += length
it.rangeindex += 1
return true, int(it.rangeindex), r
}
// Convert a Unicode code point into an array of bytes and its length.
func encodeUTF8(x rune) ([4]byte, lenType) {
// https://stackoverflow.com/questions/6240055/manually-converting-unicode-codepoints-into-utf-8-and-utf-16
@@ -102,3 +120,31 @@ func encodeUTF8(x rune) ([4]byte, lenType) {
return [4]byte{0xef, 0xbf, 0xbd, 0}, 3
}
}
// Decode a single UTF-8 character from a string.
//go:nobounds
func decodeUTF8(s string, index lenType) (rune, lenType) {
remaining := lenType(len(s)) - index // must be >= 1 before calling this function
x := s[index]
switch {
case x&0x80 == 0x00: // 0xxxxxxx
return rune(x), 1
case x&0xe0 == 0xc0: // 110xxxxx
if remaining < 2 {
return 0xfffd, 1
}
return (rune(x&0x1f) << 6) | (rune(s[index+1]) & 0x3f), 2
case x&0xf0 == 0xe0: // 1110xxxx
if remaining < 3 {
return 0xfffd, 1
}
return (rune(x&0x0f) << 12) | ((rune(s[index+1]) & 0x3f) << 6) | (rune(s[index+2]) & 0x3f), 3
case x&0xf8 == 0xf0: // 11110xxx
if remaining < 4 {
return 0xfffd, 1
}
return (rune(x&0x07) << 18) | ((rune(s[index+1]) & 0x3f) << 12) | ((rune(s[index+2]) & 0x3f) << 6) | (rune(s[index+3]) & 0x3f), 4
default:
return 0xfffd, 1
}
}