mirror of
https://github.com/go-delve/delve.git
synced 2025-10-29 09:46:56 +08:00
starbind: Make the time module available to star scripts (#3375)
This patch makes the time library available to Starlark scripts. This library is one of the very few few that are built into starlark-go (the others are json, math, proto). I've played around with Starlark scripting today, and immediately I wanted to measure how long certain computations take.
This commit is contained in:
@ -74,6 +74,8 @@ cur_scope() | Returns the current evaluation scope
|
||||
default_load_config() | Returns the current default load configuration
|
||||
<!-- END MAPPING TABLE -->
|
||||
|
||||
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:
|
||||
|
||||
@ -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 {
|
||||
|
||||
507
vendor/go.starlark.net/lib/time/time.go
generated
vendored
Normal file
507
vendor/go.starlark.net/lib/time/time.go
generated
vendored
Normal file
@ -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)
|
||||
}
|
||||
43
vendor/go.starlark.net/starlarkstruct/module.go
generated
vendored
Normal file
43
vendor/go.starlark.net/starlarkstruct/module.go
generated
vendored
Normal file
@ -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("<module %q>", 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
|
||||
}
|
||||
281
vendor/go.starlark.net/starlarkstruct/struct.go
generated
vendored
Normal file
281
vendor/go.starlark.net/starlarkstruct/struct.go
generated
vendored
Normal file
@ -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
|
||||
}
|
||||
2
vendor/modules.txt
vendored
2
vendor/modules.txt
vendored
@ -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
|
||||
|
||||
Reference in New Issue
Block a user