commit
f482de6d69
43 changed files with 2259 additions and 611 deletions
11
README.md
11
README.md
|
@ -97,6 +97,8 @@ c5 := char("X") // 'X'
|
||||||
```
|
```
|
||||||
> [Run in Playground](https://tengolang.com/?s=8d57905b82959eb244e9bbd2111e12ee04a33045)
|
> [Run in Playground](https://tengolang.com/?s=8d57905b82959eb244e9bbd2111e12ee04a33045)
|
||||||
|
|
||||||
|
_See [Variable Types](https://github.com/d5/tengo/wiki/Variable-Types) for more details on the variable types._
|
||||||
|
|
||||||
You can use the dot selector (`.`) and indexer (`[]`) operator to read or write elemens of arrays, strings, or maps.
|
You can use the dot selector (`.`) and indexer (`[]`) operator to read or write elemens of arrays, strings, or maps.
|
||||||
|
|
||||||
```golang
|
```golang
|
||||||
|
@ -115,7 +117,7 @@ m.x = 5 // add 'x' to map 'm'
|
||||||
```
|
```
|
||||||
> [Run in Playground](https://tengolang.com/?s=d510c75ed8f06ef1e22c1aaf8a7d4565c793514c)
|
> [Run in Playground](https://tengolang.com/?s=d510c75ed8f06ef1e22c1aaf8a7d4565c793514c)
|
||||||
|
|
||||||
For sequence types (string or array), you can use slice operator (`[:]`) too.
|
For sequence types (string, bytes, array), you can use slice operator (`[:]`) too.
|
||||||
|
|
||||||
```golang
|
```golang
|
||||||
a := [1, 2, 3, 4, 5][1:3] // == [2, 3]
|
a := [1, 2, 3, 4, 5][1:3] // == [2, 3]
|
||||||
|
@ -210,6 +212,13 @@ foo := 2
|
||||||
|
|
||||||
Basically `import` expression returns all the global variables defined in the module as a Map-like value. One can access the functions or variables defined in the module using `.` selector or `["key"]` indexer, but, module variables are immutable.
|
Basically `import` expression returns all the global variables defined in the module as a Map-like value. One can access the functions or variables defined in the module using `.` selector or `["key"]` indexer, but, module variables are immutable.
|
||||||
|
|
||||||
|
Also you can use `import` to load the [standard libraries](https://github.com/d5/tengo/wiki/Standard-Libraries).
|
||||||
|
|
||||||
|
```golang
|
||||||
|
math := import("math")
|
||||||
|
a := math.abs(-19.84) // == 19.84
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
## Embedding Tengo in Go
|
## Embedding Tengo in Go
|
||||||
|
|
||||||
|
|
|
@ -165,6 +165,10 @@ func Equal(t *testing.T, expected, actual interface{}, msg ...interface{}) bool
|
||||||
return Equal(t, expected.Value, actual.(objects.ReturnValue).Value)
|
return Equal(t, expected.Value, actual.(objects.ReturnValue).Value)
|
||||||
case *objects.Array:
|
case *objects.Array:
|
||||||
return equalArray(t, expected, actual.(*objects.Array))
|
return equalArray(t, expected, actual.(*objects.Array))
|
||||||
|
case *objects.Bytes:
|
||||||
|
if bytes.Compare(expected.Value, actual.(*objects.Bytes).Value) != 0 {
|
||||||
|
return failExpectedActual(t, expected.Value, actual.(*objects.Bytes).Value, msg...)
|
||||||
|
}
|
||||||
case *objects.Map:
|
case *objects.Map:
|
||||||
return equalMap(t, expected, actual.(*objects.Map))
|
return equalMap(t, expected, actual.(*objects.Map))
|
||||||
case *objects.CompiledFunction:
|
case *objects.CompiledFunction:
|
||||||
|
@ -175,6 +179,10 @@ func Equal(t *testing.T, expected, actual interface{}, msg ...interface{}) bool
|
||||||
return true
|
return true
|
||||||
case *objects.Error:
|
case *objects.Error:
|
||||||
return Equal(t, expected.Value, actual.(*objects.Error).Value)
|
return Equal(t, expected.Value, actual.(*objects.Error).Value)
|
||||||
|
case error:
|
||||||
|
if expected != actual.(error) {
|
||||||
|
return failExpectedActual(t, expected, actual, msg...)
|
||||||
|
}
|
||||||
default:
|
default:
|
||||||
panic(fmt.Errorf("type not implemented: %T", expected))
|
panic(fmt.Errorf("type not implemented: %T", expected))
|
||||||
}
|
}
|
||||||
|
|
|
@ -193,7 +193,7 @@ func compileFile(file *ast.File) (time.Duration, *compiler.Bytecode, error) {
|
||||||
|
|
||||||
start := time.Now()
|
start := time.Now()
|
||||||
|
|
||||||
c := compiler.NewCompiler(symTable, nil)
|
c := compiler.NewCompiler(symTable, nil, nil)
|
||||||
if err := c.Compile(file); err != nil {
|
if err := c.Compile(file); err != nil {
|
||||||
return time.Since(start), nil, err
|
return time.Since(start), nil, err
|
||||||
}
|
}
|
||||||
|
|
|
@ -171,7 +171,11 @@ func doRepl(in io.Reader, out io.Writer) {
|
||||||
|
|
||||||
fileSet := source.NewFileSet()
|
fileSet := source.NewFileSet()
|
||||||
globals := make([]*objects.Object, runtime.GlobalsSize)
|
globals := make([]*objects.Object, runtime.GlobalsSize)
|
||||||
|
|
||||||
symbolTable := compiler.NewSymbolTable()
|
symbolTable := compiler.NewSymbolTable()
|
||||||
|
for idx, fn := range objects.Builtins {
|
||||||
|
symbolTable.DefineBuiltin(idx, fn.Name)
|
||||||
|
}
|
||||||
|
|
||||||
for {
|
for {
|
||||||
_, _ = fmt.Fprintf(out, replPrompt)
|
_, _ = fmt.Fprintf(out, replPrompt)
|
||||||
|
@ -191,7 +195,7 @@ func doRepl(in io.Reader, out io.Writer) {
|
||||||
|
|
||||||
file = addPrints(file)
|
file = addPrints(file)
|
||||||
|
|
||||||
c := compiler.NewCompiler(symbolTable, nil)
|
c := compiler.NewCompiler(symbolTable, nil, nil)
|
||||||
if err := c.Compile(file); err != nil {
|
if err := c.Compile(file); err != nil {
|
||||||
_, _ = fmt.Fprintf(out, "Compilation error:\n %s\n", err.Error())
|
_, _ = fmt.Fprintf(out, "Compilation error:\n %s\n", err.Error())
|
||||||
continue
|
continue
|
||||||
|
@ -218,7 +222,7 @@ func compileSrc(src []byte, filename string) (*compiler.Bytecode, error) {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
c := compiler.NewCompiler(nil, nil)
|
c := compiler.NewCompiler(nil, nil, nil)
|
||||||
if err := c.Compile(file); err != nil {
|
if err := c.Compile(file); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
|
@ -45,4 +45,11 @@ func init() {
|
||||||
gob.Register(&objects.Map{})
|
gob.Register(&objects.Map{})
|
||||||
gob.Register(&objects.CompiledFunction{})
|
gob.Register(&objects.CompiledFunction{})
|
||||||
gob.Register(&objects.Undefined{})
|
gob.Register(&objects.Undefined{})
|
||||||
|
gob.Register(&objects.Error{})
|
||||||
|
gob.Register(&objects.ImmutableMap{})
|
||||||
|
gob.Register(&objects.Bytes{})
|
||||||
|
gob.Register(&objects.StringIterator{})
|
||||||
|
gob.Register(&objects.MapIterator{})
|
||||||
|
gob.Register(&objects.ImmutableMapIterator{})
|
||||||
|
gob.Register(&objects.ArrayIterator{})
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,7 +6,7 @@ import (
|
||||||
"reflect"
|
"reflect"
|
||||||
|
|
||||||
"github.com/d5/tengo/compiler/ast"
|
"github.com/d5/tengo/compiler/ast"
|
||||||
"github.com/d5/tengo/compiler/stdmods"
|
"github.com/d5/tengo/compiler/stdlib"
|
||||||
"github.com/d5/tengo/compiler/token"
|
"github.com/d5/tengo/compiler/token"
|
||||||
"github.com/d5/tengo/objects"
|
"github.com/d5/tengo/objects"
|
||||||
)
|
)
|
||||||
|
@ -20,6 +20,7 @@ type Compiler struct {
|
||||||
scopes []CompilationScope
|
scopes []CompilationScope
|
||||||
scopeIndex int
|
scopeIndex int
|
||||||
moduleLoader ModuleLoader
|
moduleLoader ModuleLoader
|
||||||
|
stdModules map[string]*objects.ImmutableMap
|
||||||
compiledModules map[string]*objects.CompiledModule
|
compiledModules map[string]*objects.CompiledModule
|
||||||
loops []*Loop
|
loops []*Loop
|
||||||
loopIndex int
|
loopIndex int
|
||||||
|
@ -28,17 +29,28 @@ type Compiler struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewCompiler creates a Compiler.
|
// NewCompiler creates a Compiler.
|
||||||
func NewCompiler(symbolTable *SymbolTable, trace io.Writer) *Compiler {
|
// User can optionally provide the symbol table if one wants to add or remove
|
||||||
|
// some global- or builtin- scope symbols. If not (nil), Compile will create
|
||||||
|
// a new symbol table and use the default builtin functions. Likewise, standard
|
||||||
|
// modules can be explicitly provided if user wants to add or remove some modules.
|
||||||
|
// By default, Compile will use all the standard modules otherwise.
|
||||||
|
func NewCompiler(symbolTable *SymbolTable, stdModules map[string]*objects.ImmutableMap, trace io.Writer) *Compiler {
|
||||||
mainScope := CompilationScope{
|
mainScope := CompilationScope{
|
||||||
instructions: make([]byte, 0),
|
instructions: make([]byte, 0),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// symbol table
|
||||||
if symbolTable == nil {
|
if symbolTable == nil {
|
||||||
symbolTable = NewSymbolTable()
|
symbolTable = NewSymbolTable()
|
||||||
|
|
||||||
|
for idx, fn := range objects.Builtins {
|
||||||
|
symbolTable.DefineBuiltin(idx, fn.Name)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for idx, fn := range objects.Builtins {
|
// standard modules
|
||||||
symbolTable.DefineBuiltin(idx, fn.Name)
|
if stdModules == nil {
|
||||||
|
stdModules = stdlib.Modules
|
||||||
}
|
}
|
||||||
|
|
||||||
return &Compiler{
|
return &Compiler{
|
||||||
|
@ -47,6 +59,7 @@ func NewCompiler(symbolTable *SymbolTable, trace io.Writer) *Compiler {
|
||||||
scopeIndex: 0,
|
scopeIndex: 0,
|
||||||
loopIndex: -1,
|
loopIndex: -1,
|
||||||
trace: trace,
|
trace: trace,
|
||||||
|
stdModules: stdModules,
|
||||||
compiledModules: make(map[string]*objects.CompiledModule),
|
compiledModules: make(map[string]*objects.CompiledModule),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -440,7 +453,7 @@ func (c *Compiler) Compile(node ast.Node) error {
|
||||||
c.emit(OpCall, len(node.Args))
|
c.emit(OpCall, len(node.Args))
|
||||||
|
|
||||||
case *ast.ImportExpr:
|
case *ast.ImportExpr:
|
||||||
stdMod, ok := stdmods.Modules[node.ModuleName]
|
stdMod, ok := c.stdModules[node.ModuleName]
|
||||||
if ok {
|
if ok {
|
||||||
// standard modules contain only globals with no code.
|
// standard modules contain only globals with no code.
|
||||||
// so no need to compile anything
|
// so no need to compile anything
|
||||||
|
@ -474,12 +487,14 @@ func (c *Compiler) Bytecode() *Bytecode {
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetModuleLoader sets or replaces the current module loader.
|
// SetModuleLoader sets or replaces the current module loader.
|
||||||
|
// Note that the module loader is used for user modules,
|
||||||
|
// not for the standard modules.
|
||||||
func (c *Compiler) SetModuleLoader(moduleLoader ModuleLoader) {
|
func (c *Compiler) SetModuleLoader(moduleLoader ModuleLoader) {
|
||||||
c.moduleLoader = moduleLoader
|
c.moduleLoader = moduleLoader
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Compiler) fork(moduleName string, symbolTable *SymbolTable) *Compiler {
|
func (c *Compiler) fork(moduleName string, symbolTable *SymbolTable) *Compiler {
|
||||||
child := NewCompiler(symbolTable, c.trace)
|
child := NewCompiler(symbolTable, c.stdModules, c.trace)
|
||||||
child.moduleName = moduleName // name of the module to compile
|
child.moduleName = moduleName // name of the module to compile
|
||||||
child.parent = c // parent to set to current compiler
|
child.parent = c // parent to set to current compiler
|
||||||
child.moduleLoader = c.moduleLoader // share module loader
|
child.moduleLoader = c.moduleLoader // share module loader
|
||||||
|
|
|
@ -972,9 +972,12 @@ func traceCompile(input string, symbols map[string]objects.Object) (res *compile
|
||||||
for name := range symbols {
|
for name := range symbols {
|
||||||
symTable.Define(name)
|
symTable.Define(name)
|
||||||
}
|
}
|
||||||
|
for idx, fn := range objects.Builtins {
|
||||||
|
symTable.DefineBuiltin(idx, fn.Name)
|
||||||
|
}
|
||||||
|
|
||||||
tr := &tracer{}
|
tr := &tracer{}
|
||||||
c := compiler.NewCompiler(symTable, tr)
|
c := compiler.NewCompiler(symTable, nil, tr)
|
||||||
parsed, err := p.ParseFile()
|
parsed, err := p.ParseFile()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
|
|
11
compiler/stdlib/errors.go
Normal file
11
compiler/stdlib/errors.go
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
package stdlib
|
||||||
|
|
||||||
|
import "github.com/d5/tengo/objects"
|
||||||
|
|
||||||
|
func wrapError(err error) objects.Object {
|
||||||
|
if err == nil {
|
||||||
|
return objects.TrueValue
|
||||||
|
}
|
||||||
|
|
||||||
|
return &objects.Error{Value: &objects.String{Value: err.Error()}}
|
||||||
|
}
|
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
|
||||||
|
}
|
605
compiler/stdlib/func_typedefs.go
Normal file
605
compiler/stdlib/func_typedefs.go
Normal file
|
@ -0,0 +1,605 @@
|
||||||
|
package stdlib
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/d5/tengo/objects"
|
||||||
|
)
|
||||||
|
|
||||||
|
// FuncAR transform a function of 'func()' signature
|
||||||
|
// into a user function object.
|
||||||
|
func FuncAR(fn func()) *objects.UserFunction {
|
||||||
|
return &objects.UserFunction{
|
||||||
|
Value: func(args ...objects.Object) (ret objects.Object, err error) {
|
||||||
|
if len(args) != 0 {
|
||||||
|
return nil, objects.ErrWrongNumArguments
|
||||||
|
}
|
||||||
|
|
||||||
|
fn()
|
||||||
|
|
||||||
|
return objects.UndefinedValue, nil
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// FuncARI transform a function of 'func() int' signature
|
||||||
|
// into a user function object.
|
||||||
|
func FuncARI(fn func() int) *objects.UserFunction {
|
||||||
|
return &objects.UserFunction{
|
||||||
|
Value: func(args ...objects.Object) (ret objects.Object, err error) {
|
||||||
|
if len(args) != 0 {
|
||||||
|
return nil, objects.ErrWrongNumArguments
|
||||||
|
}
|
||||||
|
|
||||||
|
return &objects.Int{Value: int64(fn())}, nil
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// FuncARB transform a function of 'func() bool' signature
|
||||||
|
// into a user function object.
|
||||||
|
func FuncARB(fn func() bool) *objects.UserFunction {
|
||||||
|
return &objects.UserFunction{
|
||||||
|
Value: func(args ...objects.Object) (ret objects.Object, err error) {
|
||||||
|
if len(args) != 0 {
|
||||||
|
return nil, objects.ErrWrongNumArguments
|
||||||
|
}
|
||||||
|
|
||||||
|
return &objects.Bool{Value: fn()}, nil
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// FuncARE transform a function of 'func() error' signature
|
||||||
|
// into a user function object.
|
||||||
|
func FuncARE(fn func() error) *objects.UserFunction {
|
||||||
|
return &objects.UserFunction{
|
||||||
|
Value: func(args ...objects.Object) (ret objects.Object, err error) {
|
||||||
|
if len(args) != 0 {
|
||||||
|
return nil, objects.ErrWrongNumArguments
|
||||||
|
}
|
||||||
|
|
||||||
|
return wrapError(fn()), nil
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// FuncARS transform a function of 'func() string' signature
|
||||||
|
// into a user function object.
|
||||||
|
func FuncARS(fn func() string) *objects.UserFunction {
|
||||||
|
return &objects.UserFunction{
|
||||||
|
Value: func(args ...objects.Object) (ret objects.Object, err error) {
|
||||||
|
if len(args) != 0 {
|
||||||
|
return nil, objects.ErrWrongNumArguments
|
||||||
|
}
|
||||||
|
|
||||||
|
return &objects.String{Value: fn()}, nil
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// FuncARSE transform a function of 'func() (string, error)' signature
|
||||||
|
// into a user function object.
|
||||||
|
func FuncARSE(fn func() (string, 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.String{Value: res}, nil
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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 {
|
||||||
|
return &objects.UserFunction{
|
||||||
|
Value: func(args ...objects.Object) (ret objects.Object, err error) {
|
||||||
|
if len(args) != 0 {
|
||||||
|
return nil, objects.ErrWrongNumArguments
|
||||||
|
}
|
||||||
|
|
||||||
|
return &objects.Float{Value: fn()}, nil
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// FuncARSs transform a function of 'func() []string' signature
|
||||||
|
// into a user function object.
|
||||||
|
func FuncARSs(fn func() []string) *objects.UserFunction {
|
||||||
|
return &objects.UserFunction{
|
||||||
|
Value: func(args ...objects.Object) (ret objects.Object, err error) {
|
||||||
|
if len(args) != 0 {
|
||||||
|
return nil, objects.ErrWrongNumArguments
|
||||||
|
}
|
||||||
|
|
||||||
|
arr := &objects.Array{}
|
||||||
|
for _, osArg := range fn() {
|
||||||
|
arr.Value = append(arr.Value, &objects.String{Value: osArg})
|
||||||
|
}
|
||||||
|
|
||||||
|
return arr, nil
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// FuncARIsE transform a function of 'func() ([]int, error)' signature
|
||||||
|
// into a user function object.
|
||||||
|
func FuncARIsE(fn func() ([]int, 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
|
||||||
|
}
|
||||||
|
|
||||||
|
arr := &objects.Array{}
|
||||||
|
for _, v := range res {
|
||||||
|
arr.Value = append(arr.Value, &objects.Int{Value: int64(v)})
|
||||||
|
}
|
||||||
|
|
||||||
|
return arr, nil
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// FuncAFRF transform a function of 'func(float64) float64' signature
|
||||||
|
// into a user function object.
|
||||||
|
func FuncAFRF(fn func(float64) float64) *objects.UserFunction {
|
||||||
|
return &objects.UserFunction{
|
||||||
|
Value: func(args ...objects.Object) (ret objects.Object, err error) {
|
||||||
|
if len(args) != 1 {
|
||||||
|
return nil, objects.ErrWrongNumArguments
|
||||||
|
}
|
||||||
|
|
||||||
|
f1, ok := objects.ToFloat64(args[0])
|
||||||
|
if !ok {
|
||||||
|
return nil, objects.ErrInvalidTypeConversion
|
||||||
|
}
|
||||||
|
|
||||||
|
return &objects.Float{Value: fn(f1)}, nil
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// FuncAIR transform a function of 'func(int)' signature
|
||||||
|
// into a user function object.
|
||||||
|
func FuncAIR(fn func(int)) *objects.UserFunction {
|
||||||
|
return &objects.UserFunction{
|
||||||
|
Value: func(args ...objects.Object) (ret objects.Object, err error) {
|
||||||
|
if len(args) != 1 {
|
||||||
|
return nil, objects.ErrWrongNumArguments
|
||||||
|
}
|
||||||
|
|
||||||
|
i1, ok := objects.ToInt(args[0])
|
||||||
|
if !ok {
|
||||||
|
return nil, objects.ErrInvalidTypeConversion
|
||||||
|
}
|
||||||
|
|
||||||
|
fn(i1)
|
||||||
|
|
||||||
|
return objects.UndefinedValue, nil
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// FuncAIRF transform a function of 'func(int) float64' signature
|
||||||
|
// into a user function object.
|
||||||
|
func FuncAIRF(fn func(int) float64) *objects.UserFunction {
|
||||||
|
return &objects.UserFunction{
|
||||||
|
Value: func(args ...objects.Object) (ret objects.Object, err error) {
|
||||||
|
if len(args) != 1 {
|
||||||
|
return nil, objects.ErrWrongNumArguments
|
||||||
|
}
|
||||||
|
|
||||||
|
i1, ok := objects.ToInt(args[0])
|
||||||
|
if !ok {
|
||||||
|
return nil, objects.ErrInvalidTypeConversion
|
||||||
|
}
|
||||||
|
|
||||||
|
return &objects.Float{Value: fn(i1)}, nil
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// FuncAFRI transform a function of 'func(float64) int' signature
|
||||||
|
// into a user function object.
|
||||||
|
func FuncAFRI(fn func(float64) int) *objects.UserFunction {
|
||||||
|
return &objects.UserFunction{
|
||||||
|
Value: func(args ...objects.Object) (ret objects.Object, err error) {
|
||||||
|
if len(args) != 1 {
|
||||||
|
return nil, objects.ErrWrongNumArguments
|
||||||
|
}
|
||||||
|
|
||||||
|
f1, ok := objects.ToFloat64(args[0])
|
||||||
|
if !ok {
|
||||||
|
return nil, objects.ErrInvalidTypeConversion
|
||||||
|
}
|
||||||
|
|
||||||
|
return &objects.Int{Value: int64(fn(f1))}, nil
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// FuncAFFRF transform a function of 'func(float64, float64) float64' signature
|
||||||
|
// into a user function object.
|
||||||
|
func FuncAFFRF(fn func(float64, float64) float64) *objects.UserFunction {
|
||||||
|
return &objects.UserFunction{
|
||||||
|
Value: func(args ...objects.Object) (ret objects.Object, err error) {
|
||||||
|
if len(args) != 2 {
|
||||||
|
return nil, objects.ErrWrongNumArguments
|
||||||
|
}
|
||||||
|
|
||||||
|
f1, ok := objects.ToFloat64(args[0])
|
||||||
|
if !ok {
|
||||||
|
return nil, objects.ErrInvalidTypeConversion
|
||||||
|
}
|
||||||
|
|
||||||
|
f2, ok := objects.ToFloat64(args[1])
|
||||||
|
if !ok {
|
||||||
|
return nil, objects.ErrInvalidTypeConversion
|
||||||
|
}
|
||||||
|
|
||||||
|
return &objects.Float{Value: fn(f1, f2)}, nil
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// FuncAIFRF transform a function of 'func(int, float64) float64' signature
|
||||||
|
// into a user function object.
|
||||||
|
func FuncAIFRF(fn func(int, float64) float64) *objects.UserFunction {
|
||||||
|
return &objects.UserFunction{
|
||||||
|
Value: func(args ...objects.Object) (ret objects.Object, err error) {
|
||||||
|
if len(args) != 2 {
|
||||||
|
return nil, objects.ErrWrongNumArguments
|
||||||
|
}
|
||||||
|
|
||||||
|
i1, ok := objects.ToInt(args[0])
|
||||||
|
if !ok {
|
||||||
|
return nil, objects.ErrInvalidTypeConversion
|
||||||
|
}
|
||||||
|
|
||||||
|
f2, ok := objects.ToFloat64(args[1])
|
||||||
|
if !ok {
|
||||||
|
return nil, objects.ErrInvalidTypeConversion
|
||||||
|
}
|
||||||
|
|
||||||
|
return &objects.Float{Value: fn(i1, f2)}, nil
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// FuncAFIRF transform a function of 'func(float64, int) float64' signature
|
||||||
|
// into a user function object.
|
||||||
|
func FuncAFIRF(fn func(float64, int) float64) *objects.UserFunction {
|
||||||
|
return &objects.UserFunction{
|
||||||
|
Value: func(args ...objects.Object) (ret objects.Object, err error) {
|
||||||
|
if len(args) != 2 {
|
||||||
|
return nil, objects.ErrWrongNumArguments
|
||||||
|
}
|
||||||
|
|
||||||
|
f1, ok := objects.ToFloat64(args[0])
|
||||||
|
if !ok {
|
||||||
|
return nil, objects.ErrInvalidTypeConversion
|
||||||
|
}
|
||||||
|
|
||||||
|
i2, ok := objects.ToInt(args[1])
|
||||||
|
if !ok {
|
||||||
|
return nil, objects.ErrInvalidTypeConversion
|
||||||
|
}
|
||||||
|
|
||||||
|
return &objects.Float{Value: fn(f1, i2)}, nil
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// FuncAFIRB transform a function of 'func(float64, int) bool' signature
|
||||||
|
// into a user function object.
|
||||||
|
func FuncAFIRB(fn func(float64, int) bool) *objects.UserFunction {
|
||||||
|
return &objects.UserFunction{
|
||||||
|
Value: func(args ...objects.Object) (ret objects.Object, err error) {
|
||||||
|
if len(args) != 2 {
|
||||||
|
return nil, objects.ErrWrongNumArguments
|
||||||
|
}
|
||||||
|
|
||||||
|
f1, ok := objects.ToFloat64(args[0])
|
||||||
|
if !ok {
|
||||||
|
return nil, objects.ErrInvalidTypeConversion
|
||||||
|
}
|
||||||
|
|
||||||
|
i2, ok := objects.ToInt(args[1])
|
||||||
|
if !ok {
|
||||||
|
return nil, objects.ErrInvalidTypeConversion
|
||||||
|
}
|
||||||
|
|
||||||
|
return &objects.Bool{Value: fn(f1, i2)}, nil
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// FuncAFRB transform a function of 'func(float64) bool' signature
|
||||||
|
// into a user function object.
|
||||||
|
func FuncAFRB(fn func(float64) bool) *objects.UserFunction {
|
||||||
|
return &objects.UserFunction{
|
||||||
|
Value: func(args ...objects.Object) (ret objects.Object, err error) {
|
||||||
|
if len(args) != 1 {
|
||||||
|
return nil, objects.ErrWrongNumArguments
|
||||||
|
}
|
||||||
|
|
||||||
|
f1, ok := objects.ToFloat64(args[0])
|
||||||
|
if !ok {
|
||||||
|
return nil, objects.ErrInvalidTypeConversion
|
||||||
|
}
|
||||||
|
|
||||||
|
return &objects.Bool{Value: fn(f1)}, nil
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// FuncASRS transform a function of 'func(string) string' signature into a user function object.
|
||||||
|
// User function will return 'true' if underlying native function returns nil.
|
||||||
|
func FuncASRS(fn func(string) string) *objects.UserFunction {
|
||||||
|
return &objects.UserFunction{
|
||||||
|
Value: func(args ...objects.Object) (objects.Object, error) {
|
||||||
|
if len(args) != 1 {
|
||||||
|
return nil, objects.ErrWrongNumArguments
|
||||||
|
}
|
||||||
|
|
||||||
|
s1, ok := objects.ToString(args[0])
|
||||||
|
if !ok {
|
||||||
|
return nil, objects.ErrInvalidTypeConversion
|
||||||
|
}
|
||||||
|
|
||||||
|
return &objects.String{Value: fn(s1)}, nil
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// FuncASRSE transform a function of 'func(string) (string, error)' signature into a user function object.
|
||||||
|
// User function will return 'true' if underlying native function returns nil.
|
||||||
|
func FuncASRSE(fn func(string) (string, error)) *objects.UserFunction {
|
||||||
|
return &objects.UserFunction{
|
||||||
|
Value: func(args ...objects.Object) (objects.Object, error) {
|
||||||
|
if len(args) != 1 {
|
||||||
|
return nil, objects.ErrWrongNumArguments
|
||||||
|
}
|
||||||
|
|
||||||
|
s1, ok := objects.ToString(args[0])
|
||||||
|
if !ok {
|
||||||
|
return nil, objects.ErrInvalidTypeConversion
|
||||||
|
}
|
||||||
|
|
||||||
|
res, err := fn(s1)
|
||||||
|
if err != nil {
|
||||||
|
return wrapError(err), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return &objects.String{Value: res}, nil
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// FuncASRE transform a function of 'func(string) error' signature into a user function object.
|
||||||
|
// User function will return 'true' if underlying native function returns nil.
|
||||||
|
func FuncASRE(fn func(string) error) *objects.UserFunction {
|
||||||
|
return &objects.UserFunction{
|
||||||
|
Value: func(args ...objects.Object) (objects.Object, error) {
|
||||||
|
if len(args) != 1 {
|
||||||
|
return nil, objects.ErrWrongNumArguments
|
||||||
|
}
|
||||||
|
|
||||||
|
s1, ok := objects.ToString(args[0])
|
||||||
|
if !ok {
|
||||||
|
return nil, objects.ErrInvalidTypeConversion
|
||||||
|
}
|
||||||
|
|
||||||
|
return wrapError(fn(s1)), nil
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// FuncASSRE transform a function of 'func(string, string) error' signature into a user function object.
|
||||||
|
// User function will return 'true' if underlying native function returns nil.
|
||||||
|
func FuncASSRE(fn func(string, string) error) *objects.UserFunction {
|
||||||
|
return &objects.UserFunction{
|
||||||
|
Value: func(args ...objects.Object) (objects.Object, error) {
|
||||||
|
if len(args) != 2 {
|
||||||
|
return nil, objects.ErrWrongNumArguments
|
||||||
|
}
|
||||||
|
|
||||||
|
s1, ok := objects.ToString(args[0])
|
||||||
|
if !ok {
|
||||||
|
return nil, objects.ErrInvalidTypeConversion
|
||||||
|
}
|
||||||
|
|
||||||
|
s2, ok := objects.ToString(args[1])
|
||||||
|
if !ok {
|
||||||
|
return nil, objects.ErrInvalidTypeConversion
|
||||||
|
}
|
||||||
|
|
||||||
|
return wrapError(fn(s1, s2)), nil
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// FuncASI64RE transform a function of 'func(string, int64) error' signature
|
||||||
|
// into a user function object.
|
||||||
|
func FuncASI64RE(fn func(string, int64) error) *objects.UserFunction {
|
||||||
|
return &objects.UserFunction{
|
||||||
|
Value: func(args ...objects.Object) (ret objects.Object, err error) {
|
||||||
|
if len(args) != 2 {
|
||||||
|
return nil, objects.ErrWrongNumArguments
|
||||||
|
}
|
||||||
|
|
||||||
|
s1, ok := objects.ToString(args[0])
|
||||||
|
if !ok {
|
||||||
|
return nil, objects.ErrInvalidTypeConversion
|
||||||
|
}
|
||||||
|
|
||||||
|
i2, ok := objects.ToInt64(args[1])
|
||||||
|
if !ok {
|
||||||
|
return nil, objects.ErrInvalidTypeConversion
|
||||||
|
}
|
||||||
|
|
||||||
|
return wrapError(fn(s1, i2)), nil
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// FuncAIIRE transform a function of 'func(int, int) error' signature
|
||||||
|
// into a user function object.
|
||||||
|
func FuncAIIRE(fn func(int, int) error) *objects.UserFunction {
|
||||||
|
return &objects.UserFunction{
|
||||||
|
Value: func(args ...objects.Object) (ret objects.Object, err error) {
|
||||||
|
if len(args) != 2 {
|
||||||
|
return nil, objects.ErrWrongNumArguments
|
||||||
|
}
|
||||||
|
|
||||||
|
i1, ok := objects.ToInt(args[0])
|
||||||
|
if !ok {
|
||||||
|
return nil, objects.ErrInvalidTypeConversion
|
||||||
|
}
|
||||||
|
|
||||||
|
i2, ok := objects.ToInt(args[1])
|
||||||
|
if !ok {
|
||||||
|
return nil, objects.ErrInvalidTypeConversion
|
||||||
|
}
|
||||||
|
|
||||||
|
return wrapError(fn(i1, i2)), nil
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// FuncASIIRE transform a function of 'func(string, int, int) error' signature
|
||||||
|
// into a user function object.
|
||||||
|
func FuncASIIRE(fn func(string, int, int) error) *objects.UserFunction {
|
||||||
|
return &objects.UserFunction{
|
||||||
|
Value: func(args ...objects.Object) (ret objects.Object, err error) {
|
||||||
|
if len(args) != 3 {
|
||||||
|
return nil, objects.ErrWrongNumArguments
|
||||||
|
}
|
||||||
|
|
||||||
|
s1, ok := objects.ToString(args[0])
|
||||||
|
if !ok {
|
||||||
|
return nil, objects.ErrInvalidTypeConversion
|
||||||
|
}
|
||||||
|
|
||||||
|
i2, ok := objects.ToInt(args[1])
|
||||||
|
if !ok {
|
||||||
|
return nil, objects.ErrInvalidTypeConversion
|
||||||
|
}
|
||||||
|
|
||||||
|
i3, ok := objects.ToInt(args[2])
|
||||||
|
if !ok {
|
||||||
|
return nil, objects.ErrInvalidTypeConversion
|
||||||
|
}
|
||||||
|
|
||||||
|
return wrapError(fn(s1, i2, i3)), nil
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// FuncAYRIE transform a function of 'func([]byte) (int, error)' signature
|
||||||
|
// into a user function object.
|
||||||
|
func FuncAYRIE(fn func([]byte) (int, error)) *objects.UserFunction {
|
||||||
|
return &objects.UserFunction{
|
||||||
|
Value: func(args ...objects.Object) (ret objects.Object, err error) {
|
||||||
|
if len(args) != 1 {
|
||||||
|
return nil, objects.ErrWrongNumArguments
|
||||||
|
}
|
||||||
|
|
||||||
|
y1, ok := objects.ToByteSlice(args[0])
|
||||||
|
if !ok {
|
||||||
|
return nil, objects.ErrInvalidTypeConversion
|
||||||
|
}
|
||||||
|
|
||||||
|
res, err := fn(y1)
|
||||||
|
if err != nil {
|
||||||
|
return wrapError(err), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return &objects.Int{Value: int64(res)}, nil
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// FuncASRIE transform a function of 'func(string) (int, error)' signature
|
||||||
|
// into a user function object.
|
||||||
|
func FuncASRIE(fn func(string) (int, error)) *objects.UserFunction {
|
||||||
|
return &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
|
||||||
|
}
|
||||||
|
|
||||||
|
res, err := fn(s1)
|
||||||
|
if err != nil {
|
||||||
|
return wrapError(err), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return &objects.Int{Value: int64(res)}, nil
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// FuncAIRSsE transform a function of 'func(int) ([]string, error)' signature
|
||||||
|
// into a user function object.
|
||||||
|
func FuncAIRSsE(fn func(int) ([]string, error)) *objects.UserFunction {
|
||||||
|
return &objects.UserFunction{
|
||||||
|
Value: func(args ...objects.Object) (ret objects.Object, err error) {
|
||||||
|
if len(args) != 1 {
|
||||||
|
return nil, objects.ErrWrongNumArguments
|
||||||
|
}
|
||||||
|
|
||||||
|
i1, ok := objects.ToInt(args[0])
|
||||||
|
if !ok {
|
||||||
|
return nil, objects.ErrInvalidTypeConversion
|
||||||
|
}
|
||||||
|
|
||||||
|
res, err := fn(i1)
|
||||||
|
if err != nil {
|
||||||
|
return wrapError(err), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
arr := &objects.Array{}
|
||||||
|
for _, osArg := range res {
|
||||||
|
arr.Value = append(arr.Value, &objects.String{Value: osArg})
|
||||||
|
}
|
||||||
|
|
||||||
|
return arr, nil
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
360
compiler/stdlib/func_typedefs_test.go
Normal file
360
compiler/stdlib/func_typedefs_test.go
Normal file
|
@ -0,0 +1,360 @@
|
||||||
|
package stdlib_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/d5/tengo/assert"
|
||||||
|
"github.com/d5/tengo/compiler/stdlib"
|
||||||
|
"github.com/d5/tengo/objects"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestFuncAIR(t *testing.T) {
|
||||||
|
uf := stdlib.FuncAIR(func(int) {})
|
||||||
|
ret, err := uf.Call(&objects.Int{Value: 10})
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.Equal(t, &objects.Undefined{}, ret)
|
||||||
|
ret, err = uf.Call()
|
||||||
|
assert.Equal(t, objects.ErrWrongNumArguments, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestFuncAR(t *testing.T) {
|
||||||
|
uf := stdlib.FuncAR(func() {})
|
||||||
|
ret, err := uf.Call()
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.Equal(t, &objects.Undefined{}, ret)
|
||||||
|
ret, err = uf.Call(objects.TrueValue)
|
||||||
|
assert.Equal(t, objects.ErrWrongNumArguments, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestFuncARI(t *testing.T) {
|
||||||
|
uf := stdlib.FuncARI(func() int { return 10 })
|
||||||
|
ret, err := uf.Call()
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.Equal(t, &objects.Int{Value: 10}, ret)
|
||||||
|
ret, err = uf.Call(objects.TrueValue)
|
||||||
|
assert.Equal(t, objects.ErrWrongNumArguments, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestFuncARE(t *testing.T) {
|
||||||
|
uf := stdlib.FuncARE(func() error { return nil })
|
||||||
|
ret, err := uf.Call()
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.Equal(t, objects.TrueValue, ret)
|
||||||
|
uf = stdlib.FuncARE(func() error { return 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 TestFuncARIsE(t *testing.T) {
|
||||||
|
uf := stdlib.FuncARIsE(func() ([]int, error) { return []int{1, 2, 3}, nil })
|
||||||
|
ret, err := uf.Call()
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.Equal(t, array(&objects.Int{Value: 1}, &objects.Int{Value: 2}, &objects.Int{Value: 3}), ret)
|
||||||
|
uf = stdlib.FuncARIsE(func() ([]int, 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 TestFuncARS(t *testing.T) {
|
||||||
|
uf := stdlib.FuncARS(func() string { return "foo" })
|
||||||
|
ret, err := uf.Call()
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.Equal(t, &objects.String{Value: "foo"}, ret)
|
||||||
|
ret, err = uf.Call(objects.TrueValue)
|
||||||
|
assert.Equal(t, objects.ErrWrongNumArguments, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestFuncARSE(t *testing.T) {
|
||||||
|
uf := stdlib.FuncARSE(func() (string, error) { return "foo", nil })
|
||||||
|
ret, err := uf.Call()
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.Equal(t, &objects.String{Value: "foo"}, ret)
|
||||||
|
uf = stdlib.FuncARSE(func() (string, error) { return "", 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 TestFuncARSs(t *testing.T) {
|
||||||
|
uf := stdlib.FuncARSs(func() []string { return []string{"foo", "bar"} })
|
||||||
|
ret, err := uf.Call()
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.Equal(t, array(&objects.String{Value: "foo"}, &objects.String{Value: "bar"}), ret)
|
||||||
|
ret, err = uf.Call(objects.TrueValue)
|
||||||
|
assert.Equal(t, objects.ErrWrongNumArguments, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestFuncASRE(t *testing.T) {
|
||||||
|
uf := stdlib.FuncASRE(func(a string) error { return nil })
|
||||||
|
ret, err := uf.Call(&objects.String{Value: "foo"})
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.Equal(t, objects.TrueValue, ret)
|
||||||
|
uf = stdlib.FuncASRE(func(a string) error { return 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 TestFuncASRS(t *testing.T) {
|
||||||
|
uf := stdlib.FuncASRS(func(a string) string { return a })
|
||||||
|
ret, err := uf.Call(&objects.String{Value: "foo"})
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.Equal(t, &objects.String{Value: "foo"}, ret)
|
||||||
|
ret, err = uf.Call()
|
||||||
|
assert.Equal(t, objects.ErrWrongNumArguments, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestFuncASI64RE(t *testing.T) {
|
||||||
|
uf := stdlib.FuncASI64RE(func(a string, b int64) error { return nil })
|
||||||
|
ret, err := uf.Call(&objects.String{Value: "foo"}, &objects.Int{Value: 5})
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.Equal(t, objects.TrueValue, ret)
|
||||||
|
uf = stdlib.FuncASI64RE(func(a string, b int64) error { return errors.New("some error") })
|
||||||
|
ret, err = uf.Call(&objects.String{Value: "foo"}, &objects.Int{Value: 5})
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.Equal(t, &objects.Error{Value: &objects.String{Value: "some error"}}, ret)
|
||||||
|
ret, err = uf.Call()
|
||||||
|
assert.Equal(t, objects.ErrWrongNumArguments, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestFuncAIIRE(t *testing.T) {
|
||||||
|
uf := stdlib.FuncAIIRE(func(a, b int) error { return nil })
|
||||||
|
ret, err := uf.Call(&objects.Int{Value: 5}, &objects.Int{Value: 7})
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.Equal(t, objects.TrueValue, ret)
|
||||||
|
uf = stdlib.FuncAIIRE(func(a, b int) error { return errors.New("some error") })
|
||||||
|
ret, err = uf.Call(&objects.Int{Value: 5}, &objects.Int{Value: 7})
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.Equal(t, &objects.Error{Value: &objects.String{Value: "some error"}}, ret)
|
||||||
|
ret, err = uf.Call()
|
||||||
|
assert.Equal(t, objects.ErrWrongNumArguments, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestFuncASIIRE(t *testing.T) {
|
||||||
|
uf := stdlib.FuncASIIRE(func(a string, b, c int) error { return nil })
|
||||||
|
ret, err := uf.Call(&objects.String{Value: "foo"}, &objects.Int{Value: 5}, &objects.Int{Value: 7})
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.Equal(t, objects.TrueValue, ret)
|
||||||
|
uf = stdlib.FuncASIIRE(func(a string, b, c int) error { return errors.New("some error") })
|
||||||
|
ret, err = uf.Call(&objects.String{Value: "foo"}, &objects.Int{Value: 5}, &objects.Int{Value: 7})
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.Equal(t, &objects.Error{Value: &objects.String{Value: "some error"}}, ret)
|
||||||
|
ret, err = uf.Call()
|
||||||
|
assert.Equal(t, objects.ErrWrongNumArguments, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestFuncASRSE(t *testing.T) {
|
||||||
|
uf := stdlib.FuncASRSE(func(a string) (string, error) { return a, nil })
|
||||||
|
ret, err := uf.Call(&objects.String{Value: "foo"})
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.Equal(t, &objects.String{Value: "foo"}, ret)
|
||||||
|
uf = stdlib.FuncASRSE(func(a string) (string, error) { return a, 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 TestFuncASSRE(t *testing.T) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestFuncARF(t *testing.T) {
|
||||||
|
uf := stdlib.FuncARF(func() float64 {
|
||||||
|
return 10.0
|
||||||
|
})
|
||||||
|
ret, err := uf.Call()
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.Equal(t, &objects.Float{Value: 10.0}, ret)
|
||||||
|
ret, err = uf.Call(objects.TrueValue)
|
||||||
|
assert.Equal(t, objects.ErrWrongNumArguments, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestFuncAFRF(t *testing.T) {
|
||||||
|
uf := stdlib.FuncAFRF(func(a float64) float64 {
|
||||||
|
return a
|
||||||
|
})
|
||||||
|
ret, err := uf.Call(&objects.Float{Value: 10.0})
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.Equal(t, &objects.Float{Value: 10.0}, ret)
|
||||||
|
ret, err = uf.Call()
|
||||||
|
assert.Equal(t, objects.ErrWrongNumArguments, err)
|
||||||
|
ret, err = uf.Call(objects.TrueValue, objects.TrueValue)
|
||||||
|
assert.Equal(t, objects.ErrWrongNumArguments, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestFuncAIRF(t *testing.T) {
|
||||||
|
uf := stdlib.FuncAIRF(func(a int) float64 {
|
||||||
|
return float64(a)
|
||||||
|
})
|
||||||
|
ret, err := uf.Call(&objects.Int{Value: 10.0})
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.Equal(t, &objects.Float{Value: 10.0}, ret)
|
||||||
|
ret, err = uf.Call()
|
||||||
|
assert.Equal(t, objects.ErrWrongNumArguments, err)
|
||||||
|
ret, err = uf.Call(objects.TrueValue, objects.TrueValue)
|
||||||
|
assert.Equal(t, objects.ErrWrongNumArguments, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestFuncAFRI(t *testing.T) {
|
||||||
|
uf := stdlib.FuncAFRI(func(a float64) int {
|
||||||
|
return int(a)
|
||||||
|
})
|
||||||
|
ret, err := uf.Call(&objects.Float{Value: 10.5})
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.Equal(t, &objects.Int{Value: 10}, ret)
|
||||||
|
ret, err = uf.Call()
|
||||||
|
assert.Equal(t, objects.ErrWrongNumArguments, err)
|
||||||
|
ret, err = uf.Call(objects.TrueValue, objects.TrueValue)
|
||||||
|
assert.Equal(t, objects.ErrWrongNumArguments, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestFuncAFRB(t *testing.T) {
|
||||||
|
uf := stdlib.FuncAFRB(func(a float64) bool {
|
||||||
|
return a > 0.0
|
||||||
|
})
|
||||||
|
ret, err := uf.Call(&objects.Float{Value: 0.1})
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.Equal(t, &objects.Bool{Value: true}, ret)
|
||||||
|
ret, err = uf.Call()
|
||||||
|
assert.Equal(t, objects.ErrWrongNumArguments, err)
|
||||||
|
ret, err = uf.Call(objects.TrueValue, objects.TrueValue)
|
||||||
|
assert.Equal(t, objects.ErrWrongNumArguments, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestFuncAFFRF(t *testing.T) {
|
||||||
|
uf := stdlib.FuncAFFRF(func(a, b float64) float64 {
|
||||||
|
return a + b
|
||||||
|
})
|
||||||
|
ret, err := uf.Call(&objects.Float{Value: 10.0}, &objects.Float{Value: 20.0})
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.Equal(t, &objects.Float{Value: 30.0}, ret)
|
||||||
|
ret, err = uf.Call()
|
||||||
|
assert.Equal(t, objects.ErrWrongNumArguments, err)
|
||||||
|
ret, err = uf.Call(objects.TrueValue)
|
||||||
|
assert.Equal(t, objects.ErrWrongNumArguments, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestFuncAIFRF(t *testing.T) {
|
||||||
|
uf := stdlib.FuncAIFRF(func(a int, b float64) float64 {
|
||||||
|
return float64(a) + b
|
||||||
|
})
|
||||||
|
ret, err := uf.Call(&objects.Int{Value: 10}, &objects.Float{Value: 20.0})
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.Equal(t, &objects.Float{Value: 30.0}, ret)
|
||||||
|
ret, err = uf.Call()
|
||||||
|
assert.Equal(t, objects.ErrWrongNumArguments, err)
|
||||||
|
ret, err = uf.Call(objects.TrueValue)
|
||||||
|
assert.Equal(t, objects.ErrWrongNumArguments, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestFuncAFIRF(t *testing.T) {
|
||||||
|
uf := stdlib.FuncAFIRF(func(a float64, b int) float64 {
|
||||||
|
return a + float64(b)
|
||||||
|
})
|
||||||
|
ret, err := uf.Call(&objects.Float{Value: 10.0}, &objects.Int{Value: 20})
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.Equal(t, &objects.Float{Value: 30.0}, ret)
|
||||||
|
ret, err = uf.Call()
|
||||||
|
assert.Equal(t, objects.ErrWrongNumArguments, err)
|
||||||
|
ret, err = uf.Call(objects.TrueValue)
|
||||||
|
assert.Equal(t, objects.ErrWrongNumArguments, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestFuncAFIRB(t *testing.T) {
|
||||||
|
uf := stdlib.FuncAFIRB(func(a float64, b int) bool {
|
||||||
|
return a < float64(b)
|
||||||
|
})
|
||||||
|
ret, err := uf.Call(&objects.Float{Value: 10.0}, &objects.Int{Value: 20})
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.Equal(t, &objects.Bool{Value: true}, ret)
|
||||||
|
ret, err = uf.Call()
|
||||||
|
assert.Equal(t, objects.ErrWrongNumArguments, err)
|
||||||
|
ret, err = uf.Call(objects.TrueValue)
|
||||||
|
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}
|
||||||
|
}
|
|
@ -1,4 +1,4 @@
|
||||||
package stdmods
|
package stdlib
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"math"
|
"math"
|
199
compiler/stdlib/os.go
Normal file
199
compiler/stdlib/os.go
Normal file
|
@ -0,0 +1,199 @@
|
||||||
|
package stdlib
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
|
||||||
|
"github.com/d5/tengo/objects"
|
||||||
|
)
|
||||||
|
|
||||||
|
var osModule = map[string]objects.Object{
|
||||||
|
"o_rdonly": &objects.Int{Value: int64(os.O_RDONLY)},
|
||||||
|
"o_wronly": &objects.Int{Value: int64(os.O_WRONLY)},
|
||||||
|
"o_rdwr": &objects.Int{Value: int64(os.O_RDWR)},
|
||||||
|
"o_append": &objects.Int{Value: int64(os.O_APPEND)},
|
||||||
|
"o_create": &objects.Int{Value: int64(os.O_CREATE)},
|
||||||
|
"o_excl": &objects.Int{Value: int64(os.O_EXCL)},
|
||||||
|
"o_sync": &objects.Int{Value: int64(os.O_SYNC)},
|
||||||
|
"o_trunc": &objects.Int{Value: int64(os.O_TRUNC)},
|
||||||
|
"mode_dir": &objects.Int{Value: int64(os.ModeDir)},
|
||||||
|
"mode_append": &objects.Int{Value: int64(os.ModeAppend)},
|
||||||
|
"mode_exclusive": &objects.Int{Value: int64(os.ModeExclusive)},
|
||||||
|
"mode_temporary": &objects.Int{Value: int64(os.ModeTemporary)},
|
||||||
|
"mode_symlink": &objects.Int{Value: int64(os.ModeSymlink)},
|
||||||
|
"mode_device": &objects.Int{Value: int64(os.ModeDevice)},
|
||||||
|
"mode_named_pipe": &objects.Int{Value: int64(os.ModeNamedPipe)},
|
||||||
|
"mode_socket": &objects.Int{Value: int64(os.ModeSocket)},
|
||||||
|
"mode_setuid": &objects.Int{Value: int64(os.ModeSetuid)},
|
||||||
|
"mode_setgui": &objects.Int{Value: int64(os.ModeSetgid)},
|
||||||
|
"mode_char_device": &objects.Int{Value: int64(os.ModeCharDevice)},
|
||||||
|
"mode_sticky": &objects.Int{Value: int64(os.ModeSticky)},
|
||||||
|
"mode_irregular": &objects.Int{Value: int64(os.ModeIrregular)},
|
||||||
|
"mode_type": &objects.Int{Value: int64(os.ModeType)},
|
||||||
|
"mode_perm": &objects.Int{Value: int64(os.ModePerm)},
|
||||||
|
"path_separator": &objects.Char{Value: os.PathSeparator},
|
||||||
|
"path_list_separator": &objects.Char{Value: os.PathListSeparator},
|
||||||
|
"dev_null": &objects.String{Value: os.DevNull},
|
||||||
|
"seek_set": &objects.Int{Value: int64(os.SEEK_SET)},
|
||||||
|
"seek_cur": &objects.Int{Value: int64(os.SEEK_CUR)},
|
||||||
|
"seek_end": &objects.Int{Value: int64(os.SEEK_END)},
|
||||||
|
// 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),
|
||||||
|
// executable() => string/error
|
||||||
|
"executable": &objects.UserFunction{Value: osExecutable},
|
||||||
|
// 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),
|
||||||
|
// user_cache_dir() => string/error
|
||||||
|
"user_cache_dir": FuncARSE(os.UserCacheDir),
|
||||||
|
// 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},
|
||||||
|
|
||||||
|
// TODO: implemented more functions
|
||||||
|
//"stdin": nil,
|
||||||
|
//"stdout": nil,
|
||||||
|
//"stderr": nil,
|
||||||
|
//"chtimes": nil,
|
||||||
|
//"expand": nil,
|
||||||
|
//"is_exists": nil,
|
||||||
|
//"is_not_exist": nil,
|
||||||
|
//"is_path_separator": nil,
|
||||||
|
//"is_permission": nil,
|
||||||
|
//"is_timeout": nil,
|
||||||
|
//"new_syscall_error": nil,
|
||||||
|
//"pipe": nil,
|
||||||
|
//"same_file": nil,
|
||||||
|
}
|
||||||
|
|
||||||
|
func osArgs(args ...objects.Object) (objects.Object, error) {
|
||||||
|
if len(args) != 0 {
|
||||||
|
return nil, objects.ErrWrongNumArguments
|
||||||
|
}
|
||||||
|
|
||||||
|
arr := &objects.Array{}
|
||||||
|
for _, osArg := range os.Args {
|
||||||
|
arr.Value = append(arr.Value, &objects.String{Value: osArg})
|
||||||
|
}
|
||||||
|
|
||||||
|
return arr, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func osFuncASFmRE(fn func(string, os.FileMode) error) *objects.UserFunction {
|
||||||
|
return &objects.UserFunction{
|
||||||
|
Value: func(args ...objects.Object) (objects.Object, error) {
|
||||||
|
if len(args) != 2 {
|
||||||
|
return nil, objects.ErrWrongNumArguments
|
||||||
|
}
|
||||||
|
|
||||||
|
s1, ok := objects.ToString(args[0])
|
||||||
|
if !ok {
|
||||||
|
return nil, objects.ErrInvalidTypeConversion
|
||||||
|
}
|
||||||
|
i2, ok := objects.ToInt64(args[1])
|
||||||
|
if !ok {
|
||||||
|
return nil, objects.ErrInvalidTypeConversion
|
||||||
|
}
|
||||||
|
|
||||||
|
return wrapError(fn(s1, os.FileMode(i2))), nil
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|
||||||
|
s1, ok := objects.ToString(args[0])
|
||||||
|
if !ok {
|
||||||
|
return nil, objects.ErrInvalidTypeConversion
|
||||||
|
}
|
||||||
|
|
||||||
|
res, ok := os.LookupEnv(s1)
|
||||||
|
if !ok {
|
||||||
|
return objects.FalseValue, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return &objects.String{Value: res}, nil
|
||||||
|
}
|
144
compiler/stdlib/os_file.go
Normal file
144
compiler/stdlib/os_file.go
Normal file
|
@ -0,0 +1,144 @@
|
||||||
|
package stdlib
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
|
||||||
|
"github.com/d5/tengo/objects"
|
||||||
|
)
|
||||||
|
|
||||||
|
func osFileImmutableMap(file *os.File) *objects.ImmutableMap {
|
||||||
|
return &objects.ImmutableMap{
|
||||||
|
Value: map[string]objects.Object{
|
||||||
|
// chdir() => true/error
|
||||||
|
"chdir": FuncARE(file.Chdir),
|
||||||
|
// chown(uid int, gid int) => true/error
|
||||||
|
"chown": FuncAIIRE(file.Chown),
|
||||||
|
// close() => error
|
||||||
|
"close": FuncARE(file.Close),
|
||||||
|
// name() => string
|
||||||
|
"name": FuncARS(file.Name),
|
||||||
|
// readdirnames(n int) => array(string)/error
|
||||||
|
"readdirnames": FuncAIRSsE(file.Readdirnames),
|
||||||
|
// sync() => error
|
||||||
|
"sync": FuncARE(file.Sync),
|
||||||
|
// write(bytes) => int/error
|
||||||
|
"write": FuncAYRIE(file.Write),
|
||||||
|
// write(string) => int/error
|
||||||
|
"write_string": FuncASRIE(file.WriteString),
|
||||||
|
// read(bytes) => int/error
|
||||||
|
"read": FuncAYRIE(file.Read),
|
||||||
|
// chmod(mode int) => error
|
||||||
|
"chmod": &objects.UserFunction{
|
||||||
|
Value: func(args ...objects.Object) (ret objects.Object, err error) {
|
||||||
|
if len(args) != 1 {
|
||||||
|
return nil, objects.ErrWrongNumArguments
|
||||||
|
}
|
||||||
|
|
||||||
|
i1, ok := objects.ToInt64(args[0])
|
||||||
|
if !ok {
|
||||||
|
return nil, objects.ErrInvalidTypeConversion
|
||||||
|
}
|
||||||
|
|
||||||
|
return wrapError(file.Chmod(os.FileMode(i1))), nil
|
||||||
|
},
|
||||||
|
},
|
||||||
|
// seek(offset int, whence int) => int/error
|
||||||
|
"seek": &objects.UserFunction{
|
||||||
|
Value: func(args ...objects.Object) (ret objects.Object, err error) {
|
||||||
|
if len(args) != 2 {
|
||||||
|
return nil, objects.ErrWrongNumArguments
|
||||||
|
}
|
||||||
|
|
||||||
|
i1, ok := objects.ToInt64(args[0])
|
||||||
|
if !ok {
|
||||||
|
return nil, objects.ErrInvalidTypeConversion
|
||||||
|
}
|
||||||
|
i2, ok := objects.ToInt(args[1])
|
||||||
|
if !ok {
|
||||||
|
return nil, objects.ErrInvalidTypeConversion
|
||||||
|
}
|
||||||
|
|
||||||
|
res, err := file.Seek(i1, i2)
|
||||||
|
if err != nil {
|
||||||
|
return wrapError(err), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
||||||
|
}
|
129
compiler/stdlib/os_process.go
Normal file
129
compiler/stdlib/os_process.go
Normal file
|
@ -0,0 +1,129 @@
|
||||||
|
package stdlib
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
"syscall"
|
||||||
|
|
||||||
|
"github.com/d5/tengo/objects"
|
||||||
|
)
|
||||||
|
|
||||||
|
func osProcessStateImmutableMap(state *os.ProcessState) *objects.ImmutableMap {
|
||||||
|
return &objects.ImmutableMap{
|
||||||
|
Value: map[string]objects.Object{
|
||||||
|
"exited": FuncARB(state.Exited),
|
||||||
|
"pid": FuncARI(state.Pid),
|
||||||
|
"string": FuncARS(state.String),
|
||||||
|
"success": FuncARB(state.Success),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func osProcessImmutableMap(proc *os.Process) *objects.ImmutableMap {
|
||||||
|
return &objects.ImmutableMap{
|
||||||
|
Value: map[string]objects.Object{
|
||||||
|
"kill": FuncARE(proc.Kill),
|
||||||
|
"release": FuncARE(proc.Release),
|
||||||
|
"signal": &objects.UserFunction{
|
||||||
|
Value: func(args ...objects.Object) (ret objects.Object, err error) {
|
||||||
|
if len(args) != 1 {
|
||||||
|
return nil, objects.ErrWrongNumArguments
|
||||||
|
}
|
||||||
|
|
||||||
|
i1, ok := objects.ToInt64(args[0])
|
||||||
|
if !ok {
|
||||||
|
return nil, objects.ErrInvalidTypeConversion
|
||||||
|
}
|
||||||
|
|
||||||
|
return wrapError(proc.Signal(syscall.Signal(i1))), nil
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"wait": &objects.UserFunction{
|
||||||
|
Value: func(args ...objects.Object) (ret objects.Object, err error) {
|
||||||
|
if len(args) != 0 {
|
||||||
|
return nil, objects.ErrWrongNumArguments
|
||||||
|
}
|
||||||
|
|
||||||
|
state, err := proc.Wait()
|
||||||
|
if err != nil {
|
||||||
|
return wrapError(err), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return osProcessStateImmutableMap(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
|
||||||
|
}
|
10
compiler/stdlib/stdlib.go
Normal file
10
compiler/stdlib/stdlib.go
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
package stdlib
|
||||||
|
|
||||||
|
import "github.com/d5/tengo/objects"
|
||||||
|
|
||||||
|
// Modules contain the standard modules.
|
||||||
|
var Modules = map[string]*objects.ImmutableMap{
|
||||||
|
"math": {Value: mathModule},
|
||||||
|
"os": {Value: osModule},
|
||||||
|
"exec": {Value: execModule},
|
||||||
|
}
|
|
@ -1,243 +0,0 @@
|
||||||
package stdmods
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
|
|
||||||
"github.com/d5/tengo/objects"
|
|
||||||
)
|
|
||||||
|
|
||||||
// FuncAFRF transform a function of 'func(float64) float64' signature
|
|
||||||
// into a user function object.
|
|
||||||
func FuncAFRF(fn func(float64) float64) *objects.UserFunction {
|
|
||||||
return &objects.UserFunction{
|
|
||||||
Value: func(args ...objects.Object) (ret objects.Object, err error) {
|
|
||||||
if len(args) != 1 {
|
|
||||||
return nil, objects.ErrWrongNumArguments
|
|
||||||
}
|
|
||||||
|
|
||||||
switch arg := args[0].(type) {
|
|
||||||
case *objects.Int:
|
|
||||||
return &objects.Float{Value: fn(float64(arg.Value))}, nil
|
|
||||||
case *objects.Float:
|
|
||||||
return &objects.Float{Value: fn(arg.Value)}, nil
|
|
||||||
default:
|
|
||||||
return nil, fmt.Errorf("invalid argument type: %s", arg.TypeName())
|
|
||||||
}
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// FuncARF transform a function of 'func() float64' signature
|
|
||||||
// into a user function object.
|
|
||||||
func FuncARF(fn func() float64) *objects.UserFunction {
|
|
||||||
return &objects.UserFunction{
|
|
||||||
Value: func(args ...objects.Object) (ret objects.Object, err error) {
|
|
||||||
if len(args) != 0 {
|
|
||||||
return nil, objects.ErrWrongNumArguments
|
|
||||||
}
|
|
||||||
|
|
||||||
return &objects.Float{Value: fn()}, nil
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// FuncAIRF transform a function of 'func(int) float64' signature
|
|
||||||
// into a user function object.
|
|
||||||
func FuncAIRF(fn func(int) float64) *objects.UserFunction {
|
|
||||||
return &objects.UserFunction{
|
|
||||||
Value: func(args ...objects.Object) (ret objects.Object, err error) {
|
|
||||||
if len(args) != 1 {
|
|
||||||
return nil, objects.ErrWrongNumArguments
|
|
||||||
}
|
|
||||||
|
|
||||||
switch arg := args[0].(type) {
|
|
||||||
case *objects.Int:
|
|
||||||
return &objects.Float{Value: fn(int(arg.Value))}, nil
|
|
||||||
case *objects.Float:
|
|
||||||
return &objects.Float{Value: fn(int(arg.Value))}, nil
|
|
||||||
default:
|
|
||||||
return nil, fmt.Errorf("invalid argument type: %s", arg.TypeName())
|
|
||||||
}
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// FuncAFRI transform a function of 'func(float64) int' signature
|
|
||||||
// into a user function object.
|
|
||||||
func FuncAFRI(fn func(float64) int) *objects.UserFunction {
|
|
||||||
return &objects.UserFunction{
|
|
||||||
Value: func(args ...objects.Object) (ret objects.Object, err error) {
|
|
||||||
if len(args) != 1 {
|
|
||||||
return nil, objects.ErrWrongNumArguments
|
|
||||||
}
|
|
||||||
|
|
||||||
switch arg := args[0].(type) {
|
|
||||||
case *objects.Int:
|
|
||||||
return &objects.Int{Value: int64(fn(float64(arg.Value)))}, nil
|
|
||||||
case *objects.Float:
|
|
||||||
return &objects.Int{Value: int64(fn(arg.Value))}, nil
|
|
||||||
default:
|
|
||||||
return nil, fmt.Errorf("invalid argument type: %s", arg.TypeName())
|
|
||||||
}
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// FuncAFFRF transform a function of 'func(float64, float64) float64' signature
|
|
||||||
// into a user function object.
|
|
||||||
func FuncAFFRF(fn func(float64, float64) float64) *objects.UserFunction {
|
|
||||||
return &objects.UserFunction{
|
|
||||||
Value: func(args ...objects.Object) (ret objects.Object, err error) {
|
|
||||||
if len(args) != 2 {
|
|
||||||
return nil, objects.ErrWrongNumArguments
|
|
||||||
}
|
|
||||||
|
|
||||||
var arg0, arg1 float64
|
|
||||||
|
|
||||||
switch arg := args[0].(type) {
|
|
||||||
case *objects.Int:
|
|
||||||
arg0 = float64(arg.Value)
|
|
||||||
case *objects.Float:
|
|
||||||
arg0 = arg.Value
|
|
||||||
default:
|
|
||||||
return nil, fmt.Errorf("invalid argument type: %s", arg.TypeName())
|
|
||||||
}
|
|
||||||
switch arg := args[1].(type) {
|
|
||||||
case *objects.Int:
|
|
||||||
arg1 = float64(arg.Value)
|
|
||||||
case *objects.Float:
|
|
||||||
arg1 = arg.Value
|
|
||||||
default:
|
|
||||||
return nil, fmt.Errorf("invalid argument type: %s", arg.TypeName())
|
|
||||||
}
|
|
||||||
|
|
||||||
return &objects.Float{Value: fn(arg0, arg1)}, nil
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// FuncAIFRF transform a function of 'func(int, float64) float64' signature
|
|
||||||
// into a user function object.
|
|
||||||
func FuncAIFRF(fn func(int, float64) float64) *objects.UserFunction {
|
|
||||||
return &objects.UserFunction{
|
|
||||||
Value: func(args ...objects.Object) (ret objects.Object, err error) {
|
|
||||||
if len(args) != 2 {
|
|
||||||
return nil, objects.ErrWrongNumArguments
|
|
||||||
}
|
|
||||||
|
|
||||||
var arg0 int
|
|
||||||
var arg1 float64
|
|
||||||
|
|
||||||
switch arg := args[0].(type) {
|
|
||||||
case *objects.Int:
|
|
||||||
arg0 = int(arg.Value)
|
|
||||||
case *objects.Float:
|
|
||||||
arg0 = int(arg.Value)
|
|
||||||
default:
|
|
||||||
return nil, fmt.Errorf("invalid argument type: %s", arg.TypeName())
|
|
||||||
}
|
|
||||||
switch arg := args[1].(type) {
|
|
||||||
case *objects.Int:
|
|
||||||
arg1 = float64(arg.Value)
|
|
||||||
case *objects.Float:
|
|
||||||
arg1 = arg.Value
|
|
||||||
default:
|
|
||||||
return nil, fmt.Errorf("invalid argument type: %s", arg.TypeName())
|
|
||||||
}
|
|
||||||
|
|
||||||
return &objects.Float{Value: fn(arg0, arg1)}, nil
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// FuncAFIRF transform a function of 'func(float64, int) float64' signature
|
|
||||||
// into a user function object.
|
|
||||||
func FuncAFIRF(fn func(float64, int) float64) *objects.UserFunction {
|
|
||||||
return &objects.UserFunction{
|
|
||||||
Value: func(args ...objects.Object) (ret objects.Object, err error) {
|
|
||||||
if len(args) != 2 {
|
|
||||||
return nil, objects.ErrWrongNumArguments
|
|
||||||
}
|
|
||||||
|
|
||||||
var arg0 float64
|
|
||||||
var arg1 int
|
|
||||||
|
|
||||||
switch arg := args[0].(type) {
|
|
||||||
case *objects.Int:
|
|
||||||
arg0 = float64(arg.Value)
|
|
||||||
case *objects.Float:
|
|
||||||
arg0 = float64(arg.Value)
|
|
||||||
default:
|
|
||||||
return nil, fmt.Errorf("invalid argument type: %s", arg.TypeName())
|
|
||||||
}
|
|
||||||
switch arg := args[1].(type) {
|
|
||||||
case *objects.Int:
|
|
||||||
arg1 = int(arg.Value)
|
|
||||||
case *objects.Float:
|
|
||||||
arg1 = int(arg.Value)
|
|
||||||
default:
|
|
||||||
return nil, fmt.Errorf("invalid argument type: %s", arg.TypeName())
|
|
||||||
}
|
|
||||||
|
|
||||||
return &objects.Float{Value: fn(arg0, arg1)}, nil
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// FuncAFIRB transform a function of 'func(float64, int) bool' signature
|
|
||||||
// into a user function object.
|
|
||||||
func FuncAFIRB(fn func(float64, int) bool) *objects.UserFunction {
|
|
||||||
return &objects.UserFunction{
|
|
||||||
Value: func(args ...objects.Object) (ret objects.Object, err error) {
|
|
||||||
if len(args) != 2 {
|
|
||||||
return nil, objects.ErrWrongNumArguments
|
|
||||||
}
|
|
||||||
|
|
||||||
var arg0 float64
|
|
||||||
var arg1 int
|
|
||||||
|
|
||||||
switch arg := args[0].(type) {
|
|
||||||
case *objects.Int:
|
|
||||||
arg0 = float64(arg.Value)
|
|
||||||
case *objects.Float:
|
|
||||||
arg0 = arg.Value
|
|
||||||
default:
|
|
||||||
return nil, fmt.Errorf("invalid argument type: %s", arg.TypeName())
|
|
||||||
}
|
|
||||||
switch arg := args[1].(type) {
|
|
||||||
case *objects.Int:
|
|
||||||
arg1 = int(arg.Value)
|
|
||||||
case *objects.Float:
|
|
||||||
arg1 = int(arg.Value)
|
|
||||||
default:
|
|
||||||
return nil, fmt.Errorf("invalid argument type: %s", arg.TypeName())
|
|
||||||
}
|
|
||||||
|
|
||||||
return &objects.Bool{Value: fn(arg0, arg1)}, nil
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// FuncAFRB transform a function of 'func(float64) bool' signature
|
|
||||||
// into a user function object.
|
|
||||||
func FuncAFRB(fn func(float64) bool) *objects.UserFunction {
|
|
||||||
return &objects.UserFunction{
|
|
||||||
Value: func(args ...objects.Object) (ret objects.Object, err error) {
|
|
||||||
if len(args) != 1 {
|
|
||||||
return nil, objects.ErrWrongNumArguments
|
|
||||||
}
|
|
||||||
|
|
||||||
var arg0 float64
|
|
||||||
switch arg := args[0].(type) {
|
|
||||||
case *objects.Int:
|
|
||||||
arg0 = float64(arg.Value)
|
|
||||||
case *objects.Float:
|
|
||||||
arg0 = arg.Value
|
|
||||||
default:
|
|
||||||
return nil, fmt.Errorf("invalid argument type: %s", arg.TypeName())
|
|
||||||
}
|
|
||||||
|
|
||||||
return &objects.Bool{Value: fn(arg0)}, nil
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,124 +0,0 @@
|
||||||
package stdmods_test
|
|
||||||
|
|
||||||
import (
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/d5/tengo/assert"
|
|
||||||
"github.com/d5/tengo/compiler/stdmods"
|
|
||||||
"github.com/d5/tengo/objects"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestFuncARF(t *testing.T) {
|
|
||||||
uf := stdmods.FuncARF(func() float64 {
|
|
||||||
return 10.0
|
|
||||||
})
|
|
||||||
ret, err := uf.Call()
|
|
||||||
assert.NoError(t, err)
|
|
||||||
assert.Equal(t, &objects.Float{Value: 10.0}, ret)
|
|
||||||
ret, err = uf.Call(objects.TrueValue)
|
|
||||||
assert.Error(t, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestFuncAFRF(t *testing.T) {
|
|
||||||
uf := stdmods.FuncAFRF(func(a float64) float64 {
|
|
||||||
return a
|
|
||||||
})
|
|
||||||
ret, err := uf.Call(&objects.Float{Value: 10.0})
|
|
||||||
assert.NoError(t, err)
|
|
||||||
assert.Equal(t, &objects.Float{Value: 10.0}, ret)
|
|
||||||
ret, err = uf.Call()
|
|
||||||
assert.Error(t, err)
|
|
||||||
ret, err = uf.Call(objects.TrueValue, objects.TrueValue)
|
|
||||||
assert.Error(t, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestFuncAIRF(t *testing.T) {
|
|
||||||
uf := stdmods.FuncAIRF(func(a int) float64 {
|
|
||||||
return float64(a)
|
|
||||||
})
|
|
||||||
ret, err := uf.Call(&objects.Int{Value: 10.0})
|
|
||||||
assert.NoError(t, err)
|
|
||||||
assert.Equal(t, &objects.Float{Value: 10.0}, ret)
|
|
||||||
ret, err = uf.Call()
|
|
||||||
assert.Error(t, err)
|
|
||||||
ret, err = uf.Call(objects.TrueValue, objects.TrueValue)
|
|
||||||
assert.Error(t, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestFuncAFRI(t *testing.T) {
|
|
||||||
uf := stdmods.FuncAFRI(func(a float64) int {
|
|
||||||
return int(a)
|
|
||||||
})
|
|
||||||
ret, err := uf.Call(&objects.Float{Value: 10.5})
|
|
||||||
assert.NoError(t, err)
|
|
||||||
assert.Equal(t, &objects.Int{Value: 10}, ret)
|
|
||||||
ret, err = uf.Call()
|
|
||||||
assert.Error(t, err)
|
|
||||||
ret, err = uf.Call(objects.TrueValue, objects.TrueValue)
|
|
||||||
assert.Error(t, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestFuncAFRB(t *testing.T) {
|
|
||||||
uf := stdmods.FuncAFRB(func(a float64) bool {
|
|
||||||
return a > 0.0
|
|
||||||
})
|
|
||||||
ret, err := uf.Call(&objects.Float{Value: 0.1})
|
|
||||||
assert.NoError(t, err)
|
|
||||||
assert.Equal(t, &objects.Bool{Value: true}, ret)
|
|
||||||
ret, err = uf.Call()
|
|
||||||
assert.Error(t, err)
|
|
||||||
ret, err = uf.Call(objects.TrueValue, objects.TrueValue)
|
|
||||||
assert.Error(t, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestFuncAFFRF(t *testing.T) {
|
|
||||||
uf := stdmods.FuncAFFRF(func(a, b float64) float64 {
|
|
||||||
return a + b
|
|
||||||
})
|
|
||||||
ret, err := uf.Call(&objects.Float{Value: 10.0}, &objects.Float{Value: 20.0})
|
|
||||||
assert.NoError(t, err)
|
|
||||||
assert.Equal(t, &objects.Float{Value: 30.0}, ret)
|
|
||||||
ret, err = uf.Call()
|
|
||||||
assert.Error(t, err)
|
|
||||||
ret, err = uf.Call(objects.TrueValue)
|
|
||||||
assert.Error(t, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestFuncAIFRF(t *testing.T) {
|
|
||||||
uf := stdmods.FuncAIFRF(func(a int, b float64) float64 {
|
|
||||||
return float64(a) + b
|
|
||||||
})
|
|
||||||
ret, err := uf.Call(&objects.Int{Value: 10}, &objects.Float{Value: 20.0})
|
|
||||||
assert.NoError(t, err)
|
|
||||||
assert.Equal(t, &objects.Float{Value: 30.0}, ret)
|
|
||||||
ret, err = uf.Call()
|
|
||||||
assert.Error(t, err)
|
|
||||||
ret, err = uf.Call(objects.TrueValue)
|
|
||||||
assert.Error(t, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestFuncAFIRF(t *testing.T) {
|
|
||||||
uf := stdmods.FuncAFIRF(func(a float64, b int) float64 {
|
|
||||||
return a + float64(b)
|
|
||||||
})
|
|
||||||
ret, err := uf.Call(&objects.Float{Value: 10.0}, &objects.Int{Value: 20})
|
|
||||||
assert.NoError(t, err)
|
|
||||||
assert.Equal(t, &objects.Float{Value: 30.0}, ret)
|
|
||||||
ret, err = uf.Call()
|
|
||||||
assert.Error(t, err)
|
|
||||||
ret, err = uf.Call(objects.TrueValue)
|
|
||||||
assert.Error(t, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestFuncAFIRB(t *testing.T) {
|
|
||||||
uf := stdmods.FuncAFIRB(func(a float64, b int) bool {
|
|
||||||
return a < float64(b)
|
|
||||||
})
|
|
||||||
ret, err := uf.Call(&objects.Float{Value: 10.0}, &objects.Int{Value: 20})
|
|
||||||
assert.NoError(t, err)
|
|
||||||
assert.Equal(t, &objects.Bool{Value: true}, ret)
|
|
||||||
ret, err = uf.Call()
|
|
||||||
assert.Error(t, err)
|
|
||||||
ret, err = uf.Call(objects.TrueValue)
|
|
||||||
assert.Error(t, err)
|
|
||||||
}
|
|
|
@ -1,8 +0,0 @@
|
||||||
package stdmods
|
|
||||||
|
|
||||||
import "github.com/d5/tengo/objects"
|
|
||||||
|
|
||||||
// Modules contain the standard modules.
|
|
||||||
var Modules = map[string]*objects.ModuleMap{
|
|
||||||
"math": {Value: mathModule},
|
|
||||||
}
|
|
|
@ -30,7 +30,7 @@ func (o *Bool) BinaryOp(op token.Token, rhs Object) (Object, error) {
|
||||||
|
|
||||||
// Copy returns a copy of the type.
|
// Copy returns a copy of the type.
|
||||||
func (o *Bool) Copy() Object {
|
func (o *Bool) Copy() Object {
|
||||||
v := Bool(*o)
|
v := Bool{Value: o.Value}
|
||||||
return &v
|
return &v
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,22 +1,20 @@
|
||||||
package objects
|
package objects
|
||||||
|
|
||||||
import (
|
|
||||||
"strconv"
|
|
||||||
)
|
|
||||||
|
|
||||||
func builtinString(args ...Object) (Object, error) {
|
func builtinString(args ...Object) (Object, error) {
|
||||||
if len(args) != 1 {
|
if len(args) != 1 {
|
||||||
return nil, ErrWrongNumArguments
|
return nil, ErrWrongNumArguments
|
||||||
}
|
}
|
||||||
|
|
||||||
switch arg := args[0].(type) {
|
if _, ok := args[0].(*String); ok {
|
||||||
case *String:
|
return args[0], nil
|
||||||
return arg, nil
|
|
||||||
case *Undefined:
|
|
||||||
return UndefinedValue, nil
|
|
||||||
default:
|
|
||||||
return &String{Value: arg.String()}, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
v, ok := ToString(args[0])
|
||||||
|
if ok {
|
||||||
|
return &String{Value: v}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return UndefinedValue, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func builtinInt(args ...Object) (Object, error) {
|
func builtinInt(args ...Object) (Object, error) {
|
||||||
|
@ -24,23 +22,13 @@ func builtinInt(args ...Object) (Object, error) {
|
||||||
return nil, ErrWrongNumArguments
|
return nil, ErrWrongNumArguments
|
||||||
}
|
}
|
||||||
|
|
||||||
switch arg := args[0].(type) {
|
if _, ok := args[0].(*Int); ok {
|
||||||
case *Int:
|
return args[0], nil
|
||||||
return arg, nil
|
}
|
||||||
case *Float:
|
|
||||||
return &Int{Value: int64(arg.Value)}, nil
|
v, ok := ToInt64(args[0])
|
||||||
case *Char:
|
if ok {
|
||||||
return &Int{Value: int64(arg.Value)}, nil
|
return &Int{Value: v}, nil
|
||||||
case *Bool:
|
|
||||||
if arg.Value {
|
|
||||||
return &Int{Value: 1}, nil
|
|
||||||
}
|
|
||||||
return &Int{Value: 0}, nil
|
|
||||||
case *String:
|
|
||||||
n, err := strconv.ParseInt(arg.Value, 10, 64)
|
|
||||||
if err == nil {
|
|
||||||
return &Int{Value: n}, nil
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return UndefinedValue, nil
|
return UndefinedValue, nil
|
||||||
|
@ -51,16 +39,13 @@ func builtinFloat(args ...Object) (Object, error) {
|
||||||
return nil, ErrWrongNumArguments
|
return nil, ErrWrongNumArguments
|
||||||
}
|
}
|
||||||
|
|
||||||
switch arg := args[0].(type) {
|
if _, ok := args[0].(*Float); ok {
|
||||||
case *Float:
|
return args[0], nil
|
||||||
return arg, nil
|
}
|
||||||
case *Int:
|
|
||||||
return &Float{Value: float64(arg.Value)}, nil
|
v, ok := ToFloat64(args[0])
|
||||||
case *String:
|
if ok {
|
||||||
f, err := strconv.ParseFloat(arg.Value, 64)
|
return &Float{Value: v}, nil
|
||||||
if err == nil {
|
|
||||||
return &Float{Value: f}, nil
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return UndefinedValue, nil
|
return UndefinedValue, nil
|
||||||
|
@ -71,12 +56,16 @@ func builtinBool(args ...Object) (Object, error) {
|
||||||
return nil, ErrWrongNumArguments
|
return nil, ErrWrongNumArguments
|
||||||
}
|
}
|
||||||
|
|
||||||
switch arg := args[0].(type) {
|
if _, ok := args[0].(*Bool); ok {
|
||||||
case *Bool:
|
return args[0], nil
|
||||||
return arg, nil
|
|
||||||
default:
|
|
||||||
return &Bool{Value: !arg.IsFalsy()}, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
v, ok := ToBool(args[0])
|
||||||
|
if ok {
|
||||||
|
return &Bool{Value: v}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return UndefinedValue, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func builtinChar(args ...Object) (Object, error) {
|
func builtinChar(args ...Object) (Object, error) {
|
||||||
|
@ -84,19 +73,31 @@ func builtinChar(args ...Object) (Object, error) {
|
||||||
return nil, ErrWrongNumArguments
|
return nil, ErrWrongNumArguments
|
||||||
}
|
}
|
||||||
|
|
||||||
switch arg := args[0].(type) {
|
if _, ok := args[0].(*Char); ok {
|
||||||
case *Char:
|
return args[0], nil
|
||||||
return arg, nil
|
}
|
||||||
case *Int:
|
|
||||||
return &Char{Value: rune(arg.Value)}, nil
|
v, ok := ToRune(args[0])
|
||||||
case *String:
|
if ok {
|
||||||
rs := []rune(arg.Value)
|
return &Char{Value: v}, nil
|
||||||
switch len(rs) {
|
}
|
||||||
case 0:
|
|
||||||
return &Char{}, nil
|
return UndefinedValue, nil
|
||||||
case 1:
|
}
|
||||||
return &Char{Value: rs[0]}, nil
|
|
||||||
}
|
func builtinBytes(args ...Object) (Object, error) {
|
||||||
|
if len(args) != 1 {
|
||||||
|
return nil, ErrWrongNumArguments
|
||||||
|
}
|
||||||
|
|
||||||
|
// bytes(N) => create a new bytes with given size N
|
||||||
|
if n, ok := args[0].(*Int); ok {
|
||||||
|
return &Bytes{Value: make([]byte, int(n.Value))}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
v, ok := ToByteSlice(args[0])
|
||||||
|
if ok {
|
||||||
|
return &Bytes{Value: v}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
return UndefinedValue, nil
|
return UndefinedValue, nil
|
||||||
|
|
|
@ -1,18 +0,0 @@
|
||||||
package objects
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
)
|
|
||||||
|
|
||||||
func builtinIsError(args ...Object) (Object, error) {
|
|
||||||
if len(args) != 1 {
|
|
||||||
return nil, fmt.Errorf("wrong number of arguments (got=%d, want=1)", len(args))
|
|
||||||
}
|
|
||||||
|
|
||||||
switch args[0].(type) {
|
|
||||||
case *Error:
|
|
||||||
return TrueValue, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
return FalseValue, nil
|
|
||||||
}
|
|
|
@ -1,18 +0,0 @@
|
||||||
package objects
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
)
|
|
||||||
|
|
||||||
func builtinIsUndefined(args ...Object) (Object, error) {
|
|
||||||
if len(args) != 1 {
|
|
||||||
return nil, fmt.Errorf("wrong number of arguments (got=%d, want=1)", len(args))
|
|
||||||
}
|
|
||||||
|
|
||||||
switch args[0].(type) {
|
|
||||||
case *Undefined:
|
|
||||||
return TrueValue, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
return FalseValue, nil
|
|
||||||
}
|
|
|
@ -14,6 +14,12 @@ func builtinLen(args ...Object) (Object, error) {
|
||||||
return &Int{Value: int64(len(arg.Value))}, nil
|
return &Int{Value: int64(len(arg.Value))}, nil
|
||||||
case *String:
|
case *String:
|
||||||
return &Int{Value: int64(len(arg.Value))}, nil
|
return &Int{Value: int64(len(arg.Value))}, nil
|
||||||
|
case *Bytes:
|
||||||
|
return &Int{Value: int64(len(arg.Value))}, nil
|
||||||
|
case *Map:
|
||||||
|
return &Int{Value: int64(len(arg.Value))}, nil
|
||||||
|
case *ImmutableMap:
|
||||||
|
return &Int{Value: int64(len(arg.Value))}, nil
|
||||||
default:
|
default:
|
||||||
return nil, fmt.Errorf("unsupported type for 'len' function: %s", arg.TypeName())
|
return nil, fmt.Errorf("unsupported type for 'len' function: %s", arg.TypeName())
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,7 +6,11 @@ import (
|
||||||
|
|
||||||
func builtinPrint(args ...Object) (Object, error) {
|
func builtinPrint(args ...Object) (Object, error) {
|
||||||
for _, arg := range args {
|
for _, arg := range args {
|
||||||
fmt.Println(arg.String())
|
if str, ok := arg.(*String); ok {
|
||||||
|
fmt.Println(str.Value)
|
||||||
|
} else {
|
||||||
|
fmt.Println(arg.String())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil, nil
|
return nil, nil
|
||||||
|
|
97
objects/builtin_type_checks.go
Normal file
97
objects/builtin_type_checks.go
Normal file
|
@ -0,0 +1,97 @@
|
||||||
|
package objects
|
||||||
|
|
||||||
|
func builtinIsString(args ...Object) (Object, error) {
|
||||||
|
if len(args) != 1 {
|
||||||
|
return nil, ErrWrongNumArguments
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, ok := args[0].(*String); ok {
|
||||||
|
return TrueValue, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return FalseValue, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func builtinIsInt(args ...Object) (Object, error) {
|
||||||
|
if len(args) != 1 {
|
||||||
|
return nil, ErrWrongNumArguments
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, ok := args[0].(*Int); ok {
|
||||||
|
return TrueValue, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return FalseValue, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func builtinIsFloat(args ...Object) (Object, error) {
|
||||||
|
if len(args) != 1 {
|
||||||
|
return nil, ErrWrongNumArguments
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, ok := args[0].(*Float); ok {
|
||||||
|
return TrueValue, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return FalseValue, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func builtinIsBool(args ...Object) (Object, error) {
|
||||||
|
if len(args) != 1 {
|
||||||
|
return nil, ErrWrongNumArguments
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, ok := args[0].(*Bool); ok {
|
||||||
|
return TrueValue, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return FalseValue, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func builtinIsChar(args ...Object) (Object, error) {
|
||||||
|
if len(args) != 1 {
|
||||||
|
return nil, ErrWrongNumArguments
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, ok := args[0].(*Char); ok {
|
||||||
|
return TrueValue, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return FalseValue, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func builtinIsBytes(args ...Object) (Object, error) {
|
||||||
|
if len(args) != 1 {
|
||||||
|
return nil, ErrWrongNumArguments
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, ok := args[0].(*Bytes); ok {
|
||||||
|
return TrueValue, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return FalseValue, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func builtinIsError(args ...Object) (Object, error) {
|
||||||
|
if len(args) != 1 {
|
||||||
|
return nil, ErrWrongNumArguments
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, ok := args[0].(*Error); ok {
|
||||||
|
return TrueValue, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return FalseValue, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func builtinIsUndefined(args ...Object) (Object, error) {
|
||||||
|
if len(args) != 1 {
|
||||||
|
return nil, ErrWrongNumArguments
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, ok := args[0].(*Undefined); ok {
|
||||||
|
return TrueValue, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return FalseValue, nil
|
||||||
|
}
|
|
@ -1,10 +1,13 @@
|
||||||
package objects
|
package objects
|
||||||
|
|
||||||
// Builtins contains all known builtin functions.
|
// NamedBuiltinFunc is a named builtin function.
|
||||||
var Builtins = []struct {
|
type NamedBuiltinFunc struct {
|
||||||
Name string
|
Name string
|
||||||
Func CallableFunc
|
Func CallableFunc
|
||||||
}{
|
}
|
||||||
|
|
||||||
|
// Builtins contains all default builtin functions.
|
||||||
|
var Builtins = []NamedBuiltinFunc{
|
||||||
{
|
{
|
||||||
Name: "print",
|
Name: "print",
|
||||||
Func: builtinPrint,
|
Func: builtinPrint,
|
||||||
|
@ -41,6 +44,34 @@ var Builtins = []struct {
|
||||||
Name: "char",
|
Name: "char",
|
||||||
Func: builtinChar,
|
Func: builtinChar,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
Name: "bytes",
|
||||||
|
Func: builtinBytes,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "is_int",
|
||||||
|
Func: builtinIsInt,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "is_float",
|
||||||
|
Func: builtinIsFloat,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "is_string",
|
||||||
|
Func: builtinIsString,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "is_bool",
|
||||||
|
Func: builtinIsBool,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "is_char",
|
||||||
|
Func: builtinIsChar,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "is_bytes",
|
||||||
|
Func: builtinIsBytes,
|
||||||
|
},
|
||||||
{
|
{
|
||||||
Name: "is_error",
|
Name: "is_error",
|
||||||
Func: builtinIsError,
|
Func: builtinIsError,
|
||||||
|
|
56
objects/bytes.go
Normal file
56
objects/bytes.go
Normal file
|
@ -0,0 +1,56 @@
|
||||||
|
package objects
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
|
||||||
|
"github.com/d5/tengo/compiler/token"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Bytes represents a byte array.
|
||||||
|
type Bytes struct {
|
||||||
|
Value []byte
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *Bytes) String() string {
|
||||||
|
return string(o.Value)
|
||||||
|
}
|
||||||
|
|
||||||
|
// TypeName returns the name of the type.
|
||||||
|
func (o *Bytes) TypeName() string {
|
||||||
|
return "bytes"
|
||||||
|
}
|
||||||
|
|
||||||
|
// BinaryOp returns another object that is the result of
|
||||||
|
// a given binary operator and a right-hand side object.
|
||||||
|
func (o *Bytes) BinaryOp(op token.Token, rhs Object) (Object, error) {
|
||||||
|
switch op {
|
||||||
|
case token.Add:
|
||||||
|
switch rhs := rhs.(type) {
|
||||||
|
case *Bytes:
|
||||||
|
return &Bytes{Value: append(o.Value, rhs.Value...)}, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil, ErrInvalidOperator
|
||||||
|
}
|
||||||
|
|
||||||
|
// Copy returns a copy of the type.
|
||||||
|
func (o *Bytes) Copy() Object {
|
||||||
|
return &Bytes{Value: append([]byte{}, o.Value...)}
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsFalsy returns true if the value of the type is falsy.
|
||||||
|
func (o *Bytes) IsFalsy() bool {
|
||||||
|
return len(o.Value) == 0
|
||||||
|
}
|
||||||
|
|
||||||
|
// Equals returns true if the value of the type
|
||||||
|
// is equal to the value of another object.
|
||||||
|
func (o *Bytes) Equals(x Object) bool {
|
||||||
|
t, ok := x.(*Bytes)
|
||||||
|
if !ok {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
return bytes.Compare(o.Value, t.Value) == 0
|
||||||
|
}
|
140
objects/conversion.go
Normal file
140
objects/conversion.go
Normal file
|
@ -0,0 +1,140 @@
|
||||||
|
package objects
|
||||||
|
|
||||||
|
import (
|
||||||
|
"strconv"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ToString will try to convert object o to string value.
|
||||||
|
func ToString(o Object) (v string, ok bool) {
|
||||||
|
if _, isUndefined := o.(*Undefined); isUndefined {
|
||||||
|
//ok = false
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
ok = true
|
||||||
|
|
||||||
|
if str, isStr := o.(*String); isStr {
|
||||||
|
v = str.Value
|
||||||
|
} else {
|
||||||
|
v = o.String()
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// ToInt will try to convert object o to int value.
|
||||||
|
func ToInt(o Object) (v int, ok bool) {
|
||||||
|
switch o := o.(type) {
|
||||||
|
case *Int:
|
||||||
|
v = int(o.Value)
|
||||||
|
ok = true
|
||||||
|
case *Float:
|
||||||
|
v = int(o.Value)
|
||||||
|
ok = true
|
||||||
|
case *Char:
|
||||||
|
v = int(o.Value)
|
||||||
|
ok = true
|
||||||
|
case *Bool:
|
||||||
|
if o.Value {
|
||||||
|
v = 1
|
||||||
|
}
|
||||||
|
ok = true
|
||||||
|
case *String:
|
||||||
|
c, err := strconv.ParseInt(o.Value, 10, 64)
|
||||||
|
if err == nil {
|
||||||
|
v = int(c)
|
||||||
|
ok = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//ok = false
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// ToInt64 will try to convert object o to int64 value.
|
||||||
|
func ToInt64(o Object) (v int64, ok bool) {
|
||||||
|
switch o := o.(type) {
|
||||||
|
case *Int:
|
||||||
|
v = o.Value
|
||||||
|
ok = true
|
||||||
|
case *Float:
|
||||||
|
v = int64(o.Value)
|
||||||
|
ok = true
|
||||||
|
case *Char:
|
||||||
|
v = int64(o.Value)
|
||||||
|
ok = true
|
||||||
|
case *Bool:
|
||||||
|
if o.Value {
|
||||||
|
v = 1
|
||||||
|
}
|
||||||
|
ok = true
|
||||||
|
case *String:
|
||||||
|
c, err := strconv.ParseInt(o.Value, 10, 64)
|
||||||
|
if err == nil {
|
||||||
|
v = c
|
||||||
|
ok = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//ok = false
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// ToFloat64 will try to convert object o to float64 value.
|
||||||
|
func ToFloat64(o Object) (v float64, ok bool) {
|
||||||
|
switch o := o.(type) {
|
||||||
|
case *Int:
|
||||||
|
v = float64(o.Value)
|
||||||
|
ok = true
|
||||||
|
case *Float:
|
||||||
|
v = o.Value
|
||||||
|
ok = true
|
||||||
|
case *String:
|
||||||
|
c, err := strconv.ParseFloat(o.Value, 64)
|
||||||
|
if err == nil {
|
||||||
|
v = c
|
||||||
|
ok = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//ok = false
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// ToBool will try to convert object o to bool value.
|
||||||
|
func ToBool(o Object) (v bool, ok bool) {
|
||||||
|
ok = true
|
||||||
|
v = !o.IsFalsy()
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// ToRune will try to convert object o to rune value.
|
||||||
|
func ToRune(o Object) (v rune, ok bool) {
|
||||||
|
switch o := o.(type) {
|
||||||
|
case *Int:
|
||||||
|
v = rune(o.Value)
|
||||||
|
ok = true
|
||||||
|
case *Char:
|
||||||
|
v = rune(o.Value)
|
||||||
|
ok = true
|
||||||
|
}
|
||||||
|
|
||||||
|
//ok = false
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// ToByteSlice will try to convert object o to []byte value.
|
||||||
|
func ToByteSlice(o Object) (v []byte, ok bool) {
|
||||||
|
switch o := o.(type) {
|
||||||
|
case *Bytes:
|
||||||
|
v = o.Value
|
||||||
|
ok = true
|
||||||
|
case *String:
|
||||||
|
v = []byte(o.Value)
|
||||||
|
ok = true
|
||||||
|
}
|
||||||
|
|
||||||
|
//ok = false
|
||||||
|
return
|
||||||
|
}
|
|
@ -2,8 +2,17 @@ package objects
|
||||||
|
|
||||||
import "errors"
|
import "errors"
|
||||||
|
|
||||||
|
// ErrNotCallable represents an error for calling on non-function objects.
|
||||||
|
var ErrNotCallable = errors.New("not a callable object")
|
||||||
|
|
||||||
|
// ErrNotIndexable represents an error for indexing on non-indexable objects.
|
||||||
|
var ErrNotIndexable = errors.New("non-indexable object")
|
||||||
|
|
||||||
// ErrInvalidOperator represents an error for invalid operator usage.
|
// ErrInvalidOperator represents an error for invalid operator usage.
|
||||||
var ErrInvalidOperator = errors.New("invalid operator")
|
var ErrInvalidOperator = errors.New("invalid operator")
|
||||||
|
|
||||||
// ErrWrongNumArguments represents a wrong number of arguments error.
|
// ErrWrongNumArguments represents a wrong number of arguments error.
|
||||||
var ErrWrongNumArguments = errors.New("wrong number of arguments")
|
var ErrWrongNumArguments = errors.New("wrong number of arguments")
|
||||||
|
|
||||||
|
// ErrInvalidTypeConversion represents an invalid type conversion error.
|
||||||
|
var ErrInvalidTypeConversion = errors.New("invalid type conversion")
|
||||||
|
|
|
@ -7,17 +7,17 @@ import (
|
||||||
"github.com/d5/tengo/compiler/token"
|
"github.com/d5/tengo/compiler/token"
|
||||||
)
|
)
|
||||||
|
|
||||||
// ModuleMap represents a module map object.
|
// ImmutableMap represents an immutable map object.
|
||||||
type ModuleMap struct {
|
type ImmutableMap struct {
|
||||||
Value map[string]Object
|
Value map[string]Object
|
||||||
}
|
}
|
||||||
|
|
||||||
// TypeName returns the name of the type.
|
// TypeName returns the name of the type.
|
||||||
func (o *ModuleMap) TypeName() string {
|
func (o *ImmutableMap) TypeName() string {
|
||||||
return "module"
|
return "immutable-map"
|
||||||
}
|
}
|
||||||
|
|
||||||
func (o *ModuleMap) String() string {
|
func (o *ImmutableMap) String() string {
|
||||||
var pairs []string
|
var pairs []string
|
||||||
for k, v := range o.Value {
|
for k, v := range o.Value {
|
||||||
pairs = append(pairs, fmt.Sprintf("%s: %s", k, v.String()))
|
pairs = append(pairs, fmt.Sprintf("%s: %s", k, v.String()))
|
||||||
|
@ -28,27 +28,27 @@ func (o *ModuleMap) String() string {
|
||||||
|
|
||||||
// BinaryOp returns another object that is the result of
|
// BinaryOp returns another object that is the result of
|
||||||
// a given binary operator and a right-hand side object.
|
// a given binary operator and a right-hand side object.
|
||||||
func (o *ModuleMap) BinaryOp(op token.Token, rhs Object) (Object, error) {
|
func (o *ImmutableMap) BinaryOp(op token.Token, rhs Object) (Object, error) {
|
||||||
return nil, ErrInvalidOperator
|
return nil, ErrInvalidOperator
|
||||||
}
|
}
|
||||||
|
|
||||||
// Copy returns a copy of the type.
|
// Copy returns a copy of the type.
|
||||||
func (o *ModuleMap) Copy() Object {
|
func (o *ImmutableMap) Copy() Object {
|
||||||
c := make(map[string]Object)
|
c := make(map[string]Object)
|
||||||
for k, v := range o.Value {
|
for k, v := range o.Value {
|
||||||
c[k] = v.Copy()
|
c[k] = v.Copy()
|
||||||
}
|
}
|
||||||
|
|
||||||
return &ModuleMap{Value: c}
|
return &ImmutableMap{Value: c}
|
||||||
}
|
}
|
||||||
|
|
||||||
// IsFalsy returns true if the value of the type is falsy.
|
// IsFalsy returns true if the value of the type is falsy.
|
||||||
func (o *ModuleMap) IsFalsy() bool {
|
func (o *ImmutableMap) IsFalsy() bool {
|
||||||
return len(o.Value) == 0
|
return len(o.Value) == 0
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get returns the value for the given key.
|
// Get returns the value for the given key.
|
||||||
func (o *ModuleMap) Get(key string) (Object, bool) {
|
func (o *ImmutableMap) Get(key string) (Object, bool) {
|
||||||
val, ok := o.Value[key]
|
val, ok := o.Value[key]
|
||||||
|
|
||||||
return val, ok
|
return val, ok
|
||||||
|
@ -56,8 +56,8 @@ func (o *ModuleMap) Get(key string) (Object, bool) {
|
||||||
|
|
||||||
// Equals returns true if the value of the type
|
// Equals returns true if the value of the type
|
||||||
// is equal to the value of another object.
|
// is equal to the value of another object.
|
||||||
func (o *ModuleMap) Equals(x Object) bool {
|
func (o *ImmutableMap) Equals(x Object) bool {
|
||||||
t, ok := x.(*ModuleMap)
|
t, ok := x.(*ImmutableMap)
|
||||||
if !ok {
|
if !ok {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
|
@ -2,8 +2,8 @@ package objects
|
||||||
|
|
||||||
import "github.com/d5/tengo/compiler/token"
|
import "github.com/d5/tengo/compiler/token"
|
||||||
|
|
||||||
// ModuleMapIterator represents an iterator for the module map.
|
// ImmutableMapIterator represents an iterator for the immutable map.
|
||||||
type ModuleMapIterator struct {
|
type ImmutableMapIterator struct {
|
||||||
v map[string]Object
|
v map[string]Object
|
||||||
k []string
|
k []string
|
||||||
i int
|
i int
|
||||||
|
@ -11,13 +11,13 @@ type ModuleMapIterator struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewModuleMapIterator creates a module iterator.
|
// NewModuleMapIterator creates a module iterator.
|
||||||
func NewModuleMapIterator(v *ModuleMap) Iterator {
|
func NewModuleMapIterator(v *ImmutableMap) Iterator {
|
||||||
var keys []string
|
var keys []string
|
||||||
for k := range v.Value {
|
for k := range v.Value {
|
||||||
keys = append(keys, k)
|
keys = append(keys, k)
|
||||||
}
|
}
|
||||||
|
|
||||||
return &ModuleMapIterator{
|
return &ImmutableMapIterator{
|
||||||
v: v.Value,
|
v: v.Value,
|
||||||
k: keys,
|
k: keys,
|
||||||
l: len(keys),
|
l: len(keys),
|
||||||
|
@ -25,51 +25,51 @@ func NewModuleMapIterator(v *ModuleMap) Iterator {
|
||||||
}
|
}
|
||||||
|
|
||||||
// TypeName returns the name of the type.
|
// TypeName returns the name of the type.
|
||||||
func (i *ModuleMapIterator) TypeName() string {
|
func (i *ImmutableMapIterator) TypeName() string {
|
||||||
return "module-iterator"
|
return "module-iterator"
|
||||||
}
|
}
|
||||||
|
|
||||||
func (i *ModuleMapIterator) String() string {
|
func (i *ImmutableMapIterator) String() string {
|
||||||
return "<module-iterator>"
|
return "<module-iterator>"
|
||||||
}
|
}
|
||||||
|
|
||||||
// BinaryOp returns another object that is the result of
|
// BinaryOp returns another object that is the result of
|
||||||
// a given binary operator and a right-hand side object.
|
// a given binary operator and a right-hand side object.
|
||||||
func (i *ModuleMapIterator) BinaryOp(op token.Token, rhs Object) (Object, error) {
|
func (i *ImmutableMapIterator) BinaryOp(op token.Token, rhs Object) (Object, error) {
|
||||||
return nil, ErrInvalidOperator
|
return nil, ErrInvalidOperator
|
||||||
}
|
}
|
||||||
|
|
||||||
// IsFalsy returns true if the value of the type is falsy.
|
// IsFalsy returns true if the value of the type is falsy.
|
||||||
func (i *ModuleMapIterator) IsFalsy() bool {
|
func (i *ImmutableMapIterator) IsFalsy() bool {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
// Equals returns true if the value of the type
|
// Equals returns true if the value of the type
|
||||||
// is equal to the value of another object.
|
// is equal to the value of another object.
|
||||||
func (i *ModuleMapIterator) Equals(Object) bool {
|
func (i *ImmutableMapIterator) Equals(Object) bool {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
// Copy returns a copy of the type.
|
// Copy returns a copy of the type.
|
||||||
func (i *ModuleMapIterator) Copy() Object {
|
func (i *ImmutableMapIterator) Copy() Object {
|
||||||
return &ModuleMapIterator{v: i.v, k: i.k, i: i.i, l: i.l}
|
return &ImmutableMapIterator{v: i.v, k: i.k, i: i.i, l: i.l}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Next returns true if there are more elements to iterate.
|
// Next returns true if there are more elements to iterate.
|
||||||
func (i *ModuleMapIterator) Next() bool {
|
func (i *ImmutableMapIterator) Next() bool {
|
||||||
i.i++
|
i.i++
|
||||||
return i.i <= i.l
|
return i.i <= i.l
|
||||||
}
|
}
|
||||||
|
|
||||||
// Key returns the key or index value of the current element.
|
// Key returns the key or index value of the current element.
|
||||||
func (i *ModuleMapIterator) Key() Object {
|
func (i *ImmutableMapIterator) Key() Object {
|
||||||
k := i.k[i.i-1]
|
k := i.k[i.i-1]
|
||||||
|
|
||||||
return &String{Value: k}
|
return &String{Value: k}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Value returns the value of the current element.
|
// Value returns the value of the current element.
|
||||||
func (i *ModuleMapIterator) Value() Object {
|
func (i *ImmutableMapIterator) Value() Object {
|
||||||
k := i.k[i.i-1]
|
k := i.k[i.i-1]
|
||||||
|
|
||||||
return i.v[k]
|
return i.v[k]
|
|
@ -46,6 +46,8 @@ func TestObject_TypeName(t *testing.T) {
|
||||||
assert.Equal(t, "undefined", o.TypeName())
|
assert.Equal(t, "undefined", o.TypeName())
|
||||||
o = &objects.Error{}
|
o = &objects.Error{}
|
||||||
assert.Equal(t, "error", o.TypeName())
|
assert.Equal(t, "error", o.TypeName())
|
||||||
|
o = &objects.Bytes{}
|
||||||
|
assert.Equal(t, "bytes", o.TypeName())
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestObject_IsFalsy(t *testing.T) {
|
func TestObject_IsFalsy(t *testing.T) {
|
||||||
|
@ -96,6 +98,10 @@ func TestObject_IsFalsy(t *testing.T) {
|
||||||
assert.True(t, o.IsFalsy())
|
assert.True(t, o.IsFalsy())
|
||||||
o = &objects.Error{}
|
o = &objects.Error{}
|
||||||
assert.True(t, o.IsFalsy())
|
assert.True(t, o.IsFalsy())
|
||||||
|
o = &objects.Bytes{}
|
||||||
|
assert.True(t, o.IsFalsy())
|
||||||
|
o = &objects.Bytes{Value: []byte{1, 2}}
|
||||||
|
assert.False(t, o.IsFalsy())
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestObject_String(t *testing.T) {
|
func TestObject_String(t *testing.T) {
|
||||||
|
@ -138,6 +144,10 @@ func TestObject_String(t *testing.T) {
|
||||||
assert.Equal(t, "<return-value>", o.String())
|
assert.Equal(t, "<return-value>", o.String())
|
||||||
o = &objects.Undefined{}
|
o = &objects.Undefined{}
|
||||||
assert.Equal(t, "<undefined>", o.String())
|
assert.Equal(t, "<undefined>", o.String())
|
||||||
|
o = &objects.Bytes{}
|
||||||
|
assert.Equal(t, "", o.String())
|
||||||
|
o = &objects.Bytes{Value: []byte("foo")}
|
||||||
|
assert.Equal(t, "foo", o.String())
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestObject_BinaryOp(t *testing.T) {
|
func TestObject_BinaryOp(t *testing.T) {
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
package objects
|
package objects
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"strconv"
|
||||||
|
|
||||||
"github.com/d5/tengo/compiler/token"
|
"github.com/d5/tengo/compiler/token"
|
||||||
)
|
)
|
||||||
|
@ -17,7 +17,7 @@ func (o *String) TypeName() string {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (o *String) String() string {
|
func (o *String) String() string {
|
||||||
return fmt.Sprintf("%q", o.Value)
|
return strconv.Quote(o.Value)
|
||||||
}
|
}
|
||||||
|
|
||||||
// BinaryOp returns another object that is the result of
|
// BinaryOp returns another object that is the result of
|
||||||
|
|
|
@ -40,7 +40,7 @@ func (o *UserFunction) Equals(x Object) bool {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
// Call executes a builtin function.
|
// Call invokes a user function.
|
||||||
func (o *UserFunction) Call(args ...Object) (Object, error) {
|
func (o *UserFunction) Call(args ...Object) (Object, error) {
|
||||||
return o.Value(args...)
|
return o.Value(args...)
|
||||||
}
|
}
|
||||||
|
|
|
@ -622,6 +622,25 @@ func (v *VM) Run() error {
|
||||||
v.stack[v.sp] = &val
|
v.stack[v.sp] = &val
|
||||||
v.sp++
|
v.sp++
|
||||||
|
|
||||||
|
case *objects.Bytes:
|
||||||
|
idx, ok := (*index).(*objects.Int)
|
||||||
|
if !ok {
|
||||||
|
return fmt.Errorf("non-integer array index: %s", left.TypeName())
|
||||||
|
}
|
||||||
|
|
||||||
|
if idx.Value < 0 || idx.Value >= int64(len(left.Value)) {
|
||||||
|
return fmt.Errorf("index out of bounds: %d", index)
|
||||||
|
}
|
||||||
|
|
||||||
|
if v.sp >= StackSize {
|
||||||
|
return ErrStackOverflow
|
||||||
|
}
|
||||||
|
|
||||||
|
var val objects.Object = &objects.Int{Value: int64(left.Value[idx.Value])}
|
||||||
|
|
||||||
|
v.stack[v.sp] = &val
|
||||||
|
v.sp++
|
||||||
|
|
||||||
case *objects.Map:
|
case *objects.Map:
|
||||||
key, ok := (*index).(*objects.String)
|
key, ok := (*index).(*objects.String)
|
||||||
if !ok {
|
if !ok {
|
||||||
|
@ -641,7 +660,7 @@ func (v *VM) Run() error {
|
||||||
v.stack[v.sp] = &res
|
v.stack[v.sp] = &res
|
||||||
v.sp++
|
v.sp++
|
||||||
|
|
||||||
case *objects.ModuleMap:
|
case *objects.ImmutableMap:
|
||||||
key, ok := (*index).(*objects.String)
|
key, ok := (*index).(*objects.String)
|
||||||
if !ok {
|
if !ok {
|
||||||
return fmt.Errorf("non-string key: %s", left.TypeName())
|
return fmt.Errorf("non-string key: %s", left.TypeName())
|
||||||
|
@ -754,6 +773,31 @@ func (v *VM) Run() error {
|
||||||
v.stack[v.sp] = &val
|
v.stack[v.sp] = &val
|
||||||
v.sp++
|
v.sp++
|
||||||
|
|
||||||
|
case *objects.Bytes:
|
||||||
|
numElements := int64(len(left.Value))
|
||||||
|
|
||||||
|
if lowIdx < 0 || lowIdx >= numElements {
|
||||||
|
return fmt.Errorf("index out of bounds: %d", lowIdx)
|
||||||
|
}
|
||||||
|
if highIdx < 0 {
|
||||||
|
highIdx = numElements
|
||||||
|
} else if highIdx < 0 || highIdx > numElements {
|
||||||
|
return fmt.Errorf("index out of bounds: %d", highIdx)
|
||||||
|
}
|
||||||
|
|
||||||
|
if lowIdx > highIdx {
|
||||||
|
return fmt.Errorf("invalid slice index: %d > %d", lowIdx, highIdx)
|
||||||
|
}
|
||||||
|
|
||||||
|
if v.sp >= StackSize {
|
||||||
|
return ErrStackOverflow
|
||||||
|
}
|
||||||
|
|
||||||
|
var val objects.Object = &objects.Bytes{Value: left.Value[lowIdx:highIdx]}
|
||||||
|
|
||||||
|
v.stack[v.sp] = &val
|
||||||
|
v.sp++
|
||||||
|
|
||||||
default:
|
default:
|
||||||
return fmt.Errorf("cannot slice %s", left.TypeName())
|
return fmt.Errorf("cannot slice %s", left.TypeName())
|
||||||
}
|
}
|
||||||
|
@ -1005,7 +1049,7 @@ func (v *VM) Run() error {
|
||||||
iterator = objects.NewArrayIterator(dst)
|
iterator = objects.NewArrayIterator(dst)
|
||||||
case *objects.Map:
|
case *objects.Map:
|
||||||
iterator = objects.NewMapIterator(dst)
|
iterator = objects.NewMapIterator(dst)
|
||||||
case *objects.ModuleMap:
|
case *objects.ImmutableMap:
|
||||||
iterator = objects.NewModuleMapIterator(dst)
|
iterator = objects.NewModuleMapIterator(dst)
|
||||||
case *objects.String:
|
case *objects.String:
|
||||||
iterator = objects.NewStringIterator(dst)
|
iterator = objects.NewStringIterator(dst)
|
||||||
|
@ -1213,7 +1257,7 @@ func (v *VM) callFunction(fn *objects.CompiledFunction, freeVars []*objects.Obje
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: should reuse *objects.ModuleMap for the same imports?
|
// TODO: should reuse *objects.ImmutableMap for the same imports?
|
||||||
func (v *VM) importModule(compiledModule *objects.CompiledModule) error {
|
func (v *VM) importModule(compiledModule *objects.CompiledModule) error {
|
||||||
// import module is basically to create a new instance of VM
|
// import module is basically to create a new instance of VM
|
||||||
// and run the module code and retrieve all global variables after execution.
|
// and run the module code and retrieve all global variables after execution.
|
||||||
|
@ -1230,7 +1274,7 @@ func (v *VM) importModule(compiledModule *objects.CompiledModule) error {
|
||||||
mmValue[name] = *moduleVM.globals[index]
|
mmValue[name] = *moduleVM.globals[index]
|
||||||
}
|
}
|
||||||
|
|
||||||
var mm objects.Object = &objects.ModuleMap{Value: mmValue}
|
var mm objects.Object = &objects.ImmutableMap{Value: mmValue}
|
||||||
|
|
||||||
if v.sp >= StackSize {
|
if v.sp >= StackSize {
|
||||||
return ErrStackOverflow
|
return ErrStackOverflow
|
||||||
|
|
15
runtime/vm_bytes_test.go
Normal file
15
runtime/vm_bytes_test.go
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
package runtime_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestBytes(t *testing.T) {
|
||||||
|
expect(t, `out = bytes("Hello World!")`, []byte("Hello World!"))
|
||||||
|
expect(t, `out = bytes("Hello") + bytes(" ") + bytes("World!")`, []byte("Hello World!"))
|
||||||
|
|
||||||
|
// bytes[] -> int
|
||||||
|
expect(t, `out = bytes("abcde")[0]`, 97)
|
||||||
|
expect(t, `out = bytes("abcde")[1]`, 98)
|
||||||
|
expect(t, `out = bytes("abcde")[4]`, 101)
|
||||||
|
}
|
|
@ -3,12 +3,63 @@ package runtime_test
|
||||||
import "testing"
|
import "testing"
|
||||||
|
|
||||||
func TestModule(t *testing.T) {
|
func TestModule(t *testing.T) {
|
||||||
// stdmods
|
// stdlib
|
||||||
expect(t, `math := import("math"); out = math.abs(1)`, 1.0)
|
expect(t, `math := import("math"); out = math.abs(1)`, 1.0)
|
||||||
expect(t, `math := import("math"); out = math.abs(-1)`, 1.0)
|
expect(t, `math := import("math"); out = math.abs(-1)`, 1.0)
|
||||||
expect(t, `math := import("math"); out = math.abs(1.0)`, 1.0)
|
expect(t, `math := import("math"); out = math.abs(1.0)`, 1.0)
|
||||||
expect(t, `math := import("math"); out = math.abs(-1.0)`, 1.0)
|
expect(t, `math := import("math"); out = math.abs(-1.0)`, 1.0)
|
||||||
|
|
||||||
|
// os.File
|
||||||
|
expect(t, `
|
||||||
|
os := import("os")
|
||||||
|
|
||||||
|
write_file := func(filename, data) {
|
||||||
|
file := os.create(filename)
|
||||||
|
if !file { return file }
|
||||||
|
|
||||||
|
if res := file.write(bytes(data)); is_error(res) {
|
||||||
|
return res
|
||||||
|
}
|
||||||
|
|
||||||
|
return file.close()
|
||||||
|
}
|
||||||
|
|
||||||
|
read_file := func(filename) {
|
||||||
|
file := os.open(filename)
|
||||||
|
if !file { return file }
|
||||||
|
|
||||||
|
data := bytes(100)
|
||||||
|
cnt := file.read(data)
|
||||||
|
if is_error(cnt) {
|
||||||
|
return cnt
|
||||||
|
}
|
||||||
|
|
||||||
|
file.close()
|
||||||
|
return data[:cnt]
|
||||||
|
}
|
||||||
|
|
||||||
|
if write_file("./temp", "foobar") {
|
||||||
|
out = string(read_file("./temp"))
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
// user modules
|
||||||
expectWithUserModules(t, `out = import("mod1").bar()`, 5.0, map[string]string{
|
expectWithUserModules(t, `out = import("mod1").bar()`, 5.0, map[string]string{
|
||||||
"mod1": `bar := func() { return 5.0 }`,
|
"mod1": `bar := func() { return 5.0 }`,
|
||||||
|
|
|
@ -112,6 +112,8 @@ func toObject(v interface{}) objects.Object {
|
||||||
return &objects.Char{Value: rune(v)}
|
return &objects.Char{Value: rune(v)}
|
||||||
case float64:
|
case float64:
|
||||||
return &objects.Float{Value: v}
|
return &objects.Float{Value: v}
|
||||||
|
case []byte:
|
||||||
|
return &objects.Bytes{Value: v}
|
||||||
case MAP:
|
case MAP:
|
||||||
objs := make(map[string]objects.Object)
|
objs := make(map[string]objects.Object)
|
||||||
for k, v := range v {
|
for k, v := range v {
|
||||||
|
@ -174,9 +176,12 @@ func traceCompileRun(file *ast.File, symbols map[string]objects.Object, userModu
|
||||||
sym := symTable.Define(name)
|
sym := symTable.Define(name)
|
||||||
globals[sym.Index] = &value
|
globals[sym.Index] = &value
|
||||||
}
|
}
|
||||||
|
for idx, fn := range objects.Builtins {
|
||||||
|
symTable.DefineBuiltin(idx, fn.Name)
|
||||||
|
}
|
||||||
|
|
||||||
tr := &tracer{}
|
tr := &tracer{}
|
||||||
c := compiler.NewCompiler(symTable, tr)
|
c := compiler.NewCompiler(symTable, nil, tr)
|
||||||
c.SetModuleLoader(func(moduleName string) ([]byte, error) {
|
c.SetModuleLoader(func(moduleName string) ([]byte, error) {
|
||||||
if src, ok := userModules[moduleName]; ok {
|
if src, ok := userModules[moduleName]; ok {
|
||||||
return []byte(src), nil
|
return []byte(src), nil
|
||||||
|
@ -278,6 +283,8 @@ func objectZeroCopy(o objects.Object) objects.Object {
|
||||||
return &objects.Undefined{}
|
return &objects.Undefined{}
|
||||||
case *objects.Error:
|
case *objects.Error:
|
||||||
return &objects.Error{}
|
return &objects.Error{}
|
||||||
|
case *objects.Bytes:
|
||||||
|
return &objects.Bytes{}
|
||||||
case nil:
|
case nil:
|
||||||
panic("nil")
|
panic("nil")
|
||||||
default:
|
default:
|
||||||
|
|
|
@ -2,44 +2,10 @@ package script
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"strconv"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"github.com/d5/tengo/objects"
|
"github.com/d5/tengo/objects"
|
||||||
)
|
)
|
||||||
|
|
||||||
func objectToString(o objects.Object) string {
|
|
||||||
switch val := o.(type) {
|
|
||||||
case *objects.Array:
|
|
||||||
var s []string
|
|
||||||
for _, e := range val.Value {
|
|
||||||
s = append(s, objectToString(e))
|
|
||||||
}
|
|
||||||
return "[" + strings.Join(s, ", ") + "]"
|
|
||||||
case *objects.Map:
|
|
||||||
var s []string
|
|
||||||
for k, v := range val.Value {
|
|
||||||
s = append(s, k+": "+objectToString(v))
|
|
||||||
}
|
|
||||||
return "{" + strings.Join(s, ", ") + "}"
|
|
||||||
case *objects.Int:
|
|
||||||
return strconv.FormatInt(val.Value, 10)
|
|
||||||
case *objects.Float:
|
|
||||||
return strconv.FormatFloat(val.Value, 'f', -1, 64)
|
|
||||||
case *objects.Bool:
|
|
||||||
if val.Value {
|
|
||||||
return "true"
|
|
||||||
}
|
|
||||||
return "false"
|
|
||||||
case *objects.Char:
|
|
||||||
return string(val.Value)
|
|
||||||
case *objects.String:
|
|
||||||
return val.Value
|
|
||||||
}
|
|
||||||
|
|
||||||
return o.String()
|
|
||||||
}
|
|
||||||
|
|
||||||
func objectToInterface(o objects.Object) interface{} {
|
func objectToInterface(o objects.Object) interface{} {
|
||||||
switch val := o.(type) {
|
switch val := o.(type) {
|
||||||
case *objects.Array:
|
case *objects.Array:
|
||||||
|
@ -56,6 +22,8 @@ func objectToInterface(o objects.Object) interface{} {
|
||||||
return val.Value
|
return val.Value
|
||||||
case *objects.String:
|
case *objects.String:
|
||||||
return val.Value
|
return val.Value
|
||||||
|
case *objects.Bytes:
|
||||||
|
return val.Value
|
||||||
case *objects.Undefined:
|
case *objects.Undefined:
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -81,6 +49,10 @@ func interfaceToObject(v interface{}) (objects.Object, error) {
|
||||||
return &objects.Char{Value: rune(v)}, nil
|
return &objects.Char{Value: rune(v)}, nil
|
||||||
case float64:
|
case float64:
|
||||||
return &objects.Float{Value: v}, nil
|
return &objects.Float{Value: v}, nil
|
||||||
|
case []byte:
|
||||||
|
return &objects.Bytes{Value: v}, nil
|
||||||
|
case error:
|
||||||
|
return &objects.Error{Value: &objects.String{Value: v.Error()}}, nil
|
||||||
case map[string]objects.Object:
|
case map[string]objects.Object:
|
||||||
return &objects.Map{Value: v}, nil
|
return &objects.Map{Value: v}, nil
|
||||||
case map[string]interface{}:
|
case map[string]interface{}:
|
||||||
|
|
|
@ -63,7 +63,7 @@ func (s *Script) Compile() (*Compiled, error) {
|
||||||
return nil, fmt.Errorf("parse error: %s", err.Error())
|
return nil, fmt.Errorf("parse error: %s", err.Error())
|
||||||
}
|
}
|
||||||
|
|
||||||
c := compiler.NewCompiler(symbolTable, nil)
|
c := compiler.NewCompiler(symbolTable, nil, nil)
|
||||||
if err := c.Compile(file); err != nil {
|
if err := c.Compile(file); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -94,6 +94,10 @@ func (s *Script) prepCompile() (symbolTable *compiler.SymbolTable, globals []*ob
|
||||||
}
|
}
|
||||||
|
|
||||||
symbolTable = compiler.NewSymbolTable()
|
symbolTable = compiler.NewSymbolTable()
|
||||||
|
for idx, fn := range objects.Builtins {
|
||||||
|
symbolTable.DefineBuiltin(idx, fn.Name)
|
||||||
|
}
|
||||||
|
|
||||||
globals = make([]*objects.Object, len(names), len(names))
|
globals = make([]*objects.Object, len(names), len(names))
|
||||||
|
|
||||||
for idx, name := range names {
|
for idx, name := range names {
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
package script
|
package script
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"strconv"
|
"errors"
|
||||||
|
|
||||||
"github.com/d5/tengo/objects"
|
"github.com/d5/tengo/objects"
|
||||||
)
|
)
|
||||||
|
@ -43,73 +43,41 @@ func (v *Variable) ValueType() string {
|
||||||
// Int returns int value of the variable value.
|
// Int returns int value of the variable value.
|
||||||
// It returns 0 if the value is not convertible to int.
|
// It returns 0 if the value is not convertible to int.
|
||||||
func (v *Variable) Int() int {
|
func (v *Variable) Int() int {
|
||||||
return int(v.Int64())
|
c, _ := objects.ToInt(*v.value)
|
||||||
|
|
||||||
|
return c
|
||||||
}
|
}
|
||||||
|
|
||||||
// Int64 returns int64 value of the variable value.
|
// Int64 returns int64 value of the variable value.
|
||||||
// It returns 0 if the value is not convertible to int64.
|
// It returns 0 if the value is not convertible to int64.
|
||||||
func (v *Variable) Int64() int64 {
|
func (v *Variable) Int64() int64 {
|
||||||
switch val := (*v.value).(type) {
|
c, _ := objects.ToInt64(*v.value)
|
||||||
case *objects.Int:
|
|
||||||
return val.Value
|
|
||||||
case *objects.Float:
|
|
||||||
return int64(val.Value)
|
|
||||||
case *objects.Bool:
|
|
||||||
if val.Value {
|
|
||||||
return 1
|
|
||||||
}
|
|
||||||
return 0
|
|
||||||
case *objects.Char:
|
|
||||||
return int64(val.Value)
|
|
||||||
case *objects.String:
|
|
||||||
n, _ := strconv.ParseInt(val.Value, 10, 64)
|
|
||||||
return n
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0
|
return c
|
||||||
}
|
}
|
||||||
|
|
||||||
// Float returns float64 value of the variable value.
|
// Float returns float64 value of the variable value.
|
||||||
// It returns 0.0 if the value is not convertible to float64.
|
// It returns 0.0 if the value is not convertible to float64.
|
||||||
func (v *Variable) Float() float64 {
|
func (v *Variable) Float() float64 {
|
||||||
switch val := (*v.value).(type) {
|
c, _ := objects.ToFloat64(*v.value)
|
||||||
case *objects.Int:
|
|
||||||
return float64(val.Value)
|
|
||||||
case *objects.Float:
|
|
||||||
return val.Value
|
|
||||||
case *objects.Bool:
|
|
||||||
if val.Value {
|
|
||||||
return 1
|
|
||||||
}
|
|
||||||
return 0
|
|
||||||
case *objects.String:
|
|
||||||
f, _ := strconv.ParseFloat(val.Value, 64)
|
|
||||||
return f
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0
|
return c
|
||||||
}
|
}
|
||||||
|
|
||||||
// Char returns rune value of the variable value.
|
// Char returns rune value of the variable value.
|
||||||
// It returns 0 if the value is not convertible to rune.
|
// It returns 0 if the value is not convertible to rune.
|
||||||
func (v *Variable) Char() rune {
|
func (v *Variable) Char() rune {
|
||||||
switch val := (*v.value).(type) {
|
c, _ := objects.ToRune(*v.value)
|
||||||
case *objects.Char:
|
|
||||||
return val.Value
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0
|
return c
|
||||||
}
|
}
|
||||||
|
|
||||||
// Bool returns bool value of the variable value.
|
// Bool returns bool value of the variable value.
|
||||||
// It returns 0 if the value is not convertible to bool.
|
// It returns 0 if the value is not convertible to bool.
|
||||||
func (v *Variable) Bool() bool {
|
func (v *Variable) Bool() bool {
|
||||||
switch val := (*v.value).(type) {
|
c, _ := objects.ToBool(*v.value)
|
||||||
case *objects.Bool:
|
|
||||||
return val.Value
|
|
||||||
}
|
|
||||||
|
|
||||||
return false
|
return c
|
||||||
}
|
}
|
||||||
|
|
||||||
// Array returns []interface value of the variable value.
|
// Array returns []interface value of the variable value.
|
||||||
|
@ -145,7 +113,28 @@ func (v *Variable) Map() map[string]interface{} {
|
||||||
// String returns string value of the variable value.
|
// String returns string value of the variable value.
|
||||||
// It returns 0 if the value is not convertible to string.
|
// It returns 0 if the value is not convertible to string.
|
||||||
func (v *Variable) String() string {
|
func (v *Variable) String() string {
|
||||||
return objectToString(*v.value)
|
c, _ := objects.ToString(*v.value)
|
||||||
|
|
||||||
|
return c
|
||||||
|
}
|
||||||
|
|
||||||
|
// Bytes returns a byte slice of the variable value.
|
||||||
|
// It returns nil if the value is not convertible to byte slice.
|
||||||
|
func (v *Variable) Bytes() []byte {
|
||||||
|
c, _ := objects.ToByteSlice(*v.value)
|
||||||
|
|
||||||
|
return c
|
||||||
|
}
|
||||||
|
|
||||||
|
// Error returns an error if the underlying value is error object.
|
||||||
|
// If not, this returns nil.
|
||||||
|
func (v *Variable) Error() error {
|
||||||
|
err, ok := (*v.value).(*objects.Error)
|
||||||
|
if ok {
|
||||||
|
return errors.New(err.String())
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Object returns an underlying Object of the variable value.
|
// Object returns an underlying Object of the variable value.
|
||||||
|
|
|
@ -31,6 +31,8 @@ func TestVariable(t *testing.T) {
|
||||||
IntValue: 1,
|
IntValue: 1,
|
||||||
Int64Value: 1,
|
Int64Value: 1,
|
||||||
FloatValue: 1.0,
|
FloatValue: 1.0,
|
||||||
|
CharValue: rune(1),
|
||||||
|
BoolValue: true,
|
||||||
StringValue: "1",
|
StringValue: "1",
|
||||||
Object: &objects.Int{Value: 1},
|
Object: &objects.Int{Value: 1},
|
||||||
},
|
},
|
||||||
|
@ -40,6 +42,7 @@ func TestVariable(t *testing.T) {
|
||||||
ValueType: "string",
|
ValueType: "string",
|
||||||
FloatValue: 52.11,
|
FloatValue: 52.11,
|
||||||
StringValue: "52.11",
|
StringValue: "52.11",
|
||||||
|
BoolValue: true,
|
||||||
Object: &objects.String{Value: "52.11"},
|
Object: &objects.String{Value: "52.11"},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -48,7 +51,7 @@ func TestVariable(t *testing.T) {
|
||||||
ValueType: "bool",
|
ValueType: "bool",
|
||||||
IntValue: 1,
|
IntValue: 1,
|
||||||
Int64Value: 1,
|
Int64Value: 1,
|
||||||
FloatValue: 1,
|
FloatValue: 0,
|
||||||
BoolValue: true,
|
BoolValue: true,
|
||||||
StringValue: "true",
|
StringValue: "true",
|
||||||
Object: &objects.Bool{Value: true},
|
Object: &objects.Bool{Value: true},
|
||||||
|
@ -57,7 +60,7 @@ func TestVariable(t *testing.T) {
|
||||||
Name: "d",
|
Name: "d",
|
||||||
Value: nil,
|
Value: nil,
|
||||||
ValueType: "undefined",
|
ValueType: "undefined",
|
||||||
StringValue: "<undefined>",
|
StringValue: "",
|
||||||
Object: objects.UndefinedValue,
|
Object: objects.UndefinedValue,
|
||||||
IsUndefined: true,
|
IsUndefined: true,
|
||||||
},
|
},
|
||||||
|
@ -66,15 +69,15 @@ func TestVariable(t *testing.T) {
|
||||||
for _, tc := range vars {
|
for _, tc := range vars {
|
||||||
v, err := script.NewVariable(tc.Name, tc.Value)
|
v, err := script.NewVariable(tc.Name, tc.Value)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.Equal(t, tc.Value, v.Value())
|
assert.Equal(t, tc.Value, v.Value(), "Name: %s", tc.Name)
|
||||||
assert.Equal(t, tc.ValueType, v.ValueType())
|
assert.Equal(t, tc.ValueType, v.ValueType(), "Name: %s", tc.Name)
|
||||||
assert.Equal(t, tc.IntValue, v.Int())
|
assert.Equal(t, tc.IntValue, v.Int(), "Name: %s", tc.Name)
|
||||||
assert.Equal(t, tc.Int64Value, v.Int64())
|
assert.Equal(t, tc.Int64Value, v.Int64(), "Name: %s", tc.Name)
|
||||||
assert.Equal(t, tc.FloatValue, v.Float())
|
assert.Equal(t, tc.FloatValue, v.Float(), "Name: %s", tc.Name)
|
||||||
assert.Equal(t, tc.CharValue, v.Char())
|
assert.Equal(t, tc.CharValue, v.Char(), "Name: %s", tc.Name)
|
||||||
assert.Equal(t, tc.BoolValue, v.Bool())
|
assert.Equal(t, tc.BoolValue, v.Bool(), "Name: %s", tc.Name)
|
||||||
assert.Equal(t, tc.StringValue, v.String())
|
assert.Equal(t, tc.StringValue, v.String(), "Name: %s", tc.Name)
|
||||||
assert.Equal(t, tc.Object, v.Object())
|
assert.Equal(t, tc.Object, v.Object(), "Name: %s", tc.Name)
|
||||||
assert.Equal(t, tc.IsUndefined, v.IsUndefined())
|
assert.Equal(t, tc.IsUndefined, v.IsUndefined(), "Name: %s", tc.Name)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue