diff --git a/Documentation/cli/starlark.md b/Documentation/cli/starlark.md index 7a1679c6..5b35dab7 100644 --- a/Documentation/cli/starlark.md +++ b/Documentation/cli/starlark.md @@ -74,6 +74,8 @@ cur_scope() | Returns the current evaluation scope default_load_config() | Returns the current default load configuration +In addition to these built-ins, the [time](https://pkg.go.dev/go.starlark.net/lib/time#pkg-variables) library from the starlark-go project is also available to scripts. + ## Should I use raw_command or dlv_command? There are two ways to resume the execution of the target program: diff --git a/pkg/terminal/starbind/starlark.go b/pkg/terminal/starbind/starlark.go index de37879e..ac74b47f 100644 --- a/pkg/terminal/starbind/starlark.go +++ b/pkg/terminal/starbind/starlark.go @@ -9,6 +9,7 @@ import ( "strings" "sync" + startime "go.starlark.net/lib/time" "go.starlark.net/resolve" "go.starlark.net/starlark" @@ -67,6 +68,9 @@ func New(ctx Context, out EchoWriter) *Env { env.ctx = ctx env.out = out + // Make the "time" module available to Starlark scripts. + starlark.Universe["time"] = startime.Module + env.env = env.starlarkPredeclare() env.env[dlvCommandBuiltinName] = starlark.NewBuiltin(dlvCommandBuiltinName, func(thread *starlark.Thread, _ *starlark.Builtin, args starlark.Tuple, kwargs []starlark.Tuple) (starlark.Value, error) { if err := isCancelled(thread); err != nil { diff --git a/vendor/go.starlark.net/lib/time/time.go b/vendor/go.starlark.net/lib/time/time.go new file mode 100644 index 00000000..de8bf533 --- /dev/null +++ b/vendor/go.starlark.net/lib/time/time.go @@ -0,0 +1,507 @@ +// Copyright 2021 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 time provides time-related constants and functions. +package time // import "go.starlark.net/lib/time" + +import ( + "fmt" + "sort" + "time" + + "go.starlark.net/starlark" + "go.starlark.net/starlarkstruct" + "go.starlark.net/syntax" +) + +// Module time is a Starlark module of time-related functions and constants. +// The module defines the following functions: +// +// from_timestamp(sec, nsec) - Converts the given Unix time corresponding to the number of seconds +// and (optionally) nanoseconds since January 1, 1970 UTC into an object +// of type Time. For more details, refer to https://pkg.go.dev/time#Unix. +// +// is_valid_timezone(loc) - Reports whether loc is a valid time zone name. +// +// now() - Returns the current local time. Applications may replace this function by a deterministic one. +// +// parse_duration(d) - Parses the given duration string. For more details, refer to +// https://pkg.go.dev/time#ParseDuration. +// +// parseTime(x, format, location) - Parses the given time string using a specific time format and location. +// The expected arguments are a time string (mandatory), a time format +// (optional, set to RFC3339 by default, e.g. "2021-03-22T23:20:50.52Z") +// and a name of location (optional, set to UTC by default). For more details, +// refer to https://pkg.go.dev/time#Parse and https://pkg.go.dev/time#ParseInLocation. +// +// time(year, month, day, hour, minute, second, nanosecond, location) - Returns the Time corresponding to +// yyyy-mm-dd hh:mm:ss + nsec nanoseconds +// in the appropriate zone for that time +// in the given location. All the parameters +// are optional. +// The module also defines the following constants: +// +// nanosecond - A duration representing one nanosecond. +// microsecond - A duration representing one microsecond. +// millisecond - A duration representing one millisecond. +// second - A duration representing one second. +// minute - A duration representing one minute. +// hour - A duration representing one hour. +// +var Module = &starlarkstruct.Module{ + Name: "time", + Members: starlark.StringDict{ + "from_timestamp": starlark.NewBuiltin("from_timestamp", fromTimestamp), + "is_valid_timezone": starlark.NewBuiltin("is_valid_timezone", isValidTimezone), + "now": starlark.NewBuiltin("now", now), + "parse_duration": starlark.NewBuiltin("parse_duration", parseDuration), + "parse_time": starlark.NewBuiltin("parse_time", parseTime), + "time": starlark.NewBuiltin("time", newTime), + + "nanosecond": Duration(time.Nanosecond), + "microsecond": Duration(time.Microsecond), + "millisecond": Duration(time.Millisecond), + "second": Duration(time.Second), + "minute": Duration(time.Minute), + "hour": Duration(time.Hour), + }, +} + +// NowFunc is a function that generates the current time. Intentionally exported +// so that it can be overridden, for example by applications that require their +// Starlark scripts to be fully deterministic. +var NowFunc = time.Now + +func parseDuration(thread *starlark.Thread, _ *starlark.Builtin, args starlark.Tuple, kwargs []starlark.Tuple) (starlark.Value, error) { + var d Duration + err := starlark.UnpackPositionalArgs("parse_duration", args, kwargs, 1, &d) + return d, err +} + +func isValidTimezone(thread *starlark.Thread, _ *starlark.Builtin, args starlark.Tuple, kwargs []starlark.Tuple) (starlark.Value, error) { + var s string + if err := starlark.UnpackPositionalArgs("is_valid_timezone", args, kwargs, 1, &s); err != nil { + return nil, err + } + _, err := time.LoadLocation(s) + return starlark.Bool(err == nil), nil +} + +func parseTime(thread *starlark.Thread, _ *starlark.Builtin, args starlark.Tuple, kwargs []starlark.Tuple) (starlark.Value, error) { + var ( + x string + location = "UTC" + format = time.RFC3339 + ) + if err := starlark.UnpackArgs("parse_time", args, kwargs, "x", &x, "format?", &format, "location?", &location); err != nil { + return nil, err + } + + if location == "UTC" { + t, err := time.Parse(format, x) + if err != nil { + return nil, err + } + return Time(t), nil + } + + loc, err := time.LoadLocation(location) + if err != nil { + return nil, err + } + t, err := time.ParseInLocation(format, x, loc) + if err != nil { + return nil, err + } + return Time(t), nil +} + +func fromTimestamp(thread *starlark.Thread, _ *starlark.Builtin, args starlark.Tuple, kwargs []starlark.Tuple) (starlark.Value, error) { + var ( + sec int64 + nsec int64 = 0 + ) + if err := starlark.UnpackPositionalArgs("from_timestamp", args, kwargs, 1, &sec, &nsec); err != nil { + return nil, err + } + return Time(time.Unix(sec, nsec)), nil +} + +func now(thread *starlark.Thread, _ *starlark.Builtin, args starlark.Tuple, kwargs []starlark.Tuple) (starlark.Value, error) { + return Time(NowFunc()), nil +} + +// Duration is a Starlark representation of a duration. +type Duration time.Duration + +// Assert at compile time that Duration implements Unpacker. +var _ starlark.Unpacker = (*Duration)(nil) + +// Unpack is a custom argument unpacker +func (d *Duration) Unpack(v starlark.Value) error { + switch x := v.(type) { + case Duration: + *d = x + return nil + case starlark.String: + dur, err := time.ParseDuration(string(x)) + if err != nil { + return err + } + + *d = Duration(dur) + return nil + } + + return fmt.Errorf("got %s, want a duration, string, or int", v.Type()) +} + +// String implements the Stringer interface. +func (d Duration) String() string { return time.Duration(d).String() } + +// Type returns a short string describing the value's type. +func (d Duration) Type() string { return "time.duration" } + +// Freeze renders Duration immutable. required by starlark.Value interface +// because duration is already immutable this is a no-op. +func (d Duration) Freeze() {} + +// Hash returns a function of x such that Equals(x, y) => Hash(x) == Hash(y) +// required by starlark.Value interface. +func (d Duration) Hash() (uint32, error) { + return uint32(d) ^ uint32(int64(d)>>32), nil +} + +// Truth reports whether the duration is non-zero. +func (d Duration) Truth() starlark.Bool { return d != 0 } + +// Attr gets a value for a string attribute, implementing dot expression support +// in starklark. required by starlark.HasAttrs interface. +func (d Duration) Attr(name string) (starlark.Value, error) { + switch name { + case "hours": + return starlark.Float(time.Duration(d).Hours()), nil + case "minutes": + return starlark.Float(time.Duration(d).Minutes()), nil + case "seconds": + return starlark.Float(time.Duration(d).Seconds()), nil + case "milliseconds": + return starlark.MakeInt64(time.Duration(d).Milliseconds()), nil + case "microseconds": + return starlark.MakeInt64(time.Duration(d).Microseconds()), nil + case "nanoseconds": + return starlark.MakeInt64(time.Duration(d).Nanoseconds()), nil + } + return nil, fmt.Errorf("unrecognized %s attribute %q", d.Type(), name) +} + +// AttrNames lists available dot expression strings. required by +// starlark.HasAttrs interface. +func (d Duration) AttrNames() []string { + return []string{ + "hours", + "minutes", + "seconds", + "milliseconds", + "microseconds", + "nanoseconds", + } +} + +// CompareSameType implements comparison of two Duration values. required by +// starlark.Comparable interface. +func (d Duration) CompareSameType(op syntax.Token, v starlark.Value, depth int) (bool, error) { + cmp := 0 + if x, y := d, v.(Duration); x < y { + cmp = -1 + } else if x > y { + cmp = 1 + } + return threeway(op, cmp), nil +} + +// Binary implements binary operators, which satisfies the starlark.HasBinary +// interface. operators: +// duration + duration = duration +// duration + time = time +// duration - duration = duration +// duration / duration = float +// duration / int = duration +// duration / float = duration +// duration // duration = int +// duration * int = duration +func (d Duration) Binary(op syntax.Token, y starlark.Value, side starlark.Side) (starlark.Value, error) { + x := time.Duration(d) + + switch op { + case syntax.PLUS: + switch y := y.(type) { + case Duration: + return Duration(x + time.Duration(y)), nil + case Time: + return Time(time.Time(y).Add(x)), nil + } + + case syntax.MINUS: + switch y := y.(type) { + case Duration: + return Duration(x - time.Duration(y)), nil + } + + case syntax.SLASH: + switch y := y.(type) { + case Duration: + if y == 0 { + return nil, fmt.Errorf("%s division by zero", d.Type()) + } + return starlark.Float(x.Nanoseconds()) / starlark.Float(time.Duration(y).Nanoseconds()), nil + case starlark.Int: + if side == starlark.Right { + return nil, fmt.Errorf("unsupported operation") + } + i, ok := y.Int64() + if !ok { + return nil, fmt.Errorf("int value out of range (want signed 64-bit value)") + } + if i == 0 { + return nil, fmt.Errorf("%s division by zero", d.Type()) + } + return d / Duration(i), nil + case starlark.Float: + f := float64(y) + if f == 0 { + return nil, fmt.Errorf("%s division by zero", d.Type()) + } + return Duration(float64(x.Nanoseconds()) / f), nil + } + + case syntax.SLASHSLASH: + switch y := y.(type) { + case Duration: + if y == 0 { + return nil, fmt.Errorf("%s division by zero", d.Type()) + } + return starlark.MakeInt64(x.Nanoseconds() / time.Duration(y).Nanoseconds()), nil + } + + case syntax.STAR: + switch y := y.(type) { + case starlark.Int: + i, ok := y.Int64() + if !ok { + return nil, fmt.Errorf("int value out of range (want signed 64-bit value)") + } + return d * Duration(i), nil + } + } + + return nil, nil +} + +// Time is a Starlark representation of a moment in time. +type Time time.Time + +func newTime(thread *starlark.Thread, _ *starlark.Builtin, args starlark.Tuple, kwargs []starlark.Tuple) (starlark.Value, error) { + var ( + year, month, day, hour, min, sec, nsec int + loc string + ) + if err := starlark.UnpackArgs("time", args, kwargs, + "year?", &year, + "month?", &month, + "day?", &day, + "hour?", &hour, + "minute?", &min, + "second?", &sec, + "nanosecond?", &nsec, + "location?", &loc, + ); err != nil { + return nil, err + } + if len(args) > 0 { + return nil, fmt.Errorf("time: unexpected positional arguments") + } + location, err := time.LoadLocation(loc) + if err != nil { + return nil, err + } + return Time(time.Date(year, time.Month(month), day, hour, min, sec, nsec, location)), nil +} + +// String returns the time formatted using the format string +// "2006-01-02 15:04:05.999999999 -0700 MST". +func (t Time) String() string { return time.Time(t).String() } + +// Type returns "time.time". +func (t Time) Type() string { return "time.time" } + +// Freeze renders time immutable. required by starlark.Value interface +// because Time is already immutable this is a no-op. +func (t Time) Freeze() {} + +// Hash returns a function of x such that Equals(x, y) => Hash(x) == Hash(y) +// required by starlark.Value interface. +func (t Time) Hash() (uint32, error) { + return uint32(time.Time(t).UnixNano()) ^ uint32(int64(time.Time(t).UnixNano())>>32), nil +} + +// Truth returns the truth value of an object required by starlark.Value +// interface. +func (t Time) Truth() starlark.Bool { return starlark.Bool(time.Time(t).IsZero()) } + +// Attr gets a value for a string attribute, implementing dot expression support +// in starklark. required by starlark.HasAttrs interface. +func (t Time) Attr(name string) (starlark.Value, error) { + switch name { + case "year": + return starlark.MakeInt(time.Time(t).Year()), nil + case "month": + return starlark.MakeInt(int(time.Time(t).Month())), nil + case "day": + return starlark.MakeInt(time.Time(t).Day()), nil + case "hour": + return starlark.MakeInt(time.Time(t).Hour()), nil + case "minute": + return starlark.MakeInt(time.Time(t).Minute()), nil + case "second": + return starlark.MakeInt(time.Time(t).Second()), nil + case "nanosecond": + return starlark.MakeInt(time.Time(t).Nanosecond()), nil + case "unix": + return starlark.MakeInt64(time.Time(t).Unix()), nil + case "unix_nano": + return starlark.MakeInt64(time.Time(t).UnixNano()), nil + } + return builtinAttr(t, name, timeMethods) +} + +// AttrNames lists available dot expression strings for time. required by +// starlark.HasAttrs interface. +func (t Time) AttrNames() []string { + return append(builtinAttrNames(timeMethods), + "year", + "month", + "day", + "hour", + "minute", + "second", + "nanosecond", + "unix", + "unix_nano", + ) +} + +// CompareSameType implements comparison of two Time values. required by +// starlark.Comparable interface. +func (t Time) CompareSameType(op syntax.Token, yV starlark.Value, depth int) (bool, error) { + x := time.Time(t) + y := time.Time(yV.(Time)) + cmp := 0 + if x.Before(y) { + cmp = -1 + } else if x.After(y) { + cmp = 1 + } + return threeway(op, cmp), nil +} + +// Binary implements binary operators, which satisfies the starlark.HasBinary +// interface +// time + duration = time +// time - duration = time +// time - time = duration +func (t Time) Binary(op syntax.Token, y starlark.Value, side starlark.Side) (starlark.Value, error) { + x := time.Time(t) + + switch op { + case syntax.PLUS: + switch y := y.(type) { + case Duration: + return Time(x.Add(time.Duration(y))), nil + } + case syntax.MINUS: + switch y := y.(type) { + case Duration: + return Time(x.Add(time.Duration(-y))), nil + case Time: + // time - time = duration + return Duration(x.Sub(time.Time(y))), nil + } + } + + return nil, nil +} + +var timeMethods = map[string]builtinMethod{ + "in_location": timeIn, + "format": timeFormat, +} + +func timeFormat(fnname string, recV starlark.Value, args starlark.Tuple, kwargs []starlark.Tuple) (starlark.Value, error) { + var x string + if err := starlark.UnpackPositionalArgs("format", args, kwargs, 1, &x); err != nil { + return nil, err + } + + recv := time.Time(recV.(Time)) + return starlark.String(recv.Format(x)), nil +} + +func timeIn(fnname string, recV starlark.Value, args starlark.Tuple, kwargs []starlark.Tuple) (starlark.Value, error) { + var x string + if err := starlark.UnpackPositionalArgs("in_location", args, kwargs, 1, &x); err != nil { + return nil, err + } + loc, err := time.LoadLocation(x) + if err != nil { + return nil, err + } + + recv := time.Time(recV.(Time)) + return Time(recv.In(loc)), nil +} + +type builtinMethod func(fnname string, recv starlark.Value, args starlark.Tuple, kwargs []starlark.Tuple) (starlark.Value, error) + +func builtinAttr(recv starlark.Value, name string, methods map[string]builtinMethod) (starlark.Value, error) { + method := methods[name] + if method == nil { + return nil, nil // no such method + } + + // Allocate a closure over 'method'. + impl := func(thread *starlark.Thread, b *starlark.Builtin, args starlark.Tuple, kwargs []starlark.Tuple) (starlark.Value, error) { + return method(b.Name(), b.Receiver(), args, kwargs) + } + return starlark.NewBuiltin(name, impl).BindReceiver(recv), nil +} + +func builtinAttrNames(methods map[string]builtinMethod) []string { + names := make([]string, 0, len(methods)) + for name := range methods { + names = append(names, name) + } + sort.Strings(names) + return names +} + +// Threeway interprets a three-way comparison value cmp (-1, 0, +1) +// as a boolean comparison (e.g. x < y). +func threeway(op syntax.Token, cmp int) bool { + switch op { + case syntax.EQL: + return cmp == 0 + case syntax.NEQ: + return cmp != 0 + case syntax.LE: + return cmp <= 0 + case syntax.LT: + return cmp < 0 + case syntax.GE: + return cmp >= 0 + case syntax.GT: + return cmp > 0 + } + panic(op) +} diff --git a/vendor/go.starlark.net/starlarkstruct/module.go b/vendor/go.starlark.net/starlarkstruct/module.go new file mode 100644 index 00000000..735c98ae --- /dev/null +++ b/vendor/go.starlark.net/starlarkstruct/module.go @@ -0,0 +1,43 @@ +package starlarkstruct + +import ( + "fmt" + + "go.starlark.net/starlark" +) + +// A Module is a named collection of values, +// typically a suite of functions imported by a load statement. +// +// It differs from Struct primarily in that its string representation +// does not enumerate its fields. +type Module struct { + Name string + Members starlark.StringDict +} + +var _ starlark.HasAttrs = (*Module)(nil) + +func (m *Module) Attr(name string) (starlark.Value, error) { return m.Members[name], nil } +func (m *Module) AttrNames() []string { return m.Members.Keys() } +func (m *Module) Freeze() { m.Members.Freeze() } +func (m *Module) Hash() (uint32, error) { return 0, fmt.Errorf("unhashable: %s", m.Type()) } +func (m *Module) String() string { return fmt.Sprintf("", m.Name) } +func (m *Module) Truth() starlark.Bool { return true } +func (m *Module) Type() string { return "module" } + +// MakeModule may be used as the implementation of a Starlark built-in +// function, module(name, **kwargs). It returns a new module with the +// specified name and members. +func MakeModule(thread *starlark.Thread, b *starlark.Builtin, args starlark.Tuple, kwargs []starlark.Tuple) (starlark.Value, error) { + var name string + if err := starlark.UnpackPositionalArgs(b.Name(), args, nil, 1, &name); err != nil { + return nil, err + } + members := make(starlark.StringDict, len(kwargs)) + for _, kwarg := range kwargs { + k := string(kwarg[0].(starlark.String)) + members[k] = kwarg[1] + } + return &Module{name, members}, nil +} diff --git a/vendor/go.starlark.net/starlarkstruct/struct.go b/vendor/go.starlark.net/starlarkstruct/struct.go new file mode 100644 index 00000000..7285bfa5 --- /dev/null +++ b/vendor/go.starlark.net/starlarkstruct/struct.go @@ -0,0 +1,281 @@ +// 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 new struct instance 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 +} diff --git a/vendor/modules.txt b/vendor/modules.txt index 161a09fe..29230a47 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -62,8 +62,10 @@ github.com/spf13/pflag ## explicit go.starlark.net/internal/compile go.starlark.net/internal/spell +go.starlark.net/lib/time go.starlark.net/resolve go.starlark.net/starlark +go.starlark.net/starlarkstruct go.starlark.net/syntax # golang.org/x/arch v0.0.0-20190927153633-4e8777c89be4 ## explicit