github.com/hirochachacha/plua@v0.0.0-20170217012138-c82f520cc725/stdlib/utf8/utf8.go (about) 1 package utf8 2 3 import ( 4 "unicode/utf8" 5 6 "github.com/hirochachacha/plua/object" 7 "github.com/hirochachacha/plua/object/fnutil" 8 ) 9 10 func char(th object.Thread, args ...object.Value) ([]object.Value, *object.RuntimeError) { 11 if len(args) == 0 { 12 return []object.Value{object.String("")}, nil 13 } 14 15 ap := fnutil.NewArgParser(th, args) 16 17 rs := make([]rune, len(args)) 18 19 for i := range args { 20 i64, err := ap.ToGoInt64(i) 21 if err != nil { 22 return nil, err 23 } 24 25 if i64 > utf8.MaxRune { 26 return nil, ap.ArgError(i, "value out of range") 27 } 28 29 rs[i] = rune(i64) 30 } 31 32 return []object.Value{object.String(string(rs))}, nil 33 } 34 35 func nextcode(th object.Thread, args ...object.Value) ([]object.Value, *object.RuntimeError) { 36 ap := fnutil.NewArgParser(th, args) 37 38 s, err := ap.ToGoString(0) 39 if err != nil { 40 return nil, err 41 } 42 43 off, err := ap.ToGoInt(1) 44 if err != nil { 45 return nil, err 46 } 47 48 if off >= len(s) { 49 return nil, nil 50 } 51 52 if off <= 0 { 53 r, _ := utf8.DecodeRuneInString(s) 54 if r == utf8.RuneError { 55 return nil, object.NewRuntimeError("invalid UTF-8 code") 56 } 57 58 return []object.Value{object.Integer(1), object.Integer(r)}, nil 59 } 60 61 r, rsize := utf8.DecodeRuneInString(s[off-1:]) 62 if r == utf8.RuneError { 63 return nil, object.NewRuntimeError("invalid UTF-8 code") 64 } 65 66 off += rsize 67 68 if off > len(s) { 69 return nil, nil 70 } 71 72 r, _ = utf8.DecodeRuneInString(s[off-1:]) 73 if r == utf8.RuneError { 74 return nil, object.NewRuntimeError("invalid UTF-8 code") 75 } 76 77 return []object.Value{object.Integer(off), object.Integer(r)}, nil 78 } 79 80 func codes(th object.Thread, args ...object.Value) ([]object.Value, *object.RuntimeError) { 81 ap := fnutil.NewArgParser(th, args) 82 83 s, err := ap.ToString(0) 84 if err != nil { 85 return nil, err 86 } 87 88 return []object.Value{object.GoFunction(nextcode), s, object.Integer(0)}, nil 89 } 90 91 func codepoint(th object.Thread, args ...object.Value) ([]object.Value, *object.RuntimeError) { 92 ap := fnutil.NewArgParser(th, args) 93 94 s, err := ap.ToGoString(0) 95 if err != nil { 96 return nil, err 97 } 98 99 i, err := ap.OptGoInt(1, 1) 100 if err != nil { 101 return nil, err 102 } 103 if i < 0 { 104 i = len(s) + 1 + i 105 } 106 if i <= 0 { 107 return nil, ap.ArgError(1, "out of range") 108 } 109 110 j, err := ap.OptGoInt(2, i) 111 if err != nil { 112 return nil, err 113 } 114 if j < 0 { 115 j = len(s) + 1 + j 116 } 117 if j > len(s) { 118 return nil, ap.ArgError(2, "out of range") 119 } 120 121 if i > j { 122 return nil, nil 123 } 124 125 var rets []object.Value 126 127 for { 128 r, k := utf8.DecodeRuneInString(s[i-1:]) 129 130 if r == utf8.RuneError { 131 return nil, object.NewRuntimeError("invalid UTF-8 code") 132 } 133 134 rets = append(rets, object.Integer(r)) 135 136 i += k 137 138 if i > j { 139 break 140 } 141 } 142 143 return rets, nil 144 } 145 146 func _len(th object.Thread, args ...object.Value) ([]object.Value, *object.RuntimeError) { 147 ap := fnutil.NewArgParser(th, args) 148 149 s, err := ap.ToGoString(0) 150 if err != nil { 151 return nil, err 152 } 153 154 i, err := ap.OptGoInt(1, 1) 155 if err != nil { 156 return nil, err 157 } 158 if i < 0 { 159 i = len(s) + 1 + i 160 } 161 if i <= 0 || i > len(s)+1 { 162 return nil, ap.ArgError(1, "out of range") 163 } 164 165 j, err := ap.OptGoInt(2, -1) 166 if err != nil { 167 return nil, err 168 } 169 if j < 0 { 170 j = len(s) + 1 + j 171 } 172 if j >= len(s)+1 { 173 return nil, ap.ArgError(2, "out of range") 174 } 175 176 var n int 177 for i <= j { 178 r, rsize := utf8.DecodeRuneInString(s[i-1:]) 179 if r == utf8.RuneError { 180 return []object.Value{object.False, object.Integer(i)}, nil 181 } 182 i += rsize 183 n++ 184 } 185 186 return []object.Value{object.Integer(n)}, nil 187 } 188 189 func offset(th object.Thread, args ...object.Value) ([]object.Value, *object.RuntimeError) { 190 ap := fnutil.NewArgParser(th, args) 191 192 s, err := ap.ToGoString(0) 193 if err != nil { 194 return nil, err 195 } 196 197 n, err := ap.ToGoInt(1) 198 if err != nil { 199 return nil, err 200 } 201 i := 1 202 if n < 0 { 203 i = len(s) + 1 204 } 205 206 i, err = ap.OptGoInt(2, i) 207 if err != nil { 208 return nil, err 209 } 210 if i < 0 { 211 i = len(s) + 1 + i 212 } 213 if i <= 0 || i > len(s)+1 { 214 return nil, ap.ArgError(2, "position out of range") 215 } 216 217 var r rune 218 var rsize int 219 220 switch { 221 case n < 0: 222 if i != len(s)+1 && !utf8.RuneStart(s[i-1]) { 223 return nil, object.NewRuntimeError("initial position is a continuation byte") 224 } 225 for n < 0 && i > 0 { 226 r, rsize = utf8.DecodeLastRuneInString(s[:i-1]) 227 if r == utf8.RuneError { 228 break 229 } 230 i -= rsize 231 n++ 232 } 233 case n > 0: 234 if i != len(s)+1 && !utf8.RuneStart(s[i-1]) { 235 return nil, object.NewRuntimeError("initial position is a continuation byte") 236 } 237 n-- 238 for n > 0 && i < len(s)+1 { 239 r, rsize = utf8.DecodeRuneInString(s[i-1:]) 240 if r == utf8.RuneError { 241 break 242 } 243 i += rsize 244 n-- 245 } 246 default: 247 if len(s) == i-1 { 248 return []object.Value{object.Integer(i)}, nil 249 } 250 for i > 0 && !utf8.RuneStart(s[i-1]) { 251 i-- 252 } 253 } 254 255 if n == 0 { 256 return []object.Value{object.Integer(i)}, nil 257 } 258 259 return nil, nil 260 } 261 262 func Open(th object.Thread, args ...object.Value) ([]object.Value, *object.RuntimeError) { 263 m := th.NewTableSize(0, 6) 264 265 m.Set(object.String("charpattern"), object.String("[\x00-\x7F\xC2-\xF4][\x80-\xBF]*")) 266 267 m.Set(object.String("char"), object.GoFunction(char)) 268 m.Set(object.String("codes"), object.GoFunction(codes)) 269 m.Set(object.String("codepoint"), object.GoFunction(codepoint)) 270 m.Set(object.String("len"), object.GoFunction(_len)) 271 m.Set(object.String("offset"), object.GoFunction(offset)) 272 273 return []object.Value{m}, nil 274 }