github.com/coyove/nj@v0.0.0-20221110084952-c7f8db1065c3/bas/value_shape.go (about) 1 package bas 2 3 import ( 4 "bytes" 5 "fmt" 6 "io" 7 "strconv" 8 "strings" 9 "sync" 10 11 "github.com/coyove/nj/internal" 12 "github.com/coyove/nj/typ" 13 ) 14 15 type shape interface { 16 assert(Value) error 17 add(shape) 18 String() string 19 } 20 21 type shaperArray struct { 22 fixed bool 23 shapes []shape 24 } 25 26 func (sa *shaperArray) add(s shape) { 27 sa.shapes = append(sa.shapes, s) 28 } 29 30 func (sa *shaperArray) assert(v Value) error { 31 if !v.IsArray() { 32 return fmt.Errorf("%v expects array, got %v", sa, v.simple()) 33 } 34 arr := v.Native() 35 if sa.fixed { 36 if arr.Len() != len(sa.shapes) { 37 return fmt.Errorf("%v expects array with %d elements, got %d", sa, len(sa.shapes), arr.Len()) 38 } 39 for i, s := range sa.shapes { 40 if err := s.assert(arr.Get(i)); err != nil { 41 return err 42 } 43 } 44 } else { 45 if len(sa.shapes) == 0 { 46 return nil 47 } 48 if arr.Len()%len(sa.shapes) != 0 { 49 return fmt.Errorf("%v expects array with a multiple of %d elements, got %d", sa, len(sa.shapes), arr.Len()) 50 } 51 for i := 0; i < arr.Len(); i += len(sa.shapes) { 52 for j, s := range sa.shapes { 53 if err := s.assert(arr.Get(i + j)); err != nil { 54 return fmt.Errorf("array shape %v, value at index %d: %v", sa, i+j, err) 55 } 56 } 57 } 58 } 59 return nil 60 } 61 62 func (sa *shaperArray) String() string { 63 buf := bytes.NewBufferString(internal.IfStr(sa.fixed, "(", "[")) 64 for _, s := range sa.shapes { 65 buf.WriteString(s.String()) 66 buf.WriteByte(',') 67 } 68 internal.CloseBuffer(buf, internal.IfStr(sa.fixed, ")", "]")) 69 return buf.String() 70 } 71 72 type shaperObject struct { 73 key, value shape 74 } 75 76 func (sa *shaperObject) add(s shape) { 77 if sa.key == nil { 78 sa.key = s 79 return 80 } 81 if sa.value == nil { 82 sa.value = s 83 return 84 } 85 } 86 87 func (sa *shaperObject) assert(v Value) error { 88 if !v.IsObject() { 89 return fmt.Errorf("%v expects object, got %v", sa, v.simple()) 90 } 91 if sa.key == nil && sa.value == nil { 92 return nil 93 } 94 var err error 95 v.Object().Foreach(func(k Value, v *Value) bool { 96 if err = sa.key.assert(k); err != nil { 97 err = fmt.Errorf("object shape %v key error: %v", sa, err) 98 return false 99 } 100 if sa.value != nil { 101 if err = sa.value.assert(*v); err != nil { 102 err = fmt.Errorf("object shape %v value error: %v", sa, err) 103 return false 104 } 105 } 106 return true 107 }) 108 return err 109 } 110 111 func (sa *shaperObject) String() string { 112 buf := bytes.Buffer{} 113 buf.WriteByte('{') 114 if sa.key == nil && sa.value == nil { 115 } else { 116 buf.WriteString(sa.key.String()) 117 buf.WriteByte(':') 118 if sa.value == nil { 119 buf.WriteString("any") 120 } else { 121 buf.WriteString(sa.value.String()) 122 } 123 } 124 buf.WriteByte('}') 125 return buf.String() 126 } 127 128 type shaperPrototype struct { 129 name string 130 } 131 132 func (sa *shaperPrototype) add(s shape) { 133 } 134 135 func (sa *shaperPrototype) assert(v Value) error { 136 return assertShapePrototype(v, sa.name) 137 } 138 139 func assertShapePrototype(v Value, name string) error { 140 switch v.Type() { 141 case typ.Native: 142 if v.Native().meta.Name == name { 143 return nil 144 } 145 for p := v.Native().meta.Proto; p != nil; p = p.parent { 146 if p.Name() == name { 147 return nil 148 } 149 } 150 return fmt.Errorf("expects native of prototype/name %v, got %v", name, v.Native().meta.Name) 151 case typ.Object: 152 for p := v.Object(); p != nil; p = p.parent { 153 if p.Name() == name { 154 return nil 155 } 156 } 157 return fmt.Errorf("expects object of prototype %v, got %v", name, v.Object().Name()) 158 default: 159 return fmt.Errorf("expects native or object, got %v", v.simple()) 160 } 161 } 162 163 func (sa *shaperPrototype) String() string { 164 return "@" + sa.name 165 } 166 167 type shaperPrimitive struct { 168 verbs string 169 } 170 171 func (sa *shaperPrimitive) add(s shape) { 172 } 173 174 func (sa *shaperPrimitive) assert(v Value) error { 175 return assertShapePrimitive(v, sa.verbs) 176 } 177 178 func assertShapePrimitive(v Value, verbs string) error { 179 if verbs == "" || verbs == "_" || verbs == "v" { 180 return nil 181 } 182 183 bm := [128]bool{} 184 for i := 0; i < len(verbs); i++ { 185 bm[verbs[i]] = true 186 } 187 188 ok := false 189 switch v.Type() { 190 case typ.Nil: 191 ok = bm['N'] 192 case typ.Bool: 193 ok = bm['b'] 194 case typ.Number: 195 if bm['i'] { 196 ok = v.IsInt64() 197 } else { 198 ok = bm['n'] 199 } 200 case typ.String: 201 ok = bm['s'] || bm['R'] || bm['G'] 202 case typ.Object: 203 ok = bm['o'] || bm['R'] || bm['W'] || bm['C'] 204 case typ.Native: 205 if bm['E'] { 206 ok = v.IsError() 207 } else if bm['B'] { 208 ok = v.IsBytes() 209 } else if bm['C'] { 210 _, ok = v.Native().Unwrap().(io.Closer) 211 } else if bm['W'] { 212 _, ok = v.Native().Unwrap().(io.Writer) 213 } else if bm['R'] { 214 x := v.Native().Unwrap() 215 _, ok = x.(io.Reader) 216 if !ok { 217 _, ok = x.([]byte) 218 } 219 } 220 } 221 if !ok { 222 return fmt.Errorf("%v can't match %v", &shaperPrimitive{verbs}, v.simple()) 223 } 224 return nil 225 } 226 227 func (sa *shaperPrimitive) String() string { 228 var buf []string 229 for _, b := range sa.verbs { 230 switch b { 231 case 'i': 232 buf = append(buf, "int") 233 case 'n': 234 buf = append(buf, "number") 235 case 'b': 236 buf = append(buf, "bool") 237 case 's': 238 buf = append(buf, "string") 239 case 'o': 240 buf = append(buf, "object") 241 case 'N': 242 buf = append(buf, "nil") 243 case 'E': 244 buf = append(buf, "@error") 245 case 'B': 246 buf = append(buf, "@bytes") 247 case 'R': 248 buf = append(buf, "Reader") 249 case 'W': 250 buf = append(buf, "Writer") 251 case 'C': 252 buf = append(buf, "Closer") 253 case 'G': 254 buf = append(buf, "goto") 255 default: 256 buf = append(buf, "any") 257 } 258 } 259 if len(buf) == 1 { 260 return buf[0] 261 } 262 return "<" + strings.Join(buf, ",") + ">" 263 } 264 265 type shaperOr struct { 266 shapes []shape 267 } 268 269 func (sa *shaperOr) add(s shape) { 270 sa.shapes = append(sa.shapes, s) 271 } 272 273 func (sa *shaperOr) assert(v Value) error { 274 for _, s := range sa.shapes { 275 if s.assert(v) == nil { 276 return nil 277 } 278 } 279 return fmt.Errorf("%v can't match %v", sa, v.simple()) 280 } 281 282 func (sa *shaperOr) String() string { 283 x := make([]string, len(sa.shapes)) 284 for i := range sa.shapes { 285 x[i] = sa.shapes[i].String() 286 } 287 return "<" + strings.Join(x, ",") + ">" 288 } 289 290 func shapeNextToken(s string) (token, rest string) { 291 s = strings.TrimSpace(s) 292 for i := 0; i < len(s); i++ { 293 switch s[i] { 294 case ',': 295 if i == 0 { 296 return shapeNextToken(s[1:]) 297 } 298 return s[:i], s[i+1:] 299 case '<', '(', '[', '{', ':', ')', ']', '}', '>', ' ': 300 if i == 0 { 301 return s[:1], s[1:] 302 } 303 return s[:i], s[i:] 304 } 305 } 306 return s, "" 307 } 308 309 var shapeCache sync.Map 310 311 func NewShape(s string) func(v Value) error { 312 s = strings.TrimSpace(s) 313 if f, ok := shapeCache.Load(s); ok { 314 return f.(func(Value) error) 315 } 316 317 x := buildShape(s) 318 if x == nil { 319 return func(Value) error { return nil } 320 } 321 322 f := func(v Value) error { 323 if err := x.assert(v); err != nil { 324 return fmt.Errorf("%q: %v", s, err) 325 } 326 return nil 327 } 328 shapeCache.Store(s, f) 329 return f 330 } 331 332 func buildShape(s string) shape { 333 var until byte 334 s = strings.TrimSpace(s) 335 if len(s) == 0 { 336 return nil 337 } 338 339 old := s 340 switch s[0] { 341 case '(': 342 until, s = ')', s[1:] 343 case '[': 344 until, s = ']', s[1:] 345 case '{': 346 until, s = '}', s[1:] 347 case '<': 348 until, s = '>', s[1:] 349 } 350 return shapeScan(old, &s, until) 351 } 352 353 func shapeScan(p string, s *string, until byte) shape { 354 var sa shape 355 switch until { 356 case ')': 357 sa = &shaperArray{fixed: true} 358 case ']': 359 sa = &shaperArray{} 360 case '}': 361 sa = &shaperObject{} 362 case '>': 363 sa = &shaperOr{} 364 } 365 366 for len(*s) > 0 { 367 var token string 368 token, *s = shapeNextToken(*s) 369 if token == "" { 370 panic("invalid shape form: " + strconv.Quote(p)) 371 } 372 switch token[0] { 373 case until: 374 return sa 375 case '(': 376 sa.add(shapeScan(p, s, ')')) 377 case '[': 378 sa.add(shapeScan(p, s, ']')) 379 case '{': 380 sa.add(shapeScan(p, s, '}')) 381 case '<': 382 sa.add(shapeScan(p, s, '>')) 383 case ':': 384 case '@': 385 sa2 := &shaperPrototype{name: token[1:]} 386 if sa == nil { 387 return sa2 388 } 389 sa.add(sa2) 390 default: 391 sa2 := &shaperPrimitive{verbs: token} 392 if sa == nil { 393 return sa2 394 } 395 sa.add(sa2) 396 } 397 } 398 399 return sa 400 } 401 402 func TestShapeFast(v Value, shape string) (err error) { 403 switch shape[0] { 404 case '(', '[', '{', '<': 405 err = NewShape(shape)(v) 406 case '@': 407 err = assertShapePrototype(v, shape[1:]) 408 default: 409 err = assertShapePrimitive(v, shape) 410 } 411 return 412 } 413 414 func (v Value) AssertShape(shape, msg string) Value { 415 if err := TestShapeFast(v, shape); err != nil { 416 panic(fmt.Errorf("%s: %v", msg, err)) 417 } 418 return v 419 } 420 421 func (v Value) AssertNumber(msg string) Value { 422 if v.Type() != typ.Number { 423 internal.Panic("%s: expects number, got %v", msg, v.simple()) 424 } 425 return v 426 } 427 428 func (v Value) AssertString(msg string) string { 429 if v.Type() != typ.String { 430 internal.Panic("%s: expects string, got %v", msg, v.simple()) 431 } 432 return v.Str() 433 } 434 435 func (v Value) AssertObject(msg string) *Object { 436 if v.Type() != typ.Object { 437 internal.Panic("%s: expects object, got %v", msg, v.simple()) 438 } 439 return v.Object() 440 }