go.starlark.net@v0.0.0-20231101134539-556fd59b42f6/starlark/unpack.go (about)

     1  package starlark
     2  
     3  // This file defines the Unpack helper functions used by
     4  // built-in functions to interpret their call arguments.
     5  
     6  import (
     7  	"fmt"
     8  	"log"
     9  	"reflect"
    10  	"strings"
    11  
    12  	"go.starlark.net/internal/spell"
    13  )
    14  
    15  // An Unpacker defines custom argument unpacking behavior.
    16  // See UnpackArgs.
    17  type Unpacker interface {
    18  	Unpack(v Value) error
    19  }
    20  
    21  // UnpackArgs unpacks the positional and keyword arguments into the
    22  // supplied parameter variables.  pairs is an alternating list of names
    23  // and pointers to variables.
    24  //
    25  // If the variable is a bool, integer, string, *List, *Dict, Callable,
    26  // Iterable, or user-defined implementation of Value,
    27  // UnpackArgs performs the appropriate type check.
    28  // Predeclared Go integer types uses the AsInt check.
    29  //
    30  // If the parameter name ends with "?", it is optional.
    31  //
    32  // If the parameter name ends with "??", it is optional and treats the None value
    33  // as if the argument was absent.
    34  //
    35  // If a parameter is marked optional, then all following parameters are
    36  // implicitly optional where or not they are marked.
    37  //
    38  // If the variable implements Unpacker, its Unpack argument
    39  // is called with the argument value, allowing an application
    40  // to define its own argument validation and conversion.
    41  //
    42  // If the variable implements Value, UnpackArgs may call
    43  // its Type() method while constructing the error message.
    44  //
    45  // Examples:
    46  //
    47  //      var (
    48  //          a Value
    49  //          b = MakeInt(42)
    50  //          c Value = starlark.None
    51  //      )
    52  //
    53  //      // 1. mixed parameters, like def f(a, b=42, c=None).
    54  //      err := UnpackArgs("f", args, kwargs, "a", &a, "b?", &b, "c?", &c)
    55  //
    56  //      // 2. keyword parameters only, like def f(*, a, b, c=None).
    57  //      if len(args) > 0 {
    58  //              return fmt.Errorf("f: unexpected positional arguments")
    59  //      }
    60  //      err := UnpackArgs("f", args, kwargs, "a", &a, "b?", &b, "c?", &c)
    61  //
    62  //      // 3. positional parameters only, like def f(a, b=42, c=None, /) in Python 3.8.
    63  //      err := UnpackPositionalArgs("f", args, kwargs, 1, &a, &b, &c)
    64  //
    65  // More complex forms such as def f(a, b=42, *args, c, d=123, **kwargs)
    66  // require additional logic, but their need in built-ins is exceedingly rare.
    67  //
    68  // In the examples above, the declaration of b with type Int causes UnpackArgs
    69  // to require that b's argument value, if provided, is also an int.
    70  // To allow arguments of any type, while retaining the default value of 42,
    71  // declare b as a Value:
    72  //
    73  //	var b Value = MakeInt(42)
    74  //
    75  // The zero value of a variable of type Value, such as 'a' in the
    76  // examples above, is not a valid Starlark value, so if the parameter is
    77  // optional, the caller must explicitly handle the default case by
    78  // interpreting nil as None or some computed default. The same is true
    79  // for the zero values of variables of type *List, *Dict, Callable, or
    80  // Iterable. For example:
    81  //
    82  //      // def myfunc(d=None, e=[], f={})
    83  //      var (
    84  //          d Value
    85  //          e *List
    86  //          f *Dict
    87  //      )
    88  //      err := UnpackArgs("myfunc", args, kwargs, "d?", &d, "e?", &e, "f?", &f)
    89  //      if d == nil { d = None; }
    90  //      if e == nil { e = new(List); }
    91  //      if f == nil { f = new(Dict); }
    92  //
    93  func UnpackArgs(fnname string, args Tuple, kwargs []Tuple, pairs ...interface{}) error {
    94  	nparams := len(pairs) / 2
    95  	var defined intset
    96  	defined.init(nparams)
    97  
    98  	paramName := func(x interface{}) (name string, skipNone bool) { // (no free variables)
    99  		name = x.(string)
   100  		if strings.HasSuffix(name, "??") {
   101  			name = strings.TrimSuffix(name, "??")
   102  			skipNone = true
   103  		} else if name[len(name)-1] == '?' {
   104  			name = name[:len(name)-1]
   105  		}
   106  
   107  		return name, skipNone
   108  	}
   109  
   110  	// positional arguments
   111  	if len(args) > nparams {
   112  		return fmt.Errorf("%s: got %d arguments, want at most %d",
   113  			fnname, len(args), nparams)
   114  	}
   115  	for i, arg := range args {
   116  		defined.set(i)
   117  		name, skipNone := paramName(pairs[2*i])
   118  		if skipNone {
   119  			if _, isNone := arg.(NoneType); isNone {
   120  				continue
   121  			}
   122  		}
   123  		if err := unpackOneArg(arg, pairs[2*i+1]); err != nil {
   124  			return fmt.Errorf("%s: for parameter %s: %s", fnname, name, err)
   125  		}
   126  	}
   127  
   128  	// keyword arguments
   129  kwloop:
   130  	for _, item := range kwargs {
   131  		name, arg := item[0].(String), item[1]
   132  		for i := 0; i < nparams; i++ {
   133  			pName, skipNone := paramName(pairs[2*i])
   134  			if pName == string(name) {
   135  				// found it
   136  				if defined.set(i) {
   137  					return fmt.Errorf("%s: got multiple values for keyword argument %s",
   138  						fnname, name)
   139  				}
   140  
   141  				if skipNone {
   142  					if _, isNone := arg.(NoneType); isNone {
   143  						continue kwloop
   144  					}
   145  				}
   146  
   147  				ptr := pairs[2*i+1]
   148  				if err := unpackOneArg(arg, ptr); err != nil {
   149  					return fmt.Errorf("%s: for parameter %s: %s", fnname, name, err)
   150  				}
   151  				continue kwloop
   152  			}
   153  		}
   154  		err := fmt.Errorf("%s: unexpected keyword argument %s", fnname, name)
   155  		names := make([]string, 0, nparams)
   156  		for i := 0; i < nparams; i += 2 {
   157  			param, _ := paramName(pairs[i])
   158  			names = append(names, param)
   159  		}
   160  		if n := spell.Nearest(string(name), names); n != "" {
   161  			err = fmt.Errorf("%s (did you mean %s?)", err.Error(), n)
   162  		}
   163  		return err
   164  	}
   165  
   166  	// Check that all non-optional parameters are defined.
   167  	// (We needn't check the first len(args).)
   168  	for i := len(args); i < nparams; i++ {
   169  		name := pairs[2*i].(string)
   170  		if strings.HasSuffix(name, "?") {
   171  			break // optional
   172  		}
   173  		if !defined.get(i) {
   174  			return fmt.Errorf("%s: missing argument for %s", fnname, name)
   175  		}
   176  	}
   177  
   178  	return nil
   179  }
   180  
   181  // UnpackPositionalArgs unpacks the positional arguments into
   182  // corresponding variables.  Each element of vars is a pointer; see
   183  // UnpackArgs for allowed types and conversions.
   184  //
   185  // UnpackPositionalArgs reports an error if the number of arguments is
   186  // less than min or greater than len(vars), if kwargs is nonempty, or if
   187  // any conversion fails.
   188  //
   189  // See UnpackArgs for general comments.
   190  func UnpackPositionalArgs(fnname string, args Tuple, kwargs []Tuple, min int, vars ...interface{}) error {
   191  	if len(kwargs) > 0 {
   192  		return fmt.Errorf("%s: unexpected keyword arguments", fnname)
   193  	}
   194  	max := len(vars)
   195  	if len(args) < min {
   196  		var atleast string
   197  		if min < max {
   198  			atleast = "at least "
   199  		}
   200  		return fmt.Errorf("%s: got %d arguments, want %s%d", fnname, len(args), atleast, min)
   201  	}
   202  	if len(args) > max {
   203  		var atmost string
   204  		if max > min {
   205  			atmost = "at most "
   206  		}
   207  		return fmt.Errorf("%s: got %d arguments, want %s%d", fnname, len(args), atmost, max)
   208  	}
   209  	for i, arg := range args {
   210  		if err := unpackOneArg(arg, vars[i]); err != nil {
   211  			return fmt.Errorf("%s: for parameter %d: %s", fnname, i+1, err)
   212  		}
   213  	}
   214  	return nil
   215  }
   216  
   217  func unpackOneArg(v Value, ptr interface{}) error {
   218  	// On failure, don't clobber *ptr.
   219  	switch ptr := ptr.(type) {
   220  	case Unpacker:
   221  		return ptr.Unpack(v)
   222  	case *Value:
   223  		*ptr = v
   224  	case *string:
   225  		s, ok := AsString(v)
   226  		if !ok {
   227  			return fmt.Errorf("got %s, want string", v.Type())
   228  		}
   229  		*ptr = s
   230  	case *bool:
   231  		b, ok := v.(Bool)
   232  		if !ok {
   233  			return fmt.Errorf("got %s, want bool", v.Type())
   234  		}
   235  		*ptr = bool(b)
   236  	case *int, *int8, *int16, *int32, *int64,
   237  		*uint, *uint8, *uint16, *uint32, *uint64, *uintptr:
   238  		return AsInt(v, ptr)
   239  	case *float64:
   240  		f, ok := v.(Float)
   241  		if !ok {
   242  			return fmt.Errorf("got %s, want float", v.Type())
   243  		}
   244  		*ptr = float64(f)
   245  	case **List:
   246  		list, ok := v.(*List)
   247  		if !ok {
   248  			return fmt.Errorf("got %s, want list", v.Type())
   249  		}
   250  		*ptr = list
   251  	case **Dict:
   252  		dict, ok := v.(*Dict)
   253  		if !ok {
   254  			return fmt.Errorf("got %s, want dict", v.Type())
   255  		}
   256  		*ptr = dict
   257  	case *Callable:
   258  		f, ok := v.(Callable)
   259  		if !ok {
   260  			return fmt.Errorf("got %s, want callable", v.Type())
   261  		}
   262  		*ptr = f
   263  	case *Iterable:
   264  		it, ok := v.(Iterable)
   265  		if !ok {
   266  			return fmt.Errorf("got %s, want iterable", v.Type())
   267  		}
   268  		*ptr = it
   269  	default:
   270  		// v must have type *V, where V is some subtype of starlark.Value.
   271  		ptrv := reflect.ValueOf(ptr)
   272  		if ptrv.Kind() != reflect.Ptr {
   273  			log.Panicf("internal error: not a pointer: %T", ptr)
   274  		}
   275  		paramVar := ptrv.Elem()
   276  		if !reflect.TypeOf(v).AssignableTo(paramVar.Type()) {
   277  			// The value is not assignable to the variable.
   278  
   279  			// Detect a possible bug in the Go program that called Unpack:
   280  			// If the variable *ptr is not a subtype of Value,
   281  			// no value of v can possibly work.
   282  			if !paramVar.Type().AssignableTo(reflect.TypeOf(new(Value)).Elem()) {
   283  				log.Panicf("pointer element type does not implement Value: %T", ptr)
   284  			}
   285  
   286  			// Report Starlark dynamic type error.
   287  			//
   288  			// We prefer the Starlark Value.Type name over
   289  			// its Go reflect.Type name, but calling the
   290  			// Value.Type method on the variable is not safe
   291  			// in general. If the variable is an interface,
   292  			// the call will fail. Even if the variable has
   293  			// a concrete type, it might not be safe to call
   294  			// Type() on a zero instance. Thus we must use
   295  			// recover.
   296  
   297  			// Default to Go reflect.Type name
   298  			paramType := paramVar.Type().String()
   299  
   300  			// Attempt to call Value.Type method.
   301  			func() {
   302  				defer func() { recover() }()
   303  				if typer, _ := paramVar.Interface().(interface{ Type() string }); typer != nil {
   304  					paramType = typer.Type()
   305  				}
   306  			}()
   307  			return fmt.Errorf("got %s, want %s", v.Type(), paramType)
   308  		}
   309  		paramVar.Set(reflect.ValueOf(v))
   310  	}
   311  	return nil
   312  }
   313  
   314  type intset struct {
   315  	small uint64       // bitset, used if n < 64
   316  	large map[int]bool //    set, used if n >= 64
   317  }
   318  
   319  func (is *intset) init(n int) {
   320  	if n >= 64 {
   321  		is.large = make(map[int]bool)
   322  	}
   323  }
   324  
   325  func (is *intset) set(i int) (prev bool) {
   326  	if is.large == nil {
   327  		prev = is.small&(1<<uint(i)) != 0
   328  		is.small |= 1 << uint(i)
   329  	} else {
   330  		prev = is.large[i]
   331  		is.large[i] = true
   332  	}
   333  	return
   334  }
   335  
   336  func (is *intset) get(i int) bool {
   337  	if is.large == nil {
   338  		return is.small&(1<<uint(i)) != 0
   339  	}
   340  	return is.large[i]
   341  }
   342  
   343  func (is *intset) len() int {
   344  	if is.large == nil {
   345  		// Suboptimal, but used only for error reporting.
   346  		len := 0
   347  		for i := 0; i < 64; i++ {
   348  			if is.small&(1<<uint(i)) != 0 {
   349  				len++
   350  			}
   351  		}
   352  		return len
   353  	}
   354  	return len(is.large)
   355  }