Skip to content

Commit 18e4290

Browse files
committed
Add builtin functions
1 parent 0c26c26 commit 18e4290

File tree

10 files changed

+488
-28
lines changed

10 files changed

+488
-28
lines changed

builtin/builtin.go

Lines changed: 323 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,14 @@
11
package builtin
22

33
import (
4+
"encoding/base64"
5+
"encoding/json"
46
"fmt"
57
"reflect"
8+
"strings"
9+
"time"
10+
11+
"github.com/antonmedv/expr/vm/runtime"
612
)
713

814
type Function struct {
@@ -97,4 +103,321 @@ var Functions = []*Function{
97103
Builtin1: String,
98104
Types: types(new(func(any interface{}) string)),
99105
},
106+
{
107+
Name: "trim",
108+
Func: func(args ...interface{}) (interface{}, error) {
109+
if len(args) == 1 {
110+
return strings.TrimSpace(args[0].(string)), nil
111+
} else if len(args) == 2 {
112+
return strings.Trim(args[0].(string), args[1].(string)), nil
113+
} else {
114+
return nil, fmt.Errorf("invalid number of arguments for trim (expected 1 or 2, got %d)", len(args))
115+
}
116+
},
117+
Types: types(
118+
strings.TrimSpace,
119+
strings.Trim,
120+
),
121+
},
122+
{
123+
Name: "trimPrefix",
124+
Func: func(args ...interface{}) (interface{}, error) {
125+
return strings.TrimPrefix(args[0].(string), args[1].(string)), nil
126+
},
127+
Types: types(strings.TrimPrefix),
128+
},
129+
{
130+
Name: "trimSuffix",
131+
Func: func(args ...interface{}) (interface{}, error) {
132+
return strings.TrimSuffix(args[0].(string), args[1].(string)), nil
133+
},
134+
Types: types(strings.TrimSuffix),
135+
},
136+
{
137+
Name: "upper",
138+
Builtin1: func(arg interface{}) interface{} {
139+
return strings.ToUpper(arg.(string))
140+
},
141+
Types: types(strings.ToUpper),
142+
},
143+
{
144+
Name: "lower",
145+
Builtin1: func(arg interface{}) interface{} {
146+
return strings.ToLower(arg.(string))
147+
},
148+
Types: types(strings.ToLower),
149+
},
150+
{
151+
Name: "split",
152+
Func: func(args ...interface{}) (interface{}, error) {
153+
return strings.Split(args[0].(string), args[1].(string)), nil
154+
},
155+
Types: types(strings.Split),
156+
},
157+
{
158+
Name: "splitN",
159+
Func: func(args ...interface{}) (interface{}, error) {
160+
return strings.SplitN(args[0].(string), args[1].(string), runtime.ToInt(args[2])), nil
161+
},
162+
Types: types(strings.SplitN),
163+
},
164+
{
165+
Name: "splitAfter",
166+
Func: func(args ...interface{}) (interface{}, error) {
167+
return strings.SplitAfter(args[0].(string), args[1].(string)), nil
168+
},
169+
Types: types(strings.SplitAfter),
170+
},
171+
{
172+
Name: "splitAfterN",
173+
Func: func(args ...interface{}) (interface{}, error) {
174+
return strings.SplitAfterN(args[0].(string), args[1].(string), runtime.ToInt(args[2])), nil
175+
},
176+
Types: types(strings.SplitAfterN),
177+
},
178+
{
179+
Name: "replace",
180+
Func: func(args ...interface{}) (interface{}, error) {
181+
if len(args) == 4 {
182+
return strings.Replace(args[0].(string), args[1].(string), args[2].(string), runtime.ToInt(args[3])), nil
183+
} else if len(args) == 3 {
184+
return strings.ReplaceAll(args[0].(string), args[1].(string), args[2].(string)), nil
185+
} else {
186+
return nil, fmt.Errorf("invalid number of arguments for replace (expected 3 or 4, got %d)", len(args))
187+
}
188+
},
189+
Types: types(
190+
strings.Replace,
191+
strings.ReplaceAll,
192+
),
193+
},
194+
{
195+
Name: "repeat",
196+
Func: func(args ...interface{}) (interface{}, error) {
197+
return strings.Repeat(args[0].(string), runtime.ToInt(args[1])), nil
198+
},
199+
Types: types(strings.Repeat),
200+
},
201+
{
202+
Name: "join",
203+
Func: func(args ...interface{}) (interface{}, error) {
204+
glue := ""
205+
if len(args) == 2 {
206+
glue = args[1].(string)
207+
}
208+
switch args[0].(type) {
209+
case []string:
210+
return strings.Join(args[0].([]string), glue), nil
211+
case []interface{}:
212+
var s []string
213+
for _, arg := range args[0].([]interface{}) {
214+
s = append(s, arg.(string))
215+
}
216+
return strings.Join(s, glue), nil
217+
}
218+
return nil, fmt.Errorf("invalid argument for join (type %s)", reflect.TypeOf(args[0]))
219+
},
220+
Types: types(
221+
strings.Join,
222+
new(func([]interface{}, string) string),
223+
new(func([]string) string),
224+
new(func([]interface{}) string),
225+
),
226+
},
227+
{
228+
Name: "indexOf",
229+
Func: func(args ...interface{}) (interface{}, error) {
230+
return strings.Index(args[0].(string), args[1].(string)), nil
231+
},
232+
Types: types(strings.Index),
233+
},
234+
{
235+
Name: "lastIndexOf",
236+
Func: func(args ...interface{}) (interface{}, error) {
237+
return strings.LastIndex(args[0].(string), args[1].(string)), nil
238+
},
239+
Types: types(strings.LastIndex),
240+
},
241+
{
242+
Name: "hasPrefix",
243+
Func: func(args ...interface{}) (interface{}, error) {
244+
return strings.HasPrefix(args[0].(string), args[1].(string)), nil
245+
},
246+
Types: types(strings.HasPrefix),
247+
},
248+
{
249+
Name: "hasSuffix",
250+
Func: func(args ...interface{}) (interface{}, error) {
251+
return strings.HasSuffix(args[0].(string), args[1].(string)), nil
252+
},
253+
Types: types(strings.HasSuffix),
254+
},
255+
{
256+
Name: "max",
257+
Func: Max,
258+
Validate: func(args []reflect.Type) (reflect.Type, error) {
259+
if len(args) == 0 {
260+
return anyType, fmt.Errorf("not enough arguments to call max")
261+
}
262+
for _, arg := range args {
263+
switch kind(arg) {
264+
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Float32, reflect.Float64:
265+
default:
266+
return anyType, fmt.Errorf("invalid argument for max (type %s)", arg)
267+
}
268+
}
269+
return args[0], nil
270+
},
271+
},
272+
{
273+
Name: "min",
274+
Func: Min,
275+
Validate: func(args []reflect.Type) (reflect.Type, error) {
276+
if len(args) == 0 {
277+
return anyType, fmt.Errorf("not enough arguments to call min")
278+
}
279+
for _, arg := range args {
280+
switch kind(arg) {
281+
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Float32, reflect.Float64:
282+
default:
283+
return anyType, fmt.Errorf("invalid argument for min (type %s)", arg)
284+
}
285+
}
286+
return args[0], nil
287+
},
288+
},
289+
{
290+
Name: "toJSON",
291+
Func: func(args ...interface{}) (interface{}, error) {
292+
b, err := json.MarshalIndent(args[0], "", " ")
293+
if err != nil {
294+
return nil, err
295+
}
296+
return string(b), nil
297+
},
298+
Types: types(new(func(interface{}) string)),
299+
},
300+
{
301+
Name: "fromJSON",
302+
Func: func(args ...interface{}) (interface{}, error) {
303+
var v interface{}
304+
err := json.Unmarshal([]byte(args[0].(string)), &v)
305+
if err != nil {
306+
return nil, err
307+
}
308+
return v, nil
309+
},
310+
Types: types(new(func(string) interface{})),
311+
},
312+
{
313+
Name: "toBase64",
314+
Func: func(args ...interface{}) (interface{}, error) {
315+
return base64.StdEncoding.EncodeToString([]byte(args[0].(string))), nil
316+
},
317+
Types: types(new(func(string) string)),
318+
},
319+
{
320+
Name: "fromBase64",
321+
Func: func(args ...interface{}) (interface{}, error) {
322+
b, err := base64.StdEncoding.DecodeString(args[0].(string))
323+
if err != nil {
324+
return nil, err
325+
}
326+
return string(b), nil
327+
},
328+
Types: types(new(func(string) string)),
329+
},
330+
{
331+
Name: "now",
332+
Func: func(args ...interface{}) (interface{}, error) {
333+
return time.Now(), nil
334+
},
335+
Types: types(new(func() time.Time)),
336+
},
337+
{
338+
Name: "duration",
339+
Func: func(args ...interface{}) (interface{}, error) {
340+
return time.ParseDuration(args[0].(string))
341+
},
342+
Types: types(time.ParseDuration),
343+
},
344+
{
345+
Name: "date",
346+
Func: func(args ...interface{}) (interface{}, error) {
347+
date := args[0].(string)
348+
if len(args) == 2 {
349+
layout := args[1].(string)
350+
return time.Parse(layout, date)
351+
}
352+
if len(args) == 3 {
353+
layout := args[1].(string)
354+
timeZone := args[2].(string)
355+
tz, err := time.LoadLocation(timeZone)
356+
if err != nil {
357+
return nil, err
358+
}
359+
t, err := time.ParseInLocation(layout, date, tz)
360+
if err != nil {
361+
return nil, err
362+
}
363+
return t, nil
364+
}
365+
366+
layouts := []string{
367+
time.DateOnly,
368+
time.TimeOnly,
369+
time.DateTime,
370+
time.RFC3339,
371+
time.RFC822,
372+
time.RFC850,
373+
time.RFC1123,
374+
}
375+
for _, layout := range layouts {
376+
t, err := time.Parse(layout, date)
377+
if err == nil {
378+
return t, nil
379+
}
380+
}
381+
return nil, fmt.Errorf("invalid date %s", date)
382+
},
383+
Types: types(
384+
new(func(string) time.Time),
385+
new(func(string, string) time.Time),
386+
new(func(string, string, string) time.Time),
387+
),
388+
},
389+
{
390+
Name: "first",
391+
Func: func(args ...interface{}) (interface{}, error) {
392+
return runtime.Fetch(args[0], 0), nil
393+
},
394+
Types: types(new(func([]interface{}) interface{})),
395+
},
396+
{
397+
Name: "last",
398+
Func: func(args ...interface{}) (interface{}, error) {
399+
return runtime.Fetch(args[0], -1), nil
400+
},
401+
},
402+
{
403+
Name: "get",
404+
Func: func(args ...interface{}) (out interface{}, err error) {
405+
defer func() {
406+
if r := recover(); r != nil {
407+
return
408+
}
409+
}()
410+
return runtime.Fetch(args[0], args[1]), nil
411+
},
412+
Validate: func(args []reflect.Type) (reflect.Type, error) {
413+
if len(args) != 2 {
414+
return anyType, fmt.Errorf("invalid number of arguments for get (expected 2, got %d)", len(args))
415+
}
416+
switch kind(args[0]) {
417+
case reflect.Map, reflect.Struct, reflect.Slice, reflect.Array, reflect.Interface:
418+
return anyType, nil
419+
}
420+
return anyType, fmt.Errorf("cannot get %s from %s", args[1], args[0])
421+
},
422+
},
100423
}

0 commit comments

Comments
 (0)