Skip to content

Commit

Permalink
lang: funcs: structs: Add Call and CallStruct methods for composite
Browse files Browse the repository at this point in the history
It might turn out the CallStruct is the API we want. This will depend on
the future iterations of the function engine.
  • Loading branch information
purpleidea committed Dec 8, 2024
1 parent e9dbb7b commit f5806e0
Showing 1 changed file with 131 additions and 54 deletions.
185 changes: 131 additions & 54 deletions lang/funcs/structs/composite.go
Original file line number Diff line number Diff line change
Expand Up @@ -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++ {
Expand Down Expand Up @@ -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.
Expand All @@ -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
Expand All @@ -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...
Expand All @@ -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)
}

0 comments on commit f5806e0

Please sign in to comment.