diff --git a/compiler/stdmods/errors.go b/compiler/stdmods/errors.go new file mode 100644 index 0000000..3c265eb --- /dev/null +++ b/compiler/stdmods/errors.go @@ -0,0 +1,11 @@ +package stdmods + +import "github.com/d5/tengo/objects" + +func wrapError(err error) objects.Object { + if err == nil { + return objects.TrueValue + } + + return &objects.Error{Value: &objects.String{Value: err.Error()}} +} diff --git a/compiler/stdmods/func_typedefs.go b/compiler/stdmods/func_typedefs.go index cf71edd..7db07f8 100644 --- a/compiler/stdmods/func_typedefs.go +++ b/compiler/stdmods/func_typedefs.go @@ -1,28 +1,21 @@ package stdmods import ( - "fmt" - "github.com/d5/tengo/objects" ) -// FuncAFRF transform a function of 'func(float64) float64' signature +// FuncAR transform a function of 'func()' signature // into a user function object. -func FuncAFRF(fn func(float64) float64) *objects.UserFunction { +func FuncAR(fn func()) *objects.UserFunction { return &objects.UserFunction{ Value: func(args ...objects.Object) (ret objects.Object, err error) { - if len(args) != 1 { + if len(args) != 0 { return nil, objects.ErrWrongNumArguments } - switch arg := args[0].(type) { - case *objects.Int: - return &objects.Float{Value: fn(float64(arg.Value))}, nil - case *objects.Float: - return &objects.Float{Value: fn(arg.Value)}, nil - default: - return nil, fmt.Errorf("invalid argument type: %s", arg.TypeName()) - } + fn() + + return objects.UndefinedValue, nil }, } } @@ -41,6 +34,44 @@ func FuncARF(fn func() float64) *objects.UserFunction { } } +// FuncARSs transform a function of 'func() []string' signature +// into a user function object. +func FuncARSs(fn func() []string) *objects.UserFunction { + return &objects.UserFunction{ + Value: func(args ...objects.Object) (ret objects.Object, err error) { + if len(args) != 0 { + return nil, objects.ErrWrongNumArguments + } + + arr := &objects.Array{} + for _, osArg := range fn() { + arr.Value = append(arr.Value, &objects.String{Value: osArg}) + } + + 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 { + return &objects.UserFunction{ + Value: func(args ...objects.Object) (ret objects.Object, err error) { + if len(args) != 1 { + return nil, objects.ErrWrongNumArguments + } + + f1, ok := objects.ToFloat64(args[0]) + if !ok { + return nil, objects.ErrInvalidTypeConversion + } + + return &objects.Float{Value: fn(f1)}, nil + }, + } +} + // FuncAIRF transform a function of 'func(int) float64' signature // into a user function object. func FuncAIRF(fn func(int) float64) *objects.UserFunction { @@ -50,14 +81,12 @@ func FuncAIRF(fn func(int) float64) *objects.UserFunction { return nil, objects.ErrWrongNumArguments } - switch arg := args[0].(type) { - case *objects.Int: - return &objects.Float{Value: fn(int(arg.Value))}, nil - case *objects.Float: - return &objects.Float{Value: fn(int(arg.Value))}, nil - default: - return nil, fmt.Errorf("invalid argument type: %s", arg.TypeName()) + i1, ok := objects.ToInt(args[0]) + if !ok { + return nil, objects.ErrInvalidTypeConversion } + + return &objects.Float{Value: fn(i1)}, nil }, } } @@ -71,14 +100,12 @@ func FuncAFRI(fn func(float64) int) *objects.UserFunction { return nil, objects.ErrWrongNumArguments } - switch arg := args[0].(type) { - case *objects.Int: - return &objects.Int{Value: int64(fn(float64(arg.Value)))}, nil - case *objects.Float: - return &objects.Int{Value: int64(fn(arg.Value))}, nil - default: - return nil, fmt.Errorf("invalid argument type: %s", arg.TypeName()) + f1, ok := objects.ToFloat64(args[0]) + if !ok { + return nil, objects.ErrInvalidTypeConversion } + + return &objects.Int{Value: int64(fn(f1))}, nil }, } } @@ -92,26 +119,17 @@ func FuncAFFRF(fn func(float64, float64) float64) *objects.UserFunction { return nil, objects.ErrWrongNumArguments } - var arg0, arg1 float64 - - switch arg := args[0].(type) { - case *objects.Int: - arg0 = float64(arg.Value) - case *objects.Float: - arg0 = arg.Value - default: - return nil, fmt.Errorf("invalid argument type: %s", arg.TypeName()) - } - switch arg := args[1].(type) { - case *objects.Int: - arg1 = float64(arg.Value) - case *objects.Float: - arg1 = arg.Value - default: - return nil, fmt.Errorf("invalid argument type: %s", arg.TypeName()) + f1, ok := objects.ToFloat64(args[0]) + if !ok { + return nil, objects.ErrInvalidTypeConversion } - return &objects.Float{Value: fn(arg0, arg1)}, nil + f2, ok := objects.ToFloat64(args[1]) + if !ok { + return nil, objects.ErrInvalidTypeConversion + } + + return &objects.Float{Value: fn(f1, f2)}, nil }, } } @@ -125,27 +143,17 @@ func FuncAIFRF(fn func(int, float64) float64) *objects.UserFunction { return nil, objects.ErrWrongNumArguments } - var arg0 int - var arg1 float64 - - switch arg := args[0].(type) { - case *objects.Int: - arg0 = int(arg.Value) - case *objects.Float: - arg0 = int(arg.Value) - default: - return nil, fmt.Errorf("invalid argument type: %s", arg.TypeName()) - } - switch arg := args[1].(type) { - case *objects.Int: - arg1 = float64(arg.Value) - case *objects.Float: - arg1 = arg.Value - default: - return nil, fmt.Errorf("invalid argument type: %s", arg.TypeName()) + i1, ok := objects.ToInt(args[0]) + if !ok { + return nil, objects.ErrInvalidTypeConversion } - return &objects.Float{Value: fn(arg0, arg1)}, nil + f2, ok := objects.ToFloat64(args[1]) + if !ok { + return nil, objects.ErrInvalidTypeConversion + } + + return &objects.Float{Value: fn(i1, f2)}, nil }, } } @@ -159,27 +167,17 @@ func FuncAFIRF(fn func(float64, int) float64) *objects.UserFunction { return nil, objects.ErrWrongNumArguments } - var arg0 float64 - var arg1 int - - switch arg := args[0].(type) { - case *objects.Int: - arg0 = float64(arg.Value) - case *objects.Float: - arg0 = float64(arg.Value) - default: - return nil, fmt.Errorf("invalid argument type: %s", arg.TypeName()) - } - switch arg := args[1].(type) { - case *objects.Int: - arg1 = int(arg.Value) - case *objects.Float: - arg1 = int(arg.Value) - default: - return nil, fmt.Errorf("invalid argument type: %s", arg.TypeName()) + f1, ok := objects.ToFloat64(args[0]) + if !ok { + return nil, objects.ErrInvalidTypeConversion } - return &objects.Float{Value: fn(arg0, arg1)}, nil + i2, ok := objects.ToInt(args[1]) + if !ok { + return nil, objects.ErrInvalidTypeConversion + } + + return &objects.Float{Value: fn(f1, i2)}, nil }, } } @@ -193,27 +191,17 @@ func FuncAFIRB(fn func(float64, int) bool) *objects.UserFunction { return nil, objects.ErrWrongNumArguments } - var arg0 float64 - var arg1 int - - switch arg := args[0].(type) { - case *objects.Int: - arg0 = float64(arg.Value) - case *objects.Float: - arg0 = arg.Value - default: - return nil, fmt.Errorf("invalid argument type: %s", arg.TypeName()) - } - switch arg := args[1].(type) { - case *objects.Int: - arg1 = int(arg.Value) - case *objects.Float: - arg1 = int(arg.Value) - default: - return nil, fmt.Errorf("invalid argument type: %s", arg.TypeName()) + f1, ok := objects.ToFloat64(args[0]) + if !ok { + return nil, objects.ErrInvalidTypeConversion } - return &objects.Bool{Value: fn(arg0, arg1)}, nil + i2, ok := objects.ToInt(args[1]) + if !ok { + return nil, objects.ErrInvalidTypeConversion + } + + return &objects.Bool{Value: fn(f1, i2)}, nil }, } } @@ -227,17 +215,31 @@ func FuncAFRB(fn func(float64) bool) *objects.UserFunction { return nil, objects.ErrWrongNumArguments } - var arg0 float64 - switch arg := args[0].(type) { - case *objects.Int: - arg0 = float64(arg.Value) - case *objects.Float: - arg0 = arg.Value - default: - return nil, fmt.Errorf("invalid argument type: %s", arg.TypeName()) + f1, ok := objects.ToFloat64(args[0]) + if !ok { + return nil, objects.ErrInvalidTypeConversion } - return &objects.Bool{Value: fn(arg0)}, nil + return &objects.Bool{Value: fn(f1)}, nil + }, + } +} + +// FuncASRE transform a function of 'func(string) error' signature into a user function object. +// User function will return 'true' if underlying native function returns nil. +func FuncASRE(fn func(string) error) *objects.UserFunction { + return &objects.UserFunction{ + Value: func(args ...objects.Object) (objects.Object, error) { + if len(args) != 1 { + return nil, objects.ErrWrongNumArguments + } + + s1, ok := objects.ToString(args[0]) + if !ok { + return nil, objects.ErrInvalidTypeConversion + } + + return wrapError(fn(s1)), nil }, } } diff --git a/compiler/stdmods/os.go b/compiler/stdmods/os.go new file mode 100644 index 0000000..c04fa94 --- /dev/null +++ b/compiler/stdmods/os.go @@ -0,0 +1,103 @@ +package stdmods + +import ( + "os" + + "github.com/d5/tengo/objects" +) + +var osModule = map[string]objects.Object{ + "o_rdonly": &objects.Int{Value: int64(os.O_RDONLY)}, + "o_wronly": &objects.Int{Value: int64(os.O_WRONLY)}, + "o_rdwr": &objects.Int{Value: int64(os.O_RDWR)}, + "o_append": &objects.Int{Value: int64(os.O_APPEND)}, + "o_create": &objects.Int{Value: int64(os.O_CREATE)}, + "o_excl": &objects.Int{Value: int64(os.O_EXCL)}, + "o_sync": &objects.Int{Value: int64(os.O_SYNC)}, + "o_trunc": &objects.Int{Value: int64(os.O_TRUNC)}, + + "mode_dir": &objects.Int{Value: int64(os.ModeDir)}, + "mode_append": &objects.Int{Value: int64(os.ModeAppend)}, + "mode_exclusive": &objects.Int{Value: int64(os.ModeExclusive)}, + "mode_temporary": &objects.Int{Value: int64(os.ModeTemporary)}, + "mode_symlink": &objects.Int{Value: int64(os.ModeSymlink)}, + "mode_device": &objects.Int{Value: int64(os.ModeDevice)}, + "mode_named_pipe": &objects.Int{Value: int64(os.ModeNamedPipe)}, + "mode_socket": &objects.Int{Value: int64(os.ModeSocket)}, + "mode_setuid": &objects.Int{Value: int64(os.ModeSetuid)}, + "mode_setgui": &objects.Int{Value: int64(os.ModeSetgid)}, + "mode_char_device": &objects.Int{Value: int64(os.ModeCharDevice)}, + "mode_sticky": &objects.Int{Value: int64(os.ModeSticky)}, + "mode_irregular": &objects.Int{Value: int64(os.ModeIrregular)}, + "mode_type": &objects.Int{Value: int64(os.ModeType)}, + "mode_perm": &objects.Int{Value: int64(os.ModePerm)}, + + "path_separator": &objects.Char{Value: os.PathSeparator}, + "path_list_separator": &objects.Char{Value: os.PathListSeparator}, + "dev_null": &objects.String{Value: os.DevNull}, + + "args": &objects.UserFunction{Value: osArgs}, + "chdir": FuncASRE(os.Chdir), + "chmod": &objects.UserFunction{Value: osChmod}, + "chown": &objects.UserFunction{Value: osChown}, + "clearenv": FuncAR(os.Clearenv), + "environ": FuncARSs(os.Environ), + + // TODO: system errors + //"err_invalid": &objects.Error{Value: os.ErrInvalid.Error()}, + // TODO: STDIN, STDOUT, STDERR + // "stdin": nil, + // "stdout": nil, + // "stderr": nil, +} + +func osArgs(args ...objects.Object) (objects.Object, error) { + if len(args) != 0 { + return nil, objects.ErrWrongNumArguments + } + + arr := &objects.Array{} + for _, osArg := range os.Args { + arr.Value = append(arr.Value, &objects.String{Value: osArg}) + } + + return arr, nil +} + +func osChmod(args ...objects.Object) (objects.Object, error) { + if len(args) != 2 { + return nil, objects.ErrWrongNumArguments + } + + s1, ok := objects.ToString(args[0]) + if !ok { + return nil, objects.ErrInvalidTypeConversion + } + i2, ok := objects.ToInt(args[1]) + if !ok { + return nil, objects.ErrInvalidTypeConversion + } + + return wrapError(os.Chmod(s1, os.FileMode(i2))), nil +} + +func osChown(args ...objects.Object) (objects.Object, error) { + if len(args) != 3 { + return nil, objects.ErrWrongNumArguments + } + + s1, ok := objects.ToString(args[0]) + if !ok { + return nil, objects.ErrInvalidTypeConversion + } + i2, ok := objects.ToInt(args[1]) + if !ok { + return nil, objects.ErrInvalidTypeConversion + } + i3, ok := objects.ToInt(args[2]) + if !ok { + return nil, objects.ErrInvalidTypeConversion + } + + return wrapError(os.Chown(s1, i2, i3)), nil +} diff --git a/compiler/stdmods/stdmods.go b/compiler/stdmods/stdmods.go index 9cf5963..c32f02a 100644 --- a/compiler/stdmods/stdmods.go +++ b/compiler/stdmods/stdmods.go @@ -5,4 +5,5 @@ import "github.com/d5/tengo/objects" // Modules contain the standard modules. var Modules = map[string]*objects.ModuleMap{ "math": {Value: mathModule}, + "os": {Value: osModule}, }