diff --git a/compiler/stdlib/func_typedefs.go b/compiler/stdlib/func_typedefs.go index 78fffae..d1640e9 100644 --- a/compiler/stdlib/func_typedefs.go +++ b/compiler/stdlib/func_typedefs.go @@ -34,6 +34,60 @@ func FuncARI(fn func() int) *objects.UserFunction { } } +// FuncARI64 transform a function of 'func() int64' signature +// into a user function object. +func FuncARI64(fn func() int64) *objects.UserFunction { + return &objects.UserFunction{ + Value: func(args ...objects.Object) (ret objects.Object, err error) { + if len(args) != 0 { + return nil, objects.ErrWrongNumArguments + } + + return &objects.Int{Value: fn()}, nil + }, + } +} + +// FuncAI64RI64 transform a function of 'func(int64) int64' signature +// into a user function object. +func FuncAI64RI64(fn func(int64) int64) *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.ToInt64(args[0]) + if !ok { + return nil, objects.ErrInvalidTypeConversion + } + + return &objects.Int{Value: fn(i1)}, nil + }, + } +} + +// FuncAI64R transform a function of 'func(int64)' signature +// into a user function object. +func FuncAI64R(fn func(int64)) *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.ToInt64(args[0]) + if !ok { + return nil, objects.ErrInvalidTypeConversion + } + + fn(i1) + + return objects.UndefinedValue, nil + }, + } +} + // FuncARB transform a function of 'func() bool' signature // into a user function object. func FuncARB(fn func() bool) *objects.UserFunction { @@ -175,6 +229,32 @@ func FuncARIsE(fn func() ([]int, error)) *objects.UserFunction { } } +// FuncAIRIs transform a function of 'func(int) []int' signature +// into a user function object. +func FuncAIRIs(fn func(int) []int) *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 + } + + res := fn(i1) + + arr := &objects.Array{} + for _, v := range res { + arr.Value = append(arr.Value, &objects.Int{Value: int64(v)}) + } + + return arr, nil + }, + } +} + // FuncAFRF transform a function of 'func(float64) float64' signature // into a user function object. func FuncAFRF(fn func(float64) float64) *objects.UserFunction { diff --git a/compiler/stdlib/func_typedefs_test.go b/compiler/stdlib/func_typedefs_test.go index aca7bfe..3128c01 100644 --- a/compiler/stdlib/func_typedefs_test.go +++ b/compiler/stdlib/func_typedefs_test.go @@ -16,7 +16,7 @@ func TestFuncAIR(t *testing.T) { ret, err := uf.Call(&objects.Int{Value: 10}) assert.NoError(t, err) assert.Equal(t, objects.UndefinedValue, ret) - ret, err = uf.Call() + _, err = uf.Call() assert.Equal(t, objects.ErrWrongNumArguments, err) } @@ -25,7 +25,7 @@ func TestFuncAR(t *testing.T) { ret, err := uf.Call() assert.NoError(t, err) assert.Equal(t, objects.UndefinedValue, ret) - ret, err = uf.Call(objects.TrueValue) + _, err = uf.Call(objects.TrueValue) assert.Equal(t, objects.ErrWrongNumArguments, err) } @@ -34,7 +34,7 @@ func TestFuncARI(t *testing.T) { ret, err := uf.Call() assert.NoError(t, err) assert.Equal(t, &objects.Int{Value: 10}, ret) - ret, err = uf.Call(objects.TrueValue) + _, err = uf.Call(objects.TrueValue) assert.Equal(t, objects.ErrWrongNumArguments, err) } @@ -47,7 +47,7 @@ func TestFuncARE(t *testing.T) { ret, err = uf.Call() assert.NoError(t, err) assert.Equal(t, &objects.Error{Value: &objects.String{Value: "some error"}}, ret) - ret, err = uf.Call(objects.TrueValue) + _, err = uf.Call(objects.TrueValue) assert.Equal(t, objects.ErrWrongNumArguments, err) } @@ -60,7 +60,7 @@ func TestFuncARIsE(t *testing.T) { ret, err = uf.Call() assert.NoError(t, err) assert.Equal(t, &objects.Error{Value: &objects.String{Value: "some error"}}, ret) - ret, err = uf.Call(objects.TrueValue) + _, err = uf.Call(objects.TrueValue) assert.Equal(t, objects.ErrWrongNumArguments, err) } @@ -69,7 +69,7 @@ func TestFuncARS(t *testing.T) { ret, err := uf.Call() assert.NoError(t, err) assert.Equal(t, &objects.String{Value: "foo"}, ret) - ret, err = uf.Call(objects.TrueValue) + _, err = uf.Call(objects.TrueValue) assert.Equal(t, objects.ErrWrongNumArguments, err) } @@ -82,7 +82,7 @@ func TestFuncARSE(t *testing.T) { ret, err = uf.Call() assert.NoError(t, err) assert.Equal(t, &objects.Error{Value: &objects.String{Value: "some error"}}, ret) - ret, err = uf.Call(objects.TrueValue) + _, err = uf.Call(objects.TrueValue) assert.Equal(t, objects.ErrWrongNumArguments, err) } @@ -91,7 +91,7 @@ func TestFuncARSs(t *testing.T) { ret, err := uf.Call() assert.NoError(t, err) assert.Equal(t, array(&objects.String{Value: "foo"}, &objects.String{Value: "bar"}), ret) - ret, err = uf.Call(objects.TrueValue) + _, err = uf.Call(objects.TrueValue) assert.Equal(t, objects.ErrWrongNumArguments, err) } @@ -104,7 +104,7 @@ func TestFuncASRE(t *testing.T) { ret, err = uf.Call(&objects.String{Value: "foo"}) assert.NoError(t, err) assert.Equal(t, &objects.Error{Value: &objects.String{Value: "some error"}}, ret) - ret, err = uf.Call() + _, err = uf.Call() assert.Equal(t, objects.ErrWrongNumArguments, err) } @@ -113,7 +113,7 @@ func TestFuncASRS(t *testing.T) { ret, err := uf.Call(&objects.String{Value: "foo"}) assert.NoError(t, err) assert.Equal(t, &objects.String{Value: "foo"}, ret) - ret, err = uf.Call() + _, err = uf.Call() assert.Equal(t, objects.ErrWrongNumArguments, err) } @@ -122,7 +122,7 @@ func TestFuncASRSs(t *testing.T) { ret, err := uf.Call(&objects.String{Value: "foo"}) assert.NoError(t, err) assert.Equal(t, array(&objects.String{Value: "foo"}), ret) - ret, err = uf.Call() + _, err = uf.Call() assert.Equal(t, objects.ErrWrongNumArguments, err) } @@ -135,7 +135,7 @@ func TestFuncASI64RE(t *testing.T) { ret, err = uf.Call(&objects.String{Value: "foo"}, &objects.Int{Value: 5}) assert.NoError(t, err) assert.Equal(t, &objects.Error{Value: &objects.String{Value: "some error"}}, ret) - ret, err = uf.Call() + _, err = uf.Call() assert.Equal(t, objects.ErrWrongNumArguments, err) } @@ -148,7 +148,7 @@ func TestFuncAIIRE(t *testing.T) { ret, err = uf.Call(&objects.Int{Value: 5}, &objects.Int{Value: 7}) assert.NoError(t, err) assert.Equal(t, &objects.Error{Value: &objects.String{Value: "some error"}}, ret) - ret, err = uf.Call() + _, err = uf.Call() assert.Equal(t, objects.ErrWrongNumArguments, err) } @@ -161,7 +161,7 @@ func TestFuncASIIRE(t *testing.T) { ret, err = uf.Call(&objects.String{Value: "foo"}, &objects.Int{Value: 5}, &objects.Int{Value: 7}) assert.NoError(t, err) assert.Equal(t, &objects.Error{Value: &objects.String{Value: "some error"}}, ret) - ret, err = uf.Call() + _, err = uf.Call() assert.Equal(t, objects.ErrWrongNumArguments, err) } @@ -174,7 +174,7 @@ func TestFuncASRSE(t *testing.T) { ret, err = uf.Call(&objects.String{Value: "foo"}) assert.NoError(t, err) assert.Equal(t, &objects.Error{Value: &objects.String{Value: "some error"}}, ret) - ret, err = uf.Call() + _, err = uf.Call() assert.Equal(t, objects.ErrWrongNumArguments, err) } @@ -186,7 +186,7 @@ func TestFuncASSRE(t *testing.T) { ret, err = uf.Call(&objects.String{Value: "foo"}, &objects.String{Value: "bar"}) assert.NoError(t, err) assert.Equal(t, &objects.Error{Value: &objects.String{Value: "some error"}}, ret) - ret, err = uf.Call(&objects.String{Value: "foo"}) + _, err = uf.Call(&objects.String{Value: "foo"}) assert.Equal(t, objects.ErrWrongNumArguments, err) } @@ -195,31 +195,27 @@ func TestFuncASsRS(t *testing.T) { ret, err := uf.Call(array(&objects.String{Value: "foo"}, &objects.String{Value: "bar"}), &objects.String{Value: " "}) assert.NoError(t, err) assert.Equal(t, &objects.String{Value: "foo bar"}, ret) - ret, err = uf.Call(&objects.String{Value: "foo"}) + _, err = uf.Call(&objects.String{Value: "foo"}) assert.Equal(t, objects.ErrWrongNumArguments, err) } func TestFuncARF(t *testing.T) { - uf := stdlib.FuncARF(func() float64 { - return 10.0 - }) + uf := stdlib.FuncARF(func() float64 { return 10.0 }) ret, err := uf.Call() assert.NoError(t, err) assert.Equal(t, &objects.Float{Value: 10.0}, ret) - ret, err = uf.Call(objects.TrueValue) + _, err = uf.Call(objects.TrueValue) assert.Equal(t, objects.ErrWrongNumArguments, err) } func TestFuncAFRF(t *testing.T) { - uf := stdlib.FuncAFRF(func(a float64) float64 { - return a - }) + uf := stdlib.FuncAFRF(func(a float64) float64 { return a }) ret, err := uf.Call(&objects.Float{Value: 10.0}) assert.NoError(t, err) assert.Equal(t, &objects.Float{Value: 10.0}, ret) - ret, err = uf.Call() + _, err = uf.Call() assert.Equal(t, objects.ErrWrongNumArguments, err) - ret, err = uf.Call(objects.TrueValue, objects.TrueValue) + _, err = uf.Call(objects.TrueValue, objects.TrueValue) assert.Equal(t, objects.ErrWrongNumArguments, err) } @@ -230,9 +226,9 @@ func TestFuncAIRF(t *testing.T) { ret, err := uf.Call(&objects.Int{Value: 10.0}) assert.NoError(t, err) assert.Equal(t, &objects.Float{Value: 10.0}, ret) - ret, err = uf.Call() + _, err = uf.Call() assert.Equal(t, objects.ErrWrongNumArguments, err) - ret, err = uf.Call(objects.TrueValue, objects.TrueValue) + _, err = uf.Call(objects.TrueValue, objects.TrueValue) assert.Equal(t, objects.ErrWrongNumArguments, err) } @@ -243,9 +239,9 @@ func TestFuncAFRI(t *testing.T) { ret, err := uf.Call(&objects.Float{Value: 10.5}) assert.NoError(t, err) assert.Equal(t, &objects.Int{Value: 10}, ret) - ret, err = uf.Call() + _, err = uf.Call() assert.Equal(t, objects.ErrWrongNumArguments, err) - ret, err = uf.Call(objects.TrueValue, objects.TrueValue) + _, err = uf.Call(objects.TrueValue, objects.TrueValue) assert.Equal(t, objects.ErrWrongNumArguments, err) } @@ -256,9 +252,9 @@ func TestFuncAFRB(t *testing.T) { ret, err := uf.Call(&objects.Float{Value: 0.1}) assert.NoError(t, err) assert.Equal(t, objects.TrueValue, ret) - ret, err = uf.Call() + _, err = uf.Call() assert.Equal(t, objects.ErrWrongNumArguments, err) - ret, err = uf.Call(objects.TrueValue, objects.TrueValue) + _, err = uf.Call(objects.TrueValue, objects.TrueValue) assert.Equal(t, objects.ErrWrongNumArguments, err) } @@ -269,9 +265,9 @@ func TestFuncAFFRF(t *testing.T) { ret, err := uf.Call(&objects.Float{Value: 10.0}, &objects.Float{Value: 20.0}) assert.NoError(t, err) assert.Equal(t, &objects.Float{Value: 30.0}, ret) - ret, err = uf.Call() + _, err = uf.Call() assert.Equal(t, objects.ErrWrongNumArguments, err) - ret, err = uf.Call(objects.TrueValue) + _, err = uf.Call(objects.TrueValue) assert.Equal(t, objects.ErrWrongNumArguments, err) } @@ -280,9 +276,9 @@ func TestFuncASIRS(t *testing.T) { ret, err := uf.Call(&objects.String{Value: "ab"}, &objects.Int{Value: 2}) assert.NoError(t, err) assert.Equal(t, &objects.String{Value: "abab"}, ret) - ret, err = uf.Call() + _, err = uf.Call() assert.Equal(t, objects.ErrWrongNumArguments, err) - ret, err = uf.Call(objects.TrueValue) + _, err = uf.Call(objects.TrueValue) assert.Equal(t, objects.ErrWrongNumArguments, err) } @@ -293,9 +289,9 @@ func TestFuncAIFRF(t *testing.T) { ret, err := uf.Call(&objects.Int{Value: 10}, &objects.Float{Value: 20.0}) assert.NoError(t, err) assert.Equal(t, &objects.Float{Value: 30.0}, ret) - ret, err = uf.Call() + _, err = uf.Call() assert.Equal(t, objects.ErrWrongNumArguments, err) - ret, err = uf.Call(objects.TrueValue) + _, err = uf.Call(objects.TrueValue) assert.Equal(t, objects.ErrWrongNumArguments, err) } @@ -306,9 +302,9 @@ func TestFuncAFIRF(t *testing.T) { ret, err := uf.Call(&objects.Float{Value: 10.0}, &objects.Int{Value: 20}) assert.NoError(t, err) assert.Equal(t, &objects.Float{Value: 30.0}, ret) - ret, err = uf.Call() + _, err = uf.Call() assert.Equal(t, objects.ErrWrongNumArguments, err) - ret, err = uf.Call(objects.TrueValue) + _, err = uf.Call(objects.TrueValue) assert.Equal(t, objects.ErrWrongNumArguments, err) } @@ -319,9 +315,9 @@ func TestFuncAFIRB(t *testing.T) { ret, err := uf.Call(&objects.Float{Value: 10.0}, &objects.Int{Value: 20}) assert.NoError(t, err) assert.Equal(t, objects.TrueValue, ret) - ret, err = uf.Call() + _, err = uf.Call() assert.Equal(t, objects.ErrWrongNumArguments, err) - ret, err = uf.Call(objects.TrueValue) + _, err = uf.Call(objects.TrueValue) assert.Equal(t, objects.ErrWrongNumArguments, err) } @@ -338,7 +334,7 @@ func TestFuncAIRSsE(t *testing.T) { ret, err = uf.Call(&objects.Int{Value: 10}) assert.NoError(t, err) assert.Equal(t, &objects.Error{Value: &objects.String{Value: "some error"}}, ret) - ret, err = uf.Call() + _, err = uf.Call() assert.Equal(t, objects.ErrWrongNumArguments, err) } @@ -347,7 +343,7 @@ func TestFuncASSRSs(t *testing.T) { ret, err := uf.Call(&objects.String{Value: "foo"}, &objects.String{Value: "bar"}) assert.NoError(t, err) assert.Equal(t, array(&objects.String{Value: "foo"}, &objects.String{Value: "bar"}), ret) - ret, err = uf.Call() + _, err = uf.Call() assert.Equal(t, objects.ErrWrongNumArguments, err) } @@ -356,7 +352,7 @@ func TestFuncASSIRSs(t *testing.T) { ret, err := uf.Call(&objects.String{Value: "foo"}, &objects.String{Value: "bar"}, &objects.Int{Value: 5}) assert.NoError(t, err) assert.Equal(t, array(&objects.String{Value: "foo"}, &objects.String{Value: "bar"}, &objects.String{Value: "5"}), ret) - ret, err = uf.Call() + _, err = uf.Call() assert.Equal(t, objects.ErrWrongNumArguments, err) } @@ -365,7 +361,7 @@ func TestFuncARB(t *testing.T) { ret, err := uf.Call() assert.NoError(t, err) assert.Equal(t, objects.TrueValue, ret) - ret, err = uf.Call(objects.TrueValue) + _, err = uf.Call(objects.TrueValue) assert.Equal(t, objects.ErrWrongNumArguments, err) } @@ -382,7 +378,7 @@ func TestFuncARYE(t *testing.T) { ret, err = uf.Call() assert.NoError(t, err) assert.Equal(t, &objects.Error{Value: &objects.String{Value: "some error"}}, ret) - ret, err = uf.Call(objects.TrueValue) + _, err = uf.Call(objects.TrueValue) assert.Equal(t, objects.ErrWrongNumArguments, err) } @@ -395,7 +391,7 @@ func TestFuncASRIE(t *testing.T) { ret, err = uf.Call(&objects.String{Value: "foo"}) assert.NoError(t, err) assert.Equal(t, &objects.Error{Value: &objects.String{Value: "some error"}}, ret) - ret, err = uf.Call() + _, err = uf.Call() assert.Equal(t, objects.ErrWrongNumArguments, err) } @@ -408,7 +404,7 @@ func TestFuncAYRIE(t *testing.T) { ret, err = uf.Call(&objects.Bytes{Value: []byte("foo")}) assert.NoError(t, err) assert.Equal(t, &objects.Error{Value: &objects.String{Value: "some error"}}, ret) - ret, err = uf.Call() + _, err = uf.Call() assert.Equal(t, objects.ErrWrongNumArguments, err) } @@ -417,7 +413,7 @@ func TestFuncASSRI(t *testing.T) { ret, err := uf.Call(&objects.String{Value: "foo"}, &objects.String{Value: "bar"}) assert.NoError(t, err) assert.Equal(t, &objects.Int{Value: 6}, ret) - ret, err = uf.Call(&objects.String{Value: "foo"}) + _, err = uf.Call(&objects.String{Value: "foo"}) assert.Equal(t, objects.ErrWrongNumArguments, err) } @@ -426,7 +422,7 @@ func TestFuncASSRS(t *testing.T) { ret, err := uf.Call(&objects.String{Value: "foo"}, &objects.String{Value: "bar"}) assert.NoError(t, err) assert.Equal(t, &objects.String{Value: "foobar"}, ret) - ret, err = uf.Call(&objects.String{Value: "foo"}) + _, err = uf.Call(&objects.String{Value: "foo"}) assert.Equal(t, objects.ErrWrongNumArguments, err) } @@ -435,7 +431,7 @@ func TestFuncASSRB(t *testing.T) { ret, err := uf.Call(&objects.String{Value: "123"}, &objects.String{Value: "12"}) assert.NoError(t, err) assert.Equal(t, objects.TrueValue, ret) - ret, err = uf.Call(&objects.String{Value: "foo"}) + _, err = uf.Call(&objects.String{Value: "foo"}) assert.Equal(t, objects.ErrWrongNumArguments, err) } @@ -444,7 +440,52 @@ func TestFuncAIRS(t *testing.T) { ret, err := uf.Call(&objects.Int{Value: 55}) assert.NoError(t, err) assert.Equal(t, &objects.String{Value: "55"}, ret) - ret, err = uf.Call() + _, err = uf.Call() + assert.Equal(t, objects.ErrWrongNumArguments, err) +} + +func TestFuncAIRIs(t *testing.T) { + uf := stdlib.FuncAIRIs(func(a int) []int { return []int{a, a} }) + ret, err := uf.Call(&objects.Int{Value: 55}) + assert.NoError(t, err) + assert.Equal(t, array(&objects.Int{Value: 55}, &objects.Int{Value: 55}), ret) + _, err = uf.Call() + assert.Equal(t, objects.ErrWrongNumArguments, err) +} + +func TestFuncAI64R(t *testing.T) { + uf := stdlib.FuncAIR(func(a int) {}) + ret, err := uf.Call(&objects.Int{Value: 55}) + assert.NoError(t, err) + assert.Equal(t, objects.UndefinedValue, ret) + _, err = uf.Call() + assert.Equal(t, objects.ErrWrongNumArguments, err) +} + +func TestFuncARI64(t *testing.T) { + uf := stdlib.FuncARI64(func() int64 { return 55 }) + ret, err := uf.Call() + assert.NoError(t, err) + assert.Equal(t, &objects.Int{Value: 55}, ret) + _, err = uf.Call(&objects.Int{Value: 55}) + assert.Equal(t, objects.ErrWrongNumArguments, err) +} + +func TestFuncASsSRS(t *testing.T) { + uf := stdlib.FuncASsSRS(func(a []string, b string) string { return strings.Join(a, b) }) + ret, err := uf.Call(array(&objects.String{Value: "abc"}, &objects.String{Value: "def"}), &objects.String{Value: "-"}) + assert.NoError(t, err) + assert.Equal(t, &objects.String{Value: "abc-def"}, ret) + _, err = uf.Call() + assert.Equal(t, objects.ErrWrongNumArguments, err) +} + +func TestFuncAI64RI64(t *testing.T) { + uf := stdlib.FuncAI64RI64(func(a int64) int64 { return a * 2 }) + ret, err := uf.Call(&objects.Int{Value: 55}) + assert.NoError(t, err) + assert.Equal(t, &objects.Int{Value: 110}, ret) + _, err = uf.Call() assert.Equal(t, objects.ErrWrongNumArguments, err) } diff --git a/compiler/stdlib/rand.go b/compiler/stdlib/rand.go new file mode 100644 index 0000000..4ce70ff --- /dev/null +++ b/compiler/stdlib/rand.go @@ -0,0 +1,87 @@ +package stdlib + +import ( + "math/rand" + + "github.com/d5/tengo/objects" +) + +var randModule = map[string]objects.Object{ + "int": FuncARI64(rand.Int63), + "float": FuncARF(rand.Float64), + "intn": FuncAI64RI64(rand.Int63n), + "exp_float": FuncARF(rand.ExpFloat64), + "norm_float": FuncARF(rand.NormFloat64), + "perm": FuncAIRIs(rand.Perm), + "seed": FuncAI64R(rand.Seed), + "read": &objects.UserFunction{ + Value: func(args ...objects.Object) (ret objects.Object, err error) { + if len(args) != 1 { + return nil, objects.ErrWrongNumArguments + } + + y1, ok := args[0].(*objects.Bytes) + if !ok { + return nil, objects.ErrInvalidTypeConversion + } + + res, err := rand.Read(y1.Value) + if err != nil { + ret = wrapError(err) + return + } + + return &objects.Int{Value: int64(res)}, nil + }, + }, + "rand": &objects.UserFunction{ + Value: func(args ...objects.Object) (ret objects.Object, err error) { + if len(args) != 1 { + return nil, objects.ErrWrongNumArguments + } + + i1, ok := objects.ToInt64(args[0]) + if !ok { + return nil, objects.ErrInvalidTypeConversion + } + + src := rand.NewSource(i1) + + return randRand(rand.New(src)), nil + }, + }, +} + +func randRand(r *rand.Rand) *objects.ImmutableMap { + return &objects.ImmutableMap{ + Value: map[string]objects.Object{ + "int": FuncARI64(r.Int63), + "float": FuncARF(r.Float64), + "intn": FuncAI64RI64(r.Int63n), + "exp_float": FuncARF(r.ExpFloat64), + "norm_float": FuncARF(r.NormFloat64), + "perm": FuncAIRIs(r.Perm), + "seed": FuncAI64R(r.Seed), + "read": &objects.UserFunction{ + Value: func(args ...objects.Object) (ret objects.Object, err error) { + if len(args) != 1 { + return nil, objects.ErrWrongNumArguments + } + + y1, ok := args[0].(*objects.Bytes) + if !ok { + return nil, objects.ErrInvalidTypeConversion + } + + res, err := r.Read(y1.Value) + if err != nil { + ret = wrapError(err) + return + } + + return &objects.Int{Value: int64(res)}, nil + }, + }, + }, + } +} diff --git a/compiler/stdlib/rand_test.go b/compiler/stdlib/rand_test.go new file mode 100644 index 0000000..ad08711 --- /dev/null +++ b/compiler/stdlib/rand_test.go @@ -0,0 +1,45 @@ +package stdlib_test + +import ( + "math/rand" + "testing" + + "github.com/d5/tengo/assert" + "github.com/d5/tengo/objects" +) + +func TestRand(t *testing.T) { + var seed int64 = 1234 + r := rand.New(rand.NewSource(seed)) + + module(t, "rand").call("seed", seed).expect(objects.UndefinedValue) + module(t, "rand").call("int").expect(r.Int63()) + module(t, "rand").call("float").expect(r.Float64()) + module(t, "rand").call("intn", 111).expect(r.Int63n(111)) + module(t, "rand").call("exp_float").expect(r.ExpFloat64()) + module(t, "rand").call("norm_float").expect(r.NormFloat64()) + module(t, "rand").call("perm", 10).expect(r.Perm(10)) + + buf1 := make([]byte, 10) + buf2 := &objects.Bytes{Value: make([]byte, 10)} + n, _ := r.Read(buf1) + module(t, "rand").call("read", buf2).expect(n) + assert.Equal(t, buf1, buf2.Value) + + seed = 9191 + r = rand.New(rand.NewSource(seed)) + randObj := module(t, "rand").call("rand", seed) + randObj.call("seed", seed).expect(objects.UndefinedValue) + randObj.call("int").expect(r.Int63()) + randObj.call("float").expect(r.Float64()) + randObj.call("intn", 111).expect(r.Int63n(111)) + randObj.call("exp_float").expect(r.ExpFloat64()) + randObj.call("norm_float").expect(r.NormFloat64()) + randObj.call("perm", 10).expect(r.Perm(10)) + + buf1 = make([]byte, 12) + buf2 = &objects.Bytes{Value: make([]byte, 12)} + n, _ = r.Read(buf1) + randObj.call("read", buf2).expect(n) + assert.Equal(t, buf1, buf2.Value) +} diff --git a/compiler/stdlib/stdlib.go b/compiler/stdlib/stdlib.go index 412ce62..7c3068b 100644 --- a/compiler/stdlib/stdlib.go +++ b/compiler/stdlib/stdlib.go @@ -8,4 +8,5 @@ var Modules = map[string]*objects.ImmutableMap{ "os": {Value: osModule}, "text": {Value: textModule}, "times": {Value: timesModule}, + "rand": {Value: randModule}, } diff --git a/compiler/stdlib/stdlib_test.go b/compiler/stdlib/stdlib_test.go index 37b936a..16b7728 100644 --- a/compiler/stdlib/stdlib_test.go +++ b/compiler/stdlib/stdlib_test.go @@ -122,6 +122,13 @@ func object(v interface{}) objects.Object { return &objects.ImmutableArray{Value: objs} case time.Time: return &objects.Time{Value: v} + case []int: + var objs []objects.Object + for _, e := range v { + objs = append(objs, &objects.Int{Value: int64(e)}) + } + + return &objects.Array{Value: objs} } panic(fmt.Errorf("unknown type: %T", v)) diff --git a/docs/stdlib-rand.md b/docs/stdlib-rand.md new file mode 100644 index 0000000..94ef805 --- /dev/null +++ b/docs/stdlib-rand.md @@ -0,0 +1,28 @@ +# Module - "rand" + +```golang +rand := import("rand") +``` + +## Functions + +- `seed(seed int)`: uses the provided seed value to initialize the default Source to a deterministic state. +- `exp_float() => float`: returns an exponentially distributed float64 in the range (0, +math.MaxFloat64] with an exponential distribution whose rate parameter (lambda) is 1 and whose mean is 1/lambda (1) from the default Source. +- `float() => float`: returns, as a float64, a pseudo-random number in [0.0,1.0) from the default Source. +- `int() => int`: returns a non-negative pseudo-random 63-bit integer as an int64 from the default Source. +- `intn(n int) => int`: returns, as an int64, a non-negative pseudo-random number in [0,n) from the default Source. It panics if n <= 0. +- `norm_float) => float`: returns a normally distributed float64 in the range [-math.MaxFloat64, +math.MaxFloat64] with standard normal distribution (mean = 0, stddev = 1) from the default Source. +- `perm(n int) => [int]`: returns, as a slice of n ints, a pseudo-random permutation of the integers [0,n) from the default Source. +- `read(p bytes) => int/error`: generates len(p) random bytes from the default Source and writes them into p. It always returns len(p) and a nil error. +- `rand(src_seed int) => Rand`: returns a new Rand that uses random values from src to generate other random values. + +## Rand + +- `seed(seed int)`: uses the provided seed value to initialize the default Source to a deterministic state. +- `exp_float() => float`: returns an exponentially distributed float64 in the range (0, +math.MaxFloat64] with an exponential distribution whose rate parameter (lambda) is 1 and whose mean is 1/lambda (1) from the default Source. +- `float() => float`: returns, as a float64, a pseudo-random number in [0.0,1.0) from the default Source. +- `int() => int`: returns a non-negative pseudo-random 63-bit integer as an int64 from the default Source. +- `intn(n int) => int`: returns, as an int64, a non-negative pseudo-random number in [0,n) from the default Source. It panics if n <= 0. +- `norm_float) => float`: returns a normally distributed float64 in the range [-math.MaxFloat64, +math.MaxFloat64] with standard normal distribution (mean = 0, stddev = 1) from the default Source. +- `perm(n int) => [int]`: returns, as a slice of n ints, a pseudo-random permutation of the integers [0,n) from the default Source. +- `read(p bytes) => int/error`: generates len(p) random bytes from the default Source and writes them into p. It always returns len(p) and a nil error. \ No newline at end of file diff --git a/docs/stdlib.md b/docs/stdlib.md index facce10..19707e0 100644 --- a/docs/stdlib.md +++ b/docs/stdlib.md @@ -3,4 +3,5 @@ - [os](https://github.com/d5/tengo/blob/master/docs/stdlib-os.md): platform-independent interface to operating system functionality. - [text](https://github.com/d5/tengo/blob/master/docs/stdlib-text.md): regular expressions, string conversion, and manipulation - [math](https://github.com/d5/tengo/blob/master/docs/stdlib-math.md): mathematical constants and functions -- [times](https://github.com/d5/tengo/blob/master/docs/stdlib-times.md): time-related functions \ No newline at end of file +- [times](https://github.com/d5/tengo/blob/master/docs/stdlib-times.md): time-related functions +- [rand](https://github.com/d5/tengo/blob/master/docs/stdlib-rand.md): random functions \ No newline at end of file