Documentation
¶
Index ¶
- Constants
- Variables
- func CompareDigest(a, b string) bool
- func GetCompiledRegex(pattern string) (*regexp.Regexp, error)
- func GetTimeFromObject(obj object.Object) (time.Time, object.Object)
- func NewMsgpackLibrary(c MsgpackCodec) *object.Library
- func PythonToGoDateFormat(pyFormat string) string
- func RegisterAll(p interface{ ... })
- type MsgpackCodec
- type StringIOWriter
Constants ¶
const ( RE_IGNORECASE = 2 // re.I or re.IGNORECASE RE_MULTILINE = 8 // re.M or re.MULTILINE RE_DOTALL = 16 // re.S or re.DOTALL )
Flag constants matching Python's re module
const ( ContextlibLibraryName = "contextlib" DifflibLibraryName = "difflib" IOLibraryName = "io" JSONLibraryName = "json" ReLibraryName = "re" TimeLibraryName = "time" DatetimeLibraryName = "datetime" MathLibraryName = "math" Base64LibraryName = "base64" HashlibLibraryName = "hashlib" HmacLibraryName = "hmac" RandomLibraryName = "random" URLLibLibraryName = "urllib" URLParseLibraryName = "urllib.parse" StringLibraryName = "string" UUIDLibraryName = "uuid" HTMLLibraryName = "html" StatisticsLibraryName = "statistics" FunctoolsLibraryName = "functools" TextwrapLibraryName = "textwrap" PlatformLibraryName = "platform" ItertoolsLibraryName = "itertools" CollectionsLibraryName = "collections" )
Library names as constants for easy reference
const MsgpackLibraryName = "msgpack"
MsgpackLibraryName is the import name for the MessagePack library.
Variables ¶
var ( DatetimeClass *object.Class DateClass *object.Class )
Forward declarations for classes (set in init)
var ( HashlibSHA256Builtin = makeHashBuiltin("sha256", `sha256([data]) - Create a SHA-256 hash object Returns a hash object. Call .hexdigest() or .digest() to get the result, or .update() to feed more data. Example: import hashlib print(hashlib.sha256("hello").hexdigest())`) HashlibSHA1Builtin = makeHashBuiltin("sha1", `sha1([data]) - Create a SHA-1 hash object Returns a hash object. Call .hexdigest() or .digest() to get the result.`) HashlibMD5Builtin = makeHashBuiltin("md5", `md5([data]) - Create an MD5 hash object Returns a hash object. Call .hexdigest() or .digest() to get the result.`) )
Package-level references to the constructor builtins so that hmac can identify them when passed as the digestmod argument (e.g. hashlib.sha256).
var Base64Library = object.NewLibrary(Base64LibraryName, map[string]*object.Builtin{ "b64encode": { Fn: func(ctx context.Context, kwargs object.Kwargs, args ...object.Object) object.Object { if err := errors.ExactArgs(args, 1); err != nil { return err } data, errObj := coerceToBytes(args[0]) if errObj != nil { return errObj } encoded := base64.StdEncoding.EncodeToString(data) return object.NewString(encoded) }, HelpText: `b64encode(s) - Encode bytes-like object to Base64 Accepts a Bytes value or a String (UTF-8 encoded) and returns the Base64 representation as a string.`, }, "b64decode": { Fn: func(ctx context.Context, kwargs object.Kwargs, args ...object.Object) object.Object { if err := errors.ExactArgs(args, 1); err != nil { return err } str, err := args[0].AsString() if err != nil { return err } decoded, decodeErr := base64.StdEncoding.DecodeString(str) if decodeErr != nil { return errors.NewError("base64 decode error: %s", decodeErr.Error()) } return object.NewBytes(decoded) }, HelpText: `b64decode(s) - Decode a Base64 encoded string Returns the decoded bytes as a Bytes value. Use .decode() on the result to obtain a string when the underlying data is text.`, }, }, nil, "Base64 encoding and decoding library")
var CollectionsLibrary = object.NewLibrary(CollectionsLibraryName, map[string]*object.Builtin{ "Counter": { Fn: counterConstructor, HelpText: `Counter([iterable_or_mapping], **kwargs) - Count elements A dict of element -> count, as in Python. Missing elements count 0. Example: c = collections.Counter([1, 1, 2, 3, 3, 3]) c[3] -> 3 c[4] -> 0 c.most_common(2) -> [(3, 3), (1, 2)] c.update([1]); c.total() -> 7`, }, "most_common": { Fn: counterMostCommonFn, HelpText: `most_common(counter[, n]) - Same as counter.most_common(n)`, }, "OrderedDict": { Fn: func(ctx context.Context, kwargs object.Kwargs, args ...object.Object) object.Object { od := &object.Dict{Pairs: make(map[string]object.DictPair)} if len(args) == 0 { return od } if err := errors.MaxArgs(args, 1); err != nil { return err } switch arg := args[0].(type) { case *object.List: for _, elem := range arg.Elements { tuple, ok := elem.(*object.Tuple) if !ok || len(tuple.Elements) != 2 { return errors.NewError("OrderedDict() items must be (key, value) tuples") } key := object.DictKey(tuple.Elements[0]) od.Pairs[key] = object.DictPair{ Key: tuple.Elements[0], Value: tuple.Elements[1], } } case *object.Dict: for k, v := range arg.Pairs { od.Pairs[k] = v } default: return errors.NewTypeError("list of tuples or dict", args[0].Type().String()) } return od }, HelpText: `OrderedDict([items]) - Dict that remembers insertion order Creates a dict that maintains insertion order. Note: Scriptling dicts already maintain order, so this is equivalent to dict(). Example: od = collections.OrderedDict([("a", 1), ("b", 2)])`, }, "deque": { Fn: func(ctx context.Context, kwargs object.Kwargs, args ...object.Object) object.Object { elements := []object.Object{} if len(args) >= 1 { switch arg := args[0].(type) { case *object.List: elements = make([]object.Object, len(arg.Elements)) copy(elements, arg.Elements) case *object.Tuple: elements = make([]object.Object, len(arg.Elements)) copy(elements, arg.Elements) case *object.String: for _, ch := range arg.StringValue() { elements = append(elements, object.NewString(string(ch))) } default: elems, errObj := collectIterable(ctx, args[0]) if errObj != nil { return errObj } elements = elems } } maxlen := int64(-1) mlObj := object.Object(nil) if len(args) >= 2 { mlObj = args[1] } else if v := kwargs.Get("maxlen"); v != nil { mlObj = v } if mlObj != nil { if ml, ok := mlObj.(*object.Integer); ok { maxlen = ml.IntValue() if maxlen >= 0 && int64(len(elements)) > maxlen { elements = elements[len(elements)-int(maxlen):] } } else if mlObj.Type() != object.NULL_OBJ { return errors.NewTypeError("INTEGER or None", mlObj.Type().String()) } } return createDequeInstance(elements, maxlen) }, HelpText: `deque([iterable[, maxlen]]) - Double-ended queue Creates a double-ended queue with appendleft/popleft/rotate and friends, like Python's collections.deque. maxlen (or None) bounds the length, dropping from the opposite end on overflow. Example: d = collections.deque([1, 2, 3]) d.appendleft(0) # deque([0, 1, 2, 3])`, }, "namedtuple": { Fn: func(ctx context.Context, kwargs object.Kwargs, args ...object.Object) object.Object { if err := errors.ExactArgs(args, 2); err != nil { return err } typename, ok := args[0].(*object.String) if !ok { return errors.NewTypeError("STRING", args[0].Type().String()) } var fieldNames []string switch fn := args[1].(type) { case *object.List: for _, elem := range fn.Elements { if s, ok := elem.(*object.String); ok { fieldNames = append(fieldNames, s.StringValue()) } else { return errors.NewError("field names must be strings") } } case *object.Tuple: for _, elem := range fn.Elements { if s, ok := elem.(*object.String); ok { fieldNames = append(fieldNames, s.StringValue()) } else { return errors.NewError("field names must be strings") } } case *object.String: fields := strings.FieldsFunc(fn.StringValue(), func(r rune) bool { return r == ' ' || r == ',' }) for _, f := range fields { f = strings.TrimSpace(f) if f != "" { fieldNames = append(fieldNames, f) } } default: return errors.NewTypeError("list, tuple, or string", args[1].Type().String()) } methods := make(map[string]object.Object) methods["__init__"] = &object.Builtin{ Fn: func(ctx context.Context, kwargs object.Kwargs, args ...object.Object) object.Object { if len(args) != len(fieldNames)+1 { return errors.NewArgumentError(len(args), len(fieldNames)+1) } nt := args[0].(*object.Instance) for i, name := range fieldNames { nt.SetField(name, args[i+1]) } nt.SetField("__typename__", typename) fieldNameObjs := make([]object.Object, len(fieldNames)) for i, name := range fieldNames { fieldNameObjs[i] = object.NewString(name) } nt.SetField("__fields__", &object.Tuple{Elements: fieldNameObjs}) parts := make([]string, 0, len(fieldNames)) for i, name := range fieldNames { parts = append(parts, name+"="+reprValue(args[i+1])) } nt.SetField("__str_repr__", object.NewString(typename.StringValue()+"("+strings.Join(parts, ", ")+")")) return &object.Null{} }, } methods["__getitem__"] = &object.Builtin{ Fn: func(ctx context.Context, kwargs object.Kwargs, args ...object.Object) object.Object { if err := errors.ExactArgs(args, 2); err != nil { return err } nt := args[0].(*object.Instance) key := args[1].Inspect() if key == "__typename__" || key == "__fields__" { return &object.Null{} } if value, exists := nt.GetField(key); exists { return value } return &object.Null{} }, HelpText: `__getitem__(key) - Get field value (supports nt[key] syntax)`, } methods["__repr__"] = &object.Builtin{ Fn: func(ctx context.Context, kwargs object.Kwargs, args ...object.Object) object.Object { nt := args[0].(*object.Instance) parts := make([]string, 0, len(fieldNames)) for _, name := range fieldNames { if v, exists := nt.GetField(name); exists { parts = append(parts, name+"="+reprValue(v)) } } return object.NewString(typename.StringValue() + "(" + strings.Join(parts, ", ") + ")") }, } ntClass := &object.Class{ Name: typename.StringValue(), Methods: methods, } return ntClass }, HelpText: `namedtuple(typename, field_names) - Create a named tuple class Creates a class for creating named tuple instances with direct attribute access. Example: Point = collections.namedtuple("Point", ["x", "y"]) p = Point(1, 2) p.x # 1 (direct attribute access) p["y"] # 2 (dict-style access) p.x() # Also works for backward compatibility`, }, "ChainMap": { Fn: func(ctx context.Context, kwargs object.Kwargs, args ...object.Object) object.Object { chainMap := &object.Dict{Pairs: make(map[string]object.DictPair)} maps := make([]object.Object, len(args)) for i, arg := range args { if _, ok := arg.(*object.Dict); !ok { return errors.NewTypeError("DICT", arg.Type().String()) } maps[i] = arg } for i := len(args) - 1; i >= 0; i-- { d := args[i].(*object.Dict) for k, v := range d.Pairs { chainMap.Pairs[k] = v } } return chainMap }, HelpText: `ChainMap(*maps) - Group multiple dicts for single lookup Creates a single dict view over multiple dicts. First dict has priority. Example: d1 = {"a": 1} d2 = {"b": 2, "a": 10} cm = collections.ChainMap(d1, d2) cm["a"] # 1 (from d1) cm["b"] # 2 (from d2)`, }, }, map[string]object.Object{ "Counter": CounterClass, "DefaultDict": DefaultDictClass, "defaultdict": DefaultDictClass, }, "Python-compatible collections library for specialized container datatypes")
CollectionsLibrary provides Python-like collections functions
var ContextlibLibrary = object.NewLibrary(ContextlibLibraryName, map[string]*object.Builtin{ "suppress": { Fn: suppressNew, HelpText: `suppress(*exc_types) - Context manager that silently suppresses the given exception types. If no exception types are given, all exceptions are suppressed. Use Exception to suppress all standard exceptions. Example: with contextlib.suppress(ValueError): int("not a number") # silently ignored`, }, }, nil, "Utilities for common tasks involving the with statement")
var CounterClass = &object.Class{ Name: "Counter", Methods: map[string]object.Object{ "__init__": counterMethod("__init__([iterable_or_mapping], **kwargs)", func(ctx context.Context, kwargs object.Kwargs, args ...object.Object) object.Object { if err := errors.RangeArgs(args, 1, 2); err != nil { return err } _, d := counterSelf(args) if len(args) == 2 { if err := counterApply(ctx, d, args[1], 1); err != nil { return err } } if err := counterApplyKwargs(d, kwargs, 1); err != nil { return err } return &object.Null{} }), "__getitem__": counterMethod("c[key] - count of key, 0 when missing", func(ctx context.Context, kwargs object.Kwargs, args ...object.Object) object.Object { if err := errors.ExactArgs(args, 2); err != nil { return err } _, d := counterSelf(args) return counterCount(d, args[1]) }), "__setitem__": counterMethod("c[key] = count", func(ctx context.Context, kwargs object.Kwargs, args ...object.Object) object.Object { if err := errors.ExactArgs(args, 3); err != nil { return err } if !object.IsHashable(args[1]) { return &object.Exception{Message: "unhashable type: '" + strings.ToLower(args[1].Type().String()) + "'", ExceptionType: object.ExceptionTypeTypeError, Raised: true} } _, d := counterSelf(args) d.Pairs[object.DictKey(args[1])] = object.DictPair{Key: args[1], Value: args[2]} return &object.Null{} }), "__delitem__": counterMethod("del c[key]", func(ctx context.Context, kwargs object.Kwargs, args ...object.Object) object.Object { if err := errors.ExactArgs(args, 2); err != nil { return err } _, d := counterSelf(args) delete(d.Pairs, object.DictKey(args[1])) return &object.Null{} }), "__len__": counterMethod("len(c)", func(ctx context.Context, kwargs object.Kwargs, args ...object.Object) object.Object { _, d := counterSelf(args) return object.NewInteger(int64(len(d.Pairs))) }), "__bool__": counterMethod("bool(c)", func(ctx context.Context, kwargs object.Kwargs, args ...object.Object) object.Object { _, d := counterSelf(args) return object.NewBoolean(len(d.Pairs) > 0) }), "__contains__": counterMethod("key in c", func(ctx context.Context, kwargs object.Kwargs, args ...object.Object) object.Object { if err := errors.ExactArgs(args, 2); err != nil { return err } _, d := counterSelf(args) _, ok := d.Pairs[object.DictKey(args[1])] return object.NewBoolean(ok) }), "__iter__": counterMethod("iter(c) - the keys", func(ctx context.Context, kwargs object.Kwargs, args ...object.Object) object.Object { _, d := counterSelf(args) return d.CreateIterator() }), "__eq__": counterMethod("c == other - same counts", func(ctx context.Context, kwargs object.Kwargs, args ...object.Object) object.Object { if err := errors.ExactArgs(args, 2); err != nil { return err } _, d := counterSelf(args) var other *object.Dict switch o := args[1].(type) { case *object.Instance: if o.Class != counterClassRef { return object.NewBoolean(false) } other = counterData(o) case *object.Dict: other = o default: return object.NewBoolean(false) } same := func(a, b *object.Dict) bool { for k, p := range a.Pairs { var q object.Object = counterZero if pair, ok := b.Pairs[k]; ok { q = pair.Value } x, xOK := countValue(p.Value) y, yOK := countValue(q) if xOK && yOK { if x != y { return false } } else if p.Value.Inspect() != q.Inspect() { return false } } return true } return object.NewBoolean(same(d, other) && same(other, d)) }), "__repr__": counterMethod("repr(c) - Counter({'a': 2, 'b': 1})", func(ctx context.Context, kwargs object.Kwargs, args ...object.Object) object.Object { _, d := counterSelf(args) entries := counterSorted(d) if len(entries) == 0 { return object.NewString("Counter()") } parts := make([]string, len(entries)) for i, e := range entries { parts[i] = counterKeyRepr(e.key) + ": " + counterKeyRepr(e.count) } return object.NewString("Counter({" + strings.Join(parts, ", ") + "})") }), "__add__": counterOperator('+'), "__sub__": counterOperator('-'), "__or__": counterOperator('|'), "__and__": counterOperator('&'), "keys": counterMethod("keys() - view of the elements", func(ctx context.Context, kwargs object.Kwargs, args ...object.Object) object.Object { _, d := counterSelf(args) return &object.DictKeys{Dict: d} }), "values": counterMethod("values() - view of the counts", func(ctx context.Context, kwargs object.Kwargs, args ...object.Object) object.Object { _, d := counterSelf(args) return &object.DictValues{Dict: d} }), "items": counterMethod("items() - view of (element, count) pairs", func(ctx context.Context, kwargs object.Kwargs, args ...object.Object) object.Object { _, d := counterSelf(args) return &object.DictItems{Dict: d} }), "get": counterMethod("get(key, default=None)", func(ctx context.Context, kwargs object.Kwargs, args ...object.Object) object.Object { if err := errors.RangeArgs(args, 2, 3); err != nil { return err } _, d := counterSelf(args) if pair, ok := d.Pairs[object.DictKey(args[1])]; ok { return pair.Value } if len(args) == 3 { return args[2] } return &object.Null{} }), "pop": counterMethod("pop(key[, default])", func(ctx context.Context, kwargs object.Kwargs, args ...object.Object) object.Object { if err := errors.RangeArgs(args, 2, 3); err != nil { return err } _, d := counterSelf(args) k := object.DictKey(args[1]) if pair, ok := d.Pairs[k]; ok { delete(d.Pairs, k) return pair.Value } if len(args) == 3 { return args[2] } return &object.Exception{Message: counterKeyRepr(args[1]), ExceptionType: object.ExceptionTypeKeyError, Raised: true} }), "update": counterMethod("update([iterable_or_mapping], **kwargs) - add counts", func(ctx context.Context, kwargs object.Kwargs, args ...object.Object) object.Object { if err := errors.RangeArgs(args, 1, 2); err != nil { return err } _, d := counterSelf(args) if len(args) == 2 { if err := counterApply(ctx, d, args[1], 1); err != nil { return err } } if err := counterApplyKwargs(d, kwargs, 1); err != nil { return err } return &object.Null{} }), "subtract": counterMethod("subtract([iterable_or_mapping], **kwargs) - subtract counts (may go negative)", func(ctx context.Context, kwargs object.Kwargs, args ...object.Object) object.Object { if err := errors.RangeArgs(args, 1, 2); err != nil { return err } _, d := counterSelf(args) if len(args) == 2 { if err := counterApply(ctx, d, args[1], -1); err != nil { return err } } if err := counterApplyKwargs(d, kwargs, -1); err != nil { return err } return &object.Null{} }), "total": counterMethod("total() - sum of all counts", func(ctx context.Context, kwargs object.Kwargs, args ...object.Object) object.Object { _, d := counterSelf(args) var sum object.Object = counterZero for _, p := range d.Pairs { var err object.Object if sum, err = addCounts(sum, p.Value, 1); err != nil { return err } } return sum }), "most_common": counterMethod("most_common([n]) - (element, count) pairs, most common first", func(ctx context.Context, kwargs object.Kwargs, args ...object.Object) object.Object { if err := errors.RangeArgs(args, 1, 2); err != nil { return err } inst, _ := counterSelf(args) n, all, errObj := mostCommonArg(args, 1) if errObj != nil { return errObj } return counterMostCommon(inst, n, all) }), "elements": counterMethod("elements() - each element repeated by its count", func(ctx context.Context, kwargs object.Kwargs, args ...object.Object) object.Object { _, d := counterSelf(args) var out []object.Object for _, e := range counterSorted(d) { n, isInt := e.count.(*object.Integer) if !isInt { if _, isFloat := e.count.(*object.Float); isFloat { return &object.Exception{Message: "'float' object cannot be interpreted as an integer", ExceptionType: object.ExceptionTypeTypeError, Raised: true} } continue } for i := int64(0); i < n.IntValue(); i++ { out = append(out, e.key) } } return &object.List{Elements: out} }), "copy": counterMethod("copy() - a shallow copy", func(ctx context.Context, kwargs object.Kwargs, args ...object.Object) object.Object { _, d := counterSelf(args) out := newCounter() od := counterData(out) for k, p := range d.Pairs { od.Pairs[k] = p } return out }), "clear": counterMethod("clear() - remove all elements", func(ctx context.Context, kwargs object.Kwargs, args ...object.Object) object.Object { _, d := counterSelf(args) d.Pairs = make(map[string]object.DictPair) return &object.Null{} }), }, }
var DatetimeLibrary = object.NewLibrary(DatetimeLibraryName, map[string]*object.Builtin{ "timedelta": timedeltaBuiltinNew, "datetime": datetimeConstructorBuiltin, "date": dateConstructorBuiltin, }, nil, "Date and time manipulation library.", )
DatetimeLibrary is the main datetime module
var DefaultDictClass = &object.Class{ Name: "DefaultDict", Methods: map[string]object.Object{ "__init__": &object.Builtin{ Fn: func(ctx context.Context, kwargs object.Kwargs, args ...object.Object) object.Object { if err := errors.ExactArgs(args, 2); err != nil { return err } dd := args[0].(*object.Instance) factory := args[1] dd.SetField("__default_factory__", factory) return &object.Null{} }, }, "__getitem__": &object.Builtin{ Fn: func(ctx context.Context, kwargs object.Kwargs, args ...object.Object) object.Object { if err := errors.ExactArgs(args, 2); err != nil { return err } dd := args[0].(*object.Instance) key := args[1].Inspect() if value, exists := dd.GetField(key); exists { return value } factory, hasFactory := dd.GetField("__default_factory__") if !hasFactory { return &object.Null{} } // Create default value based on factory var defaultValue object.Object switch f := factory.(type) { case *object.Builtin: defaultValue = f.Fn(ctx, object.NewKwargs(nil)) if object.IsError(defaultValue) { defaultValue = f.Fn(ctx, object.NewKwargs(nil), object.NewInteger(0)) if object.IsError(defaultValue) { return defaultValue } } case *object.String: switch f.StringValue() { case "int": defaultValue = object.NewInteger(0) case "float": defaultValue = object.NewFloat(0) case "str": defaultValue = object.NewString("") case "list": defaultValue = &object.List{Elements: []object.Object{}} case "dict": defaultValue = &object.Dict{Pairs: make(map[string]object.DictPair)} default: return errors.NewError("unknown default factory type: %s", f.StringValue()) } default: return errors.NewError("default_factory must be a builtin function or type name") } dd.SetField(key, defaultValue) return defaultValue }, HelpText: `__getitem__(key) - Get value with default creation (supports d[key] syntax)`, }, "__setitem__": &object.Builtin{ Fn: func(ctx context.Context, kwargs object.Kwargs, args ...object.Object) object.Object { if err := errors.ExactArgs(args, 3); err != nil { return err } dd := args[0].(*object.Instance) key := args[1].Inspect() value := args[2] dd.SetField(key, value) return &object.Null{} }, HelpText: `__setitem__(key, value) - Set value (supports d[key] = value syntax)`, }, }, }
DefaultDict class for dicts with default factory behavior
var DequeClass = &object.Class{ Name: "deque", Methods: map[string]object.Object{ "append": &object.Builtin{Fn: func(ctx context.Context, kwargs object.Kwargs, args ...object.Object) object.Object { if err := errors.ExactArgs(args, 2); err != nil { return err } inst := args[0].(*object.Instance) elems := append(dequeElems(inst), args[1]) setDequeElems(inst, dequeClamp(inst, elems, true)) return &object.Null{} }}, "appendleft": &object.Builtin{Fn: func(ctx context.Context, kwargs object.Kwargs, args ...object.Object) object.Object { if err := errors.ExactArgs(args, 2); err != nil { return err } inst := args[0].(*object.Instance) elems := append([]object.Object{args[1]}, dequeElems(inst)...) setDequeElems(inst, dequeClamp(inst, elems, false)) return &object.Null{} }}, "pop": &object.Builtin{Fn: func(ctx context.Context, kwargs object.Kwargs, args ...object.Object) object.Object { if err := errors.ExactArgs(args, 1); err != nil { return err } inst := args[0].(*object.Instance) elems := dequeElems(inst) if len(elems) == 0 { return &object.Exception{Message: "pop from an empty deque", ExceptionType: object.ExceptionTypeIndexError, Raised: true} } v := elems[len(elems)-1] setDequeElems(inst, elems[:len(elems)-1]) return v }}, "popleft": &object.Builtin{Fn: func(ctx context.Context, kwargs object.Kwargs, args ...object.Object) object.Object { if err := errors.ExactArgs(args, 1); err != nil { return err } inst := args[0].(*object.Instance) elems := dequeElems(inst) if len(elems) == 0 { return &object.Exception{Message: "pop from an empty deque", ExceptionType: object.ExceptionTypeIndexError, Raised: true} } v := elems[0] setDequeElems(inst, elems[1:]) return v }}, "extend": &object.Builtin{Fn: func(ctx context.Context, kwargs object.Kwargs, args ...object.Object) object.Object { if err := errors.ExactArgs(args, 2); err != nil { return err } inst := args[0].(*object.Instance) add, errObj := collectIterable(ctx, args[1]) if errObj != nil { return errObj } elems := append(dequeElems(inst), add...) setDequeElems(inst, dequeClamp(inst, elems, true)) return &object.Null{} }}, "extendleft": &object.Builtin{Fn: func(ctx context.Context, kwargs object.Kwargs, args ...object.Object) object.Object { if err := errors.ExactArgs(args, 2); err != nil { return err } inst := args[0].(*object.Instance) add, errObj := collectIterable(ctx, args[1]) if errObj != nil { return errObj } elems := dequeElems(inst) for _, v := range add { elems = append([]object.Object{v}, elems...) } setDequeElems(inst, dequeClamp(inst, elems, false)) return &object.Null{} }}, "rotate": &object.Builtin{Fn: func(ctx context.Context, kwargs object.Kwargs, args ...object.Object) object.Object { if len(args) < 1 || len(args) > 2 { return errors.NewError("rotate() takes 0-1 arguments (%d given)", len(args)-1) } inst := args[0].(*object.Instance) n := int64(1) if len(args) == 2 { v, err := args[1].AsInt() if err != nil { return errors.ParameterError("n", err) } n = v } elems := dequeElems(inst) length := int64(len(elems)) if length == 0 { return &object.Null{} } n = ((n % length) + length) % length rotated := append(append([]object.Object{}, elems[length-n:]...), elems[:length-n]...) setDequeElems(inst, rotated) return &object.Null{} }}, "clear": &object.Builtin{Fn: func(ctx context.Context, kwargs object.Kwargs, args ...object.Object) object.Object { if err := errors.ExactArgs(args, 1); err != nil { return err } setDequeElems(args[0].(*object.Instance), []object.Object{}) return &object.Null{} }}, "copy": &object.Builtin{Fn: func(ctx context.Context, kwargs object.Kwargs, args ...object.Object) object.Object { if err := errors.ExactArgs(args, 1); err != nil { return err } inst := args[0].(*object.Instance) elems := append([]object.Object{}, dequeElems(inst)...) ml := int64(-1) if m, ok := inst.Field("maxlen").(*object.Integer); ok { ml = m.IntValue() } return newDeque(elems, ml) }}, "count": &object.Builtin{Fn: func(ctx context.Context, kwargs object.Kwargs, args ...object.Object) object.Object { if err := errors.ExactArgs(args, 2); err != nil { return err } n := int64(0) for _, v := range dequeElems(args[0].(*object.Instance)) { if objectsEqual(v, args[1]) { n++ } } return object.NewInteger(n) }}, "__len__": &object.Builtin{Fn: func(ctx context.Context, kwargs object.Kwargs, args ...object.Object) object.Object { return object.NewInteger(int64(len(dequeElems(args[0].(*object.Instance))))) }}, "__iter__": &object.Builtin{Fn: func(ctx context.Context, kwargs object.Kwargs, args ...object.Object) object.Object { elems := append([]object.Object{}, dequeElems(args[0].(*object.Instance))...) i := 0 return object.NewIterator(func() (object.Object, bool) { if i >= len(elems) { return nil, false } v := elems[i] i++ return v, true }) }}, "__getitem__": &object.Builtin{Fn: func(ctx context.Context, kwargs object.Kwargs, args ...object.Object) object.Object { if err := errors.ExactArgs(args, 2); err != nil { return err } elems := dequeElems(args[0].(*object.Instance)) idx, err := args[1].AsInt() if err != nil { return errors.ParameterError("index", err) } if idx < 0 { idx += int64(len(elems)) } if idx < 0 || idx >= int64(len(elems)) { return &object.Exception{Message: "deque index out of range", ExceptionType: object.ExceptionTypeIndexError, Raised: true} } return elems[idx] }}, "__bool__": &object.Builtin{Fn: func(ctx context.Context, kwargs object.Kwargs, args ...object.Object) object.Object { return object.NewBoolean(len(dequeElems(args[0].(*object.Instance))) > 0) }}, "__str__": &object.Builtin{Fn: func(ctx context.Context, kwargs object.Kwargs, args ...object.Object) object.Object { inst := args[0].(*object.Instance) parts := make([]string, 0, 8) for _, v := range dequeElems(inst) { parts = append(parts, v.Inspect()) } out := "deque([" + strings.Join(parts, ", ") + "]" if m, ok := inst.Field("maxlen").(*object.Integer); ok { out += ", maxlen=" + m.Inspect() } return object.NewString(out + ")") }}, }, }
DequeClass is collections.deque: a double-ended queue.
var DifflibLibrary = object.NewLibrary(DifflibLibraryName, map[string]*object.Builtin{ "unified_diff": { Fn: func(ctx context.Context, kwargs object.Kwargs, args ...object.Object) object.Object { if err := errors.MinArgs(args, 2); err != nil { return err } aStr, e := args[0].AsString() if e != nil { return errors.NewTypeError("STRING", args[0].Type().String()) } bStr, e := args[1].AsString() if e != nil { return errors.NewTypeError("STRING", args[1].Type().String()) } fromFile := kwargs.MustGetString("fromfile", "") toFile := kwargs.MustGetString("tofile", "") n := int(kwargs.MustGetInt("n", 3)) return object.NewString(unifiedDiff(splitLines(aStr), splitLines(bStr), fromFile, toFile, n)) }, HelpText: `unified_diff(a, b, fromfile="", tofile="", n=3) - Return a unified format diff string`, }, "ratio": { Fn: func(ctx context.Context, kwargs object.Kwargs, args ...object.Object) object.Object { if err := errors.ExactArgs(args, 2); err != nil { return err } aStr, e := args[0].AsString() if e != nil { return errors.NewTypeError("STRING", args[0].Type().String()) } bStr, e := args[1].AsString() if e != nil { return errors.NewTypeError("STRING", args[1].Type().String()) } r := sequenceRatio(charSplit(aStr), charSplit(bStr)) return object.NewFloat(math.Round(r*100) / 100) }, HelpText: `ratio(a, b) - Return a similarity ratio between 0.0 and 1.0`, }, "opcodes": { Fn: func(ctx context.Context, kwargs object.Kwargs, args ...object.Object) object.Object { if err := errors.ExactArgs(args, 2); err != nil { return err } aStr, e := args[0].AsString() if e != nil { return errors.NewTypeError("STRING", args[0].Type().String()) } bStr, e := args[1].AsString() if e != nil { return errors.NewTypeError("STRING", args[1].Type().String()) } ops := computeOpcodes(splitLines(aStr), splitLines(bStr)) result := make([]object.Object, len(ops)) for i, op := range ops { result[i] = &object.Tuple{Elements: []object.Object{ object.NewString(string(op.tag)), object.NewInteger(int64(op.i1)), object.NewInteger(int64(op.i2)), object.NewInteger(int64(op.j1)), object.NewInteger(int64(op.j2)), }} } return &object.List{Elements: result} }, HelpText: `opcodes(a, b) - Return list of (tag, i1, i2, j1, j2) tuples describing how to turn a into b`, }, "get_close_matches": { Fn: func(ctx context.Context, kwargs object.Kwargs, args ...object.Object) object.Object { if err := errors.MinArgs(args, 2); err != nil { return err } word, e := args[0].AsString() if e != nil { return errors.NewTypeError("STRING", args[0].Type().String()) } possElems, e2 := args[1].AsList() if e2 != nil { return errors.NewTypeError("LIST", args[1].Type().String()) } n := int(kwargs.MustGetInt("n", 3)) cutoff := kwargs.MustGetFloat("cutoff", 0.6) if len(args) >= 3 { if v, err := args[2].AsInt(); err == nil { n = int(v) } } if len(args) >= 4 { if v, err := args[3].AsFloat(); err == nil { cutoff = v } } poss := make([]string, 0, len(possElems)) for _, p := range possElems { if s, err := p.AsString(); err == nil { poss = append(poss, s) } } matches := getCloseMatches(word, poss, n, cutoff) elems := make([]object.Object, len(matches)) for i, m := range matches { elems[i] = object.NewString(m) } return &object.List{Elements: elems} }, HelpText: `get_close_matches(word, possibilities, n=3, cutoff=0.6) - Return list of best matches from possibilities`, }, }, nil, "Helpers for computing deltas between sequences")
var FunctoolsLibrary = object.NewLibrary(FunctoolsLibraryName, map[string]*object.Builtin{ "reduce": { Fn: func(ctx context.Context, kwargs object.Kwargs, args ...object.Object) object.Object { if len(args) < 2 || len(args) > 3 { return errors.NewError("reduce() requires 2 or 3 arguments") } callable := args[0] switch callable.(type) { case *object.Function, *object.LambdaFunction, *object.Builtin, *object.BoundMethod, *object.Class: default: return errors.NewTypeError("callable", callable.Type().String()) } list, ok := args[1].(*object.List) if !ok { return errors.NewTypeError("LIST", args[1].Type().String()) } if len(list.Elements) == 0 { if len(args) == 3 { return args[2] } return errors.NewError("reduce() of empty sequence with no initial value") } var accumulator object.Object startIdx := 0 if len(args) == 3 { accumulator = args[2] } else { accumulator = list.Elements[0] startIdx = 1 } eval := evaliface.FromContext(ctx) if eval == nil { return errors.NewError("evaluator not available in context") } for i := startIdx; i < len(list.Elements); i++ { result := eval.CallObjectFunction(ctx, callable, []object.Object{accumulator, list.Elements[i]}, nil, nil) if result == nil { return errors.NewError("reduce function returned nil") } if object.IsError(result) || result.Type() == object.EXCEPTION_OBJ { return result } accumulator = result } return accumulator }, HelpText: `reduce(function, iterable[, initializer]) - Apply function cumulatively to items Parameters: function - Function taking 2 arguments (accumulator, item) iterable - List of items to reduce initializer - Optional starting value Returns: Reduced value Example: import functools def add(x, y): return x + y functools.reduce(add, [1, 2, 3, 4]) # 10 functools.reduce(add, [1, 2, 3], 10) # 16`, }, "partial": { Fn: func(ctx context.Context, kwargs object.Kwargs, args ...object.Object) object.Object { if len(args) < 1 { return errors.NewError("partial() requires at least 1 argument") } var fn *object.Function var builtin *object.Builtin if f, ok := args[0].(*object.Function); ok { fn = f } else if b, ok := args[0].(*object.Builtin); ok { builtin = b } else { return errors.NewTypeError("FUNCTION", args[0].Type().String()) } partialArgs := make([]object.Object, len(args)-1) copy(partialArgs, args[1:]) partialKwargs := make(map[string]object.Object) for k, v := range kwargs.Kwargs { partialKwargs[k] = v } return &object.Builtin{ Fn: func(ctx context.Context, kwargs object.Kwargs, args ...object.Object) object.Object { allArgs := make([]object.Object, len(partialArgs)+len(args)) copy(allArgs, partialArgs) copy(allArgs[len(partialArgs):], args) allKwargs := make(map[string]object.Object) for k, v := range partialKwargs { allKwargs[k] = v } for k, v := range kwargs.Kwargs { allKwargs[k] = v } if fn != nil { eval := evaliface.FromContext(ctx) if eval == nil { return errors.NewError("evaluator not available in context") } return eval.CallFunction(ctx, fn, allArgs, allKwargs) } return builtin.Fn(ctx, object.NewKwargs(allKwargs), allArgs...) }, HelpText: "Partial function application", } }, HelpText: `partial(func, *args, **kwargs) - Create a partial function application Parameters: func - Function to partially apply *args - Arguments to pre-fill **kwargs - Keyword arguments to pre-fill Returns: New function with pre-filled arguments Example: import functools def add(x, y): return x + y add_five = functools.partial(add, 5) add_five(3) # 8`, }, }, nil, "Higher-order functions and operations on callable objects")
var HMACClass = &object.Class{ Name: "HMAC", Methods: map[string]object.Object{ "update": &object.Builtin{ Fn: func(ctx context.Context, kwargs object.Kwargs, args ...object.Object) object.Object { if err := errors.ExactArgs(args, 2); err != nil { return err } inst, ok := args[0].(*object.Instance) if !ok { return errors.NewTypeError("HMAC", args[0].Type().String()) } state, ok := inst.NativeData.(*hmacState) if !ok { return errors.NewError("invalid hmac object") } b, errObj := toByteString(args[1]) if errObj != nil { return errObj } state.data = append(state.data, b...) return &object.Null{} }, HelpText: `update(data) - Feed data into the HMAC Appends data (a string treated as bytes, or a list of byte values) to the message being authenticated. Returns None.`, }, "digest": &object.Builtin{ Fn: func(ctx context.Context, kwargs object.Kwargs, args ...object.Object) object.Object { if err := errors.ExactArgs(args, 1); err != nil { return err } inst, ok := args[0].(*object.Instance) if !ok { return errors.NewTypeError("HMAC", args[0].Type().String()) } state, ok := inst.NativeData.(*hmacState) if !ok { return errors.NewError("invalid hmac object") } return object.NewBytes(computeHmacDigest(state)) }, HelpText: `digest() - Return the raw HMAC as a Bytes value Returns the MAC of the data passed to the hmac so far, as a Bytes object. Use hexdigest() for a lowercase-hex string representation.`, }, "hexdigest": &object.Builtin{ Fn: func(ctx context.Context, kwargs object.Kwargs, args ...object.Object) object.Object { if err := errors.ExactArgs(args, 1); err != nil { return err } inst, ok := args[0].(*object.Instance) if !ok { return errors.NewTypeError("HMAC", args[0].Type().String()) } state, ok := inst.NativeData.(*hmacState) if !ok { return errors.NewError("invalid hmac object") } return object.NewString(hex.EncodeToString(computeHmacDigest(state))) }, HelpText: `hexdigest() - Return the HMAC as a hexadecimal string Example: import hmac sig = hmac.new("secret", "payload", "sha256").hexdigest()`, }, "copy": &object.Builtin{ Fn: func(ctx context.Context, kwargs object.Kwargs, args ...object.Object) object.Object { if err := errors.ExactArgs(args, 1); err != nil { return err } inst, ok := args[0].(*object.Instance) if !ok { return errors.NewTypeError("HMAC", args[0].Type().String()) } state, ok := inst.NativeData.(*hmacState) if !ok { return errors.NewError("invalid hmac object") } newKey := make([]byte, len(state.key)) copy(newKey, state.key) newData := make([]byte, len(state.data)) copy(newData, state.data) return object.NewInstanceWithData(inst.Class, map[string]object.Object{ "name": object.NewString("hmac-" + state.alg), "digest_size": object.NewInteger(int64(hashDigestSize(state.alg))), "block_size": object.NewInteger(int64(hashBlockSize(state.alg))), }, &hmacState{key: newKey, data: newData, alg: state.alg}) }, HelpText: `copy() - Return a copy of the HMAC object`, }, }, }
HMACClass is the class exposed for objects returned by hmac.new().
var HTMLLibrary = object.NewLibrary(HTMLLibraryName, map[string]*object.Builtin{ "escape": { Fn: func(ctx context.Context, kwargs object.Kwargs, args ...object.Object) object.Object { if err := errors.RangeArgs(args, 1, 2); err != nil { return err } str, ok := args[0].(*object.String) if !ok { return errors.NewTypeError("STRING", args[0].Type().String()) } for _, k := range kwargs.Keys() { if k != "quote" { return newArgTypeError("escape() got an unexpected keyword argument '%s'", k) } } quote := true if len(args) == 2 { if kwargs.Has("quote") { return newArgTypeError("escape() got multiple values for argument 'quote'") } quote = isTruthy(args[1]) } else if q := kwargs.Get("quote"); q != nil { quote = isTruthy(q) } if quote { return object.NewString(htmlEscapeQuote.Replace(str.StringValue())) } return object.NewString(htmlEscapeNoQuote.Replace(str.StringValue())) }, HelpText: `escape(s, quote=True) - Escape HTML special characters Converts &, < and > to HTML-safe sequences. If quote is true (the default), also converts " to " and ' to '. Parameters: s - String to escape quote - Also escape quote characters (default True) Returns: Escaped string Example: import html safe = html.escape("<script>alert('xss')</script>") print(safe) # "<script>alert('xss')</script>"`, }, "unescape": { Fn: func(ctx context.Context, kwargs object.Kwargs, args ...object.Object) object.Object { if err := errors.ExactArgs(args, 1); err != nil { return err } str, ok := args[0].(*object.String) if !ok { return errors.NewTypeError("STRING", args[0].Type().String()) } result := html.UnescapeString(str.StringValue()) result = unescapeNumericEntities(result) return object.NewString(result) }, HelpText: `unescape(s) - Unescape HTML entities Converts HTML entities back to their corresponding characters. Handles named entities (<, >, &, ", ') and numeric entities (<, <). Parameters: s - String with HTML entities to unescape Returns: Unescaped string Example: import html text = html.unescape("<script>") print(text) # "<script>"`, }, }, nil, "HTML escaping and unescaping library")
var HashClass = &object.Class{ Name: "Hash", Methods: map[string]object.Object{ "update": &object.Builtin{ Fn: func(ctx context.Context, kwargs object.Kwargs, args ...object.Object) object.Object { if err := errors.ExactArgs(args, 2); err != nil { return err } inst, ok := args[0].(*object.Instance) if !ok { return errors.NewTypeError("Hash", args[0].Type().String()) } state, ok := inst.NativeData.(*hashState) if !ok { return errors.NewError("invalid hash object") } b, errObj := toByteString(args[1]) if errObj != nil { return errObj } state.data = append(state.data, b...) return &object.Null{} }, HelpText: `update(data) - Feed data into the hash Appends the given data (a string treated as bytes, or a list of byte values) to the data already fed into the hash. Returns None. Example: import hashlib h = hashlib.sha256() h.update("foo") h.update("bar") print(h.hexdigest()) # hash of "foobar"`, }, "digest": &object.Builtin{ Fn: func(ctx context.Context, kwargs object.Kwargs, args ...object.Object) object.Object { if err := errors.ExactArgs(args, 1); err != nil { return err } inst, ok := args[0].(*object.Instance) if !ok { return errors.NewTypeError("Hash", args[0].Type().String()) } state, ok := inst.NativeData.(*hashState) if !ok { return errors.NewError("invalid hash object") } h := hashConstructor(state.alg)() h.Write(state.data) return object.NewBytes(h.Sum(nil)) }, HelpText: `digest() - Return the raw hash as a Bytes value Returns the digest of the data passed to the hash so far, as a Bytes object. Use hexdigest() for a lowercase-hex string representation.`, }, "hexdigest": &object.Builtin{ Fn: func(ctx context.Context, kwargs object.Kwargs, args ...object.Object) object.Object { if err := errors.ExactArgs(args, 1); err != nil { return err } inst, ok := args[0].(*object.Instance) if !ok { return errors.NewTypeError("Hash", args[0].Type().String()) } state, ok := inst.NativeData.(*hashState) if !ok { return errors.NewError("invalid hash object") } h := hashConstructor(state.alg)() h.Write(state.data) return object.NewString(hex.EncodeToString(h.Sum(nil))) }, HelpText: `hexdigest() - Return the hash as a hexadecimal string Returns the digest of the data passed to the hash so far, encoded as lowercase hexadecimal. Example: import hashlib print(hashlib.sha256("hello").hexdigest()) # "2cf24dba5fb0a30e26e83b2ac5b9e29e1b161e5c1fa7425e73043362938b9824"`, }, "copy": &object.Builtin{ Fn: func(ctx context.Context, kwargs object.Kwargs, args ...object.Object) object.Object { if err := errors.ExactArgs(args, 1); err != nil { return err } inst, ok := args[0].(*object.Instance) if !ok { return errors.NewTypeError("Hash", args[0].Type().String()) } state, ok := inst.NativeData.(*hashState) if !ok { return errors.NewError("invalid hash object") } newData := make([]byte, len(state.data)) copy(newData, state.data) return object.NewInstanceWithData(inst.Class, map[string]object.Object{ "name": object.NewString(state.alg), "digest_size": object.NewInteger(int64(hashDigestSize(state.alg))), "block_size": object.NewInteger(int64(hashBlockSize(state.alg))), }, &hashState{data: newData, alg: state.alg}) }, HelpText: `copy() - Return a copy of the hash object Returns a new hash object with the same algorithm and accumulated data. Updates to one object do not affect the other.`, }, }, }
HashClass is the class exposed for objects returned by hashlib constructors.
var HashlibLibrary = object.NewLibrary(HashlibLibraryName, map[string]*object.Builtin{ "sha256": HashlibSHA256Builtin, "sha1": HashlibSHA1Builtin, "md5": HashlibMD5Builtin, }, nil, "Cryptographic hash functions library")
var HmacLibrary = object.NewLibrary(HmacLibraryName, map[string]*object.Builtin{ "new": { Fn: func(ctx context.Context, kwargs object.Kwargs, args ...object.Object) object.Object { if err := errors.RangeArgs(args, 1, 3); err != nil { return err } key, errObj := toByteString(args[0]) if errObj != nil { return errObj } var data []byte if len(args) >= 2 { if _, isNull := args[1].(*object.Null); !isNull { b, e := toByteString(args[1]) if e != nil { return e } data = b } } var digestmod object.Object if len(args) >= 3 { digestmod = args[2] } alg, e := resolveHmacAlg(digestmod) if e != nil { return e } return newHmacInstance(alg, key, data) }, HelpText: `new(key, msg=None, digestmod=None) - Create an HMAC object key and msg are byte buffers (a string treated as bytes, or a list of byte values such as returned by str.encode()). digestmod may be a string name ("sha256", "sha1", "md5"), a hashlib constructor (e.g. hashlib.sha256), or omitted (defaults to sha256). Call .hexdigest() or .digest() for the result. Example: import hmac, hashlib sig = "sha256=" + hmac.new(secret.encode(), body, hashlib.sha256).hexdigest() ok = hmac.compare_digest(sig, signature)`, }, "digest": { Fn: func(ctx context.Context, kwargs object.Kwargs, args ...object.Object) object.Object { if err := errors.ExactArgs(args, 3); err != nil { return err } key, errObj := toByteString(args[0]) if errObj != nil { return errObj } msg, errObj := toByteString(args[1]) if errObj != nil { return errObj } alg, e := resolveHmacAlg(args[2]) if e != nil { return e } return object.NewBytes(computeHmacDigest(&hmacState{key: key, data: msg, alg: alg})) }, HelpText: `digest(key, msg, digestmod) - One-shot HMAC as a raw byte string`, }, "compare_digest": { Fn: func(ctx context.Context, kwargs object.Kwargs, args ...object.Object) object.Object { if err := errors.ExactArgs(args, 2); err != nil { return err } a, okA := args[0].(*object.String) b, okB := args[1].(*object.String) if !okA || !okB { return errors.NewError("compare_digest() requires two string arguments") } return object.NewBoolean(CompareDigest(a.StringValue(), b.StringValue())) }, HelpText: `compare_digest(a, b) - Constant-time string comparison Compares two strings in constant time to help prevent timing attacks. Returns True if equal, False otherwise. Use this to compare signature values. Example: import hmac ok = hmac.compare_digest(expected_signature, received_signature)`, }, }, nil, "HMAC (Keyed-Hashing for Message Authentication) library")
HmacLibrary provides HMAC (Keyed-Hashing for Message Authentication).
var IOLibrary = object.NewLibrary(IOLibraryName, map[string]*object.Builtin{ "StringIO": { Fn: stringIONew, HelpText: `StringIO([initial_value=""]) - In-memory string buffer Creates a StringIO object that behaves like a file opened for text I/O. Optional initial_value pre-fills the buffer. Methods: write(s), getvalue(), read([n]), readline(), seek(pos), tell(), truncate([pos]), close() Supports the with statement: with io.StringIO() as buf: ...`, }, }, nil, "In-memory I/O streams")
var ItertoolsLibrary = object.NewLibrary(ItertoolsLibraryName, map[string]*object.Builtin{ "chain": { Fn: func(ctx context.Context, kwargs object.Kwargs, args ...object.Object) object.Object { result := []object.Object{} for _, arg := range args { switch a := arg.(type) { case *object.List: result = append(result, a.Elements...) case *object.Tuple: result = append(result, a.Elements...) case *object.String: for _, ch := range a.StringValue() { result = append(result, object.NewString(string(ch))) } default: elems, errObj := collectIterable(ctx, arg) if errObj != nil { return errObj } result = append(result, elems...) } } return &object.List{Elements: result} }, HelpText: `chain(*iterables) - Chain multiple iterables together Returns a list with elements from all iterables concatenated. Example: itertools.chain([1, 2], [3, 4]) -> [1, 2, 3, 4] itertools.chain("ab", "cd") -> ["a", "b", "c", "d"]`, }, "repeat": { Fn: func(ctx context.Context, kwargs object.Kwargs, args ...object.Object) object.Object { if err := errors.RangeArgs(args, 1, 2); err != nil { return err } if err := checkKwargs("repeat", kwargs, "times"); err != nil { return err } elem := args[0] timesObj, terr := optionalArg("repeat", args, kwargs, 1, "times") if terr != nil { return terr } if timesObj == nil { return object.NewInfiniteIterator(func() (object.Object, bool) { return elem, true }) } n, ok := timesObj.(*object.Integer) if !ok { return errors.NewTypeError("INTEGER", timesObj.Type().String()) } left := n.IntValue() return object.NewIterator(func() (object.Object, bool) { if left <= 0 { return nil, false } left-- return elem, true }) }, HelpText: `repeat(elem[, times]) - Repeat element times times Returns an iterator giving elem times times, or forever when times is omitted, like Python's itertools.repeat. Use list() for a list, or consume an endless one with next(), zip() or itertools.islice. Example: list(itertools.repeat("x", 3)) -> ["x", "x", "x"] list(zip(range(3), itertools.repeat(0))) -> [(0, 0), (1, 0), (2, 0)]`, }, "cycle": { Fn: func(ctx context.Context, kwargs object.Kwargs, args ...object.Object) object.Object { if err := errors.RangeArgs(args, 1, 2); err != nil { return err } if src, isIter := args[0].(*object.Iterator); isIter && len(args) == 1 { first, any := src.Next() if !any { return object.NewIterator(func() (object.Object, bool) { return nil, false }) } pending := true var saved []object.Object exhausted := false i := 0 return object.NewInfiniteIterator(func() (object.Object, bool) { if pending { pending = false if !object.IsPropagating(first) { saved = append(saved, first) } return first, true } if !exhausted { if v, ok := src.Next(); ok { if !object.IsError(v) && v.Type() != object.EXCEPTION_OBJ { saved = append(saved, v) } return v, true } exhausted = true } if len(saved) == 0 { return nil, false } elem := saved[i%len(saved)] i++ return elem, true }) } var elements []object.Object switch a := args[0].(type) { case *object.List: elements = a.Elements case *object.Tuple: elements = a.Elements case *object.String: for _, ch := range a.StringValue() { elements = append(elements, object.NewString(string(ch))) } default: elems, errObj := collectIterable(ctx, args[0]) if errObj != nil { return errObj } elements = elems } if len(args) == 2 { n, ok := args[1].(*object.Integer) if !ok { return errors.NewTypeError("INTEGER", args[1].Type().String()) } if len(elements) == 0 || n.IntValue() <= 0 { return &object.List{Elements: []object.Object{}} } result := make([]object.Object, 0, len(elements)*int(n.IntValue())) for i := int64(0); i < n.IntValue(); i++ { result = append(result, elements...) } return &object.List{Elements: result} } if len(elements) == 0 { return object.NewIterator(func() (object.Object, bool) { return nil, false }) } i := 0 return object.NewInfiniteIterator(func() (object.Object, bool) { elem := elements[i%len(elements)] i++ return elem, true }) }, HelpText: `cycle(iterable) - Cycle through iterable endlessly Returns an infinite iterator, like Python's itertools.cycle; consume it with next() or itertools.islice. The legacy form cycle(iterable, n) returns the materialized list of n repetitions.`, }, "count": { Fn: func(ctx context.Context, kwargs object.Kwargs, args ...object.Object) object.Object { { if len(args) > 2 { return &object.Exception{ Message: fmt.Sprintf("count() takes at most 2 arguments (%d given)", len(args)), ExceptionType: object.ExceptionTypeTypeError, Raised: true, } } if err := checkKwargs("count", kwargs, "start", "step"); err != nil { return err } startObj, serr := optionalArg("count", args, kwargs, 0, "start") if serr != nil { return serr } stepObj, terr := optionalArg("count", args, kwargs, 1, "step") if terr != nil { return terr } if _, isNull := stepObj.(*object.Null); isNull { stepObj = nil } if startObj == nil { startObj = object.NewInteger(0) } if stepObj == nil { stepObj = object.NewInteger(1) } for _, o := range []object.Object{startObj, stepObj} { switch o.(type) { case *object.Integer, *object.Float: default: return errors.NewTypeError("a number is required", o.Type().String()) } } si, sInt := startObj.(*object.Integer) ti, tInt := stepObj.(*object.Integer) if sInt && tInt { cur, step := si.IntValue(), ti.IntValue() return object.NewInfiniteIterator(func() (object.Object, bool) { v := object.NewInteger(cur) cur += step return v, true }) } curF, _ := startObj.AsFloat() stepF, _ := stepObj.AsFloat() return object.NewInfiniteIterator(func() (object.Object, bool) { v := object.NewFloat(curF) curF += stepF return v, true }) } }, HelpText: `count(start=0, step=1) - Count up from start by step, forever Returns an infinite iterator like Python's itertools.count; consume it with next(), zip(), enumerate() or itertools.islice.`, }, "islice": { Fn: func(ctx context.Context, kwargs object.Kwargs, args ...object.Object) object.Object { if err := errors.RangeArgs(args, 2, 4); err != nil { return err } start, stop, step, errObj := parseIsliceBounds(args) if errObj != nil { return errObj } if iter, isIter := args[0].(*object.Iterator); isIter { pos := int64(0) done := false next := func() (object.Object, bool) { for !done { if stop >= 0 && pos >= stop { break } val, ok := iter.Next() if !ok { break } if object.IsPropagating(val) { done = true return val, true } p := pos pos++ if p >= start && (p-start)%step == 0 { return val, true } } done = true return nil, false } if stop < 0 && iter.Infinite() { return object.NewInfiniteIterator(next) } return object.NewIterator(next) } elements, errObj := collectIterable(ctx, args[0]) if errObj != nil { return errObj } if stop < 0 || stop > int64(len(elements)) { stop = int64(len(elements)) } result := []object.Object{} for i := start; i < stop; i += step { result = append(result, elements[i]) } return &object.List{Elements: result} }, HelpText: `islice(iterable, stop) or islice(iterable, start, stop[, step]) - Slice an iterable Returns a list with elements from the iterable sliced by indices. Example: itertools.islice([0, 1, 2, 3, 4], 3) -> [0, 1, 2] itertools.islice([0, 1, 2, 3, 4], 1, 4) -> [1, 2, 3] itertools.islice([0, 1, 2, 3, 4], 0, 5, 2) -> [0, 2, 4]`, }, "takewhile": { Fn: func(ctx context.Context, kwargs object.Kwargs, args ...object.Object) object.Object { if err := errors.ExactArgs(args, 2); err != nil { return err } pred := args[0] if !isCallable(pred) { return errors.NewTypeError("callable", pred.Type().String()) } next, ok := object.IterSource(args[1]) if !ok { return notIterableError(args[1]) } result := []object.Object{} for { if err := ctx.Err(); err != nil { return errors.NewError("%s", err.Error()) } elem, more := next() if !more { break } if object.IsPropagating(elem) { return elem } res := callCallable(ctx, pred, elem) if object.IsError(res) || res.Type() == object.EXCEPTION_OBJ { return res } if !isTruthy(res) { break } result = append(result, elem) } return &object.List{Elements: result} }, HelpText: `takewhile(predicate, iterable) - Take elements while predicate is true Returns a list with elements from the start of iterable as long as predicate is true. Example: itertools.takewhile(lambda x: x < 5, [1, 3, 5, 2, 4]) -> [1, 3]`, }, "dropwhile": { Fn: func(ctx context.Context, kwargs object.Kwargs, args ...object.Object) object.Object { if err := errors.ExactArgs(args, 2); err != nil { return err } pred := args[0] if !isCallable(pred) { return errors.NewTypeError("callable", pred.Type().String()) } var elements []object.Object switch a := args[1].(type) { case *object.List: elements = a.Elements case *object.Tuple: elements = a.Elements default: elems, errObj := collectIterable(ctx, args[1]) if errObj != nil { return errObj } elements = elems } result := []object.Object{} dropping := true for _, elem := range elements { if dropping { res := callCallable(ctx, pred, elem) if object.IsError(res) || res.Type() == object.EXCEPTION_OBJ { return res } if isTruthy(res) { continue } dropping = false } result = append(result, elem) } return &object.List{Elements: result} }, HelpText: `dropwhile(predicate, iterable) - Drop elements while predicate is true Returns a list with elements after the predicate becomes false. Example: itertools.dropwhile(lambda x: x < 5, [1, 3, 5, 2, 4]) -> [5, 2, 4]`, }, "zip_longest": { Fn: func(ctx context.Context, kwargs object.Kwargs, args ...object.Object) object.Object { if len(args) < 1 { return &object.List{Elements: []object.Object{}} } fillvalue := object.Object(&object.Null{}) if kwargs.Len() > 0 { if kwargs.Has("fillvalue") { fillvalue = kwargs.Get("fillvalue") } } iterables := make([][]object.Object, len(args)) maxLen := 0 for i, arg := range args { switch a := arg.(type) { case *object.List: iterables[i] = a.Elements case *object.Tuple: iterables[i] = a.Elements case *object.String: chars := []object.Object{} for _, ch := range a.StringValue() { chars = append(chars, object.NewString(string(ch))) } iterables[i] = chars default: elems, errObj := collectIterable(ctx, arg) if errObj != nil { return errObj } iterables[i] = elems } if len(iterables[i]) > maxLen { maxLen = len(iterables[i]) } } result := []object.Object{} for j := 0; j < maxLen; j++ { tuple := make([]object.Object, len(iterables)) for i, iter := range iterables { if j < len(iter) { tuple[i] = iter[j] } else { tuple[i] = fillvalue } } result = append(result, &object.Tuple{Elements: tuple}) } return &object.List{Elements: result} }, HelpText: `zip_longest(*iterables, fillvalue=None) - Zip iterables, filling shorter ones Zips iterables together, using fillvalue for missing values in shorter iterables. Example: itertools.zip_longest([1, 2, 3], ["a", "b"]) -> [(1, "a"), (2, "b"), (3, None)] itertools.zip_longest([1, 2], ["a"], fillvalue="-") -> [(1, "a"), (2, "-")]`, }, "product": { Fn: func(ctx context.Context, kwargs object.Kwargs, args ...object.Object) object.Object { if len(args) < 1 { return &object.List{Elements: []object.Object{&object.Tuple{Elements: []object.Object{}}}} } repeatN := int64(1) if v := kwargs.Get("repeat"); v != nil { if n, err := v.AsInt(); err == nil { repeatN = n } else { return errors.ParameterError("repeat", err) } } collected := make([][]object.Object, 0, len(args)) for _, arg := range args { switch a := arg.(type) { case *object.List: collected = append(collected, a.Elements) case *object.Tuple: collected = append(collected, a.Elements) case *object.String: chars := []object.Object{} for _, ch := range a.StringValue() { chars = append(chars, object.NewString(string(ch))) } collected = append(collected, chars) default: elems, errObj := collectIterable(ctx, arg) if errObj != nil { return errObj } collected = append(collected, elems) } } iterables := make([][]object.Object, 0, int(repeatN)*len(collected)) for rep := int64(0); rep < repeatN; rep++ { iterables = append(iterables, collected...) } for _, iter := range iterables { if len(iter) == 0 { return &object.List{Elements: []object.Object{}} } } result := []object.Object{} indices := make([]int, len(iterables)) for { tuple := make([]object.Object, len(iterables)) for i, idx := range indices { tuple[i] = iterables[i][idx] } result = append(result, &object.Tuple{Elements: tuple}) carry := true for i := len(indices) - 1; i >= 0 && carry; i-- { indices[i]++ if indices[i] >= len(iterables[i]) { indices[i] = 0 } else { carry = false } } if carry { break } } return &object.List{Elements: result} }, HelpText: `product(*iterables) - Cartesian product of iterables Returns all possible combinations (tuples) of elements from input iterables. Example: itertools.product([1, 2], ["a", "b"]) -> [(1, "a"), (1, "b"), (2, "a"), (2, "b")] itertools.product([1, 2], [3, 4]) -> [(1, 3), (1, 4), (2, 3), (2, 4)]`, }, "permutations": { Fn: func(ctx context.Context, kwargs object.Kwargs, args ...object.Object) object.Object { args, kerr := kwargsToPositional("permutations", args, kwargs, "iterable", "r") if kerr != nil { return kerr } if err := errors.RangeArgs(args, 1, 2); err != nil { return err } var elements []object.Object switch a := args[0].(type) { case *object.List: elements = a.Elements case *object.Tuple: elements = a.Elements case *object.String: for _, ch := range a.StringValue() { elements = append(elements, object.NewString(string(ch))) } default: elems, errObj := collectIterable(ctx, args[0]) if errObj != nil { return errObj } elements = elems } r := len(elements) if len(args) == 2 && args[1].Type() != object.NULL_OBJ { if rArg, ok := args[1].(*object.Integer); ok { r = int(rArg.IntValue()) } else { return errors.NewTypeError("INTEGER", args[1].Type().String()) } } if r < 0 { return errors.NewValueError("r must be non-negative") } if r > len(elements) { return &object.List{Elements: []object.Object{}} } result := []object.Object{} generatePermutations(elements, r, []object.Object{}, make([]bool, len(elements)), &result) return &object.List{Elements: result} }, HelpText: `permutations(iterable[, r]) - Generate permutations Returns all r-length permutations of elements from iterable. If r is not specified, defaults to length of iterable (full permutations). Example: itertools.permutations([1, 2, 3], 2) -> [(1, 2), (1, 3), (2, 1), (2, 3), (3, 1), (3, 2)] itertools.permutations("ab") -> [("a", "b"), ("b", "a")]`, }, "combinations": { Fn: func(ctx context.Context, kwargs object.Kwargs, args ...object.Object) object.Object { args, kerr := kwargsToPositional("combinations", args, kwargs, "iterable", "r") if kerr != nil { return kerr } if err := errors.ExactArgs(args, 2); err != nil { return err } var elements []object.Object switch a := args[0].(type) { case *object.List: elements = a.Elements case *object.Tuple: elements = a.Elements case *object.String: for _, ch := range a.StringValue() { elements = append(elements, object.NewString(string(ch))) } default: elems, errObj := collectIterable(ctx, args[0]) if errObj != nil { return errObj } elements = elems } rArg, ok := args[1].(*object.Integer) if !ok { return errors.NewTypeError("INTEGER", args[1].Type().String()) } r := int(rArg.IntValue()) if r < 0 || r > len(elements) { return &object.List{Elements: []object.Object{}} } result := []object.Object{} generateCombinations(elements, r, 0, []object.Object{}, &result) return &object.List{Elements: result} }, HelpText: `combinations(iterable, r) - Generate combinations Returns all r-length combinations of elements from iterable (without repetition). Example: itertools.combinations([1, 2, 3], 2) -> [(1, 2), (1, 3), (2, 3)] itertools.combinations("abc", 2) -> [("a", "b"), ("a", "c"), ("b", "c")]`, }, "combinations_with_replacement": { Fn: func(ctx context.Context, kwargs object.Kwargs, args ...object.Object) object.Object { args, kerr := kwargsToPositional("combinations_with_replacement", args, kwargs, "iterable", "r") if kerr != nil { return kerr } if err := errors.ExactArgs(args, 2); err != nil { return err } var elements []object.Object switch a := args[0].(type) { case *object.List: elements = a.Elements case *object.Tuple: elements = a.Elements case *object.String: for _, ch := range a.StringValue() { elements = append(elements, object.NewString(string(ch))) } default: elems, errObj := collectIterable(ctx, args[0]) if errObj != nil { return errObj } elements = elems } rArg, ok := args[1].(*object.Integer) if !ok { return errors.NewTypeError("INTEGER", args[1].Type().String()) } r := int(rArg.IntValue()) if r < 0 { return &object.List{Elements: []object.Object{}} } if len(elements) == 0 && r > 0 { return &object.List{Elements: []object.Object{}} } result := []object.Object{} generateCombinationsWithReplacement(elements, r, 0, []object.Object{}, &result) return &object.List{Elements: result} }, HelpText: `combinations_with_replacement(iterable, r) - Generate combinations with replacement Returns all r-length combinations of elements from iterable (with repetition allowed). Example: itertools.combinations_with_replacement([1, 2], 2) -> [(1, 1), (1, 2), (2, 2)] itertools.combinations_with_replacement("ab", 2) -> [("a", "a"), ("a", "b"), ("b", "b")]`, }, "groupby": { Fn: func(ctx context.Context, kwargs object.Kwargs, args ...object.Object) object.Object { if err := errors.RangeArgs(args, 1, 2); err != nil { return err } var elements []object.Object switch a := args[0].(type) { case *object.List: elements = a.Elements case *object.Tuple: elements = a.Elements default: elems, errObj := collectIterable(ctx, args[0]) if errObj != nil { return errObj } elements = elems } if err := checkKwargs("groupby", kwargs, "key"); err != nil { return err } keyFunc, kerr := optionalArg("groupby", args, kwargs, 1, "key") if kerr != nil { return kerr } if keyFunc != nil && !isCallable(keyFunc) { return errors.NewTypeError("callable", keyFunc.Type().String()) } if len(elements) == 0 { return &object.List{Elements: []object.Object{}} } result := []object.Object{} var currentKey object.Object var currentGroup []object.Object for i, elem := range elements { var key object.Object if keyFunc != nil { key = callCallable(ctx, keyFunc, elem) if object.IsError(key) || key.Type() == object.EXCEPTION_OBJ { return key } } else { key = elem } if i == 0 { currentKey = key currentGroup = []object.Object{elem} } else if objectsEqual(key, currentKey) { currentGroup = append(currentGroup, elem) } else { result = append(result, &object.Tuple{Elements: []object.Object{ currentKey, &object.List{Elements: currentGroup}, }}) currentKey = key currentGroup = []object.Object{elem} } } if len(currentGroup) > 0 { result = append(result, &object.Tuple{Elements: []object.Object{ currentKey, &object.List{Elements: currentGroup}, }}) } return &object.List{Elements: result} }, HelpText: `groupby(iterable, key=None) - Group consecutive elements Groups consecutive elements that have the same key value. Returns list of (key, group) tuples where group is a list. Example: itertools.groupby([1, 1, 2, 2, 3]) -> [(1, [1, 1]), (2, [2, 2]), (3, [3])] itertools.groupby(["aa", "ab", "ba"], lambda x: x[0]) -> [("a", ["aa", "ab"]), ("b", ["ba"])]`, }, "accumulate": { Fn: func(ctx context.Context, kwargs object.Kwargs, args ...object.Object) object.Object { if err := errors.RangeArgs(args, 1, 2); err != nil { return err } var elements []object.Object switch a := args[0].(type) { case *object.List: elements = a.Elements case *object.Tuple: elements = a.Elements default: elems, errObj := collectIterable(ctx, args[0]) if errObj != nil { return errObj } elements = elems } if err := checkKwargs("accumulate", kwargs, "func", "initial"); err != nil { return err } accumFunc, ferr := optionalArg("accumulate", args, kwargs, 1, "func") if ferr != nil { return ferr } if accumFunc != nil && !isCallable(accumFunc) { return errors.NewTypeError("callable", accumFunc.Type().String()) } if initial, _ := optionalArg("accumulate", nil, kwargs, 0, "initial"); initial != nil { elements = append([]object.Object{initial}, elements...) } if len(elements) == 0 { return &object.List{Elements: []object.Object{}} } result := []object.Object{elements[0]} accumulator := elements[0] for i := 1; i < len(elements); i++ { if accumFunc != nil { accumulator = callCallable(ctx, accumFunc, accumulator, elements[i]) if object.IsError(accumulator) || accumulator.Type() == object.EXCEPTION_OBJ { return accumulator } } else { accumulator = addObjects(accumulator, elements[i]) if object.IsError(accumulator) { return accumulator } } result = append(result, accumulator) } return &object.List{Elements: result} }, HelpText: `accumulate(iterable[, func, *, initial=None]) - Running totals/accumulation Returns list of accumulated values. Default is sum, but can provide custom function. If initial is given, it is emitted first and seeds the accumulation. Example: itertools.accumulate([1, 2, 3, 4]) -> [1, 3, 6, 10] itertools.accumulate([1, 2, 3], operator.mul) -> [1, 2, 6]`, }, "filterfalse": { Fn: func(ctx context.Context, kwargs object.Kwargs, args ...object.Object) object.Object { if err := errors.ExactArgs(args, 2); err != nil { return err } pred := args[0] if !isCallable(pred) { return errors.NewTypeError("callable", pred.Type().String()) } var elements []object.Object switch a := args[1].(type) { case *object.List: elements = a.Elements case *object.Tuple: elements = a.Elements default: elems, errObj := collectIterable(ctx, args[1]) if errObj != nil { return errObj } elements = elems } result := []object.Object{} for _, elem := range elements { res := callCallable(ctx, pred, elem) if object.IsError(res) || res.Type() == object.EXCEPTION_OBJ { return res } if !isTruthy(res) { result = append(result, elem) } } return &object.List{Elements: result} }, HelpText: `filterfalse(predicate, iterable) - Filter elements where predicate is false Returns elements for which the predicate returns false. Example: itertools.filterfalse(lambda x: x % 2, [1, 2, 3, 4]) -> [2, 4]`, }, "starmap": { Fn: func(ctx context.Context, kwargs object.Kwargs, args ...object.Object) object.Object { if err := errors.ExactArgs(args, 2); err != nil { return err } fn := args[0] if !isCallable(fn) { return errors.NewTypeError("callable", fn.Type().String()) } var elements []object.Object switch a := args[1].(type) { case *object.List: elements = a.Elements case *object.Tuple: elements = a.Elements default: elems, errObj := collectIterable(ctx, args[1]) if errObj != nil { return errObj } elements = elems } result := []object.Object{} for _, elem := range elements { var fnArgs []object.Object switch e := elem.(type) { case *object.List: fnArgs = e.Elements case *object.Tuple: fnArgs = e.Elements default: return errors.NewError("starmap() iterable must contain sequences") } res := callCallable(ctx, fn, fnArgs...) if object.IsError(res) || res.Type() == object.EXCEPTION_OBJ { return res } result = append(result, res) } return &object.List{Elements: result} }, HelpText: `starmap(func, iterable) - Apply function to argument tuples Applies function using elements of each tuple as arguments. Example: itertools.starmap(pow, [(2, 3), (3, 2)]) -> [8, 9]`, }, "compress": { Fn: func(ctx context.Context, kwargs object.Kwargs, args ...object.Object) object.Object { if err := errors.ExactArgs(args, 2); err != nil { return err } data, dataOK := object.IterSource(args[0]) if !dataOK { return notIterableError(args[0]) } selectors, selOK := object.IterSource(args[1]) if !selOK { return notIterableError(args[1]) } if isEndless(args[0]) && isEndless(args[1]) { return errors.NewError("%s", object.InfiniteIteratorMessage) } result := []object.Object{} for { if err := ctx.Err(); err != nil { return errors.NewError("%s", err.Error()) } d, ok := data() if !ok { break } if object.IsPropagating(d) { return d } sel, ok := selectors() if !ok { break } if object.IsPropagating(sel) { return sel } if isTruthy(sel) { result = append(result, d) } } return &object.List{Elements: result} }, HelpText: `compress(data, selectors) - Filter data based on selectors Returns elements from data where corresponding selector is truthy. Example: itertools.compress([1, 2, 3, 4], [True, False, True, False]) -> [1, 3] itertools.compress("abcd", [1, 0, 1, 0]) -> ["a", "c"]`, }, "pairwise": { Fn: func(ctx context.Context, kwargs object.Kwargs, args ...object.Object) object.Object { if err := errors.ExactArgs(args, 1); err != nil { return err } var elements []object.Object switch a := args[0].(type) { case *object.List: elements = a.Elements case *object.Tuple: elements = a.Elements case *object.String: for _, ch := range a.StringValue() { elements = append(elements, object.NewString(string(ch))) } default: elems, errObj := collectIterable(ctx, args[0]) if errObj != nil { return errObj } elements = elems } if len(elements) < 2 { return &object.List{Elements: []object.Object{}} } result := make([]object.Object, len(elements)-1) for i := 0; i < len(elements)-1; i++ { result[i] = &object.Tuple{Elements: []object.Object{elements[i], elements[i+1]}} } return &object.List{Elements: result} }, HelpText: `pairwise(iterable) - Return successive overlapping pairs Returns consecutive pairs from the iterable. Example: itertools.pairwise([1, 2, 3, 4]) -> [(1, 2), (2, 3), (3, 4)] itertools.pairwise("abc") -> [("a", "b"), ("b", "c")]`, }, "batched": { Fn: func(ctx context.Context, kwargs object.Kwargs, args ...object.Object) object.Object { args, kerr := kwargsToPositional("batched", args, kwargs, "iterable", "n") if kerr != nil { return kerr } if err := errors.ExactArgs(args, 2); err != nil { return err } var elements []object.Object switch a := args[0].(type) { case *object.List: elements = a.Elements case *object.Tuple: elements = a.Elements case *object.String: for _, ch := range a.StringValue() { elements = append(elements, object.NewString(string(ch))) } default: elems, errObj := collectIterable(ctx, args[0]) if errObj != nil { return errObj } elements = elems } n, ok := args[1].(*object.Integer) if !ok { return errors.NewTypeError("INTEGER", args[1].Type().String()) } if n.IntValue() <= 0 { return errors.NewValueError("n must be at least one") } batchSize := int(n.IntValue()) result := []object.Object{} for i := 0; i < len(elements); i += batchSize { end := i + batchSize if end > len(elements) { end = len(elements) } batch := make([]object.Object, end-i) copy(batch, elements[i:end]) result = append(result, &object.Tuple{Elements: batch}) } return &object.List{Elements: result} }, HelpText: `batched(iterable, n) - Batch elements into tuples of size n Groups elements into batches of n elements each. Example: itertools.batched([1, 2, 3, 4, 5], 2) -> [(1, 2), (3, 4), (5,)] itertools.batched("abcdef", 3) -> [("a", "b", "c"), ("d", "e", "f")]`, }, }, nil, "Python-compatible itertools library for iteration utilities")
ItertoolsLibrary provides Python-like itertools functions
var JSONLibrary = object.NewLibrary(JSONLibraryName, map[string]*object.Builtin{ "loads": { Fn: jsonLoads, HelpText: `loads(json_string) - Parse JSON string Parses a JSON string and returns the corresponding Scriptling object.`, }, "dumps": { Fn: jsonDumps, HelpText: `dumps(obj, indent="") - Serialize object to JSON string Converts a Scriptling object to its JSON string representation. Optional indent parameter for pretty-printing: a string used verbatim, or a number of spaces (indent=2 is the common idiom; indent=0 newline-separates with no spaces). Object keys are always emitted in sorted order.`, }, "parse": { Fn: jsonLoads, HelpText: `parse(json_string) - Parse JSON string (alias for loads) Parses a JSON string and returns the corresponding Scriptling object.`, }, "stringify": { Fn: jsonDumps, HelpText: `stringify(obj, indent="") - Serialize object to JSON string (alias for dumps) Converts a Scriptling object to its JSON string representation. Optional indent parameter for pretty-printing.`, }, }, nil, "JSON encoding and decoding library")
var MatchClass = &object.Class{ Name: "Match", Methods: map[string]object.Object{ "group": &object.Builtin{ Fn: func(ctx context.Context, kwargs object.Kwargs, args ...object.Object) object.Object { if len(args) > 2 { return errors.NewError("group() takes at most 1 argument (%d given)", len(args)) } match := args[0].(*object.Instance) groups := match.Field("_groups").(*object.List).Elements names := match.Field("_group_names").(*object.List).Elements groupNum := 0 if len(args) == 2 { if args[1].Type() == object.STRING_OBJ { name, _ := args[1].AsString() for i, n := range names { if ns, ok := n.(*object.String); ok && ns.StringValue() == name { return groups[i] } } return &object.Exception{Message: "no such group", ExceptionType: object.ExceptionTypeIndexError, Raised: true} } if args[1].Type() != object.INTEGER_OBJ { return errors.NewTypeError("INTEGER or STRING", args[1].Type().String()) } val, _ := args[1].AsInt() groupNum = int(val) } if groupNum < 0 || groupNum >= len(groups) { return &object.Exception{Message: "no such group", ExceptionType: object.ExceptionTypeIndexError, Raised: true} } return groups[groupNum] }, HelpText: `group([n]) - Return the nth matched group (0 = full match), or the group captured under a (?P<name>...) name`, }, "groupdict": &object.Builtin{ Fn: func(ctx context.Context, kwargs object.Kwargs, args ...object.Object) object.Object { if err := errors.ExactArgs(args, 1); err != nil { return err } match := args[0].(*object.Instance) groups := match.Field("_groups").(*object.List).Elements names := match.Field("_group_names").(*object.List).Elements result := &object.Dict{Pairs: make(map[string]object.DictPair)} for i, n := range names { ns, ok := n.(*object.String) if !ok || ns.StringValue() == "" || i >= len(groups) { continue } result.SetByString(ns.StringValue(), groups[i]) } return result }, HelpText: `groupdict() - Return a dict of named groups to their matched text`, }, "groups": &object.Builtin{ Fn: func(ctx context.Context, kwargs object.Kwargs, args ...object.Object) object.Object { if err := errors.ExactArgs(args, 1); err != nil { return err } match := args[0].(*object.Instance) groups := match.Field("_groups").(*object.List).Elements if len(groups) <= 1 { return &object.Tuple{Elements: []object.Object{}} } elements := make([]object.Object, len(groups)-1) for i := 1; i < len(groups); i++ { elements[i-1] = groups[i] } return &object.Tuple{Elements: elements} }, HelpText: `groups() - Return tuple of all matched groups (excluding group 0)`, }, "start": &object.Builtin{ Fn: func(ctx context.Context, kwargs object.Kwargs, args ...object.Object) object.Object { if len(args) > 2 { return errors.NewError("start() takes at most 1 argument (%d given)", len(args)) } match := args[0].(*object.Instance) groupNum := 0 if len(args) == 2 { if args[1].Type() != object.INTEGER_OBJ { return errors.NewTypeError("INTEGER", args[1].Type().String()) } val, _ := args[1].AsInt() groupNum = int(val) } if groupNum != 0 { return errors.NewError("start() only supports group 0") } return match.Field("_start") }, HelpText: `start(n=0) - Return start position of nth group`, }, "end": &object.Builtin{ Fn: func(ctx context.Context, kwargs object.Kwargs, args ...object.Object) object.Object { if len(args) > 2 { return errors.NewError("end() takes at most 1 argument (%d given)", len(args)) } match := args[0].(*object.Instance) groupNum := 0 if len(args) == 2 { if args[1].Type() != object.INTEGER_OBJ { return errors.NewTypeError("INTEGER", args[1].Type().String()) } val, _ := args[1].AsInt() groupNum = int(val) } if groupNum != 0 { return errors.NewError("end() only supports group 0") } return match.Field("_end") }, HelpText: `end(n=0) - Return end position of nth group`, }, "span": &object.Builtin{ Fn: func(ctx context.Context, kwargs object.Kwargs, args ...object.Object) object.Object { if len(args) > 2 { return errors.NewError("span() takes at most 1 argument (%d given)", len(args)) } match := args[0].(*object.Instance) groupNum := 0 if len(args) == 2 { if args[1].Type() != object.INTEGER_OBJ { return errors.NewTypeError("INTEGER", args[1].Type().String()) } val, _ := args[1].AsInt() groupNum = int(val) } if groupNum != 0 { return errors.NewError("span() only supports group 0") } return &object.Tuple{Elements: []object.Object{ match.Field("_start"), match.Field("_end"), }} }, HelpText: `span(n=0) - Return (start, end) tuple for nth group`, }, }, }
Match class definition
var MathLibrary = object.NewLibrary(MathLibraryName, map[string]*object.Builtin{ "sqrt": { Fn: oneFloatFunc(math.Sqrt), HelpText: `sqrt(x) - Return the square root of x x must be a non-negative number (integer or float). Returns a float.`, }, "pow": { Fn: twoFloatFunc(math.Pow), HelpText: `pow(base, exp) - Return base raised to the power exp base and exp can be integers or floats. Returns a float.`, }, "fabs": { Fn: oneFloatFunc(math.Abs), HelpText: `fabs(x) - Return the absolute value of x as a float x can be an integer or float. Always returns a float.`, }, "floor": { Fn: oneIntOrFloatFunc( func(i *object.Integer) object.Object { return i }, func(f *object.Float) object.Object { return object.NewInteger(int64(math.Floor(f.FloatValue()))) }, ), HelpText: `floor(x) - Return the floor of x x can be an integer or float. Returns the largest integer less than or equal to x.`, }, "ceil": { Fn: oneIntOrFloatFunc( func(i *object.Integer) object.Object { return i }, func(f *object.Float) object.Object { return object.NewInteger(int64(math.Ceil(f.FloatValue()))) }, ), HelpText: `ceil(x) - Return the ceiling of x x can be an integer or float. Returns the smallest integer greater than or equal to x.`, }, "sin": { Fn: oneFloatFunc(math.Sin), HelpText: `sin(x) - Return the sine of x (radians) x can be an integer or float in radians. Returns a float.`, }, "cos": { Fn: oneFloatFunc(math.Cos), HelpText: `cos(x) - Return the cosine of x (radians) x can be an integer or float in radians. Returns a float.`, }, "tan": { Fn: oneFloatFunc(math.Tan), HelpText: `tan(x) - Return the tangent of x (radians) x can be an integer or float in radians. Returns a float.`, }, "log": { Fn: func(ctx context.Context, kwargs object.Kwargs, args ...object.Object) object.Object { if err := errors.ExactArgs(args, 1); err != nil { return err } x, err := args[0].AsFloat() if err != nil { return errors.NewTypeError("INTEGER or FLOAT", args[0].Type().String()) } if x <= 0 { return errors.NewError("log: domain error") } return object.NewFloat(math.Log(x)) }, HelpText: `log(x) - Return the natural logarithm of x x must be positive (integer or float). Returns a float.`, }, "exp": { Fn: oneFloatFunc(math.Exp), HelpText: `exp(x) - Return e raised to the power x x can be an integer or float. Returns a float.`, }, "degrees": { Fn: oneFloatFunc(func(x float64) float64 { return x * 180.0 / math.Pi }), HelpText: `degrees(x) - Convert radians to degrees x can be an integer or float in radians. Returns a float in degrees.`, }, "radians": { Fn: oneFloatFunc(func(x float64) float64 { return x * math.Pi / 180.0 }), HelpText: `radians(x) - Convert degrees to radians x can be an integer or float in degrees. Returns a float in radians.`, }, "fmod": { Fn: func(ctx context.Context, kwargs object.Kwargs, args ...object.Object) object.Object { if err := errors.ExactArgs(args, 2); err != nil { return err } x, err := args[0].AsFloat() if err != nil { return errors.NewTypeError("INTEGER or FLOAT", args[0].Type().String()) } y, err := args[1].AsFloat() if err != nil { return errors.NewTypeError("INTEGER or FLOAT", args[1].Type().String()) } if y == 0 { return errors.NewValueError("fmod: division by zero") } return object.NewFloat(math.Mod(x, y)) }, HelpText: `fmod(x, y) - Return the floating-point remainder of x/y x and y can be integers or floats. y must not be zero. Returns a float.`, }, "isclose": { Fn: func(ctx context.Context, kwargs object.Kwargs, args ...object.Object) object.Object { if err := errors.ExactArgs(args, 2); err != nil { return err } a, err := args[0].AsFloat() if err != nil { return errors.NewTypeError("NUMBER", args[0].Type().String()) } b, err := args[1].AsFloat() if err != nil { return errors.NewTypeError("NUMBER", args[1].Type().String()) } relTol := 1e-09 if v := kwargs.Get("rel_tol"); v != nil { relTol, err = v.AsFloat() if err != nil { return errors.ParameterError("rel_tol", err) } } absTol := 0.0 if v := kwargs.Get("abs_tol"); v != nil { absTol, err = v.AsFloat() if err != nil { return errors.ParameterError("abs_tol", err) } } if relTol < 0 || absTol < 0 { return errors.NewError("tolerances must be non-negative") } if math.IsNaN(a) || math.IsNaN(b) { return object.NewBoolean(false) } if a == b { return object.NewBoolean(true) } if math.IsInf(a, 0) || math.IsInf(b, 0) { return object.NewBoolean(false) } diff := math.Abs(a - b) close := diff <= math.Max(relTol*math.Max(math.Abs(a), math.Abs(b)), absTol) if close { return object.NewBoolean(true) } return object.NewBoolean(false) }, HelpText: `isclose(a, b, rel_tol=1e-09, abs_tol=0.0) - Compare floats for near-equality Returns True if a and b are approximately equal within the relative and absolute tolerances, matching Python's math.isclose.`, }, "gcd": { Fn: func(ctx context.Context, kwargs object.Kwargs, args ...object.Object) object.Object { if err := errors.ExactArgs(args, 2); err != nil { return err } a, err := args[0].AsInt() if err != nil { return errors.NewTypeError("INTEGER", args[0].Type().String()) } b, err := args[1].AsInt() if err != nil { return errors.NewTypeError("INTEGER", args[1].Type().String()) } return object.NewInteger(gcd(a, b)) }, HelpText: `gcd(a, b) - Return the greatest common divisor of a and b a and b must be integers. Returns an integer.`, }, "factorial": { Fn: func(ctx context.Context, kwargs object.Kwargs, args ...object.Object) object.Object { if err := errors.ExactArgs(args, 1); err != nil { return err } n, err := args[0].AsInt() if err != nil { return errors.NewTypeError("INTEGER", args[0].Type().String()) } if n < 0 { return errors.NewError("factorial: negative number") } if n > 20 { return errors.NewError("factorial: result too large") } result := int64(1) for i := int64(2); i <= n; i++ { result *= i } return object.NewInteger(result) }, HelpText: `factorial(n) - Return n! n must be a non-negative integer <= 20. Returns an integer.`, }, "isnan": { Fn: oneIntOrFloatFunc( func(i *object.Integer) object.Object { return object.NewBoolean(false) }, func(f *object.Float) object.Object { return object.NewBoolean(math.IsNaN(f.FloatValue())) }, ), HelpText: `isnan(x) - Check if x is NaN (Not a Number) Returns True if x is NaN, False otherwise.`, }, "isinf": { Fn: oneIntOrFloatFunc( func(i *object.Integer) object.Object { return object.NewBoolean(false) }, func(f *object.Float) object.Object { return object.NewBoolean(math.IsInf(f.FloatValue(), 0)) }, ), HelpText: `isinf(x) - Check if x is infinite Returns True if x is positive or negative infinity.`, }, "isfinite": { Fn: oneIntOrFloatFunc( func(i *object.Integer) object.Object { return object.NewBoolean(true) }, func(f *object.Float) object.Object { return object.NewBoolean(!math.IsNaN(f.FloatValue()) && !math.IsInf(f.FloatValue(), 0)) }, ), HelpText: `isfinite(x) - Check if x is finite Returns True if x is neither NaN nor infinite.`, }, "copysign": { Fn: twoFloatFunc(math.Copysign), HelpText: `copysign(x, y) - Return x with the sign of y Returns a float with magnitude of x and sign of y.`, }, "trunc": { Fn: oneIntOrFloatFunc( func(i *object.Integer) object.Object { return i }, func(f *object.Float) object.Object { return object.NewInteger(int64(math.Trunc(f.FloatValue()))) }, ), HelpText: `trunc(x) - Truncate x to the nearest integer toward zero Returns an integer.`, }, "log10": { Fn: oneFloatFunc(math.Log10), HelpText: `log10(x) - Return the base-10 logarithm of x x must be positive. Returns a float.`, }, "log2": { Fn: oneFloatFunc(math.Log2), HelpText: `log2(x) - Return the base-2 logarithm of x x must be positive. Returns a float.`, }, "hypot": { Fn: twoFloatFunc(math.Hypot), HelpText: `hypot(x, y) - Return the Euclidean distance sqrt(x*x + y*y) Returns a float.`, }, "asin": { Fn: oneFloatFunc(math.Asin), HelpText: `asin(x) - Return the arc sine of x in radians x must be in the range [-1, 1]. Returns a float.`, }, "acos": { Fn: oneFloatFunc(math.Acos), HelpText: `acos(x) - Return the arc cosine of x in radians x must be in the range [-1, 1]. Returns a float.`, }, "atan": { Fn: oneFloatFunc(math.Atan), HelpText: `atan(x) - Return the arc tangent of x in radians Returns a float in the range [-pi/2, pi/2].`, }, "atan2": { Fn: twoFloatFunc(math.Atan2), HelpText: `atan2(y, x) - Return the arc tangent of y/x in radians Correctly handles the quadrant of the result. Returns a float in the range [-pi, pi].`, }, "tanh": { Fn: oneFloatFunc(math.Tanh), HelpText: `tanh(x) - Return the hyperbolic tangent of x Returns a float in the range [-1, 1].`, }, "softmax": { Fn: func(ctx context.Context, kwargs object.Kwargs, args ...object.Object) object.Object { if err := errors.ExactArgs(args, 1); err != nil { return err } var vals []float64 inputIsFA := false switch a := args[0].(type) { case *object.FloatArray: if a.Is2D() { return errors.NewError("softmax: expected 1D array, got 2D") } vals = a.Data inputIsFA = true case *object.List: n := len(a.Elements) if n == 0 { return errors.NewError("softmax: input list cannot be empty") } vals = make([]float64, n) for i, el := range a.Elements { f, err := el.AsFloat() if err != nil { return errors.NewTypeError("INTEGER or FLOAT", el.Type().String()) } vals[i] = f } default: return errors.NewTypeError("LIST or FLOAT_ARRAY", args[0].Type().String()) } if len(vals) == 0 { return errors.NewError("softmax: input cannot be empty") } maxVal := math.Inf(-1) for _, v := range vals { if v > maxVal { maxVal = v } } var sum float64 exps := make([]float64, len(vals)) for i, v := range vals { exps[i] = math.Exp(v - maxVal) sum += exps[i] } result := make([]float64, len(vals)) for i, e := range exps { result[i] = e / sum } if inputIsFA { return object.NewFloatArray1D(result) } out := make([]object.Object, len(result)) for i, v := range result { out[i] = object.NewFloat(v) } return &object.List{Elements: out} }, HelpText: `softmax(x) - Return numerically stable softmax of a vector Returns a FloatArray probability distribution summing to 1.0.`, }, "dot": { Fn: func(ctx context.Context, kwargs object.Kwargs, args ...object.Object) object.Object { if err := errors.ExactArgs(args, 2); err != nil { return err } var aData, bData []float64 switch a := args[0].(type) { case *object.FloatArray: if a.Is2D() { return errors.NewError("dot: expected 1D array, got 2D") } aData = a.Data case *object.List: aData = make([]float64, len(a.Elements)) for i, el := range a.Elements { f, err := el.AsFloat() if err != nil { return errors.NewTypeError("INTEGER or FLOAT", el.Type().String()) } aData[i] = f } default: return errors.NewTypeError("LIST or FLOAT_ARRAY", args[0].Type().String()) } switch b := args[1].(type) { case *object.FloatArray: if b.Is2D() { return errors.NewError("dot: expected 1D array, got 2D") } bData = b.Data case *object.List: bData = make([]float64, len(b.Elements)) for i, el := range b.Elements { f, err := el.AsFloat() if err != nil { return errors.NewTypeError("INTEGER or FLOAT", el.Type().String()) } bData[i] = f } default: return errors.NewTypeError("LIST or FLOAT_ARRAY", args[1].Type().String()) } if len(aData) != len(bData) { return errors.NewError("dot: vectors must have the same length") } var sum float64 for i := range aData { sum += aData[i] * bData[i] } return object.NewFloat(sum) }, HelpText: `dot(a, b) - Return the dot product of two vectors a and b must be lists or 1D FloatArrays of numbers with the same length.`, }, "matmul": { Fn: func(ctx context.Context, kwargs object.Kwargs, args ...object.Object) object.Object { if err := errors.ExactArgs(args, 2); err != nil { return err } aRows, aFA, errObj := toFloatMatrix(args[0], "matmul", "a") if errObj != nil { return errObj } bRows, bFA, errObj := toFloatMatrix(args[1], "matmul", "b") if errObj != nil { return errObj } if len(aRows) == 0 || len(bRows) == 0 { return errors.NewError("matmul: matrices cannot be empty") } k := len(aRows[0]) if len(bRows) != k { return errors.NewError("matmul: inner dimensions mismatch (%d vs %d)", k, len(bRows)) } n := len(bRows[0]) result := make([][]float64, len(aRows)) for i := range result { result[i] = make([]float64, n) } for i, aRow := range aRows { for j := 0; j < n; j++ { var sum float64 for l := 0; l < k; l++ { sum += aRow[l] * bRows[l][j] } result[i][j] = sum } } return toMatrixOutput(result, aFA || bFA, n) }, HelpText: `matmul(a, b) - Matrix-matrix multiply a is (M x K), b is (K x N). Returns (M x N) matrix as list of lists.`, }, "transpose": { Fn: func(ctx context.Context, kwargs object.Kwargs, args ...object.Object) object.Object { if err := errors.ExactArgs(args, 1); err != nil { return err } rows, useFA, errObj := toFloatMatrix(args[0], "transpose", "matrix") if errObj != nil { return errObj } if len(rows) == 0 { if useFA { return object.NewFloatArray2D(nil, 0, 0) } return &object.List{Elements: []object.Object{}} } m := len(rows) n := len(rows[0]) result := make([][]float64, n) for j := range result { result[j] = make([]float64, m) for i := 0; i < m; i++ { result[j][i] = rows[i][j] } } return toMatrixOutput(result, useFA, m) }, HelpText: `transpose(m) - Transpose a 2D matrix Rows become columns. Returns a new matrix.`, }, "mat_add": { Fn: func(ctx context.Context, kwargs object.Kwargs, args ...object.Object) object.Object { if err := errors.ExactArgs(args, 2); err != nil { return err } aRows, aFA, errObj := toFloatMatrix(args[0], "mat_add", "a") if errObj != nil { return errObj } bRows, bFA, errObj := toFloatMatrix(args[1], "mat_add", "b") if errObj != nil { return errObj } if len(aRows) != len(bRows) { return errors.NewError("mat_add: matrices must have the same shape") } if len(aRows) == 0 { if aFA || bFA { return object.NewFloatArray2D(nil, 0, 0) } return &object.List{Elements: []object.Object{}} } if len(aRows[0]) != len(bRows[0]) { return errors.NewError("mat_add: matrices must have the same shape") } result := make([][]float64, len(aRows)) for i := range aRows { result[i] = make([]float64, len(aRows[0])) for j := range aRows[0] { result[i][j] = aRows[i][j] + bRows[i][j] } } return toMatrixOutput(result, aFA || bFA, len(aRows[0])) }, HelpText: `mat_add(a, b) - Element-wise addition of two matrices a and b must have the same shape. Returns a new matrix.`, }, "erf": { Fn: oneFloatFunc(math.Erf), HelpText: `erf(x) - Return the error function of x Returns a float in the range [-1, 1].`, }, "erfc": { Fn: oneFloatFunc(math.Erfc), HelpText: `erfc(x) - Return the complementary error function of x Returns a float in the range [0, 2].`, }, "gamma": { Fn: oneFloatFunc(math.Gamma), HelpText: `gamma(x) - Return the gamma function of x Returns a float.`, }, "lgamma": { Fn: func(ctx context.Context, kwargs object.Kwargs, args ...object.Object) object.Object { if err := errors.ExactArgs(args, 1); err != nil { return err } x, err := args[0].AsFloat() if err != nil { return errors.NewTypeError("INTEGER or FLOAT", args[0].Type().String()) } val, sign := math.Lgamma(x) return &object.List{Elements: []object.Object{ object.NewFloat(val), object.NewInteger(int64(sign)), }} }, HelpText: `lgamma(x) - Return the natural log of the absolute value of the gamma function Returns a list [log_abs_gamma, sign].`, }, "nextafter": { Fn: twoFloatFunc(math.Nextafter), HelpText: `nextafter(x, y) - Return the next floating-point value after x towards y Returns a float.`, }, "cbrt": { Fn: oneFloatFunc(math.Cbrt), HelpText: `cbrt(x) - Return the cube root of x Returns a float.`, }, "remainder": { Fn: twoFloatFunc(math.Remainder), HelpText: `remainder(x, y) - Return the IEEE 754-style remainder of x/y Returns a float.`, }, "log1p": { Fn: oneFloatFunc(math.Log1p), HelpText: `log1p(x) - Return log(1+x) accurately for small x Returns a float.`, }, "expm1": { Fn: oneFloatFunc(math.Expm1), HelpText: `expm1(x) - Return exp(x)-1 accurately for small x Returns a float.`, }, "comb": { Fn: func(ctx context.Context, kwargs object.Kwargs, args ...object.Object) object.Object { if err := errors.ExactArgs(args, 2); err != nil { return err } n, err := args[0].AsInt() if err != nil { return err } k, err := args[1].AsInt() if err != nil { return err } if n < 0 || k < 0 { return errors.NewError("comb: arguments must be non-negative") } if k > n { return object.NewInteger(0) } if k == 0 || k == n { return object.NewInteger(1) } if k > n-k { k = n - k } var result big.Int result.Binomial(n, k) if !result.IsInt64() { return errors.NewError("comb: result too large") } return object.NewInteger(result.Int64()) }, HelpText: `comb(n, k) - Return the number of ways to choose k items from n Also known as the binomial coefficient. n and k must be non-negative integers.`, }, "perm": { Fn: func(ctx context.Context, kwargs object.Kwargs, args ...object.Object) object.Object { if err := errors.RangeArgs(args, 1, 2); err != nil { return err } n, err := args[0].AsInt() if err != nil { return err } if n < 0 { return errors.NewError("perm: argument must be non-negative") } k := n if len(args) == 2 { k, err = args[1].AsInt() if err != nil { return err } } if k < 0 || k > n { return object.NewInteger(0) } var result int64 = 1 for i := int64(0); i < k; i++ { if result > math.MaxInt64/(n-i) { return errors.NewError("perm: result too large") } result *= n - i } return object.NewInteger(result) }, HelpText: `perm(n[, k]) - Return the number of ways to choose k items from n with order n and k must be non-negative integers. If k is omitted, returns n!.`, }, "prod": { Fn: func(ctx context.Context, kwargs object.Kwargs, args ...object.Object) object.Object { if err := errors.ExactArgs(args, 1); err != nil { return err } list, ok := args[0].(*object.List) if !ok { return errors.NewTypeError("LIST", args[0].Type().String()) } if len(list.Elements) == 0 { return object.NewInteger(1) } if kwargs.Has("start") && kwargs.Get("start") != nil { result, err := kwargs.Get("start").AsFloat() if err != nil { return err } for _, el := range list.Elements { v, err := el.AsFloat() if err != nil { return errors.NewTypeError("INTEGER or FLOAT", el.Type().String()) } result *= v } return object.NewFloat(result) } var intResult int64 = 1 allInt := true overflow := false for _, el := range list.Elements { vInt, ok := el.(*object.Integer) if !ok { allInt = false break } if overflow || intResult == 0 { continue } if vInt.IntValue() != 0 && intResult > math.MaxInt64/vInt.IntValue() { return errors.NewError("integer overflow in product") } intResult *= vInt.IntValue() } if allInt && !overflow { return object.NewInteger(intResult) } var result float64 = 1 for _, el := range list.Elements { v, err := el.AsFloat() if err != nil { return errors.NewTypeError("INTEGER or FLOAT", el.Type().String()) } result *= v } return object.NewFloat(result) }, HelpText: `prod(iterable, start=1) - Return the product of all elements With start keyword, begins multiplication from that value. Returns an integer for all-integer inputs, float otherwise.`, }, "dist": { Fn: func(ctx context.Context, kwargs object.Kwargs, args ...object.Object) object.Object { if err := errors.ExactArgs(args, 2); err != nil { return err } pList, ok := args[0].(*object.List) if !ok { return errors.NewTypeError("LIST", args[0].Type().String()) } qList, ok := args[1].(*object.List) if !ok { return errors.NewTypeError("LIST", args[1].Type().String()) } if len(pList.Elements) != len(qList.Elements) { return errors.NewError("dist: points must have the same dimension") } var sum float64 for i := 0; i < len(pList.Elements); i++ { p, err := pList.Elements[i].AsFloat() if err != nil { return errors.NewTypeError("INTEGER or FLOAT", pList.Elements[i].Type().String()) } q, err := qList.Elements[i].AsFloat() if err != nil { return errors.NewTypeError("INTEGER or FLOAT", qList.Elements[i].Type().String()) } d := p - q sum += d * d } return object.NewFloat(math.Sqrt(sum)) }, HelpText: `dist(p, q) - Return the Euclidean distance between two points p and q must be lists of numbers with the same length.`, }, "array": { Fn: func(ctx context.Context, kwargs object.Kwargs, args ...object.Object) object.Object { if err := errors.ExactArgs(args, 1); err != nil { return err } switch a := args[0].(type) { case *object.FloatArray: return a case *object.List: if len(a.Elements) == 0 { return object.NewFloatArray1D([]float64{}) } if inner, ok := a.Elements[0].(*object.List); ok { rows := len(a.Elements) cols := len(inner.Elements) data := make([]float64, 0, rows*cols) for _, rowObj := range a.Elements { row, ok := rowObj.(*object.List) if !ok { return errors.NewError("array: cannot mix list rows with non-list rows") } if len(row.Elements) != cols { return errors.NewError("array: all rows must have the same length") } for _, el := range row.Elements { f, err := el.AsFloat() if err != nil { return errors.NewTypeError("INTEGER or FLOAT", el.Type().String()) } data = append(data, f) } } return object.NewFloatArray2D(data, rows, cols) } if innerFA, ok := a.Elements[0].(*object.FloatArray); ok && !innerFA.Is2D() { cols := len(innerFA.Data) rows := len(a.Elements) data := make([]float64, 0, rows*cols) for _, rowObj := range a.Elements { fa, ok := rowObj.(*object.FloatArray) if !ok || fa.Is2D() { return errors.NewError("array: cannot mix FloatArray rows with non-FloatArray rows") } if len(fa.Data) != cols { return errors.NewError("array: all rows must have the same length") } data = append(data, fa.Data...) } return object.NewFloatArray2D(data, rows, cols) } data := make([]float64, len(a.Elements)) for i, el := range a.Elements { f, err := el.AsFloat() if err != nil { return errors.NewTypeError("INTEGER or FLOAT", el.Type().String()) } data[i] = f } return object.NewFloatArray1D(data) default: return errors.NewTypeError("LIST or FLOAT_ARRAY", args[0].Type().String()) } }, HelpText: `array(data) - Create an efficient FloatArray from a list Accepts a 1D list of numbers or a 2D list of lists. Returns a FloatArray that avoids per-element boxing overhead.`, }, "shape": { Fn: func(ctx context.Context, kwargs object.Kwargs, args ...object.Object) object.Object { if err := errors.ExactArgs(args, 1); err != nil { return err } fa, ok := args[0].(*object.FloatArray) if !ok { return errors.NewTypeError("FLOAT_ARRAY", args[0].Type().String()) } elems := make([]object.Object, len(fa.Shape)) for i, s := range fa.Shape { elems[i] = object.NewInteger(int64(s)) } return &object.List{Elements: elems} }, HelpText: `shape(a) - Return the shape of a FloatArray as a list of ints`, }, }, map[string]object.Object{ "pi": object.NewFloat(math.Pi), "e": object.NewFloat(math.E), "inf": object.NewFloat(math.Inf(1)), "nan": object.NewFloat(math.NaN()), "tau": object.NewFloat(2 * math.Pi), }, "Mathematical functions library")
var MsgpackLibrary = NewMsgpackLibrary(shamaton.New())
MsgpackLibrary is the default library instance, backed by the same codec as gossip's DefaultConfig (shamaton), so a fresh Scriptling instance and a fresh gossip cluster agree on the wire format without any explicit wiring.
var ParseResultClass = &object.Class{ Name: "ParseResult", Methods: map[string]object.Object{ "geturl": &object.Builtin{ Fn: func(ctx context.Context, kwargs object.Kwargs, args ...object.Object) object.Object { if err := errors.ExactArgs(args, 1); err != nil { return err } result := args[0].(*object.Instance) scheme, _ := result.Field("scheme").(*object.String) netloc, _ := result.Field("netloc").(*object.String) path, _ := result.Field("path").(*object.String) query, _ := result.Field("query").(*object.String) fragment, _ := result.Field("fragment").(*object.String) url := scheme.StringValue() + "://" if netloc.StringValue() != "" { url += netloc.StringValue() } url += path.StringValue() if query.StringValue() != "" { url += "?" + query.StringValue() } if fragment.StringValue() != "" { url += "#" + fragment.StringValue() } return object.NewString(url) }, HelpText: `geturl() - Reconstruct URL from parsed components`, }, }, }
ParseResult class for URL parsing results
var PlatformLibrary = object.NewLibrary(PlatformLibraryName, map[string]*object.Builtin{ "python_version": { Fn: func(ctx context.Context, kwargs object.Kwargs, args ...object.Object) object.Object { if err := errors.ExactArgs(args, 0); err != nil { return err } return object.NewString(build.Version) }, HelpText: `python_version() - Returns the Python version Returns the Python version (Scriptling version for compatibility).`, }, "system": { Fn: func(ctx context.Context, kwargs object.Kwargs, args ...object.Object) object.Object { if err := errors.ExactArgs(args, 0); err != nil { return err } switch runtime.GOOS { case "darwin": return object.NewString("Darwin") case "linux": return object.NewString("Linux") case "windows": return object.NewString("Windows") case "freebsd": return object.NewString("FreeBSD") default: return object.NewString(runtime.GOOS) } }, HelpText: `system() - Returns the system/OS name Returns 'Darwin', 'Linux', 'Windows', etc.`, }, "architecture": { Fn: func(ctx context.Context, kwargs object.Kwargs, args ...object.Object) object.Object { if err := errors.ExactArgs(args, 0); err != nil { return err } arch := "64bit" if runtime.GOARCH == "386" || runtime.GOARCH == "arm" { arch = "32bit" } return &object.List{Elements: []object.Object{object.NewString(arch), object.NewString("")}} }, HelpText: `architecture() - Returns the architecture Returns a list like ['64bit', ''] indicating bits and linkage.`, }, "machine": { Fn: func(ctx context.Context, kwargs object.Kwargs, args ...object.Object) object.Object { if err := errors.ExactArgs(args, 0); err != nil { return err } return object.NewString(runtime.GOARCH) }, HelpText: `machine() - Returns the machine type (architecture) Returns 'amd64', 'arm64', 'arm', etc.`, }, "platform": { Fn: func(ctx context.Context, kwargs object.Kwargs, args ...object.Object) object.Object { if err := errors.ExactArgs(args, 0); err != nil { return err } return object.NewString(runtime.GOOS + "-" + runtime.GOARCH) }, HelpText: `platform() - Returns a string identifying the platform Returns a string like 'darwin-amd64', 'linux-amd64', etc.`, }, "scriptling_version": { Fn: func(ctx context.Context, kwargs object.Kwargs, args ...object.Object) object.Object { if err := errors.ExactArgs(args, 0); err != nil { return err } return object.NewString(build.Version) }, HelpText: `scriptling_version() - Returns Scriptling version string Returns the current version of Scriptling.`, }, "processor": { Fn: func(ctx context.Context, kwargs object.Kwargs, args ...object.Object) object.Object { if err := errors.ExactArgs(args, 0); err != nil { return err } return object.NewString(runtime.GOARCH) }, HelpText: `processor() - Returns the processor name Returns the processor name, often same as machine.`, }, "node": { Fn: func(ctx context.Context, kwargs object.Kwargs, args ...object.Object) object.Object { if err := errors.ExactArgs(args, 0); err != nil { return err } if hostname, err := os.Hostname(); err == nil { return object.NewString(hostname) } return object.NewString("") }, HelpText: `node() - Returns the network name (hostname) Returns the computer's network name.`, }, "release": { Fn: func(ctx context.Context, kwargs object.Kwargs, args ...object.Object) object.Object { if err := errors.ExactArgs(args, 0); err != nil { return err } return object.NewString(build.Version) }, HelpText: `release() - Returns the system release Returns the system release (Scriptling version for compatibility).`, }, "version": { Fn: func(ctx context.Context, kwargs object.Kwargs, args ...object.Object) object.Object { if err := errors.ExactArgs(args, 0); err != nil { return err } return object.NewString(build.Version) }, HelpText: `version() - Returns the system version Returns the system version (Scriptling version for compatibility).`, }, "uname": { Fn: func(ctx context.Context, kwargs object.Kwargs, args ...object.Object) object.Object { if err := errors.ExactArgs(args, 0); err != nil { return err } // Get system name (capitalized, matching system() function) var systemName string switch runtime.GOOS { case "darwin": systemName = "Darwin" case "linux": systemName = "Linux" case "windows": systemName = "Windows" case "freebsd": systemName = "FreeBSD" default: systemName = runtime.GOOS } result := &object.Dict{Pairs: make(map[string]object.DictPair)} result.SetByString("system", object.NewString(systemName)) result.SetByString("machine", object.NewString(runtime.GOARCH)) result.SetByString("processor", object.NewString(runtime.GOARCH)) node := "" if hostname, err := os.Hostname(); err == nil { node = hostname } result.SetByString("node", object.NewString(node)) result.SetByString("release", object.NewString(build.Version)) result.SetByString("version", object.NewString(build.Version)) return result }, HelpText: `uname() - Returns system information Returns a dict with keys: system, node, release, version, machine, processor`, }, }, nil, "Access to underlying platform's identifying data")
PlatformLibrary provides system/platform information (Python's platform module)
var RandomLibrary = object.NewLibrary(RandomLibraryName, map[string]*object.Builtin{ "seed": { Fn: func(ctx context.Context, kwargs object.Kwargs, args ...object.Object) object.Object { if err := errors.MaxArgs(args, 1); err != nil { return err } var seedVal int64 if len(args) == 0 { seedVal = time.Now().UnixNano() } else { switch arg := args[0].(type) { case *object.Integer: seedVal = arg.IntValue() case *object.Float: seedVal = int64(arg.FloatValue()) default: return errors.NewTypeError("INTEGER or FLOAT", args[0].Type().String()) } } rng = rand.New(rand.NewSource(seedVal)) return &object.Null{} }, HelpText: `seed([a]) - Initialize the random number generator If a is omitted, current time is used. Otherwise, a is used as the seed.`, }, "randint": { Fn: func(ctx context.Context, kwargs object.Kwargs, args ...object.Object) object.Object { args, kerr := kwargsToPositional("randint", args, kwargs, "a", "b") if kerr != nil { return kerr } if err := errors.ExactArgs(args, 2); err != nil { return err } var min, max int64 switch arg := args[0].(type) { case *object.Integer: min = arg.IntValue() default: return errors.NewTypeError("INTEGER", args[0].Type().String()) } switch arg := args[1].(type) { case *object.Integer: max = arg.IntValue() default: return errors.NewTypeError("INTEGER", args[1].Type().String()) } if min > max { return errors.NewError("randint() min must be <= max") } val := min + rng.Int63n(max-min+1) return object.NewInteger(val) }, HelpText: `randint(min, max) - Return random integer Returns a random integer N such that min <= N <= max.`, }, "random": { Fn: func(ctx context.Context, kwargs object.Kwargs, args ...object.Object) object.Object { if err := errors.ExactArgs(args, 0); err != nil { return err } return object.NewFloat(rng.Float64()) }, HelpText: `random() - Return random float Returns a random float in the range [0.0, 1.0).`, }, "choice": { Fn: func(ctx context.Context, kwargs object.Kwargs, args ...object.Object) object.Object { if err := errors.ExactArgs(args, 1); err != nil { return err } if str, ok := args[0].(*object.String); ok { if len(str.StringValue()) == 0 { return errors.NewError("choice() string cannot be empty") } idx := rng.Intn(len(str.StringValue())) return object.NewString(string(str.StringValue()[idx])) } if list, ok := args[0].(*object.List); ok { if len(list.Elements) == 0 { return errors.NewError("choice() list cannot be empty") } idx := rng.Intn(len(list.Elements)) return list.Elements[idx] } return errors.NewTypeError("LIST or STRING", args[0].Type().String()) }, HelpText: `choice(seq) - Return random element from sequence Returns a randomly selected element from the given list or string.`, }, "shuffle": { Fn: func(ctx context.Context, kwargs object.Kwargs, args ...object.Object) object.Object { if err := errors.ExactArgs(args, 1); err != nil { return err } if list, ok := args[0].(*object.List); ok { n := len(list.Elements) for i := n - 1; i > 0; i-- { j := rng.Intn(i + 1) list.Elements[i], list.Elements[j] = list.Elements[j], list.Elements[i] } return &object.Null{} } return errors.NewTypeError("LIST", args[0].Type().String()) }, HelpText: `shuffle(list) - Shuffle list in place Randomly shuffles the elements of the list in place using the Fisher-Yates algorithm. Returns None.`, }, "uniform": { Fn: func(ctx context.Context, kwargs object.Kwargs, args ...object.Object) object.Object { args, kerr := kwargsToPositional("uniform", args, kwargs, "a", "b") if kerr != nil { return kerr } if err := errors.ExactArgs(args, 2); err != nil { return err } var a, b float64 switch arg := args[0].(type) { case *object.Integer: a = float64(arg.IntValue()) case *object.Float: a = arg.FloatValue() default: return errors.NewTypeError("INTEGER or FLOAT", args[0].Type().String()) } switch arg := args[1].(type) { case *object.Integer: b = float64(arg.IntValue()) case *object.Float: b = arg.FloatValue() default: return errors.NewTypeError("INTEGER or FLOAT", args[1].Type().String()) } val := a + rng.Float64()*(b-a) return object.NewFloat(val) }, HelpText: `uniform(a, b) - Return random float N such that a <= N <= b Returns a random floating-point number N such that a <= N <= b.`, }, "sample": { Fn: func(ctx context.Context, kwargs object.Kwargs, args ...object.Object) object.Object { args, kerr := kwargsToPositional("sample", args, kwargs, "population", "k") if kerr != nil { return kerr } if err := errors.ExactArgs(args, 2); err != nil { return err } k, ok := args[1].(*object.Integer) if !ok { return errors.NewTypeError("INTEGER", args[1].Type().String()) } // Python accepts any sequence (list, tuple, str, range, ...). A // range is indexed rather than expanded, so sampling from // range(10**9) costs only k picks. var n int64 var at func(i int64) object.Object if it, isIter := args[0].(*object.Iterator); isIter { if start, step, count, isRange := it.RangeRemaining(); isRange { n = count at = func(i int64) object.Object { return object.NewInteger(start + i*step) } } } if at == nil { list, errObj := collectIterable(ctx, args[0]) if errObj != nil { return errObj } n = int64(len(list)) at = func(i int64) object.Object { return list[i] } } if k.IntValue() < 0 || k.IntValue() > n { return errors.NewValueError("Sample larger than population or is negative") } swapped := map[int64]int64{} index := func(i int64) int64 { if v, ok := swapped[i]; ok { return v } return i } result := make([]object.Object, k.IntValue()) for i := int64(0); i < k.IntValue(); i++ { j := i + rng.Int63n(n-i) vi, vj := index(i), index(j) swapped[j] = vi result[i] = at(vj) } return &object.List{Elements: result} }, HelpText: `sample(population, k) - Return k unique random elements from population Returns a k length list of unique elements chosen from the population sequence. Used for random sampling without replacement.`, }, "randrange": { Fn: func(ctx context.Context, kwargs object.Kwargs, args ...object.Object) object.Object { if len(args) < 1 || len(args) > 3 { return errors.NewError("randrange() takes 1 to 3 arguments (%d given)", len(args)) } var start, stop, step int64 step = 1 switch len(args) { case 1: start = 0 if i, ok := args[0].(*object.Integer); ok { stop = i.IntValue() } else { return errors.NewTypeError("INTEGER", args[0].Type().String()) } case 2: if i, ok := args[0].(*object.Integer); ok { start = i.IntValue() } else { return errors.NewTypeError("INTEGER", args[0].Type().String()) } if i, ok := args[1].(*object.Integer); ok { stop = i.IntValue() } else { return errors.NewTypeError("INTEGER", args[1].Type().String()) } case 3: if i, ok := args[0].(*object.Integer); ok { start = i.IntValue() } else { return errors.NewTypeError("INTEGER", args[0].Type().String()) } if i, ok := args[1].(*object.Integer); ok { stop = i.IntValue() } else { return errors.NewTypeError("INTEGER", args[1].Type().String()) } if i, ok := args[2].(*object.Integer); ok { step = i.IntValue() } else { return errors.NewTypeError("INTEGER", args[2].Type().String()) } } if step == 0 { return errors.NewError("randrange() step argument must not be zero") } var n int64 if step > 0 { n = (stop - start + step - 1) / step } else { n = (start - stop - step - 1) / (-step) } if n <= 0 { return errors.NewError("randrange() empty range") } return object.NewInteger(start + step*rng.Int63n(n)) }, HelpText: `randrange(stop) or randrange(start, stop[, step]) - Return random integer from range Returns a randomly selected element from range(start, stop, step). Like randint, but doesn't include the endpoint.`, }, "gauss": { Fn: gaussianRandom, HelpText: `gauss(mu, sigma) - Return random number from Gaussian distribution mu is the mean, sigma is the standard deviation.`, }, "normalvariate": { Fn: gaussianRandom, HelpText: `normalvariate(mu, sigma) - Return random number from normal distribution mu is the mean, sigma is the standard deviation. Same as gauss() but provided for compatibility.`, }, "expovariate": { Fn: func(ctx context.Context, kwargs object.Kwargs, args ...object.Object) object.Object { if err := errors.ExactArgs(args, 1); err != nil { return err } var lambd float64 switch arg := args[0].(type) { case *object.Integer: lambd = float64(arg.IntValue()) case *object.Float: lambd = arg.FloatValue() default: return errors.NewTypeError("INTEGER or FLOAT", args[0].Type().String()) } if lambd == 0 { return errors.NewError("expovariate() lambda must not be zero") } val := -math.Log(1.0-rng.Float64()) / lambd return object.NewFloat(val) }, HelpText: `expovariate(lambd) - Return random number from exponential distribution lambd is 1.0 divided by the desired mean.`, }, "choices": { Fn: func(ctx context.Context, kwargs object.Kwargs, args ...object.Object) object.Object { if err := errors.RangeArgs(args, 1, 3); err != nil { return err } for _, key := range kwargs.Keys() { if key != "weights" && key != "cum_weights" && key != "k" { return newArgTypeError("choices() got an unexpected keyword argument '%s'", key) } } population, errObj := collectIterable(ctx, args[0]) if errObj != nil { return errObj } n := len(population) weightList := func(name string, obj object.Object) ([]float64, object.Object) { if obj == nil { return nil, nil } if _, isNull := obj.(*object.Null); isNull { return nil, nil } var elems []object.Object switch w := obj.(type) { case *object.List: elems = w.Elements case *object.Tuple: elems = w.Elements default: return nil, errors.NewTypeError("LIST", obj.Type().String()) } if len(elems) != n { return nil, errors.NewValueError("The number of %s does not match the population", name) } out := make([]float64, n) for i, w := range elems { f, err := w.AsFloat() if err != nil { return nil, errors.NewTypeError("INTEGER or FLOAT", w.Type().String()) } if math.IsNaN(f) || math.IsInf(f, 0) { return nil, errors.NewError("choices: %s must be finite", name) } out[i] = f } return out, nil } wObj := kwargs.Get("weights") if len(args) >= 2 { if kwargs.Has("weights") { return errors.NewError("choices: weights specified both positionally and by keyword") } wObj = args[1] } weights, werr := weightList("weights", wObj) if werr != nil { return werr } cumWeights, cerr := weightList("cum_weights", kwargs.Get("cum_weights")) if cerr != nil { return cerr } if weights != nil && cumWeights != nil { return newArgTypeError("Cannot specify both weights and cumulative weights") } k := 1 kObj := kwargs.Get("k") if len(args) >= 3 { if kwargs.Has("k") { return errors.NewError("choices: k specified both positionally and by keyword") } kObj = args[2] } if kObj != nil { kVal, err := kObj.AsInt() if err != nil { return err } if kVal < 0 { return errors.NewError("choices: k must be non-negative") } k = int(kVal) } if k == 0 { return &object.List{Elements: []object.Object{}} } if n == 0 { return &object.Exception{Message: "Cannot choose from an empty sequence", ExceptionType: object.ExceptionTypeIndexError, Raised: true} } if cumWeights == nil { if weights == nil { weights = make([]float64, n) for i := range weights { weights[i] = 1.0 } } for _, f := range weights { if f < 0 { return errors.NewError("choices: weights must be finite and non-negative") } } cumWeights = make([]float64, n) cumWeights[0] = weights[0] for i := 1; i < n; i++ { cumWeights[i] = cumWeights[i-1] + weights[i] } } total := cumWeights[n-1] if math.IsInf(total, 0) || math.IsNaN(total) { return errors.NewValueError("Total of weights must be finite") } if total <= 0 { return errors.NewValueError("Total of weights must be greater than zero") } result := make([]object.Object, k) for i := 0; i < k; i++ { r := rng.Float64() * total idx := 0 for idx < n-1 && cumWeights[idx] <= r { idx++ } result[i] = population[idx] } return &object.List{Elements: result} }, HelpText: `choices(population, weights=None, *, cum_weights=None, k=1) - Weighted random sampling with replacement Select k items from population with the given relative weights, or with cumulative weights (cum_weights). Specifying both is an error. Returns a list of k selected items.`, }, "betavariate": { Fn: func(ctx context.Context, kwargs object.Kwargs, args ...object.Object) object.Object { if err := errors.ExactArgs(args, 2); err != nil { return err } alpha, err := args[0].AsFloat() if err != nil { return errors.NewTypeError("INTEGER or FLOAT", args[0].Type().String()) } beta, err := args[1].AsFloat() if err != nil { return errors.NewTypeError("INTEGER or FLOAT", args[1].Type().String()) } if alpha <= 0 || beta <= 0 { return errors.NewError("betavariate: alpha and beta must be positive") } x := sampleGamma(alpha) y := sampleGamma(beta) if x+y == 0 { return object.NewFloat(0) } return object.NewFloat(x / (x + y)) }, HelpText: `betavariate(alpha, beta) - Return random number from beta distribution alpha and beta must be positive. Returns a float in [0, 1].`, }, "gammavariate": { Fn: func(ctx context.Context, kwargs object.Kwargs, args ...object.Object) object.Object { if err := errors.ExactArgs(args, 2); err != nil { return err } alpha, err := args[0].AsFloat() if err != nil { return errors.NewTypeError("INTEGER or FLOAT", args[0].Type().String()) } beta, err := args[1].AsFloat() if err != nil { return errors.NewTypeError("INTEGER or FLOAT", args[1].Type().String()) } if alpha <= 0 || beta <= 0 { return errors.NewError("gammavariate: alpha and beta must be positive") } return object.NewFloat(sampleGamma(alpha) * beta) }, HelpText: `gammavariate(alpha, beta) - Return random number from gamma distribution alpha (shape) and beta (scale) must be positive.`, }, "triangular": { Fn: func(ctx context.Context, kwargs object.Kwargs, args ...object.Object) object.Object { if err := errors.MaxArgs(args, 3); err != nil { return err } if err := checkKwargs("triangular", kwargs, "low", "high", "mode"); err != nil { return err } vals := [3]float64{0, 1, 0} hasMode := false for i, name := range []string{"low", "high", "mode"} { v, aerr := optionalArg("triangular", args, kwargs, i, name) if aerr != nil { return aerr } if v == nil { if name != "mode" && (len(args) > i || kwargs.Has(name)) { return errors.NewTypeError("INTEGER or FLOAT", "NULL") } continue } f, err := v.AsFloat() if err != nil { return errors.NewTypeError("INTEGER or FLOAT", v.Type().String()) } vals[i] = f hasMode = hasMode || i == 2 } low, high, mode := vals[0], vals[1], vals[2] if !hasMode { mode = (low + high) / 2.0 } if low == high { return object.NewFloat(low) } u := rng.Float64() c := (mode - low) / (high - low) var val float64 if u <= c { val = low + math.Sqrt(u*(high-low)*(mode-low)) } else { val = high - math.Sqrt((1-u)*(high-low)*(high-mode)) } return object.NewFloat(val) }, HelpText: `triangular(low, high[, mode]) - Return random number from triangular distribution low is the minimum, high is the maximum, mode is the peak (defaults to midpoint).`, }, "paretovariate": { Fn: func(ctx context.Context, kwargs object.Kwargs, args ...object.Object) object.Object { if err := errors.ExactArgs(args, 1); err != nil { return err } alpha, err := args[0].AsFloat() if err != nil { return errors.NewTypeError("INTEGER or FLOAT", args[0].Type().String()) } if alpha <= 0 { return errors.NewError("paretovariate: alpha must be positive") } u := rng.Float64() if u == 0 { u = math.SmallestNonzeroFloat64 } return object.NewFloat(1.0 / math.Pow(u, 1.0/alpha)) }, HelpText: `paretovariate(alpha) - Return random number from Pareto distribution alpha is the shape parameter, must be positive.`, }, "weibullvariate": { Fn: func(ctx context.Context, kwargs object.Kwargs, args ...object.Object) object.Object { if err := errors.ExactArgs(args, 2); err != nil { return err } alpha, err := args[0].AsFloat() if err != nil { return errors.NewTypeError("INTEGER or FLOAT", args[0].Type().String()) } beta, err := args[1].AsFloat() if err != nil { return errors.NewTypeError("INTEGER or FLOAT", args[1].Type().String()) } if alpha <= 0 || beta <= 0 { return errors.NewError("weibullvariate: alpha and beta must be positive") } u := rng.Float64() if u == 0 { u = math.SmallestNonzeroFloat64 } return object.NewFloat(alpha * math.Pow(-math.Log(u), 1.0/beta)) }, HelpText: `weibullvariate(alpha, beta) - Return random number from Weibull distribution alpha is the scale parameter, beta is the shape parameter. Both must be positive.`, }, }, nil, "Random number generation library")
var ReLibrary = object.NewLibrary(ReLibraryName, map[string]*object.Builtin{ "match": { Fn: func(ctx context.Context, kwargs object.Kwargs, args ...object.Object) object.Object { if len(args) < 2 || len(args) > 3 { return errors.NewError("match() takes 2 or 3 arguments (%d given)", len(args)) } if args[0].Type() != object.STRING_OBJ || args[1].Type() != object.STRING_OBJ { return errors.NewTypeError("STRING", "mixed types") } pattern, _ := args[0].AsString() text, _ := args[1].AsString() flags, err := getFlags(args, kwargs, 2) if err != nil { return errors.NewError("%s", err.Error()) } pattern = applyFlags(pattern, flags) re, err := GetCompiledRegex(pattern) if err != nil { return errors.NewError("regex compile error: %s", err.Error()) } match := re.FindStringSubmatchIndex(text) if match == nil || match[0] != 0 { return &object.Null{} } groups := make([]string, 0) for i := 0; i < len(match); i += 2 { if match[i] >= 0 && match[i+1] >= 0 { groups = append(groups, text[match[i]:match[i+1]]) } else { groups = append(groups, "") } } return createMatchInstance(groups, match[0], match[1], re.SubexpNames()) }, HelpText: `match(pattern, string, flags=0) - Match pattern at start of string Returns a Match object if the pattern matches at the beginning, or None if no match. Use match.group(0) for the full match, match.group(1) for the first group, etc. Methods on Match object: group(n=0) - Return the nth matched group (0 = full match) groups() - Return tuple of all matched groups (excluding group 0) start(n=0) - Return start position of nth group end(n=0) - Return end position of nth group span(n=0) - Return (start, end) tuple for nth group Flags: re.IGNORECASE or re.I - Case-insensitive matching re.MULTILINE or re.M - ^ and $ match at line boundaries re.DOTALL or re.S - . matches newlines`, }, "search": { Fn: func(ctx context.Context, kwargs object.Kwargs, args ...object.Object) object.Object { if len(args) < 2 || len(args) > 3 { return errors.NewError("search() takes 2 or 3 arguments (%d given)", len(args)) } if args[0].Type() != object.STRING_OBJ || args[1].Type() != object.STRING_OBJ { return errors.NewTypeError("STRING", "mixed types") } pattern, _ := args[0].AsString() text, _ := args[1].AsString() flags, err := getFlags(args, kwargs, 2) if err != nil { return errors.NewError("%s", err.Error()) } pattern = applyFlags(pattern, flags) re, err := GetCompiledRegex(pattern) if err != nil { return errors.NewError("regex compile error: %s", err.Error()) } match := re.FindStringSubmatchIndex(text) if match == nil { return &object.Null{} } groups := make([]string, 0) for i := 0; i < len(match); i += 2 { if match[i] >= 0 && match[i+1] >= 0 { groups = append(groups, text[match[i]:match[i+1]]) } else { groups = append(groups, "") } } return createMatchInstance(groups, match[0], match[1], re.SubexpNames()) }, HelpText: `search(pattern, string, flags=0) - Search for pattern anywhere in string Returns a Match object for the first match, or None if no match found. Use match.group(0) for the full match, match.group(1) for the first group, etc. Methods on Match object: group(n=0) - Return the nth matched group (0 = full match) groups() - Return tuple of all matched groups (excluding group 0) start(n=0) - Return start position of nth group end(n=0) - Return end position of nth group span(n=0) - Return (start, end) tuple for nth group Flags: re.IGNORECASE or re.I - Case-insensitive matching re.MULTILINE or re.M - ^ and $ match at line boundaries re.DOTALL or re.S - . matches newlines`, }, "findall": { Fn: func(ctx context.Context, kwargs object.Kwargs, args ...object.Object) object.Object { if len(args) < 2 || len(args) > 3 { return errors.NewError("findall() takes 2 or 3 arguments (%d given)", len(args)) } if args[0].Type() != object.STRING_OBJ || args[1].Type() != object.STRING_OBJ { return errors.NewTypeError("STRING", "mixed types") } pattern, _ := args[0].AsString() text, _ := args[1].AsString() flags, err := getFlags(args, kwargs, 2) if err != nil { return errors.NewError("%s", err.Error()) } pattern = applyFlags(pattern, flags) re, err := GetCompiledRegex(pattern) if err != nil { return errors.NewError("regex compile error: %s", err.Error()) } matches := re.FindAllStringSubmatch(text, -1) elements := make([]object.Object, len(matches)) numGroups := re.NumSubexp() for i, match := range matches { if numGroups == 0 { elements[i] = object.NewString(match[0]) } else if numGroups == 1 { elements[i] = object.NewString(match[1]) } else { groupElements := make([]object.Object, numGroups) for j := 1; j <= numGroups; j++ { groupElements[j-1] = object.NewString(match[j]) } elements[i] = &object.Tuple{Elements: groupElements} } } return &object.List{Elements: elements} }, HelpText: `findall(pattern, string, flags=0) - Find all matches Returns a list of all substrings that match the regex pattern. If the pattern contains capturing groups, returns a list of tuples containing the groups. If there is one capturing group, returns a list of strings for that group. If there are no capturing groups, returns a list of strings for the full matches. Flags: re.IGNORECASE or re.I - Case-insensitive matching re.MULTILINE or re.M - ^ and $ match at line boundaries re.DOTALL or re.S - . matches newlines`, }, "finditer": { Fn: func(ctx context.Context, kwargs object.Kwargs, args ...object.Object) object.Object { if len(args) < 2 || len(args) > 3 { return errors.NewError("finditer() takes 2 or 3 arguments (%d given)", len(args)) } if args[0].Type() != object.STRING_OBJ || args[1].Type() != object.STRING_OBJ { return errors.NewTypeError("STRING", "mixed types") } pattern, _ := args[0].AsString() text, _ := args[1].AsString() flags, err := getFlags(args, kwargs, 2) if err != nil { return errors.NewError("%s", err.Error()) } pattern = applyFlags(pattern, flags) re, err := GetCompiledRegex(pattern) if err != nil { return errors.NewError("regex compile error: %s", err.Error()) } allMatches := re.FindAllStringSubmatchIndex(text, -1) elements := make([]object.Object, len(allMatches)) for i, match := range allMatches { elements[i] = createMatchInstance(groupsFromIndices(text, match), match[0], match[1], re.SubexpNames()) } return &object.List{Elements: elements} }, HelpText: `finditer(pattern, string, flags=0) - Find all matches as Match objects Returns a list of Match objects for all matches of the regex pattern in the string. Flags: re.IGNORECASE or re.I - Case-insensitive matching re.MULTILINE or re.M - ^ and $ match at line boundaries re.DOTALL or re.S - . matches newlines`, }, "sub": { Fn: func(ctx context.Context, kwargs object.Kwargs, args ...object.Object) object.Object { if len(args) < 3 || len(args) > 5 { return errors.NewError("sub() takes 3 to 5 arguments (%d given)", len(args)) } if args[0].Type() != object.STRING_OBJ { return errors.NewTypeError("STRING", args[0].Type().String()) } if args[2].Type() != object.STRING_OBJ { return errors.NewTypeError("STRING", args[2].Type().String()) } pattern, _ := args[0].AsString() text, _ := args[2].AsString() isFunc := false var replacementStr string var replacementFunc object.Object switch args[1].Type() { case object.STRING_OBJ: replacementStr, _ = args[1].AsString() case object.FUNCTION_OBJ, object.BUILTIN_OBJ, object.LAMBDA_OBJ: isFunc = true replacementFunc = args[1] default: return errors.NewTypeError("STRING or FUNCTION", args[1].Type().String()) } count := -1 countObj := object.Object(nil) if len(args) > 3 { countObj = args[3] } else if v := kwargs.Get("count"); v != nil { countObj = v } if countObj != nil { if countObj.Type() != object.INTEGER_OBJ { return errors.NewError("count must be an integer") } val, _ := countObj.AsInt() count = int(val) if count == 0 { count = -1 } } flags, err := getFlags(args, kwargs, 4) if err != nil { return errors.NewError("%s", err.Error()) } pattern = applyFlags(pattern, flags) re, err := GetCompiledRegex(pattern) if err != nil { return errors.NewError("regex compile error: %s", err.Error()) } var result string if isFunc { replaced := 0 allMatches := re.FindAllStringSubmatchIndex(text, -1) if len(allMatches) == 0 { return object.NewString(text) } // Build result by replacing matches var resultBuilder []byte lastEnd := 0 for _, match := range allMatches { if count > 0 && replaced >= count { break } resultBuilder = append(resultBuilder, text[lastEnd:match[0]]...) matchObj := createMatchInstance(groupsFromIndices(text, match), match[0], match[1], re.SubexpNames()) // Call the replacement function var resultObj object.Object if replacementFunc.Type() == object.BUILTIN_OBJ { builtin := replacementFunc.(*object.Builtin) resultObj = builtin.Fn(ctx, object.Kwargs{}, matchObj) } else { eval := evaliface.FromContext(ctx) if eval == nil { return errors.NewError("evaluator not available in context") } env := object.NewEnvironment() if ctxEnv, ok := ctx.Value("scriptling-env").(*object.Environment); ok { env = ctxEnv } resultObj = eval.CallObjectFunction(ctx, replacementFunc, []object.Object{matchObj}, nil, env) } if resultObj.Type() == object.ERROR_OBJ || resultObj.Type() == object.EXCEPTION_OBJ { return resultObj } if resultStr, err := resultObj.AsString(); err == nil { resultBuilder = append(resultBuilder, resultStr...) } else { resultBuilder = append(resultBuilder, text[match[0]:match[1]]...) } lastEnd = match[1] replaced++ } resultBuilder = append(resultBuilder, text[lastEnd:]...) result = string(resultBuilder) } else if count < 0 { result = re.ReplaceAllString(text, pythonReplToGo(replacementStr)) } else { replaced := 0 result = re.ReplaceAllStringFunc(text, func(match string) string { if replaced < count { replaced++ return re.ReplaceAllString(match, pythonReplToGo(replacementStr)) } return match }) } return object.NewString(result) }, HelpText: `sub(pattern, repl, string, count=0, flags=0) - Replace matches Replaces occurrences of the regex pattern in the string with the replacement. The replacement can be either a string or a function that takes a Match object and returns a string. If count is 0 (default), all occurrences are replaced. If count > 0, only the first count occurrences are replaced. Flags: re.IGNORECASE or re.I - Case-insensitive matching re.MULTILINE or re.M - ^ and $ match at line boundaries re.DOTALL or re.S - . matches newlines`, }, "subn": { Fn: func(ctx context.Context, kwargs object.Kwargs, args ...object.Object) object.Object { result := reSubBuiltin.Fn(ctx, kwargs, args...) if object.IsError(result) || result.Type() == object.EXCEPTION_OBJ { return result } newText, _ := result.(*object.String) pattern, _ := args[0].AsString() text, _ := args[2].AsString() count := int64(-1) countObj := object.Object(nil) if len(args) > 3 { countObj = args[3] } else if v := kwargs.Get("count"); v != nil { countObj = v } if countObj != nil { if v, err := countObj.AsInt(); err == nil { count = v } } if flags, err := getFlags(args, kwargs, 4); err == nil { pattern = applyFlags(pattern, flags) } re, err := GetCompiledRegex(pattern) if err != nil { return errors.NewError("regex compile error: %s", err.Error()) } all := re.FindAllStringIndex(text, int(count)) return &object.Tuple{Elements: []object.Object{newText, object.NewInteger(int64(len(all)))}} }, HelpText: `subn(pattern, repl, string, count=0) - Replace matches, returning (new_string, n) Like sub(), but returns a tuple of the modified string and the number of substitutions made.`, }, "split": { Fn: func(ctx context.Context, kwargs object.Kwargs, args ...object.Object) object.Object { if len(args) < 2 || len(args) > 4 { return errors.NewError("split() takes 2 to 4 arguments (%d given)", len(args)) } if args[0].Type() != object.STRING_OBJ || args[1].Type() != object.STRING_OBJ { return errors.NewTypeError("STRING", "mixed types") } pattern, _ := args[0].AsString() text, _ := args[1].AsString() maxsplit := -1 maxObj := object.Object(nil) if len(args) > 2 { maxObj = args[2] } else if v := kwargs.Get("maxsplit"); v != nil { maxObj = v } if maxObj != nil { if maxObj.Type() != object.INTEGER_OBJ { return errors.NewError("maxsplit must be an integer") } val, _ := maxObj.AsInt() maxsplit = int(val) if maxsplit == 0 { maxsplit = -1 } } flags, err := getFlags(args, kwargs, 3) if err != nil { return errors.NewError("%s", err.Error()) } pattern = applyFlags(pattern, flags) re, err := GetCompiledRegex(pattern) if err != nil { return errors.NewError("regex compile error: %s", err.Error()) } splitN := maxsplit if maxsplit > 0 { splitN = maxsplit + 1 } parts := re.Split(text, splitN) elements := make([]object.Object, len(parts)) for i, part := range parts { elements[i] = object.NewString(part) } return &object.List{Elements: elements} }, HelpText: `split(pattern, string, maxsplit=0, flags=0) - Split string by pattern Splits the string by occurrences of the regex pattern and returns a list of substrings. If maxsplit is 0 (default), all occurrences are split. If maxsplit > 0, at most maxsplit splits are done. Flags: re.IGNORECASE or re.I - Case-insensitive matching re.MULTILINE or re.M - ^ and $ match at line boundaries re.DOTALL or re.S - . matches newlines`, }, "compile": { Fn: func(ctx context.Context, kwargs object.Kwargs, args ...object.Object) object.Object { if len(args) < 1 || len(args) > 2 { return errors.NewError("compile() takes 1 or 2 arguments (%d given)", len(args)) } if args[0].Type() != object.STRING_OBJ { return errors.NewTypeError("STRING", args[0].Type().String()) } pattern, _ := args[0].AsString() flags, err := getFlags(args, kwargs, 1) if err != nil { return errors.NewError("%s", err.Error()) } pattern = applyFlags(pattern, flags) _, err = GetCompiledRegex(pattern) if err != nil { return errors.NewError("regex compile error: %s", err.Error()) } return createRegexInstance(pattern, flags) }, HelpText: `compile(pattern, flags=0) - Compile regex pattern Validates and caches a regex pattern for later use. Returns the pattern if valid. Flags: re.IGNORECASE or re.I - Case-insensitive matching re.MULTILINE or re.M - ^ and $ match at line boundaries re.DOTALL or re.S - . matches newlines`, }, "escape": { Fn: func(ctx context.Context, kwargs object.Kwargs, args ...object.Object) object.Object { if err := errors.ExactArgs(args, 1); err != nil { return err } if args[0].Type() != object.STRING_OBJ { return errors.NewTypeError("STRING", args[0].Type().String()) } text, _ := args[0].AsString() escaped := regexp.QuoteMeta(text) return object.NewString(escaped) }, HelpText: `escape(pattern) - Escape special regex characters Returns a string with all special regex characters escaped.`, }, "fullmatch": { Fn: func(ctx context.Context, kwargs object.Kwargs, args ...object.Object) object.Object { if len(args) < 2 || len(args) > 3 { return errors.NewError("fullmatch() takes 2 or 3 arguments (%d given)", len(args)) } if args[0].Type() != object.STRING_OBJ || args[1].Type() != object.STRING_OBJ { return errors.NewTypeError("STRING", "mixed types") } pattern, _ := args[0].AsString() text, _ := args[1].AsString() flags, err := getFlags(args, kwargs, 2) if err != nil { return errors.NewError("%s", err.Error()) } pattern = applyFlags(pattern, flags) re, err := GetCompiledRegex(pattern) if err != nil { return errors.NewError("regex compile error: %s", err.Error()) } match := re.FindStringSubmatchIndex(text) if match == nil || match[0] != 0 || match[1] != len(text) { return &object.Null{} } groups := make([]string, 0) for i := 0; i < len(match); i += 2 { if match[i] >= 0 && match[i+1] >= 0 { groups = append(groups, text[match[i]:match[i+1]]) } else { groups = append(groups, "") } } return createMatchInstance(groups, match[0], match[1], re.SubexpNames()) }, HelpText: `fullmatch(pattern, string, flags=0) - Match entire string Returns a Match object if the regex pattern matches the entire string, or None if no match. Flags: re.IGNORECASE or re.I - Case-insensitive matching re.MULTILINE or re.M - ^ and $ match at line boundaries re.DOTALL or re.S - . matches newlines`, }, }, map[string]object.Object{ "IGNORECASE": object.NewInteger(RE_IGNORECASE), "I": object.NewInteger(RE_IGNORECASE), "MULTILINE": object.NewInteger(RE_MULTILINE), "M": object.NewInteger(RE_MULTILINE), "DOTALL": object.NewInteger(RE_DOTALL), "S": object.NewInteger(RE_DOTALL), }, "Regular expression library")
var RegexClass = &object.Class{ Name: "Regex", Methods: map[string]object.Object{ "match": &object.Builtin{ Fn: func(ctx context.Context, kwargs object.Kwargs, args ...object.Object) object.Object { if err := errors.ExactArgs(args, 2); err != nil { return err } if args[1].Type() != object.STRING_OBJ { return errors.NewTypeError("STRING", args[1].Type().String()) } regex := args[0].(*object.Instance) pattern := regex.Field("pattern").(*object.String).StringValue() text, _ := args[1].AsString() re, err := GetCompiledRegex(pattern) if err != nil { return errors.NewError("regex compile error: %s", err.Error()) } match := re.FindStringSubmatchIndex(text) if match == nil || match[0] != 0 { return &object.Null{} } return createMatchInstance(groupsFromIndices(text, match), match[0], match[1], re.SubexpNames()) }, HelpText: `match(string) - Match pattern at start of string Returns a Match object if the pattern matches at the beginning, or None if no match.`, }, "search": &object.Builtin{ Fn: func(ctx context.Context, kwargs object.Kwargs, args ...object.Object) object.Object { if err := errors.ExactArgs(args, 2); err != nil { return err } if args[1].Type() != object.STRING_OBJ { return errors.NewTypeError("STRING", args[1].Type().String()) } regex := args[0].(*object.Instance) pattern := regex.Field("pattern").(*object.String).StringValue() text, _ := args[1].AsString() re, err := GetCompiledRegex(pattern) if err != nil { return errors.NewError("regex compile error: %s", err.Error()) } match := re.FindStringSubmatchIndex(text) if match == nil { return &object.Null{} } return createMatchInstance(groupsFromIndices(text, match), match[0], match[1], re.SubexpNames()) }, HelpText: `search(string) - Search for pattern anywhere in string Returns a Match object for the first match, or None if no match found.`, }, "findall": &object.Builtin{ Fn: func(ctx context.Context, kwargs object.Kwargs, args ...object.Object) object.Object { if err := errors.ExactArgs(args, 2); err != nil { return err } if args[1].Type() != object.STRING_OBJ { return errors.NewTypeError("STRING", args[1].Type().String()) } regex := args[0].(*object.Instance) pattern := regex.Field("pattern").(*object.String).StringValue() text, _ := args[1].AsString() re, err := GetCompiledRegex(pattern) if err != nil { return errors.NewError("regex compile error: %s", err.Error()) } numGroups := re.NumSubexp() if numGroups == 0 { matches := re.FindAllString(text, -1) elements := make([]object.Object, len(matches)) for i, match := range matches { elements[i] = object.NewString(match) } return &object.List{Elements: elements} } matches := re.FindAllStringSubmatchIndex(text, -1) elements := make([]object.Object, len(matches)) for i, match := range matches { if numGroups == 1 { start, end := match[2], match[3] if start >= 0 && end >= 0 { elements[i] = object.NewString(text[start:end]) } else { elements[i] = object.NewString("") } continue } groupElements := make([]object.Object, numGroups) for j := 0; j < numGroups; j++ { start, end := match[(j+1)*2], match[(j+1)*2+1] if start >= 0 && end >= 0 { groupElements[j] = object.NewString(text[start:end]) } else { groupElements[j] = object.NewString("") } } elements[i] = &object.Tuple{Elements: groupElements} } return &object.List{Elements: elements} }, HelpText: `findall(string) - Find all matches Returns a list of all substrings that match the regex pattern. If the pattern contains capturing groups, returns a list of tuples containing the groups. If there is one capturing group, returns a list of strings for that group. If there are no capturing groups, returns a list of strings for the full matches.`, }, "finditer": &object.Builtin{ Fn: func(ctx context.Context, kwargs object.Kwargs, args ...object.Object) object.Object { if err := errors.ExactArgs(args, 2); err != nil { return err } if args[1].Type() != object.STRING_OBJ { return errors.NewTypeError("STRING", args[1].Type().String()) } regex := args[0].(*object.Instance) pattern := regex.Field("pattern").(*object.String).StringValue() text, _ := args[1].AsString() re, err := GetCompiledRegex(pattern) if err != nil { return errors.NewError("regex compile error: %s", err.Error()) } allMatches := re.FindAllStringSubmatchIndex(text, -1) elements := make([]object.Object, len(allMatches)) for i, match := range allMatches { groups := make([]string, 0) for j := 0; j < len(match); j += 2 { if match[j] >= 0 && match[j+1] >= 0 { groups = append(groups, text[match[j]:match[j+1]]) } else { groups = append(groups, "") } } elements[i] = createMatchInstance(groups, match[0], match[1], re.SubexpNames()) } return &object.List{Elements: elements} }, HelpText: `finditer(string) - Find all matches as Match objects Returns a list of Match objects for all matches of the regex pattern in the string.`, }, }, }
Regex class definition
var StatisticsLibrary = object.NewLibrary(StatisticsLibraryName, map[string]*object.Builtin{ "mean": { Fn: func(ctx context.Context, kwargs object.Kwargs, args ...object.Object) object.Object { if err := errors.ExactArgs(args, 1); err != nil { return err } values, err := extractNumbers(args[0]) if err != nil { return err } if len(values) == 0 { return errors.NewError("mean requires at least one data point") } sum := 0.0 for _, v := range values { sum += v } return object.NewFloat(sum / float64(len(values))) }, HelpText: `mean(data) - Return the arithmetic mean of data Parameters: data - List of numbers Returns: Float Example: import statistics statistics.mean([1, 2, 3, 4, 5]) # 3.0`, }, "fmean": { Fn: func(ctx context.Context, kwargs object.Kwargs, args ...object.Object) object.Object { if err := errors.ExactArgs(args, 1); err != nil { return err } values, err := extractNumbers(args[0]) if err != nil { return err } if len(values) == 0 { return errors.NewError("fmean requires at least one data point") } sum := 0.0 for _, v := range values { sum += v } return object.NewFloat(sum / float64(len(values))) }, HelpText: `fmean(data) - Return the arithmetic mean of data (faster float version) Parameters: data - List of numbers Returns: Float Example: import statistics statistics.fmean([1.0, 2.0, 3.0]) # 2.0`, }, "geometric_mean": { Fn: func(ctx context.Context, kwargs object.Kwargs, args ...object.Object) object.Object { if err := errors.ExactArgs(args, 1); err != nil { return err } values, err := extractNumbers(args[0]) if err != nil { return err } if len(values) == 0 { return errors.NewError("geometric_mean requires at least one data point") } logSum := 0.0 for _, v := range values { if v <= 0 { return errors.NewError("geometric_mean requires positive numbers") } logSum += math.Log(v) } return object.NewFloat(math.Exp(logSum / float64(len(values)))) }, HelpText: `geometric_mean(data) - Return the geometric mean of data Parameters: data - List of positive numbers Returns: Float Example: import statistics statistics.geometric_mean([1, 2, 4]) # 2.0`, }, "harmonic_mean": { Fn: func(ctx context.Context, kwargs object.Kwargs, args ...object.Object) object.Object { if err := errors.ExactArgs(args, 1); err != nil { return err } values, err := extractNumbers(args[0]) if err != nil { return err } if len(values) == 0 { return errors.NewError("harmonic_mean requires at least one data point") } reciprocalSum := 0.0 for _, v := range values { if v <= 0 { return errors.NewError("harmonic_mean requires positive numbers") } reciprocalSum += 1.0 / v } return object.NewFloat(float64(len(values)) / reciprocalSum) }, HelpText: `harmonic_mean(data) - Return the harmonic mean of data Parameters: data - List of positive numbers Returns: Float Example: import statistics statistics.harmonic_mean([1, 2, 4]) # ~1.714`, }, "median": { Fn: func(ctx context.Context, kwargs object.Kwargs, args ...object.Object) object.Object { if err := errors.ExactArgs(args, 1); err != nil { return err } values, err := extractNumbers(args[0]) if err != nil { return err } if len(values) == 0 { return errors.NewError("median requires at least one data point") } sorted := make([]float64, len(values)) copy(sorted, values) sort.Float64s(sorted) n := len(sorted) if n%2 == 0 { return object.NewFloat((sorted[n/2-1] + sorted[n/2]) / 2) } if list, ok := args[0].(*object.List); ok { ordered := make([]object.Object, len(list.Elements)) copy(ordered, list.Elements) sort.SliceStable(ordered, func(i, j int) bool { return numericOf(ordered[i]) < numericOf(ordered[j]) }) if mid, ok := ordered[n/2].(*object.Integer); ok { return mid } } return object.NewFloat(sorted[n/2]) }, HelpText: `median(data) - Return the median (middle value) of data Parameters: data - List of numbers Returns: Float Example: import statistics statistics.median([1, 3, 5]) # 3.0 statistics.median([1, 3, 5, 7]) # 4.0`, }, "mode": { Fn: func(ctx context.Context, kwargs object.Kwargs, args ...object.Object) object.Object { if err := errors.ExactArgs(args, 1); err != nil { return err } list, ok := args[0].(*object.List) if !ok { return errors.NewTypeError("LIST", args[0].Type().String()) } if len(list.Elements) == 0 { return errors.NewError("mode requires at least one data point") } counts := make(map[string]int) elements := make(map[string]object.Object) for _, elem := range list.Elements { key := elem.Inspect() counts[key]++ elements[key] = elem } maxCount := 0 var modeKey string for key, count := range counts { if count > maxCount { maxCount = count modeKey = key } } return elements[modeKey] }, HelpText: `mode(data) - Return the most common value in data Parameters: data - List of values Returns: Most frequent value (same type as input elements) Example: import statistics statistics.mode([1, 1, 2, 3]) # 1 statistics.mode(["a", "b", "a"]) # "a"`, }, "stdev": { Fn: func(ctx context.Context, kwargs object.Kwargs, args ...object.Object) object.Object { if err := errors.ExactArgs(args, 1); err != nil { return err } values, err := extractNumbers(args[0]) if err != nil { return err } if len(values) < 2 { return errors.NewError("stdev requires at least two data points") } variance := sampleVariance(values) return object.NewFloat(math.Sqrt(variance)) }, HelpText: `stdev(data) - Return the sample standard deviation of data Parameters: data - List of numbers (at least 2) Returns: Float Example: import statistics statistics.stdev([1, 2, 3, 4, 5]) # ~1.58`, }, "pstdev": { Fn: func(ctx context.Context, kwargs object.Kwargs, args ...object.Object) object.Object { if err := errors.ExactArgs(args, 1); err != nil { return err } values, err := extractNumbers(args[0]) if err != nil { return err } if len(values) < 1 { return errors.NewError("pstdev requires at least one data point") } variance := populationVariance(values) return object.NewFloat(math.Sqrt(variance)) }, HelpText: `pstdev(data) - Return the population standard deviation of data Parameters: data - List of numbers Returns: Float Example: import statistics statistics.pstdev([1, 2, 3, 4, 5]) # ~1.41`, }, "variance": { Fn: func(ctx context.Context, kwargs object.Kwargs, args ...object.Object) object.Object { if err := errors.ExactArgs(args, 1); err != nil { return err } values, err := extractNumbers(args[0]) if err != nil { return err } if len(values) < 2 { return errors.NewError("variance requires at least two data points") } return object.NewFloat(sampleVariance(values)) }, HelpText: `variance(data) - Return the sample variance of data Parameters: data - List of numbers (at least 2) Returns: Float Example: import statistics statistics.variance([1, 2, 3, 4, 5]) # 2.5`, }, "pvariance": { Fn: func(ctx context.Context, kwargs object.Kwargs, args ...object.Object) object.Object { if err := errors.ExactArgs(args, 1); err != nil { return err } values, err := extractNumbers(args[0]) if err != nil { return err } if len(values) < 1 { return errors.NewError("pvariance requires at least one data point") } return object.NewFloat(populationVariance(values)) }, HelpText: `pvariance(data) - Return the population variance of data Parameters: data - List of numbers Returns: Float Example: import statistics statistics.pvariance([1, 2, 3, 4, 5]) # 2.0`, }, }, nil, "Statistical functions library")
var StringLibrary = object.NewLibrary(StringLibraryName, nil, map[string]object.Object{ "ascii_letters": object.NewString(asciiLetters), "ascii_lowercase": object.NewString(asciiLowercase), "ascii_uppercase": object.NewString(asciiUppercase), "digits": object.NewString(digits), "hexdigits": object.NewString(hexdigits), "octdigits": object.NewString(octdigits), "punctuation": object.NewString(punctuation), "whitespace": object.NewString(whitespace), "printable": object.NewString(printable), }, "String constants for character classification", )
var TextwrapLibrary = object.NewLibrary(TextwrapLibraryName, map[string]*object.Builtin{ "wrap": { Fn: func(ctx context.Context, kwargs object.Kwargs, args ...object.Object) object.Object { text, w, err := textwrapArgs("wrap", args, kwargs) if err != nil { return err } lines, err := w.wrap(text) if err != nil { return err } elements := make([]object.Object, len(lines)) for i, line := range lines { elements[i] = object.NewString(line) } return &object.List{Elements: elements} }, HelpText: `wrap(text, width=70, **kwargs) - Wrap a single paragraph of text Parameters (as Python's textwrap.TextWrapper): text - Text to wrap width - Maximum line width (default 70) initial_indent - String prepended to the first line (default "") subsequent_indent - String prepended to all other lines (default "") expand_tabs - Expand tabs to spaces first (default True) tabsize - Tab size used by expand_tabs (default 8) replace_whitespace - Replace each whitespace char with a space (default True) fix_sentence_endings - Two spaces after sentence endings (default False) break_long_words - Break words longer than width (default True) drop_whitespace - Drop whitespace at line starts/ends (default True) break_on_hyphens - Allow breaks after hyphens in words (default True) max_lines - Truncate output to at most this many lines (default None) placeholder - Appended to truncated output (default " [...]") Returns: List of lines Example: import textwrap lines = textwrap.wrap("Hello world, this is a long line", 10)`, }, "fill": { Fn: func(ctx context.Context, kwargs object.Kwargs, args ...object.Object) object.Object { text, w, err := textwrapArgs("fill", args, kwargs) if err != nil { return err } lines, err := w.wrap(text) if err != nil { return err } return object.NewString(strings.Join(lines, "\n")) }, HelpText: `fill(text, width=70, **kwargs) - Wrap text and return a single string Accepts the same keyword options as wrap(). Returns: Wrapped text as single string with newlines Example: import textwrap result = textwrap.fill("Hello world, this is a long line", 10)`, }, "dedent": { Fn: func(ctx context.Context, kwargs object.Kwargs, args ...object.Object) object.Object { if len(args) != 1 { return errors.NewError("dedent() requires exactly 1 argument") } text, ok := args[0].(*object.String) if !ok { return errors.NewTypeError("STRING", args[0].Type().String()) } return object.NewString(dedentText(text.StringValue())) }, HelpText: `dedent(text) - Remove common leading whitespace from all lines Parameters: text - Text to dedent Returns: Dedented text Example: import textwrap text = """ Hello World """ result = textwrap.dedent(text) # Lines start at column 0`, }, "indent": { Fn: func(ctx context.Context, kwargs object.Kwargs, args ...object.Object) object.Object { if len(args) < 2 { return errors.NewError("indent() requires at least 2 arguments") } text, ok := args[0].(*object.String) if !ok { return errors.NewTypeError("STRING", args[0].Type().String()) } prefix, ok := args[1].(*object.String) if !ok { return errors.NewTypeError("STRING", args[1].Type().String()) } for _, k := range kwargs.Keys() { if k != "predicate" { return newArgTypeError("indent() got an unexpected keyword argument '%s'", k) } } var predicate object.Object if len(args) >= 3 { predicate = args[2] } else if p := kwargs.Get("predicate"); p != nil { predicate = p } if _, isNull := predicate.(*object.Null); isNull { predicate = nil } // Split keeping line endings, like str.splitlines(True). var b strings.Builder for _, line := range strings.SplitAfter(text.StringValue(), "\n") { if line == "" { continue } add := false if predicate != nil { res := callCallable(ctx, predicate, object.NewString(line)) if object.IsError(res) || res.Type() == object.EXCEPTION_OBJ { return res } add = isTruthy(res) } else { add = strings.TrimSpace(line) != "" } if add { b.WriteString(prefix.StringValue()) } b.WriteString(line) } return object.NewString(b.String()) }, HelpText: `indent(text, prefix, predicate=None) - Add prefix to lines Parameters: text - Text to indent prefix - String to add to beginning of each line predicate - Optional callable; only lines for which it returns true are indented (default: lines that are not whitespace-only) Returns: Indented text Example: import textwrap result = textwrap.indent("Hello\nWorld", " ") # " Hello\n World"`, }, "shorten": { Fn: func(ctx context.Context, kwargs object.Kwargs, args ...object.Object) object.Object { if kwargs.Has("max_lines") { return newArgTypeError("shorten() got multiple values for keyword argument 'max_lines'") } if len(args) < 2 && !kwargs.Has("width") { return errors.NewError("shorten() missing required argument: 'width'") } text, w, err := textwrapArgs("shorten", args, kwargs) if err != nil { return err } w.maxLines = 1 lines, err := w.wrap(strings.Join(strings.Fields(text), " ")) if err != nil { return err } return object.NewString(strings.Join(lines, "\n")) }, HelpText: `shorten(text, width, **kwargs) - Collapse and truncate text to fit width Parameters: text - Text to shorten width - Maximum width including placeholder placeholder - String to indicate truncation (default " [...]") (other wrap() keyword options are also accepted) Returns: Shortened text Example: import textwrap result = textwrap.shorten("Hello World!", 11) # "Hello [...]"`, }, }, nil, "Text wrapping and filling utilities")
var TimeLibrary = object.NewLibrary(TimeLibraryName, map[string]*object.Builtin{ "now": { Fn: func(ctx context.Context, kwargs object.Kwargs, args ...object.Object) object.Object { return object.NewString(time.Now().Format("2006-01-02T15:04:05.999999")) }, HelpText: `now() - Return current date and time Returns the current date and time as an ISO 8601 formatted string (YYYY-MM-DDTHH:MM:SS.ffffff).`, }, "time": { Fn: func(ctx context.Context, kwargs object.Kwargs, args ...object.Object) object.Object { return object.NewFloat(float64(time.Now().UnixNano()) / 1e9) }, HelpText: `time() - Return current time in seconds Returns the current time as a floating point number of seconds since the Unix epoch.`, }, "perf_counter": { Fn: func(ctx context.Context, kwargs object.Kwargs, args ...object.Object) object.Object { return object.NewFloat(time.Since(startTime).Seconds()) }, HelpText: `perf_counter() - Return performance counter Returns the value of a performance counter in fractional seconds.`, }, "sleep": { Fn: func(ctx context.Context, kwargs object.Kwargs, args ...object.Object) object.Object { if err := errors.ExactArgs(args, 1); err != nil { return err } seconds, err := args[0].AsFloat() if err != nil { return errors.ParameterError("seconds", err) } timer := time.NewTimer(time.Duration(seconds * float64(time.Second))) defer timer.Stop() // Release the interpreter lock while blocked so other goroutines // (e.g. runtime.background shared threads) can run script code. var result object.Object = &object.Null{} object.RunBlocking(ctx, func() { select { case <-ctx.Done(): if ctx.Err() == context.DeadlineExceeded { result = errors.NewTimeoutError() } else { result = errors.NewCancelledError() } case <-timer.C: } }) return result }, HelpText: `sleep(seconds) - Sleep for specified seconds Suspends execution for the given number of seconds.`, }, "localtime": { Fn: func(ctx context.Context, kwargs object.Kwargs, args ...object.Object) object.Object { var t time.Time if len(args) == 0 { t = time.Now() } else if len(args) == 1 { if ts, err := args[0].AsFloat(); err == nil { t = time.Unix(int64(ts), 0) } else if instance, ok := args[0].(*object.Instance); ok { if dt, err := GetTimeFromObject(instance); err == nil { t = dt } else { return errors.NewTypeError("INTEGER, FLOAT, or datetime instance", args[0].Type().String()) } } else { return errors.NewTypeError("INTEGER, FLOAT, or datetime instance", args[0].Type().String()) } } else { if err := errors.MaxArgs(args, 1); err != nil { return err } } return timeToTuple(t, false) }, HelpText: `localtime([timestamp_or_datetime]) - Convert to local time tuple Returns a time tuple in local time. If timestamp/datetime is omitted, uses current time.`, }, "gmtime": { Fn: func(ctx context.Context, kwargs object.Kwargs, args ...object.Object) object.Object { var t time.Time if len(args) == 0 { t = time.Now() } else if len(args) == 1 { if ts, err := args[0].AsFloat(); err == nil { t = time.Unix(int64(ts), 0) } else if instance, ok := args[0].(*object.Instance); ok { if dt, err := GetTimeFromObject(instance); err == nil { t = dt } else { return errors.NewTypeError("INTEGER, FLOAT, or datetime instance", args[0].Type().String()) } } else { return errors.NewTypeError("INTEGER, FLOAT, or datetime instance", args[0].Type().String()) } } else { if err := errors.MaxArgs(args, 1); err != nil { return err } } return timeToTuple(t, true) }, HelpText: `gmtime([timestamp_or_datetime]) - Convert to UTC time tuple Returns a time tuple in UTC. If timestamp/datetime is omitted, uses current time.`, }, "mktime": { Fn: func(ctx context.Context, kwargs object.Kwargs, args ...object.Object) object.Object { if err := errors.ExactArgs(args, 1); err != nil { return err } tuple, err := args[0].AsList() if err != nil { return err } if len(tuple) != 9 { return errors.NewError("time tuple must have exactly 9 elements") } year, _ := tuple[0].AsInt() month, _ := tuple[1].AsInt() day, _ := tuple[2].AsInt() hour, _ := tuple[3].AsInt() minute, _ := tuple[4].AsInt() second, _ := tuple[5].AsInt() t := time.Date(int(year), time.Month(month), int(day), int(hour), int(minute), int(second), 0, time.Local) return object.NewFloat(float64(t.Unix())) }, HelpText: `mktime(tuple) - Convert time tuple to timestamp Converts a time tuple (9 elements) to a Unix timestamp.`, }, "strftime": { Fn: func(ctx context.Context, kwargs object.Kwargs, args ...object.Object) object.Object { if err := errors.RangeArgs(args, 1, 2); err != nil { return err } format, err := args[0].AsString() if err != nil { return err } var t time.Time if len(args) == 1 { t = time.Now() } else { tuple, err := args[1].AsList() if err != nil { return err } if len(tuple) != 9 { return errors.NewError("time tuple must have exactly 9 elements") } year, _ := tuple[0].AsInt() month, _ := tuple[1].AsInt() day, _ := tuple[2].AsInt() hour, _ := tuple[3].AsInt() minute, _ := tuple[4].AsInt() second, _ := tuple[5].AsInt() t = time.Date(int(year), time.Month(month), int(day), int(hour), int(minute), int(second), 0, time.Local) } return object.NewString(t.Format(pythonToGoFormat(format))) }, HelpText: `strftime(format[, tuple]) - Format time as string Formats a time according to the given format string. If tuple is omitted, uses current time.`, }, "strptime": { Fn: func(ctx context.Context, kwargs object.Kwargs, args ...object.Object) object.Object { if err := errors.ExactArgs(args, 2); err != nil { return err } str, err := args[0].AsString() if err != nil { return err } format, err := args[1].AsString() if err != nil { return err } t, parseErr := time.Parse(pythonToGoFormat(format), str) if parseErr != nil { return errors.NewError("strptime() parse error: %s", parseErr.Error()) } return timeToTuple(t, false) }, HelpText: `strptime(string, format) - Parse time from string Parses a time string according to the given format and returns a time tuple.`, }, "asctime": { Fn: func(ctx context.Context, kwargs object.Kwargs, args ...object.Object) object.Object { var t time.Time if len(args) == 0 { t = time.Now() } else if len(args) == 1 { tuple, err := args[0].AsList() if err != nil { return err } if len(tuple) != 9 { return errors.NewError("time tuple must have exactly 9 elements") } year, _ := tuple[0].AsInt() month, _ := tuple[1].AsInt() day, _ := tuple[2].AsInt() hour, _ := tuple[3].AsInt() minute, _ := tuple[4].AsInt() second, _ := tuple[5].AsInt() t = time.Date(int(year), time.Month(month), int(day), int(hour), int(minute), int(second), 0, time.Local) } else { if err := errors.MaxArgs(args, 1); err != nil { return err } } return object.NewString(t.Format("Mon Jan 2 15:04:05 2006")) }, HelpText: `asctime([tuple]) - Convert time tuple to string Converts a time tuple to a string in the format 'Mon Jan 2 15:04:05 2006'. If tuple is omitted, uses current time.`, }, "ctime": { Fn: func(ctx context.Context, kwargs object.Kwargs, args ...object.Object) object.Object { var t time.Time if len(args) == 0 { t = time.Now() } else if len(args) == 1 { timestamp, err := args[0].AsFloat() if err != nil { return errors.ParameterError("timestamp", err) } t = time.Unix(int64(timestamp), 0) } else { if err := errors.MaxArgs(args, 1); err != nil { return err } } return object.NewString(t.Format("Mon Jan 2 15:04:05 2006")) }, HelpText: `ctime([timestamp]) - Convert timestamp to string Converts a Unix timestamp to a string in the format 'Mon Jan 2 15:04:05 2006'. If timestamp is omitted, uses current time.`, }, }, nil, "Time-related functions library")
var TimedeltaClass = &object.Class{ Name: "timedelta", Methods: map[string]object.Object{ "total_seconds": &object.Builtin{ Fn: func(ctx context.Context, kwargs object.Kwargs, args ...object.Object) object.Object { if err := errors.MinArgs(args, 1); err != nil { return err } inst, ok := args[0].(*object.Instance) if !ok { return errors.NewTypeError("timedelta instance", args[0].Type().String()) } total, ok := inst.Field("_total").(*object.Float) if !ok { return errors.NewTypeError("timedelta instance", args[0].Type().String()) } return object.NewFloat(total.FloatValue()) }, HelpText: "total_seconds() - Return the total duration in seconds", }, "__str__": &object.Builtin{ Fn: func(ctx context.Context, kwargs object.Kwargs, args ...object.Object) object.Object { if err := errors.MinArgs(args, 1); err != nil { return err } inst, ok := args[0].(*object.Instance) if !ok { return errors.NewTypeError("timedelta instance", args[0].Type().String()) } if repr, ok := inst.Field("__str_repr__").(*object.String); ok { return repr } return object.NewString("") }, HelpText: "__str__() - Return the Python-style duration string", }, }, }
TimedeltaClass is the class behind datetime.timedelta values.
var URLLibLibrary = object.NewLibrary(URLLibLibraryName, nil, nil, "URL handling modules")
URLLibrary is the parent urllib module - registered so `import urllib` works
var URLParseLibrary = object.NewLibrary(URLParseLibraryName, map[string]*object.Builtin{ "quote": { Fn: func(ctx context.Context, kwargs object.Kwargs, args ...object.Object) object.Object { if err := errors.RangeArgs(args, 1, 2); err != nil { return err } str, err := args[0].AsString() if err != nil { return err } safe := "" if len(args) == 2 { safe, err = args[1].AsString() if err != nil { return err } } encoded := urlQuote(str, safe) return object.NewString(encoded) }, HelpText: `quote(string, safe='') - URL encode string Returns a URL-encoded version of the string. Characters in 'safe' are not encoded.`, }, "quote_plus": { Fn: func(ctx context.Context, kwargs object.Kwargs, args ...object.Object) object.Object { if err := errors.RangeArgs(args, 1, 2); err != nil { return err } str, err := args[0].AsString() if err != nil { return err } safe := "" if len(args) == 2 { safe, err = args[1].AsString() if err != nil { return err } } encoded := urlQuote(str, safe) encoded = strings.ReplaceAll(encoded, "%20", "+") return object.NewString(encoded) }, HelpText: `quote_plus(string, safe='') - URL encode string with + for spaces Like quote(), but also replaces spaces with plus signs.`, }, "unquote": { Fn: func(ctx context.Context, kwargs object.Kwargs, args ...object.Object) object.Object { if err := errors.ExactArgs(args, 1); err != nil { return err } str, err := args[0].AsString() if err != nil { return err } decoded, urlErr := url.PathUnescape(str) if urlErr != nil { return errors.NewError("unquote() invalid URL encoding") } return object.NewString(decoded) }, HelpText: `unquote(string) - URL decode string Returns a URL-decoded version of the string.`, }, "unquote_plus": { Fn: func(ctx context.Context, kwargs object.Kwargs, args ...object.Object) object.Object { if err := errors.ExactArgs(args, 1); err != nil { return err } str, err := args[0].AsString() if err != nil { return err } str = strings.ReplaceAll(str, "+", " ") decoded, urlErr := url.PathUnescape(str) if urlErr != nil { return errors.NewError("unquote_plus() invalid URL encoding") } return object.NewString(decoded) }, HelpText: `unquote_plus(string) - URL decode string with + as spaces Like unquote(), but also replaces plus signs with spaces.`, }, "urlparse": { Fn: func(ctx context.Context, kwargs object.Kwargs, args ...object.Object) object.Object { if err := errors.ExactArgs(args, 1); err != nil { return err } str, err := args[0].AsString() if err != nil { return err } u, urlErr := url.Parse(str) if urlErr != nil { return errors.NewError("urlparse() invalid URL") } netloc := u.Host if u.User != nil { if pwd, hasPwd := u.User.Password(); hasPwd { netloc = u.User.Username() + ":" + pwd + "@" + u.Host } else { netloc = u.User.Username() + "@" + u.Host } } return createParseResultInstance(u.Scheme, netloc, u.Path, "", u.RawQuery, u.Fragment) }, HelpText: `urlparse(urlstring) - Parse URL into components Returns a ParseResult object with URL components: scheme, netloc, path, params, query, fragment. Access components as attributes: result.scheme, result.netloc, etc. Use result.geturl() to reconstruct URL.`, }, "urlunparse": { Fn: func(ctx context.Context, kwargs object.Kwargs, args ...object.Object) object.Object { if err := errors.ExactArgs(args, 1); err != nil { return err } switch arg := args[0].(type) { case *object.Instance: if arg.Class == ParseResultClass { u := &url.URL{} if value, ok := arg.GetField("scheme"); ok { if str, err := value.AsString(); err == nil { u.Scheme = str } } if value, ok := arg.GetField("netloc"); ok { if str, err := value.AsString(); err == nil { u.Host = str } } if value, ok := arg.GetField("path"); ok { if str, err := value.AsString(); err == nil { u.Path = str } } if value, ok := arg.GetField("query"); ok { if str, err := value.AsString(); err == nil { u.RawQuery = str } } if value, ok := arg.GetField("fragment"); ok { if str, err := value.AsString(); err == nil { u.Fragment = str } } return object.NewString(u.String()) } return errors.NewTypeError("DICT, LIST, TUPLE, or ParseResult", args[0].Type().String()) case *object.Dict: u := &url.URL{} if value, ok := arg.GetByString("scheme"); ok { if str, err := value.Value.AsString(); err == nil { u.Scheme = str } } if value, ok := arg.GetByString("netloc"); ok { if str, err := value.Value.AsString(); err == nil { u.Host = str } } if value, ok := arg.GetByString("path"); ok { if str, err := value.Value.AsString(); err == nil { u.Path = str } } if value, ok := arg.GetByString("query"); ok { if str, err := value.Value.AsString(); err == nil { u.RawQuery = str } } if value, ok := arg.GetByString("fragment"); ok { if str, err := value.Value.AsString(); err == nil { u.Fragment = str } } return object.NewString(u.String()) case *object.List: if len(arg.Elements) != 6 { return errors.NewError("urlunparse() requires exactly 6 elements") } components := make([]string, 6) for i, elem := range arg.Elements { str, err := elem.AsString() if err != nil { return err } components[i] = str } u := &url.URL{ Scheme: components[0], Host: components[1], Path: components[2], RawQuery: components[4], Fragment: components[5], } return object.NewString(u.String()) case *object.Tuple: if len(arg.Elements) != 6 { return errors.NewError("urlunparse() requires exactly 6 elements") } components := make([]string, 6) for i, elem := range arg.Elements { str, err := elem.AsString() if err != nil { return err } components[i] = str } u := &url.URL{ Scheme: components[0], Host: components[1], Path: components[2], RawQuery: components[4], Fragment: components[5], } return object.NewString(u.String()) default: return errors.NewTypeError("DICT, LIST, TUPLE, or ParseResult", args[0].Type().String()) } }, HelpText: `urlunparse(components) - Construct URL from components Constructs a URL string from a 6-tuple or dict of URL components.`, }, "urljoin": { Fn: func(ctx context.Context, kwargs object.Kwargs, args ...object.Object) object.Object { if err := errors.ExactArgs(args, 2); err != nil { return err } base, err := args[0].AsString() if err != nil { return err } ref, err := args[1].AsString() if err != nil { return err } baseURL, urlErr := url.Parse(base) if urlErr != nil { return errors.NewError("urljoin() invalid base URL") } refURL, urlErr := url.Parse(ref) if urlErr != nil { return errors.NewError("urljoin() invalid reference URL") } joined := baseURL.ResolveReference(refURL) return object.NewString(joined.String()) }, HelpText: `urljoin(base, url) - Join base URL with reference Joins a base URL with a reference URL, resolving relative references.`, }, "urlsplit": { Fn: func(ctx context.Context, kwargs object.Kwargs, args ...object.Object) object.Object { if err := errors.ExactArgs(args, 1); err != nil { return err } str, err := args[0].AsString() if err != nil { return err } u, urlErr := url.Parse(str) if urlErr != nil { return errors.NewError("urlsplit() invalid URL") } netloc := u.Host if u.User != nil { if pwd, hasPwd := u.User.Password(); hasPwd { netloc = u.User.Username() + ":" + pwd + "@" + u.Host } else { netloc = u.User.Username() + "@" + u.Host } } elements := []object.Object{ object.NewString(u.Scheme), object.NewString(netloc), object.NewString(u.Path), object.NewString(u.RawQuery), object.NewString(u.Fragment), } return &object.List{Elements: elements} }, HelpText: `urlsplit(urlstring) - Split URL into components Returns a 5-tuple: (scheme, netloc, path, query, fragment).`, }, "urlunsplit": { Fn: func(ctx context.Context, kwargs object.Kwargs, args ...object.Object) object.Object { if err := errors.ExactArgs(args, 1); err != nil { return err } elements, err := args[0].AsList() if err != nil { return errors.ParameterError("elements", err) } if len(elements) != 5 { return errors.NewError("urlunsplit() requires exactly 5 elements") } components := make([]string, 5) for i, elem := range elements { str, err := elem.AsString() if err != nil { return err } components[i] = str } u := &url.URL{ Scheme: components[0], Host: components[1], Path: components[2], RawQuery: components[3], Fragment: components[4], } return object.NewString(u.String()) }, HelpText: `urlunsplit(components) - Construct URL from component tuple Constructs a URL string from a 5-tuple of URL components.`, }, "parse_qs": { Fn: func(ctx context.Context, kwargs object.Kwargs, args ...object.Object) object.Object { if err := errors.RangeArgs(args, 1, 2); err != nil { return err } str, err := args[0].AsString() if err != nil { return err } values, urlErr := url.ParseQuery(str) if urlErr != nil { return errors.NewError("parse_qs() invalid query string") } pairs := make(map[string]object.DictPair) for key, vals := range values { elements := make([]object.Object, len(vals)) for i, val := range vals { elements[i] = object.NewString(val) } keyObj := object.NewString(key) pairs[object.DictKey(keyObj)] = object.DictPair{ Key: keyObj, Value: &object.List{Elements: elements}, } } return &object.Dict{Pairs: pairs} }, HelpText: `parse_qs(qs, keep_blank_values=False) - Parse query string Parses a URL query string and returns a dictionary where values are lists.`, }, "parse_qsl": { Fn: func(ctx context.Context, kwargs object.Kwargs, args ...object.Object) object.Object { if err := errors.RangeArgs(args, 1, 2); err != nil { return err } str, err := args[0].AsString() if err != nil { return err } values, urlErr := url.ParseQuery(str) if urlErr != nil { return errors.NewError("parse_qsl() invalid query string") } // Convert to list of (key, value) tuples var result []object.Object for key, vals := range values { for _, val := range vals { tuple := &object.Tuple{ Elements: []object.Object{ object.NewString(key), object.NewString(val), }, } result = append(result, tuple) } } return &object.List{Elements: result} }, HelpText: `parse_qsl(qs, keep_blank_values=False) - Parse query string as list Parses a URL query string and returns a list of (key, value) tuples.`, }, "urlencode": { Fn: func(ctx context.Context, kwargs object.Kwargs, args ...object.Object) object.Object { if err := errors.RangeArgs(args, 1, 2); err != nil { return err } values := make(url.Values) switch arg := args[0].(type) { case *object.Dict: for _, pair := range arg.Pairs { keyStr, keyErr := pair.Key.AsString() if keyErr != nil { continue } switch val := pair.Value.(type) { case *object.String: values[keyStr] = []string{val.StringValue()} case *object.List: strVals := make([]string, len(val.Elements)) for i, elem := range val.Elements { if str, err := elem.AsString(); err == nil { strVals[i] = str } } values[keyStr] = strVals } } case *object.List: for _, elem := range arg.Elements { if tuple, ok := elem.(*object.Tuple); ok && len(tuple.Elements) == 2 { if key, err := tuple.Elements[0].AsString(); err == nil { if val, err := tuple.Elements[1].AsString(); err == nil { values.Add(key, val) } } } } default: return errors.NewTypeError("DICT or LIST", args[0].Type().String()) } return object.NewString(values.Encode()) }, HelpText: `urlencode(query, doseq=False) - Encode dictionary as query string Encodes a dictionary or list of tuples into a URL query string.`, }, }, nil, "URL parsing and manipulation (urllib.parse compatible)")
URLParseLibrary implements Python's urllib.parse module
var UUIDLibrary = object.NewLibrary(UUIDLibraryName, map[string]*object.Builtin{ "uuid1": { Fn: func(ctx context.Context, kwargs object.Kwargs, args ...object.Object) object.Object { if err := errors.ExactArgs(args, 0); err != nil { return err } id, err := uuid.NewUUID() if err != nil { return errors.NewError("failed to generate UUID v1: %s", err.Error()) } return object.NewString(id.String()) }, HelpText: `uuid1() - Generate a UUID version 1 (time-based) Returns a UUID based on current time and MAC address. Format: xxxxxxxx-xxxx-1xxx-yxxx-xxxxxxxxxxxx Example: import uuid id = uuid.uuid1() print(id) # e.g., "f47ac10b-58cc-1e4c-a26f-e3fc32165abc"`, }, "uuid4": { Fn: func(ctx context.Context, kwargs object.Kwargs, args ...object.Object) object.Object { if err := errors.ExactArgs(args, 0); err != nil { return err } return object.NewString(uuid.New().String()) }, HelpText: `uuid4() - Generate a UUID version 4 (random) Returns a randomly generated UUID. Format: xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx Example: import uuid id = uuid.uuid4() print(id) # e.g., "550e8400-e29b-41d4-a716-446655440000"`, }, "uuid7": { Fn: func(ctx context.Context, kwargs object.Kwargs, args ...object.Object) object.Object { if err := errors.ExactArgs(args, 0); err != nil { return err } id, err := uuid.NewV7() if err != nil { return errors.NewError("failed to generate UUID v7: %s", err.Error()) } return object.NewString(id.String()) }, HelpText: `uuid7() - Generate a UUID version 7 (Unix timestamp-based, sortable) Returns a UUID based on Unix timestamp in milliseconds. UUIDs generated in sequence will sort in chronological order. Format: xxxxxxxx-xxxx-7xxx-yxxx-xxxxxxxxxxxx Example: import uuid id = uuid.uuid7() print(id) # e.g., "018f6b1c-4e5d-7abc-8def-0123456789ab"`, }, }, nil, "UUID generation library")
Functions ¶
func CompareDigest ¶ added in v0.14.0
CompareDigest performs a constant-time comparison of two strings. It is the single implementation shared by the hmac and secrets libraries (in CPython, secrets.compare_digest is hmac.compare_digest).
func GetCompiledRegex ¶
GetCompiledRegex retrieves a compiled regex from cache or compiles and caches it
func GetTimeFromObject ¶
GetTimeFromObject extracts time.Time from a datetime/date instance
func NewMsgpackLibrary ¶ added in v0.19.0
func NewMsgpackLibrary(c MsgpackCodec) *object.Library
NewMsgpackLibrary builds a msgpack library backed by the given codec. Pass nil to use the default (gossip's ShamatonMsgpackCodec — matching the gossip DefaultConfig). The returned library can be registered with a Scriptling instance via RegisterLibrary.
Embedders wanting a different codec can build their own library directly:
p.RegisterLibrary(stdlib.NewMsgpackLibrary(gossipvmihailenco.New()))
or, to swap the default that stdlib.RegisterAll picks up, reassign before calling RegisterAll:
stdlib.MsgpackLibrary = stdlib.NewMsgpackLibrary(myCodec)
func PythonToGoDateFormat ¶
PythonToGoDateFormat converts Python datetime format codes to Go format
func RegisterAll ¶
RegisterAll registers all standard libraries
Types ¶
type MsgpackCodec ¶ added in v0.19.0
type MsgpackCodec interface {
Name() string
Marshal(v interface{}) ([]byte, error)
Unmarshal(data []byte, v interface{}) error
}
MsgpackCodec is the interface a MessagePack (or binary-compatible) codec must satisfy. It is structurally identical to github.com/paularlott/gossip/codec.Serializer, so any gossip codec (ShamatonMsgpackCodec, VmihailencoMsgpackCodec, HashicorpMsgpackCodec) can be passed directly to NewMsgpackLibrary — letting a single driver instance be shared between Scriptling's msgpack library and a gossip cluster so both sides agree on the wire format.
Example — share one codec between gossip and Scriptling:
import gossipcodec "github.com/paularlott/gossip/codec" codec := gossipshamaton.New() // or Vmihailenco, etc. // gossip side gossipCfg := gossip.DefaultConfig() // already defaults to shamaton gossipCfg.MsgCodec = codec // scriptling side p.RegisterLibrary(stdlib.NewMsgpackLibrary(codec))
type StringIOWriter ¶ added in v0.2.0
type StringIOWriter struct {
// contains filtered or unexported fields
}
StringIOWriter implements io.Writer for a StringIO instance.
func GetStringIOWriter ¶ added in v0.2.0
func GetStringIOWriter(inst *object.Instance) (*StringIOWriter, bool)
GetStringIOWriter returns an io.Writer backed by a StringIO instance, for use with print(file=buf) in the evaluator.