Skip to content

Commit 07f6cf2

Browse files
committed
feat: add lambda
1 parent 397d5cf commit 07f6cf2

File tree

2 files changed

+70
-7
lines changed

2 files changed

+70
-7
lines changed

dsl/ops/func_do.go

+1-2
Original file line numberDiff line numberDiff line change
@@ -50,9 +50,8 @@ func (*doOp) EvalExpr(ctx context.Context, args []types.Value) (_ types.Value, _
5050
}()
5151

5252
var lastVal types.Value
53-
empty := make([]types.Value, 0)
5453
for i := range args {
55-
v, rbFunc, err := args[i].(types.Op).EvalExpr(ctx, empty)
54+
v, rbFunc, err := args[i].(types.Lambda).Call(ctx)
5655
g.Add(rbFunc)
5756
if err != nil {
5857
result = g.Error(err)

dsl/types/lambda.go

+69-5
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,82 @@
11
package types
22

3-
import "context"
3+
import (
4+
"context"
5+
6+
"github.com/pkg/errors"
7+
)
48

59
// Lambda can be applicable, and it has an expression to execute.
6-
type Lambda Value
10+
type Lambda interface {
11+
Value
12+
13+
// Call calls this lambda with given args
14+
Call(ctx context.Context, args ...Value) (Value, func(context.Context), error)
15+
}
16+
17+
type argT Array
18+
19+
// ArgsDef is passed to builder function, the argument of NewLambda()
20+
type ArgsDef struct {
21+
args []argT
22+
}
23+
24+
// Define returns placeholder expression of given argument
25+
func (def *ArgsDef) Define(n int, name String, typ Type) (Value, error) {
26+
if n <= 0 {
27+
return nil, errors.New("the number of argument must be positive")
28+
}
29+
for n > len(def.args) {
30+
def.args = append(def.args, nil)
31+
}
32+
if def.args[n-1] != nil {
33+
return nil, errors.Errorf("the %dth argument is already taken", n)
34+
}
35+
argExpr := []Value{NewString("arg"), name}
36+
def.args[n-1] = NewArray(argExpr, AnyValue)
37+
return def.args[n-1], nil
38+
}
39+
40+
// Inject replaces expr of ["arg", expr] with given values
41+
func (def *ArgsDef) Inject(args []Value) error {
42+
if len(args) != len(def.args) {
43+
return errors.Errorf("expected %d arity but got %d", len(def.args), len(args))
44+
}
45+
for i := range args {
46+
if def.args[i] == nil {
47+
return errors.Errorf("%dth arg is not taken", i+1)
48+
}
49+
def.args[i].Value()[1] = args[i]
50+
}
51+
return nil
52+
}
753

854
// NewLambda creates lambda value.
955
// Signature must have 1 type at least for a return type.
10-
func NewLambda(t Type, rest ...Type) Lambda {
11-
return &lambdaT{typ: NewLambdaType(t, rest...)}
56+
func NewLambda(builder func(*ArgsDef) (Expr, []Type, error)) (Lambda, error) {
57+
def := &ArgsDef{args: make([]argT, 0)}
58+
expr, sig, err := builder(def)
59+
if err != nil {
60+
return nil, errors.Wrap(err, "builder function returned an error")
61+
}
62+
return &lambdaT{
63+
def: def,
64+
expr: expr,
65+
typ: NewLambdaType(sig[0], sig[1:]...),
66+
}, nil
1267
}
1368

1469
type lambdaT struct {
15-
typ Type
70+
def *ArgsDef
71+
expr Expr
72+
typ Type
73+
}
74+
75+
func (v *lambdaT) Call(ctx context.Context, args ...Value) (Value, func(context.Context), error) {
76+
if err := v.def.Inject(args); err != nil {
77+
return nil, nil, err
78+
}
79+
return v.expr.Eval(ctx)
1680
}
1781

1882
func (v *lambdaT) Invert(context.Context) (Value, error) {

0 commit comments

Comments
 (0)