mirror of
				https://github.com/k3s-io/kubernetes.git
				synced 2025-10-31 05:40:42 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			282 lines
		
	
	
		
			8.2 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			282 lines
		
	
	
		
			8.2 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
| // Copyright 2017 The Bazel Authors. All rights reserved.
 | |
| // Use of this source code is governed by a BSD-style
 | |
| // license that can be found in the LICENSE file.
 | |
| 
 | |
| // Package starlarkstruct defines the Starlark types 'struct' and
 | |
| // 'module', both optional language extensions.
 | |
| //
 | |
| package starlarkstruct // import "go.starlark.net/starlarkstruct"
 | |
| 
 | |
| // It is tempting to introduce a variant of Struct that is a wrapper
 | |
| // around a Go struct value, for stronger typing guarantees and more
 | |
| // efficient and convenient field lookup. However:
 | |
| // 1) all fields of Starlark structs are optional, so we cannot represent
 | |
| //    them using more specific types such as String, Int, *Depset, and
 | |
| //    *File, as such types give no way to represent missing fields.
 | |
| // 2) the efficiency gain of direct struct field access is rather
 | |
| //    marginal: finding the index of a field by binary searching on the
 | |
| //    sorted list of field names is quite fast compared to the other
 | |
| //    overheads.
 | |
| // 3) the gains in compactness and spatial locality are also rather
 | |
| //    marginal: the array behind the []entry slice is (due to field name
 | |
| //    strings) only a factor of 2 larger than the corresponding Go struct
 | |
| //    would be, and, like the Go struct, requires only a single allocation.
 | |
| 
 | |
| import (
 | |
| 	"fmt"
 | |
| 	"sort"
 | |
| 	"strings"
 | |
| 
 | |
| 	"go.starlark.net/starlark"
 | |
| 	"go.starlark.net/syntax"
 | |
| )
 | |
| 
 | |
| // Make is the implementation of a built-in function that instantiates
 | |
| // an immutable struct from the specified keyword arguments.
 | |
| //
 | |
| // An application can add 'struct' to the Starlark environment like so:
 | |
| //
 | |
| // 	globals := starlark.StringDict{
 | |
| // 		"struct":  starlark.NewBuiltin("struct", starlarkstruct.Make),
 | |
| // 	}
 | |
| //
 | |
| func Make(_ *starlark.Thread, _ *starlark.Builtin, args starlark.Tuple, kwargs []starlark.Tuple) (starlark.Value, error) {
 | |
| 	if len(args) > 0 {
 | |
| 		return nil, fmt.Errorf("struct: unexpected positional arguments")
 | |
| 	}
 | |
| 	return FromKeywords(Default, kwargs), nil
 | |
| }
 | |
| 
 | |
| // FromKeywords returns a new struct instance whose fields are specified by the
 | |
| // key/value pairs in kwargs.  (Each kwargs[i][0] must be a starlark.String.)
 | |
| func FromKeywords(constructor starlark.Value, kwargs []starlark.Tuple) *Struct {
 | |
| 	if constructor == nil {
 | |
| 		panic("nil constructor")
 | |
| 	}
 | |
| 	s := &Struct{
 | |
| 		constructor: constructor,
 | |
| 		entries:     make(entries, 0, len(kwargs)),
 | |
| 	}
 | |
| 	for _, kwarg := range kwargs {
 | |
| 		k := string(kwarg[0].(starlark.String))
 | |
| 		v := kwarg[1]
 | |
| 		s.entries = append(s.entries, entry{k, v})
 | |
| 	}
 | |
| 	sort.Sort(s.entries)
 | |
| 	return s
 | |
| }
 | |
| 
 | |
| // FromStringDict returns a whose elements are those of d.
 | |
| // The constructor parameter specifies the constructor; use Default for an ordinary struct.
 | |
| func FromStringDict(constructor starlark.Value, d starlark.StringDict) *Struct {
 | |
| 	if constructor == nil {
 | |
| 		panic("nil constructor")
 | |
| 	}
 | |
| 	s := &Struct{
 | |
| 		constructor: constructor,
 | |
| 		entries:     make(entries, 0, len(d)),
 | |
| 	}
 | |
| 	for k, v := range d {
 | |
| 		s.entries = append(s.entries, entry{k, v})
 | |
| 	}
 | |
| 	sort.Sort(s.entries)
 | |
| 	return s
 | |
| }
 | |
| 
 | |
| // Struct is an immutable Starlark type that maps field names to values.
 | |
| // It is not iterable and does not support len.
 | |
| //
 | |
| // A struct has a constructor, a distinct value that identifies a class
 | |
| // of structs, and which appears in the struct's string representation.
 | |
| //
 | |
| // Operations such as x+y fail if the constructors of the two operands
 | |
| // are not equal.
 | |
| //
 | |
| // The default constructor, Default, is the string "struct", but
 | |
| // clients may wish to 'brand' structs for their own purposes.
 | |
| // The constructor value appears in the printed form of the value,
 | |
| // and is accessible using the Constructor method.
 | |
| //
 | |
| // Use Attr to access its fields and AttrNames to enumerate them.
 | |
| type Struct struct {
 | |
| 	constructor starlark.Value
 | |
| 	entries     entries // sorted by name
 | |
| }
 | |
| 
 | |
| // Default is the default constructor for structs.
 | |
| // It is merely the string "struct".
 | |
| const Default = starlark.String("struct")
 | |
| 
 | |
| type entries []entry
 | |
| 
 | |
| func (a entries) Len() int           { return len(a) }
 | |
| func (a entries) Less(i, j int) bool { return a[i].name < a[j].name }
 | |
| func (a entries) Swap(i, j int)      { a[i], a[j] = a[j], a[i] }
 | |
| 
 | |
| type entry struct {
 | |
| 	name  string
 | |
| 	value starlark.Value
 | |
| }
 | |
| 
 | |
| var (
 | |
| 	_ starlark.HasAttrs  = (*Struct)(nil)
 | |
| 	_ starlark.HasBinary = (*Struct)(nil)
 | |
| )
 | |
| 
 | |
| // ToStringDict adds a name/value entry to d for each field of the struct.
 | |
| func (s *Struct) ToStringDict(d starlark.StringDict) {
 | |
| 	for _, e := range s.entries {
 | |
| 		d[e.name] = e.value
 | |
| 	}
 | |
| }
 | |
| 
 | |
| func (s *Struct) String() string {
 | |
| 	buf := new(strings.Builder)
 | |
| 	if s.constructor == Default {
 | |
| 		// NB: The Java implementation always prints struct
 | |
| 		// even for Bazel provider instances.
 | |
| 		buf.WriteString("struct") // avoid String()'s quotation
 | |
| 	} else {
 | |
| 		buf.WriteString(s.constructor.String())
 | |
| 	}
 | |
| 	buf.WriteByte('(')
 | |
| 	for i, e := range s.entries {
 | |
| 		if i > 0 {
 | |
| 			buf.WriteString(", ")
 | |
| 		}
 | |
| 		buf.WriteString(e.name)
 | |
| 		buf.WriteString(" = ")
 | |
| 		buf.WriteString(e.value.String())
 | |
| 	}
 | |
| 	buf.WriteByte(')')
 | |
| 	return buf.String()
 | |
| }
 | |
| 
 | |
| // Constructor returns the constructor used to create this struct.
 | |
| func (s *Struct) Constructor() starlark.Value { return s.constructor }
 | |
| 
 | |
| func (s *Struct) Type() string         { return "struct" }
 | |
| func (s *Struct) Truth() starlark.Bool { return true } // even when empty
 | |
| func (s *Struct) Hash() (uint32, error) {
 | |
| 	// Same algorithm as Tuple.hash, but with different primes.
 | |
| 	var x, m uint32 = 8731, 9839
 | |
| 	for _, e := range s.entries {
 | |
| 		namehash, _ := starlark.String(e.name).Hash()
 | |
| 		x = x ^ 3*namehash
 | |
| 		y, err := e.value.Hash()
 | |
| 		if err != nil {
 | |
| 			return 0, err
 | |
| 		}
 | |
| 		x = x ^ y*m
 | |
| 		m += 7349
 | |
| 	}
 | |
| 	return x, nil
 | |
| }
 | |
| func (s *Struct) Freeze() {
 | |
| 	for _, e := range s.entries {
 | |
| 		e.value.Freeze()
 | |
| 	}
 | |
| }
 | |
| 
 | |
| func (x *Struct) Binary(op syntax.Token, y starlark.Value, side starlark.Side) (starlark.Value, error) {
 | |
| 	if y, ok := y.(*Struct); ok && op == syntax.PLUS {
 | |
| 		if side == starlark.Right {
 | |
| 			x, y = y, x
 | |
| 		}
 | |
| 
 | |
| 		if eq, err := starlark.Equal(x.constructor, y.constructor); err != nil {
 | |
| 			return nil, fmt.Errorf("in %s + %s: error comparing constructors: %v",
 | |
| 				x.constructor, y.constructor, err)
 | |
| 		} else if !eq {
 | |
| 			return nil, fmt.Errorf("cannot add structs of different constructors: %s + %s",
 | |
| 				x.constructor, y.constructor)
 | |
| 		}
 | |
| 
 | |
| 		z := make(starlark.StringDict, x.len()+y.len())
 | |
| 		for _, e := range x.entries {
 | |
| 			z[e.name] = e.value
 | |
| 		}
 | |
| 		for _, e := range y.entries {
 | |
| 			z[e.name] = e.value
 | |
| 		}
 | |
| 
 | |
| 		return FromStringDict(x.constructor, z), nil
 | |
| 	}
 | |
| 	return nil, nil // unhandled
 | |
| }
 | |
| 
 | |
| // Attr returns the value of the specified field.
 | |
| func (s *Struct) Attr(name string) (starlark.Value, error) {
 | |
| 	// Binary search the entries.
 | |
| 	// This implementation is a specialization of
 | |
| 	// sort.Search that avoids dynamic dispatch.
 | |
| 	n := len(s.entries)
 | |
| 	i, j := 0, n
 | |
| 	for i < j {
 | |
| 		h := int(uint(i+j) >> 1)
 | |
| 		if s.entries[h].name < name {
 | |
| 			i = h + 1
 | |
| 		} else {
 | |
| 			j = h
 | |
| 		}
 | |
| 	}
 | |
| 	if i < n && s.entries[i].name == name {
 | |
| 		return s.entries[i].value, nil
 | |
| 	}
 | |
| 
 | |
| 	var ctor string
 | |
| 	if s.constructor != Default {
 | |
| 		ctor = s.constructor.String() + " "
 | |
| 	}
 | |
| 	return nil, starlark.NoSuchAttrError(
 | |
| 		fmt.Sprintf("%sstruct has no .%s attribute", ctor, name))
 | |
| }
 | |
| 
 | |
| func (s *Struct) len() int { return len(s.entries) }
 | |
| 
 | |
| // AttrNames returns a new sorted list of the struct fields.
 | |
| func (s *Struct) AttrNames() []string {
 | |
| 	names := make([]string, len(s.entries))
 | |
| 	for i, e := range s.entries {
 | |
| 		names[i] = e.name
 | |
| 	}
 | |
| 	return names
 | |
| }
 | |
| 
 | |
| func (x *Struct) CompareSameType(op syntax.Token, y_ starlark.Value, depth int) (bool, error) {
 | |
| 	y := y_.(*Struct)
 | |
| 	switch op {
 | |
| 	case syntax.EQL:
 | |
| 		return structsEqual(x, y, depth)
 | |
| 	case syntax.NEQ:
 | |
| 		eq, err := structsEqual(x, y, depth)
 | |
| 		return !eq, err
 | |
| 	default:
 | |
| 		return false, fmt.Errorf("%s %s %s not implemented", x.Type(), op, y.Type())
 | |
| 	}
 | |
| }
 | |
| 
 | |
| func structsEqual(x, y *Struct, depth int) (bool, error) {
 | |
| 	if x.len() != y.len() {
 | |
| 		return false, nil
 | |
| 	}
 | |
| 
 | |
| 	if eq, err := starlark.Equal(x.constructor, y.constructor); err != nil {
 | |
| 		return false, fmt.Errorf("error comparing struct constructors %v and %v: %v",
 | |
| 			x.constructor, y.constructor, err)
 | |
| 	} else if !eq {
 | |
| 		return false, nil
 | |
| 	}
 | |
| 
 | |
| 	for i, n := 0, x.len(); i < n; i++ {
 | |
| 		if x.entries[i].name != y.entries[i].name {
 | |
| 			return false, nil
 | |
| 		} else if eq, err := starlark.EqualDepth(x.entries[i].value, y.entries[i].value, depth-1); err != nil {
 | |
| 			return false, err
 | |
| 		} else if !eq {
 | |
| 			return false, nil
 | |
| 		}
 | |
| 	}
 | |
| 	return true, nil
 | |
| }
 |