From f752601ff20654f2342e8d38ef989d32056778df Mon Sep 17 00:00:00 2001 From: Daniel Kang Date: Tue, 29 Jan 2019 15:05:03 -0800 Subject: [PATCH] add strconv functions --- compiler/stdlib/func_typedefs.go | 19 +++ compiler/stdlib/func_typedefs_test.go | 9 ++ compiler/stdlib/text.go | 191 ++++++++++++++++++++++++++ compiler/stdlib/text_test.go | 17 +++ 4 files changed, 236 insertions(+) diff --git a/compiler/stdlib/func_typedefs.go b/compiler/stdlib/func_typedefs.go index 987ca6b..78fffae 100644 --- a/compiler/stdlib/func_typedefs.go +++ b/compiler/stdlib/func_typedefs.go @@ -829,3 +829,22 @@ func FuncAIRSsE(fn func(int) ([]string, error)) *objects.UserFunction { }, } } + +// FuncAIRS transform a function of 'func(int) string' signature +// into a user function object. +func FuncAIRS(fn func(int) string) *objects.UserFunction { + return &objects.UserFunction{ + Value: func(args ...objects.Object) (ret objects.Object, err error) { + if len(args) != 1 { + return nil, objects.ErrWrongNumArguments + } + + i1, ok := objects.ToInt(args[0]) + if !ok { + return nil, objects.ErrInvalidTypeConversion + } + + return &objects.String{Value: fn(i1)}, nil + }, + } +} diff --git a/compiler/stdlib/func_typedefs_test.go b/compiler/stdlib/func_typedefs_test.go index cfbdf8c..aca7bfe 100644 --- a/compiler/stdlib/func_typedefs_test.go +++ b/compiler/stdlib/func_typedefs_test.go @@ -439,6 +439,15 @@ func TestFuncASSRB(t *testing.T) { assert.Equal(t, objects.ErrWrongNumArguments, err) } +func TestFuncAIRS(t *testing.T) { + uf := stdlib.FuncAIRS(func(a int) string { return strconv.Itoa(a) }) + ret, err := uf.Call(&objects.Int{Value: 55}) + assert.NoError(t, err) + assert.Equal(t, &objects.String{Value: "55"}, ret) + ret, err = uf.Call() + assert.Equal(t, objects.ErrWrongNumArguments, err) +} + func array(elements ...objects.Object) *objects.Array { return &objects.Array{Value: elements} } diff --git a/compiler/stdlib/text.go b/compiler/stdlib/text.go index 19b2bc4..07ff119 100644 --- a/compiler/stdlib/text.go +++ b/compiler/stdlib/text.go @@ -2,6 +2,7 @@ package stdlib import ( "regexp" + "strconv" "strings" "github.com/d5/tengo/objects" @@ -325,6 +326,196 @@ var textModule = map[string]objects.Object{ "trim_space": FuncASRS(strings.TrimSpace), // trim_suffix(s, suffix) => string "trim_suffix": FuncASSRS(strings.TrimSuffix), + // atoi(str) => int/error + "atoi": FuncASRIE(strconv.Atoi), + // format_bool(b) => string + "format_bool": &objects.UserFunction{ + Value: func(args ...objects.Object) (ret objects.Object, err error) { + if len(args) != 1 { + err = objects.ErrWrongNumArguments + return + } + + b1, ok := args[0].(*objects.Bool) + if !ok { + err = objects.ErrInvalidTypeConversion + return + } + + if b1 == objects.TrueValue { + ret = &objects.String{Value: "true"} + } else { + ret = &objects.String{Value: "false"} + } + + return + }, + }, + // format_float(f, fmt, prec, bits) => string + "format_float": &objects.UserFunction{ + Value: func(args ...objects.Object) (ret objects.Object, err error) { + if len(args) != 4 { + err = objects.ErrWrongNumArguments + return + } + + f1, ok := args[0].(*objects.Float) + if !ok { + err = objects.ErrInvalidTypeConversion + return + } + + s2, ok := objects.ToString(args[1]) + if !ok { + err = objects.ErrInvalidTypeConversion + return + } + + i3, ok := objects.ToInt(args[2]) + if !ok { + err = objects.ErrInvalidTypeConversion + return + } + + i4, ok := objects.ToInt(args[3]) + if !ok { + err = objects.ErrInvalidTypeConversion + return + } + + ret = &objects.String{Value: strconv.FormatFloat(f1.Value, s2[0], i3, i4)} + + return + }, + }, + // format_int(i, base) => string + "format_int": &objects.UserFunction{ + Value: func(args ...objects.Object) (ret objects.Object, err error) { + if len(args) != 2 { + err = objects.ErrWrongNumArguments + return + } + + i1, ok := args[0].(*objects.Int) + if !ok { + err = objects.ErrInvalidTypeConversion + return + } + + i2, ok := objects.ToInt(args[1]) + if !ok { + err = objects.ErrInvalidTypeConversion + return + } + + ret = &objects.String{Value: strconv.FormatInt(i1.Value, i2)} + + return + }, + }, + // itoa(i) => string + "itoa": FuncAIRS(strconv.Itoa), + // parse_bool(str) => bool/error + "parse_bool": &objects.UserFunction{ + Value: func(args ...objects.Object) (ret objects.Object, err error) { + if len(args) != 1 { + err = objects.ErrWrongNumArguments + return + } + + s1, ok := args[0].(*objects.String) + if !ok { + err = objects.ErrInvalidTypeConversion + return + } + + parsed, err := strconv.ParseBool(s1.Value) + if err != nil { + ret = wrapError(err) + return + } + + if parsed { + ret = objects.TrueValue + } else { + ret = objects.FalseValue + } + + return + }, + }, + // parse_float(str, bits) => float/error + "parse_float": &objects.UserFunction{ + Value: func(args ...objects.Object) (ret objects.Object, err error) { + if len(args) != 2 { + err = objects.ErrWrongNumArguments + return + } + + s1, ok := args[0].(*objects.String) + if !ok { + err = objects.ErrInvalidTypeConversion + return + } + + i2, ok := objects.ToInt(args[1]) + if !ok { + err = objects.ErrInvalidTypeConversion + return + } + + parsed, err := strconv.ParseFloat(s1.Value, i2) + if err != nil { + ret = wrapError(err) + return + } + + ret = &objects.Float{Value: parsed} + + return + }, + }, + // parse_int(str, base, bits) => int/error + "parse_int": &objects.UserFunction{ + Value: func(args ...objects.Object) (ret objects.Object, err error) { + if len(args) != 3 { + err = objects.ErrWrongNumArguments + return + } + + s1, ok := args[0].(*objects.String) + if !ok { + err = objects.ErrInvalidTypeConversion + return + } + + i2, ok := objects.ToInt(args[1]) + if !ok { + err = objects.ErrInvalidTypeConversion + return + } + + i3, ok := objects.ToInt(args[2]) + if !ok { + err = objects.ErrInvalidTypeConversion + return + } + + parsed, err := strconv.ParseInt(s1.Value, i2, i3) + if err != nil { + ret = wrapError(err) + return + } + + ret = &objects.Int{Value: parsed} + + return + }, + }, + // quote(str) => string + "quote": FuncASRS(strconv.Quote), + // unquote(str) => string/error + "unquote": FuncASRSE(strconv.Unquote), } func stringsRegexpImmutableMap(re *regexp.Regexp) *objects.ImmutableMap { diff --git a/compiler/stdlib/text_test.go b/compiler/stdlib/text_test.go index 17fcb22..5509fa6 100644 --- a/compiler/stdlib/text_test.go +++ b/compiler/stdlib/text_test.go @@ -180,4 +180,21 @@ func TestText(t *testing.T) { module(t, "text").call("contains", "abcde", "abcde").expect(true) module(t, "text").call("contains", "abc", "abcde").expect(false) module(t, "text").call("contains", "ab cd", "bc").expect(false) + + module(t, "text").call("replace", "", "", "", -1).expect("") + module(t, "text").call("replace", "abcd", "a", "x", -1).expect("xbcd") + module(t, "text").call("replace", "aaaa", "a", "x", -1).expect("xxxx") + module(t, "text").call("replace", "aaaa", "a", "x", 0).expect("aaaa") + module(t, "text").call("replace", "aaaa", "a", "x", 2).expect("xxaa") + module(t, "text").call("replace", "abcd", "bc", "x", -1).expect("axd") + + module(t, "text").call("format_bool", true).expect("true") + module(t, "text").call("format_bool", false).expect("false") + module(t, "text").call("format_float", -19.84, 'f', -1, 64).expect("-19.84") + module(t, "text").call("format_int", -1984, 10).expect("-1984") + module(t, "text").call("format_int", 1984, 8).expect("3700") + module(t, "text").call("parse_bool", "true").expect(true) + module(t, "text").call("parse_bool", "0").expect(false) + module(t, "text").call("parse_float", "-19.84", 64).expect(-19.84) + module(t, "text").call("parse_int", "-1984", 10, 64).expect(-1984) }