lang: funcs: structs: Add Call and CallStruct methods for composite

It might turn out the CallStruct is the API we want. This will depend on
the future iterations of the function engine.
This commit is contained in:
James Shubin
2024-12-08 17:43:41 -05:00
parent e9dbb7b86c
commit f5806e0617

View File

@@ -91,6 +91,21 @@ func (obj *CompositeFunc) Info() *interfaces.Info {
Out: obj.Type, // this is the output type for the expression
}
// This populates the .Map and .Ord fields of the above type.
obj.makeStructType(typ) // hack =D
return &interfaces.Info{
Pure: true,
Memo: false, // TODO: ???
Sig: typ,
Err: obj.Validate(),
}
}
// makeStructType is a helper that adds the map/ord properties onto a type. This
// should match the type we expect for this composite struct.
func (obj *CompositeFunc) makeStructType(typ *types.Type) {
switch obj.Type.Kind {
case types.KindList: // wrapped in a struct with `length` many keys
for i := 0; i < obj.Len; i++ {
@@ -119,13 +134,6 @@ func (obj *CompositeFunc) Info() *interfaces.Info {
typ.Map = obj.Type.Map
typ.Ord = obj.Type.Ord
}
return &interfaces.Info{
Pure: true,
Memo: false, // TODO: ???
Sig: typ,
Err: obj.Validate(),
}
}
// Init runs some startup code for this composite function.
@@ -146,8 +154,11 @@ func (obj *CompositeFunc) Stream(ctx context.Context) error {
obj.init.Input = nil // don't infinite loop back
if obj.last == nil {
// FIXME: can we get an empty struct?
result := obj.Type.New() // new list or map
result, err := obj.StructCall(ctx, obj.last)
if err != nil {
return err
}
obj.result = result
select {
case obj.init.Output <- result: // send
@@ -167,51 +178,18 @@ func (obj *CompositeFunc) Stream(ctx context.Context) error {
}
obj.last = input // store for next
var result types.Value
switch obj.Type.Kind {
case types.KindList:
// XXX: this duplicates the same logic that exists in Value() as implemented on *ExprList
// XXX: have this call that function to get the result?
result = obj.Type.New() // new list
input := input.(*types.StructValue) // must be!
for i := 0; i < obj.Len; i++ { // build it
value, exists := input.Lookup(fmt.Sprintf("%d", i)) // argNames as integers!
if !exists {
return fmt.Errorf("missing input index `%d`", i)
}
if err := result.(*types.ListValue).Add(value); err != nil {
return errwrap.Wrapf(err, "can't build list index `%d`", i)
}
}
case types.KindMap:
result = obj.Type.New() // new map
input := (input.(*types.StructValue)).Struct() // must be!
l := len(input)
if l%2 != 0 {
return fmt.Errorf("expected even number of inputs for a map, got: %d", l)
}
// each key should be named `key:0`, `val:0`, `key:1`, `val:1`,
// and so on for as many key pairs as we have... remember that
// the number of keys pairs is known statically in this case!
for i := 0; i < l/2; i++ { // build it
key, exists := input[fmt.Sprintf("key:%d", i)]
if !exists {
return fmt.Errorf("missing input key `key:%d`", i)
}
val, exists := input[fmt.Sprintf("val:%d", i)]
if !exists {
return fmt.Errorf("missing input val `val:%d`", i)
}
if err := result.(*types.MapValue).Add(key, val); err != nil {
return errwrap.Wrapf(err, "can't build map key with index `%d`", i)
}
}
case types.KindStruct:
result = input
// TODO: use the normal Call interface instead?
//args, err := interfaces.StructToCallableArgs(input) // []types.Value, error)
//if err != nil {
// return err
//}
//result, err := obj.Call(ctx, args)
//if err != nil {
// return err
//}
result, err := obj.StructCall(ctx, input)
if err != nil {
return err
}
// skip sending an update...
@@ -232,3 +210,102 @@ func (obj *CompositeFunc) Stream(ctx context.Context) error {
}
}
}
// StructCall is a different Call API which is sometimes easier to implement.
func (obj *CompositeFunc) StructCall(ctx context.Context, st types.Value) (types.Value, error) {
if st == nil {
// FIXME: can we get an empty struct?
result := obj.Type.New() // new list or map
return result, nil
}
var result types.Value
switch obj.Type.Kind {
case types.KindList:
// XXX: this duplicates the same logic that exists in Value() as implemented on *ExprList
// XXX: have this call that function to get the result?
result = obj.Type.New() // new list
input := st.(*types.StructValue) // must be!
for i := 0; i < obj.Len; i++ { // build it
value, exists := input.Lookup(fmt.Sprintf("%d", i)) // argNames as integers!
if !exists {
return nil, fmt.Errorf("missing input index `%d`", i)
}
if err := result.(*types.ListValue).Add(value); err != nil {
return nil, errwrap.Wrapf(err, "can't build list index `%d`", i)
}
}
case types.KindMap:
result = obj.Type.New() // new map
input := (st.(*types.StructValue)).Struct() // must be!
l := len(input)
if l%2 != 0 {
return nil, fmt.Errorf("expected even number of inputs for a map, got: %d", l)
}
// each key should be named `key:0`, `val:0`, `key:1`, `val:1`,
// and so on for as many key pairs as we have... remember that
// the number of keys pairs is known statically in this case!
for i := 0; i < l/2; i++ { // build it
key, exists := input[fmt.Sprintf("key:%d", i)]
if !exists {
return nil, fmt.Errorf("missing input key `key:%d`", i)
}
val, exists := input[fmt.Sprintf("val:%d", i)]
if !exists {
return nil, fmt.Errorf("missing input val `val:%d`", i)
}
if err := result.(*types.MapValue).Add(key, val); err != nil {
return nil, errwrap.Wrapf(err, "can't build map key with index `%d`", i)
}
}
case types.KindStruct:
result = st
}
return result, nil
}
// Call this function with the input args and return the value if it is possible
// to do so at this time.
func (obj *CompositeFunc) Call(ctx context.Context, args []types.Value) (types.Value, error) {
typ := &types.Type{
Kind: types.KindStruct,
Map: make(map[string]*types.Type),
Ord: []string{},
}
obj.makeStructType(typ) // hack =D
st := (typ.New().(*types.StructValue)) // new struct
switch obj.Type.Kind {
case types.KindList:
for i, arg := range args {
key := fmt.Sprintf("%d", i)
st.V[key] = arg
}
case types.KindMap:
for i, arg := range args {
if i%2 == 0 {
key1 := fmt.Sprintf("key:%d", i)
st.V[key1] = arg
} else {
key2 := fmt.Sprintf("val:%d", i)
st.V[key2] = arg
}
}
case types.KindStruct:
for i, arg := range args {
key := obj.Type.Ord[i]
st.V[key] = arg
}
}
return obj.StructCall(ctx, st)
}