Skip to content
This repository was archived by the owner on Mar 16, 2025. It is now read-only.

Commit 4a217dd

Browse files
committed
add reflect-based function definition to reduce boilerplate
1 parent 46e6efe commit 4a217dd

8 files changed

Lines changed: 1454 additions & 0 deletions

File tree

Lines changed: 219 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,219 @@
1+
// SPDX-FileCopyrightText: 2023 Christoph Mewes
2+
// SPDX-License-Identifier: MIT
3+
4+
package native
5+
6+
import (
7+
"reflect"
8+
9+
"go.xrstf.de/rudi/pkg/eval/types"
10+
"go.xrstf.de/rudi/pkg/lang/ast"
11+
)
12+
13+
var (
14+
dummy ast.Expression
15+
expressionType = reflect.TypeOf(&dummy).Elem()
16+
)
17+
18+
type argsConsumer func(ctx types.Context, args []cachedExpression) (asserted []any, remaining []cachedExpression, err error)
19+
20+
func isAny(t reflect.Type) bool {
21+
return t.Kind() == reflect.Interface && t.Name() == ""
22+
}
23+
24+
func newConsumerFunc(t reflect.Type) argsConsumer {
25+
switch t.Kind() {
26+
case reflect.Bool:
27+
return boolConsumer
28+
case reflect.Int64:
29+
return intConsumer
30+
case reflect.Float64:
31+
return floatConsumer
32+
case reflect.String:
33+
return stringConsumer
34+
case reflect.Slice:
35+
// we only support []any
36+
if isAny(t.Elem()) {
37+
return vectorConsumer
38+
}
39+
40+
case reflect.Map:
41+
// we only support map[string]any
42+
// TODO: Check key as well.
43+
if isAny(t.Elem()) {
44+
return objectConsumer
45+
}
46+
47+
case reflect.Interface:
48+
// empty interface (any)
49+
if isAny(t) {
50+
return anyConsumer
51+
}
52+
53+
// allow unevaluated access to the argument expression
54+
if t.AssignableTo(expressionType) {
55+
return expressionConsumer
56+
}
57+
}
58+
59+
return nil
60+
}
61+
62+
func boolConsumer(ctx types.Context, args []cachedExpression) (asserted []any, remaining []cachedExpression, err error) {
63+
if len(args) == 0 {
64+
return nil, nil, nil
65+
}
66+
67+
evaluated, err := args[0].Eval(ctx)
68+
if err != nil {
69+
return nil, nil, err
70+
}
71+
72+
coalesced, err := ctx.Coalesce().ToBool(evaluated)
73+
if err != nil {
74+
return nil, args, nil
75+
}
76+
77+
return []any{coalesced}, args[1:], nil
78+
}
79+
80+
func intConsumer(ctx types.Context, args []cachedExpression) (asserted []any, remaining []cachedExpression, err error) {
81+
if len(args) == 0 {
82+
return nil, nil, nil
83+
}
84+
85+
evaluated, err := args[0].Eval(ctx)
86+
if err != nil {
87+
return nil, nil, err
88+
}
89+
90+
coalesced, err := ctx.Coalesce().ToInt64(evaluated)
91+
if err != nil {
92+
return nil, args, nil
93+
}
94+
95+
return []any{coalesced}, args[1:], nil
96+
}
97+
98+
func floatConsumer(ctx types.Context, args []cachedExpression) (asserted []any, remaining []cachedExpression, err error) {
99+
if len(args) == 0 {
100+
return nil, nil, nil
101+
}
102+
103+
evaluated, err := args[0].Eval(ctx)
104+
if err != nil {
105+
return nil, nil, err
106+
}
107+
108+
coalesced, err := ctx.Coalesce().ToFloat64(evaluated)
109+
if err != nil {
110+
return nil, args, nil
111+
}
112+
113+
return []any{coalesced}, args[1:], nil
114+
}
115+
116+
func stringConsumer(ctx types.Context, args []cachedExpression) (asserted []any, remaining []cachedExpression, err error) {
117+
if len(args) == 0 {
118+
return nil, nil, nil
119+
}
120+
121+
evaluated, err := args[0].Eval(ctx)
122+
if err != nil {
123+
return nil, nil, err
124+
}
125+
126+
coalesced, err := ctx.Coalesce().ToString(evaluated)
127+
if err != nil {
128+
return nil, args, nil
129+
}
130+
131+
return []any{coalesced}, args[1:], nil
132+
}
133+
134+
func vectorConsumer(ctx types.Context, args []cachedExpression) (asserted []any, remaining []cachedExpression, err error) {
135+
if len(args) == 0 {
136+
return nil, nil, nil
137+
}
138+
139+
evaluated, err := args[0].Eval(ctx)
140+
if err != nil {
141+
return nil, nil, err
142+
}
143+
144+
coalesced, err := ctx.Coalesce().ToVector(evaluated)
145+
if err != nil {
146+
return nil, args, nil
147+
}
148+
149+
return []any{coalesced}, args[1:], nil
150+
}
151+
152+
func objectConsumer(ctx types.Context, args []cachedExpression) (asserted []any, remaining []cachedExpression, err error) {
153+
if len(args) == 0 {
154+
return nil, nil, nil
155+
}
156+
157+
evaluated, err := args[0].Eval(ctx)
158+
if err != nil {
159+
return nil, nil, err
160+
}
161+
162+
coalesced, err := ctx.Coalesce().ToObject(evaluated)
163+
if err != nil {
164+
return nil, args, nil
165+
}
166+
167+
return []any{coalesced}, args[1:], nil
168+
}
169+
170+
func anyConsumer(ctx types.Context, args []cachedExpression) (asserted []any, remaining []cachedExpression, err error) {
171+
if len(args) == 0 {
172+
return nil, nil, nil
173+
}
174+
175+
evaluated, err := args[0].Eval(ctx)
176+
if err != nil {
177+
return nil, nil, err
178+
}
179+
180+
return []any{evaluated}, args[1:], nil
181+
}
182+
183+
func expressionConsumer(ctx types.Context, args []cachedExpression) (asserted []any, remaining []cachedExpression, err error) {
184+
if len(args) == 0 {
185+
return nil, nil, nil
186+
}
187+
188+
return []any{args[0].expr}, args[1:], nil
189+
}
190+
191+
func toVariadicConsumer(singleConsumer argsConsumer) argsConsumer {
192+
return func(ctx types.Context, args []cachedExpression) (asserted []any, remaining []cachedExpression, err error) {
193+
leftover := args
194+
result := []any{}
195+
196+
for len(leftover) > 0 {
197+
var (
198+
consumed []any
199+
err error
200+
)
201+
202+
consumed, leftover, err = singleConsumer(ctx, leftover)
203+
if err != nil {
204+
return nil, nil, err
205+
}
206+
207+
// Variadic consumers must consume all args, so a noMatch is not allowed;
208+
// it's not an error though, we just have to signal that in total, the
209+
// matching was not a success.
210+
if consumed == nil {
211+
return nil, nil, nil
212+
}
213+
214+
result = append(result, consumed[0])
215+
}
216+
217+
return result, nil, nil
218+
}
219+
}

0 commit comments

Comments
 (0)