add 'exec' module
This commit is contained in:
parent
a8d838ad3e
commit
5164d1de9f
5 changed files with 220 additions and 0 deletions
116
compiler/stdlib/exec.go
Normal file
116
compiler/stdlib/exec.go
Normal file
|
@ -0,0 +1,116 @@
|
|||
package stdlib
|
||||
|
||||
import (
|
||||
"os/exec"
|
||||
|
||||
"github.com/d5/tengo/objects"
|
||||
)
|
||||
|
||||
var execModule = map[string]objects.Object{
|
||||
// look_path(file string) => string/error
|
||||
"look_path": FuncASRSE(exec.LookPath),
|
||||
// command(name string, args array(string)) => imap(cmd)
|
||||
"command": &objects.UserFunction{Value: execCommand},
|
||||
}
|
||||
|
||||
func execCmdImmutableMap(cmd *exec.Cmd) *objects.ImmutableMap {
|
||||
return &objects.ImmutableMap{
|
||||
Value: map[string]objects.Object{
|
||||
// combined_output() => bytes/error
|
||||
"combined_output": FuncARYE(cmd.CombinedOutput),
|
||||
// output() => bytes/error
|
||||
"output": FuncARYE(cmd.Output),
|
||||
// run() => error
|
||||
"run": FuncARE(cmd.Run),
|
||||
// start() => error
|
||||
"start": FuncARE(cmd.Start),
|
||||
// wait() => error
|
||||
"wait": FuncARE(cmd.Wait),
|
||||
// set_path(path string)
|
||||
"set_path": &objects.UserFunction{
|
||||
Value: func(args ...objects.Object) (ret objects.Object, err error) {
|
||||
if len(args) != 1 {
|
||||
return nil, objects.ErrWrongNumArguments
|
||||
}
|
||||
|
||||
s1, ok := objects.ToString(args[0])
|
||||
if !ok {
|
||||
return nil, objects.ErrInvalidTypeConversion
|
||||
}
|
||||
|
||||
cmd.Path = s1
|
||||
|
||||
return objects.UndefinedValue, nil
|
||||
},
|
||||
},
|
||||
// set_dir(dir string)
|
||||
"set_dir": &objects.UserFunction{
|
||||
Value: func(args ...objects.Object) (ret objects.Object, err error) {
|
||||
if len(args) != 1 {
|
||||
return nil, objects.ErrWrongNumArguments
|
||||
}
|
||||
|
||||
s1, ok := objects.ToString(args[0])
|
||||
if !ok {
|
||||
return nil, objects.ErrInvalidTypeConversion
|
||||
}
|
||||
|
||||
cmd.Dir = s1
|
||||
|
||||
return objects.UndefinedValue, nil
|
||||
},
|
||||
},
|
||||
// set_env(env array(string))
|
||||
"set_env": &objects.UserFunction{
|
||||
Value: func(args ...objects.Object) (ret objects.Object, err error) {
|
||||
if len(args) != 1 {
|
||||
return nil, objects.ErrWrongNumArguments
|
||||
}
|
||||
|
||||
envs, err := stringArray(args[0])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
cmd.Env = envs
|
||||
|
||||
return objects.UndefinedValue, nil
|
||||
},
|
||||
},
|
||||
// process() => imap(process)
|
||||
"process": &objects.UserFunction{
|
||||
Value: func(args ...objects.Object) (ret objects.Object, err error) {
|
||||
if len(args) != 0 {
|
||||
return nil, objects.ErrWrongNumArguments
|
||||
}
|
||||
|
||||
return osProcessImmutableMap(cmd.Process), nil
|
||||
},
|
||||
},
|
||||
// TODO: implement pipes
|
||||
//"stderr_pipe": nil,
|
||||
//"stdin_pipe": nil,
|
||||
//"stdout_pipe": nil,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func execCommand(args ...objects.Object) (objects.Object, error) {
|
||||
if len(args) != 2 {
|
||||
return nil, objects.ErrWrongNumArguments
|
||||
}
|
||||
|
||||
name, ok := objects.ToString(args[0])
|
||||
if !ok {
|
||||
return nil, objects.ErrInvalidTypeConversion
|
||||
}
|
||||
|
||||
arg, err := stringArray(args[1])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
res := exec.Command(name, arg...)
|
||||
|
||||
return execCmdImmutableMap(res), nil
|
||||
}
|
|
@ -95,6 +95,25 @@ func FuncARSE(fn func() (string, error)) *objects.UserFunction {
|
|||
}
|
||||
}
|
||||
|
||||
// FuncARYE transform a function of 'func() ([]byte, error)' signature
|
||||
// into a user function object.
|
||||
func FuncARYE(fn func() ([]byte, error)) *objects.UserFunction {
|
||||
return &objects.UserFunction{
|
||||
Value: func(args ...objects.Object) (ret objects.Object, err error) {
|
||||
if len(args) != 0 {
|
||||
return nil, objects.ErrWrongNumArguments
|
||||
}
|
||||
|
||||
res, err := fn()
|
||||
if err != nil {
|
||||
return wrapError(err), nil
|
||||
}
|
||||
|
||||
return &objects.Bytes{Value: res}, nil
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// FuncARF transform a function of 'func() float64' signature
|
||||
// into a user function object.
|
||||
func FuncARF(fn func() float64) *objects.UserFunction {
|
||||
|
|
|
@ -286,6 +286,75 @@ func TestFuncAFIRB(t *testing.T) {
|
|||
assert.Equal(t, objects.ErrWrongNumArguments, err)
|
||||
}
|
||||
|
||||
func TestFuncAIRSsE(t *testing.T) {
|
||||
uf := stdlib.FuncAIRSsE(func(a int) ([]string, error) {
|
||||
return []string{"foo", "bar"}, nil
|
||||
})
|
||||
ret, err := uf.Call(&objects.Int{Value: 10})
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, array(&objects.String{Value: "foo"}, &objects.String{Value: "bar"}), ret)
|
||||
uf = stdlib.FuncAIRSsE(func(a int) ([]string, error) {
|
||||
return nil, errors.New("some error")
|
||||
})
|
||||
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()
|
||||
assert.Equal(t, objects.ErrWrongNumArguments, err)
|
||||
}
|
||||
|
||||
func TestFuncARB(t *testing.T) {
|
||||
uf := stdlib.FuncARB(func() bool { return true })
|
||||
ret, err := uf.Call()
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, objects.TrueValue, ret)
|
||||
ret, err = uf.Call(objects.TrueValue)
|
||||
assert.Equal(t, objects.ErrWrongNumArguments, err)
|
||||
}
|
||||
|
||||
func TestFuncARYE(t *testing.T) {
|
||||
uf := stdlib.FuncARYE(func() ([]byte, error) {
|
||||
return []byte("foo bar"), nil
|
||||
})
|
||||
ret, err := uf.Call()
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, &objects.Bytes{Value: []byte("foo bar")}, ret)
|
||||
uf = stdlib.FuncARYE(func() ([]byte, error) {
|
||||
return nil, errors.New("some error")
|
||||
})
|
||||
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)
|
||||
assert.Equal(t, objects.ErrWrongNumArguments, err)
|
||||
}
|
||||
|
||||
func TestFuncASRIE(t *testing.T) {
|
||||
uf := stdlib.FuncASRIE(func(a string) (int, error) { return 5, nil })
|
||||
ret, err := uf.Call(&objects.String{Value: "foo"})
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, &objects.Int{Value: 5}, ret)
|
||||
uf = stdlib.FuncASRIE(func(a string) (int, error) { return 0, errors.New("some error") })
|
||||
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()
|
||||
assert.Equal(t, objects.ErrWrongNumArguments, err)
|
||||
}
|
||||
|
||||
func TestFuncAYRIE(t *testing.T) {
|
||||
uf := stdlib.FuncAYRIE(func(a []byte) (int, error) { return 5, nil })
|
||||
ret, err := uf.Call(&objects.Bytes{Value: []byte("foo")})
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, &objects.Int{Value: 5}, ret)
|
||||
uf = stdlib.FuncAYRIE(func(a []byte) (int, error) { return 0, errors.New("some error") })
|
||||
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()
|
||||
assert.Equal(t, objects.ErrWrongNumArguments, err)
|
||||
}
|
||||
|
||||
func array(elements ...objects.Object) *objects.Array {
|
||||
return &objects.Array{Value: elements}
|
||||
}
|
||||
|
|
|
@ -6,4 +6,5 @@ import "github.com/d5/tengo/objects"
|
|||
var Modules = map[string]*objects.ImmutableMap{
|
||||
"math": {Value: mathModule},
|
||||
"os": {Value: osModule},
|
||||
"exec": {Value: execModule},
|
||||
}
|
||||
|
|
|
@ -45,6 +45,21 @@ if write_file("./temp", "foobar") {
|
|||
os.remove("./temp")
|
||||
`, "foobar")
|
||||
|
||||
// exec.command
|
||||
expect(t, `
|
||||
exec := import("exec")
|
||||
|
||||
echo := func(args) {
|
||||
cmd := exec.command("echo", args)
|
||||
if is_error(cmd) { return cmd.value }
|
||||
output := cmd.output()
|
||||
if is_error(output) { return output.value }
|
||||
return output
|
||||
}
|
||||
|
||||
out = echo(["foo", "bar"])
|
||||
`, []byte("foo bar\n"))
|
||||
|
||||
// user modules
|
||||
expectWithUserModules(t, `out = import("mod1").bar()`, 5.0, map[string]string{
|
||||
"mod1": `bar := func() { return 5.0 }`,
|
||||
|
|
Loading…
Reference in a new issue