ucl/ucl/objs.go

647 lines
12 KiB
Go
Raw Normal View History

2024-04-27 00:11:22 +00:00
package ucl
2024-04-10 10:45:58 +00:00
import (
"context"
"errors"
2024-04-10 12:19:11 +00:00
"fmt"
2024-04-24 11:29:26 +00:00
"reflect"
"strconv"
2024-09-06 10:55:19 +00:00
"strings"
"time"
2024-09-06 10:55:19 +00:00
"github.com/lmika/gopkgs/fp/slices"
)
type Object interface {
2024-04-11 12:05:05 +00:00
String() string
2024-04-13 11:46:50 +00:00
Truthy() bool
2024-04-10 12:19:11 +00:00
}
type Listable interface {
Len() int
Index(i int) Object
}
type Iterable interface {
HasNext() bool
// Next returns the next object from the iterable if one exists, otherwise
// returns nil, false.
Next(ctx context.Context) (Object, error)
}
type ModListable interface {
Listable
// Insert adds a new item to the list. idx can be a positive
// number from 0 to len(), in which case the object will be inserted
// at that position. If idx is negative, then the item will be inserted
// at that position from the right.
Insert(idx int, obj Object) error
}
2025-05-17 00:34:39 +00:00
type Hashable interface {
Len() int
Value(k string) Object
Each(func(k string, v Object) error) error
}
type ListObject []Object
func NewListObject() *ListObject {
return &ListObject{}
}
2024-04-16 12:05:21 +00:00
func (lo *ListObject) Append(o Object) {
2024-04-17 10:43:25 +00:00
*lo = append(*lo, o)
}
func (lo *ListObject) Insert(idx int, obj Object) error {
if idx != -1 {
return errors.New("not supported")
}
*lo = append(*lo, obj)
return nil
}
func (s *ListObject) String() string {
return fmt.Sprintf("%v", []Object(*s))
2024-04-16 12:05:21 +00:00
}
func (s *ListObject) Truthy() bool {
return len(*s) > 0
2024-04-16 12:05:21 +00:00
}
func (s *ListObject) Len() int {
return len(*s)
}
func (s *ListObject) Index(i int) Object {
return (*s)[i]
}
type iteratorObject struct {
Iterable
}
func (i iteratorObject) String() string {
return "iterator{}"
2024-09-06 10:55:19 +00:00
}
func (i iteratorObject) Truthy() bool {
return i.Iterable.HasNext()
2024-09-06 10:55:19 +00:00
}
type hashObject map[string]Object
2024-04-16 12:05:21 +00:00
func (s hashObject) String() string {
2024-09-06 10:55:19 +00:00
if len(s) == 0 {
return "[:]"
}
sb := strings.Builder{}
sb.WriteString("[")
for k, v := range s {
if sb.Len() != 1 {
sb.WriteString(" ")
}
sb.WriteString(k)
sb.WriteString(":")
sb.WriteString(v.String())
}
sb.WriteString("]")
return sb.String()
2024-04-16 12:05:21 +00:00
}
func (s hashObject) Truthy() bool {
return len(s) > 0
}
func (s hashObject) Len() int {
return len(s)
}
func (s hashObject) Value(k string) Object {
2024-04-24 11:09:52 +00:00
return s[k]
}
func (s hashObject) Each(fn func(k string, v Object) error) error {
for k, v := range s {
if err := fn(k, v); err != nil {
return err
}
}
return nil
}
type StringObject string
2024-04-10 12:19:11 +00:00
func (s StringObject) String() string {
2024-04-10 12:19:11 +00:00
return string(s)
}
2024-04-10 10:45:58 +00:00
func (s StringObject) Truthy() bool {
2024-04-13 11:46:50 +00:00
return string(s) != ""
}
2025-01-17 23:30:20 +00:00
type IntObject int
2024-04-24 10:12:39 +00:00
2025-01-17 23:30:20 +00:00
func (i IntObject) String() string {
2024-04-24 10:12:39 +00:00
return strconv.Itoa(int(i))
}
2025-01-17 23:30:20 +00:00
func (i IntObject) Truthy() bool {
2024-04-24 10:12:39 +00:00
return i != 0
}
2024-04-18 12:24:19 +00:00
type boolObject bool
func (b boolObject) String() string {
if b {
2024-09-06 10:55:19 +00:00
return "true"
2024-04-18 12:24:19 +00:00
}
2024-09-06 10:55:19 +00:00
return "false"
2024-04-18 12:24:19 +00:00
}
func (b boolObject) Truthy() bool {
return bool(b)
}
type timeObject time.Time
func (t timeObject) String() string {
return time.Time(t).Format(time.RFC3339)
}
func (t timeObject) Truthy() bool {
return !time.Time(t).IsZero()
}
func toGoValue(obj Object) (interface{}, bool) {
2024-04-11 12:05:05 +00:00
switch v := obj.(type) {
case nil:
return nil, true
case StringObject:
2024-04-11 12:05:05 +00:00
return string(v), true
2025-01-17 23:30:20 +00:00
case IntObject:
2024-04-24 10:12:39 +00:00
return int(v), true
2024-09-05 11:57:39 +00:00
case boolObject:
return bool(v), true
case timeObject:
return time.Time(v), true
case *ListObject:
xs := make([]interface{}, 0, len(*v))
for _, va := range *v {
2024-04-16 12:05:21 +00:00
x, ok := toGoValue(va)
if !ok {
continue
}
xs = append(xs, x)
}
return xs, true
case hashObject:
xs := make(map[string]interface{})
for k, va := range v {
x, ok := toGoValue(va)
if !ok {
continue
}
xs[k] = x
}
return xs, true
case iteratorObject:
return v.Iterable, true
case Iterable:
return v, true
case proxyObject:
return v.p, true
2024-04-24 11:09:52 +00:00
case listableProxyObject:
return v.orig.Interface(), true
2024-04-24 11:09:52 +00:00
case structProxyObject:
return v.orig.Interface(), true
2024-04-11 12:05:05 +00:00
}
return nil, false
}
func fromGoValue(v any) (Object, error) {
switch t := v.(type) {
case Object:
return t, nil
case Iterable:
return iteratorObject{t}, nil
case nil:
return nil, nil
case string:
return StringObject(t), nil
2024-04-24 11:09:52 +00:00
case int:
2025-01-17 23:30:20 +00:00
return IntObject(t), nil
2024-09-05 11:57:39 +00:00
case bool:
return boolObject(t), nil
case time.Time:
return timeObject(t), nil
}
return fromGoReflectValue(reflect.ValueOf(v))
}
func fromGoReflectValue(resVal reflect.Value) (Object, error) {
if !resVal.IsValid() {
return nil, nil
}
2024-04-24 11:09:52 +00:00
switch resVal.Kind() {
case reflect.Slice:
return listableProxyObject{v: resVal, orig: resVal}, nil
2024-04-24 11:09:52 +00:00
case reflect.Struct:
return newStructProxyObject(resVal, resVal), nil
case reflect.Pointer:
switch resVal.Elem().Kind() {
case reflect.Slice:
return listableProxyObject{v: resVal.Elem(), orig: resVal}, nil
case reflect.Struct:
return newStructProxyObject(resVal.Elem(), resVal), nil
}
return fromGoReflectValue(resVal.Elem())
2024-04-24 11:09:52 +00:00
}
return proxyObject{resVal.Interface()}, nil
}
2024-04-13 11:46:50 +00:00
type macroArgs struct {
eval evaluator
ec *evalCtx
2024-04-24 10:12:39 +00:00
hasPipe bool
pipeArg Object
ast *astCmd
argShift int
2024-04-13 11:46:50 +00:00
}
func (ma macroArgs) nargs() int {
2025-05-17 21:20:52 +00:00
return len(ma.ast.InvokeArgs[ma.argShift:])
2024-04-13 11:46:50 +00:00
}
func (ma *macroArgs) shift(n int) {
ma.argShift += n
}
func (ma macroArgs) identIs(ctx context.Context, n int, expectedIdent string) bool {
2025-05-17 21:20:52 +00:00
if n >= len(ma.ast.InvokeArgs[ma.argShift:]) {
2024-04-13 11:46:50 +00:00
return false
}
2025-05-17 21:20:52 +00:00
if len(ma.ast.InvokeArgs[ma.argShift+n].DotSuffix) != 0 {
2024-05-10 23:16:34 +00:00
return false
}
2025-05-17 21:20:52 +00:00
lit := ma.ast.InvokeArgs[ma.argShift+n].Arg.Ident
2024-04-13 11:46:50 +00:00
if lit == nil {
return false
}
2024-05-04 01:35:31 +00:00
return lit.String() == expectedIdent
2024-04-13 11:46:50 +00:00
}
func (ma *macroArgs) shiftIdent(ctx context.Context) (string, bool) {
2025-05-17 21:20:52 +00:00
if ma.argShift >= len(ma.ast.InvokeArgs) {
return "", false
}
2025-05-17 21:20:52 +00:00
if len(ma.ast.InvokeArgs[ma.argShift].DotSuffix) != 0 {
2024-05-10 23:16:34 +00:00
return "", false
}
2025-05-17 21:20:52 +00:00
lit := ma.ast.InvokeArgs[ma.argShift].Arg.Ident
if lit != nil {
ma.argShift += 1
2024-05-04 01:35:31 +00:00
return lit.String(), true
}
return "", false
}
func (ma macroArgs) evalArg(ctx context.Context, n int) (Object, error) {
2025-05-17 21:20:52 +00:00
if n >= len(ma.ast.InvokeArgs[ma.argShift:]) {
2024-04-13 11:46:50 +00:00
return nil, errors.New("not enough arguments") // FIX
}
2025-05-17 21:20:52 +00:00
return ma.eval.evalDot(ctx, ma.ec, ma.ast.InvokeArgs[ma.argShift+n])
2024-04-13 11:46:50 +00:00
}
func (ma macroArgs) evalBlock(ctx context.Context, n int, args []Object, pushScope bool) (Object, error) {
2024-04-13 11:46:50 +00:00
obj, err := ma.evalArg(ctx, n)
if err != nil {
return nil, err
}
switch v := obj.(type) {
case blockObject:
ec := ma.ec
if pushScope {
ec = ec.fork()
}
for i, n := range v.block.Names {
if i < len(args) {
ec.setOrDefineVar(n, args[i])
}
}
2024-04-13 11:46:50 +00:00
return ma.eval.evalBlock(ctx, ec, v.block)
case StringObject:
iv := ma.ec.lookupInvokable(string(v))
if iv == nil {
return nil, errors.New("'" + string(v) + "' is not invokable")
2024-04-16 12:28:12 +00:00
}
return iv.invoke(ctx, invocationArgs{
eval: ma.eval,
inst: ma.eval.inst,
ec: ma.ec,
args: args,
})
2024-04-16 12:28:12 +00:00
}
return nil, errors.New("expected an invokable arg")
2024-04-13 11:46:50 +00:00
}
2024-10-21 11:00:06 +00:00
type errObject struct{ err error }
func (eo errObject) String() string {
return "error:" + eo.err.Error()
}
func (eo errObject) Truthy() bool {
return true
}
2024-04-10 10:45:58 +00:00
type invocationArgs struct {
eval evaluator
inst *Inst
ec *evalCtx
args []Object
kwargs map[string]*ListObject
2024-04-18 10:53:25 +00:00
}
func (ia invocationArgs) expectArgn(x int) error {
if len(ia.args) < x {
return errors.New("expected at least " + strconv.Itoa(x) + " args")
}
return nil
2024-04-10 10:45:58 +00:00
}
2024-04-10 12:19:11 +00:00
func (ia invocationArgs) stringArg(i int) (string, error) {
if len(ia.args) < i {
return "", errors.New("expected at least " + strconv.Itoa(i) + " args")
}
s, ok := ia.args[i].(fmt.Stringer)
if !ok {
return "", errors.New("expected a string arg")
}
return s.String(), nil
}
2024-09-04 12:18:15 +00:00
func (ia invocationArgs) intArg(i int) (int, error) {
if len(ia.args) < i {
return 0, errors.New("expected at least " + strconv.Itoa(i) + " args")
}
switch v := ia.args[i].(type) {
2025-01-17 23:30:20 +00:00
case IntObject:
2024-09-04 12:18:15 +00:00
return int(v), nil
default:
return 0, errors.New("expected an int arg")
}
}
func (ia invocationArgs) invokableArg(i int) (invokable, error) {
if len(ia.args) < i {
return nil, errors.New("expected at least " + strconv.Itoa(i) + " args")
}
switch v := ia.args[i].(type) {
case invokable:
return v, nil
case StringObject:
iv := ia.ec.lookupInvokable(string(v))
if iv == nil {
return nil, errors.New("'" + string(v) + "' is not invokable")
}
return iv, nil
}
return nil, errors.New("expected an invokable arg")
}
func (ia invocationArgs) fork(args []Object) invocationArgs {
2024-04-18 10:53:25 +00:00
return invocationArgs{
eval: ia.eval,
inst: ia.inst,
ec: ia.ec,
args: args,
kwargs: nil,
2024-04-18 10:53:25 +00:00
}
}
func (ia invocationArgs) shift(i int) invocationArgs {
2024-04-28 00:07:20 +00:00
if len(ia.args) < i {
return ia
}
return invocationArgs{
eval: ia.eval,
inst: ia.inst,
ec: ia.ec,
args: ia.args[i:],
kwargs: ia.kwargs,
}
}
// invokable is an Object that can be executed as a command
2024-04-10 10:45:58 +00:00
type invokable interface {
invoke(ctx context.Context, args invocationArgs) (Object, error)
2024-04-10 10:45:58 +00:00
}
2024-04-13 11:46:50 +00:00
type macroable interface {
invokeMacro(ctx context.Context, args macroArgs) (Object, error)
2024-04-13 11:46:50 +00:00
}
type pipeInvokable interface {
2024-04-11 10:47:59 +00:00
invokable
}
type invokableFunc func(ctx context.Context, args invocationArgs) (Object, error)
2024-04-10 10:45:58 +00:00
func (i invokableFunc) invoke(ctx context.Context, args invocationArgs) (Object, error) {
2024-04-10 10:45:58 +00:00
return i(ctx, args)
}
2024-04-11 10:47:59 +00:00
2024-04-13 11:46:50 +00:00
type blockObject struct {
block *astBlock
closedEC *evalCtx
2024-04-13 11:46:50 +00:00
}
func (bo blockObject) String() string {
return "block"
}
func (bo blockObject) Truthy() bool {
return len(bo.block.Statements) > 0
}
func (bo blockObject) invoke(ctx context.Context, args invocationArgs) (Object, error) {
ec := bo.closedEC.fork()
for i, n := range bo.block.Names {
if i < len(args.args) {
2024-04-27 00:11:22 +00:00
ec.setOrDefineVar(n, args.args[i])
}
}
return args.eval.evalBlock(ctx, ec, bo.block)
}
type macroFunc func(ctx context.Context, args macroArgs) (Object, error)
2024-04-13 11:46:50 +00:00
func (i macroFunc) invokeMacro(ctx context.Context, args macroArgs) (Object, error) {
2024-04-13 11:46:50 +00:00
return i(ctx, args)
}
func isTruthy(obj Object) bool {
2024-04-13 11:46:50 +00:00
if obj == nil {
return false
}
return obj.Truthy()
}
type proxyObject struct {
p interface{}
}
func (p proxyObject) String() string {
return fmt.Sprintf("proxyObject{%T}", p.p)
}
func (p proxyObject) Truthy() bool {
2024-04-27 00:11:22 +00:00
return p.p != nil
}
2024-04-24 11:09:52 +00:00
type listableProxyObject struct {
v reflect.Value
orig reflect.Value
2024-04-24 11:09:52 +00:00
}
func (p listableProxyObject) String() string {
return fmt.Sprintf("listableProxyObject{%v}", p.v.Type())
}
func (p listableProxyObject) Truthy() bool {
2024-04-27 00:11:22 +00:00
return p.v.Len() > 0
2024-04-24 11:09:52 +00:00
}
func (p listableProxyObject) Len() int {
return p.v.Len()
}
func (p listableProxyObject) Index(i int) Object {
2024-04-24 11:09:52 +00:00
e, err := fromGoValue(p.v.Index(i).Interface())
if err != nil {
return nil
}
return e
}
type structProxyObject struct {
v reflect.Value
orig reflect.Value
vf []reflect.StructField
2024-04-24 11:29:26 +00:00
}
func newStructProxyObject(v reflect.Value, orig reflect.Value) structProxyObject {
2024-04-24 11:29:26 +00:00
return structProxyObject{
v: v,
orig: orig,
vf: slices.Filter(reflect.VisibleFields(v.Type()), func(t reflect.StructField) bool { return t.IsExported() }),
2024-04-24 11:29:26 +00:00
}
2024-04-24 11:09:52 +00:00
}
func (s structProxyObject) String() string {
return fmt.Sprintf("structProxyObject{%v}", s.v.Type())
}
func (s structProxyObject) Truthy() bool {
return true
}
func (s structProxyObject) Len() int {
2024-04-24 11:29:26 +00:00
return len(s.vf)
2024-04-24 11:09:52 +00:00
}
func (s structProxyObject) Value(k string) Object {
f := s.v.FieldByName(k)
if !f.IsValid() {
return nil
}
if f.Kind() == reflect.Ptr {
if f.IsNil() {
return nil
}
f = f.Elem()
}
e, err := fromGoValue(f.Interface())
2024-04-24 11:09:52 +00:00
if err != nil {
return nil
}
return e
}
func (s structProxyObject) Each(fn func(k string, v Object) error) error {
2024-04-24 11:29:26 +00:00
for _, f := range s.vf {
v, err := fromGoValue(s.v.FieldByName(f.Name).Interface())
2024-04-24 11:09:52 +00:00
if err != nil {
v = nil
}
2024-04-24 11:29:26 +00:00
if err := fn(f.Name, v); err != nil {
2024-04-24 11:09:52 +00:00
return err
}
}
return nil
}
2024-04-30 10:55:06 +00:00
type OpaqueObject struct {
v any
}
func Opaque(v any) OpaqueObject {
return OpaqueObject{v: v}
}
func (p OpaqueObject) String() string {
return fmt.Sprintf("opaque{%T}", p.v)
}
func (p OpaqueObject) Truthy() bool {
return p.v != nil
}
2024-04-30 10:55:06 +00:00
type errBreak struct {
isCont bool
ret Object
2024-04-30 10:55:06 +00:00
}
func (e errBreak) Error() string {
if e.isCont {
return "continue"
}
return "break"
}
type errReturn struct {
ret Object
2024-04-30 10:55:06 +00:00
}
func (e errReturn) Error() string {
return "return"
}
2024-04-30 11:55:18 +00:00
var ErrHalt = errors.New("halt")