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  }