1set/starlet

View on GitHub
lib/atom/atom.go

Summary

Maintainability
B
6 hrs
Test Coverage
// Package atom provides atomic operations for integers, floats and strings.
// Inspired by the sync/atomic and go.uber.org/atomic packages from Go.
package atom

import (
    "fmt"
    "strings"
    "sync"

    tps "github.com/1set/starlet/dataconv/types"
    "go.starlark.net/starlark"
    "go.starlark.net/starlarkstruct"
    "go.starlark.net/syntax"
    "go.uber.org/atomic"
)

// ModuleName defines the expected name for this Module when used in starlark's load() function, eg: load('atom', 'new_int')
const ModuleName = "atom"

var (
    once       sync.Once
    atomModule starlark.StringDict
)

// LoadModule loads the atom module. It is concurrency-safe and idempotent.
func LoadModule() (starlark.StringDict, error) {
    once.Do(func() {
        atomModule = starlark.StringDict{
            ModuleName: &starlarkstruct.Module{
                Name: ModuleName,
                Members: starlark.StringDict{
                    "new_int":    starlark.NewBuiltin(ModuleName+".new_int", newInt),
                    "new_float":  starlark.NewBuiltin(ModuleName+".new_float", newFloat),
                    "new_string": starlark.NewBuiltin(ModuleName+".new_string", newString),
                },
            },
        }
    })
    return atomModule, nil
}

// for integer

var (
    _ starlark.Value      = (*AtomicInt)(nil)
    _ starlark.HasAttrs   = (*AtomicInt)(nil)
    _ starlark.Comparable = (*AtomicInt)(nil)
)

func newInt(thread *starlark.Thread, b *starlark.Builtin, args starlark.Tuple, kwargs []starlark.Tuple) (starlark.Value, error) {
    var value int64
    if err := starlark.UnpackArgs(b.Name(), args, kwargs, "value?", &value); err != nil {
        return nil, err
    }
    return &AtomicInt{val: atomic.NewInt64(value)}, nil
}

type AtomicInt struct {
    val    *atomic.Int64
    frozen bool
}

func (a *AtomicInt) String() string {
    return fmt.Sprintf("<atom_int:%d>", a.val.Load())
}

func (a *AtomicInt) Type() string {
    return "atom_int"
}

func (a *AtomicInt) Freeze() {
    a.frozen = true
}

func (a *AtomicInt) Truth() starlark.Bool {
    return a.val.Load() != 0
}

func (a *AtomicInt) Hash() (uint32, error) {
    //return 0, fmt.Errorf("unhashable: %s", a.Type())
    return hashInt64(a.val.Load()), nil
}

func (a *AtomicInt) Attr(name string) (starlark.Value, error) {
    return builtinAttr(a, name, intMethods)
}

func (a *AtomicInt) AttrNames() []string {
    return builtinAttrNames(intMethods)
}

func (a *AtomicInt) CompareSameType(op syntax.Token, y_ starlark.Value, depth int) (bool, error) {
    vx := a.val.Load()
    y := y_.(*AtomicInt)
    vy := y.val.Load()

    cmp := 0
    if vx < vy {
        cmp = -1
    } else if vx > vy {
        cmp = 1
    } else {
        cmp = 0
    }
    return threewayCompare(op, cmp)
}

// for float

var (
    _ starlark.Value      = (*AtomicFloat)(nil)
    _ starlark.HasAttrs   = (*AtomicFloat)(nil)
    _ starlark.Comparable = (*AtomicFloat)(nil)
)

type AtomicFloat struct {
    val    *atomic.Float64
    frozen bool
}

func newFloat(thread *starlark.Thread, b *starlark.Builtin, args starlark.Tuple, kwargs []starlark.Tuple) (starlark.Value, error) {
    var value tps.FloatOrInt
    if err := starlark.UnpackArgs(b.Name(), args, kwargs, "value?", &value); err != nil {
        return nil, err
    }
    return &AtomicFloat{val: atomic.NewFloat64(value.GoFloat())}, nil
}

func (a *AtomicFloat) String() string {
    return fmt.Sprintf("<atom_float:%v>", a.val.Load())
}

func (a *AtomicFloat) Type() string {
    return "atom_float"
}

func (a *AtomicFloat) Freeze() {
    a.frozen = true
}

func (a *AtomicFloat) Truth() starlark.Bool {
    return a.val.Load() != 0
}

func (a *AtomicFloat) Hash() (uint32, error) {
    return hashFloat64(a.val.Load()), nil
}

func (a *AtomicFloat) Attr(name string) (starlark.Value, error) {
    return builtinAttr(a, name, floatMethods)
}

func (a *AtomicFloat) AttrNames() []string {
    return builtinAttrNames(floatMethods)
}

func (a *AtomicFloat) CompareSameType(op syntax.Token, y_ starlark.Value, depth int) (bool, error) {
    vx := a.val.Load()
    y := y_.(*AtomicFloat)
    vy := y.val.Load()

    cmp := 0
    if vx < vy {
        cmp = -1
    } else if vx > vy {
        cmp = 1
    } else {
        cmp = 0
    }
    return threewayCompare(op, cmp)
}

// for string

var (
    _ starlark.Value      = (*AtomicString)(nil)
    _ starlark.HasAttrs   = (*AtomicString)(nil)
    _ starlark.Comparable = (*AtomicString)(nil)
)

type AtomicString struct {
    val    *atomic.String
    frozen bool
}

func newString(thread *starlark.Thread, b *starlark.Builtin, args starlark.Tuple, kwargs []starlark.Tuple) (starlark.Value, error) {
    var value string
    if err := starlark.UnpackArgs(b.Name(), args, kwargs, "value?", &value); err != nil {
        return nil, err
    }
    return &AtomicString{val: atomic.NewString(value)}, nil
}

func (a *AtomicString) String() string {
    return fmt.Sprintf("<atom_string:%q>", a.val.Load())
}

func (a *AtomicString) Type() string {
    return "atom_string"
}

func (a *AtomicString) Freeze() {
    a.frozen = true
}

func (a *AtomicString) Truth() starlark.Bool {
    return a.val.Load() != ""
}

func (a *AtomicString) Hash() (uint32, error) {
    return hashString(a.val.Load()), nil
}

func (a *AtomicString) Attr(name string) (starlark.Value, error) {
    return builtinAttr(a, name, stringMethods)
}

func (a *AtomicString) AttrNames() []string {
    return builtinAttrNames(stringMethods)
}

func (a *AtomicString) CompareSameType(op syntax.Token, y_ starlark.Value, depth int) (bool, error) {
    vx := a.val.Load()
    y := y_.(*AtomicString)
    vy := y.val.Load()

    return threewayCompare(op, strings.Compare(vx, vy))
}