From 306fe1b24c8b56bd5458f96f1a7db76167a5430d Mon Sep 17 00:00:00 2001 From: Daniel Kang Date: Tue, 29 Jan 2019 19:52:00 -0800 Subject: [PATCH] clean up stdlib code --- compiler/stdlib/math.go | 6 - compiler/stdlib/os.go | 290 +++-- compiler/stdlib/{exec.go => os_exec.go} | 35 +- compiler/stdlib/os_file.go | 75 +- compiler/stdlib/os_process.go | 79 +- compiler/stdlib/stdlib.go | 1 - compiler/stdlib/text.go | 1118 +++++++--------- compiler/stdlib/text_regexp.go | 167 +++ compiler/stdlib/times.go | 1563 +++++++++++------------ runtime/vm_module_test.go | 14 +- 10 files changed, 1604 insertions(+), 1744 deletions(-) rename compiler/stdlib/{exec.go => os_exec.go} (69%) create mode 100644 compiler/stdlib/text_regexp.go diff --git a/compiler/stdlib/math.go b/compiler/stdlib/math.go index c92dce2..05ff825 100644 --- a/compiler/stdlib/math.go +++ b/compiler/stdlib/math.go @@ -71,10 +71,4 @@ var mathModule = map[string]objects.Object{ "y0": FuncAFRF(math.Y0), "y1": FuncAFRF(math.Y1), "yn": FuncAIFRF(math.Yn), - // TODO: functions that have multiple returns - // Should these be tuple assignment? Or Map return? - //"frexp": nil, - //"lgamma": nil, - //"modf": nil, - //"sincos": nil, } diff --git a/compiler/stdlib/os.go b/compiler/stdlib/os.go index 21ad689..d032b50 100644 --- a/compiler/stdlib/os.go +++ b/compiler/stdlib/os.go @@ -3,6 +3,7 @@ package stdlib import ( "io" "os" + "os/exec" "github.com/d5/tengo/objects" ) @@ -36,82 +37,110 @@ var osModule = map[string]objects.Object{ "seek_set": &objects.Int{Value: int64(io.SeekStart)}, "seek_cur": &objects.Int{Value: int64(io.SeekCurrent)}, "seek_end": &objects.Int{Value: int64(io.SeekEnd)}, - // args() => array(string) - "args": &objects.UserFunction{Value: osArgs}, - // chdir(dir string) => error - "chdir": FuncASRE(os.Chdir), - // chmod(name string, mode int) => error - "chmod": osFuncASFmRE(os.Chmod), - // chown(name string, uid int, gid int) => error - "chown": FuncASIIRE(os.Chown), - // clearenv() - "clearenv": FuncAR(os.Clearenv), - // environ() => array(string) - "environ": FuncARSs(os.Environ), - // exit(code int) - "exit": FuncAIR(os.Exit), - // expand_env(s string) => string - "expand_env": FuncASRS(os.ExpandEnv), - // getegid() => int - "getegid": FuncARI(os.Getegid), - // getenv(s string) => string - "getenv": FuncASRS(os.Getenv), - // geteuid() => int - "geteuid": FuncARI(os.Geteuid), - // getgid() => int - "getgid": FuncARI(os.Getgid), - // getgroups() => array(string)/error - "getgroups": FuncARIsE(os.Getgroups), - // getpagesize() => int - "getpagesize": FuncARI(os.Getpagesize), - // getpid() => int - "getpid": FuncARI(os.Getpid), - // getppid() => int - "getppid": FuncARI(os.Getppid), - // getuid() => int - "getuid": FuncARI(os.Getuid), - // getwd() => string/error - "getwd": FuncARSE(os.Getwd), - // hostname() => string/error - "hostname": FuncARSE(os.Hostname), - // lchown(name string, uid int, gid int) => error - "lchown": FuncASIIRE(os.Lchown), - // link(oldname string, newname string) => error - "link": FuncASSRE(os.Link), - // lookup_env(key string) => string/false - "lookup_env": &objects.UserFunction{Value: osLookupEnv}, - // mkdir(name string, perm int) => error - "mkdir": osFuncASFmRE(os.Mkdir), - // mkdir_all(name string, perm int) => error - "mkdir_all": osFuncASFmRE(os.MkdirAll), - // readlink(name string) => string/error - "readlink": FuncASRSE(os.Readlink), - // remove(name string) => error - "remove": FuncASRE(os.Remove), - // remove_all(name string) => error - "remove_all": FuncASRE(os.RemoveAll), - // rename(oldpath string, newpath string) => error - "rename": FuncASSRE(os.Rename), - // setenv(key string, value string) => error - "setenv": FuncASSRE(os.Setenv), - // symlink(oldname string newname string) => error - "symlink": FuncASSRE(os.Symlink), - // temp_dir() => string - "temp_dir": FuncARS(os.TempDir), - // truncate(name string, size int) => error - "truncate": FuncASI64RE(os.Truncate), - // unsetenv(key string) => error - "unsetenv": FuncASRE(os.Unsetenv), - // create(name string) => imap(file)/error - "create": &objects.UserFunction{Value: osCreate}, - // open(name string) => imap(file)/error - "open": &objects.UserFunction{Value: osOpen}, - // open_file(name string, flag int, perm int) => imap(file)/error - "open_file": &objects.UserFunction{Value: osOpenFile}, - // find_process(pid int) => imap(process)/error - "find_process": &objects.UserFunction{Value: osFindProcess}, - // start_process(name string, argv array(string), dir string, env array(string)) => imap(process)/error - "start_process": &objects.UserFunction{Value: osStartProcess}, + "args": &objects.UserFunction{Value: osArgs}, // args() => array(string) + "chdir": FuncASRE(os.Chdir), // chdir(dir string) => error + "chmod": osFuncASFmRE(os.Chmod), // chmod(name string, mode int) => error + "chown": FuncASIIRE(os.Chown), // chown(name string, uid int, gid int) => error + "clearenv": FuncAR(os.Clearenv), // clearenv() + "environ": FuncARSs(os.Environ), // environ() => array(string) + "exit": FuncAIR(os.Exit), // exit(code int) + "expand_env": FuncASRS(os.ExpandEnv), // expand_env(s string) => string + "getegid": FuncARI(os.Getegid), // getegid() => int + "getenv": FuncASRS(os.Getenv), // getenv(s string) => string + "geteuid": FuncARI(os.Geteuid), // geteuid() => int + "getgid": FuncARI(os.Getgid), // getgid() => int + "getgroups": FuncARIsE(os.Getgroups), // getgroups() => array(string)/error + "getpagesize": FuncARI(os.Getpagesize), // getpagesize() => int + "getpid": FuncARI(os.Getpid), // getpid() => int + "getppid": FuncARI(os.Getppid), // getppid() => int + "getuid": FuncARI(os.Getuid), // getuid() => int + "getwd": FuncARSE(os.Getwd), // getwd() => string/error + "hostname": FuncARSE(os.Hostname), // hostname() => string/error + "lchown": FuncASIIRE(os.Lchown), // lchown(name string, uid int, gid int) => error + "link": FuncASSRE(os.Link), // link(oldname string, newname string) => error + "lookup_env": &objects.UserFunction{Value: osLookupEnv}, // lookup_env(key string) => string/false + "mkdir": osFuncASFmRE(os.Mkdir), // mkdir(name string, perm int) => error + "mkdir_all": osFuncASFmRE(os.MkdirAll), // mkdir_all(name string, perm int) => error + "readlink": FuncASRSE(os.Readlink), // readlink(name string) => string/error + "remove": FuncASRE(os.Remove), // remove(name string) => error + "remove_all": FuncASRE(os.RemoveAll), // remove_all(name string) => error + "rename": FuncASSRE(os.Rename), // rename(oldpath string, newpath string) => error + "setenv": FuncASSRE(os.Setenv), // setenv(key string, value string) => error + "symlink": FuncASSRE(os.Symlink), // symlink(oldname string newname string) => error + "temp_dir": FuncARS(os.TempDir), // temp_dir() => string + "truncate": FuncASI64RE(os.Truncate), // truncate(name string, size int) => error + "unsetenv": FuncASRE(os.Unsetenv), // unsetenv(key string) => error + "create": &objects.UserFunction{Value: osCreate}, // create(name string) => imap(file)/error + "open": &objects.UserFunction{Value: osOpen}, // open(name string) => imap(file)/error + "open_file": &objects.UserFunction{Value: osOpenFile}, // open_file(name string, flag int, perm int) => imap(file)/error + "find_process": &objects.UserFunction{Value: osFindProcess}, // find_process(pid int) => imap(process)/error + "start_process": &objects.UserFunction{Value: osStartProcess}, // start_process(name string, argv array(string), dir string, env array(string)) => imap(process)/error + "exec_look_path": FuncASRSE(exec.LookPath), // exec_look_path(file) => string/error + "exec": &objects.UserFunction{Value: osExec}, // exec(name, args...) => command +} + +func osCreate(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 + } + + res, err := os.Create(s1) + if err != nil { + return wrapError(err), nil + } + + return makeOSFile(res), nil +} + +func osOpen(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 + } + + res, err := os.Open(s1) + if err != nil { + return wrapError(err), nil + } + + return makeOSFile(res), nil +} + +func osOpenFile(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 + } + + res, err := os.OpenFile(s1, i2, os.FileMode(i3)) + if err != nil { + return wrapError(err), nil + } + + return makeOSFile(res), nil } func osArgs(args ...objects.Object) (objects.Object, error) { @@ -148,19 +177,6 @@ func osFuncASFmRE(fn func(string, os.FileMode) error) *objects.UserFunction { } } -func osExecutable(args ...objects.Object) (objects.Object, error) { - if len(args) != 0 { - return nil, objects.ErrWrongNumArguments - } - - res, err := os.Executable() - if err != nil { - return wrapError(err), nil - } - - return &objects.String{Value: res}, nil -} - func osLookupEnv(args ...objects.Object) (objects.Object, error) { if len(args) != 1 { return nil, objects.ErrWrongNumArguments @@ -178,3 +194,99 @@ func osLookupEnv(args ...objects.Object) (objects.Object, error) { return &objects.String{Value: res}, nil } + +func osExec(args ...objects.Object) (objects.Object, error) { + if len(args) == 0 { + return nil, objects.ErrWrongNumArguments + } + + name, ok := objects.ToString(args[0]) + if !ok { + return nil, objects.ErrInvalidTypeConversion + } + + var execArgs []string + for _, arg := range args[1:] { + execArg, ok := objects.ToString(arg) + if !ok { + return nil, objects.ErrInvalidTypeConversion + } + + execArgs = append(execArgs, execArg) + } + + return makeOSExecCommand(exec.Command(name, execArgs...)), nil +} + +func osFindProcess(args ...objects.Object) (objects.Object, error) { + if len(args) != 1 { + return nil, objects.ErrWrongNumArguments + } + + i1, ok := objects.ToInt(args[0]) + if !ok { + return nil, objects.ErrInvalidTypeConversion + } + + proc, err := os.FindProcess(i1) + if err != nil { + return wrapError(err), nil + } + + return makeOSProcess(proc), nil +} + +func osStartProcess(args ...objects.Object) (objects.Object, error) { + if len(args) != 4 { + return nil, objects.ErrWrongNumArguments + } + + name, ok := objects.ToString(args[0]) + if !ok { + return nil, objects.ErrInvalidTypeConversion + } + + argv, err := stringArray(args[1]) + if err != nil { + return nil, err + } + + dir, ok := objects.ToString(args[2]) + if !ok { + return nil, objects.ErrInvalidTypeConversion + } + + env, err := stringArray(args[3]) + if err != nil { + return nil, err + } + + proc, err := os.StartProcess(name, argv, &os.ProcAttr{ + Dir: dir, + Env: env, + }) + if err != nil { + return wrapError(err), nil + } + + return makeOSProcess(proc), nil +} + +func stringArray(o objects.Object) ([]string, error) { + arr, ok := o.(*objects.Array) + if !ok { + return nil, objects.ErrInvalidTypeConversion + } + + var sarr []string + for _, elem := range arr.Value { + str, ok := elem.(*objects.String) + if !ok { + return nil, objects.ErrInvalidTypeConversion + } + + sarr = append(sarr, str.Value) + } + + return sarr, nil +} diff --git a/compiler/stdlib/exec.go b/compiler/stdlib/os_exec.go similarity index 69% rename from compiler/stdlib/exec.go rename to compiler/stdlib/os_exec.go index 2577d10..b2bc743 100644 --- a/compiler/stdlib/exec.go +++ b/compiler/stdlib/os_exec.go @@ -6,14 +6,7 @@ import ( "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 { +func makeOSExecCommand(cmd *exec.Cmd) *objects.ImmutableMap { return &objects.ImmutableMap{ Value: map[string]objects.Object{ // combined_output() => bytes/error @@ -84,33 +77,9 @@ func execCmdImmutableMap(cmd *exec.Cmd) *objects.ImmutableMap { return nil, objects.ErrWrongNumArguments } - return osProcessImmutableMap(cmd.Process), nil + return makeOSProcess(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 -} diff --git a/compiler/stdlib/os_file.go b/compiler/stdlib/os_file.go index 5c0007b..3b9c4b6 100644 --- a/compiler/stdlib/os_file.go +++ b/compiler/stdlib/os_file.go @@ -6,7 +6,7 @@ import ( "github.com/d5/tengo/objects" ) -func osFileImmutableMap(file *os.File) *objects.ImmutableMap { +func makeOSFile(file *os.File) *objects.ImmutableMap { return &objects.ImmutableMap{ Value: map[string]objects.Object{ // chdir() => true/error @@ -66,79 +66,6 @@ func osFileImmutableMap(file *os.File) *objects.ImmutableMap { return &objects.Int{Value: res}, nil }, }, - // TODO: implement more functions - //"fd": nil, - //"read_at": nil, - //"readdir": nil, - //"set_deadline": nil, - //"set_read_deadline": nil, - //"set_write_deadline": nil, - //"stat": nil, - //"write_at": nil, }, } } - -func osCreate(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 - } - - res, err := os.Create(s1) - if err != nil { - return wrapError(err), nil - } - - return osFileImmutableMap(res), nil -} - -func osOpen(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 - } - - res, err := os.Open(s1) - if err != nil { - return wrapError(err), nil - } - - return osFileImmutableMap(res), nil -} - -func osOpenFile(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 - } - - res, err := os.OpenFile(s1, i2, os.FileMode(i3)) - if err != nil { - return wrapError(err), nil - } - - return osFileImmutableMap(res), nil -} diff --git a/compiler/stdlib/os_process.go b/compiler/stdlib/os_process.go index 45872d7..5cdc856 100644 --- a/compiler/stdlib/os_process.go +++ b/compiler/stdlib/os_process.go @@ -7,7 +7,7 @@ import ( "github.com/d5/tengo/objects" ) -func osProcessStateImmutableMap(state *os.ProcessState) *objects.ImmutableMap { +func makeOSProcessState(state *os.ProcessState) *objects.ImmutableMap { return &objects.ImmutableMap{ Value: map[string]objects.Object{ "exited": FuncARB(state.Exited), @@ -18,7 +18,7 @@ func osProcessStateImmutableMap(state *os.ProcessState) *objects.ImmutableMap { } } -func osProcessImmutableMap(proc *os.Process) *objects.ImmutableMap { +func makeOSProcess(proc *os.Process) *objects.ImmutableMap { return &objects.ImmutableMap{ Value: map[string]objects.Object{ "kill": FuncARE(proc.Kill), @@ -48,82 +48,9 @@ func osProcessImmutableMap(proc *os.Process) *objects.ImmutableMap { return wrapError(err), nil } - return osProcessStateImmutableMap(state), nil + return makeOSProcessState(state), nil }, }, }, } } - -func osFindProcess(args ...objects.Object) (objects.Object, error) { - if len(args) != 1 { - return nil, objects.ErrWrongNumArguments - } - - i1, ok := objects.ToInt(args[0]) - if !ok { - return nil, objects.ErrInvalidTypeConversion - } - - proc, err := os.FindProcess(i1) - if err != nil { - return wrapError(err), nil - } - - return osProcessImmutableMap(proc), nil -} - -func osStartProcess(args ...objects.Object) (objects.Object, error) { - if len(args) != 4 { - return nil, objects.ErrWrongNumArguments - } - - name, ok := objects.ToString(args[0]) - if !ok { - return nil, objects.ErrInvalidTypeConversion - } - - argv, err := stringArray(args[1]) - if err != nil { - return nil, err - } - - dir, ok := objects.ToString(args[2]) - if !ok { - return nil, objects.ErrInvalidTypeConversion - } - - env, err := stringArray(args[3]) - if err != nil { - return nil, err - } - - proc, err := os.StartProcess(name, argv, &os.ProcAttr{ - Dir: dir, - Env: env, - }) - if err != nil { - return wrapError(err), nil - } - - return osProcessImmutableMap(proc), nil -} - -func stringArray(o objects.Object) ([]string, error) { - arr, ok := o.(*objects.Array) - if !ok { - return nil, objects.ErrInvalidTypeConversion - } - - var sarr []string - for _, elem := range arr.Value { - str, ok := elem.(*objects.String) - if !ok { - return nil, objects.ErrInvalidTypeConversion - } - - sarr = append(sarr, str.Value) - } - - return sarr, nil -} diff --git a/compiler/stdlib/stdlib.go b/compiler/stdlib/stdlib.go index 4b914e7..412ce62 100644 --- a/compiler/stdlib/stdlib.go +++ b/compiler/stdlib/stdlib.go @@ -6,7 +6,6 @@ import "github.com/d5/tengo/objects" var Modules = map[string]*objects.ImmutableMap{ "math": {Value: mathModule}, "os": {Value: osModule}, - "exec": {Value: execModule}, "text": {Value: textModule}, "times": {Value: timesModule}, } diff --git a/compiler/stdlib/text.go b/compiler/stdlib/text.go index 07ff119..7548a3d 100644 --- a/compiler/stdlib/text.go +++ b/compiler/stdlib/text.go @@ -9,671 +9,461 @@ import ( ) var textModule = map[string]objects.Object{ - // re_match(pattern, text) => bool/error - "re_match": &objects.UserFunction{ - Value: func(args ...objects.Object) (ret objects.Object, err error) { - if len(args) != 2 { - err = objects.ErrWrongNumArguments - return - } - - s1, ok := objects.ToString(args[0]) - if !ok { - err = objects.ErrInvalidTypeConversion - return - } - - s2, ok := objects.ToString(args[1]) - if !ok { - err = objects.ErrInvalidTypeConversion - return - } - - matched, err := regexp.MatchString(s1, s2) - if err != nil { - ret = wrapError(err) - return - } - - if matched { - ret = objects.TrueValue - } else { - ret = objects.FalseValue - } - - return - }, - }, - - // re_find(pattern, text) => array(array({text:,begin:,end:}))/undefined - // re_find(pattern, text, maxCount) => array(array({text:,begin:,end:}))/undefined - "re_find": &objects.UserFunction{ - Value: func(args ...objects.Object) (ret objects.Object, err error) { - numArgs := len(args) - if numArgs != 2 && numArgs != 3 { - err = objects.ErrWrongNumArguments - return - } - - s1, ok := objects.ToString(args[0]) - if !ok { - err = objects.ErrInvalidTypeConversion - return - } - - re, err := regexp.Compile(s1) - if err != nil { - ret = wrapError(err) - return - } - - s2, ok := objects.ToString(args[1]) - if !ok { - err = objects.ErrInvalidTypeConversion - return - } - - if numArgs < 3 { - m := re.FindStringSubmatchIndex(s2) - if m == nil { - ret = objects.UndefinedValue - return - } - - arr := &objects.Array{} - for i := 0; i < len(m); i += 2 { - arr.Value = append(arr.Value, &objects.ImmutableMap{Value: map[string]objects.Object{ - "text": &objects.String{Value: s2[m[i]:m[i+1]]}, - "begin": &objects.Int{Value: int64(m[i])}, - "end": &objects.Int{Value: int64(m[i+1])}, - }}) - } - - ret = &objects.Array{Value: []objects.Object{arr}} - - return - } - - i3, ok := objects.ToInt(args[2]) - if !ok { - err = objects.ErrInvalidTypeConversion - return - } - m := re.FindAllStringSubmatchIndex(s2, i3) - if m == nil { - ret = objects.UndefinedValue - return - } - - arr := &objects.Array{} - for _, m := range m { - subMatch := &objects.Array{} - for i := 0; i < len(m); i += 2 { - subMatch.Value = append(subMatch.Value, &objects.ImmutableMap{Value: map[string]objects.Object{ - "text": &objects.String{Value: s2[m[i]:m[i+1]]}, - "begin": &objects.Int{Value: int64(m[i])}, - "end": &objects.Int{Value: int64(m[i+1])}, - }}) - } - - arr.Value = append(arr.Value, subMatch) - } - - ret = arr - - return - }, - }, - - // re_replace(pattern, text, repl) => string/error - "re_replace": &objects.UserFunction{ - Value: func(args ...objects.Object) (ret objects.Object, err error) { - if len(args) != 3 { - err = objects.ErrWrongNumArguments - return - } - - s1, ok := objects.ToString(args[0]) - if !ok { - err = objects.ErrInvalidTypeConversion - return - } - - s2, ok := objects.ToString(args[1]) - if !ok { - err = objects.ErrInvalidTypeConversion - return - } - - s3, ok := objects.ToString(args[2]) - if !ok { - err = objects.ErrInvalidTypeConversion - return - } - - re, err := regexp.Compile(s1) - if err != nil { - ret = wrapError(err) - } else { - ret = &objects.String{Value: re.ReplaceAllString(s2, s3)} - } - - return - }, - }, - - // re_split(pattern, text) => array(string)/error - // re_split(pattern, text, maxCount) => array(string)/error - "re_split": &objects.UserFunction{ - Value: func(args ...objects.Object) (ret objects.Object, err error) { - numArgs := len(args) - if numArgs != 2 && numArgs != 3 { - err = objects.ErrWrongNumArguments - return - } - - s1, ok := objects.ToString(args[0]) - if !ok { - err = objects.ErrInvalidTypeConversion - return - } - - s2, ok := objects.ToString(args[1]) - if !ok { - err = objects.ErrInvalidTypeConversion - return - } - - var i3 = -1 - if numArgs > 2 { - i3, ok = objects.ToInt(args[2]) - if !ok { - err = objects.ErrInvalidTypeConversion - return - } - } - - re, err := regexp.Compile(s1) - if err != nil { - ret = wrapError(err) - return - } - - arr := &objects.Array{} - for _, s := range re.Split(s2, i3) { - arr.Value = append(arr.Value, &objects.String{Value: s}) - } - - ret = arr - - return - }, - }, - - // re_compile(pattern) => Regexp/error - "re_compile": &objects.UserFunction{ - Value: func(args ...objects.Object) (ret objects.Object, err error) { - if len(args) != 1 { - err = objects.ErrWrongNumArguments - return - } - - s1, ok := objects.ToString(args[0]) - if !ok { - err = objects.ErrInvalidTypeConversion - return - } - - re, err := regexp.Compile(s1) - if err != nil { - ret = wrapError(err) - } else { - ret = stringsRegexpImmutableMap(re) - } - - return - }, - }, - - // compare(a, b) => int - "compare": FuncASSRI(strings.Compare), - // contains(s, substr) => bool - "contains": FuncASSRB(strings.Contains), - // contains_any(s, chars) => bool - "contains_any": FuncASSRB(strings.ContainsAny), - // count(s, substr) => int - "count": FuncASSRI(strings.Count), - // "equal_fold(s, t) => bool - "equal_fold": FuncASSRB(strings.EqualFold), - // fields(s) => array(string) - "fields": FuncASRSs(strings.Fields), - // has_prefix(s, prefix) => bool - "has_prefix": FuncASSRB(strings.HasPrefix), - // has_suffix(s, suffix) => bool - "has_suffix": FuncASSRB(strings.HasSuffix), - // index(s, substr) => int - "index": FuncASSRI(strings.Index), - // index_any(s, chars) => int - "index_any": FuncASSRI(strings.IndexAny), - // join(arr, sep) => string - "join": FuncASsSRS(strings.Join), - // last_index(s, substr) => int - "last_index": FuncASSRI(strings.LastIndex), - // last_index_any(s, chars) => int - "last_index_any": FuncASSRI(strings.LastIndexAny), - // repeat(s, count) => string - "repeat": FuncASIRS(strings.Repeat), - // replace(s, old, new, n) => string - "replace": &objects.UserFunction{ - Value: func(args ...objects.Object) (ret objects.Object, err error) { - if len(args) != 4 { - err = objects.ErrWrongNumArguments - return - } - - s1, ok := objects.ToString(args[0]) - if !ok { - err = objects.ErrInvalidTypeConversion - return - } - - s2, ok := objects.ToString(args[1]) - if !ok { - err = objects.ErrInvalidTypeConversion - return - } - - s3, ok := objects.ToString(args[2]) - if !ok { - err = objects.ErrInvalidTypeConversion - return - } - - i4, ok := objects.ToInt(args[3]) - if !ok { - err = objects.ErrInvalidTypeConversion - return - } - - ret = &objects.String{Value: strings.Replace(s1, s2, s3, i4)} - - return - }, - }, - // split(s, sep) => []string - "split": FuncASSRSs(strings.Split), - // split_after(s, sep) => []string - "split_after": FuncASSRSs(strings.SplitAfter), - // split_after_n(s, sep, n) => []string - "split_after_n": FuncASSIRSs(strings.SplitAfterN), - // split_n(s, sep, n) => []string - "split_n": FuncASSIRSs(strings.SplitN), - // title(s) => string - "title": FuncASRS(strings.Title), - // to_lower(s) => string - "to_lower": FuncASRS(strings.ToLower), - // to_title(s) => string - "to_title": FuncASRS(strings.ToTitle), - // to_upper(s) => string - "to_upper": FuncASRS(strings.ToUpper), - // trim_left(s, cutset) => string - "trim_left": FuncASSRS(strings.TrimLeft), - // trim_prefix(s, prefix) => string - "trim_prefix": FuncASSRS(strings.TrimPrefix), - // trim_right(s, cutset) => string - "trim_right": FuncASSRS(strings.TrimRight), - // trim_space(s) => string - "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), + "re_match": &objects.UserFunction{Value: textREMatch}, // re_match(pattern, text) => bool/error + "re_find": &objects.UserFunction{Value: textREFind}, // re_find(pattern, text, count) => array(array({text:,begin:,end:}))/undefined + "re_replace": &objects.UserFunction{Value: textREReplace}, // re_replace(pattern, text, repl) => string/error + "re_split": &objects.UserFunction{Value: textRESplit}, // re_split(pattern, text, count) => array(string)/error + "re_compile": &objects.UserFunction{Value: textRECompile}, // re_compile(pattern) => Regexp/error + "compare": FuncASSRI(strings.Compare), // compare(a, b) => int + "contains": FuncASSRB(strings.Contains), // contains(s, substr) => bool + "contains_any": FuncASSRB(strings.ContainsAny), // contains_any(s, chars) => bool + "count": FuncASSRI(strings.Count), // count(s, substr) => int + "equal_fold": FuncASSRB(strings.EqualFold), // "equal_fold(s, t) => bool + "fields": FuncASRSs(strings.Fields), // fields(s) => array(string) + "has_prefix": FuncASSRB(strings.HasPrefix), // has_prefix(s, prefix) => bool + "has_suffix": FuncASSRB(strings.HasSuffix), // has_suffix(s, suffix) => bool + "index": FuncASSRI(strings.Index), // index(s, substr) => int + "index_any": FuncASSRI(strings.IndexAny), // index_any(s, chars) => int + "join": FuncASsSRS(strings.Join), // join(arr, sep) => string + "last_index": FuncASSRI(strings.LastIndex), // last_index(s, substr) => int + "last_index_any": FuncASSRI(strings.LastIndexAny), // last_index_any(s, chars) => int + "repeat": FuncASIRS(strings.Repeat), // repeat(s, count) => string + "replace": &objects.UserFunction{Value: textReplace}, // replace(s, old, new, n) => string + "split": FuncASSRSs(strings.Split), // split(s, sep) => []string + "split_after": FuncASSRSs(strings.SplitAfter), // split_after(s, sep) => []string + "split_after_n": FuncASSIRSs(strings.SplitAfterN), // split_after_n(s, sep, n) => []string + "split_n": FuncASSIRSs(strings.SplitN), // split_n(s, sep, n) => []string + "title": FuncASRS(strings.Title), // title(s) => string + "to_lower": FuncASRS(strings.ToLower), // to_lower(s) => string + "to_title": FuncASRS(strings.ToTitle), // to_title(s) => string + "to_upper": FuncASRS(strings.ToUpper), // to_upper(s) => string + "trim_left": FuncASSRS(strings.TrimLeft), // trim_left(s, cutset) => string + "trim_prefix": FuncASSRS(strings.TrimPrefix), // trim_prefix(s, prefix) => string + "trim_right": FuncASSRS(strings.TrimRight), // trim_right(s, cutset) => string + "trim_space": FuncASRS(strings.TrimSpace), // trim_space(s) => string + "trim_suffix": FuncASSRS(strings.TrimSuffix), // trim_suffix(s, suffix) => string + "atoi": FuncASRIE(strconv.Atoi), // atoi(str) => int/error + "format_bool": &objects.UserFunction{Value: textFormatBool}, // format_bool(b) => string + "format_float": &objects.UserFunction{Value: textFormatFloat}, // format_float(f, fmt, prec, bits) => string + "format_int": &objects.UserFunction{Value: textFormatInt}, // format_int(i, base) => string + "itoa": FuncAIRS(strconv.Itoa), // itoa(i) => string + "parse_bool": &objects.UserFunction{Value: textParseBool}, // parse_bool(str) => bool/error + "parse_float": &objects.UserFunction{Value: textParseFloat}, // parse_float(str, bits) => float/error + "parse_int": &objects.UserFunction{Value: textParseInt}, // parse_int(str, base, bits) => int/error + "quote": FuncASRS(strconv.Quote), // quote(str) => string + "unquote": FuncASRSE(strconv.Unquote), // unquote(str) => string/error } -func stringsRegexpImmutableMap(re *regexp.Regexp) *objects.ImmutableMap { - return &objects.ImmutableMap{ - Value: map[string]objects.Object{ - // match(text) => bool - "match": &objects.UserFunction{ - Value: func(args ...objects.Object) (ret objects.Object, err error) { - if len(args) != 1 { - err = objects.ErrWrongNumArguments - return - } - - s1, ok := objects.ToString(args[0]) - if !ok { - err = objects.ErrInvalidTypeConversion - return - } - - if re.MatchString(s1) { - ret = objects.TrueValue - } else { - ret = objects.FalseValue - } - - return - }, - }, - - // find(text) => array(array({text:,begin:,end:}))/undefined - // find(text, maxCount) => array(array({text:,begin:,end:}))/undefined - "find": &objects.UserFunction{ - Value: func(args ...objects.Object) (ret objects.Object, err error) { - numArgs := len(args) - if numArgs != 1 && numArgs != 2 { - err = objects.ErrWrongNumArguments - return - } - - s1, ok := objects.ToString(args[0]) - if !ok { - err = objects.ErrInvalidTypeConversion - return - } - - if numArgs == 1 { - m := re.FindStringSubmatchIndex(s1) - if m == nil { - ret = objects.UndefinedValue - return - } - - arr := &objects.Array{} - for i := 0; i < len(m); i += 2 { - arr.Value = append(arr.Value, &objects.ImmutableMap{Value: map[string]objects.Object{ - "text": &objects.String{Value: s1[m[i]:m[i+1]]}, - "begin": &objects.Int{Value: int64(m[i])}, - "end": &objects.Int{Value: int64(m[i+1])}, - }}) - } - - ret = &objects.Array{Value: []objects.Object{arr}} - - return - } - - i2, ok := objects.ToInt(args[1]) - if !ok { - err = objects.ErrInvalidTypeConversion - return - } - m := re.FindAllStringSubmatchIndex(s1, i2) - if m == nil { - ret = objects.UndefinedValue - return - } - - arr := &objects.Array{} - for _, m := range m { - subMatch := &objects.Array{} - for i := 0; i < len(m); i += 2 { - subMatch.Value = append(subMatch.Value, &objects.ImmutableMap{Value: map[string]objects.Object{ - "text": &objects.String{Value: s1[m[i]:m[i+1]]}, - "begin": &objects.Int{Value: int64(m[i])}, - "end": &objects.Int{Value: int64(m[i+1])}, - }}) - } - - arr.Value = append(arr.Value, subMatch) - } - - ret = arr - - return - }, - }, - - // replace(src, repl) => string - "replace": &objects.UserFunction{ - Value: func(args ...objects.Object) (ret objects.Object, err error) { - if len(args) != 2 { - err = objects.ErrWrongNumArguments - return - } - - s1, ok := objects.ToString(args[0]) - if !ok { - err = objects.ErrInvalidTypeConversion - return - } - - s2, ok := objects.ToString(args[1]) - if !ok { - err = objects.ErrInvalidTypeConversion - return - } - - ret = &objects.String{Value: re.ReplaceAllString(s1, s2)} - - return - }, - }, - - // split(text) => array(string) - // split(text, maxCount) => array(string) - "split": &objects.UserFunction{ - Value: func(args ...objects.Object) (ret objects.Object, err error) { - numArgs := len(args) - if numArgs != 1 && numArgs != 2 { - err = objects.ErrWrongNumArguments - return - } - - s1, ok := objects.ToString(args[0]) - if !ok { - err = objects.ErrInvalidTypeConversion - return - } - - var i2 = -1 - if numArgs > 1 { - i2, ok = objects.ToInt(args[1]) - if !ok { - err = objects.ErrInvalidTypeConversion - return - } - } - - arr := &objects.Array{} - for _, s := range re.Split(s1, i2) { - arr.Value = append(arr.Value, &objects.String{Value: s}) - } - - ret = arr - - return - }, - }, - }, +func textREMatch(args ...objects.Object) (ret objects.Object, err error) { + if len(args) != 2 { + err = objects.ErrWrongNumArguments + return } + + s1, ok := objects.ToString(args[0]) + if !ok { + err = objects.ErrInvalidTypeConversion + return + } + + s2, ok := objects.ToString(args[1]) + if !ok { + err = objects.ErrInvalidTypeConversion + return + } + + matched, err := regexp.MatchString(s1, s2) + if err != nil { + ret = wrapError(err) + return + } + + if matched { + ret = objects.TrueValue + } else { + ret = objects.FalseValue + } + + return +} + +func textREFind(args ...objects.Object) (ret objects.Object, err error) { + numArgs := len(args) + if numArgs != 2 && numArgs != 3 { + err = objects.ErrWrongNumArguments + return + } + + s1, ok := objects.ToString(args[0]) + if !ok { + err = objects.ErrInvalidTypeConversion + return + } + + re, err := regexp.Compile(s1) + if err != nil { + ret = wrapError(err) + return + } + + s2, ok := objects.ToString(args[1]) + if !ok { + err = objects.ErrInvalidTypeConversion + return + } + + if numArgs < 3 { + m := re.FindStringSubmatchIndex(s2) + if m == nil { + ret = objects.UndefinedValue + return + } + + arr := &objects.Array{} + for i := 0; i < len(m); i += 2 { + arr.Value = append(arr.Value, &objects.ImmutableMap{Value: map[string]objects.Object{ + "text": &objects.String{Value: s2[m[i]:m[i+1]]}, + "begin": &objects.Int{Value: int64(m[i])}, + "end": &objects.Int{Value: int64(m[i+1])}, + }}) + } + + ret = &objects.Array{Value: []objects.Object{arr}} + + return + } + + i3, ok := objects.ToInt(args[2]) + if !ok { + err = objects.ErrInvalidTypeConversion + return + } + m := re.FindAllStringSubmatchIndex(s2, i3) + if m == nil { + ret = objects.UndefinedValue + return + } + + arr := &objects.Array{} + for _, m := range m { + subMatch := &objects.Array{} + for i := 0; i < len(m); i += 2 { + subMatch.Value = append(subMatch.Value, &objects.ImmutableMap{Value: map[string]objects.Object{ + "text": &objects.String{Value: s2[m[i]:m[i+1]]}, + "begin": &objects.Int{Value: int64(m[i])}, + "end": &objects.Int{Value: int64(m[i+1])}, + }}) + } + + arr.Value = append(arr.Value, subMatch) + } + + ret = arr + + return +} + +func textREReplace(args ...objects.Object) (ret objects.Object, err error) { + if len(args) != 3 { + err = objects.ErrWrongNumArguments + return + } + + s1, ok := objects.ToString(args[0]) + if !ok { + err = objects.ErrInvalidTypeConversion + return + } + + s2, ok := objects.ToString(args[1]) + if !ok { + err = objects.ErrInvalidTypeConversion + return + } + + s3, ok := objects.ToString(args[2]) + if !ok { + err = objects.ErrInvalidTypeConversion + return + } + + re, err := regexp.Compile(s1) + if err != nil { + ret = wrapError(err) + } else { + ret = &objects.String{Value: re.ReplaceAllString(s2, s3)} + } + + return +} + +func textRESplit(args ...objects.Object) (ret objects.Object, err error) { + numArgs := len(args) + if numArgs != 2 && numArgs != 3 { + err = objects.ErrWrongNumArguments + return + } + + s1, ok := objects.ToString(args[0]) + if !ok { + err = objects.ErrInvalidTypeConversion + return + } + + s2, ok := objects.ToString(args[1]) + if !ok { + err = objects.ErrInvalidTypeConversion + return + } + + var i3 = -1 + if numArgs > 2 { + i3, ok = objects.ToInt(args[2]) + if !ok { + err = objects.ErrInvalidTypeConversion + return + } + } + + re, err := regexp.Compile(s1) + if err != nil { + ret = wrapError(err) + return + } + + arr := &objects.Array{} + for _, s := range re.Split(s2, i3) { + arr.Value = append(arr.Value, &objects.String{Value: s}) + } + + ret = arr + + return +} + +func textRECompile(args ...objects.Object) (ret objects.Object, err error) { + if len(args) != 1 { + err = objects.ErrWrongNumArguments + return + } + + s1, ok := objects.ToString(args[0]) + if !ok { + err = objects.ErrInvalidTypeConversion + return + } + + re, err := regexp.Compile(s1) + if err != nil { + ret = wrapError(err) + } else { + ret = makeTextRegexp(re) + } + + return +} + +func textReplace(args ...objects.Object) (ret objects.Object, err error) { + if len(args) != 4 { + err = objects.ErrWrongNumArguments + return + } + + s1, ok := objects.ToString(args[0]) + if !ok { + err = objects.ErrInvalidTypeConversion + return + } + + s2, ok := objects.ToString(args[1]) + if !ok { + err = objects.ErrInvalidTypeConversion + return + } + + s3, ok := objects.ToString(args[2]) + if !ok { + err = objects.ErrInvalidTypeConversion + return + } + + i4, ok := objects.ToInt(args[3]) + if !ok { + err = objects.ErrInvalidTypeConversion + return + } + + ret = &objects.String{Value: strings.Replace(s1, s2, s3, i4)} + + return +} + +func textFormatBool(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 +} + +func textFormatFloat(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 +} + +func textFormatInt(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 +} + +func textParseBool(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 +} + +func textParseFloat(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 +} + +func textParseInt(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 } diff --git a/compiler/stdlib/text_regexp.go b/compiler/stdlib/text_regexp.go new file mode 100644 index 0000000..94bf2ed --- /dev/null +++ b/compiler/stdlib/text_regexp.go @@ -0,0 +1,167 @@ +package stdlib + +import ( + "regexp" + + "github.com/d5/tengo/objects" +) + +func makeTextRegexp(re *regexp.Regexp) *objects.ImmutableMap { + return &objects.ImmutableMap{ + Value: map[string]objects.Object{ + // match(text) => bool + "match": &objects.UserFunction{ + Value: func(args ...objects.Object) (ret objects.Object, err error) { + if len(args) != 1 { + err = objects.ErrWrongNumArguments + return + } + + s1, ok := objects.ToString(args[0]) + if !ok { + err = objects.ErrInvalidTypeConversion + return + } + + if re.MatchString(s1) { + ret = objects.TrueValue + } else { + ret = objects.FalseValue + } + + return + }, + }, + + // find(text) => array(array({text:,begin:,end:}))/undefined + // find(text, maxCount) => array(array({text:,begin:,end:}))/undefined + "find": &objects.UserFunction{ + Value: func(args ...objects.Object) (ret objects.Object, err error) { + numArgs := len(args) + if numArgs != 1 && numArgs != 2 { + err = objects.ErrWrongNumArguments + return + } + + s1, ok := objects.ToString(args[0]) + if !ok { + err = objects.ErrInvalidTypeConversion + return + } + + if numArgs == 1 { + m := re.FindStringSubmatchIndex(s1) + if m == nil { + ret = objects.UndefinedValue + return + } + + arr := &objects.Array{} + for i := 0; i < len(m); i += 2 { + arr.Value = append(arr.Value, &objects.ImmutableMap{Value: map[string]objects.Object{ + "text": &objects.String{Value: s1[m[i]:m[i+1]]}, + "begin": &objects.Int{Value: int64(m[i])}, + "end": &objects.Int{Value: int64(m[i+1])}, + }}) + } + + ret = &objects.Array{Value: []objects.Object{arr}} + + return + } + + i2, ok := objects.ToInt(args[1]) + if !ok { + err = objects.ErrInvalidTypeConversion + return + } + m := re.FindAllStringSubmatchIndex(s1, i2) + if m == nil { + ret = objects.UndefinedValue + return + } + + arr := &objects.Array{} + for _, m := range m { + subMatch := &objects.Array{} + for i := 0; i < len(m); i += 2 { + subMatch.Value = append(subMatch.Value, &objects.ImmutableMap{Value: map[string]objects.Object{ + "text": &objects.String{Value: s1[m[i]:m[i+1]]}, + "begin": &objects.Int{Value: int64(m[i])}, + "end": &objects.Int{Value: int64(m[i+1])}, + }}) + } + + arr.Value = append(arr.Value, subMatch) + } + + ret = arr + + return + }, + }, + + // replace(src, repl) => string + "replace": &objects.UserFunction{ + Value: func(args ...objects.Object) (ret objects.Object, err error) { + if len(args) != 2 { + err = objects.ErrWrongNumArguments + return + } + + s1, ok := objects.ToString(args[0]) + if !ok { + err = objects.ErrInvalidTypeConversion + return + } + + s2, ok := objects.ToString(args[1]) + if !ok { + err = objects.ErrInvalidTypeConversion + return + } + + ret = &objects.String{Value: re.ReplaceAllString(s1, s2)} + + return + }, + }, + + // split(text) => array(string) + // split(text, maxCount) => array(string) + "split": &objects.UserFunction{ + Value: func(args ...objects.Object) (ret objects.Object, err error) { + numArgs := len(args) + if numArgs != 1 && numArgs != 2 { + err = objects.ErrWrongNumArguments + return + } + + s1, ok := objects.ToString(args[0]) + if !ok { + err = objects.ErrInvalidTypeConversion + return + } + + var i2 = -1 + if numArgs > 1 { + i2, ok = objects.ToInt(args[1]) + if !ok { + err = objects.ErrInvalidTypeConversion + return + } + } + + arr := &objects.Array{} + for _, s := range re.Split(s1, i2) { + arr.Value = append(arr.Value, &objects.String{Value: s}) + } + + ret = arr + + return + }, + }, + }, + } +} diff --git a/compiler/stdlib/times.go b/compiler/stdlib/times.go index 4027c4e..fd4f7cc 100644 --- a/compiler/stdlib/times.go +++ b/compiler/stdlib/times.go @@ -7,795 +7,776 @@ import ( ) var timesModule = map[string]objects.Object{ - // time format constants - "format_ansic": &objects.String{Value: time.ANSIC}, - "format_unix_date": &objects.String{Value: time.UnixDate}, - "format_ruby_date": &objects.String{Value: time.RubyDate}, - "format_rfc822": &objects.String{Value: time.RFC822}, - "format_rfc822z": &objects.String{Value: time.RFC822Z}, - "format_rfc850": &objects.String{Value: time.RFC850}, - "format_rfc1123": &objects.String{Value: time.RFC1123}, - "format_rfc1123z": &objects.String{Value: time.RFC1123Z}, - "format_rfc3339": &objects.String{Value: time.RFC3339}, - "format_rfc3339_nano": &objects.String{Value: time.RFC3339Nano}, - "format_kitchen": &objects.String{Value: time.Kitchen}, - "format_stamp": &objects.String{Value: time.Stamp}, - "format_stamp_milli": &objects.String{Value: time.StampMilli}, - "format_stamp_micro": &objects.String{Value: time.StampMicro}, - "format_stamp_nano": &objects.String{Value: time.StampNano}, - // duration constants - "nanosecond": &objects.Int{Value: int64(time.Nanosecond)}, - "microsecond": &objects.Int{Value: int64(time.Microsecond)}, - "millisecond": &objects.Int{Value: int64(time.Millisecond)}, - "second": &objects.Int{Value: int64(time.Second)}, - "minute": &objects.Int{Value: int64(time.Minute)}, - "hour": &objects.Int{Value: int64(time.Hour)}, - // month constants - "january": &objects.Int{Value: int64(time.January)}, - "february": &objects.Int{Value: int64(time.February)}, - "march": &objects.Int{Value: int64(time.March)}, - "april": &objects.Int{Value: int64(time.April)}, - "may": &objects.Int{Value: int64(time.May)}, - "june": &objects.Int{Value: int64(time.June)}, - "july": &objects.Int{Value: int64(time.July)}, - "august": &objects.Int{Value: int64(time.August)}, - "september": &objects.Int{Value: int64(time.September)}, - "october": &objects.Int{Value: int64(time.October)}, - "november": &objects.Int{Value: int64(time.November)}, - "december": &objects.Int{Value: int64(time.December)}, - // sleep(int) - "sleep": &objects.UserFunction{ - Value: func(args ...objects.Object) (ret objects.Object, err error) { - if len(args) != 1 { - err = objects.ErrWrongNumArguments - return - } - - i1, ok := objects.ToInt64(args[0]) - if !ok { - err = objects.ErrInvalidTypeConversion - return - } - - time.Sleep(time.Duration(i1)) - ret = objects.UndefinedValue - - return - }, - }, - // parse_duration(str) => int - "parse_duration": &objects.UserFunction{ - Value: func(args ...objects.Object) (ret objects.Object, err error) { - if len(args) != 1 { - err = objects.ErrWrongNumArguments - return - } - - s1, ok := objects.ToString(args[0]) - if !ok { - err = objects.ErrInvalidTypeConversion - return - } - - dur, err := time.ParseDuration(s1) - if err != nil { - ret = wrapError(err) - return - } - - ret = &objects.Int{Value: int64(dur)} - - return - }, - }, - // since(time) => int - "since": &objects.UserFunction{ - Value: func(args ...objects.Object) (ret objects.Object, err error) { - if len(args) != 1 { - err = objects.ErrWrongNumArguments - return - } - - t1, ok := objects.ToTime(args[0]) - if !ok { - err = objects.ErrInvalidTypeConversion - return - } - - ret = &objects.Int{Value: int64(time.Since(t1))} - - return - }, - }, - // until(time) => int - "until": &objects.UserFunction{ - Value: func(args ...objects.Object) (ret objects.Object, err error) { - if len(args) != 1 { - err = objects.ErrWrongNumArguments - return - } - - t1, ok := objects.ToTime(args[0]) - if !ok { - err = objects.ErrInvalidTypeConversion - return - } - - ret = &objects.Int{Value: int64(time.Until(t1))} - - return - }, - }, - // duration_hours(int) => float - "duration_hours": &objects.UserFunction{ - Value: func(args ...objects.Object) (ret objects.Object, err error) { - if len(args) != 1 { - err = objects.ErrWrongNumArguments - return - } - - i1, ok := objects.ToInt64(args[0]) - if !ok { - err = objects.ErrInvalidTypeConversion - return - } - - ret = &objects.Float{Value: time.Duration(i1).Hours()} - - return - }, - }, - // duration_minutes(int) => float - "duration_minutes": &objects.UserFunction{ - Value: func(args ...objects.Object) (ret objects.Object, err error) { - if len(args) != 1 { - err = objects.ErrWrongNumArguments - return - } - - i1, ok := objects.ToInt64(args[0]) - if !ok { - err = objects.ErrInvalidTypeConversion - return - } - - ret = &objects.Float{Value: time.Duration(i1).Minutes()} - - return - }, - }, - // duration_nanoseconds(int) => int - "duration_nanoseconds": &objects.UserFunction{ - Value: func(args ...objects.Object) (ret objects.Object, err error) { - if len(args) != 1 { - err = objects.ErrWrongNumArguments - return - } - - i1, ok := objects.ToInt64(args[0]) - if !ok { - err = objects.ErrInvalidTypeConversion - return - } - - ret = &objects.Int{Value: time.Duration(i1).Nanoseconds()} - - return - }, - }, - // duration_seconds(int) => float - "duration_seconds": &objects.UserFunction{ - Value: func(args ...objects.Object) (ret objects.Object, err error) { - if len(args) != 1 { - err = objects.ErrWrongNumArguments - return - } - - i1, ok := objects.ToInt64(args[0]) - if !ok { - err = objects.ErrInvalidTypeConversion - return - } - - ret = &objects.Float{Value: time.Duration(i1).Seconds()} - - return - }, - }, - // duration_string(int) => string - "duration_string": &objects.UserFunction{ - Value: func(args ...objects.Object) (ret objects.Object, err error) { - if len(args) != 1 { - err = objects.ErrWrongNumArguments - return - } - - i1, ok := objects.ToInt64(args[0]) - if !ok { - err = objects.ErrInvalidTypeConversion - return - } - - ret = &objects.String{Value: time.Duration(i1).String()} - - return - }, - }, - // month_string(int) => string - "month_string": &objects.UserFunction{ - Value: func(args ...objects.Object) (ret objects.Object, err error) { - if len(args) != 1 { - err = objects.ErrWrongNumArguments - return - } - - i1, ok := objects.ToInt64(args[0]) - if !ok { - err = objects.ErrInvalidTypeConversion - return - } - - ret = &objects.String{Value: time.Month(i1).String()} - - return - }, - }, - // date(year, month, day, hour, min, sec, nsec) => time - "date": &objects.UserFunction{ - Value: func(args ...objects.Object) (ret objects.Object, err error) { - if len(args) != 7 { - err = objects.ErrWrongNumArguments - return - } - - i1, ok := objects.ToInt(args[0]) - 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 - } - i4, ok := objects.ToInt(args[3]) - if !ok { - err = objects.ErrInvalidTypeConversion - return - } - i5, ok := objects.ToInt(args[4]) - if !ok { - err = objects.ErrInvalidTypeConversion - return - } - i6, ok := objects.ToInt(args[5]) - if !ok { - err = objects.ErrInvalidTypeConversion - return - } - i7, ok := objects.ToInt(args[6]) - if !ok { - err = objects.ErrInvalidTypeConversion - return - } - - ret = &objects.Time{Value: time.Date(i1, time.Month(i2), i3, i4, i5, i6, i7, time.Now().Location())} - - return - }, - }, - // now() => time - "now": &objects.UserFunction{ - Value: func(args ...objects.Object) (ret objects.Object, err error) { - if len(args) != 0 { - err = objects.ErrWrongNumArguments - return - } - - ret = &objects.Time{Value: time.Now()} - - return - }, - }, - // parse(format, str) => time - "parse": &objects.UserFunction{ - Value: func(args ...objects.Object) (ret objects.Object, err error) { - if len(args) != 2 { - err = objects.ErrWrongNumArguments - return - } - - s1, ok := objects.ToString(args[0]) - if !ok { - err = objects.ErrInvalidTypeConversion - return - } - - s2, ok := objects.ToString(args[1]) - if !ok { - err = objects.ErrInvalidTypeConversion - return - } - - parsed, err := time.Parse(s1, s2) - if err != nil { - ret = wrapError(err) - return - } - - ret = &objects.Time{Value: parsed} - - return - }, - }, - // unix(sec, nsec) => time - "unix": &objects.UserFunction{ - Value: func(args ...objects.Object) (ret objects.Object, err error) { - if len(args) != 2 { - err = objects.ErrWrongNumArguments - return - } - - i1, ok := objects.ToInt64(args[0]) - if !ok { - err = objects.ErrInvalidTypeConversion - return - } - - i2, ok := objects.ToInt64(args[1]) - if !ok { - err = objects.ErrInvalidTypeConversion - return - } - - ret = &objects.Time{Value: time.Unix(i1, i2)} - - return - }, - }, - // add(time, int) => time - "add": &objects.UserFunction{ - Value: func(args ...objects.Object) (ret objects.Object, err error) { - if len(args) != 2 { - err = objects.ErrWrongNumArguments - return - } - - t1, ok := objects.ToTime(args[0]) - if !ok { - err = objects.ErrInvalidTypeConversion - return - } - - i2, ok := objects.ToInt64(args[1]) - if !ok { - err = objects.ErrInvalidTypeConversion - return - } - - ret = &objects.Time{Value: t1.Add(time.Duration(i2))} - - return - }, - }, - // add_date(time, years, months, days) => time - "add_date": &objects.UserFunction{ - Value: func(args ...objects.Object) (ret objects.Object, err error) { - if len(args) != 4 { - err = objects.ErrWrongNumArguments - return - } - - t1, ok := objects.ToTime(args[0]) - 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 - } - - i4, ok := objects.ToInt(args[3]) - if !ok { - err = objects.ErrInvalidTypeConversion - return - } - - ret = &objects.Time{Value: t1.AddDate(i2, i3, i4)} - - return - }, - }, - // after(t time, u time) => bool - "after": &objects.UserFunction{ - Value: func(args ...objects.Object) (ret objects.Object, err error) { - if len(args) != 2 { - err = objects.ErrWrongNumArguments - return - } - - t1, ok := objects.ToTime(args[0]) - if !ok { - err = objects.ErrInvalidTypeConversion - return - } - - t2, ok := objects.ToTime(args[1]) - if !ok { - err = objects.ErrInvalidTypeConversion - return - } - - if t1.After(t2) { - ret = objects.TrueValue - } else { - ret = objects.FalseValue - } - - return - }, - }, - // before(t time, u time) => bool - "before": &objects.UserFunction{ - Value: func(args ...objects.Object) (ret objects.Object, err error) { - if len(args) != 2 { - err = objects.ErrWrongNumArguments - return - } - - t1, ok := objects.ToTime(args[0]) - if !ok { - err = objects.ErrInvalidTypeConversion - return - } - - t2, ok := objects.ToTime(args[1]) - if !ok { - err = objects.ErrInvalidTypeConversion - return - } - - if t1.Before(t2) { - ret = objects.TrueValue - } else { - ret = objects.FalseValue - } - - return - }, - }, - // time_day(time) => int - "time_day": &objects.UserFunction{ - Value: func(args ...objects.Object) (ret objects.Object, err error) { - if len(args) != 1 { - err = objects.ErrWrongNumArguments - return - } - - t1, ok := objects.ToTime(args[0]) - if !ok { - err = objects.ErrInvalidTypeConversion - return - } - - ret = &objects.Int{Value: int64(t1.Day())} - - return - }, - }, - // time_hour(time) => int - "time_hour": &objects.UserFunction{ - Value: func(args ...objects.Object) (ret objects.Object, err error) { - if len(args) != 1 { - err = objects.ErrWrongNumArguments - return - } - - t1, ok := objects.ToTime(args[0]) - if !ok { - err = objects.ErrInvalidTypeConversion - return - } - - ret = &objects.Int{Value: int64(t1.Hour())} - - return - }, - }, - // time_year(time) => int - "time_year": &objects.UserFunction{ - Value: func(args ...objects.Object) (ret objects.Object, err error) { - if len(args) != 1 { - err = objects.ErrWrongNumArguments - return - } - - t1, ok := objects.ToTime(args[0]) - if !ok { - err = objects.ErrInvalidTypeConversion - return - } - - ret = &objects.Int{Value: int64(t1.Year())} - - return - }, - }, - // time_month(time) => int - "time_month": &objects.UserFunction{ - Value: func(args ...objects.Object) (ret objects.Object, err error) { - if len(args) != 1 { - err = objects.ErrWrongNumArguments - return - } - - t1, ok := objects.ToTime(args[0]) - if !ok { - err = objects.ErrInvalidTypeConversion - return - } - - ret = &objects.Int{Value: int64(t1.Month())} - - return - }, - }, - // time_minute(time) => int - "time_minute": &objects.UserFunction{ - Value: func(args ...objects.Object) (ret objects.Object, err error) { - if len(args) != 1 { - err = objects.ErrWrongNumArguments - return - } - - t1, ok := objects.ToTime(args[0]) - if !ok { - err = objects.ErrInvalidTypeConversion - return - } - - ret = &objects.Int{Value: int64(t1.Minute())} - - return - }, - }, - // time_second(time) => int - "time_second": &objects.UserFunction{ - Value: func(args ...objects.Object) (ret objects.Object, err error) { - if len(args) != 1 { - err = objects.ErrWrongNumArguments - return - } - - t1, ok := objects.ToTime(args[0]) - if !ok { - err = objects.ErrInvalidTypeConversion - return - } - - ret = &objects.Int{Value: int64(t1.Second())} - - return - }, - }, - // time_nanosecond(time) => int - "time_nanosecond": &objects.UserFunction{ - Value: func(args ...objects.Object) (ret objects.Object, err error) { - if len(args) != 1 { - err = objects.ErrWrongNumArguments - return - } - - t1, ok := objects.ToTime(args[0]) - if !ok { - err = objects.ErrInvalidTypeConversion - return - } - - ret = &objects.Int{Value: int64(t1.Nanosecond())} - - return - }, - }, - // time_unix(time) => int - "time_unix": &objects.UserFunction{ - Value: func(args ...objects.Object) (ret objects.Object, err error) { - if len(args) != 1 { - err = objects.ErrWrongNumArguments - return - } - - t1, ok := objects.ToTime(args[0]) - if !ok { - err = objects.ErrInvalidTypeConversion - return - } - - ret = &objects.Int{Value: int64(t1.Unix())} - - return - }, - }, - // time_unix_nano(time) => int - "time_unix_nano": &objects.UserFunction{ - Value: func(args ...objects.Object) (ret objects.Object, err error) { - if len(args) != 1 { - err = objects.ErrWrongNumArguments - return - } - - t1, ok := objects.ToTime(args[0]) - if !ok { - err = objects.ErrInvalidTypeConversion - return - } - - ret = &objects.Int{Value: int64(t1.UnixNano())} - - return - }, - }, - // time_format(time, format) => string - "time_format": &objects.UserFunction{ - Value: func(args ...objects.Object) (ret objects.Object, err error) { - if len(args) != 2 { - err = objects.ErrWrongNumArguments - return - } - - t1, ok := objects.ToTime(args[0]) - if !ok { - err = objects.ErrInvalidTypeConversion - return - } - - s2, ok := objects.ToString(args[1]) - if !ok { - err = objects.ErrInvalidTypeConversion - return - } - - ret = &objects.String{Value: t1.Format(s2)} - - return - }, - }, - // is_zero(time) => bool - "is_zero": &objects.UserFunction{ - Value: func(args ...objects.Object) (ret objects.Object, err error) { - if len(args) != 1 { - err = objects.ErrWrongNumArguments - return - } - - t1, ok := objects.ToTime(args[0]) - if !ok { - err = objects.ErrInvalidTypeConversion - return - } - - if t1.IsZero() { - ret = objects.TrueValue - } else { - ret = objects.FalseValue - } - - return - }, - }, - // to_local(time) => time - "to_local": &objects.UserFunction{ - Value: func(args ...objects.Object) (ret objects.Object, err error) { - if len(args) != 1 { - err = objects.ErrWrongNumArguments - return - } - - t1, ok := objects.ToTime(args[0]) - if !ok { - err = objects.ErrInvalidTypeConversion - return - } - - ret = &objects.Time{Value: t1.Local()} - - return - }, - }, - // to_utc(time) => time - "to_utc": &objects.UserFunction{ - Value: func(args ...objects.Object) (ret objects.Object, err error) { - if len(args) != 1 { - err = objects.ErrWrongNumArguments - return - } - - t1, ok := objects.ToTime(args[0]) - if !ok { - err = objects.ErrInvalidTypeConversion - return - } - - ret = &objects.Time{Value: t1.UTC()} - - return - }, - }, - // location(time) => string - "time_location": &objects.UserFunction{ - Value: func(args ...objects.Object) (ret objects.Object, err error) { - if len(args) != 1 { - err = objects.ErrWrongNumArguments - return - } - - t1, ok := objects.ToTime(args[0]) - if !ok { - err = objects.ErrInvalidTypeConversion - return - } - - ret = &objects.String{Value: t1.Location().String()} - - return - }, - }, - // time_string(time) => string - "time_string": &objects.UserFunction{ - Value: func(args ...objects.Object) (ret objects.Object, err error) { - if len(args) != 1 { - err = objects.ErrWrongNumArguments - return - } - - t1, ok := objects.ToTime(args[0]) - if !ok { - err = objects.ErrInvalidTypeConversion - return - } - - ret = &objects.String{Value: t1.String()} - - return - }, - }, - // sub(t time, u time) => int - "sub": &objects.UserFunction{ - Value: func(args ...objects.Object) (ret objects.Object, err error) { - if len(args) != 2 { - err = objects.ErrWrongNumArguments - return - } - - t1, ok := objects.ToTime(args[0]) - if !ok { - err = objects.ErrInvalidTypeConversion - return - } - - t2, ok := objects.ToTime(args[1]) - if !ok { - err = objects.ErrInvalidTypeConversion - return - } - - ret = &objects.Int{Value: int64(t1.Sub(t2))} - - return - }, - }, + "format_ansic": &objects.String{Value: time.ANSIC}, + "format_unix_date": &objects.String{Value: time.UnixDate}, + "format_ruby_date": &objects.String{Value: time.RubyDate}, + "format_rfc822": &objects.String{Value: time.RFC822}, + "format_rfc822z": &objects.String{Value: time.RFC822Z}, + "format_rfc850": &objects.String{Value: time.RFC850}, + "format_rfc1123": &objects.String{Value: time.RFC1123}, + "format_rfc1123z": &objects.String{Value: time.RFC1123Z}, + "format_rfc3339": &objects.String{Value: time.RFC3339}, + "format_rfc3339_nano": &objects.String{Value: time.RFC3339Nano}, + "format_kitchen": &objects.String{Value: time.Kitchen}, + "format_stamp": &objects.String{Value: time.Stamp}, + "format_stamp_milli": &objects.String{Value: time.StampMilli}, + "format_stamp_micro": &objects.String{Value: time.StampMicro}, + "format_stamp_nano": &objects.String{Value: time.StampNano}, + "nanosecond": &objects.Int{Value: int64(time.Nanosecond)}, + "microsecond": &objects.Int{Value: int64(time.Microsecond)}, + "millisecond": &objects.Int{Value: int64(time.Millisecond)}, + "second": &objects.Int{Value: int64(time.Second)}, + "minute": &objects.Int{Value: int64(time.Minute)}, + "hour": &objects.Int{Value: int64(time.Hour)}, + "january": &objects.Int{Value: int64(time.January)}, + "february": &objects.Int{Value: int64(time.February)}, + "march": &objects.Int{Value: int64(time.March)}, + "april": &objects.Int{Value: int64(time.April)}, + "may": &objects.Int{Value: int64(time.May)}, + "june": &objects.Int{Value: int64(time.June)}, + "july": &objects.Int{Value: int64(time.July)}, + "august": &objects.Int{Value: int64(time.August)}, + "september": &objects.Int{Value: int64(time.September)}, + "october": &objects.Int{Value: int64(time.October)}, + "november": &objects.Int{Value: int64(time.November)}, + "december": &objects.Int{Value: int64(time.December)}, + "sleep": &objects.UserFunction{Value: timesSleep}, // sleep(int) + "parse_duration": &objects.UserFunction{Value: timesParseDuration}, // parse_duration(str) => int + "since": &objects.UserFunction{Value: timesSince}, // since(time) => int + "until": &objects.UserFunction{Value: timesUntil}, // until(time) => int + "duration_hours": &objects.UserFunction{Value: timesDurationHours}, // duration_hours(int) => float + "duration_minutes": &objects.UserFunction{Value: timesDurationMinutes}, // duration_minutes(int) => float + "duration_nanoseconds": &objects.UserFunction{Value: timesDurationNanoseconds}, // duration_nanoseconds(int) => int + "duration_seconds": &objects.UserFunction{Value: timesDurationSeconds}, // duration_seconds(int) => float + "duration_string": &objects.UserFunction{Value: timesDurationString}, // duration_string(int) => string + "month_string": &objects.UserFunction{Value: timesMonthString}, // month_string(int) => string + "date": &objects.UserFunction{Value: timesDate}, // date(year, month, day, hour, min, sec, nsec) => time + "now": &objects.UserFunction{Value: timesNow}, // now() => time + "parse": &objects.UserFunction{Value: timesParse}, // parse(format, str) => time + "unix": &objects.UserFunction{Value: timesUnix}, // unix(sec, nsec) => time + "add": &objects.UserFunction{Value: timesAdd}, // add(time, int) => time + "add_date": &objects.UserFunction{Value: timesAddDate}, // add_date(time, years, months, days) => time + "sub": &objects.UserFunction{Value: timesSub}, // sub(t time, u time) => int + "after": &objects.UserFunction{Value: timesAfter}, // after(t time, u time) => bool + "before": &objects.UserFunction{Value: timesBefore}, // before(t time, u time) => bool + "time_year": &objects.UserFunction{Value: timesTimeYear}, // time_year(time) => int + "time_month": &objects.UserFunction{Value: timesTimeMonth}, // time_month(time) => int + "time_day": &objects.UserFunction{Value: timesTimeDay}, // time_day(time) => int + "time_weekday": &objects.UserFunction{Value: timesTimeWeekday}, // time_weekday(time) => int + "time_hour": &objects.UserFunction{Value: timesTimeHour}, // time_hour(time) => int + "time_minute": &objects.UserFunction{Value: timesTimeMinute}, // time_minute(time) => int + "time_second": &objects.UserFunction{Value: timesTimeSecond}, // time_second(time) => int + "time_nanosecond": &objects.UserFunction{Value: timesTimeNanosecond}, // time_nanosecond(time) => int + "time_unix": &objects.UserFunction{Value: timesTimeUnix}, // time_unix(time) => int + "time_unix_nano": &objects.UserFunction{Value: timesTimeUnixNano}, // time_unix_nano(time) => int + "time_format": &objects.UserFunction{Value: timesTimeFormat}, // time_format(time, format) => string + "time_location": &objects.UserFunction{Value: timesTimeLocation}, // time_location(time) => string + "time_string": &objects.UserFunction{Value: timesTimeString}, // time_string(time) => string + "is_zero": &objects.UserFunction{Value: timesIsZero}, // is_zero(time) => bool + "to_local": &objects.UserFunction{Value: timesToLocal}, // to_local(time) => time + "to_utc": &objects.UserFunction{Value: timesToUTC}, // to_utc(time) => time +} + +func timesSleep(args ...objects.Object) (ret objects.Object, err error) { + if len(args) != 1 { + err = objects.ErrWrongNumArguments + return + } + + i1, ok := objects.ToInt64(args[0]) + if !ok { + err = objects.ErrInvalidTypeConversion + return + } + + time.Sleep(time.Duration(i1)) + ret = objects.UndefinedValue + + return +} + +func timesParseDuration(args ...objects.Object) (ret objects.Object, err error) { + if len(args) != 1 { + err = objects.ErrWrongNumArguments + return + } + + s1, ok := objects.ToString(args[0]) + if !ok { + err = objects.ErrInvalidTypeConversion + return + } + + dur, err := time.ParseDuration(s1) + if err != nil { + ret = wrapError(err) + return + } + + ret = &objects.Int{Value: int64(dur)} + + return +} + +func timesSince(args ...objects.Object) (ret objects.Object, err error) { + if len(args) != 1 { + err = objects.ErrWrongNumArguments + return + } + + t1, ok := objects.ToTime(args[0]) + if !ok { + err = objects.ErrInvalidTypeConversion + return + } + + ret = &objects.Int{Value: int64(time.Since(t1))} + + return +} + +func timesUntil(args ...objects.Object) (ret objects.Object, err error) { + if len(args) != 1 { + err = objects.ErrWrongNumArguments + return + } + + t1, ok := objects.ToTime(args[0]) + if !ok { + err = objects.ErrInvalidTypeConversion + return + } + + ret = &objects.Int{Value: int64(time.Until(t1))} + + return +} + +func timesDurationHours(args ...objects.Object) (ret objects.Object, err error) { + if len(args) != 1 { + err = objects.ErrWrongNumArguments + return + } + + i1, ok := objects.ToInt64(args[0]) + if !ok { + err = objects.ErrInvalidTypeConversion + return + } + + ret = &objects.Float{Value: time.Duration(i1).Hours()} + + return +} + +func timesDurationMinutes(args ...objects.Object) (ret objects.Object, err error) { + if len(args) != 1 { + err = objects.ErrWrongNumArguments + return + } + + i1, ok := objects.ToInt64(args[0]) + if !ok { + err = objects.ErrInvalidTypeConversion + return + } + + ret = &objects.Float{Value: time.Duration(i1).Minutes()} + + return +} + +func timesDurationNanoseconds(args ...objects.Object) (ret objects.Object, err error) { + if len(args) != 1 { + err = objects.ErrWrongNumArguments + return + } + + i1, ok := objects.ToInt64(args[0]) + if !ok { + err = objects.ErrInvalidTypeConversion + return + } + + ret = &objects.Int{Value: time.Duration(i1).Nanoseconds()} + + return +} + +func timesDurationSeconds(args ...objects.Object) (ret objects.Object, err error) { + if len(args) != 1 { + err = objects.ErrWrongNumArguments + return + } + + i1, ok := objects.ToInt64(args[0]) + if !ok { + err = objects.ErrInvalidTypeConversion + return + } + + ret = &objects.Float{Value: time.Duration(i1).Seconds()} + + return +} + +func timesDurationString(args ...objects.Object) (ret objects.Object, err error) { + if len(args) != 1 { + err = objects.ErrWrongNumArguments + return + } + + i1, ok := objects.ToInt64(args[0]) + if !ok { + err = objects.ErrInvalidTypeConversion + return + } + + ret = &objects.String{Value: time.Duration(i1).String()} + + return +} + +func timesMonthString(args ...objects.Object) (ret objects.Object, err error) { + if len(args) != 1 { + err = objects.ErrWrongNumArguments + return + } + + i1, ok := objects.ToInt64(args[0]) + if !ok { + err = objects.ErrInvalidTypeConversion + return + } + + ret = &objects.String{Value: time.Month(i1).String()} + + return +} + +func timesDate(args ...objects.Object) (ret objects.Object, err error) { + if len(args) != 7 { + err = objects.ErrWrongNumArguments + return + } + + i1, ok := objects.ToInt(args[0]) + 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 + } + i4, ok := objects.ToInt(args[3]) + if !ok { + err = objects.ErrInvalidTypeConversion + return + } + i5, ok := objects.ToInt(args[4]) + if !ok { + err = objects.ErrInvalidTypeConversion + return + } + i6, ok := objects.ToInt(args[5]) + if !ok { + err = objects.ErrInvalidTypeConversion + return + } + i7, ok := objects.ToInt(args[6]) + if !ok { + err = objects.ErrInvalidTypeConversion + return + } + + ret = &objects.Time{Value: time.Date(i1, time.Month(i2), i3, i4, i5, i6, i7, time.Now().Location())} + + return +} + +func timesNow(args ...objects.Object) (ret objects.Object, err error) { + if len(args) != 0 { + err = objects.ErrWrongNumArguments + return + } + + ret = &objects.Time{Value: time.Now()} + + return +} + +func timesParse(args ...objects.Object) (ret objects.Object, err error) { + if len(args) != 2 { + err = objects.ErrWrongNumArguments + return + } + + s1, ok := objects.ToString(args[0]) + if !ok { + err = objects.ErrInvalidTypeConversion + return + } + + s2, ok := objects.ToString(args[1]) + if !ok { + err = objects.ErrInvalidTypeConversion + return + } + + parsed, err := time.Parse(s1, s2) + if err != nil { + ret = wrapError(err) + return + } + + ret = &objects.Time{Value: parsed} + + return +} + +func timesUnix(args ...objects.Object) (ret objects.Object, err error) { + if len(args) != 2 { + err = objects.ErrWrongNumArguments + return + } + + i1, ok := objects.ToInt64(args[0]) + if !ok { + err = objects.ErrInvalidTypeConversion + return + } + + i2, ok := objects.ToInt64(args[1]) + if !ok { + err = objects.ErrInvalidTypeConversion + return + } + + ret = &objects.Time{Value: time.Unix(i1, i2)} + + return +} + +func timesAdd(args ...objects.Object) (ret objects.Object, err error) { + if len(args) != 2 { + err = objects.ErrWrongNumArguments + return + } + + t1, ok := objects.ToTime(args[0]) + if !ok { + err = objects.ErrInvalidTypeConversion + return + } + + i2, ok := objects.ToInt64(args[1]) + if !ok { + err = objects.ErrInvalidTypeConversion + return + } + + ret = &objects.Time{Value: t1.Add(time.Duration(i2))} + + return +} + +func timesSub(args ...objects.Object) (ret objects.Object, err error) { + if len(args) != 2 { + err = objects.ErrWrongNumArguments + return + } + + t1, ok := objects.ToTime(args[0]) + if !ok { + err = objects.ErrInvalidTypeConversion + return + } + + t2, ok := objects.ToTime(args[1]) + if !ok { + err = objects.ErrInvalidTypeConversion + return + } + + ret = &objects.Int{Value: int64(t1.Sub(t2))} + + return +} + +func timesAddDate(args ...objects.Object) (ret objects.Object, err error) { + if len(args) != 4 { + err = objects.ErrWrongNumArguments + return + } + + t1, ok := objects.ToTime(args[0]) + 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 + } + + i4, ok := objects.ToInt(args[3]) + if !ok { + err = objects.ErrInvalidTypeConversion + return + } + + ret = &objects.Time{Value: t1.AddDate(i2, i3, i4)} + + return +} + +func timesAfter(args ...objects.Object) (ret objects.Object, err error) { + if len(args) != 2 { + err = objects.ErrWrongNumArguments + return + } + + t1, ok := objects.ToTime(args[0]) + if !ok { + err = objects.ErrInvalidTypeConversion + return + } + + t2, ok := objects.ToTime(args[1]) + if !ok { + err = objects.ErrInvalidTypeConversion + return + } + + if t1.After(t2) { + ret = objects.TrueValue + } else { + ret = objects.FalseValue + } + + return +} + +func timesBefore(args ...objects.Object) (ret objects.Object, err error) { + if len(args) != 2 { + err = objects.ErrWrongNumArguments + return + } + + t1, ok := objects.ToTime(args[0]) + if !ok { + err = objects.ErrInvalidTypeConversion + return + } + + t2, ok := objects.ToTime(args[1]) + if !ok { + err = objects.ErrInvalidTypeConversion + return + } + + if t1.Before(t2) { + ret = objects.TrueValue + } else { + ret = objects.FalseValue + } + + return +} + +func timesTimeYear(args ...objects.Object) (ret objects.Object, err error) { + if len(args) != 1 { + err = objects.ErrWrongNumArguments + return + } + + t1, ok := objects.ToTime(args[0]) + if !ok { + err = objects.ErrInvalidTypeConversion + return + } + + ret = &objects.Int{Value: int64(t1.Year())} + + return +} + +func timesTimeMonth(args ...objects.Object) (ret objects.Object, err error) { + if len(args) != 1 { + err = objects.ErrWrongNumArguments + return + } + + t1, ok := objects.ToTime(args[0]) + if !ok { + err = objects.ErrInvalidTypeConversion + return + } + + ret = &objects.Int{Value: int64(t1.Month())} + + return +} + +func timesTimeDay(args ...objects.Object) (ret objects.Object, err error) { + if len(args) != 1 { + err = objects.ErrWrongNumArguments + return + } + + t1, ok := objects.ToTime(args[0]) + if !ok { + err = objects.ErrInvalidTypeConversion + return + } + + ret = &objects.Int{Value: int64(t1.Day())} + + return +} + +func timesTimeWeekday(args ...objects.Object) (ret objects.Object, err error) { + if len(args) != 1 { + err = objects.ErrWrongNumArguments + return + } + + t1, ok := objects.ToTime(args[0]) + if !ok { + err = objects.ErrInvalidTypeConversion + return + } + + ret = &objects.Int{Value: int64(t1.Weekday())} + + return +} + +func timesTimeHour(args ...objects.Object) (ret objects.Object, err error) { + if len(args) != 1 { + err = objects.ErrWrongNumArguments + return + } + + t1, ok := objects.ToTime(args[0]) + if !ok { + err = objects.ErrInvalidTypeConversion + return + } + + ret = &objects.Int{Value: int64(t1.Hour())} + + return +} + +func timesTimeMinute(args ...objects.Object) (ret objects.Object, err error) { + if len(args) != 1 { + err = objects.ErrWrongNumArguments + return + } + + t1, ok := objects.ToTime(args[0]) + if !ok { + err = objects.ErrInvalidTypeConversion + return + } + + ret = &objects.Int{Value: int64(t1.Minute())} + + return +} + +func timesTimeSecond(args ...objects.Object) (ret objects.Object, err error) { + if len(args) != 1 { + err = objects.ErrWrongNumArguments + return + } + + t1, ok := objects.ToTime(args[0]) + if !ok { + err = objects.ErrInvalidTypeConversion + return + } + + ret = &objects.Int{Value: int64(t1.Second())} + + return +} + +func timesTimeNanosecond(args ...objects.Object) (ret objects.Object, err error) { + if len(args) != 1 { + err = objects.ErrWrongNumArguments + return + } + + t1, ok := objects.ToTime(args[0]) + if !ok { + err = objects.ErrInvalidTypeConversion + return + } + + ret = &objects.Int{Value: int64(t1.Nanosecond())} + + return +} + +func timesTimeUnix(args ...objects.Object) (ret objects.Object, err error) { + if len(args) != 1 { + err = objects.ErrWrongNumArguments + return + } + + t1, ok := objects.ToTime(args[0]) + if !ok { + err = objects.ErrInvalidTypeConversion + return + } + + ret = &objects.Int{Value: int64(t1.Unix())} + + return +} + +func timesTimeUnixNano(args ...objects.Object) (ret objects.Object, err error) { + if len(args) != 1 { + err = objects.ErrWrongNumArguments + return + } + + t1, ok := objects.ToTime(args[0]) + if !ok { + err = objects.ErrInvalidTypeConversion + return + } + + ret = &objects.Int{Value: int64(t1.UnixNano())} + + return +} + +func timesTimeFormat(args ...objects.Object) (ret objects.Object, err error) { + if len(args) != 2 { + err = objects.ErrWrongNumArguments + return + } + + t1, ok := objects.ToTime(args[0]) + if !ok { + err = objects.ErrInvalidTypeConversion + return + } + + s2, ok := objects.ToString(args[1]) + if !ok { + err = objects.ErrInvalidTypeConversion + return + } + + ret = &objects.String{Value: t1.Format(s2)} + + return +} + +func timesIsZero(args ...objects.Object) (ret objects.Object, err error) { + if len(args) != 1 { + err = objects.ErrWrongNumArguments + return + } + + t1, ok := objects.ToTime(args[0]) + if !ok { + err = objects.ErrInvalidTypeConversion + return + } + + if t1.IsZero() { + ret = objects.TrueValue + } else { + ret = objects.FalseValue + } + + return +} + +func timesToLocal(args ...objects.Object) (ret objects.Object, err error) { + if len(args) != 1 { + err = objects.ErrWrongNumArguments + return + } + + t1, ok := objects.ToTime(args[0]) + if !ok { + err = objects.ErrInvalidTypeConversion + return + } + + ret = &objects.Time{Value: t1.Local()} + + return +} + +func timesToUTC(args ...objects.Object) (ret objects.Object, err error) { + if len(args) != 1 { + err = objects.ErrWrongNumArguments + return + } + + t1, ok := objects.ToTime(args[0]) + if !ok { + err = objects.ErrInvalidTypeConversion + return + } + + ret = &objects.Time{Value: t1.UTC()} + + return +} + +func timesTimeLocation(args ...objects.Object) (ret objects.Object, err error) { + if len(args) != 1 { + err = objects.ErrWrongNumArguments + return + } + + t1, ok := objects.ToTime(args[0]) + if !ok { + err = objects.ErrInvalidTypeConversion + return + } + + ret = &objects.String{Value: t1.Location().String()} + + return +} + +func timesTimeString(args ...objects.Object) (ret objects.Object, err error) { + if len(args) != 1 { + err = objects.ErrWrongNumArguments + return + } + + t1, ok := objects.ToTime(args[0]) + if !ok { + err = objects.ErrInvalidTypeConversion + return + } + + ret = &objects.String{Value: t1.String()} + + return } diff --git a/runtime/vm_module_test.go b/runtime/vm_module_test.go index 19fcf67..e059968 100644 --- a/runtime/vm_module_test.go +++ b/runtime/vm_module_test.go @@ -47,17 +47,11 @@ os.remove("./temp") // 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 +os := import("os") +cmd := os.exec("echo", "foo", "bar") +if !is_error(cmd) { + out = cmd.output() } - -out = echo(["foo", "bar"]) `, []byte("foo bar\n")) // user modules