Improvements on compiler/VM error reporting (filename:line:col)
- add type infos to VM error messages - add 'Name' to UserFunction objects - add 'expectErrorString' to VM tests - replace vm.expectError() with vm.expectErrorString() to make it more explicit - add source map info to VM error messages - optimization in function calls - add file/line/col info to compiler errors - change stdlib module to be loaded from VM (instead of compiler) so they can be properly loaded after the source is compiled into binary - VM can take builtin modules optionally
This commit is contained in:
parent
cb1c8a405e
commit
3500c686b3
78 changed files with 3785 additions and 2858 deletions
|
@ -33,7 +33,7 @@ func Error(t *testing.T, err error, msg ...interface{}) bool {
|
|||
|
||||
// Nil asserts v is nil.
|
||||
func Nil(t *testing.T, v interface{}, msg ...interface{}) bool {
|
||||
if v == nil {
|
||||
if isNil(v) {
|
||||
return true
|
||||
}
|
||||
|
||||
|
@ -60,7 +60,7 @@ func False(t *testing.T, v bool, msg ...interface{}) bool {
|
|||
|
||||
// NotNil asserts v is not nil.
|
||||
func NotNil(t *testing.T, v interface{}, msg ...interface{}) bool {
|
||||
if v != nil {
|
||||
if !isNil(v) {
|
||||
return true
|
||||
}
|
||||
|
||||
|
@ -78,7 +78,7 @@ func IsType(t *testing.T, expected, actual interface{}, msg ...interface{}) bool
|
|||
|
||||
// Equal asserts expected and actual are equal.
|
||||
func Equal(t *testing.T, expected, actual interface{}, msg ...interface{}) bool {
|
||||
if expected == nil {
|
||||
if isNil(expected) {
|
||||
return Nil(t, actual, "expected nil, but got not nil")
|
||||
}
|
||||
if !NotNil(t, actual, "expected not nil, but got nil") {
|
||||
|
@ -175,6 +175,13 @@ func Equal(t *testing.T, expected, actual interface{}, msg ...interface{}) bool
|
|||
if !expected.Equals(actual.(objects.Object)) {
|
||||
return failExpectedActual(t, expected, actual, msg...)
|
||||
}
|
||||
case *source.FileSet:
|
||||
return equalFileSet(t, expected, actual.(*source.FileSet), msg...)
|
||||
case *source.File:
|
||||
return Equal(t, expected.Name, actual.(*source.File).Name, msg...) &&
|
||||
Equal(t, expected.Base, actual.(*source.File).Base, msg...) &&
|
||||
Equal(t, expected.Size, actual.(*source.File).Size, msg...) &&
|
||||
True(t, equalIntSlice(expected.Lines, actual.(*source.File).Lines), msg...)
|
||||
case error:
|
||||
if expected != actual.(error) {
|
||||
return failExpectedActual(t, expected, actual, msg...)
|
||||
|
@ -245,8 +252,6 @@ func equalSymbol(a, b *compiler.Symbol) bool {
|
|||
}
|
||||
|
||||
func equalObjectSlice(t *testing.T, expected, actual []objects.Object, msg ...interface{}) bool {
|
||||
// TODO: this test does not differentiate nil vs empty slice
|
||||
|
||||
if !Equal(t, len(expected), len(actual), msg...) {
|
||||
return false
|
||||
}
|
||||
|
@ -260,6 +265,20 @@ func equalObjectSlice(t *testing.T, expected, actual []objects.Object, msg ...in
|
|||
return true
|
||||
}
|
||||
|
||||
func equalFileSet(t *testing.T, expected, actual *source.FileSet, msg ...interface{}) bool {
|
||||
if !Equal(t, len(expected.Files), len(actual.Files), msg...) {
|
||||
return false
|
||||
}
|
||||
for i, f := range expected.Files {
|
||||
if !Equal(t, f, actual.Files[i], msg...) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
return Equal(t, expected.Base, actual.Base) &&
|
||||
Equal(t, expected.LastFile, actual.LastFile)
|
||||
}
|
||||
|
||||
func equalObjectMap(t *testing.T, expected, actual map[string]objects.Object, msg ...interface{}) bool {
|
||||
if !Equal(t, len(expected), len(actual), msg...) {
|
||||
return false
|
||||
|
@ -303,3 +322,17 @@ func equalClosure(t *testing.T, expected, actual objects.Object, msg ...interfac
|
|||
|
||||
return true
|
||||
}
|
||||
|
||||
func isNil(v interface{}) bool {
|
||||
if v == nil {
|
||||
return true
|
||||
}
|
||||
|
||||
value := reflect.ValueOf(v)
|
||||
kind := value.Kind()
|
||||
if kind >= reflect.Chan && kind <= reflect.Slice && value.IsNil() {
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
|
|
@ -175,7 +175,7 @@ func runBench(input []byte) (parseTime time.Duration, compileTime time.Duration,
|
|||
|
||||
func parse(input []byte) (time.Duration, *ast.File, error) {
|
||||
fileSet := source.NewFileSet()
|
||||
inputFile := fileSet.AddFile("test", -1, len(input))
|
||||
inputFile := fileSet.AddFile("bench", -1, len(input))
|
||||
|
||||
start := time.Now()
|
||||
|
||||
|
@ -193,7 +193,7 @@ func compileFile(file *ast.File) (time.Duration, *compiler.Bytecode, error) {
|
|||
|
||||
start := time.Now()
|
||||
|
||||
c := compiler.NewCompiler(symTable, nil, nil, nil)
|
||||
c := compiler.NewCompiler(file.InputFile, symTable, nil, nil, nil)
|
||||
if err := c.Compile(file); err != nil {
|
||||
return time.Since(start), nil, err
|
||||
}
|
||||
|
@ -206,7 +206,7 @@ func runVM(bytecode *compiler.Bytecode) (time.Duration, objects.Object, error) {
|
|||
|
||||
start := time.Now()
|
||||
|
||||
v := runtime.NewVM(bytecode, globals)
|
||||
v := runtime.NewVM(bytecode, globals, nil)
|
||||
if err := v.Run(); err != nil {
|
||||
return time.Since(start), nil, err
|
||||
}
|
||||
|
|
|
@ -148,7 +148,7 @@ func compileAndRun(data []byte, inputFile string) (err error) {
|
|||
return
|
||||
}
|
||||
|
||||
machine := runtime.NewVM(bytecode, nil)
|
||||
machine := runtime.NewVM(bytecode, nil, nil)
|
||||
|
||||
err = machine.Run()
|
||||
if err != nil {
|
||||
|
@ -165,7 +165,7 @@ func runCompiled(data []byte) (err error) {
|
|||
return
|
||||
}
|
||||
|
||||
machine := runtime.NewVM(bytecode, nil)
|
||||
machine := runtime.NewVM(bytecode, nil, nil)
|
||||
|
||||
err = machine.Run()
|
||||
if err != nil {
|
||||
|
@ -198,7 +198,8 @@ func runREPL(in io.Reader, out io.Writer) {
|
|||
|
||||
line := stdin.Text()
|
||||
|
||||
file, err := parser.ParseFile(fileSet.AddFile("test", -1, len(line)), []byte(line), nil)
|
||||
srcFile := fileSet.AddFile("repl", -1, len(line))
|
||||
file, err := parser.ParseFile(srcFile, []byte(line), nil)
|
||||
if err != nil {
|
||||
_, _ = fmt.Fprintf(out, "error: %s\n", err.Error())
|
||||
continue
|
||||
|
@ -206,7 +207,7 @@ func runREPL(in io.Reader, out io.Writer) {
|
|||
|
||||
file = addPrints(file)
|
||||
|
||||
c := compiler.NewCompiler(symbolTable, constants, nil, nil)
|
||||
c := compiler.NewCompiler(srcFile, symbolTable, constants, nil, nil)
|
||||
if err := c.Compile(file); err != nil {
|
||||
_, _ = fmt.Fprintf(out, "Compilation error:\n %s\n", err.Error())
|
||||
continue
|
||||
|
@ -214,7 +215,7 @@ func runREPL(in io.Reader, out io.Writer) {
|
|||
|
||||
bytecode := c.Bytecode()
|
||||
|
||||
machine := runtime.NewVM(bytecode, globals)
|
||||
machine := runtime.NewVM(bytecode, globals, nil)
|
||||
if err != nil {
|
||||
_, _ = fmt.Fprintf(out, "VM error:\n %s\n", err.Error())
|
||||
continue
|
||||
|
@ -230,14 +231,15 @@ func runREPL(in io.Reader, out io.Writer) {
|
|||
|
||||
func compileSrc(src []byte, filename string) (*compiler.Bytecode, error) {
|
||||
fileSet := source.NewFileSet()
|
||||
srcFile := fileSet.AddFile(filename, -1, len(src))
|
||||
|
||||
p := parser.NewParser(fileSet.AddFile(filename, -1, len(src)), src, nil)
|
||||
p := parser.NewParser(srcFile, src, nil)
|
||||
file, err := p.ParseFile()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
c := compiler.NewCompiler(nil, nil, nil, nil)
|
||||
c := compiler.NewCompiler(srcFile, nil, nil, nil, nil)
|
||||
if err := c.Compile(file); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
|
@ -14,12 +14,12 @@ type File struct {
|
|||
|
||||
// Pos returns the position of first character belonging to the node.
|
||||
func (n *File) Pos() source.Pos {
|
||||
return source.Pos(n.InputFile.Base())
|
||||
return source.Pos(n.InputFile.Base)
|
||||
}
|
||||
|
||||
// End returns the position of first character immediately after the node.
|
||||
func (n *File) End() source.Pos {
|
||||
return source.Pos(n.InputFile.Base() + n.InputFile.Size())
|
||||
return source.Pos(n.InputFile.Base + n.InputFile.Size)
|
||||
}
|
||||
|
||||
func (n *File) String() string {
|
||||
|
|
|
@ -6,12 +6,14 @@ import (
|
|||
"io"
|
||||
"reflect"
|
||||
|
||||
"github.com/d5/tengo/compiler/source"
|
||||
"github.com/d5/tengo/objects"
|
||||
)
|
||||
|
||||
// Bytecode is a compiled instructions and constants.
|
||||
type Bytecode struct {
|
||||
Instructions []byte
|
||||
FileSet *source.FileSet
|
||||
MainFunction *objects.CompiledFunction
|
||||
Constants []objects.Object
|
||||
}
|
||||
|
||||
|
@ -19,7 +21,13 @@ type Bytecode struct {
|
|||
func (b *Bytecode) Decode(r io.Reader) error {
|
||||
dec := gob.NewDecoder(r)
|
||||
|
||||
if err := dec.Decode(&b.Instructions); err != nil {
|
||||
if err := dec.Decode(&b.FileSet); err != nil {
|
||||
return err
|
||||
}
|
||||
// TODO: files in b.FileSet.File does not have their 'set' field properly set to b.FileSet
|
||||
// as it's private field and not serialized by gob encoder/decoder.
|
||||
|
||||
if err := dec.Decode(&b.MainFunction); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
|
@ -39,7 +47,11 @@ func (b *Bytecode) Decode(r io.Reader) error {
|
|||
func (b *Bytecode) Encode(w io.Writer) error {
|
||||
enc := gob.NewEncoder(w)
|
||||
|
||||
if err := enc.Encode(b.Instructions); err != nil {
|
||||
if err := enc.Encode(b.FileSet); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := enc.Encode(b.MainFunction); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
|
@ -50,7 +62,7 @@ func (b *Bytecode) Encode(w io.Writer) error {
|
|||
// FormatInstructions returns human readable string representations of
|
||||
// compiled instructions.
|
||||
func (b *Bytecode) FormatInstructions() []string {
|
||||
return FormatInstructions(b.Instructions, 0)
|
||||
return FormatInstructions(b.MainFunction.Instructions, 0)
|
||||
}
|
||||
|
||||
// FormatConstants returns human readable string representations of
|
||||
|
@ -94,21 +106,29 @@ func cleanupObjects(o objects.Object) objects.Object {
|
|||
}
|
||||
|
||||
func init() {
|
||||
gob.Register(&objects.Int{})
|
||||
gob.Register(&objects.Float{})
|
||||
gob.Register(&objects.String{})
|
||||
gob.Register(&objects.Bool{})
|
||||
gob.Register(&objects.Char{})
|
||||
gob.Register(&source.FileSet{})
|
||||
gob.Register(&source.File{})
|
||||
gob.Register(&objects.Array{})
|
||||
gob.Register(&objects.ImmutableArray{})
|
||||
gob.Register(&objects.Map{})
|
||||
gob.Register(&objects.ImmutableMap{})
|
||||
gob.Register(&objects.CompiledFunction{})
|
||||
gob.Register(&objects.Undefined{})
|
||||
gob.Register(&objects.Error{})
|
||||
gob.Register(&objects.Bytes{})
|
||||
gob.Register(&objects.StringIterator{})
|
||||
gob.Register(&objects.MapIterator{})
|
||||
gob.Register(&objects.ArrayIterator{})
|
||||
gob.Register(&objects.Bool{})
|
||||
gob.Register(&objects.Break{})
|
||||
gob.Register(&objects.BuiltinFunction{})
|
||||
gob.Register(&objects.Bytes{})
|
||||
gob.Register(&objects.Char{})
|
||||
gob.Register(&objects.Closure{})
|
||||
gob.Register(&objects.CompiledFunction{})
|
||||
gob.Register(&objects.Continue{})
|
||||
gob.Register(&objects.Error{})
|
||||
gob.Register(&objects.Float{})
|
||||
gob.Register(&objects.ImmutableArray{})
|
||||
gob.Register(&objects.ImmutableMap{})
|
||||
gob.Register(&objects.Int{})
|
||||
gob.Register(&objects.Map{})
|
||||
gob.Register(&objects.MapIterator{})
|
||||
gob.Register(&objects.ReturnValue{})
|
||||
gob.Register(&objects.String{})
|
||||
gob.Register(&objects.StringIterator{})
|
||||
gob.Register(&objects.Time{})
|
||||
gob.Register(&objects.Undefined{})
|
||||
gob.Register(&objects.UserFunction{})
|
||||
}
|
||||
|
|
|
@ -7,11 +7,17 @@ import (
|
|||
|
||||
"github.com/d5/tengo/assert"
|
||||
"github.com/d5/tengo/compiler"
|
||||
"github.com/d5/tengo/compiler/source"
|
||||
"github.com/d5/tengo/objects"
|
||||
)
|
||||
|
||||
type srcfile struct {
|
||||
name string
|
||||
size int
|
||||
}
|
||||
|
||||
func TestBytecode(t *testing.T) {
|
||||
testBytecodeSerialization(t, &compiler.Bytecode{})
|
||||
testBytecodeSerialization(t, bytecode(concat(), objectsArray()))
|
||||
|
||||
testBytecodeSerialization(t, bytecode(
|
||||
concat(), objectsArray(
|
||||
|
@ -48,7 +54,7 @@ func TestBytecode(t *testing.T) {
|
|||
&objects.String{Value: "bar"},
|
||||
objects.UndefinedValue)))
|
||||
|
||||
testBytecodeSerialization(t, bytecode(
|
||||
testBytecodeSerialization(t, bytecodeFileSet(
|
||||
concat(
|
||||
compiler.MakeInstruction(compiler.OpConstant, 0),
|
||||
compiler.MakeInstruction(compiler.OpSetGlobal, 0),
|
||||
|
@ -82,7 +88,24 @@ func TestBytecode(t *testing.T) {
|
|||
compiler.MakeInstruction(compiler.OpSetLocal, 0),
|
||||
compiler.MakeInstruction(compiler.OpGetLocal, 0),
|
||||
compiler.MakeInstruction(compiler.OpClosure, 5, 1),
|
||||
compiler.MakeInstruction(compiler.OpReturnValue)))))
|
||||
compiler.MakeInstruction(compiler.OpReturnValue))),
|
||||
fileSet(srcfile{name: "file1", size: 100}, srcfile{name: "file2", size: 200})))
|
||||
}
|
||||
|
||||
func fileSet(files ...srcfile) *source.FileSet {
|
||||
fileSet := source.NewFileSet()
|
||||
for _, f := range files {
|
||||
fileSet.AddFile(f.name, -1, f.size)
|
||||
}
|
||||
return fileSet
|
||||
}
|
||||
|
||||
func bytecodeFileSet(instructions []byte, constants []objects.Object, fileSet *source.FileSet) *compiler.Bytecode {
|
||||
return &compiler.Bytecode{
|
||||
FileSet: fileSet,
|
||||
MainFunction: &objects.CompiledFunction{Instructions: instructions},
|
||||
Constants: constants,
|
||||
}
|
||||
}
|
||||
|
||||
func testBytecodeSerialization(t *testing.T, b *compiler.Bytecode) {
|
||||
|
@ -94,6 +117,7 @@ func testBytecodeSerialization(t *testing.T, b *compiler.Bytecode) {
|
|||
err = r.Decode(bytes.NewReader(buf.Bytes()))
|
||||
assert.NoError(t, err)
|
||||
|
||||
assert.Equal(t, b.Instructions, r.Instructions)
|
||||
assert.Equal(t, b.FileSet, r.FileSet)
|
||||
assert.Equal(t, b.MainFunction, r.MainFunction)
|
||||
assert.Equal(t, b.Constants, r.Constants)
|
||||
}
|
||||
|
|
|
@ -1,9 +1,12 @@
|
|||
package compiler
|
||||
|
||||
import "github.com/d5/tengo/compiler/source"
|
||||
|
||||
// CompilationScope represents a compiled instructions
|
||||
// and the last two instructions that were emitted.
|
||||
type CompilationScope struct {
|
||||
instructions []byte
|
||||
lastInstructions [2]EmittedInstruction
|
||||
symbolInit map[string]bool
|
||||
sourceMap map[int]source.Pos
|
||||
}
|
||||
|
|
|
@ -6,13 +6,15 @@ import (
|
|||
"reflect"
|
||||
|
||||
"github.com/d5/tengo/compiler/ast"
|
||||
"github.com/d5/tengo/compiler/stdlib"
|
||||
"github.com/d5/tengo/compiler/source"
|
||||
"github.com/d5/tengo/compiler/token"
|
||||
"github.com/d5/tengo/objects"
|
||||
"github.com/d5/tengo/stdlib"
|
||||
)
|
||||
|
||||
// Compiler compiles the AST into a bytecode.
|
||||
type Compiler struct {
|
||||
file *source.File
|
||||
parent *Compiler
|
||||
moduleName string
|
||||
constants []objects.Object
|
||||
|
@ -20,7 +22,7 @@ type Compiler struct {
|
|||
scopes []CompilationScope
|
||||
scopeIndex int
|
||||
moduleLoader ModuleLoader
|
||||
stdModules map[string]*objects.ImmutableMap
|
||||
builtinModules map[string]bool
|
||||
compiledModules map[string]*objects.CompiledFunction
|
||||
loops []*Loop
|
||||
loopIndex int
|
||||
|
@ -34,9 +36,10 @@ type Compiler struct {
|
|||
// 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, constants []objects.Object, stdModules map[string]*objects.ImmutableMap, trace io.Writer) *Compiler {
|
||||
func NewCompiler(file *source.File, symbolTable *SymbolTable, constants []objects.Object, builtinModules map[string]bool, trace io.Writer) *Compiler {
|
||||
mainScope := CompilationScope{
|
||||
instructions: make([]byte, 0),
|
||||
symbolInit: make(map[string]bool),
|
||||
sourceMap: make(map[int]source.Pos),
|
||||
}
|
||||
|
||||
// symbol table
|
||||
|
@ -48,19 +51,23 @@ func NewCompiler(symbolTable *SymbolTable, constants []objects.Object, stdModule
|
|||
}
|
||||
}
|
||||
|
||||
// standard modules
|
||||
if stdModules == nil {
|
||||
stdModules = stdlib.Modules
|
||||
// builtin modules
|
||||
if builtinModules == nil {
|
||||
builtinModules = make(map[string]bool)
|
||||
for name := range stdlib.Modules {
|
||||
builtinModules[name] = true
|
||||
}
|
||||
}
|
||||
|
||||
return &Compiler{
|
||||
file: file,
|
||||
symbolTable: symbolTable,
|
||||
constants: constants,
|
||||
scopes: []CompilationScope{mainScope},
|
||||
scopeIndex: 0,
|
||||
loopIndex: -1,
|
||||
trace: trace,
|
||||
stdModules: stdModules,
|
||||
builtinModules: builtinModules,
|
||||
compiledModules: make(map[string]*objects.CompiledFunction),
|
||||
}
|
||||
}
|
||||
|
@ -87,7 +94,7 @@ func (c *Compiler) Compile(node ast.Node) error {
|
|||
if err := c.Compile(node.Expr); err != nil {
|
||||
return err
|
||||
}
|
||||
c.emit(OpPop)
|
||||
c.emit(node, OpPop)
|
||||
|
||||
case *ast.IncDecStmt:
|
||||
op := token.AddAssign
|
||||
|
@ -95,7 +102,7 @@ func (c *Compiler) Compile(node ast.Node) error {
|
|||
op = token.SubAssign
|
||||
}
|
||||
|
||||
return c.compileAssign([]ast.Expr{node.Expr}, []ast.Expr{&ast.IntLit{Value: 1}}, op)
|
||||
return c.compileAssign(node, []ast.Expr{node.Expr}, []ast.Expr{&ast.IntLit{Value: 1}}, op)
|
||||
|
||||
case *ast.ParenExpr:
|
||||
if err := c.Compile(node.Expr); err != nil {
|
||||
|
@ -116,7 +123,7 @@ func (c *Compiler) Compile(node ast.Node) error {
|
|||
return err
|
||||
}
|
||||
|
||||
c.emit(OpGreaterThan)
|
||||
c.emit(node, OpGreaterThan)
|
||||
|
||||
return nil
|
||||
} else if node.Token == token.LessEq {
|
||||
|
@ -127,7 +134,7 @@ func (c *Compiler) Compile(node ast.Node) error {
|
|||
return err
|
||||
}
|
||||
|
||||
c.emit(OpGreaterThanEqual)
|
||||
c.emit(node, OpGreaterThanEqual)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
@ -141,60 +148,60 @@ func (c *Compiler) Compile(node ast.Node) error {
|
|||
|
||||
switch node.Token {
|
||||
case token.Add:
|
||||
c.emit(OpAdd)
|
||||
c.emit(node, OpAdd)
|
||||
case token.Sub:
|
||||
c.emit(OpSub)
|
||||
c.emit(node, OpSub)
|
||||
case token.Mul:
|
||||
c.emit(OpMul)
|
||||
c.emit(node, OpMul)
|
||||
case token.Quo:
|
||||
c.emit(OpDiv)
|
||||
c.emit(node, OpDiv)
|
||||
case token.Rem:
|
||||
c.emit(OpRem)
|
||||
c.emit(node, OpRem)
|
||||
case token.Greater:
|
||||
c.emit(OpGreaterThan)
|
||||
c.emit(node, OpGreaterThan)
|
||||
case token.GreaterEq:
|
||||
c.emit(OpGreaterThanEqual)
|
||||
c.emit(node, OpGreaterThanEqual)
|
||||
case token.Equal:
|
||||
c.emit(OpEqual)
|
||||
c.emit(node, OpEqual)
|
||||
case token.NotEqual:
|
||||
c.emit(OpNotEqual)
|
||||
c.emit(node, OpNotEqual)
|
||||
case token.And:
|
||||
c.emit(OpBAnd)
|
||||
c.emit(node, OpBAnd)
|
||||
case token.Or:
|
||||
c.emit(OpBOr)
|
||||
c.emit(node, OpBOr)
|
||||
case token.Xor:
|
||||
c.emit(OpBXor)
|
||||
c.emit(node, OpBXor)
|
||||
case token.AndNot:
|
||||
c.emit(OpBAndNot)
|
||||
c.emit(node, OpBAndNot)
|
||||
case token.Shl:
|
||||
c.emit(OpBShiftLeft)
|
||||
c.emit(node, OpBShiftLeft)
|
||||
case token.Shr:
|
||||
c.emit(OpBShiftRight)
|
||||
c.emit(node, OpBShiftRight)
|
||||
default:
|
||||
return fmt.Errorf("unknown operator: %s", node.Token.String())
|
||||
return c.errorf(node, "invalid binary operator: %s", node.Token.String())
|
||||
}
|
||||
|
||||
case *ast.IntLit:
|
||||
c.emit(OpConstant, c.addConstant(&objects.Int{Value: node.Value}))
|
||||
c.emit(node, OpConstant, c.addConstant(&objects.Int{Value: node.Value}))
|
||||
|
||||
case *ast.FloatLit:
|
||||
c.emit(OpConstant, c.addConstant(&objects.Float{Value: node.Value}))
|
||||
c.emit(node, OpConstant, c.addConstant(&objects.Float{Value: node.Value}))
|
||||
|
||||
case *ast.BoolLit:
|
||||
if node.Value {
|
||||
c.emit(OpTrue)
|
||||
c.emit(node, OpTrue)
|
||||
} else {
|
||||
c.emit(OpFalse)
|
||||
c.emit(node, OpFalse)
|
||||
}
|
||||
|
||||
case *ast.StringLit:
|
||||
c.emit(OpConstant, c.addConstant(&objects.String{Value: node.Value}))
|
||||
c.emit(node, OpConstant, c.addConstant(&objects.String{Value: node.Value}))
|
||||
|
||||
case *ast.CharLit:
|
||||
c.emit(OpConstant, c.addConstant(&objects.Char{Value: node.Value}))
|
||||
c.emit(node, OpConstant, c.addConstant(&objects.Char{Value: node.Value}))
|
||||
|
||||
case *ast.UndefinedLit:
|
||||
c.emit(OpNull)
|
||||
c.emit(node, OpNull)
|
||||
|
||||
case *ast.UnaryExpr:
|
||||
if err := c.Compile(node.Expr); err != nil {
|
||||
|
@ -203,15 +210,15 @@ func (c *Compiler) Compile(node ast.Node) error {
|
|||
|
||||
switch node.Token {
|
||||
case token.Not:
|
||||
c.emit(OpLNot)
|
||||
c.emit(node, OpLNot)
|
||||
case token.Sub:
|
||||
c.emit(OpMinus)
|
||||
c.emit(node, OpMinus)
|
||||
case token.Xor:
|
||||
c.emit(OpBComplement)
|
||||
c.emit(node, OpBComplement)
|
||||
case token.Add:
|
||||
// do nothing?
|
||||
default:
|
||||
return fmt.Errorf("unknown operator: %s", node.Token.String())
|
||||
return c.errorf(node, "invalid unary operator: %s", node.Token.String())
|
||||
}
|
||||
|
||||
case *ast.IfStmt:
|
||||
|
@ -232,7 +239,7 @@ func (c *Compiler) Compile(node ast.Node) error {
|
|||
}
|
||||
|
||||
// first jump placeholder
|
||||
jumpPos1 := c.emit(OpJumpFalsy, 0)
|
||||
jumpPos1 := c.emit(node, OpJumpFalsy, 0)
|
||||
|
||||
if err := c.Compile(node.Body); err != nil {
|
||||
return err
|
||||
|
@ -240,7 +247,7 @@ func (c *Compiler) Compile(node ast.Node) error {
|
|||
|
||||
if node.Else != nil {
|
||||
// second jump placeholder
|
||||
jumpPos2 := c.emit(OpJump, 0)
|
||||
jumpPos2 := c.emit(node, OpJump, 0)
|
||||
|
||||
// update first jump offset
|
||||
curPos := len(c.currentInstructions())
|
||||
|
@ -269,19 +276,19 @@ func (c *Compiler) Compile(node ast.Node) error {
|
|||
if node.Token == token.Break {
|
||||
curLoop := c.currentLoop()
|
||||
if curLoop == nil {
|
||||
return fmt.Errorf("break statement outside loop")
|
||||
return c.errorf(node, "break not allowed outside loop")
|
||||
}
|
||||
pos := c.emit(OpJump, 0)
|
||||
pos := c.emit(node, OpJump, 0)
|
||||
curLoop.Breaks = append(curLoop.Breaks, pos)
|
||||
} else if node.Token == token.Continue {
|
||||
curLoop := c.currentLoop()
|
||||
if curLoop == nil {
|
||||
return fmt.Errorf("continue statement outside loop")
|
||||
return c.errorf(node, "continue not allowed outside loop")
|
||||
}
|
||||
pos := c.emit(OpJump, 0)
|
||||
pos := c.emit(node, OpJump, 0)
|
||||
curLoop.Continues = append(curLoop.Continues, pos)
|
||||
} else {
|
||||
return fmt.Errorf("unknown branch statement: %s", node.Token.String())
|
||||
panic(fmt.Errorf("invalid branch statement: %s", node.Token.String()))
|
||||
}
|
||||
|
||||
case *ast.BlockStmt:
|
||||
|
@ -292,25 +299,25 @@ func (c *Compiler) Compile(node ast.Node) error {
|
|||
}
|
||||
|
||||
case *ast.AssignStmt:
|
||||
if err := c.compileAssign(node.LHS, node.RHS, node.Token); err != nil {
|
||||
if err := c.compileAssign(node, node.LHS, node.RHS, node.Token); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
case *ast.Ident:
|
||||
symbol, _, ok := c.symbolTable.Resolve(node.Name)
|
||||
if !ok {
|
||||
return fmt.Errorf("undefined variable: %s", node.Name)
|
||||
return c.errorf(node, "unresolved reference '%s'", node.Name)
|
||||
}
|
||||
|
||||
switch symbol.Scope {
|
||||
case ScopeGlobal:
|
||||
c.emit(OpGetGlobal, symbol.Index)
|
||||
c.emit(node, OpGetGlobal, symbol.Index)
|
||||
case ScopeLocal:
|
||||
c.emit(OpGetLocal, symbol.Index)
|
||||
c.emit(node, OpGetLocal, symbol.Index)
|
||||
case ScopeBuiltin:
|
||||
c.emit(OpGetBuiltin, symbol.Index)
|
||||
c.emit(node, OpGetBuiltin, symbol.Index)
|
||||
case ScopeFree:
|
||||
c.emit(OpGetFree, symbol.Index)
|
||||
c.emit(node, OpGetFree, symbol.Index)
|
||||
}
|
||||
|
||||
case *ast.ArrayLit:
|
||||
|
@ -320,12 +327,12 @@ func (c *Compiler) Compile(node ast.Node) error {
|
|||
}
|
||||
}
|
||||
|
||||
c.emit(OpArray, len(node.Elements))
|
||||
c.emit(node, OpArray, len(node.Elements))
|
||||
|
||||
case *ast.MapLit:
|
||||
for _, elt := range node.Elements {
|
||||
// key
|
||||
c.emit(OpConstant, c.addConstant(&objects.String{Value: elt.Key}))
|
||||
c.emit(node, OpConstant, c.addConstant(&objects.String{Value: elt.Key}))
|
||||
|
||||
// value
|
||||
if err := c.Compile(elt.Value); err != nil {
|
||||
|
@ -333,7 +340,7 @@ func (c *Compiler) Compile(node ast.Node) error {
|
|||
}
|
||||
}
|
||||
|
||||
c.emit(OpMap, len(node.Elements)*2)
|
||||
c.emit(node, OpMap, len(node.Elements)*2)
|
||||
|
||||
case *ast.SelectorExpr: // selector on RHS side
|
||||
if err := c.Compile(node.Expr); err != nil {
|
||||
|
@ -344,7 +351,7 @@ func (c *Compiler) Compile(node ast.Node) error {
|
|||
return err
|
||||
}
|
||||
|
||||
c.emit(OpIndex)
|
||||
c.emit(node, OpIndex)
|
||||
|
||||
case *ast.IndexExpr:
|
||||
if err := c.Compile(node.Expr); err != nil {
|
||||
|
@ -355,7 +362,7 @@ func (c *Compiler) Compile(node ast.Node) error {
|
|||
return err
|
||||
}
|
||||
|
||||
c.emit(OpIndex)
|
||||
c.emit(node, OpIndex)
|
||||
|
||||
case *ast.SliceExpr:
|
||||
if err := c.Compile(node.Expr); err != nil {
|
||||
|
@ -367,7 +374,7 @@ func (c *Compiler) Compile(node ast.Node) error {
|
|||
return err
|
||||
}
|
||||
} else {
|
||||
c.emit(OpNull)
|
||||
c.emit(node, OpNull)
|
||||
}
|
||||
|
||||
if node.High != nil {
|
||||
|
@ -375,10 +382,10 @@ func (c *Compiler) Compile(node ast.Node) error {
|
|||
return err
|
||||
}
|
||||
} else {
|
||||
c.emit(OpNull)
|
||||
c.emit(node, OpNull)
|
||||
}
|
||||
|
||||
c.emit(OpSliceIndex)
|
||||
c.emit(node, OpSliceIndex)
|
||||
|
||||
case *ast.FuncLit:
|
||||
c.enterScope()
|
||||
|
@ -396,12 +403,12 @@ func (c *Compiler) Compile(node ast.Node) error {
|
|||
|
||||
// add OpReturn if function returns nothing
|
||||
if !c.lastInstructionIs(OpReturnValue) && !c.lastInstructionIs(OpReturn) {
|
||||
c.emit(OpReturn)
|
||||
c.emit(node, OpReturn)
|
||||
}
|
||||
|
||||
freeSymbols := c.symbolTable.FreeSymbols()
|
||||
numLocals := c.symbolTable.MaxSymbols()
|
||||
instructions := c.leaveScope()
|
||||
instructions, sourceMap := c.leaveScope()
|
||||
|
||||
for _, s := range freeSymbols {
|
||||
switch s.Scope {
|
||||
|
@ -444,15 +451,15 @@ func (c *Compiler) Compile(node ast.Node) error {
|
|||
// 0009 SETL 0
|
||||
//
|
||||
|
||||
c.emit(OpNull)
|
||||
c.emit(OpDefineLocal, s.Index)
|
||||
c.emit(node, OpNull)
|
||||
c.emit(node, OpDefineLocal, s.Index)
|
||||
|
||||
s.LocalAssigned = true
|
||||
}
|
||||
|
||||
c.emit(OpGetLocal, s.Index)
|
||||
c.emit(node, OpGetLocal, s.Index)
|
||||
case ScopeFree:
|
||||
c.emit(OpGetFree, s.Index)
|
||||
c.emit(node, OpGetFree, s.Index)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -460,28 +467,29 @@ func (c *Compiler) Compile(node ast.Node) error {
|
|||
Instructions: instructions,
|
||||
NumLocals: numLocals,
|
||||
NumParameters: len(node.Type.Params.List),
|
||||
SourceMap: sourceMap,
|
||||
}
|
||||
|
||||
if len(freeSymbols) > 0 {
|
||||
c.emit(OpClosure, c.addConstant(compiledFunction), len(freeSymbols))
|
||||
c.emit(node, OpClosure, c.addConstant(compiledFunction), len(freeSymbols))
|
||||
} else {
|
||||
c.emit(OpConstant, c.addConstant(compiledFunction))
|
||||
c.emit(node, OpConstant, c.addConstant(compiledFunction))
|
||||
}
|
||||
|
||||
case *ast.ReturnStmt:
|
||||
if c.symbolTable.Parent(true) == nil {
|
||||
// outside the function
|
||||
return fmt.Errorf("return statement outside function")
|
||||
return c.errorf(node, "return not allowed outside function")
|
||||
}
|
||||
|
||||
if node.Result == nil {
|
||||
c.emit(OpReturn)
|
||||
c.emit(node, OpReturn)
|
||||
} else {
|
||||
if err := c.Compile(node.Result); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
c.emit(OpReturnValue)
|
||||
c.emit(node, OpReturnValue)
|
||||
}
|
||||
|
||||
case *ast.CallExpr:
|
||||
|
@ -495,28 +503,26 @@ func (c *Compiler) Compile(node ast.Node) error {
|
|||
}
|
||||
}
|
||||
|
||||
c.emit(OpCall, len(node.Args))
|
||||
c.emit(node, OpCall, len(node.Args))
|
||||
|
||||
case *ast.ImportExpr:
|
||||
stdMod, ok := c.stdModules[node.ModuleName]
|
||||
if ok {
|
||||
// standard modules contain only globals with no code.
|
||||
// so no need to compile anything
|
||||
c.emit(OpConstant, c.addConstant(stdMod))
|
||||
if c.builtinModules[node.ModuleName] {
|
||||
c.emit(node, OpConstant, c.addConstant(&objects.String{Value: node.ModuleName}))
|
||||
c.emit(node, OpGetBuiltinModule)
|
||||
} else {
|
||||
userMod, err := c.compileModule(node.ModuleName)
|
||||
userMod, err := c.compileModule(node)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
c.emit(OpConstant, c.addConstant(userMod))
|
||||
c.emit(OpCall, 0)
|
||||
c.emit(node, OpConstant, c.addConstant(userMod))
|
||||
c.emit(node, OpCall, 0)
|
||||
}
|
||||
|
||||
case *ast.ExportStmt:
|
||||
// export statement must be in top-level scope
|
||||
if c.scopeIndex != 0 {
|
||||
return fmt.Errorf("cannot use 'export' inside function")
|
||||
return c.errorf(node, "export not allowed inside function")
|
||||
}
|
||||
|
||||
// export statement is simply ignore when compiling non-module code
|
||||
|
@ -528,22 +534,22 @@ func (c *Compiler) Compile(node ast.Node) error {
|
|||
return err
|
||||
}
|
||||
|
||||
c.emit(OpImmutable)
|
||||
c.emit(OpReturnValue)
|
||||
c.emit(node, OpImmutable)
|
||||
c.emit(node, OpReturnValue)
|
||||
|
||||
case *ast.ErrorExpr:
|
||||
if err := c.Compile(node.Expr); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
c.emit(OpError)
|
||||
c.emit(node, OpError)
|
||||
|
||||
case *ast.ImmutableExpr:
|
||||
if err := c.Compile(node.Expr); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
c.emit(OpImmutable)
|
||||
c.emit(node, OpImmutable)
|
||||
|
||||
case *ast.CondExpr:
|
||||
if err := c.Compile(node.Cond); err != nil {
|
||||
|
@ -551,14 +557,14 @@ func (c *Compiler) Compile(node ast.Node) error {
|
|||
}
|
||||
|
||||
// first jump placeholder
|
||||
jumpPos1 := c.emit(OpJumpFalsy, 0)
|
||||
jumpPos1 := c.emit(node, OpJumpFalsy, 0)
|
||||
|
||||
if err := c.Compile(node.True); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// second jump placeholder
|
||||
jumpPos2 := c.emit(OpJump, 0)
|
||||
jumpPos2 := c.emit(node, OpJump, 0)
|
||||
|
||||
// update first jump offset
|
||||
curPos := len(c.currentInstructions())
|
||||
|
@ -579,8 +585,12 @@ func (c *Compiler) Compile(node ast.Node) error {
|
|||
// Bytecode returns a compiled bytecode.
|
||||
func (c *Compiler) Bytecode() *Bytecode {
|
||||
return &Bytecode{
|
||||
Instructions: c.currentInstructions(),
|
||||
Constants: c.constants,
|
||||
FileSet: c.file.Set(),
|
||||
MainFunction: &objects.CompiledFunction{
|
||||
Instructions: c.currentInstructions(),
|
||||
SourceMap: c.currentSourceMap(),
|
||||
},
|
||||
Constants: c.constants,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -591,8 +601,8 @@ func (c *Compiler) SetModuleLoader(moduleLoader ModuleLoader) {
|
|||
c.moduleLoader = moduleLoader
|
||||
}
|
||||
|
||||
func (c *Compiler) fork(moduleName string, symbolTable *SymbolTable) *Compiler {
|
||||
child := NewCompiler(symbolTable, nil, c.stdModules, c.trace)
|
||||
func (c *Compiler) fork(file *source.File, moduleName string, symbolTable *SymbolTable) *Compiler {
|
||||
child := NewCompiler(file, symbolTable, nil, c.builtinModules, c.trace)
|
||||
child.moduleName = moduleName // name of the module to compile
|
||||
child.parent = c // parent to set to current compiler
|
||||
child.moduleLoader = c.moduleLoader // share module loader
|
||||
|
@ -600,6 +610,14 @@ func (c *Compiler) fork(moduleName string, symbolTable *SymbolTable) *Compiler {
|
|||
return child
|
||||
}
|
||||
|
||||
func (c *Compiler) errorf(node ast.Node, format string, args ...interface{}) error {
|
||||
return &Error{
|
||||
fileSet: c.file.Set(),
|
||||
node: node,
|
||||
error: fmt.Errorf(format, args...),
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Compiler) addConstant(o objects.Object) int {
|
||||
if c.parent != nil {
|
||||
// module compilers will use their parent's constants array
|
||||
|
@ -666,9 +684,15 @@ func (c *Compiler) changeOperand(opPos int, operand ...int) {
|
|||
c.replaceInstruction(opPos, inst)
|
||||
}
|
||||
|
||||
func (c *Compiler) emit(opcode Opcode, operands ...int) int {
|
||||
func (c *Compiler) emit(node ast.Node, opcode Opcode, operands ...int) int {
|
||||
filePos := source.NoPos
|
||||
if node != nil {
|
||||
filePos = node.Pos()
|
||||
}
|
||||
|
||||
inst := MakeInstruction(opcode, operands...)
|
||||
pos := c.addInstruction(inst)
|
||||
c.scopes[c.scopeIndex].sourceMap[pos] = filePos
|
||||
c.setLastInstruction(opcode, pos)
|
||||
|
||||
if c.trace != nil {
|
||||
|
|
|
@ -1,50 +1,37 @@
|
|||
package compiler
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"github.com/d5/tengo/compiler/ast"
|
||||
"github.com/d5/tengo/compiler/token"
|
||||
)
|
||||
|
||||
func (c *Compiler) compileAssign(lhs, rhs []ast.Expr, op token.Token) error {
|
||||
func (c *Compiler) compileAssign(node ast.Node, lhs, rhs []ast.Expr, op token.Token) error {
|
||||
numLHS, numRHS := len(lhs), len(rhs)
|
||||
if numLHS < numRHS {
|
||||
// # of LHS must be >= # of RHS
|
||||
return fmt.Errorf("assigntment count error: %d < %d", numLHS, numRHS)
|
||||
if numLHS > 1 || numRHS > 1 {
|
||||
return c.errorf(node, "tuple assignment not allowed")
|
||||
}
|
||||
if numLHS > 1 {
|
||||
// TODO: until we fully implement the tuple assignment
|
||||
return fmt.Errorf("tuple assignment not implemented")
|
||||
}
|
||||
//if numLHS > 1 && op != token.Assign && op != token.Define {
|
||||
// return fmt.Errorf("invalid operator for tuple assignment: %s", op.String())
|
||||
//}
|
||||
|
||||
// resolve and compile left-hand side
|
||||
ident, selectors, err := resolveAssignLHS(lhs[0])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
ident, selectors := resolveAssignLHS(lhs[0])
|
||||
numSel := len(selectors)
|
||||
|
||||
if op == token.Define && numSel > 0 {
|
||||
// using selector on new variable does not make sense
|
||||
return errors.New("cannot use selector with ':='")
|
||||
return c.errorf(node, "operator ':=' not allowed with selector")
|
||||
}
|
||||
|
||||
symbol, depth, exists := c.symbolTable.Resolve(ident)
|
||||
if op == token.Define {
|
||||
if depth == 0 && exists {
|
||||
return fmt.Errorf("'%s' redeclared in this block", ident)
|
||||
return c.errorf(node, "'%s' redeclared in this block", ident)
|
||||
}
|
||||
|
||||
symbol = c.symbolTable.Define(ident)
|
||||
} else {
|
||||
if !exists {
|
||||
return fmt.Errorf("unresolved reference '%s'", ident)
|
||||
return c.errorf(node, "unresolved reference '%s'", ident)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -64,27 +51,27 @@ func (c *Compiler) compileAssign(lhs, rhs []ast.Expr, op token.Token) error {
|
|||
|
||||
switch op {
|
||||
case token.AddAssign:
|
||||
c.emit(OpAdd)
|
||||
c.emit(node, OpAdd)
|
||||
case token.SubAssign:
|
||||
c.emit(OpSub)
|
||||
c.emit(node, OpSub)
|
||||
case token.MulAssign:
|
||||
c.emit(OpMul)
|
||||
c.emit(node, OpMul)
|
||||
case token.QuoAssign:
|
||||
c.emit(OpDiv)
|
||||
c.emit(node, OpDiv)
|
||||
case token.RemAssign:
|
||||
c.emit(OpRem)
|
||||
c.emit(node, OpRem)
|
||||
case token.AndAssign:
|
||||
c.emit(OpBAnd)
|
||||
c.emit(node, OpBAnd)
|
||||
case token.OrAssign:
|
||||
c.emit(OpBOr)
|
||||
c.emit(node, OpBOr)
|
||||
case token.AndNotAssign:
|
||||
c.emit(OpBAndNot)
|
||||
c.emit(node, OpBAndNot)
|
||||
case token.XorAssign:
|
||||
c.emit(OpBXor)
|
||||
c.emit(node, OpBXor)
|
||||
case token.ShlAssign:
|
||||
c.emit(OpBShiftLeft)
|
||||
c.emit(node, OpBShiftLeft)
|
||||
case token.ShrAssign:
|
||||
c.emit(OpBShiftRight)
|
||||
c.emit(node, OpBShiftRight)
|
||||
}
|
||||
|
||||
// compile selector expressions (right to left)
|
||||
|
@ -97,18 +84,18 @@ func (c *Compiler) compileAssign(lhs, rhs []ast.Expr, op token.Token) error {
|
|||
switch symbol.Scope {
|
||||
case ScopeGlobal:
|
||||
if numSel > 0 {
|
||||
c.emit(OpSetSelGlobal, symbol.Index, numSel)
|
||||
c.emit(node, OpSetSelGlobal, symbol.Index, numSel)
|
||||
} else {
|
||||
c.emit(OpSetGlobal, symbol.Index)
|
||||
c.emit(node, OpSetGlobal, symbol.Index)
|
||||
}
|
||||
case ScopeLocal:
|
||||
if numSel > 0 {
|
||||
c.emit(OpSetSelLocal, symbol.Index, numSel)
|
||||
c.emit(node, OpSetSelLocal, symbol.Index, numSel)
|
||||
} else {
|
||||
if op == token.Define && !symbol.LocalAssigned {
|
||||
c.emit(OpDefineLocal, symbol.Index)
|
||||
c.emit(node, OpDefineLocal, symbol.Index)
|
||||
} else {
|
||||
c.emit(OpSetLocal, symbol.Index)
|
||||
c.emit(node, OpSetLocal, symbol.Index)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -116,39 +103,30 @@ func (c *Compiler) compileAssign(lhs, rhs []ast.Expr, op token.Token) error {
|
|||
symbol.LocalAssigned = true
|
||||
case ScopeFree:
|
||||
if numSel > 0 {
|
||||
c.emit(OpSetSelFree, symbol.Index, numSel)
|
||||
c.emit(node, OpSetSelFree, symbol.Index, numSel)
|
||||
} else {
|
||||
c.emit(OpSetFree, symbol.Index)
|
||||
c.emit(node, OpSetFree, symbol.Index)
|
||||
}
|
||||
default:
|
||||
return fmt.Errorf("invalid assignment variable scope: %s", symbol.Scope)
|
||||
panic(fmt.Errorf("invalid assignment variable scope: %s", symbol.Scope))
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func resolveAssignLHS(expr ast.Expr) (name string, selectors []ast.Expr, err error) {
|
||||
func resolveAssignLHS(expr ast.Expr) (name string, selectors []ast.Expr) {
|
||||
switch term := expr.(type) {
|
||||
case *ast.SelectorExpr:
|
||||
name, selectors, err = resolveAssignLHS(term.Expr)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
name, selectors = resolveAssignLHS(term.Expr)
|
||||
selectors = append(selectors, term.Sel)
|
||||
|
||||
return
|
||||
|
||||
case *ast.IndexExpr:
|
||||
name, selectors, err = resolveAssignLHS(term.Expr)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
name, selectors = resolveAssignLHS(term.Expr)
|
||||
selectors = append(selectors, term.Index)
|
||||
|
||||
case *ast.Ident:
|
||||
return term.Name, nil, nil
|
||||
name = term.Name
|
||||
}
|
||||
|
||||
return
|
||||
|
|
17
compiler/compiler_error_report_test.go
Normal file
17
compiler/compiler_error_report_test.go
Normal file
|
@ -0,0 +1,17 @@
|
|||
package compiler_test
|
||||
|
||||
import "testing"
|
||||
|
||||
func TestCompilerErrorReport(t *testing.T) {
|
||||
expectError(t, `import("user1")`, "test:1:1: module file read error: open user1.tengo: no such file or directory")
|
||||
|
||||
expectError(t, `a = 1`, "test:1:1: unresolved reference 'a'")
|
||||
expectError(t, `a, b := 1, 2`, "test:1:1: tuple assignment not allowed")
|
||||
expectError(t, `a.b := 1`, "not allowed with selector")
|
||||
expectError(t, `a:=1; a:=3`, "test:1:7: 'a' redeclared in this block")
|
||||
|
||||
expectError(t, `return 5`, "test:1:1: return not allowed outside function")
|
||||
expectError(t, `func() { break }`, "test:1:10: break not allowed outside loop")
|
||||
expectError(t, `func() { continue }`, "test:1:10: continue not allowed outside loop")
|
||||
expectError(t, `func() { export 5 }`, "test:1:10: export not allowed inside function")
|
||||
}
|
|
@ -27,7 +27,7 @@ func (c *Compiler) compileForStmt(stmt *ast.ForStmt) error {
|
|||
return err
|
||||
}
|
||||
// condition jump position
|
||||
postCondPos = c.emit(OpJumpFalsy, 0)
|
||||
postCondPos = c.emit(stmt, OpJumpFalsy, 0)
|
||||
}
|
||||
|
||||
// enter loop
|
||||
|
@ -52,7 +52,7 @@ func (c *Compiler) compileForStmt(stmt *ast.ForStmt) error {
|
|||
}
|
||||
|
||||
// back to condition
|
||||
c.emit(OpJump, preCondPos)
|
||||
c.emit(stmt, OpJump, preCondPos)
|
||||
|
||||
// post-statement position
|
||||
postStmtPos := len(c.currentInstructions())
|
||||
|
@ -94,11 +94,11 @@ func (c *Compiler) compileForInStmt(stmt *ast.ForInStmt) error {
|
|||
if err := c.Compile(stmt.Iterable); err != nil {
|
||||
return err
|
||||
}
|
||||
c.emit(OpIteratorInit)
|
||||
c.emit(stmt, OpIteratorInit)
|
||||
if itSymbol.Scope == ScopeGlobal {
|
||||
c.emit(OpSetGlobal, itSymbol.Index)
|
||||
c.emit(stmt, OpSetGlobal, itSymbol.Index)
|
||||
} else {
|
||||
c.emit(OpDefineLocal, itSymbol.Index)
|
||||
c.emit(stmt, OpDefineLocal, itSymbol.Index)
|
||||
}
|
||||
|
||||
// pre-condition position
|
||||
|
@ -107,14 +107,14 @@ func (c *Compiler) compileForInStmt(stmt *ast.ForInStmt) error {
|
|||
// condition
|
||||
// :it.HasMore()
|
||||
if itSymbol.Scope == ScopeGlobal {
|
||||
c.emit(OpGetGlobal, itSymbol.Index)
|
||||
c.emit(stmt, OpGetGlobal, itSymbol.Index)
|
||||
} else {
|
||||
c.emit(OpGetLocal, itSymbol.Index)
|
||||
c.emit(stmt, OpGetLocal, itSymbol.Index)
|
||||
}
|
||||
c.emit(OpIteratorNext)
|
||||
c.emit(stmt, OpIteratorNext)
|
||||
|
||||
// condition jump position
|
||||
postCondPos := c.emit(OpJumpFalsy, 0)
|
||||
postCondPos := c.emit(stmt, OpJumpFalsy, 0)
|
||||
|
||||
// enter loop
|
||||
loop := c.enterLoop()
|
||||
|
@ -123,15 +123,15 @@ func (c *Compiler) compileForInStmt(stmt *ast.ForInStmt) error {
|
|||
if stmt.Key.Name != "_" {
|
||||
keySymbol := c.symbolTable.Define(stmt.Key.Name)
|
||||
if itSymbol.Scope == ScopeGlobal {
|
||||
c.emit(OpGetGlobal, itSymbol.Index)
|
||||
c.emit(stmt, OpGetGlobal, itSymbol.Index)
|
||||
} else {
|
||||
c.emit(OpGetLocal, itSymbol.Index)
|
||||
c.emit(stmt, OpGetLocal, itSymbol.Index)
|
||||
}
|
||||
c.emit(OpIteratorKey)
|
||||
c.emit(stmt, OpIteratorKey)
|
||||
if keySymbol.Scope == ScopeGlobal {
|
||||
c.emit(OpSetGlobal, keySymbol.Index)
|
||||
c.emit(stmt, OpSetGlobal, keySymbol.Index)
|
||||
} else {
|
||||
c.emit(OpDefineLocal, keySymbol.Index)
|
||||
c.emit(stmt, OpDefineLocal, keySymbol.Index)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -139,15 +139,15 @@ func (c *Compiler) compileForInStmt(stmt *ast.ForInStmt) error {
|
|||
if stmt.Value.Name != "_" {
|
||||
valueSymbol := c.symbolTable.Define(stmt.Value.Name)
|
||||
if itSymbol.Scope == ScopeGlobal {
|
||||
c.emit(OpGetGlobal, itSymbol.Index)
|
||||
c.emit(stmt, OpGetGlobal, itSymbol.Index)
|
||||
} else {
|
||||
c.emit(OpGetLocal, itSymbol.Index)
|
||||
c.emit(stmt, OpGetLocal, itSymbol.Index)
|
||||
}
|
||||
c.emit(OpIteratorValue)
|
||||
c.emit(stmt, OpIteratorValue)
|
||||
if valueSymbol.Scope == ScopeGlobal {
|
||||
c.emit(OpSetGlobal, valueSymbol.Index)
|
||||
c.emit(stmt, OpSetGlobal, valueSymbol.Index)
|
||||
} else {
|
||||
c.emit(OpDefineLocal, valueSymbol.Index)
|
||||
c.emit(stmt, OpDefineLocal, valueSymbol.Index)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -163,7 +163,7 @@ func (c *Compiler) compileForInStmt(stmt *ast.ForInStmt) error {
|
|||
postBodyPos := len(c.currentInstructions())
|
||||
|
||||
// back to condition
|
||||
c.emit(OpJump, preCondPos)
|
||||
c.emit(stmt, OpJump, preCondPos)
|
||||
|
||||
// post-statement position
|
||||
postStmtPos := len(c.currentInstructions())
|
||||
|
|
|
@ -14,9 +14,9 @@ func (c *Compiler) compileLogical(node *ast.BinaryExpr) error {
|
|||
// jump position
|
||||
var jumpPos int
|
||||
if node.Token == token.LAnd {
|
||||
jumpPos = c.emit(OpAndJump, 0)
|
||||
jumpPos = c.emit(node, OpAndJump, 0)
|
||||
} else {
|
||||
jumpPos = c.emit(OpOrJump, 0)
|
||||
jumpPos = c.emit(node, OpOrJump, 0)
|
||||
}
|
||||
|
||||
// right side term
|
||||
|
|
|
@ -1,25 +1,22 @@
|
|||
package compiler
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"strings"
|
||||
|
||||
"github.com/d5/tengo/compiler/ast"
|
||||
"github.com/d5/tengo/compiler/parser"
|
||||
"github.com/d5/tengo/compiler/source"
|
||||
"github.com/d5/tengo/objects"
|
||||
)
|
||||
|
||||
var (
|
||||
fileSet = source.NewFileSet()
|
||||
)
|
||||
|
||||
func (c *Compiler) compileModule(moduleName string) (*objects.CompiledFunction, error) {
|
||||
compiledModule, exists := c.loadCompiledModule(moduleName)
|
||||
func (c *Compiler) compileModule(expr *ast.ImportExpr) (*objects.CompiledFunction, error) {
|
||||
compiledModule, exists := c.loadCompiledModule(expr.ModuleName)
|
||||
if exists {
|
||||
return compiledModule, nil
|
||||
}
|
||||
|
||||
moduleName := expr.ModuleName
|
||||
|
||||
// read module source from loader
|
||||
var moduleSrc []byte
|
||||
if c.moduleLoader == nil {
|
||||
|
@ -28,17 +25,17 @@ func (c *Compiler) compileModule(moduleName string) (*objects.CompiledFunction,
|
|||
moduleName += ".tengo"
|
||||
}
|
||||
|
||||
if err := c.checkCyclicImports(moduleName); err != nil {
|
||||
if err := c.checkCyclicImports(expr, moduleName); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var err error
|
||||
moduleSrc, err = ioutil.ReadFile(moduleName)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, c.errorf(expr, "module file read error: %s", err.Error())
|
||||
}
|
||||
} else {
|
||||
if err := c.checkCyclicImports(moduleName); err != nil {
|
||||
if err := c.checkCyclicImports(expr, moduleName); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
|
@ -59,18 +56,19 @@ func (c *Compiler) compileModule(moduleName string) (*objects.CompiledFunction,
|
|||
return compiledModule, nil
|
||||
}
|
||||
|
||||
func (c *Compiler) checkCyclicImports(moduleName string) error {
|
||||
func (c *Compiler) checkCyclicImports(node ast.Node, moduleName string) error {
|
||||
if c.moduleName == moduleName {
|
||||
return fmt.Errorf("cyclic module import: %s", moduleName)
|
||||
return c.errorf(node, "cyclic module import: %s", moduleName)
|
||||
} else if c.parent != nil {
|
||||
return c.parent.checkCyclicImports(moduleName)
|
||||
return c.parent.checkCyclicImports(node, moduleName)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *Compiler) doCompileModule(moduleName string, src []byte) (*objects.CompiledFunction, error) {
|
||||
p := parser.NewParser(fileSet.AddFile(moduleName, -1, len(src)), src, nil)
|
||||
modFile := c.file.Set().AddFile(moduleName, -1, len(src))
|
||||
p := parser.NewParser(modFile, src, nil)
|
||||
file, err := p.ParseFile()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
@ -90,20 +88,20 @@ func (c *Compiler) doCompileModule(moduleName string, src []byte) (*objects.Comp
|
|||
symbolTable = symbolTable.Fork(false)
|
||||
|
||||
// compile module
|
||||
moduleCompiler := c.fork(moduleName, symbolTable)
|
||||
moduleCompiler := c.fork(modFile, moduleName, symbolTable)
|
||||
if err := moduleCompiler.Compile(file); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// add OpReturn (== export undefined) if export is missing
|
||||
if !moduleCompiler.lastInstructionIs(OpReturnValue) {
|
||||
moduleCompiler.emit(OpReturn)
|
||||
moduleCompiler.emit(nil, OpReturn)
|
||||
}
|
||||
|
||||
return &objects.CompiledFunction{
|
||||
Instructions: moduleCompiler.Bytecode().Instructions,
|
||||
NumLocals: symbolTable.MaxSymbols(),
|
||||
}, nil
|
||||
compiledFunc := moduleCompiler.Bytecode().MainFunction
|
||||
compiledFunc.NumLocals = symbolTable.MaxSymbols()
|
||||
|
||||
return compiledFunc, nil
|
||||
}
|
||||
|
||||
func (c *Compiler) loadCompiledModule(moduleName string) (mod *objects.CompiledFunction, ok bool) {
|
||||
|
|
|
@ -1,13 +1,19 @@
|
|||
package compiler
|
||||
|
||||
import "github.com/d5/tengo/compiler/source"
|
||||
|
||||
func (c *Compiler) currentInstructions() []byte {
|
||||
return c.scopes[c.scopeIndex].instructions
|
||||
}
|
||||
|
||||
func (c *Compiler) currentSourceMap() map[int]source.Pos {
|
||||
return c.scopes[c.scopeIndex].sourceMap
|
||||
}
|
||||
|
||||
func (c *Compiler) enterScope() {
|
||||
scope := CompilationScope{
|
||||
instructions: make([]byte, 0),
|
||||
symbolInit: make(map[string]bool),
|
||||
symbolInit: make(map[string]bool),
|
||||
sourceMap: make(map[int]source.Pos),
|
||||
}
|
||||
|
||||
c.scopes = append(c.scopes, scope)
|
||||
|
@ -20,8 +26,9 @@ func (c *Compiler) enterScope() {
|
|||
}
|
||||
}
|
||||
|
||||
func (c *Compiler) leaveScope() []byte {
|
||||
instructions := c.currentInstructions()
|
||||
func (c *Compiler) leaveScope() (instructions []byte, sourceMap map[int]source.Pos) {
|
||||
instructions = c.currentInstructions()
|
||||
sourceMap = c.currentSourceMap()
|
||||
|
||||
c.scopes = c.scopes[:len(c.scopes)-1]
|
||||
c.scopeIndex--
|
||||
|
@ -32,5 +39,5 @@ func (c *Compiler) leaveScope() []byte {
|
|||
c.printTrace("SCOPL", c.scopeIndex)
|
||||
}
|
||||
|
||||
return instructions
|
||||
return
|
||||
}
|
||||
|
|
|
@ -874,11 +874,11 @@ func() {
|
|||
intObject(1),
|
||||
intObject(1))))
|
||||
|
||||
expectError(t, `import("user1")`) // unknown module name
|
||||
expectError(t, `import("user1")`, "no such file or directory") // unknown module name
|
||||
}
|
||||
|
||||
func concat(instructions ...[]byte) []byte {
|
||||
concat := make([]byte, 0)
|
||||
var concat []byte
|
||||
for _, i := range instructions {
|
||||
concat = append(concat, i...)
|
||||
}
|
||||
|
@ -888,7 +888,8 @@ func concat(instructions ...[]byte) []byte {
|
|||
|
||||
func bytecode(instructions []byte, constants []objects.Object) *compiler.Bytecode {
|
||||
return &compiler.Bytecode{
|
||||
Instructions: instructions,
|
||||
FileSet: source.NewFileSet(),
|
||||
MainFunction: &objects.CompiledFunction{Instructions: instructions},
|
||||
Constants: constants,
|
||||
}
|
||||
}
|
||||
|
@ -913,7 +914,7 @@ func expect(t *testing.T, input string, expected *compiler.Bytecode) (ok bool) {
|
|||
return
|
||||
}
|
||||
|
||||
func expectError(t *testing.T, input string) (ok bool) {
|
||||
func expectError(t *testing.T, input, expected string) (ok bool) {
|
||||
_, trace, err := traceCompile(input, nil)
|
||||
|
||||
defer func() {
|
||||
|
@ -924,16 +925,21 @@ func expectError(t *testing.T, input string) (ok bool) {
|
|||
}
|
||||
}()
|
||||
|
||||
ok = assert.Error(t, err)
|
||||
if !assert.Error(t, err) {
|
||||
return
|
||||
}
|
||||
|
||||
if !assert.True(t, strings.Contains(err.Error(), expected), "expected error string: %s, got: %s", expected, err.Error()) {
|
||||
return
|
||||
}
|
||||
|
||||
ok = true
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func equalBytecode(t *testing.T, expected, actual *compiler.Bytecode) bool {
|
||||
expectedInstructions := strings.Join(compiler.FormatInstructions(expected.Instructions, 0), "\n")
|
||||
actualInstructions := strings.Join(compiler.FormatInstructions(actual.Instructions, 0), "\n")
|
||||
|
||||
return assert.Equal(t, expectedInstructions, actualInstructions) &&
|
||||
return assert.Equal(t, expected.MainFunction, actual.MainFunction) &&
|
||||
equalConstants(t, expected.Constants, actual.Constants)
|
||||
}
|
||||
|
||||
|
@ -975,7 +981,7 @@ func traceCompile(input string, symbols map[string]objects.Object) (res *compile
|
|||
}
|
||||
|
||||
tr := &tracer{}
|
||||
c := compiler.NewCompiler(symTable, nil, nil, tr)
|
||||
c := compiler.NewCompiler(file, symTable, nil, nil, tr)
|
||||
parsed, err := p.ParseFile()
|
||||
if err != nil {
|
||||
return
|
||||
|
|
20
compiler/error.go
Normal file
20
compiler/error.go
Normal file
|
@ -0,0 +1,20 @@
|
|||
package compiler
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/d5/tengo/compiler/ast"
|
||||
"github.com/d5/tengo/compiler/source"
|
||||
)
|
||||
|
||||
// Error represents a compiler error.
|
||||
type Error struct {
|
||||
fileSet *source.FileSet
|
||||
node ast.Node
|
||||
error error
|
||||
}
|
||||
|
||||
func (e *Error) Error() string {
|
||||
filePos := e.fileSet.Position(e.node.Pos())
|
||||
return fmt.Sprintf("%s: %s", filePos, e.error.Error())
|
||||
}
|
|
@ -1,7 +1,7 @@
|
|||
package compiler
|
||||
|
||||
// Opcode represents a single byte operation code.
|
||||
type Opcode byte
|
||||
type Opcode = byte
|
||||
|
||||
// List of opcodes
|
||||
const (
|
||||
|
@ -53,6 +53,7 @@ const (
|
|||
OpSetFree // Set free variables
|
||||
OpSetSelFree // Set free variables using selectors
|
||||
OpGetBuiltin // Get builtin function
|
||||
OpGetBuiltinModule // Get builtin module
|
||||
OpClosure // Push closure
|
||||
OpIteratorInit // Iterator init
|
||||
OpIteratorNext // Iterator next
|
||||
|
@ -107,6 +108,7 @@ var OpcodeNames = [...]string{
|
|||
OpDefineLocal: "DEFL",
|
||||
OpSetSelLocal: "SETSL",
|
||||
OpGetBuiltin: "BUILTIN",
|
||||
OpGetBuiltinModule: "BLTMOD",
|
||||
OpClosure: "CLOSURE",
|
||||
OpGetFree: "GETF",
|
||||
OpSetFree: "SETF",
|
||||
|
@ -164,6 +166,7 @@ var OpcodeOperands = [...][]int{
|
|||
OpDefineLocal: {1},
|
||||
OpSetSelLocal: {1, 1},
|
||||
OpGetBuiltin: {1},
|
||||
OpGetBuiltinModule: {},
|
||||
OpClosure: {2, 1},
|
||||
OpGetFree: {1},
|
||||
OpSetFree: {1},
|
||||
|
|
|
@ -8,9 +8,9 @@ import (
|
|||
)
|
||||
|
||||
// ParseSource parses source code 'src' and builds an AST.
|
||||
func ParseSource(src []byte, trace io.Writer) (res *ast.File, err error) {
|
||||
func ParseSource(filename string, src []byte, trace io.Writer) (res *ast.File, err error) {
|
||||
fileSet := source.NewFileSet()
|
||||
file := fileSet.AddFile("", -1, len(src))
|
||||
file := fileSet.AddFile(filename, -1, len(src))
|
||||
|
||||
return ParseFile(file, src, trace)
|
||||
}
|
||||
|
|
|
@ -1158,8 +1158,8 @@ func (p *Parser) printTrace(a ...interface{}) {
|
|||
}
|
||||
|
||||
func (p *Parser) safePos(pos source.Pos) source.Pos {
|
||||
fileBase := p.file.Base()
|
||||
fileSize := p.file.Size()
|
||||
fileBase := p.file.Base
|
||||
fileSize := p.file.Size
|
||||
|
||||
if int(pos) < fileBase || int(pos) > fileBase+fileSize {
|
||||
return source.Pos(fileBase + fileSize)
|
||||
|
|
|
@ -36,7 +36,7 @@ func (o *tracer) Write(p []byte) (n int, err error) {
|
|||
|
||||
func expect(t *testing.T, input string, fn expectedFn) (ok bool) {
|
||||
testFileSet := source.NewFileSet()
|
||||
testFile := testFileSet.AddFile("", -1, len(input))
|
||||
testFile := testFileSet.AddFile("test", -1, len(input))
|
||||
|
||||
defer func() {
|
||||
if !ok {
|
||||
|
@ -76,7 +76,7 @@ func expect(t *testing.T, input string, fn expectedFn) (ok bool) {
|
|||
|
||||
func expectError(t *testing.T, input string) (ok bool) {
|
||||
testFileSet := source.NewFileSet()
|
||||
testFile := testFileSet.AddFile("", -1, len(input))
|
||||
testFile := testFileSet.AddFile("test", -1, len(input))
|
||||
|
||||
defer func() {
|
||||
if !ok {
|
||||
|
@ -102,12 +102,12 @@ func expectString(t *testing.T, input, expected string) (ok bool) {
|
|||
if !ok {
|
||||
// print trace
|
||||
tr := &tracer{}
|
||||
_, _ = parser.ParseSource([]byte(input), tr)
|
||||
_, _ = parser.ParseSource("test", []byte(input), tr)
|
||||
t.Logf("Trace:\n%s", strings.Join(tr.out, ""))
|
||||
}
|
||||
}()
|
||||
|
||||
actual, err := parser.ParseSource([]byte(input), nil)
|
||||
actual, err := parser.ParseSource("test", []byte(input), nil)
|
||||
if !assert.NoError(t, err) {
|
||||
return
|
||||
}
|
||||
|
|
|
@ -38,8 +38,8 @@ type Scanner struct {
|
|||
|
||||
// NewScanner creates a Scanner.
|
||||
func NewScanner(file *source.File, src []byte, errorHandler ErrorHandler, mode Mode) *Scanner {
|
||||
if file.Size() != len(src) {
|
||||
panic(fmt.Sprintf("file size (%d) does not match src len (%d)", file.Size(), len(src)))
|
||||
if file.Size != len(src) {
|
||||
panic(fmt.Sprintf("file size (%d) does not match src len (%d)", file.Size, len(src)))
|
||||
}
|
||||
|
||||
s := &Scanner{
|
||||
|
|
|
@ -213,7 +213,7 @@ func TestStripCR(t *testing.T) {
|
|||
}
|
||||
|
||||
func scanExpect(t *testing.T, input string, mode scanner.Mode, expected ...scanResult) bool {
|
||||
testFile := testFileSet.AddFile("", testFileSet.Base(), len(input))
|
||||
testFile := testFileSet.AddFile("test", -1, len(input))
|
||||
|
||||
s := scanner.NewScanner(
|
||||
testFile,
|
||||
|
|
|
@ -1,50 +1,34 @@
|
|||
package source
|
||||
|
||||
import (
|
||||
"sync"
|
||||
)
|
||||
|
||||
// File represents a source file.
|
||||
type File struct {
|
||||
set *FileSet
|
||||
name string // file name as provided to AddFile
|
||||
base int // Pos value range for this file is [base...base+size]
|
||||
size int // file size as provided to AddFile
|
||||
mutex sync.Mutex
|
||||
lines []int // lines contains the offset of the first character for each line (the first entry is always 0)
|
||||
// File set for the file
|
||||
set *FileSet
|
||||
// File name as provided to AddFile
|
||||
Name string
|
||||
// Pos value range for this file is [base...base+size]
|
||||
Base int
|
||||
// File size as provided to AddFile
|
||||
Size int
|
||||
// Lines contains the offset of the first character for each line (the first entry is always 0)
|
||||
Lines []int
|
||||
}
|
||||
|
||||
// Name returns the file name.
|
||||
func (f *File) Name() string {
|
||||
return f.name
|
||||
}
|
||||
|
||||
// Base returns the base position of the file.
|
||||
func (f *File) Base() int {
|
||||
return f.base
|
||||
}
|
||||
|
||||
// Size returns the size of the file.
|
||||
func (f *File) Size() int {
|
||||
return f.size
|
||||
// Set returns FileSet.
|
||||
func (f *File) Set() *FileSet {
|
||||
return f.set
|
||||
}
|
||||
|
||||
// LineCount returns the current number of lines.
|
||||
func (f *File) LineCount() int {
|
||||
f.mutex.Lock()
|
||||
n := len(f.lines)
|
||||
f.mutex.Unlock()
|
||||
|
||||
return n
|
||||
return len(f.Lines)
|
||||
}
|
||||
|
||||
// AddLine adds a new line.
|
||||
func (f *File) AddLine(offset int) {
|
||||
f.mutex.Lock()
|
||||
if i := len(f.lines); (i == 0 || f.lines[i-1] < offset) && offset < f.size {
|
||||
f.lines = append(f.lines, offset)
|
||||
if i := len(f.Lines); (i == 0 || f.Lines[i-1] < offset) && offset < f.Size {
|
||||
f.Lines = append(f.Lines, offset)
|
||||
}
|
||||
f.mutex.Unlock()
|
||||
}
|
||||
|
||||
// LineStart returns the position of the first character in the line.
|
||||
|
@ -53,38 +37,35 @@ func (f *File) LineStart(line int) Pos {
|
|||
panic("illegal line number (line numbering starts at 1)")
|
||||
}
|
||||
|
||||
f.mutex.Lock()
|
||||
defer f.mutex.Unlock()
|
||||
|
||||
if line > len(f.lines) {
|
||||
if line > len(f.Lines) {
|
||||
panic("illegal line number")
|
||||
}
|
||||
|
||||
return Pos(f.base + f.lines[line-1])
|
||||
return Pos(f.Base + f.Lines[line-1])
|
||||
}
|
||||
|
||||
// FileSetPos returns the position in the file set.
|
||||
func (f *File) FileSetPos(offset int) Pos {
|
||||
if offset > f.size {
|
||||
if offset > f.Size {
|
||||
panic("illegal file offset")
|
||||
}
|
||||
|
||||
return Pos(f.base + offset)
|
||||
return Pos(f.Base + offset)
|
||||
}
|
||||
|
||||
// Offset translates the file set position into the file offset.
|
||||
func (f *File) Offset(p Pos) int {
|
||||
if int(p) < f.base || int(p) > f.base+f.size {
|
||||
if int(p) < f.Base || int(p) > f.Base+f.Size {
|
||||
panic("illegal Pos value")
|
||||
}
|
||||
|
||||
return int(p) - f.base
|
||||
return int(p) - f.Base
|
||||
}
|
||||
|
||||
// Position translates the file set position into the file position.
|
||||
func (f *File) Position(p Pos) (pos FilePos) {
|
||||
if p != NoPos {
|
||||
if int(p) < f.base || int(p) > f.base+f.size {
|
||||
if int(p) < f.Base || int(p) > f.Base+f.Size {
|
||||
panic("illegal Pos value")
|
||||
}
|
||||
|
||||
|
@ -95,7 +76,7 @@ func (f *File) Position(p Pos) (pos FilePos) {
|
|||
}
|
||||
|
||||
func (f *File) position(p Pos) (pos FilePos) {
|
||||
offset := int(p) - f.base
|
||||
offset := int(p) - f.Base
|
||||
pos.Offset = offset
|
||||
pos.Filename, pos.Line, pos.Column = f.unpack(offset)
|
||||
|
||||
|
@ -103,12 +84,9 @@ func (f *File) position(p Pos) (pos FilePos) {
|
|||
}
|
||||
|
||||
func (f *File) unpack(offset int) (filename string, line, column int) {
|
||||
f.mutex.Lock()
|
||||
defer f.mutex.Unlock()
|
||||
|
||||
filename = f.name
|
||||
if i := searchInts(f.lines, offset); i >= 0 {
|
||||
line, column = i+1, offset-f.lines[i]+1
|
||||
filename = f.Name
|
||||
if i := searchInts(f.Lines, offset); i >= 0 {
|
||||
line, column = i+1, offset-f.Lines[i]+1
|
||||
}
|
||||
|
||||
return
|
||||
|
|
|
@ -2,51 +2,37 @@ package source
|
|||
|
||||
import (
|
||||
"sort"
|
||||
"sync"
|
||||
)
|
||||
|
||||
// FileSet represents a set of source files.
|
||||
type FileSet struct {
|
||||
mutex sync.RWMutex // protects the file set
|
||||
base int // base offset for the next file
|
||||
files []*File // list of files in the order added to the set
|
||||
last *File // cache of last file looked up
|
||||
Base int // base offset for the next file
|
||||
Files []*File // list of files in the order added to the set
|
||||
LastFile *File // cache of last file looked up
|
||||
}
|
||||
|
||||
// NewFileSet creates a new file set.
|
||||
func NewFileSet() *FileSet {
|
||||
return &FileSet{
|
||||
base: 1, // 0 == NoPos
|
||||
Base: 1, // 0 == NoPos
|
||||
}
|
||||
}
|
||||
|
||||
// Base returns the current base position of the file set.
|
||||
func (s *FileSet) Base() int {
|
||||
s.mutex.RLock()
|
||||
b := s.base
|
||||
s.mutex.RUnlock()
|
||||
|
||||
return b
|
||||
}
|
||||
|
||||
// AddFile adds a new file in the file set.
|
||||
func (s *FileSet) AddFile(filename string, base, size int) *File {
|
||||
s.mutex.Lock()
|
||||
defer s.mutex.Unlock()
|
||||
|
||||
if base < 0 {
|
||||
base = s.base
|
||||
base = s.Base
|
||||
}
|
||||
if base < s.base || size < 0 {
|
||||
if base < s.Base || size < 0 {
|
||||
panic("illegal base or size")
|
||||
}
|
||||
|
||||
f := &File{
|
||||
set: s,
|
||||
name: filename,
|
||||
base: base,
|
||||
size: size,
|
||||
lines: []int{0},
|
||||
Name: filename,
|
||||
Base: base,
|
||||
Size: size,
|
||||
Lines: []int{0},
|
||||
}
|
||||
|
||||
base += size + 1 // +1 because EOF also has a position
|
||||
|
@ -55,9 +41,9 @@ func (s *FileSet) AddFile(filename string, base, size int) *File {
|
|||
}
|
||||
|
||||
// add the file to the file set
|
||||
s.base = base
|
||||
s.files = append(s.files, f)
|
||||
s.last = f
|
||||
s.Base = base
|
||||
s.Files = append(s.Files, f)
|
||||
s.LastFile = f
|
||||
|
||||
return f
|
||||
}
|
||||
|
@ -86,32 +72,25 @@ func (s *FileSet) Position(p Pos) (pos FilePos) {
|
|||
}
|
||||
|
||||
func (s *FileSet) file(p Pos) *File {
|
||||
s.mutex.RLock()
|
||||
|
||||
// common case: p is in last file
|
||||
if f := s.last; f != nil && f.base <= int(p) && int(p) <= f.base+f.size {
|
||||
s.mutex.RUnlock()
|
||||
if f := s.LastFile; f != nil && f.Base <= int(p) && int(p) <= f.Base+f.Size {
|
||||
return f
|
||||
}
|
||||
|
||||
// p is not in last file - search all files
|
||||
if i := searchFiles(s.files, int(p)); i >= 0 {
|
||||
f := s.files[i]
|
||||
if i := searchFiles(s.Files, int(p)); i >= 0 {
|
||||
f := s.Files[i]
|
||||
|
||||
// f.base <= int(p) by definition of searchFiles
|
||||
if int(p) <= f.base+f.size {
|
||||
s.mutex.RUnlock()
|
||||
s.mutex.Lock()
|
||||
s.last = f // race is ok - s.last is only a cache
|
||||
s.mutex.Unlock()
|
||||
if int(p) <= f.Base+f.Size {
|
||||
s.LastFile = f // race is ok - s.last is only a cache
|
||||
return f
|
||||
}
|
||||
}
|
||||
|
||||
s.mutex.RUnlock()
|
||||
return nil
|
||||
}
|
||||
|
||||
func searchFiles(a []*File, x int) int {
|
||||
return sort.Search(len(a), func(i int) bool { return a[i].base > x }) - 1
|
||||
return sort.Search(len(a), func(i int) bool { return a[i].Base > x }) - 1
|
||||
}
|
||||
|
|
|
@ -1,930 +0,0 @@
|
|||
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
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// FuncARI64 transform a function of 'func() int64' signature
|
||||
// into a user function object.
|
||||
func FuncARI64(fn func() int64) *objects.UserFunction {
|
||||
return &objects.UserFunction{
|
||||
Value: func(args ...objects.Object) (ret objects.Object, err error) {
|
||||
if len(args) != 0 {
|
||||
return nil, objects.ErrWrongNumArguments
|
||||
}
|
||||
|
||||
return &objects.Int{Value: fn()}, nil
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// FuncAI64RI64 transform a function of 'func(int64) int64' signature
|
||||
// into a user function object.
|
||||
func FuncAI64RI64(fn func(int64) int64) *objects.UserFunction {
|
||||
return &objects.UserFunction{
|
||||
Value: func(args ...objects.Object) (ret objects.Object, err error) {
|
||||
if len(args) != 1 {
|
||||
return nil, objects.ErrWrongNumArguments
|
||||
}
|
||||
|
||||
i1, ok := objects.ToInt64(args[0])
|
||||
if !ok {
|
||||
return nil, objects.ErrInvalidTypeConversion
|
||||
}
|
||||
|
||||
return &objects.Int{Value: fn(i1)}, nil
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// FuncAI64R transform a function of 'func(int64)' signature
|
||||
// into a user function object.
|
||||
func FuncAI64R(fn func(int64)) *objects.UserFunction {
|
||||
return &objects.UserFunction{
|
||||
Value: func(args ...objects.Object) (ret objects.Object, err error) {
|
||||
if len(args) != 1 {
|
||||
return nil, objects.ErrWrongNumArguments
|
||||
}
|
||||
|
||||
i1, ok := objects.ToInt64(args[0])
|
||||
if !ok {
|
||||
return nil, objects.ErrInvalidTypeConversion
|
||||
}
|
||||
|
||||
fn(i1)
|
||||
|
||||
return objects.UndefinedValue, nil
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// FuncARB transform a function of 'func() bool' signature
|
||||
// into a user function object.
|
||||
func FuncARB(fn func() bool) *objects.UserFunction {
|
||||
return &objects.UserFunction{
|
||||
Value: func(args ...objects.Object) (ret objects.Object, err error) {
|
||||
if len(args) != 0 {
|
||||
return nil, objects.ErrWrongNumArguments
|
||||
}
|
||||
|
||||
if fn() {
|
||||
return objects.TrueValue, nil
|
||||
}
|
||||
|
||||
return objects.FalseValue, 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
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// FuncAIRIs transform a function of 'func(int) []int' signature
|
||||
// into a user function object.
|
||||
func FuncAIRIs(fn func(int) []int) *objects.UserFunction {
|
||||
return &objects.UserFunction{
|
||||
Value: func(args ...objects.Object) (ret objects.Object, err error) {
|
||||
if len(args) != 1 {
|
||||
return nil, objects.ErrWrongNumArguments
|
||||
}
|
||||
|
||||
i1, ok := objects.ToInt(args[0])
|
||||
if !ok {
|
||||
return nil, objects.ErrInvalidTypeConversion
|
||||
}
|
||||
|
||||
res := fn(i1)
|
||||
|
||||
arr := &objects.Array{}
|
||||
for _, v := range res {
|
||||
arr.Value = append(arr.Value, &objects.Int{Value: int64(v)})
|
||||
}
|
||||
|
||||
return arr, nil
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// FuncAFRF transform a function of 'func(float64) float64' signature
|
||||
// into a user function object.
|
||||
func FuncAFRF(fn func(float64) float64) *objects.UserFunction {
|
||||
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
|
||||
}
|
||||
|
||||
if fn(f1, i2) {
|
||||
return objects.TrueValue, nil
|
||||
}
|
||||
|
||||
return objects.FalseValue, 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
|
||||
}
|
||||
|
||||
if fn(f1) {
|
||||
return objects.TrueValue, nil
|
||||
}
|
||||
|
||||
return objects.FalseValue, 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
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// FuncASRSs transform a function of 'func(string) []string' signature into a user function object.
|
||||
func FuncASRSs(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
|
||||
}
|
||||
|
||||
res := fn(s1)
|
||||
|
||||
arr := &objects.Array{}
|
||||
for _, osArg := range res {
|
||||
arr.Value = append(arr.Value, &objects.String{Value: osArg})
|
||||
}
|
||||
|
||||
return arr, 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
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// FuncASSRSs transform a function of 'func(string, string) []string' signature into a user function object.
|
||||
func FuncASSRSs(fn func(string, string) []string) *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
|
||||
}
|
||||
|
||||
arr := &objects.Array{}
|
||||
for _, res := range fn(s1, s2) {
|
||||
arr.Value = append(arr.Value, &objects.String{Value: res})
|
||||
}
|
||||
|
||||
return arr, nil
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// FuncASSIRSs transform a function of 'func(string, string, int) []string' signature into a user function object.
|
||||
func FuncASSIRSs(fn func(string, string, int) []string) *objects.UserFunction {
|
||||
return &objects.UserFunction{
|
||||
Value: func(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
|
||||
}
|
||||
|
||||
s2, ok := objects.ToString(args[1])
|
||||
if !ok {
|
||||
return nil, objects.ErrInvalidTypeConversion
|
||||
}
|
||||
|
||||
i3, ok := objects.ToInt(args[2])
|
||||
if !ok {
|
||||
return nil, objects.ErrInvalidTypeConversion
|
||||
}
|
||||
|
||||
arr := &objects.Array{}
|
||||
for _, res := range fn(s1, s2, i3) {
|
||||
arr.Value = append(arr.Value, &objects.String{Value: res})
|
||||
}
|
||||
|
||||
return arr, nil
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// FuncASSRI transform a function of 'func(string, string) int' signature into a user function object.
|
||||
func FuncASSRI(fn func(string, string) int) *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 &objects.Int{Value: int64(fn(s1, s2))}, nil
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// FuncASSRS transform a function of 'func(string, string) string' signature into a user function object.
|
||||
func FuncASSRS(fn func(string, string) string) *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 &objects.String{Value: fn(s1, s2)}, nil
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// FuncASSRB transform a function of 'func(string, string) bool' signature into a user function object.
|
||||
func FuncASSRB(fn func(string, string) bool) *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
|
||||
}
|
||||
|
||||
if fn(s1, s2) {
|
||||
return objects.TrueValue, nil
|
||||
}
|
||||
|
||||
return objects.FalseValue, nil
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// FuncASsSRS transform a function of 'func([]string, string) string' signature into a user function object.
|
||||
func FuncASsSRS(fn func([]string, string) string) *objects.UserFunction {
|
||||
return &objects.UserFunction{
|
||||
Value: func(args ...objects.Object) (objects.Object, error) {
|
||||
if len(args) != 2 {
|
||||
return nil, objects.ErrWrongNumArguments
|
||||
}
|
||||
|
||||
var ss1 []string
|
||||
arr, ok := args[0].(*objects.Array)
|
||||
if !ok {
|
||||
return nil, objects.ErrInvalidTypeConversion
|
||||
}
|
||||
for _, a := range arr.Value {
|
||||
as, ok := objects.ToString(a)
|
||||
if !ok {
|
||||
return nil, objects.ErrInvalidTypeConversion
|
||||
}
|
||||
ss1 = append(ss1, as)
|
||||
}
|
||||
|
||||
s2, ok := objects.ToString(args[1])
|
||||
if !ok {
|
||||
return nil, objects.ErrInvalidTypeConversion
|
||||
}
|
||||
|
||||
return &objects.String{Value: fn(ss1, 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
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// FuncASIRS transform a function of 'func(string, int) string' signature
|
||||
// into a user function object.
|
||||
func FuncASIRS(fn func(string, int) string) *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.ToInt(args[1])
|
||||
if !ok {
|
||||
return nil, objects.ErrInvalidTypeConversion
|
||||
}
|
||||
|
||||
return &objects.String{Value: fn(s1, 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 _, r := range res {
|
||||
arr.Value = append(arr.Value, &objects.String{Value: r})
|
||||
}
|
||||
|
||||
return arr, nil
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// FuncAIRS transform a function of 'func(int) string' signature
|
||||
// into a user function object.
|
||||
func FuncAIRS(fn func(int) string) *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.String{Value: fn(i1)}, nil
|
||||
},
|
||||
}
|
||||
}
|
|
@ -1,74 +0,0 @@
|
|||
package stdlib
|
||||
|
||||
import (
|
||||
"math"
|
||||
|
||||
"github.com/d5/tengo/objects"
|
||||
)
|
||||
|
||||
var mathModule = map[string]objects.Object{
|
||||
"e": &objects.Float{Value: math.E},
|
||||
"pi": &objects.Float{Value: math.Pi},
|
||||
"phi": &objects.Float{Value: math.Phi},
|
||||
"sqrt2": &objects.Float{Value: math.Sqrt2},
|
||||
"sqrtE": &objects.Float{Value: math.SqrtE},
|
||||
"sqrtPi": &objects.Float{Value: math.SqrtPi},
|
||||
"sqrtPhi": &objects.Float{Value: math.SqrtPhi},
|
||||
"ln2": &objects.Float{Value: math.Ln2},
|
||||
"log2E": &objects.Float{Value: math.Log2E},
|
||||
"ln10": &objects.Float{Value: math.Ln10},
|
||||
"log10E": &objects.Float{Value: math.Log10E},
|
||||
"abs": FuncAFRF(math.Abs),
|
||||
"acos": FuncAFRF(math.Acos),
|
||||
"acosh": FuncAFRF(math.Acosh),
|
||||
"asin": FuncAFRF(math.Asin),
|
||||
"asinh": FuncAFRF(math.Asinh),
|
||||
"atan": FuncAFRF(math.Atan),
|
||||
"atan2": FuncAFFRF(math.Atan2),
|
||||
"atanh": FuncAFRF(math.Atanh),
|
||||
"cbrt": FuncAFRF(math.Cbrt),
|
||||
"ceil": FuncAFRF(math.Ceil),
|
||||
"copysign": FuncAFFRF(math.Copysign),
|
||||
"cos": FuncAFRF(math.Cos),
|
||||
"cosh": FuncAFRF(math.Cosh),
|
||||
"dim": FuncAFFRF(math.Dim),
|
||||
"erf": FuncAFRF(math.Erf),
|
||||
"erfc": FuncAFRF(math.Erfc),
|
||||
"exp": FuncAFRF(math.Exp),
|
||||
"exp2": FuncAFRF(math.Exp2),
|
||||
"expm1": FuncAFRF(math.Expm1),
|
||||
"floor": FuncAFRF(math.Floor),
|
||||
"gamma": FuncAFRF(math.Gamma),
|
||||
"hypot": FuncAFFRF(math.Hypot),
|
||||
"ilogb": FuncAFRI(math.Ilogb),
|
||||
"inf": FuncAIRF(math.Inf),
|
||||
"is_inf": FuncAFIRB(math.IsInf),
|
||||
"is_nan": FuncAFRB(math.IsNaN),
|
||||
"j0": FuncAFRF(math.J0),
|
||||
"j1": FuncAFRF(math.J1),
|
||||
"jn": FuncAIFRF(math.Jn),
|
||||
"ldexp": FuncAFIRF(math.Ldexp),
|
||||
"log": FuncAFRF(math.Log),
|
||||
"log10": FuncAFRF(math.Log10),
|
||||
"log1p": FuncAFRF(math.Log1p),
|
||||
"log2": FuncAFRF(math.Log2),
|
||||
"logb": FuncAFRF(math.Logb),
|
||||
"max": FuncAFFRF(math.Max),
|
||||
"min": FuncAFFRF(math.Min),
|
||||
"mod": FuncAFFRF(math.Mod),
|
||||
"nan": FuncARF(math.NaN),
|
||||
"nextafter": FuncAFFRF(math.Nextafter),
|
||||
"pow": FuncAFFRF(math.Pow),
|
||||
"pow10": FuncAIRF(math.Pow10),
|
||||
"remainder": FuncAFFRF(math.Remainder),
|
||||
"signbit": FuncAFRB(math.Signbit),
|
||||
"sin": FuncAFRF(math.Sin),
|
||||
"sinh": FuncAFRF(math.Sinh),
|
||||
"sqrt": FuncAFRF(math.Sqrt),
|
||||
"tan": FuncAFRF(math.Tan),
|
||||
"tanh": FuncAFRF(math.Tanh),
|
||||
"trunc": FuncAFRF(math.Trunc),
|
||||
"y0": FuncAFRF(math.Y0),
|
||||
"y1": FuncAFRF(math.Y1),
|
||||
"yn": FuncAIFRF(math.Yn),
|
||||
}
|
|
@ -1,346 +0,0 @@
|
|||
package stdlib
|
||||
|
||||
import (
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"os/exec"
|
||||
|
||||
"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_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(io.SeekStart)},
|
||||
"seek_cur": &objects.Int{Value: int64(io.SeekCurrent)},
|
||||
"seek_end": &objects.Int{Value: int64(io.SeekEnd)},
|
||||
"args": &objects.UserFunction{Value: osArgs}, // args() => array(string)
|
||||
"chdir": FuncASRE(os.Chdir), // chdir(dir string) => error
|
||||
"chmod": osFuncASFmRE(os.Chmod), // chmod(name string, mode int) => error
|
||||
"chown": FuncASIIRE(os.Chown), // chown(name string, uid int, gid int) => error
|
||||
"clearenv": FuncAR(os.Clearenv), // clearenv()
|
||||
"environ": FuncARSs(os.Environ), // environ() => array(string)
|
||||
"exit": FuncAIR(os.Exit), // exit(code int)
|
||||
"expand_env": FuncASRS(os.ExpandEnv), // expand_env(s string) => string
|
||||
"getegid": FuncARI(os.Getegid), // getegid() => int
|
||||
"getenv": FuncASRS(os.Getenv), // getenv(s string) => string
|
||||
"geteuid": FuncARI(os.Geteuid), // geteuid() => int
|
||||
"getgid": FuncARI(os.Getgid), // getgid() => int
|
||||
"getgroups": FuncARIsE(os.Getgroups), // getgroups() => array(string)/error
|
||||
"getpagesize": FuncARI(os.Getpagesize), // getpagesize() => int
|
||||
"getpid": FuncARI(os.Getpid), // getpid() => int
|
||||
"getppid": FuncARI(os.Getppid), // getppid() => int
|
||||
"getuid": FuncARI(os.Getuid), // getuid() => int
|
||||
"getwd": FuncARSE(os.Getwd), // getwd() => string/error
|
||||
"hostname": FuncARSE(os.Hostname), // hostname() => string/error
|
||||
"lchown": FuncASIIRE(os.Lchown), // lchown(name string, uid int, gid int) => error
|
||||
"link": FuncASSRE(os.Link), // link(oldname string, newname string) => error
|
||||
"lookup_env": &objects.UserFunction{Value: osLookupEnv}, // lookup_env(key string) => string/false
|
||||
"mkdir": osFuncASFmRE(os.Mkdir), // mkdir(name string, perm int) => error
|
||||
"mkdir_all": osFuncASFmRE(os.MkdirAll), // mkdir_all(name string, perm int) => error
|
||||
"readlink": FuncASRSE(os.Readlink), // readlink(name string) => string/error
|
||||
"remove": FuncASRE(os.Remove), // remove(name string) => error
|
||||
"remove_all": FuncASRE(os.RemoveAll), // remove_all(name string) => error
|
||||
"rename": FuncASSRE(os.Rename), // rename(oldpath string, newpath string) => error
|
||||
"setenv": FuncASSRE(os.Setenv), // setenv(key string, value string) => error
|
||||
"symlink": FuncASSRE(os.Symlink), // symlink(oldname string newname string) => error
|
||||
"temp_dir": FuncARS(os.TempDir), // temp_dir() => string
|
||||
"truncate": FuncASI64RE(os.Truncate), // truncate(name string, size int) => error
|
||||
"unsetenv": FuncASRE(os.Unsetenv), // unsetenv(key string) => error
|
||||
"create": &objects.UserFunction{Value: osCreate}, // create(name string) => imap(file)/error
|
||||
"open": &objects.UserFunction{Value: osOpen}, // open(name string) => imap(file)/error
|
||||
"open_file": &objects.UserFunction{Value: osOpenFile}, // open_file(name string, flag int, perm int) => imap(file)/error
|
||||
"find_process": &objects.UserFunction{Value: osFindProcess}, // find_process(pid int) => imap(process)/error
|
||||
"start_process": &objects.UserFunction{Value: osStartProcess}, // start_process(name string, argv array(string), dir string, env array(string)) => imap(process)/error
|
||||
"exec_look_path": FuncASRSE(exec.LookPath), // exec_look_path(file) => string/error
|
||||
"exec": &objects.UserFunction{Value: osExec}, // exec(name, args...) => command
|
||||
"stat": &objects.UserFunction{Value: osStat}, // stat(name) => imap(fileinfo)/error
|
||||
"read_file": &objects.UserFunction{Value: osReadFile}, // readfile(name) => array(byte)/error
|
||||
}
|
||||
|
||||
func osReadFile(args ...objects.Object) (ret objects.Object, err error) {
|
||||
if len(args) != 1 {
|
||||
return nil, objects.ErrWrongNumArguments
|
||||
}
|
||||
|
||||
fname, ok := objects.ToString(args[0])
|
||||
if !ok {
|
||||
return nil, objects.ErrInvalidTypeConversion
|
||||
}
|
||||
|
||||
bytes, err := ioutil.ReadFile(fname)
|
||||
if err != nil {
|
||||
return wrapError(err), nil
|
||||
}
|
||||
|
||||
return &objects.Bytes{Value: bytes}, nil
|
||||
}
|
||||
|
||||
func osStat(args ...objects.Object) (ret objects.Object, err error) {
|
||||
if len(args) != 1 {
|
||||
return nil, objects.ErrWrongNumArguments
|
||||
}
|
||||
|
||||
fname, ok := objects.ToString(args[0])
|
||||
if !ok {
|
||||
return nil, objects.ErrInvalidTypeConversion
|
||||
}
|
||||
|
||||
stat, err := os.Stat(fname)
|
||||
if err != nil {
|
||||
return wrapError(err), nil
|
||||
}
|
||||
|
||||
fstat := &objects.ImmutableMap{
|
||||
Value: map[string]objects.Object{
|
||||
"name": &objects.String{Value: stat.Name()},
|
||||
"mtime": &objects.Time{Value: stat.ModTime()},
|
||||
"size": &objects.Int{Value: stat.Size()},
|
||||
"mode": &objects.Int{Value: int64(stat.Mode())},
|
||||
},
|
||||
}
|
||||
|
||||
if stat.IsDir() {
|
||||
fstat.Value["directory"] = objects.TrueValue
|
||||
} else {
|
||||
fstat.Value["directory"] = objects.FalseValue
|
||||
}
|
||||
|
||||
return fstat, 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 makeOSFile(res), nil
|
||||
}
|
||||
|
||||
func osOpen(args ...objects.Object) (objects.Object, error) {
|
||||
if len(args) != 1 {
|
||||
return nil, objects.ErrWrongNumArguments
|
||||
}
|
||||
|
||||
s1, ok := objects.ToString(args[0])
|
||||
if !ok {
|
||||
return nil, objects.ErrInvalidTypeConversion
|
||||
}
|
||||
|
||||
res, err := os.Open(s1)
|
||||
if err != nil {
|
||||
return wrapError(err), nil
|
||||
}
|
||||
|
||||
return makeOSFile(res), nil
|
||||
}
|
||||
|
||||
func osOpenFile(args ...objects.Object) (objects.Object, error) {
|
||||
if len(args) != 3 {
|
||||
return nil, objects.ErrWrongNumArguments
|
||||
}
|
||||
|
||||
s1, ok := objects.ToString(args[0])
|
||||
if !ok {
|
||||
return nil, objects.ErrInvalidTypeConversion
|
||||
}
|
||||
|
||||
i2, ok := objects.ToInt(args[1])
|
||||
if !ok {
|
||||
return nil, objects.ErrInvalidTypeConversion
|
||||
}
|
||||
|
||||
i3, ok := objects.ToInt(args[2])
|
||||
if !ok {
|
||||
return nil, objects.ErrInvalidTypeConversion
|
||||
}
|
||||
|
||||
res, err := os.OpenFile(s1, i2, os.FileMode(i3))
|
||||
if err != nil {
|
||||
return wrapError(err), nil
|
||||
}
|
||||
|
||||
return makeOSFile(res), nil
|
||||
}
|
||||
|
||||
func osArgs(args ...objects.Object) (objects.Object, error) {
|
||||
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 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
|
||||
}
|
||||
|
||||
func osExec(args ...objects.Object) (objects.Object, error) {
|
||||
if len(args) == 0 {
|
||||
return nil, objects.ErrWrongNumArguments
|
||||
}
|
||||
|
||||
name, ok := objects.ToString(args[0])
|
||||
if !ok {
|
||||
return nil, objects.ErrInvalidTypeConversion
|
||||
}
|
||||
|
||||
var execArgs []string
|
||||
for _, arg := range args[1:] {
|
||||
execArg, ok := objects.ToString(arg)
|
||||
if !ok {
|
||||
return nil, objects.ErrInvalidTypeConversion
|
||||
}
|
||||
|
||||
execArgs = append(execArgs, execArg)
|
||||
}
|
||||
|
||||
return makeOSExecCommand(exec.Command(name, execArgs...)), nil
|
||||
}
|
||||
|
||||
func osFindProcess(args ...objects.Object) (objects.Object, error) {
|
||||
if len(args) != 1 {
|
||||
return nil, objects.ErrWrongNumArguments
|
||||
}
|
||||
|
||||
i1, ok := objects.ToInt(args[0])
|
||||
if !ok {
|
||||
return nil, objects.ErrInvalidTypeConversion
|
||||
}
|
||||
|
||||
proc, err := os.FindProcess(i1)
|
||||
if err != nil {
|
||||
return wrapError(err), nil
|
||||
}
|
||||
|
||||
return makeOSProcess(proc), nil
|
||||
}
|
||||
|
||||
func osStartProcess(args ...objects.Object) (objects.Object, error) {
|
||||
if len(args) != 4 {
|
||||
return nil, objects.ErrWrongNumArguments
|
||||
}
|
||||
|
||||
name, ok := objects.ToString(args[0])
|
||||
if !ok {
|
||||
return nil, objects.ErrInvalidTypeConversion
|
||||
}
|
||||
|
||||
argv, err := stringArray(args[1])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
dir, ok := objects.ToString(args[2])
|
||||
if !ok {
|
||||
return nil, objects.ErrInvalidTypeConversion
|
||||
}
|
||||
|
||||
env, err := stringArray(args[3])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
proc, err := os.StartProcess(name, argv, &os.ProcAttr{
|
||||
Dir: dir,
|
||||
Env: env,
|
||||
})
|
||||
if err != nil {
|
||||
return wrapError(err), nil
|
||||
}
|
||||
|
||||
return makeOSProcess(proc), nil
|
||||
}
|
||||
|
||||
func stringArray(o objects.Object) ([]string, error) {
|
||||
arr, ok := o.(*objects.Array)
|
||||
if !ok {
|
||||
return nil, objects.ErrInvalidTypeConversion
|
||||
}
|
||||
|
||||
var sarr []string
|
||||
for _, elem := range arr.Value {
|
||||
str, ok := elem.(*objects.String)
|
||||
if !ok {
|
||||
return nil, objects.ErrInvalidTypeConversion
|
||||
}
|
||||
|
||||
sarr = append(sarr, str.Value)
|
||||
}
|
||||
|
||||
return sarr, nil
|
||||
}
|
|
@ -1,87 +0,0 @@
|
|||
package stdlib
|
||||
|
||||
import (
|
||||
"math/rand"
|
||||
|
||||
"github.com/d5/tengo/objects"
|
||||
)
|
||||
|
||||
var randModule = map[string]objects.Object{
|
||||
"int": FuncARI64(rand.Int63),
|
||||
"float": FuncARF(rand.Float64),
|
||||
"intn": FuncAI64RI64(rand.Int63n),
|
||||
"exp_float": FuncARF(rand.ExpFloat64),
|
||||
"norm_float": FuncARF(rand.NormFloat64),
|
||||
"perm": FuncAIRIs(rand.Perm),
|
||||
"seed": FuncAI64R(rand.Seed),
|
||||
"read": &objects.UserFunction{
|
||||
Value: func(args ...objects.Object) (ret objects.Object, err error) {
|
||||
if len(args) != 1 {
|
||||
return nil, objects.ErrWrongNumArguments
|
||||
}
|
||||
|
||||
y1, ok := args[0].(*objects.Bytes)
|
||||
if !ok {
|
||||
return nil, objects.ErrInvalidTypeConversion
|
||||
}
|
||||
|
||||
res, err := rand.Read(y1.Value)
|
||||
if err != nil {
|
||||
ret = wrapError(err)
|
||||
return
|
||||
}
|
||||
|
||||
return &objects.Int{Value: int64(res)}, nil
|
||||
},
|
||||
},
|
||||
"rand": &objects.UserFunction{
|
||||
Value: func(args ...objects.Object) (ret objects.Object, err error) {
|
||||
if len(args) != 1 {
|
||||
return nil, objects.ErrWrongNumArguments
|
||||
}
|
||||
|
||||
i1, ok := objects.ToInt64(args[0])
|
||||
if !ok {
|
||||
return nil, objects.ErrInvalidTypeConversion
|
||||
}
|
||||
|
||||
src := rand.NewSource(i1)
|
||||
|
||||
return randRand(rand.New(src)), nil
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
func randRand(r *rand.Rand) *objects.ImmutableMap {
|
||||
return &objects.ImmutableMap{
|
||||
Value: map[string]objects.Object{
|
||||
"int": FuncARI64(r.Int63),
|
||||
"float": FuncARF(r.Float64),
|
||||
"intn": FuncAI64RI64(r.Int63n),
|
||||
"exp_float": FuncARF(r.ExpFloat64),
|
||||
"norm_float": FuncARF(r.NormFloat64),
|
||||
"perm": FuncAIRIs(r.Perm),
|
||||
"seed": FuncAI64R(r.Seed),
|
||||
"read": &objects.UserFunction{
|
||||
Value: func(args ...objects.Object) (ret objects.Object, err error) {
|
||||
if len(args) != 1 {
|
||||
return nil, objects.ErrWrongNumArguments
|
||||
}
|
||||
|
||||
y1, ok := args[0].(*objects.Bytes)
|
||||
if !ok {
|
||||
return nil, objects.ErrInvalidTypeConversion
|
||||
}
|
||||
|
||||
res, err := r.Read(y1.Value)
|
||||
if err != nil {
|
||||
ret = wrapError(err)
|
||||
return
|
||||
}
|
||||
|
||||
return &objects.Int{Value: int64(res)}, nil
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
|
@ -1,12 +0,0 @@
|
|||
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},
|
||||
"text": {Value: textModule},
|
||||
"times": {Value: timesModule},
|
||||
"rand": {Value: randModule},
|
||||
}
|
|
@ -1,469 +0,0 @@
|
|||
package stdlib
|
||||
|
||||
import (
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/d5/tengo/objects"
|
||||
)
|
||||
|
||||
var textModule = map[string]objects.Object{
|
||||
"re_match": &objects.UserFunction{Value: textREMatch}, // re_match(pattern, text) => bool/error
|
||||
"re_find": &objects.UserFunction{Value: textREFind}, // re_find(pattern, text, count) => [[{text:,begin:,end:}]]/undefined
|
||||
"re_replace": &objects.UserFunction{Value: textREReplace}, // re_replace(pattern, text, repl) => string/error
|
||||
"re_split": &objects.UserFunction{Value: textRESplit}, // re_split(pattern, text, count) => [string]/error
|
||||
"re_compile": &objects.UserFunction{Value: textRECompile}, // re_compile(pattern) => Regexp/error
|
||||
"compare": FuncASSRI(strings.Compare), // compare(a, b) => int
|
||||
"contains": FuncASSRB(strings.Contains), // contains(s, substr) => bool
|
||||
"contains_any": FuncASSRB(strings.ContainsAny), // contains_any(s, chars) => bool
|
||||
"count": FuncASSRI(strings.Count), // count(s, substr) => int
|
||||
"equal_fold": FuncASSRB(strings.EqualFold), // "equal_fold(s, t) => bool
|
||||
"fields": FuncASRSs(strings.Fields), // fields(s) => [string]
|
||||
"has_prefix": FuncASSRB(strings.HasPrefix), // has_prefix(s, prefix) => bool
|
||||
"has_suffix": FuncASSRB(strings.HasSuffix), // has_suffix(s, suffix) => bool
|
||||
"index": FuncASSRI(strings.Index), // index(s, substr) => int
|
||||
"index_any": FuncASSRI(strings.IndexAny), // index_any(s, chars) => int
|
||||
"join": FuncASsSRS(strings.Join), // join(arr, sep) => string
|
||||
"last_index": FuncASSRI(strings.LastIndex), // last_index(s, substr) => int
|
||||
"last_index_any": FuncASSRI(strings.LastIndexAny), // last_index_any(s, chars) => int
|
||||
"repeat": FuncASIRS(strings.Repeat), // repeat(s, count) => string
|
||||
"replace": &objects.UserFunction{Value: textReplace}, // replace(s, old, new, n) => string
|
||||
"split": FuncASSRSs(strings.Split), // split(s, sep) => [string]
|
||||
"split_after": FuncASSRSs(strings.SplitAfter), // split_after(s, sep) => [string]
|
||||
"split_after_n": FuncASSIRSs(strings.SplitAfterN), // split_after_n(s, sep, n) => [string]
|
||||
"split_n": FuncASSIRSs(strings.SplitN), // split_n(s, sep, n) => [string]
|
||||
"title": FuncASRS(strings.Title), // title(s) => string
|
||||
"to_lower": FuncASRS(strings.ToLower), // to_lower(s) => string
|
||||
"to_title": FuncASRS(strings.ToTitle), // to_title(s) => string
|
||||
"to_upper": FuncASRS(strings.ToUpper), // to_upper(s) => string
|
||||
"trim_left": FuncASSRS(strings.TrimLeft), // trim_left(s, cutset) => string
|
||||
"trim_prefix": FuncASSRS(strings.TrimPrefix), // trim_prefix(s, prefix) => string
|
||||
"trim_right": FuncASSRS(strings.TrimRight), // trim_right(s, cutset) => string
|
||||
"trim_space": FuncASRS(strings.TrimSpace), // trim_space(s) => string
|
||||
"trim_suffix": FuncASSRS(strings.TrimSuffix), // trim_suffix(s, suffix) => string
|
||||
"atoi": FuncASRIE(strconv.Atoi), // atoi(str) => int/error
|
||||
"format_bool": &objects.UserFunction{Value: textFormatBool}, // format_bool(b) => string
|
||||
"format_float": &objects.UserFunction{Value: textFormatFloat}, // format_float(f, fmt, prec, bits) => string
|
||||
"format_int": &objects.UserFunction{Value: textFormatInt}, // format_int(i, base) => string
|
||||
"itoa": FuncAIRS(strconv.Itoa), // itoa(i) => string
|
||||
"parse_bool": &objects.UserFunction{Value: textParseBool}, // parse_bool(str) => bool/error
|
||||
"parse_float": &objects.UserFunction{Value: textParseFloat}, // parse_float(str, bits) => float/error
|
||||
"parse_int": &objects.UserFunction{Value: textParseInt}, // parse_int(str, base, bits) => int/error
|
||||
"quote": FuncASRS(strconv.Quote), // quote(str) => string
|
||||
"unquote": FuncASRSE(strconv.Unquote), // unquote(str) => string/error
|
||||
}
|
||||
|
||||
func textREMatch(args ...objects.Object) (ret objects.Object, err error) {
|
||||
if len(args) != 2 {
|
||||
err = objects.ErrWrongNumArguments
|
||||
return
|
||||
}
|
||||
|
||||
s1, ok := objects.ToString(args[0])
|
||||
if !ok {
|
||||
err = objects.ErrInvalidTypeConversion
|
||||
return
|
||||
}
|
||||
|
||||
s2, ok := objects.ToString(args[1])
|
||||
if !ok {
|
||||
err = objects.ErrInvalidTypeConversion
|
||||
return
|
||||
}
|
||||
|
||||
matched, err := regexp.MatchString(s1, s2)
|
||||
if err != nil {
|
||||
ret = wrapError(err)
|
||||
return
|
||||
}
|
||||
|
||||
if matched {
|
||||
ret = objects.TrueValue
|
||||
} else {
|
||||
ret = objects.FalseValue
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func textREFind(args ...objects.Object) (ret objects.Object, err error) {
|
||||
numArgs := len(args)
|
||||
if numArgs != 2 && numArgs != 3 {
|
||||
err = objects.ErrWrongNumArguments
|
||||
return
|
||||
}
|
||||
|
||||
s1, ok := objects.ToString(args[0])
|
||||
if !ok {
|
||||
err = objects.ErrInvalidTypeConversion
|
||||
return
|
||||
}
|
||||
|
||||
re, err := regexp.Compile(s1)
|
||||
if err != nil {
|
||||
ret = wrapError(err)
|
||||
return
|
||||
}
|
||||
|
||||
s2, ok := objects.ToString(args[1])
|
||||
if !ok {
|
||||
err = objects.ErrInvalidTypeConversion
|
||||
return
|
||||
}
|
||||
|
||||
if numArgs < 3 {
|
||||
m := re.FindStringSubmatchIndex(s2)
|
||||
if m == nil {
|
||||
ret = objects.UndefinedValue
|
||||
return
|
||||
}
|
||||
|
||||
arr := &objects.Array{}
|
||||
for i := 0; i < len(m); i += 2 {
|
||||
arr.Value = append(arr.Value, &objects.ImmutableMap{Value: map[string]objects.Object{
|
||||
"text": &objects.String{Value: s2[m[i]:m[i+1]]},
|
||||
"begin": &objects.Int{Value: int64(m[i])},
|
||||
"end": &objects.Int{Value: int64(m[i+1])},
|
||||
}})
|
||||
}
|
||||
|
||||
ret = &objects.Array{Value: []objects.Object{arr}}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
i3, ok := objects.ToInt(args[2])
|
||||
if !ok {
|
||||
err = objects.ErrInvalidTypeConversion
|
||||
return
|
||||
}
|
||||
m := re.FindAllStringSubmatchIndex(s2, i3)
|
||||
if m == nil {
|
||||
ret = objects.UndefinedValue
|
||||
return
|
||||
}
|
||||
|
||||
arr := &objects.Array{}
|
||||
for _, m := range m {
|
||||
subMatch := &objects.Array{}
|
||||
for i := 0; i < len(m); i += 2 {
|
||||
subMatch.Value = append(subMatch.Value, &objects.ImmutableMap{Value: map[string]objects.Object{
|
||||
"text": &objects.String{Value: s2[m[i]:m[i+1]]},
|
||||
"begin": &objects.Int{Value: int64(m[i])},
|
||||
"end": &objects.Int{Value: int64(m[i+1])},
|
||||
}})
|
||||
}
|
||||
|
||||
arr.Value = append(arr.Value, subMatch)
|
||||
}
|
||||
|
||||
ret = arr
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func textREReplace(args ...objects.Object) (ret objects.Object, err error) {
|
||||
if len(args) != 3 {
|
||||
err = objects.ErrWrongNumArguments
|
||||
return
|
||||
}
|
||||
|
||||
s1, ok := objects.ToString(args[0])
|
||||
if !ok {
|
||||
err = objects.ErrInvalidTypeConversion
|
||||
return
|
||||
}
|
||||
|
||||
s2, ok := objects.ToString(args[1])
|
||||
if !ok {
|
||||
err = objects.ErrInvalidTypeConversion
|
||||
return
|
||||
}
|
||||
|
||||
s3, ok := objects.ToString(args[2])
|
||||
if !ok {
|
||||
err = objects.ErrInvalidTypeConversion
|
||||
return
|
||||
}
|
||||
|
||||
re, err := regexp.Compile(s1)
|
||||
if err != nil {
|
||||
ret = wrapError(err)
|
||||
} else {
|
||||
ret = &objects.String{Value: re.ReplaceAllString(s2, s3)}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func textRESplit(args ...objects.Object) (ret objects.Object, err error) {
|
||||
numArgs := len(args)
|
||||
if numArgs != 2 && numArgs != 3 {
|
||||
err = objects.ErrWrongNumArguments
|
||||
return
|
||||
}
|
||||
|
||||
s1, ok := objects.ToString(args[0])
|
||||
if !ok {
|
||||
err = objects.ErrInvalidTypeConversion
|
||||
return
|
||||
}
|
||||
|
||||
s2, ok := objects.ToString(args[1])
|
||||
if !ok {
|
||||
err = objects.ErrInvalidTypeConversion
|
||||
return
|
||||
}
|
||||
|
||||
var i3 = -1
|
||||
if numArgs > 2 {
|
||||
i3, ok = objects.ToInt(args[2])
|
||||
if !ok {
|
||||
err = objects.ErrInvalidTypeConversion
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
re, err := regexp.Compile(s1)
|
||||
if err != nil {
|
||||
ret = wrapError(err)
|
||||
return
|
||||
}
|
||||
|
||||
arr := &objects.Array{}
|
||||
for _, s := range re.Split(s2, i3) {
|
||||
arr.Value = append(arr.Value, &objects.String{Value: s})
|
||||
}
|
||||
|
||||
ret = arr
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func textRECompile(args ...objects.Object) (ret objects.Object, err error) {
|
||||
if len(args) != 1 {
|
||||
err = objects.ErrWrongNumArguments
|
||||
return
|
||||
}
|
||||
|
||||
s1, ok := objects.ToString(args[0])
|
||||
if !ok {
|
||||
err = objects.ErrInvalidTypeConversion
|
||||
return
|
||||
}
|
||||
|
||||
re, err := regexp.Compile(s1)
|
||||
if err != nil {
|
||||
ret = wrapError(err)
|
||||
} else {
|
||||
ret = makeTextRegexp(re)
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func textReplace(args ...objects.Object) (ret objects.Object, err error) {
|
||||
if len(args) != 4 {
|
||||
err = objects.ErrWrongNumArguments
|
||||
return
|
||||
}
|
||||
|
||||
s1, ok := objects.ToString(args[0])
|
||||
if !ok {
|
||||
err = objects.ErrInvalidTypeConversion
|
||||
return
|
||||
}
|
||||
|
||||
s2, ok := objects.ToString(args[1])
|
||||
if !ok {
|
||||
err = objects.ErrInvalidTypeConversion
|
||||
return
|
||||
}
|
||||
|
||||
s3, ok := objects.ToString(args[2])
|
||||
if !ok {
|
||||
err = objects.ErrInvalidTypeConversion
|
||||
return
|
||||
}
|
||||
|
||||
i4, ok := objects.ToInt(args[3])
|
||||
if !ok {
|
||||
err = objects.ErrInvalidTypeConversion
|
||||
return
|
||||
}
|
||||
|
||||
ret = &objects.String{Value: strings.Replace(s1, s2, s3, i4)}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func textFormatBool(args ...objects.Object) (ret objects.Object, err error) {
|
||||
if len(args) != 1 {
|
||||
err = objects.ErrWrongNumArguments
|
||||
return
|
||||
}
|
||||
|
||||
b1, ok := args[0].(*objects.Bool)
|
||||
if !ok {
|
||||
err = objects.ErrInvalidTypeConversion
|
||||
return
|
||||
}
|
||||
|
||||
if b1 == objects.TrueValue {
|
||||
ret = &objects.String{Value: "true"}
|
||||
} else {
|
||||
ret = &objects.String{Value: "false"}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func textFormatFloat(args ...objects.Object) (ret objects.Object, err error) {
|
||||
if len(args) != 4 {
|
||||
err = objects.ErrWrongNumArguments
|
||||
return
|
||||
}
|
||||
|
||||
f1, ok := args[0].(*objects.Float)
|
||||
if !ok {
|
||||
err = objects.ErrInvalidTypeConversion
|
||||
return
|
||||
}
|
||||
|
||||
s2, ok := objects.ToString(args[1])
|
||||
if !ok {
|
||||
err = objects.ErrInvalidTypeConversion
|
||||
return
|
||||
}
|
||||
|
||||
i3, ok := objects.ToInt(args[2])
|
||||
if !ok {
|
||||
err = objects.ErrInvalidTypeConversion
|
||||
return
|
||||
}
|
||||
|
||||
i4, ok := objects.ToInt(args[3])
|
||||
if !ok {
|
||||
err = objects.ErrInvalidTypeConversion
|
||||
return
|
||||
}
|
||||
|
||||
ret = &objects.String{Value: strconv.FormatFloat(f1.Value, s2[0], i3, i4)}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func textFormatInt(args ...objects.Object) (ret objects.Object, err error) {
|
||||
if len(args) != 2 {
|
||||
err = objects.ErrWrongNumArguments
|
||||
return
|
||||
}
|
||||
|
||||
i1, ok := args[0].(*objects.Int)
|
||||
if !ok {
|
||||
err = objects.ErrInvalidTypeConversion
|
||||
return
|
||||
}
|
||||
|
||||
i2, ok := objects.ToInt(args[1])
|
||||
if !ok {
|
||||
err = objects.ErrInvalidTypeConversion
|
||||
return
|
||||
}
|
||||
|
||||
ret = &objects.String{Value: strconv.FormatInt(i1.Value, i2)}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func textParseBool(args ...objects.Object) (ret objects.Object, err error) {
|
||||
if len(args) != 1 {
|
||||
err = objects.ErrWrongNumArguments
|
||||
return
|
||||
}
|
||||
|
||||
s1, ok := args[0].(*objects.String)
|
||||
if !ok {
|
||||
err = objects.ErrInvalidTypeConversion
|
||||
return
|
||||
}
|
||||
|
||||
parsed, err := strconv.ParseBool(s1.Value)
|
||||
if err != nil {
|
||||
ret = wrapError(err)
|
||||
return
|
||||
}
|
||||
|
||||
if parsed {
|
||||
ret = objects.TrueValue
|
||||
} else {
|
||||
ret = objects.FalseValue
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func textParseFloat(args ...objects.Object) (ret objects.Object, err error) {
|
||||
if len(args) != 2 {
|
||||
err = objects.ErrWrongNumArguments
|
||||
return
|
||||
}
|
||||
|
||||
s1, ok := args[0].(*objects.String)
|
||||
if !ok {
|
||||
err = objects.ErrInvalidTypeConversion
|
||||
return
|
||||
}
|
||||
|
||||
i2, ok := objects.ToInt(args[1])
|
||||
if !ok {
|
||||
err = objects.ErrInvalidTypeConversion
|
||||
return
|
||||
}
|
||||
|
||||
parsed, err := strconv.ParseFloat(s1.Value, i2)
|
||||
if err != nil {
|
||||
ret = wrapError(err)
|
||||
return
|
||||
}
|
||||
|
||||
ret = &objects.Float{Value: parsed}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func textParseInt(args ...objects.Object) (ret objects.Object, err error) {
|
||||
if len(args) != 3 {
|
||||
err = objects.ErrWrongNumArguments
|
||||
return
|
||||
}
|
||||
|
||||
s1, ok := args[0].(*objects.String)
|
||||
if !ok {
|
||||
err = objects.ErrInvalidTypeConversion
|
||||
return
|
||||
}
|
||||
|
||||
i2, ok := objects.ToInt(args[1])
|
||||
if !ok {
|
||||
err = objects.ErrInvalidTypeConversion
|
||||
return
|
||||
}
|
||||
|
||||
i3, ok := objects.ToInt(args[2])
|
||||
if !ok {
|
||||
err = objects.ErrInvalidTypeConversion
|
||||
return
|
||||
}
|
||||
|
||||
parsed, err := strconv.ParseInt(s1.Value, i2, i3)
|
||||
if err != nil {
|
||||
ret = wrapError(err)
|
||||
return
|
||||
}
|
||||
|
||||
ret = &objects.Int{Value: parsed}
|
||||
|
||||
return
|
||||
}
|
|
@ -107,7 +107,7 @@ func (o *Array) IndexGet(index Object) (res Object, err error) {
|
|||
func (o *Array) IndexSet(index, value Object) (err error) {
|
||||
intIdx, ok := ToInt(index)
|
||||
if !ok {
|
||||
err = ErrInvalidTypeConversion
|
||||
err = ErrInvalidIndexType
|
||||
return
|
||||
}
|
||||
|
||||
|
|
|
@ -1,10 +1,6 @@
|
|||
package objects
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
)
|
||||
|
||||
// append(src, items...)
|
||||
// append(arr, items...)
|
||||
func builtinAppend(args ...Object) (Object, error) {
|
||||
if len(args) < 2 {
|
||||
return nil, ErrWrongNumArguments
|
||||
|
@ -13,7 +9,13 @@ func builtinAppend(args ...Object) (Object, error) {
|
|||
switch arg := args[0].(type) {
|
||||
case *Array:
|
||||
return &Array{Value: append(arg.Value, args[1:]...)}, nil
|
||||
case *ImmutableArray:
|
||||
return &Array{Value: append(arg.Value, args[1:]...)}, nil
|
||||
default:
|
||||
return nil, fmt.Errorf("unsupported type for 'append' function: %s", arg.TypeName())
|
||||
return nil, ErrInvalidArgumentType{
|
||||
Name: "first",
|
||||
Expected: "array",
|
||||
Found: arg.TypeName(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,12 +6,13 @@ import (
|
|||
|
||||
// BuiltinFunction represents a builtin function.
|
||||
type BuiltinFunction struct {
|
||||
Name string
|
||||
Value CallableFunc
|
||||
}
|
||||
|
||||
// TypeName returns the name of the type.
|
||||
func (o *BuiltinFunction) TypeName() string {
|
||||
return "builtin-function"
|
||||
return "builtin-function:" + o.Name
|
||||
}
|
||||
|
||||
func (o *BuiltinFunction) String() string {
|
||||
|
|
|
@ -4,6 +4,7 @@ import (
|
|||
"encoding/json"
|
||||
)
|
||||
|
||||
// to_json(v object) => bytes
|
||||
func builtinToJSON(args ...Object) (Object, error) {
|
||||
if len(args) != 1 {
|
||||
return nil, ErrWrongNumArguments
|
||||
|
@ -11,12 +12,13 @@ func builtinToJSON(args ...Object) (Object, error) {
|
|||
|
||||
res, err := json.Marshal(objectToInterface(args[0]))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return &Error{Value: &String{Value: err.Error()}}, nil
|
||||
}
|
||||
|
||||
return &Bytes{Value: res}, nil
|
||||
}
|
||||
|
||||
// from_json(data string/bytes) => object
|
||||
func builtinFromJSON(args ...Object) (Object, error) {
|
||||
if len(args) != 1 {
|
||||
return nil, ErrWrongNumArguments
|
||||
|
@ -28,15 +30,19 @@ func builtinFromJSON(args ...Object) (Object, error) {
|
|||
case *Bytes:
|
||||
err := json.Unmarshal(o.Value, &target)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return &Error{Value: &String{Value: err.Error()}}, nil
|
||||
}
|
||||
case *String:
|
||||
err := json.Unmarshal([]byte(o.Value), &target)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return &Error{Value: &String{Value: err.Error()}}, nil
|
||||
}
|
||||
default:
|
||||
return nil, ErrInvalidTypeConversion
|
||||
return nil, ErrInvalidArgumentType{
|
||||
Name: "first",
|
||||
Expected: "bytes/string",
|
||||
Found: args[0].TypeName(),
|
||||
}
|
||||
}
|
||||
|
||||
res, err := FromInterface(target)
|
||||
|
|
|
@ -1,9 +1,6 @@
|
|||
package objects
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
)
|
||||
|
||||
// len(obj object) => int
|
||||
func builtinLen(args ...Object) (Object, error) {
|
||||
if len(args) != 1 {
|
||||
return nil, ErrWrongNumArguments
|
||||
|
@ -23,6 +20,10 @@ func builtinLen(args ...Object) (Object, error) {
|
|||
case *ImmutableMap:
|
||||
return &Int{Value: int64(len(arg.Value))}, nil
|
||||
default:
|
||||
return nil, fmt.Errorf("unsupported type for 'len' function: %s", arg.TypeName())
|
||||
return nil, ErrInvalidArgumentType{
|
||||
Name: "first",
|
||||
Expected: "array/string/bytes/map",
|
||||
Found: arg.TypeName(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -26,7 +26,11 @@ func builtinPrintf(args ...Object) (Object, error) {
|
|||
|
||||
format, ok := args[0].(*String)
|
||||
if !ok {
|
||||
return nil, ErrInvalidTypeConversion
|
||||
return nil, ErrInvalidArgumentType{
|
||||
Name: "format",
|
||||
Expected: "string",
|
||||
Found: args[0].TypeName(),
|
||||
}
|
||||
}
|
||||
if numArgs == 1 {
|
||||
fmt.Print(format)
|
||||
|
@ -52,7 +56,11 @@ func builtinSprintf(args ...Object) (Object, error) {
|
|||
|
||||
format, ok := args[0].(*String)
|
||||
if !ok {
|
||||
return nil, ErrInvalidTypeConversion
|
||||
return nil, ErrInvalidArgumentType{
|
||||
Name: "format",
|
||||
Expected: "string",
|
||||
Found: args[0].TypeName(),
|
||||
}
|
||||
}
|
||||
if numArgs == 1 {
|
||||
return format, nil // okay to return 'format' directly as String is immutable
|
||||
|
|
|
@ -1,14 +1,16 @@
|
|||
package objects
|
||||
|
||||
import (
|
||||
"github.com/d5/tengo/compiler/source"
|
||||
"github.com/d5/tengo/compiler/token"
|
||||
)
|
||||
|
||||
// CompiledFunction represents a compiled function.
|
||||
type CompiledFunction struct {
|
||||
Instructions []byte
|
||||
NumLocals int
|
||||
NumLocals int // number of local variables (including function parameters)
|
||||
NumParameters int
|
||||
SourceMap map[int]source.Pos
|
||||
}
|
||||
|
||||
// TypeName returns the name of the type.
|
||||
|
|
|
@ -245,5 +245,5 @@ func FromInterface(v interface{}) (Object, error) {
|
|||
return v, nil
|
||||
}
|
||||
|
||||
return nil, fmt.Errorf("unsupported value type: %T", v)
|
||||
return nil, fmt.Errorf("cannot convert to object: %T", v)
|
||||
}
|
||||
|
|
|
@ -1,24 +1,32 @@
|
|||
package objects
|
||||
|
||||
import "errors"
|
||||
|
||||
// ErrNotIndexable means the type is not indexable.
|
||||
var ErrNotIndexable = errors.New("type is not indexable")
|
||||
|
||||
// ErrNotIndexAssignable means the type is not index-assignable.
|
||||
var ErrNotIndexAssignable = errors.New("type is not index-assignable")
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
// ErrIndexOutOfBounds is an error where a given index is out of the bounds.
|
||||
var ErrIndexOutOfBounds = errors.New("index out of bounds")
|
||||
|
||||
// ErrInvalidIndexType means the type is not supported as an index.
|
||||
// ErrInvalidIndexType represents an invalid index type.
|
||||
var ErrInvalidIndexType = errors.New("invalid index type")
|
||||
|
||||
// ErrInvalidIndexValueType represents an invalid index value type.
|
||||
var ErrInvalidIndexValueType = errors.New("invalid index value type")
|
||||
|
||||
// ErrInvalidOperator represents an error for invalid operator usage.
|
||||
var ErrInvalidOperator = errors.New("invalid operator")
|
||||
|
||||
// ErrWrongNumArguments represents a wrong number of arguments error.
|
||||
var ErrWrongNumArguments = errors.New("wrong number of arguments")
|
||||
|
||||
// ErrInvalidTypeConversion represents an invalid type conversion error.
|
||||
var ErrInvalidTypeConversion = errors.New("invalid type conversion")
|
||||
// ErrInvalidArgumentType represents an invalid argument value type error.
|
||||
type ErrInvalidArgumentType struct {
|
||||
Name string
|
||||
Expected string
|
||||
Found string
|
||||
}
|
||||
|
||||
func (e ErrInvalidArgumentType) Error() string {
|
||||
return fmt.Sprintf("invalid type for argument '%s': expected %s, found %s", e.Name, e.Expected, e.Found)
|
||||
}
|
||||
|
|
|
@ -51,7 +51,7 @@ func (o *ImmutableMap) IsFalsy() bool {
|
|||
func (o *ImmutableMap) IndexGet(index Object) (res Object, err error) {
|
||||
strIdx, ok := ToString(index)
|
||||
if !ok {
|
||||
err = ErrInvalidTypeConversion
|
||||
err = ErrInvalidIndexType
|
||||
return
|
||||
}
|
||||
|
||||
|
|
|
@ -94,7 +94,7 @@ func (o *Map) IndexGet(index Object) (res Object, err error) {
|
|||
func (o *Map) IndexSet(index, value Object) (err error) {
|
||||
strIdx, ok := ToString(index)
|
||||
if !ok {
|
||||
err = ErrInvalidTypeConversion
|
||||
err = ErrInvalidIndexType
|
||||
return
|
||||
}
|
||||
|
||||
|
|
|
@ -34,8 +34,10 @@ func TestObject_TypeName(t *testing.T) {
|
|||
assert.Equal(t, "break", o.TypeName())
|
||||
o = &objects.Continue{}
|
||||
assert.Equal(t, "continue", o.TypeName())
|
||||
o = &objects.BuiltinFunction{}
|
||||
assert.Equal(t, "builtin-function", o.TypeName())
|
||||
o = &objects.BuiltinFunction{Name: "fn"}
|
||||
assert.Equal(t, "builtin-function:fn", o.TypeName())
|
||||
o = &objects.UserFunction{Name: "fn"}
|
||||
assert.Equal(t, "user-function:fn", o.TypeName())
|
||||
o = &objects.Closure{}
|
||||
assert.Equal(t, "closure", o.TypeName())
|
||||
o = &objects.CompiledFunction{}
|
||||
|
|
|
@ -6,12 +6,13 @@ import (
|
|||
|
||||
// UserFunction represents a user function.
|
||||
type UserFunction struct {
|
||||
Name string
|
||||
Value CallableFunc
|
||||
}
|
||||
|
||||
// TypeName returns the name of the type.
|
||||
func (o *UserFunction) TypeName() string {
|
||||
return "user-function"
|
||||
return "user-function:" + o.Name
|
||||
}
|
||||
|
||||
func (o *UserFunction) String() string {
|
||||
|
|
519
runtime/vm.go
519
runtime/vm.go
|
@ -1,13 +1,14 @@
|
|||
package runtime
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"sync/atomic"
|
||||
|
||||
"github.com/d5/tengo/compiler"
|
||||
"github.com/d5/tengo/compiler/source"
|
||||
"github.com/d5/tengo/compiler/token"
|
||||
"github.com/d5/tengo/objects"
|
||||
"github.com/d5/tengo/stdlib"
|
||||
)
|
||||
|
||||
const (
|
||||
|
@ -30,42 +31,50 @@ var (
|
|||
|
||||
// VM is a virtual machine that executes the bytecode compiled by Compiler.
|
||||
type VM struct {
|
||||
constants []objects.Object
|
||||
stack []*objects.Object
|
||||
sp int
|
||||
globals []*objects.Object
|
||||
frames []Frame
|
||||
framesIndex int
|
||||
curFrame *Frame
|
||||
curInsts []byte
|
||||
curIPLimit int
|
||||
ip int
|
||||
aborting int64
|
||||
constants []objects.Object
|
||||
stack []*objects.Object
|
||||
sp int
|
||||
globals []*objects.Object
|
||||
fileSet *source.FileSet
|
||||
frames []Frame
|
||||
framesIndex int
|
||||
curFrame *Frame
|
||||
curInsts []byte
|
||||
curIPLimit int
|
||||
ip int
|
||||
aborting int64
|
||||
builtinModules map[string]*objects.Object
|
||||
}
|
||||
|
||||
// NewVM creates a VM.
|
||||
func NewVM(bytecode *compiler.Bytecode, globals []*objects.Object) *VM {
|
||||
func NewVM(bytecode *compiler.Bytecode, globals []*objects.Object, builtinModules map[string]*objects.Object) *VM {
|
||||
if globals == nil {
|
||||
globals = make([]*objects.Object, GlobalsSize)
|
||||
}
|
||||
|
||||
if builtinModules == nil {
|
||||
builtinModules = stdlib.Modules
|
||||
}
|
||||
|
||||
frames := make([]Frame, MaxFrames)
|
||||
frames[0].fn = &objects.CompiledFunction{Instructions: bytecode.Instructions}
|
||||
frames[0].fn = bytecode.MainFunction
|
||||
frames[0].freeVars = nil
|
||||
frames[0].ip = -1
|
||||
frames[0].basePointer = 0
|
||||
|
||||
return &VM{
|
||||
constants: bytecode.Constants,
|
||||
stack: make([]*objects.Object, StackSize),
|
||||
sp: 0,
|
||||
globals: globals,
|
||||
frames: frames,
|
||||
framesIndex: 1,
|
||||
curFrame: &(frames[0]),
|
||||
curInsts: frames[0].fn.Instructions,
|
||||
curIPLimit: len(frames[0].fn.Instructions) - 1,
|
||||
ip: -1,
|
||||
constants: bytecode.Constants,
|
||||
stack: make([]*objects.Object, StackSize),
|
||||
sp: 0,
|
||||
globals: globals,
|
||||
fileSet: bytecode.FileSet,
|
||||
frames: frames,
|
||||
framesIndex: 1,
|
||||
curFrame: &(frames[0]),
|
||||
curInsts: frames[0].fn.Instructions,
|
||||
curIPLimit: len(frames[0].fn.Instructions) - 1,
|
||||
ip: -1,
|
||||
builtinModules: builtinModules,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -85,10 +94,11 @@ func (v *VM) Run() error {
|
|||
v.ip = -1
|
||||
atomic.StoreInt64(&v.aborting, 0)
|
||||
|
||||
mainloop:
|
||||
for v.ip < v.curIPLimit && (atomic.LoadInt64(&v.aborting) == 0) {
|
||||
v.ip++
|
||||
|
||||
switch compiler.Opcode(v.curInsts[v.ip]) {
|
||||
switch v.curInsts[v.ip] {
|
||||
case compiler.OpConstant:
|
||||
cidx := int(v.curInsts[v.ip+2]) | int(v.curInsts[v.ip+1])<<8
|
||||
v.ip += 2
|
||||
|
@ -115,7 +125,13 @@ func (v *VM) Run() error {
|
|||
|
||||
res, err := (*left).BinaryOp(token.Add, *right)
|
||||
if err != nil {
|
||||
return err
|
||||
filePos := v.fileSet.Position(v.curFrame.fn.SourceMap[v.ip])
|
||||
if err == objects.ErrInvalidOperator {
|
||||
return fmt.Errorf("%s: invalid operation: %s + %s",
|
||||
filePos, (*left).TypeName(), (*right).TypeName())
|
||||
}
|
||||
|
||||
return fmt.Errorf("%s: %s", filePos, err.Error())
|
||||
}
|
||||
|
||||
if v.sp >= StackSize {
|
||||
|
@ -132,7 +148,13 @@ func (v *VM) Run() error {
|
|||
|
||||
res, err := (*left).BinaryOp(token.Sub, *right)
|
||||
if err != nil {
|
||||
return err
|
||||
filePos := v.fileSet.Position(v.curFrame.fn.SourceMap[v.ip])
|
||||
if err == objects.ErrInvalidOperator {
|
||||
return fmt.Errorf("%s: invalid operation: %s - %s",
|
||||
filePos, (*left).TypeName(), (*right).TypeName())
|
||||
}
|
||||
|
||||
return fmt.Errorf("%s: %s", filePos, err.Error())
|
||||
}
|
||||
|
||||
if v.sp >= StackSize {
|
||||
|
@ -149,7 +171,13 @@ func (v *VM) Run() error {
|
|||
|
||||
res, err := (*left).BinaryOp(token.Mul, *right)
|
||||
if err != nil {
|
||||
return err
|
||||
filePos := v.fileSet.Position(v.curFrame.fn.SourceMap[v.ip])
|
||||
if err == objects.ErrInvalidOperator {
|
||||
return fmt.Errorf("%s: invalid operation: %s * %s",
|
||||
filePos, (*left).TypeName(), (*right).TypeName())
|
||||
}
|
||||
|
||||
return fmt.Errorf("%s: %s", filePos, err.Error())
|
||||
}
|
||||
|
||||
if v.sp >= StackSize {
|
||||
|
@ -166,7 +194,13 @@ func (v *VM) Run() error {
|
|||
|
||||
res, err := (*left).BinaryOp(token.Quo, *right)
|
||||
if err != nil {
|
||||
return err
|
||||
filePos := v.fileSet.Position(v.curFrame.fn.SourceMap[v.ip])
|
||||
if err == objects.ErrInvalidOperator {
|
||||
return fmt.Errorf("%s: invalid operation: %s / %s",
|
||||
filePos, (*left).TypeName(), (*right).TypeName())
|
||||
}
|
||||
|
||||
return fmt.Errorf("%s: %s", filePos, err.Error())
|
||||
}
|
||||
|
||||
if v.sp >= StackSize {
|
||||
|
@ -183,7 +217,13 @@ func (v *VM) Run() error {
|
|||
|
||||
res, err := (*left).BinaryOp(token.Rem, *right)
|
||||
if err != nil {
|
||||
return err
|
||||
filePos := v.fileSet.Position(v.curFrame.fn.SourceMap[v.ip])
|
||||
if err == objects.ErrInvalidOperator {
|
||||
return fmt.Errorf("%s: invalid operation: %s %% %s",
|
||||
filePos, (*left).TypeName(), (*right).TypeName())
|
||||
}
|
||||
|
||||
return fmt.Errorf("%s: %s", filePos, err.Error())
|
||||
}
|
||||
|
||||
if v.sp >= StackSize {
|
||||
|
@ -200,7 +240,13 @@ func (v *VM) Run() error {
|
|||
|
||||
res, err := (*left).BinaryOp(token.And, *right)
|
||||
if err != nil {
|
||||
return err
|
||||
filePos := v.fileSet.Position(v.curFrame.fn.SourceMap[v.ip])
|
||||
if err == objects.ErrInvalidOperator {
|
||||
return fmt.Errorf("%s: invalid operation: %s & %s",
|
||||
filePos, (*left).TypeName(), (*right).TypeName())
|
||||
}
|
||||
|
||||
return fmt.Errorf("%s: %s", filePos, err.Error())
|
||||
}
|
||||
|
||||
if v.sp >= StackSize {
|
||||
|
@ -217,7 +263,13 @@ func (v *VM) Run() error {
|
|||
|
||||
res, err := (*left).BinaryOp(token.Or, *right)
|
||||
if err != nil {
|
||||
return err
|
||||
filePos := v.fileSet.Position(v.curFrame.fn.SourceMap[v.ip])
|
||||
if err == objects.ErrInvalidOperator {
|
||||
return fmt.Errorf("%s: invalid operation: %s | %s",
|
||||
filePos, (*left).TypeName(), (*right).TypeName())
|
||||
}
|
||||
|
||||
return fmt.Errorf("%s: %s", filePos, err.Error())
|
||||
}
|
||||
|
||||
if v.sp >= StackSize {
|
||||
|
@ -234,7 +286,13 @@ func (v *VM) Run() error {
|
|||
|
||||
res, err := (*left).BinaryOp(token.Xor, *right)
|
||||
if err != nil {
|
||||
return err
|
||||
filePos := v.fileSet.Position(v.curFrame.fn.SourceMap[v.ip])
|
||||
if err == objects.ErrInvalidOperator {
|
||||
return fmt.Errorf("%s: invalid operation: %s ^ %s",
|
||||
filePos, (*left).TypeName(), (*right).TypeName())
|
||||
}
|
||||
|
||||
return fmt.Errorf("%s: %s", filePos, err.Error())
|
||||
}
|
||||
|
||||
if v.sp >= StackSize {
|
||||
|
@ -251,7 +309,13 @@ func (v *VM) Run() error {
|
|||
|
||||
res, err := (*left).BinaryOp(token.AndNot, *right)
|
||||
if err != nil {
|
||||
return err
|
||||
filePos := v.fileSet.Position(v.curFrame.fn.SourceMap[v.ip])
|
||||
if err == objects.ErrInvalidOperator {
|
||||
return fmt.Errorf("%s: invalid operation: %s &^ %s",
|
||||
filePos, (*left).TypeName(), (*right).TypeName())
|
||||
}
|
||||
|
||||
return fmt.Errorf("%s: %s", filePos, err.Error())
|
||||
}
|
||||
|
||||
if v.sp >= StackSize {
|
||||
|
@ -268,7 +332,13 @@ func (v *VM) Run() error {
|
|||
|
||||
res, err := (*left).BinaryOp(token.Shl, *right)
|
||||
if err != nil {
|
||||
return err
|
||||
filePos := v.fileSet.Position(v.curFrame.fn.SourceMap[v.ip])
|
||||
if err == objects.ErrInvalidOperator {
|
||||
return fmt.Errorf("%s: invalid operation: %s << %s",
|
||||
filePos, (*left).TypeName(), (*right).TypeName())
|
||||
}
|
||||
|
||||
return fmt.Errorf("%s: %s", filePos, err.Error())
|
||||
}
|
||||
|
||||
if v.sp >= StackSize {
|
||||
|
@ -285,7 +355,13 @@ func (v *VM) Run() error {
|
|||
|
||||
res, err := (*left).BinaryOp(token.Shr, *right)
|
||||
if err != nil {
|
||||
return err
|
||||
filePos := v.fileSet.Position(v.curFrame.fn.SourceMap[v.ip])
|
||||
if err == objects.ErrInvalidOperator {
|
||||
return fmt.Errorf("%s: invalid operation: %s >> %s",
|
||||
filePos, (*left).TypeName(), (*right).TypeName())
|
||||
}
|
||||
|
||||
return fmt.Errorf("%s: %s", filePos, err.Error())
|
||||
}
|
||||
|
||||
if v.sp >= StackSize {
|
||||
|
@ -334,7 +410,13 @@ func (v *VM) Run() error {
|
|||
|
||||
res, err := (*left).BinaryOp(token.Greater, *right)
|
||||
if err != nil {
|
||||
return err
|
||||
filePos := v.fileSet.Position(v.curFrame.fn.SourceMap[v.ip])
|
||||
if err == objects.ErrInvalidOperator {
|
||||
return fmt.Errorf("%s: invalid operation: %s > %s",
|
||||
filePos, (*left).TypeName(), (*right).TypeName())
|
||||
}
|
||||
|
||||
return fmt.Errorf("%s: %s", filePos, err.Error())
|
||||
}
|
||||
|
||||
if v.sp >= StackSize {
|
||||
|
@ -351,7 +433,13 @@ func (v *VM) Run() error {
|
|||
|
||||
res, err := (*left).BinaryOp(token.GreaterEq, *right)
|
||||
if err != nil {
|
||||
return err
|
||||
filePos := v.fileSet.Position(v.curFrame.fn.SourceMap[v.ip])
|
||||
if err == objects.ErrInvalidOperator {
|
||||
return fmt.Errorf("%s: invalid operation: %s >= %s",
|
||||
filePos, (*left).TypeName(), (*right).TypeName())
|
||||
}
|
||||
|
||||
return fmt.Errorf("%s: %s", filePos, err.Error())
|
||||
}
|
||||
|
||||
if v.sp >= StackSize {
|
||||
|
@ -410,7 +498,8 @@ func (v *VM) Run() error {
|
|||
v.stack[v.sp] = &res
|
||||
v.sp++
|
||||
default:
|
||||
return fmt.Errorf("invalid operation on %s", (*operand).TypeName())
|
||||
filePos := v.fileSet.Position(v.curFrame.fn.SourceMap[v.ip])
|
||||
return fmt.Errorf("%s: invalid operation: ^%s", filePos, (*operand).TypeName())
|
||||
}
|
||||
|
||||
case compiler.OpMinus:
|
||||
|
@ -437,7 +526,8 @@ func (v *VM) Run() error {
|
|||
v.stack[v.sp] = &res
|
||||
v.sp++
|
||||
default:
|
||||
return fmt.Errorf("invalid operation on %s", (*operand).TypeName())
|
||||
filePos := v.fileSet.Position(v.curFrame.fn.SourceMap[v.ip])
|
||||
return fmt.Errorf("%s: invalid operation: -%s", filePos, (*operand).TypeName())
|
||||
}
|
||||
|
||||
case compiler.OpJumpFalsy:
|
||||
|
@ -496,7 +586,8 @@ func (v *VM) Run() error {
|
|||
v.sp -= numSelectors + 1
|
||||
|
||||
if err := indexAssign(v.globals[globalIndex], val, selectors); err != nil {
|
||||
return err
|
||||
filePos := v.fileSet.Position(v.curFrame.fn.SourceMap[v.ip-3])
|
||||
return fmt.Errorf("%s: %s", filePos, err.Error())
|
||||
}
|
||||
|
||||
case compiler.OpGetGlobal:
|
||||
|
@ -586,7 +677,13 @@ func (v *VM) Run() error {
|
|||
case objects.Indexable:
|
||||
val, err := left.IndexGet(*index)
|
||||
if err != nil {
|
||||
return err
|
||||
filePos := v.fileSet.Position(v.curFrame.fn.SourceMap[v.ip])
|
||||
|
||||
if err == objects.ErrInvalidIndexType {
|
||||
return fmt.Errorf("%s: invalid index type: %s", filePos, (*index).TypeName())
|
||||
}
|
||||
|
||||
return fmt.Errorf("%s: %s", filePos, err.Error())
|
||||
}
|
||||
if val == nil {
|
||||
val = objects.UndefinedValue
|
||||
|
@ -602,7 +699,8 @@ func (v *VM) Run() error {
|
|||
case *objects.Error: // err.value
|
||||
key, ok := (*index).(*objects.String)
|
||||
if !ok || key.Value != "value" {
|
||||
return errors.New("invalid selector on error")
|
||||
filePos := v.fileSet.Position(v.curFrame.fn.SourceMap[v.ip])
|
||||
return fmt.Errorf("%s: invalid index on error", filePos)
|
||||
}
|
||||
|
||||
if v.sp >= StackSize {
|
||||
|
@ -613,7 +711,8 @@ func (v *VM) Run() error {
|
|||
v.sp++
|
||||
|
||||
default:
|
||||
return objects.ErrNotIndexable
|
||||
filePos := v.fileSet.Position(v.curFrame.fn.SourceMap[v.ip])
|
||||
return fmt.Errorf("%s: not indexable: %s", filePos, left.TypeName())
|
||||
}
|
||||
|
||||
case compiler.OpSliceIndex:
|
||||
|
@ -627,7 +726,8 @@ func (v *VM) Run() error {
|
|||
if low, ok := (*low).(*objects.Int); ok {
|
||||
lowIdx = low.Value
|
||||
} else {
|
||||
return fmt.Errorf("non-integer slice index: %s", low.TypeName())
|
||||
filePos := v.fileSet.Position(v.curFrame.fn.SourceMap[v.ip])
|
||||
return fmt.Errorf("%s: invalid slice index type: %s", filePos, low.TypeName())
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -640,11 +740,13 @@ func (v *VM) Run() error {
|
|||
} else if high, ok := (*high).(*objects.Int); ok {
|
||||
highIdx = high.Value
|
||||
} else {
|
||||
return fmt.Errorf("non-integer slice index: %s", high.TypeName())
|
||||
filePos := v.fileSet.Position(v.curFrame.fn.SourceMap[v.ip])
|
||||
return fmt.Errorf("%s: invalid slice index type: %s", filePos, high.TypeName())
|
||||
}
|
||||
|
||||
if lowIdx > highIdx {
|
||||
return fmt.Errorf("invalid slice index: %d > %d", lowIdx, highIdx)
|
||||
filePos := v.fileSet.Position(v.curFrame.fn.SourceMap[v.ip])
|
||||
return fmt.Errorf("%s: invalid slice index: %d > %d", filePos, lowIdx, highIdx)
|
||||
}
|
||||
|
||||
if lowIdx < 0 {
|
||||
|
@ -675,11 +777,13 @@ func (v *VM) Run() error {
|
|||
} else if high, ok := (*high).(*objects.Int); ok {
|
||||
highIdx = high.Value
|
||||
} else {
|
||||
return fmt.Errorf("non-integer slice index: %s", high.TypeName())
|
||||
filePos := v.fileSet.Position(v.curFrame.fn.SourceMap[v.ip])
|
||||
return fmt.Errorf("%s: invalid slice index type: %s", filePos, high.TypeName())
|
||||
}
|
||||
|
||||
if lowIdx > highIdx {
|
||||
return fmt.Errorf("invalid slice index: %d > %d", lowIdx, highIdx)
|
||||
filePos := v.fileSet.Position(v.curFrame.fn.SourceMap[v.ip])
|
||||
return fmt.Errorf("%s: invalid slice index: %d > %d", filePos, lowIdx, highIdx)
|
||||
}
|
||||
|
||||
if lowIdx < 0 {
|
||||
|
@ -711,11 +815,13 @@ func (v *VM) Run() error {
|
|||
} else if high, ok := (*high).(*objects.Int); ok {
|
||||
highIdx = high.Value
|
||||
} else {
|
||||
return fmt.Errorf("non-integer slice index: %s", high.TypeName())
|
||||
filePos := v.fileSet.Position(v.curFrame.fn.SourceMap[v.ip])
|
||||
return fmt.Errorf("%s: invalid slice index type: %s", filePos, high.TypeName())
|
||||
}
|
||||
|
||||
if lowIdx > highIdx {
|
||||
return fmt.Errorf("invalid slice index: %d > %d", lowIdx, highIdx)
|
||||
filePos := v.fileSet.Position(v.curFrame.fn.SourceMap[v.ip])
|
||||
return fmt.Errorf("%s: invalid slice index: %d > %d", filePos, lowIdx, highIdx)
|
||||
}
|
||||
|
||||
if lowIdx < 0 {
|
||||
|
@ -747,11 +853,13 @@ func (v *VM) Run() error {
|
|||
} else if high, ok := (*high).(*objects.Int); ok {
|
||||
highIdx = high.Value
|
||||
} else {
|
||||
return fmt.Errorf("non-integer slice index: %s", high.TypeName())
|
||||
filePos := v.fileSet.Position(v.curFrame.fn.SourceMap[v.ip])
|
||||
return fmt.Errorf("%s: invalid slice index type: %s", filePos, high.TypeName())
|
||||
}
|
||||
|
||||
if lowIdx > highIdx {
|
||||
return fmt.Errorf("invalid slice index: %d > %d", lowIdx, highIdx)
|
||||
filePos := v.fileSet.Position(v.curFrame.fn.SourceMap[v.ip])
|
||||
return fmt.Errorf("%s: invalid slice index: %d > %d", filePos, lowIdx, highIdx)
|
||||
}
|
||||
|
||||
if lowIdx < 0 {
|
||||
|
@ -780,17 +888,75 @@ func (v *VM) Run() error {
|
|||
numArgs := int(v.curInsts[v.ip+1])
|
||||
v.ip++
|
||||
|
||||
callee := *v.stack[v.sp-1-numArgs]
|
||||
value := *v.stack[v.sp-1-numArgs]
|
||||
|
||||
switch callee := callee.(type) {
|
||||
switch callee := value.(type) {
|
||||
case *objects.Closure:
|
||||
if err := v.callFunction(callee.Fn, callee.Free, numArgs); err != nil {
|
||||
return err
|
||||
if numArgs != callee.Fn.NumParameters {
|
||||
filePos := v.fileSet.Position(v.curFrame.fn.SourceMap[v.ip-1])
|
||||
return fmt.Errorf("%s: wrong number of arguments: want=%d, got=%d",
|
||||
filePos, callee.Fn.NumParameters, numArgs)
|
||||
}
|
||||
|
||||
// test if it's tail-call
|
||||
if callee.Fn == v.curFrame.fn { // recursion
|
||||
nextOp := v.curInsts[v.ip+1]
|
||||
if nextOp == compiler.OpReturnValue ||
|
||||
(nextOp == compiler.OpPop && compiler.OpReturn == v.curInsts[v.ip+2]) {
|
||||
for p := 0; p < numArgs; p++ {
|
||||
v.stack[v.curFrame.basePointer+p] = v.stack[v.sp-numArgs+p]
|
||||
}
|
||||
v.sp -= numArgs + 1
|
||||
v.ip = -1 // reset IP to beginning of the frame
|
||||
continue mainloop
|
||||
}
|
||||
}
|
||||
|
||||
// update call frame
|
||||
v.curFrame.ip = v.ip // store current ip before call
|
||||
v.curFrame = &(v.frames[v.framesIndex])
|
||||
v.curFrame.fn = callee.Fn
|
||||
v.curFrame.freeVars = callee.Free
|
||||
v.curFrame.basePointer = v.sp - numArgs
|
||||
v.curInsts = callee.Fn.Instructions
|
||||
v.ip = -1
|
||||
v.curIPLimit = len(v.curInsts) - 1
|
||||
v.framesIndex++
|
||||
v.sp = v.sp - numArgs + callee.Fn.NumLocals
|
||||
|
||||
case *objects.CompiledFunction:
|
||||
if err := v.callFunction(callee, nil, numArgs); err != nil {
|
||||
return err
|
||||
if numArgs != callee.NumParameters {
|
||||
filePos := v.fileSet.Position(v.curFrame.fn.SourceMap[v.ip-1])
|
||||
return fmt.Errorf("%s: wrong number of arguments: want=%d, got=%d",
|
||||
filePos, callee.NumParameters, numArgs)
|
||||
}
|
||||
|
||||
// test if it's tail-call
|
||||
if callee == v.curFrame.fn { // recursion
|
||||
nextOp := v.curInsts[v.ip+1]
|
||||
if nextOp == compiler.OpReturnValue ||
|
||||
(nextOp == compiler.OpPop && compiler.OpReturn == v.curInsts[v.ip+2]) {
|
||||
for p := 0; p < numArgs; p++ {
|
||||
v.stack[v.curFrame.basePointer+p] = v.stack[v.sp-numArgs+p]
|
||||
}
|
||||
v.sp -= numArgs + 1
|
||||
v.ip = -1 // reset IP to beginning of the frame
|
||||
continue mainloop
|
||||
}
|
||||
}
|
||||
|
||||
// update call frame
|
||||
v.curFrame.ip = v.ip // store current ip before call
|
||||
v.curFrame = &(v.frames[v.framesIndex])
|
||||
v.curFrame.fn = callee
|
||||
v.curFrame.freeVars = nil
|
||||
v.curFrame.basePointer = v.sp - numArgs
|
||||
v.curInsts = callee.Instructions
|
||||
v.ip = -1
|
||||
v.curIPLimit = len(v.curInsts) - 1
|
||||
v.framesIndex++
|
||||
v.sp = v.sp - numArgs + callee.NumLocals
|
||||
|
||||
case objects.Callable:
|
||||
var args []objects.Object
|
||||
for _, arg := range v.stack[v.sp-numArgs : v.sp] {
|
||||
|
@ -802,7 +968,19 @@ func (v *VM) Run() error {
|
|||
|
||||
// runtime error
|
||||
if err != nil {
|
||||
return err
|
||||
filePos := v.fileSet.Position(v.curFrame.fn.SourceMap[v.ip-1])
|
||||
|
||||
if err == objects.ErrWrongNumArguments {
|
||||
return fmt.Errorf("%s: wrong number of arguments in call to '%s'",
|
||||
filePos, value.TypeName())
|
||||
}
|
||||
|
||||
if err, ok := err.(objects.ErrInvalidArgumentType); ok {
|
||||
return fmt.Errorf("%s: invalid type for argument '%s' in call to '%s': expected %s, found %s",
|
||||
filePos, err.Name, value.TypeName(), err.Expected, err.Found)
|
||||
}
|
||||
|
||||
return fmt.Errorf("%s: %s", filePos, err.Error())
|
||||
}
|
||||
|
||||
// nil return -> undefined
|
||||
|
@ -816,8 +994,10 @@ func (v *VM) Run() error {
|
|||
|
||||
v.stack[v.sp] = &ret
|
||||
v.sp++
|
||||
|
||||
default:
|
||||
return fmt.Errorf("calling non-function: %s", callee.TypeName())
|
||||
filePos := v.fileSet.Position(v.curFrame.fn.SourceMap[v.ip-1])
|
||||
return fmt.Errorf("%s: not callable: %s", filePos, callee.TypeName())
|
||||
}
|
||||
|
||||
case compiler.OpReturnValue:
|
||||
|
@ -834,9 +1014,10 @@ func (v *VM) Run() error {
|
|||
//v.sp = lastFrame.basePointer - 1
|
||||
v.sp = lastFrame.basePointer
|
||||
|
||||
if v.sp-1 >= StackSize {
|
||||
return ErrStackOverflow
|
||||
}
|
||||
// skip stack overflow check because (newSP) <= (oldSP)
|
||||
//if v.sp-1 >= StackSize {
|
||||
// return ErrStackOverflow
|
||||
//}
|
||||
|
||||
v.stack[v.sp-1] = retVal
|
||||
//v.sp++
|
||||
|
@ -852,9 +1033,10 @@ func (v *VM) Run() error {
|
|||
//v.sp = lastFrame.basePointer - 1
|
||||
v.sp = lastFrame.basePointer
|
||||
|
||||
if v.sp-1 >= StackSize {
|
||||
return ErrStackOverflow
|
||||
}
|
||||
// skip stack overflow check because (newSP) <= (oldSP)
|
||||
//if v.sp-1 >= StackSize {
|
||||
// return ErrStackOverflow
|
||||
//}
|
||||
|
||||
v.stack[v.sp-1] = undefinedPtr
|
||||
//v.sp++
|
||||
|
@ -898,7 +1080,8 @@ func (v *VM) Run() error {
|
|||
sp := v.curFrame.basePointer + localIndex
|
||||
|
||||
if err := indexAssign(v.stack[sp], val, selectors); err != nil {
|
||||
return err
|
||||
filePos := v.fileSet.Position(v.curFrame.fn.SourceMap[v.ip-2])
|
||||
return fmt.Errorf("%s: %s", filePos, err.Error())
|
||||
}
|
||||
|
||||
case compiler.OpGetLocal:
|
||||
|
@ -925,15 +1108,54 @@ func (v *VM) Run() error {
|
|||
v.stack[v.sp] = &builtinFuncs[builtinIndex]
|
||||
v.sp++
|
||||
|
||||
case compiler.OpGetBuiltinModule:
|
||||
val := v.stack[v.sp-1]
|
||||
v.sp--
|
||||
|
||||
moduleName := (*val).(*objects.String).Value
|
||||
|
||||
module, ok := v.builtinModules[moduleName]
|
||||
if !ok {
|
||||
filePos := v.fileSet.Position(v.curFrame.fn.SourceMap[v.ip-3])
|
||||
return fmt.Errorf("%s: module '%s' not found", filePos, moduleName)
|
||||
}
|
||||
|
||||
if v.sp >= StackSize {
|
||||
return ErrStackOverflow
|
||||
}
|
||||
|
||||
v.stack[v.sp] = module
|
||||
v.sp++
|
||||
|
||||
case compiler.OpClosure:
|
||||
constIndex := int(v.curInsts[v.ip+2]) | int(v.curInsts[v.ip+1])<<8
|
||||
numFree := int(v.curInsts[v.ip+3])
|
||||
v.ip += 3
|
||||
|
||||
if err := v.pushClosure(constIndex, numFree); err != nil {
|
||||
return err
|
||||
fn, ok := v.constants[constIndex].(*objects.CompiledFunction)
|
||||
if !ok {
|
||||
filePos := v.fileSet.Position(v.curFrame.fn.SourceMap[v.ip-3])
|
||||
return fmt.Errorf("%s: not function: %s", filePos, fn.TypeName())
|
||||
}
|
||||
|
||||
free := make([]*objects.Object, numFree)
|
||||
for i := 0; i < numFree; i++ {
|
||||
free[i] = v.stack[v.sp-numFree+i]
|
||||
}
|
||||
v.sp -= numFree
|
||||
|
||||
if v.sp >= StackSize {
|
||||
return ErrStackOverflow
|
||||
}
|
||||
|
||||
var cl objects.Object = &objects.Closure{
|
||||
Fn: fn,
|
||||
Free: free,
|
||||
}
|
||||
|
||||
v.stack[v.sp] = &cl
|
||||
v.sp++
|
||||
|
||||
case compiler.OpGetFree:
|
||||
freeIndex := int(v.curInsts[v.ip+1])
|
||||
v.ip++
|
||||
|
@ -958,7 +1180,8 @@ func (v *VM) Run() error {
|
|||
v.sp -= numSelectors + 1
|
||||
|
||||
if err := indexAssign(v.curFrame.freeVars[freeIndex], val, selectors); err != nil {
|
||||
return err
|
||||
filePos := v.fileSet.Position(v.curFrame.fn.SourceMap[v.ip-2])
|
||||
return fmt.Errorf("%s: %s", filePos, err.Error())
|
||||
}
|
||||
|
||||
case compiler.OpSetFree:
|
||||
|
@ -978,7 +1201,8 @@ func (v *VM) Run() error {
|
|||
|
||||
iterable, ok := (*dst).(objects.Iterable)
|
||||
if !ok {
|
||||
return fmt.Errorf("non-iterable type: %s", (*dst).TypeName())
|
||||
filePos := v.fileSet.Position(v.curFrame.fn.SourceMap[v.ip])
|
||||
return fmt.Errorf("%s: not iterable: %s", filePos, (*dst).TypeName())
|
||||
}
|
||||
|
||||
iterator = iterable.Iterate()
|
||||
|
@ -1034,13 +1258,13 @@ func (v *VM) Run() error {
|
|||
v.sp++
|
||||
|
||||
default:
|
||||
return fmt.Errorf("unknown opcode: %d", v.curInsts[v.ip])
|
||||
panic(fmt.Errorf("unknown opcode: %d", v.curInsts[v.ip]))
|
||||
}
|
||||
}
|
||||
|
||||
// check if stack still has some objects left
|
||||
if v.sp > 0 && atomic.LoadInt64(&v.aborting) == 0 {
|
||||
return fmt.Errorf("non empty stack after execution: %d", v.sp)
|
||||
panic(fmt.Errorf("non empty stack after execution: %d", v.sp))
|
||||
}
|
||||
|
||||
return nil
|
||||
|
@ -1056,141 +1280,21 @@ func (v *VM) FrameInfo() (frameIndex, ip int) {
|
|||
return v.framesIndex - 1, v.ip
|
||||
}
|
||||
|
||||
func (v *VM) pushClosure(constIndex, numFree int) error {
|
||||
c := v.constants[constIndex]
|
||||
|
||||
fn, ok := c.(*objects.CompiledFunction)
|
||||
if !ok {
|
||||
return fmt.Errorf("not a function: %s", fn.TypeName())
|
||||
}
|
||||
|
||||
free := make([]*objects.Object, numFree)
|
||||
for i := 0; i < numFree; i++ {
|
||||
free[i] = v.stack[v.sp-numFree+i]
|
||||
}
|
||||
v.sp -= numFree
|
||||
|
||||
if v.sp >= StackSize {
|
||||
return ErrStackOverflow
|
||||
}
|
||||
|
||||
var cl objects.Object = &objects.Closure{
|
||||
Fn: fn,
|
||||
Free: free,
|
||||
}
|
||||
|
||||
v.stack[v.sp] = &cl
|
||||
v.sp++
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (v *VM) callFunction(fn *objects.CompiledFunction, freeVars []*objects.Object, numArgs int) error {
|
||||
if numArgs != fn.NumParameters {
|
||||
return fmt.Errorf("wrong number of arguments: want=%d, got=%d",
|
||||
fn.NumParameters, numArgs)
|
||||
}
|
||||
|
||||
// check if this is a tail-call (recursive call right before return)
|
||||
if fn == v.curFrame.fn { // recursion
|
||||
nextOp := compiler.Opcode(v.curInsts[v.ip+1])
|
||||
if nextOp == compiler.OpReturnValue || // tail call
|
||||
(nextOp == compiler.OpPop &&
|
||||
compiler.OpReturn == compiler.Opcode(v.curInsts[v.ip+2])) {
|
||||
|
||||
// stack before tail-call
|
||||
//
|
||||
// |--------|
|
||||
// | | <- SP current
|
||||
// |--------|
|
||||
// | *ARG2 | for next function (tail-call)
|
||||
// |--------|
|
||||
// | *ARG1 | for next function (tail-call)
|
||||
// |--------|
|
||||
// | FUNC | function itself
|
||||
// |--------|
|
||||
// | LOCAL3 | for current function
|
||||
// |--------|
|
||||
// | LOCAL2 | for current function
|
||||
// |--------|
|
||||
// | ARG2 | for current function
|
||||
// |--------|
|
||||
// | ARG1 | <- BP for current function
|
||||
// |--------|
|
||||
|
||||
for p := 0; p < numArgs; p++ {
|
||||
v.stack[v.curFrame.basePointer+p] = v.stack[v.sp-numArgs+p]
|
||||
}
|
||||
v.sp -= numArgs + 1
|
||||
v.ip = -1 // reset IP to beginning of the frame
|
||||
|
||||
// stack after tail-call
|
||||
//
|
||||
// |--------|
|
||||
// | |
|
||||
// |--------|
|
||||
// | *ARG2 |
|
||||
// |--------|
|
||||
// | *ARG1 |
|
||||
// |--------|
|
||||
// | FUNC | <- SP current
|
||||
// |--------|
|
||||
// | LOCAL3 | for current function
|
||||
// |--------|
|
||||
// | LOCAL2 | for current function
|
||||
// |--------|
|
||||
// | *ARG2 | (copied)
|
||||
// |--------|
|
||||
// | *ARG1 | <- BP (copied)
|
||||
// |--------|
|
||||
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// store current ip before call
|
||||
v.curFrame.ip = v.ip
|
||||
|
||||
// update call frame
|
||||
v.curFrame = &(v.frames[v.framesIndex])
|
||||
v.curFrame.fn = fn
|
||||
v.curFrame.freeVars = freeVars
|
||||
v.curFrame.basePointer = v.sp - numArgs
|
||||
v.curInsts = fn.Instructions
|
||||
v.ip = -1
|
||||
v.curIPLimit = len(v.curInsts) - 1
|
||||
v.framesIndex++
|
||||
|
||||
v.sp = v.sp - numArgs + fn.NumLocals
|
||||
|
||||
// stack after the function call
|
||||
//
|
||||
// |--------|
|
||||
// | | <- SP after function call
|
||||
// |--------|
|
||||
// | LOCAL4 | (BP+3)
|
||||
// |--------|
|
||||
// | LOCAL3 | (BP+2) <- SP before function call
|
||||
// |--------|
|
||||
// | ARG2 | (BP+1)
|
||||
// |--------|
|
||||
// | ARG1 | (BP+0) <- BP
|
||||
// |--------|
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func indexAssign(dst, src *objects.Object, selectors []*objects.Object) error {
|
||||
numSel := len(selectors)
|
||||
|
||||
for sidx := numSel - 1; sidx > 0; sidx-- {
|
||||
indexable, ok := (*dst).(objects.Indexable)
|
||||
if !ok {
|
||||
return objects.ErrNotIndexable
|
||||
return fmt.Errorf("not indexable: %s", (*dst).TypeName())
|
||||
}
|
||||
|
||||
next, err := indexable.IndexGet(*selectors[sidx])
|
||||
if err != nil {
|
||||
if err == objects.ErrInvalidIndexType {
|
||||
return fmt.Errorf("invalid index type: %s", (*selectors[sidx]).TypeName())
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
|
@ -1199,15 +1303,26 @@ func indexAssign(dst, src *objects.Object, selectors []*objects.Object) error {
|
|||
|
||||
indexAssignable, ok := (*dst).(objects.IndexAssignable)
|
||||
if !ok {
|
||||
return objects.ErrNotIndexAssignable
|
||||
return fmt.Errorf("not index-assignable: %s", (*dst).TypeName())
|
||||
}
|
||||
|
||||
return indexAssignable.IndexSet(*selectors[0], *src)
|
||||
if err := indexAssignable.IndexSet(*selectors[0], *src); err != nil {
|
||||
if err == objects.ErrInvalidIndexValueType {
|
||||
return fmt.Errorf("invaid index value type: %s", (*src).TypeName())
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func init() {
|
||||
builtinFuncs = make([]objects.Object, len(objects.Builtins))
|
||||
for i, b := range objects.Builtins {
|
||||
builtinFuncs[i] = &objects.BuiltinFunction{Value: b.Func}
|
||||
builtinFuncs[i] = &objects.BuiltinFunction{
|
||||
Name: b.Name,
|
||||
Value: b.Func,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -15,7 +15,7 @@ func TestArray(t *testing.T) {
|
|||
expect(t, `func () { a1 := [1, 2, 3]; a2 := a1; a1[0] = 5; out = a2 }()`, ARR{5, 2, 3})
|
||||
|
||||
// array index set
|
||||
expectError(t, `a1 := [1, 2, 3]; a1[3] = 5`)
|
||||
expectError(t, `a1 := [1, 2, 3]; a1[3] = 5`, "index out of bounds")
|
||||
|
||||
// index operator
|
||||
arr := ARR{1, 2, 3, 4, 5, 6}
|
||||
|
@ -48,8 +48,8 @@ func TestArray(t *testing.T) {
|
|||
expect(t, fmt.Sprintf("out = %s[:%d]", arrStr, arrLen+1), arr)
|
||||
expect(t, fmt.Sprintf("out = %s[%d:%d]", arrStr, 2, 2), ARR{})
|
||||
|
||||
expectError(t, fmt.Sprintf("out = %s[:%d]", arrStr, -1))
|
||||
expectError(t, fmt.Sprintf("out = %s[%d:]", arrStr, arrLen+1))
|
||||
expectError(t, fmt.Sprintf("out = %s[%d:%d]", arrStr, 0, -1))
|
||||
expectError(t, fmt.Sprintf("%s[%d:%d]", arrStr, 2, 1))
|
||||
expectError(t, fmt.Sprintf("%s[:%d]", arrStr, -1), "invalid slice index")
|
||||
expectError(t, fmt.Sprintf("%s[%d:]", arrStr, arrLen+1), "invalid slice index")
|
||||
expectError(t, fmt.Sprintf("%s[%d:%d]", arrStr, 0, -1), "invalid slice index")
|
||||
expectError(t, fmt.Sprintf("%s[%d:%d]", arrStr, 2, 1), "invalid slice index")
|
||||
}
|
||||
|
|
|
@ -35,8 +35,8 @@ func() {
|
|||
}()
|
||||
}()`, 4)
|
||||
|
||||
expectError(t, `a := 1; a := 2`) // redeclared in the same scope
|
||||
expectError(t, `func() { a := 1; a := 2 }()`) // redeclared in the same scope
|
||||
expectError(t, `a := 1; a := 2`, "redeclared") // redeclared in the same scope
|
||||
expectError(t, `func() { a := 1; a := 2 }()`, "redeclared") // redeclared in the same scope
|
||||
|
||||
expect(t, `a := 1; a += 2; out = a`, 3)
|
||||
expect(t, `a := 1; a += 4 - 2;; out = a`, 3)
|
||||
|
@ -47,11 +47,11 @@ func() {
|
|||
expect(t, `a := 10; a /= 2;; out = a`, 5)
|
||||
expect(t, `a := 10; a /= 5 - 3;; out = a`, 5)
|
||||
|
||||
// +=, -=, *=, /= operator does not define new variable
|
||||
expectError(t, `a += 4`)
|
||||
expectError(t, `a -= 4`)
|
||||
expectError(t, `a *= 4`)
|
||||
expectError(t, `a /= 4`)
|
||||
// composite assignment operator does not define new variable
|
||||
expectError(t, `a += 4`, "unresolved reference")
|
||||
expectError(t, `a -= 4`, "unresolved reference")
|
||||
expectError(t, `a *= 4`, "unresolved reference")
|
||||
expectError(t, `a /= 4`, "unresolved reference")
|
||||
|
||||
expect(t, `
|
||||
f1 := func() {
|
||||
|
@ -193,8 +193,8 @@ out = func() {
|
|||
`, 136)
|
||||
|
||||
// assigning different type value
|
||||
expect(t, `a := 1; a = "foo"; out = a`, "foo") // global
|
||||
expect(t, `func() { a := 1; a = "foo"; out = a }()`, "foo") // local
|
||||
expect(t, `a := 1; a = "foo"; out = a`, "foo") // global
|
||||
expect(t, `func() { a := 1; a = "foo"; out = a }()`, "foo") // local
|
||||
expect(t, `
|
||||
out = func() {
|
||||
a := 5
|
||||
|
@ -250,7 +250,7 @@ a := {
|
|||
f: [9, 8]
|
||||
}
|
||||
}
|
||||
a.x.e = "bar"`)
|
||||
a.x.e = "bar"`, "not index-assignable")
|
||||
|
||||
// multi-variables
|
||||
//expect(t, `a, b = 1, 2; out = a + b`, 3)
|
||||
|
|
|
@ -31,23 +31,25 @@ func TestBoolean(t *testing.T) {
|
|||
expect(t, `out = (1 > 2) == true`, false)
|
||||
expect(t, `out = (1 > 2) == false`, true)
|
||||
|
||||
expectError(t, `5 + true`)
|
||||
expectError(t, `5 + true; 5`)
|
||||
expectError(t, `-true`)
|
||||
expectError(t, `true + false`)
|
||||
expectError(t, `5; true + false; 5`)
|
||||
expectError(t, `if (10 > 1) { true + false; }`)
|
||||
expectError(t, `5 + true`, "invalid operation")
|
||||
expectError(t, `5 + true; 5`, "invalid operation")
|
||||
expectError(t, `-true`, "invalid operation")
|
||||
expectError(t, `true + false`, "invalid operation")
|
||||
expectError(t, `5; true + false; 5`, "invalid operation")
|
||||
expectError(t, `if (10 > 1) { true + false; }`, "invalid operation")
|
||||
expectError(t, `
|
||||
if (10 > 1) {
|
||||
func() {
|
||||
if (10 > 1) {
|
||||
return true + false;
|
||||
}
|
||||
if (10 > 1) {
|
||||
return true + false;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
`)
|
||||
expectError(t, `if (true + false) { 10 }`)
|
||||
expectError(t, `10 + (true + false)`)
|
||||
expectError(t, `(true + false) + 20`)
|
||||
expectError(t, `!(true + false)`)
|
||||
return 1;
|
||||
}
|
||||
}()
|
||||
`, "invalid operation")
|
||||
expectError(t, `if (true + false) { 10 }`, "invalid operation")
|
||||
expectError(t, `10 + (true + false)`, "invalid operation")
|
||||
expectError(t, `(true + false) + 20`, "invalid operation")
|
||||
expectError(t, `!(true + false)`, "invalid operation")
|
||||
}
|
||||
|
|
|
@ -18,11 +18,11 @@ func TestBuiltinFunction(t *testing.T) {
|
|||
expect(t, `out = len(immutable([1, 2, 3]))`, 3)
|
||||
expect(t, `out = len(immutable({}))`, 0)
|
||||
expect(t, `out = len(immutable({a:1, b:2}))`, 2)
|
||||
expectError(t, `len(1)`)
|
||||
expectError(t, `len("one", "two")`)
|
||||
expectError(t, `len(1)`, "invalid type for argument")
|
||||
expectError(t, `len("one", "two")`, "wrong number of arguments")
|
||||
|
||||
expect(t, `out = copy(1)`, 1)
|
||||
expectError(t, `out = copy(1, 2)`)
|
||||
expectError(t, `copy(1, 2)`, "wrong number of arguments")
|
||||
|
||||
expect(t, `out = append([1, 2, 3], 4)`, ARR{1, 2, 3, 4})
|
||||
expect(t, `out = append([1, 2, 3], 4, 5, 6)`, ARR{1, 2, 3, 4, 5, 6})
|
||||
|
@ -157,8 +157,8 @@ func TestBuiltinFunction(t *testing.T) {
|
|||
expect(t, `out = sprintf("foo %d %v %s", 1, 2, "bar")`, "foo 1 2 bar")
|
||||
expect(t, `out = sprintf("foo %v", [1, "bar", true])`, "foo [1 bar true]")
|
||||
expect(t, `out = sprintf("foo %v %d", [1, "bar", true], 19)`, "foo [1 bar true] 19")
|
||||
expectError(t, `sprintf(1)`) // format has to be String
|
||||
expectError(t, `sprintf('c')`) // format has to be String
|
||||
expectError(t, `sprintf(1)`, "invalid type for argument") // format has to be String
|
||||
expectError(t, `sprintf('c')`, "invalid type for argument") // format has to be String
|
||||
|
||||
// type_name
|
||||
expect(t, `out = type_name(1)`, "int")
|
||||
|
@ -192,5 +192,4 @@ func TestBuiltinFunction(t *testing.T) {
|
|||
expect(t, `a := func(x) { return func() { return x } }; out = is_callable(a)`, true) // function
|
||||
expect(t, `a := func(x) { return func() { return x } }; out = is_callable(a(5))`, true) // closure
|
||||
expectWithSymbols(t, `out = is_callable(x)`, true, SYM{"x": &StringArray{Value: []string{"foo", "bar"}}}) // user object
|
||||
|
||||
}
|
||||
|
|
48
runtime/vm_error_report_test.go
Normal file
48
runtime/vm_error_report_test.go
Normal file
|
@ -0,0 +1,48 @@
|
|||
package runtime_test
|
||||
|
||||
import "testing"
|
||||
|
||||
func TestVMErrorInfo(t *testing.T) {
|
||||
expectError(t, `a := 5
|
||||
a + "boo"`,
|
||||
"test:2:1: invalid operation: int + string")
|
||||
|
||||
expectError(t, `a := 5
|
||||
b := a(5)`,
|
||||
"test:2:6: not callable: int")
|
||||
|
||||
expectError(t, `a := 5
|
||||
b := {}
|
||||
b.x.y = 10`,
|
||||
"test:3:1: not index-assignable: undefined")
|
||||
|
||||
expectError(t, `
|
||||
a := func() {
|
||||
b := 5
|
||||
b += "foo"
|
||||
}
|
||||
a()`,
|
||||
"test:4:2: invalid operation: int + string")
|
||||
|
||||
expectErrorWithUserModules(t, `a := 5
|
||||
a + import("mod1")`, map[string]string{
|
||||
"mod1": `export "foo"`,
|
||||
}, "test:2:2: invalid operation: int + string")
|
||||
|
||||
expectErrorWithUserModules(t, `a := import("mod1")()`, map[string]string{
|
||||
"mod1": `
|
||||
export func() {
|
||||
b := 5
|
||||
return b + "foo"
|
||||
}`,
|
||||
}, "mod1:4:9: invalid operation: int + string")
|
||||
|
||||
expectErrorWithUserModules(t, `a := import("mod1")()`, map[string]string{
|
||||
"mod1": `export import("mod2")()`,
|
||||
"mod2": `
|
||||
export func() {
|
||||
b := 5
|
||||
return b + "foo"
|
||||
}`,
|
||||
}, "mod2:4:9: invalid operation: int + string")
|
||||
}
|
|
@ -15,7 +15,7 @@ func TestError(t *testing.T) {
|
|||
expect(t, `out = error("some error").value`, "some error")
|
||||
expect(t, `out = error("some error")["value"]`, "some error")
|
||||
|
||||
expectError(t, `error("error").err`)
|
||||
expectError(t, `error("error").value_`)
|
||||
expectError(t, `error([1,2,3])[1]`)
|
||||
expectError(t, `error("error").err`, "invalid index on error")
|
||||
expectError(t, `error("error").value_`, "invalid index on error")
|
||||
expectError(t, `error([1,2,3])[1]`, "invalid index on error")
|
||||
}
|
||||
|
|
|
@ -135,9 +135,9 @@ func TestFunction(t *testing.T) {
|
|||
out = outer() + g
|
||||
`, 50)
|
||||
|
||||
expectError(t, `func() { return 1; }(1)`)
|
||||
expectError(t, `func(a) { return a; }()`)
|
||||
expectError(t, `func(a, b) { return a + b; }(1)`)
|
||||
expectError(t, `func() { return 1; }(1)`, "wrong number of arguments")
|
||||
expectError(t, `func(a) { return a; }()`, "wrong number of arguments")
|
||||
expectError(t, `func(a, b) { return a + b; }(1)`, "wrong number of arguments")
|
||||
|
||||
expect(t, `
|
||||
f1 := func(a) {
|
||||
|
@ -218,7 +218,7 @@ out = func() {
|
|||
return sum(5)
|
||||
}()`, 15)
|
||||
|
||||
expectError(t, `return 5`)
|
||||
expectError(t, `return 5`, "return not allowed outside function")
|
||||
|
||||
// closure and block scopes
|
||||
expect(t, `
|
||||
|
|
|
@ -14,11 +14,11 @@ func TestImmutable(t *testing.T) {
|
|||
expect(t, `a := immutable(1); a = 5; out = a`, 5)
|
||||
|
||||
// array
|
||||
expectError(t, `a := immutable([1, 2, 3]); a[1] = 5`)
|
||||
expectError(t, `a := immutable(["foo", [1,2,3]]); a[1] = "bar"`)
|
||||
expectError(t, `a := immutable([1, 2, 3]); a[1] = 5`, "not index-assignable")
|
||||
expectError(t, `a := immutable(["foo", [1,2,3]]); a[1] = "bar"`, "not index-assignable")
|
||||
expect(t, `a := immutable(["foo", [1,2,3]]); a[1][1] = "bar"; out = a`, IARR{"foo", ARR{1, "bar", 3}})
|
||||
expectError(t, `a := immutable(["foo", immutable([1,2,3])]); a[1][1] = "bar"`)
|
||||
expectError(t, `a := ["foo", immutable([1,2,3])]; a[1][1] = "bar"`)
|
||||
expectError(t, `a := immutable(["foo", immutable([1,2,3])]); a[1][1] = "bar"`, "not index-assignable")
|
||||
expectError(t, `a := ["foo", immutable([1,2,3])]; a[1][1] = "bar"`, "not index-assignable")
|
||||
expect(t, `a := immutable([1,2,3]); b := copy(a); b[1] = 5; out = b`, ARR{1, 5, 3})
|
||||
expect(t, `a := immutable([1,2,3]); b := copy(a); b[1] = 5; out = a`, IARR{1, 2, 3})
|
||||
expect(t, `out = immutable([1,2,3]) == [1,2,3]`, true)
|
||||
|
@ -33,11 +33,11 @@ func TestImmutable(t *testing.T) {
|
|||
expect(t, `a := immutable([1, 2, 3]); out = a[5]`, objects.UndefinedValue)
|
||||
|
||||
// map
|
||||
expectError(t, `a := immutable({b: 1, c: 2}); a.b = 5`)
|
||||
expectError(t, `a := immutable({b: 1, c: 2}); a["b"] = "bar"`)
|
||||
expectError(t, `a := immutable({b: 1, c: 2}); a.b = 5`, "not index-assignable")
|
||||
expectError(t, `a := immutable({b: 1, c: 2}); a["b"] = "bar"`, "not index-assignable")
|
||||
expect(t, `a := immutable({b: 1, c: [1,2,3]}); a.c[1] = "bar"; out = a`, IMAP{"b": 1, "c": ARR{1, "bar", 3}})
|
||||
expectError(t, `a := immutable({b: 1, c: immutable([1,2,3])}); a.c[1] = "bar"`)
|
||||
expectError(t, `a := {b: 1, c: immutable([1,2,3])}; a.c[1] = "bar"`)
|
||||
expectError(t, `a := immutable({b: 1, c: immutable([1,2,3])}); a.c[1] = "bar"`, "not index-assignable")
|
||||
expectError(t, `a := {b: 1, c: immutable([1,2,3])}; a.c[1] = "bar"`, "not index-assignable")
|
||||
expect(t, `out = immutable({a:1,b:2}) == {a:1,b:2}`, true)
|
||||
expect(t, `out = immutable({a:1,b:2}) == immutable({a:1,b:2})`, true)
|
||||
expect(t, `out = {a:1,b:2} == immutable({a:1,b:2})`, true)
|
||||
|
@ -50,5 +50,5 @@ func TestImmutable(t *testing.T) {
|
|||
expect(t, `a := immutable({a:1,b:2}); out = a.c`, objects.UndefinedValue)
|
||||
|
||||
expect(t, `a := immutable({b: 5, c: "foo"}); out = a.b`, 5)
|
||||
expectError(t, `a := immutable({b: 5, c: "foo"}); a.b = 10`)
|
||||
expectError(t, `a := immutable({b: 5, c: "foo"}); a.b = 10`, "not index-assignable")
|
||||
}
|
||||
|
|
|
@ -13,10 +13,10 @@ func TestIncDec(t *testing.T) {
|
|||
// this seems strange but it works because 'a += b' is
|
||||
// translated into 'a = a + b' and string type takes other types for + operator.
|
||||
expect(t, `a := "foo"; a++; out = a`, "foo1")
|
||||
expectError(t, `a := "foo"; a--`)
|
||||
expectError(t, `a := "foo"; a--`, "invalid operation")
|
||||
|
||||
expectError(t, `a++`) // not declared
|
||||
expectError(t, `a--`) // not declared
|
||||
expectError(t, `a++`, "unresolved reference") // not declared
|
||||
expectError(t, `a--`, "unresolved reference") // not declared
|
||||
//expectError(t, `a := 0; b := a++`) // inc-dec is statement not expression <- parser error
|
||||
expectError(t, `4++`)
|
||||
expectError(t, `4++`, "unresolved reference")
|
||||
}
|
||||
|
|
|
@ -51,7 +51,7 @@ func (o *StringDict) IndexSet(index, value objects.Object) error {
|
|||
|
||||
strVal, ok := objects.ToString(value)
|
||||
if !ok {
|
||||
return objects.ErrInvalidTypeConversion
|
||||
return objects.ErrInvalidIndexValueType
|
||||
}
|
||||
|
||||
o.Value[strings.ToLower(strIdx.Value)] = strVal
|
||||
|
@ -95,7 +95,7 @@ func (o *StringCircle) IndexSet(index, value objects.Object) error {
|
|||
|
||||
strVal, ok := objects.ToString(value)
|
||||
if !ok {
|
||||
return objects.ErrInvalidTypeConversion
|
||||
return objects.ErrInvalidIndexValueType
|
||||
}
|
||||
|
||||
o.Value[r] = strVal
|
||||
|
@ -184,7 +184,7 @@ func (o *StringArray) IndexGet(index objects.Object) (objects.Object, error) {
|
|||
func (o *StringArray) IndexSet(index, value objects.Object) error {
|
||||
strVal, ok := objects.ToString(value)
|
||||
if !ok {
|
||||
return objects.ErrInvalidTypeConversion
|
||||
return objects.ErrInvalidIndexValueType
|
||||
}
|
||||
|
||||
intIdx, ok := index.(*objects.Int)
|
||||
|
@ -207,7 +207,11 @@ func (o *StringArray) Call(args ...objects.Object) (ret objects.Object, err erro
|
|||
|
||||
s1, ok := objects.ToString(args[0])
|
||||
if !ok {
|
||||
return nil, objects.ErrInvalidTypeConversion
|
||||
return nil, objects.ErrInvalidArgumentType{
|
||||
Name: "first",
|
||||
Expected: "string(compatible)",
|
||||
Found: args[0].TypeName(),
|
||||
}
|
||||
}
|
||||
|
||||
for i, v := range o.Value {
|
||||
|
@ -224,7 +228,7 @@ func TestIndexable(t *testing.T) {
|
|||
expectWithSymbols(t, `out = dict["a"]`, "foo", SYM{"dict": dict()})
|
||||
expectWithSymbols(t, `out = dict["B"]`, "bar", SYM{"dict": dict()})
|
||||
expectWithSymbols(t, `out = dict["x"]`, objects.UndefinedValue, SYM{"dict": dict()})
|
||||
expectErrorWithSymbols(t, `out = dict[0]`, SYM{"dict": dict()})
|
||||
expectErrorWithSymbols(t, `dict[0]`, SYM{"dict": dict()}, "invalid index type")
|
||||
|
||||
strCir := func() *StringCircle { return &StringCircle{Value: []string{"one", "two", "three"}} }
|
||||
expectWithSymbols(t, `out = cir[0]`, "one", SYM{"cir": strCir()})
|
||||
|
@ -232,7 +236,7 @@ func TestIndexable(t *testing.T) {
|
|||
expectWithSymbols(t, `out = cir[-1]`, "three", SYM{"cir": strCir()})
|
||||
expectWithSymbols(t, `out = cir[-2]`, "two", SYM{"cir": strCir()})
|
||||
expectWithSymbols(t, `out = cir[3]`, "one", SYM{"cir": strCir()})
|
||||
expectErrorWithSymbols(t, `out = cir["a"]`, SYM{"cir": strCir()})
|
||||
expectErrorWithSymbols(t, `cir["a"]`, SYM{"cir": strCir()}, "invalid index type")
|
||||
|
||||
strArr := func() *StringArray { return &StringArray{Value: []string{"one", "two", "three"}} }
|
||||
expectWithSymbols(t, `out = arr["one"]`, 0, SYM{"arr": strArr()})
|
||||
|
@ -240,7 +244,7 @@ func TestIndexable(t *testing.T) {
|
|||
expectWithSymbols(t, `out = arr["four"]`, objects.UndefinedValue, SYM{"arr": strArr()})
|
||||
expectWithSymbols(t, `out = arr[0]`, "one", SYM{"arr": strArr()})
|
||||
expectWithSymbols(t, `out = arr[1]`, "two", SYM{"arr": strArr()})
|
||||
expectErrorWithSymbols(t, `out = arr[-1]`, SYM{"arr": strArr()})
|
||||
expectErrorWithSymbols(t, `arr[-1]`, SYM{"arr": strArr()}, "index out of bounds")
|
||||
}
|
||||
|
||||
func TestIndexAssignable(t *testing.T) {
|
||||
|
@ -248,17 +252,17 @@ func TestIndexAssignable(t *testing.T) {
|
|||
expectWithSymbols(t, `dict["a"] = "1984"; out = dict["a"]`, "1984", SYM{"dict": dict()})
|
||||
expectWithSymbols(t, `dict["c"] = "1984"; out = dict["c"]`, "1984", SYM{"dict": dict()})
|
||||
expectWithSymbols(t, `dict["c"] = 1984; out = dict["C"]`, "1984", SYM{"dict": dict()})
|
||||
expectErrorWithSymbols(t, `dict[0] = "1984"`, SYM{"dict": dict()})
|
||||
expectErrorWithSymbols(t, `dict[0] = "1984"`, SYM{"dict": dict()}, "invalid index type")
|
||||
|
||||
strCir := func() *StringCircle { return &StringCircle{Value: []string{"one", "two", "three"}} }
|
||||
expectWithSymbols(t, `cir[0] = "ONE"; out = cir[0]`, "ONE", SYM{"cir": strCir()})
|
||||
expectWithSymbols(t, `cir[1] = "TWO"; out = cir[1]`, "TWO", SYM{"cir": strCir()})
|
||||
expectWithSymbols(t, `cir[-1] = "THREE"; out = cir[2]`, "THREE", SYM{"cir": strCir()})
|
||||
expectWithSymbols(t, `cir[0] = "ONE"; out = cir[3]`, "ONE", SYM{"cir": strCir()})
|
||||
expectErrorWithSymbols(t, `cir["a"] = "ONE"`, SYM{"cir": strCir()})
|
||||
expectErrorWithSymbols(t, `cir["a"] = "ONE"`, SYM{"cir": strCir()}, "invalid index type")
|
||||
|
||||
strArr := func() *StringArray { return &StringArray{Value: []string{"one", "two", "three"}} }
|
||||
expectWithSymbols(t, `arr[0] = "ONE"; out = arr[0]`, "ONE", SYM{"arr": strArr()})
|
||||
expectWithSymbols(t, `arr[1] = "TWO"; out = arr[1]`, "TWO", SYM{"arr": strArr()})
|
||||
expectErrorWithSymbols(t, `arr["one"] = "ONE"`, SYM{"arr": strArr()})
|
||||
expectErrorWithSymbols(t, `arr["one"] = "ONE"`, SYM{"arr": strArr()}, "invalid index type")
|
||||
}
|
||||
|
|
|
@ -86,10 +86,10 @@ func TestUserModules(t *testing.T) {
|
|||
// export value is immutable
|
||||
expectErrorWithUserModules(t, `m1 := import("mod1"); m1.a = 5`, map[string]string{
|
||||
"mod1": `export {a: 1, b: 2}`,
|
||||
})
|
||||
}, "not index-assignable")
|
||||
expectErrorWithUserModules(t, `m1 := import("mod1"); m1[1] = 5`, map[string]string{
|
||||
"mod1": `export [1, 2, 3]`,
|
||||
})
|
||||
}, "not index-assignable")
|
||||
|
||||
// code after export statement will not be executed
|
||||
expectWithUserModules(t, `out = import("mod1")`, 10, map[string]string{
|
||||
|
@ -160,27 +160,27 @@ export func() {
|
|||
expectErrorWithUserModules(t, `import("mod1")`, map[string]string{
|
||||
"mod1": `import("mod2")`,
|
||||
"mod2": `import("mod1")`,
|
||||
})
|
||||
}, "mod2:1:1: cyclic module import")
|
||||
// (main) -> mod1 -> mod2 -> mod3 -> mod1
|
||||
expectErrorWithUserModules(t, `import("mod1")`, map[string]string{
|
||||
"mod1": `import("mod2")`,
|
||||
"mod2": `import("mod3")`,
|
||||
"mod3": `import("mod1")`,
|
||||
})
|
||||
}, "mod3:1:1: cyclic module import")
|
||||
// (main) -> mod1 -> mod2 -> mod3 -> mod2
|
||||
expectErrorWithUserModules(t, `import("mod1")`, map[string]string{
|
||||
"mod1": `import("mod2")`,
|
||||
"mod2": `import("mod3")`,
|
||||
"mod3": `import("mod2")`,
|
||||
})
|
||||
}, "mod3:1:1: cyclic module import")
|
||||
|
||||
// unknown modules
|
||||
expectErrorWithUserModules(t, `import("mod0")`, map[string]string{
|
||||
"mod1": `a := 5`,
|
||||
})
|
||||
}, "module 'mod0' not found")
|
||||
expectErrorWithUserModules(t, `import("mod1")`, map[string]string{
|
||||
"mod1": `import("mod2")`,
|
||||
})
|
||||
}, "module 'mod2' not found")
|
||||
|
||||
// module is immutable but its variables is not necessarily immutable.
|
||||
expectWithUserModules(t, `m1 := import("mod1"); m1.a.b = 5; out = m1.a.b`, 5, map[string]string{
|
||||
|
@ -198,15 +198,15 @@ export func() {
|
|||
// 'export' must be in the top-level
|
||||
expectErrorWithUserModules(t, `import("mod1")`, map[string]string{
|
||||
"mod1": `func() { export 5 }()`,
|
||||
})
|
||||
}, "mod1:1:10: export not allowed inside function")
|
||||
expectErrorWithUserModules(t, `import("mod1")`, map[string]string{
|
||||
"mod1": `func() { func() { export 5 }() }()`,
|
||||
})
|
||||
}, "mod1:1:19: export not allowed inside function")
|
||||
|
||||
// module cannot access outer scope
|
||||
expectErrorWithUserModules(t, `a := 5; import("mod")`, map[string]string{
|
||||
expectErrorWithUserModules(t, `a := 5; import("mod1")`, map[string]string{
|
||||
"mod1": `export a`,
|
||||
})
|
||||
}, "mod1:1:8: unresolved reference 'a'")
|
||||
}
|
||||
|
||||
func TestModuleBlockScopes(t *testing.T) {
|
||||
|
|
|
@ -21,7 +21,7 @@ a := {
|
|||
}
|
||||
out = a.b.c`, 4)
|
||||
|
||||
expectError(t, `
|
||||
expect(t, `
|
||||
a := {
|
||||
b: {
|
||||
c: 4,
|
||||
|
@ -29,9 +29,9 @@ a := {
|
|||
},
|
||||
c: "foo bar"
|
||||
}
|
||||
out = a.x.c`)
|
||||
b := a.x.c`, objects.UndefinedValue)
|
||||
|
||||
expectError(t, `
|
||||
expect(t, `
|
||||
a := {
|
||||
b: {
|
||||
c: 4,
|
||||
|
@ -39,7 +39,7 @@ a := {
|
|||
},
|
||||
c: "foo bar"
|
||||
}
|
||||
out = a.x.y`)
|
||||
b := a.x.y`, objects.UndefinedValue)
|
||||
|
||||
expect(t, `a := {b: 1, c: "foo"}; a.b = 2; out = a.b`, 2)
|
||||
expect(t, `a := {b: 1, c: "foo"}; a.c = 2; out = a.c`, 2) // type not checked on sub-field
|
||||
|
@ -81,10 +81,10 @@ func() {
|
|||
}()
|
||||
`, 9)
|
||||
|
||||
expectError(t, `a := {b: {c: 1}}; a.d.c = 2`)
|
||||
expectError(t, `a := [1, 2, 3]; a.b = 2`)
|
||||
expectError(t, `a := "foo"; a.b = 2`)
|
||||
expectError(t, `func() { a := {b: {c: 1}}; a.d.c = 2 }()`)
|
||||
expectError(t, `func() { a := [1, 2, 3]; a.b = 2 }()`)
|
||||
expectError(t, `func() { a := "foo"; a.b = 2 }()`)
|
||||
expectError(t, `a := {b: {c: 1}}; a.d.c = 2`, "not index-assignable")
|
||||
expectError(t, `a := [1, 2, 3]; a.b = 2`, "invalid index type")
|
||||
expectError(t, `a := "foo"; a.b = 2`, "not index-assignable")
|
||||
expectError(t, `func() { a := {b: {c: 1}}; a.d.c = 2 }()`, "not index-assignable")
|
||||
expectError(t, `func() { a := [1, 2, 3]; a.b = 2 }()`, "invalid index type")
|
||||
expectError(t, `func() { a := "foo"; a.b = 2 }()`, "not index-assignable")
|
||||
}
|
||||
|
|
|
@ -48,10 +48,10 @@ func TestString(t *testing.T) {
|
|||
expect(t, fmt.Sprintf("out = %s[:%d]", strStr, strLen+1), str)
|
||||
expect(t, fmt.Sprintf("out = %s[%d:%d]", strStr, 2, 2), "")
|
||||
|
||||
expectError(t, fmt.Sprintf("out = %s[:%d]", strStr, -1))
|
||||
expectError(t, fmt.Sprintf("out = %s[%d:]", strStr, strLen+1))
|
||||
expectError(t, fmt.Sprintf("out = %s[%d:%d]", strStr, 0, -1))
|
||||
expectError(t, fmt.Sprintf("%s[%d:%d]", strStr, 2, 1))
|
||||
expectError(t, fmt.Sprintf("%s[:%d]", strStr, -1), "invalid slice index")
|
||||
expectError(t, fmt.Sprintf("%s[%d:]", strStr, strLen+1), "invalid slice index")
|
||||
expectError(t, fmt.Sprintf("%s[%d:%d]", strStr, 0, -1), "invalid slice index")
|
||||
expectError(t, fmt.Sprintf("%s[%d:%d]", strStr, 2, 1), "invalid slice index")
|
||||
|
||||
// string concatenation with other types
|
||||
expect(t, `out = "foo" + 1`, "foo1")
|
||||
|
@ -68,7 +68,7 @@ func TestString(t *testing.T) {
|
|||
// also works with "+=" operator
|
||||
expect(t, `out = "foo"; out += 1.5`, "foo1.5")
|
||||
// string concats works only when string is LHS
|
||||
expectError(t, `1 + "foo"`)
|
||||
expectError(t, `1 + "foo"`, "invalid operation")
|
||||
|
||||
expectError(t, `"foo" - "bar"`)
|
||||
expectError(t, `"foo" - "bar"`, "invalid operation")
|
||||
}
|
||||
|
|
|
@ -78,6 +78,19 @@ iter := func(n, max) {
|
|||
return iter(n+1, max)
|
||||
}
|
||||
out = iter(0, 9999)
|
||||
`, 9999)
|
||||
expect(t, `
|
||||
c := 0
|
||||
iter := func(n, max) {
|
||||
if n == max {
|
||||
return
|
||||
}
|
||||
|
||||
c++
|
||||
iter(n+1, max)
|
||||
}
|
||||
iter(0, 9999)
|
||||
out = c
|
||||
`, 9999)
|
||||
}
|
||||
|
||||
|
|
|
@ -52,11 +52,16 @@ func expectWithUserModules(t *testing.T, input string, expected interface{}, use
|
|||
runVM(t, file, expected, nil, userModules)
|
||||
}
|
||||
|
||||
func expectError(t *testing.T, input string) {
|
||||
expectErrorWithUserModules(t, input, nil)
|
||||
func expectError(t *testing.T, input, expected string) {
|
||||
expected = strings.TrimSpace(expected)
|
||||
if expected == "" {
|
||||
panic("expected must not be empty")
|
||||
}
|
||||
|
||||
expectErrorWithUserModules(t, input, nil, expected)
|
||||
}
|
||||
|
||||
func expectErrorWithUserModules(t *testing.T, input string, userModules map[string]string) {
|
||||
func expectErrorWithUserModules(t *testing.T, input string, userModules map[string]string, expected string) {
|
||||
// parse
|
||||
program := parse(t, input)
|
||||
if program == nil {
|
||||
|
@ -64,10 +69,14 @@ func expectErrorWithUserModules(t *testing.T, input string, userModules map[stri
|
|||
}
|
||||
|
||||
// compiler/VM
|
||||
runVMError(t, program, nil, userModules)
|
||||
_, trace, err := traceCompileRun(program, nil, userModules)
|
||||
if !assert.Error(t, err) ||
|
||||
!assert.True(t, strings.Contains(err.Error(), expected), "expected error string: %s, got: %s", expected, err.Error()) {
|
||||
t.Log("\n" + strings.Join(trace, "\n"))
|
||||
}
|
||||
}
|
||||
|
||||
func expectErrorWithSymbols(t *testing.T, input string, symbols map[string]objects.Object) {
|
||||
func expectErrorWithSymbols(t *testing.T, input string, symbols map[string]objects.Object, expected string) {
|
||||
// parse
|
||||
program := parse(t, input)
|
||||
if program == nil {
|
||||
|
@ -75,7 +84,11 @@ func expectErrorWithSymbols(t *testing.T, input string, symbols map[string]objec
|
|||
}
|
||||
|
||||
// compiler/VM
|
||||
runVMError(t, program, symbols, nil)
|
||||
_, trace, err := traceCompileRun(program, symbols, nil)
|
||||
if !assert.Error(t, err) ||
|
||||
!assert.True(t, strings.Contains(err.Error(), expected), "expected error string: %s, got: %s", expected, err.Error()) {
|
||||
t.Log("\n" + strings.Join(trace, "\n"))
|
||||
}
|
||||
}
|
||||
|
||||
func runVM(t *testing.T, file *ast.File, expected interface{}, symbols map[string]objects.Object, userModules map[string]string) (ok bool) {
|
||||
|
@ -103,21 +116,6 @@ func runVM(t *testing.T, file *ast.File, expected interface{}, symbols map[strin
|
|||
return
|
||||
}
|
||||
|
||||
// TODO: should differentiate compile-time error, runtime error, and, error object returned
|
||||
func runVMError(t *testing.T, file *ast.File, symbols map[string]objects.Object, userModules map[string]string) (ok bool) {
|
||||
_, trace, err := traceCompileRun(file, symbols, userModules)
|
||||
|
||||
defer func() {
|
||||
if !ok {
|
||||
t.Log("\n" + strings.Join(trace, "\n"))
|
||||
}
|
||||
}()
|
||||
|
||||
ok = assert.Error(t, err)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func errorObject(v interface{}) *objects.Error {
|
||||
return &objects.Error{Value: toObject(v)}
|
||||
}
|
||||
|
@ -230,7 +228,7 @@ func traceCompileRun(file *ast.File, symbols map[string]objects.Object, userModu
|
|||
}
|
||||
|
||||
tr := &tracer{}
|
||||
c := compiler.NewCompiler(symTable, nil, nil, tr)
|
||||
c := compiler.NewCompiler(file.InputFile, symTable, nil, nil, tr)
|
||||
c.SetModuleLoader(func(moduleName string) ([]byte, error) {
|
||||
if src, ok := userModules[moduleName]; ok {
|
||||
return []byte(src), nil
|
||||
|
@ -248,7 +246,7 @@ func traceCompileRun(file *ast.File, symbols map[string]objects.Object, userModu
|
|||
trace = append(trace, fmt.Sprintf("\n[Compiled Constants]\n\n%s", strings.Join(bytecode.FormatConstants(), "\n")))
|
||||
trace = append(trace, fmt.Sprintf("\n[Compiled Instructions]\n\n%s\n", strings.Join(bytecode.FormatInstructions(), "\n")))
|
||||
|
||||
v = runtime.NewVM(bytecode, globals)
|
||||
v = runtime.NewVM(bytecode, globals, nil)
|
||||
|
||||
err = v.Run()
|
||||
{
|
||||
|
@ -296,7 +294,7 @@ func formatGlobals(globals []*objects.Object) (formatted []string) {
|
|||
|
||||
func parse(t *testing.T, input string) *ast.File {
|
||||
testFileSet := source.NewFileSet()
|
||||
testFile := testFileSet.AddFile("", -1, len(input))
|
||||
testFile := testFileSet.AddFile("test", -1, len(input))
|
||||
|
||||
file, err := parser.ParseFile(testFile, []byte(input), nil)
|
||||
if !assert.NoError(t, err) {
|
||||
|
|
|
@ -7,9 +7,9 @@ import (
|
|||
"github.com/d5/tengo/compiler"
|
||||
"github.com/d5/tengo/compiler/parser"
|
||||
"github.com/d5/tengo/compiler/source"
|
||||
"github.com/d5/tengo/compiler/stdlib"
|
||||
"github.com/d5/tengo/objects"
|
||||
"github.com/d5/tengo/runtime"
|
||||
"github.com/d5/tengo/stdlib"
|
||||
)
|
||||
|
||||
// Script can simplify compilation and execution of embedded scripts.
|
||||
|
@ -87,14 +87,15 @@ func (s *Script) Compile() (*Compiled, error) {
|
|||
}
|
||||
|
||||
fileSet := source.NewFileSet()
|
||||
srcFile := fileSet.AddFile("(main)", -1, len(s.input))
|
||||
|
||||
p := parser.NewParser(fileSet.AddFile("", -1, len(s.input)), s.input, nil)
|
||||
p := parser.NewParser(srcFile, s.input, nil)
|
||||
file, err := p.ParseFile()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("parse error: %s", err.Error())
|
||||
}
|
||||
|
||||
c := compiler.NewCompiler(symbolTable, nil, stdModules, nil)
|
||||
c := compiler.NewCompiler(srcFile, symbolTable, nil, stdModules, nil)
|
||||
|
||||
if s.userModuleLoader != nil {
|
||||
c.SetModuleLoader(s.userModuleLoader)
|
||||
|
@ -106,7 +107,7 @@ func (s *Script) Compile() (*Compiled, error) {
|
|||
|
||||
return &Compiled{
|
||||
symbolTable: symbolTable,
|
||||
machine: runtime.NewVM(c.Bytecode(), globals),
|
||||
machine: runtime.NewVM(c.Bytecode(), globals, nil),
|
||||
}, nil
|
||||
}
|
||||
|
||||
|
@ -135,7 +136,7 @@ func (s *Script) RunContext(ctx context.Context) (compiled *Compiled, err error)
|
|||
return
|
||||
}
|
||||
|
||||
func (s *Script) prepCompile() (symbolTable *compiler.SymbolTable, stdModules map[string]*objects.ImmutableMap, globals []*objects.Object, err error) {
|
||||
func (s *Script) prepCompile() (symbolTable *compiler.SymbolTable, stdModules map[string]bool, globals []*objects.Object, err error) {
|
||||
var names []string
|
||||
for name := range s.variables {
|
||||
names = append(names, name)
|
||||
|
@ -148,10 +149,10 @@ func (s *Script) prepCompile() (symbolTable *compiler.SymbolTable, stdModules ma
|
|||
}
|
||||
}
|
||||
|
||||
stdModules = make(map[string]*objects.ImmutableMap)
|
||||
for name, mod := range stdlib.Modules {
|
||||
stdModules = make(map[string]bool)
|
||||
for name := range stdlib.Modules {
|
||||
if !s.removedStdModules[name] {
|
||||
stdModules[name] = mod
|
||||
stdModules[name] = true
|
||||
}
|
||||
}
|
||||
|
||||
|
|
1057
stdlib/func_typedefs.go
Normal file
1057
stdlib/func_typedefs.go
Normal file
File diff suppressed because it is too large
Load diff
|
@ -7,215 +7,216 @@ import (
|
|||
"testing"
|
||||
|
||||
"github.com/d5/tengo/assert"
|
||||
"github.com/d5/tengo/compiler/stdlib"
|
||||
"github.com/d5/tengo/objects"
|
||||
"github.com/d5/tengo/stdlib"
|
||||
)
|
||||
|
||||
func TestFuncAIR(t *testing.T) {
|
||||
uf := stdlib.FuncAIR(func(int) {})
|
||||
ret, err := uf.Call(&objects.Int{Value: 10})
|
||||
ret, err := funcCall(uf, &objects.Int{Value: 10})
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, objects.UndefinedValue, ret)
|
||||
_, err = uf.Call()
|
||||
_, err = funcCall(uf)
|
||||
assert.Equal(t, objects.ErrWrongNumArguments, err)
|
||||
}
|
||||
|
||||
func TestFuncAR(t *testing.T) {
|
||||
uf := stdlib.FuncAR(func() {})
|
||||
ret, err := uf.Call()
|
||||
ret, err := funcCall(uf)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, objects.UndefinedValue, ret)
|
||||
_, err = uf.Call(objects.TrueValue)
|
||||
_, err = funcCall(uf, objects.TrueValue)
|
||||
assert.Equal(t, objects.ErrWrongNumArguments, err)
|
||||
}
|
||||
|
||||
func TestFuncARI(t *testing.T) {
|
||||
uf := stdlib.FuncARI(func() int { return 10 })
|
||||
ret, err := uf.Call()
|
||||
ret, err := funcCall(uf)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, &objects.Int{Value: 10}, ret)
|
||||
_, err = uf.Call(objects.TrueValue)
|
||||
_, err = funcCall(uf, objects.TrueValue)
|
||||
assert.Equal(t, objects.ErrWrongNumArguments, err)
|
||||
}
|
||||
|
||||
func TestFuncARE(t *testing.T) {
|
||||
uf := stdlib.FuncARE(func() error { return nil })
|
||||
ret, err := uf.Call()
|
||||
ret, err := funcCall(uf)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, objects.TrueValue, ret)
|
||||
uf = stdlib.FuncARE(func() error { return errors.New("some error") })
|
||||
ret, err = uf.Call()
|
||||
ret, err = funcCall(uf)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, &objects.Error{Value: &objects.String{Value: "some error"}}, ret)
|
||||
_, err = uf.Call(objects.TrueValue)
|
||||
_, err = funcCall(uf, 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()
|
||||
ret, err := funcCall(uf)
|
||||
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()
|
||||
ret, err = funcCall(uf)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, &objects.Error{Value: &objects.String{Value: "some error"}}, ret)
|
||||
_, err = uf.Call(objects.TrueValue)
|
||||
_, err = funcCall(uf, objects.TrueValue)
|
||||
assert.Equal(t, objects.ErrWrongNumArguments, err)
|
||||
}
|
||||
|
||||
func TestFuncARS(t *testing.T) {
|
||||
uf := stdlib.FuncARS(func() string { return "foo" })
|
||||
ret, err := uf.Call()
|
||||
ret, err := funcCall(uf)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, &objects.String{Value: "foo"}, ret)
|
||||
_, err = uf.Call(objects.TrueValue)
|
||||
_, err = funcCall(uf, 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()
|
||||
ret, err := funcCall(uf)
|
||||
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()
|
||||
ret, err = funcCall(uf)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, &objects.Error{Value: &objects.String{Value: "some error"}}, ret)
|
||||
_, err = uf.Call(objects.TrueValue)
|
||||
_, err = funcCall(uf, 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()
|
||||
ret, err := funcCall(uf)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, array(&objects.String{Value: "foo"}, &objects.String{Value: "bar"}), ret)
|
||||
_, err = uf.Call(objects.TrueValue)
|
||||
_, err = funcCall(uf, 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"})
|
||||
ret, err := funcCall(uf, &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"})
|
||||
ret, err = funcCall(uf, &objects.String{Value: "foo"})
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, &objects.Error{Value: &objects.String{Value: "some error"}}, ret)
|
||||
_, err = uf.Call()
|
||||
_, err = funcCall(uf)
|
||||
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"})
|
||||
ret, err := funcCall(uf, &objects.String{Value: "foo"})
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, &objects.String{Value: "foo"}, ret)
|
||||
_, err = uf.Call()
|
||||
_, err = funcCall(uf)
|
||||
assert.Equal(t, objects.ErrWrongNumArguments, err)
|
||||
}
|
||||
|
||||
func TestFuncASRSs(t *testing.T) {
|
||||
uf := stdlib.FuncASRSs(func(a string) []string { return []string{a} })
|
||||
ret, err := uf.Call(&objects.String{Value: "foo"})
|
||||
ret, err := funcCall(uf, &objects.String{Value: "foo"})
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, array(&objects.String{Value: "foo"}), ret)
|
||||
_, err = uf.Call()
|
||||
_, err = funcCall(uf)
|
||||
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})
|
||||
ret, err := funcCall(uf, &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})
|
||||
ret, err = funcCall(uf, &objects.String{Value: "foo"}, &objects.Int{Value: 5})
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, &objects.Error{Value: &objects.String{Value: "some error"}}, ret)
|
||||
_, err = uf.Call()
|
||||
_, err = funcCall(uf)
|
||||
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})
|
||||
ret, err := funcCall(uf, &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})
|
||||
ret, err = funcCall(uf, &objects.Int{Value: 5}, &objects.Int{Value: 7})
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, &objects.Error{Value: &objects.String{Value: "some error"}}, ret)
|
||||
_, err = uf.Call()
|
||||
_, err = funcCall(uf)
|
||||
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})
|
||||
ret, err := funcCall(uf, &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})
|
||||
ret, err = funcCall(uf, &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)
|
||||
_, err = uf.Call()
|
||||
_, err = funcCall(uf)
|
||||
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"})
|
||||
ret, err := funcCall(uf, &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"})
|
||||
ret, err = funcCall(uf, &objects.String{Value: "foo"})
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, &objects.Error{Value: &objects.String{Value: "some error"}}, ret)
|
||||
_, err = uf.Call()
|
||||
_, err = funcCall(uf)
|
||||
assert.Equal(t, objects.ErrWrongNumArguments, err)
|
||||
}
|
||||
|
||||
func TestFuncASSRE(t *testing.T) {
|
||||
uf := stdlib.FuncASSRE(func(a, b string) error { return nil })
|
||||
ret, err := uf.Call(&objects.String{Value: "foo"}, &objects.String{Value: "bar"})
|
||||
ret, err := funcCall(uf, &objects.String{Value: "foo"}, &objects.String{Value: "bar"})
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, objects.TrueValue, ret)
|
||||
uf = stdlib.FuncASSRE(func(a, b string) error { return errors.New("some error") })
|
||||
ret, err = uf.Call(&objects.String{Value: "foo"}, &objects.String{Value: "bar"})
|
||||
ret, err = funcCall(uf, &objects.String{Value: "foo"}, &objects.String{Value: "bar"})
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, &objects.Error{Value: &objects.String{Value: "some error"}}, ret)
|
||||
_, err = uf.Call(&objects.String{Value: "foo"})
|
||||
_, err = funcCall(uf, &objects.String{Value: "foo"})
|
||||
assert.Equal(t, objects.ErrWrongNumArguments, err)
|
||||
}
|
||||
|
||||
func TestFuncASsRS(t *testing.T) {
|
||||
uf := stdlib.FuncASsSRS(func(a []string, b string) string { return strings.Join(a, b) })
|
||||
ret, err := uf.Call(array(&objects.String{Value: "foo"}, &objects.String{Value: "bar"}), &objects.String{Value: " "})
|
||||
ret, err := funcCall(uf, array(&objects.String{Value: "foo"}, &objects.String{Value: "bar"}), &objects.String{Value: " "})
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, &objects.String{Value: "foo bar"}, ret)
|
||||
_, err = uf.Call(&objects.String{Value: "foo"})
|
||||
_, err = funcCall(uf, &objects.String{Value: "foo"})
|
||||
assert.Equal(t, objects.ErrWrongNumArguments, err)
|
||||
}
|
||||
|
||||
func TestFuncARF(t *testing.T) {
|
||||
uf := stdlib.FuncARF(func() float64 { return 10.0 })
|
||||
ret, err := uf.Call()
|
||||
ret, err := funcCall(uf)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, &objects.Float{Value: 10.0}, ret)
|
||||
_, err = uf.Call(objects.TrueValue)
|
||||
_, err = funcCall(uf, 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})
|
||||
ret, err := funcCall(uf, &objects.Float{Value: 10.0})
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, &objects.Float{Value: 10.0}, ret)
|
||||
_, err = uf.Call()
|
||||
_, err = funcCall(uf)
|
||||
assert.Equal(t, objects.ErrWrongNumArguments, err)
|
||||
_, err = uf.Call(objects.TrueValue, objects.TrueValue)
|
||||
_, err = funcCall(uf, objects.TrueValue, objects.TrueValue)
|
||||
assert.Equal(t, objects.ErrWrongNumArguments, err)
|
||||
}
|
||||
|
||||
|
@ -223,12 +224,12 @@ func TestFuncAIRF(t *testing.T) {
|
|||
uf := stdlib.FuncAIRF(func(a int) float64 {
|
||||
return float64(a)
|
||||
})
|
||||
ret, err := uf.Call(&objects.Int{Value: 10.0})
|
||||
ret, err := funcCall(uf, &objects.Int{Value: 10.0})
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, &objects.Float{Value: 10.0}, ret)
|
||||
_, err = uf.Call()
|
||||
_, err = funcCall(uf)
|
||||
assert.Equal(t, objects.ErrWrongNumArguments, err)
|
||||
_, err = uf.Call(objects.TrueValue, objects.TrueValue)
|
||||
_, err = funcCall(uf, objects.TrueValue, objects.TrueValue)
|
||||
assert.Equal(t, objects.ErrWrongNumArguments, err)
|
||||
}
|
||||
|
||||
|
@ -236,12 +237,12 @@ func TestFuncAFRI(t *testing.T) {
|
|||
uf := stdlib.FuncAFRI(func(a float64) int {
|
||||
return int(a)
|
||||
})
|
||||
ret, err := uf.Call(&objects.Float{Value: 10.5})
|
||||
ret, err := funcCall(uf, &objects.Float{Value: 10.5})
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, &objects.Int{Value: 10}, ret)
|
||||
_, err = uf.Call()
|
||||
_, err = funcCall(uf)
|
||||
assert.Equal(t, objects.ErrWrongNumArguments, err)
|
||||
_, err = uf.Call(objects.TrueValue, objects.TrueValue)
|
||||
_, err = funcCall(uf, objects.TrueValue, objects.TrueValue)
|
||||
assert.Equal(t, objects.ErrWrongNumArguments, err)
|
||||
}
|
||||
|
||||
|
@ -249,12 +250,12 @@ 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})
|
||||
ret, err := funcCall(uf, &objects.Float{Value: 0.1})
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, objects.TrueValue, ret)
|
||||
_, err = uf.Call()
|
||||
_, err = funcCall(uf)
|
||||
assert.Equal(t, objects.ErrWrongNumArguments, err)
|
||||
_, err = uf.Call(objects.TrueValue, objects.TrueValue)
|
||||
_, err = funcCall(uf, objects.TrueValue, objects.TrueValue)
|
||||
assert.Equal(t, objects.ErrWrongNumArguments, err)
|
||||
}
|
||||
|
||||
|
@ -262,23 +263,23 @@ 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})
|
||||
ret, err := funcCall(uf, &objects.Float{Value: 10.0}, &objects.Float{Value: 20.0})
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, &objects.Float{Value: 30.0}, ret)
|
||||
_, err = uf.Call()
|
||||
_, err = funcCall(uf)
|
||||
assert.Equal(t, objects.ErrWrongNumArguments, err)
|
||||
_, err = uf.Call(objects.TrueValue)
|
||||
_, err = funcCall(uf, objects.TrueValue)
|
||||
assert.Equal(t, objects.ErrWrongNumArguments, err)
|
||||
}
|
||||
|
||||
func TestFuncASIRS(t *testing.T) {
|
||||
uf := stdlib.FuncASIRS(func(a string, b int) string { return strings.Repeat(a, b) })
|
||||
ret, err := uf.Call(&objects.String{Value: "ab"}, &objects.Int{Value: 2})
|
||||
ret, err := funcCall(uf, &objects.String{Value: "ab"}, &objects.Int{Value: 2})
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, &objects.String{Value: "abab"}, ret)
|
||||
_, err = uf.Call()
|
||||
_, err = funcCall(uf)
|
||||
assert.Equal(t, objects.ErrWrongNumArguments, err)
|
||||
_, err = uf.Call(objects.TrueValue)
|
||||
_, err = funcCall(uf, objects.TrueValue)
|
||||
assert.Equal(t, objects.ErrWrongNumArguments, err)
|
||||
}
|
||||
|
||||
|
@ -286,12 +287,12 @@ 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})
|
||||
ret, err := funcCall(uf, &objects.Int{Value: 10}, &objects.Float{Value: 20.0})
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, &objects.Float{Value: 30.0}, ret)
|
||||
_, err = uf.Call()
|
||||
_, err = funcCall(uf)
|
||||
assert.Equal(t, objects.ErrWrongNumArguments, err)
|
||||
_, err = uf.Call(objects.TrueValue)
|
||||
_, err = funcCall(uf, objects.TrueValue)
|
||||
assert.Equal(t, objects.ErrWrongNumArguments, err)
|
||||
}
|
||||
|
||||
|
@ -299,12 +300,12 @@ 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})
|
||||
ret, err := funcCall(uf, &objects.Float{Value: 10.0}, &objects.Int{Value: 20})
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, &objects.Float{Value: 30.0}, ret)
|
||||
_, err = uf.Call()
|
||||
_, err = funcCall(uf)
|
||||
assert.Equal(t, objects.ErrWrongNumArguments, err)
|
||||
_, err = uf.Call(objects.TrueValue)
|
||||
_, err = funcCall(uf, objects.TrueValue)
|
||||
assert.Equal(t, objects.ErrWrongNumArguments, err)
|
||||
}
|
||||
|
||||
|
@ -312,12 +313,12 @@ 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})
|
||||
ret, err := funcCall(uf, &objects.Float{Value: 10.0}, &objects.Int{Value: 20})
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, objects.TrueValue, ret)
|
||||
_, err = uf.Call()
|
||||
_, err = funcCall(uf)
|
||||
assert.Equal(t, objects.ErrWrongNumArguments, err)
|
||||
_, err = uf.Call(objects.TrueValue)
|
||||
_, err = funcCall(uf, objects.TrueValue)
|
||||
assert.Equal(t, objects.ErrWrongNumArguments, err)
|
||||
}
|
||||
|
||||
|
@ -325,43 +326,43 @@ 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})
|
||||
ret, err := funcCall(uf, &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})
|
||||
ret, err = funcCall(uf, &objects.Int{Value: 10})
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, &objects.Error{Value: &objects.String{Value: "some error"}}, ret)
|
||||
_, err = uf.Call()
|
||||
_, err = funcCall(uf)
|
||||
assert.Equal(t, objects.ErrWrongNumArguments, err)
|
||||
}
|
||||
|
||||
func TestFuncASSRSs(t *testing.T) {
|
||||
uf := stdlib.FuncASSRSs(func(a, b string) []string { return []string{a, b} })
|
||||
ret, err := uf.Call(&objects.String{Value: "foo"}, &objects.String{Value: "bar"})
|
||||
ret, err := funcCall(uf, &objects.String{Value: "foo"}, &objects.String{Value: "bar"})
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, array(&objects.String{Value: "foo"}, &objects.String{Value: "bar"}), ret)
|
||||
_, err = uf.Call()
|
||||
_, err = funcCall(uf)
|
||||
assert.Equal(t, objects.ErrWrongNumArguments, err)
|
||||
}
|
||||
|
||||
func TestFuncASSIRSs(t *testing.T) {
|
||||
uf := stdlib.FuncASSIRSs(func(a, b string, c int) []string { return []string{a, b, strconv.Itoa(c)} })
|
||||
ret, err := uf.Call(&objects.String{Value: "foo"}, &objects.String{Value: "bar"}, &objects.Int{Value: 5})
|
||||
ret, err := funcCall(uf, &objects.String{Value: "foo"}, &objects.String{Value: "bar"}, &objects.Int{Value: 5})
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, array(&objects.String{Value: "foo"}, &objects.String{Value: "bar"}, &objects.String{Value: "5"}), ret)
|
||||
_, err = uf.Call()
|
||||
_, err = funcCall(uf)
|
||||
assert.Equal(t, objects.ErrWrongNumArguments, err)
|
||||
}
|
||||
|
||||
func TestFuncARB(t *testing.T) {
|
||||
uf := stdlib.FuncARB(func() bool { return true })
|
||||
ret, err := uf.Call()
|
||||
ret, err := funcCall(uf)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, objects.TrueValue, ret)
|
||||
_, err = uf.Call(objects.TrueValue)
|
||||
_, err = funcCall(uf, objects.TrueValue)
|
||||
assert.Equal(t, objects.ErrWrongNumArguments, err)
|
||||
}
|
||||
|
||||
|
@ -369,126 +370,131 @@ func TestFuncARYE(t *testing.T) {
|
|||
uf := stdlib.FuncARYE(func() ([]byte, error) {
|
||||
return []byte("foo bar"), nil
|
||||
})
|
||||
ret, err := uf.Call()
|
||||
ret, err := funcCall(uf)
|
||||
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()
|
||||
ret, err = funcCall(uf)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, &objects.Error{Value: &objects.String{Value: "some error"}}, ret)
|
||||
_, err = uf.Call(objects.TrueValue)
|
||||
_, err = funcCall(uf, 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"})
|
||||
ret, err := funcCall(uf, &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"})
|
||||
ret, err = funcCall(uf, &objects.String{Value: "foo"})
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, &objects.Error{Value: &objects.String{Value: "some error"}}, ret)
|
||||
_, err = uf.Call()
|
||||
_, err = funcCall(uf)
|
||||
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")})
|
||||
ret, err := funcCall(uf, &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")})
|
||||
ret, err = funcCall(uf, &objects.Bytes{Value: []byte("foo")})
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, &objects.Error{Value: &objects.String{Value: "some error"}}, ret)
|
||||
_, err = uf.Call()
|
||||
_, err = funcCall(uf)
|
||||
assert.Equal(t, objects.ErrWrongNumArguments, err)
|
||||
}
|
||||
|
||||
func TestFuncASSRI(t *testing.T) {
|
||||
uf := stdlib.FuncASSRI(func(a, b string) int { return len(a) + len(b) })
|
||||
ret, err := uf.Call(&objects.String{Value: "foo"}, &objects.String{Value: "bar"})
|
||||
ret, err := funcCall(uf, &objects.String{Value: "foo"}, &objects.String{Value: "bar"})
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, &objects.Int{Value: 6}, ret)
|
||||
_, err = uf.Call(&objects.String{Value: "foo"})
|
||||
_, err = funcCall(uf, &objects.String{Value: "foo"})
|
||||
assert.Equal(t, objects.ErrWrongNumArguments, err)
|
||||
}
|
||||
|
||||
func TestFuncASSRS(t *testing.T) {
|
||||
uf := stdlib.FuncASSRS(func(a, b string) string { return a + b })
|
||||
ret, err := uf.Call(&objects.String{Value: "foo"}, &objects.String{Value: "bar"})
|
||||
ret, err := funcCall(uf, &objects.String{Value: "foo"}, &objects.String{Value: "bar"})
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, &objects.String{Value: "foobar"}, ret)
|
||||
_, err = uf.Call(&objects.String{Value: "foo"})
|
||||
_, err = funcCall(uf, &objects.String{Value: "foo"})
|
||||
assert.Equal(t, objects.ErrWrongNumArguments, err)
|
||||
}
|
||||
|
||||
func TestFuncASSRB(t *testing.T) {
|
||||
uf := stdlib.FuncASSRB(func(a, b string) bool { return len(a) > len(b) })
|
||||
ret, err := uf.Call(&objects.String{Value: "123"}, &objects.String{Value: "12"})
|
||||
ret, err := funcCall(uf, &objects.String{Value: "123"}, &objects.String{Value: "12"})
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, objects.TrueValue, ret)
|
||||
_, err = uf.Call(&objects.String{Value: "foo"})
|
||||
_, err = funcCall(uf, &objects.String{Value: "foo"})
|
||||
assert.Equal(t, objects.ErrWrongNumArguments, err)
|
||||
}
|
||||
|
||||
func TestFuncAIRS(t *testing.T) {
|
||||
uf := stdlib.FuncAIRS(func(a int) string { return strconv.Itoa(a) })
|
||||
ret, err := uf.Call(&objects.Int{Value: 55})
|
||||
ret, err := funcCall(uf, &objects.Int{Value: 55})
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, &objects.String{Value: "55"}, ret)
|
||||
_, err = uf.Call()
|
||||
_, err = funcCall(uf)
|
||||
assert.Equal(t, objects.ErrWrongNumArguments, err)
|
||||
}
|
||||
|
||||
func TestFuncAIRIs(t *testing.T) {
|
||||
uf := stdlib.FuncAIRIs(func(a int) []int { return []int{a, a} })
|
||||
ret, err := uf.Call(&objects.Int{Value: 55})
|
||||
ret, err := funcCall(uf, &objects.Int{Value: 55})
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, array(&objects.Int{Value: 55}, &objects.Int{Value: 55}), ret)
|
||||
_, err = uf.Call()
|
||||
_, err = funcCall(uf)
|
||||
assert.Equal(t, objects.ErrWrongNumArguments, err)
|
||||
}
|
||||
|
||||
func TestFuncAI64R(t *testing.T) {
|
||||
uf := stdlib.FuncAIR(func(a int) {})
|
||||
ret, err := uf.Call(&objects.Int{Value: 55})
|
||||
ret, err := funcCall(uf, &objects.Int{Value: 55})
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, objects.UndefinedValue, ret)
|
||||
_, err = uf.Call()
|
||||
_, err = funcCall(uf)
|
||||
assert.Equal(t, objects.ErrWrongNumArguments, err)
|
||||
}
|
||||
|
||||
func TestFuncARI64(t *testing.T) {
|
||||
uf := stdlib.FuncARI64(func() int64 { return 55 })
|
||||
ret, err := uf.Call()
|
||||
ret, err := funcCall(uf)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, &objects.Int{Value: 55}, ret)
|
||||
_, err = uf.Call(&objects.Int{Value: 55})
|
||||
_, err = funcCall(uf, &objects.Int{Value: 55})
|
||||
assert.Equal(t, objects.ErrWrongNumArguments, err)
|
||||
}
|
||||
|
||||
func TestFuncASsSRS(t *testing.T) {
|
||||
uf := stdlib.FuncASsSRS(func(a []string, b string) string { return strings.Join(a, b) })
|
||||
ret, err := uf.Call(array(&objects.String{Value: "abc"}, &objects.String{Value: "def"}), &objects.String{Value: "-"})
|
||||
ret, err := funcCall(uf, array(&objects.String{Value: "abc"}, &objects.String{Value: "def"}), &objects.String{Value: "-"})
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, &objects.String{Value: "abc-def"}, ret)
|
||||
_, err = uf.Call()
|
||||
_, err = funcCall(uf)
|
||||
assert.Equal(t, objects.ErrWrongNumArguments, err)
|
||||
}
|
||||
|
||||
func TestFuncAI64RI64(t *testing.T) {
|
||||
uf := stdlib.FuncAI64RI64(func(a int64) int64 { return a * 2 })
|
||||
ret, err := uf.Call(&objects.Int{Value: 55})
|
||||
ret, err := funcCall(uf, &objects.Int{Value: 55})
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, &objects.Int{Value: 110}, ret)
|
||||
_, err = uf.Call()
|
||||
_, err = funcCall(uf)
|
||||
assert.Equal(t, objects.ErrWrongNumArguments, err)
|
||||
}
|
||||
|
||||
func funcCall(fn objects.CallableFunc, args ...objects.Object) (objects.Object, error) {
|
||||
userFunc := &objects.UserFunction{Value: fn}
|
||||
return userFunc.Call(args...)
|
||||
}
|
||||
|
||||
func array(elements ...objects.Object) *objects.Array {
|
||||
return &objects.Array{Value: elements}
|
||||
}
|
74
stdlib/math.go
Normal file
74
stdlib/math.go
Normal file
|
@ -0,0 +1,74 @@
|
|||
package stdlib
|
||||
|
||||
import (
|
||||
"math"
|
||||
|
||||
"github.com/d5/tengo/objects"
|
||||
)
|
||||
|
||||
var mathModule = map[string]objects.Object{
|
||||
"e": &objects.Float{Value: math.E},
|
||||
"pi": &objects.Float{Value: math.Pi},
|
||||
"phi": &objects.Float{Value: math.Phi},
|
||||
"sqrt2": &objects.Float{Value: math.Sqrt2},
|
||||
"sqrtE": &objects.Float{Value: math.SqrtE},
|
||||
"sqrtPi": &objects.Float{Value: math.SqrtPi},
|
||||
"sqrtPhi": &objects.Float{Value: math.SqrtPhi},
|
||||
"ln2": &objects.Float{Value: math.Ln2},
|
||||
"log2E": &objects.Float{Value: math.Log2E},
|
||||
"ln10": &objects.Float{Value: math.Ln10},
|
||||
"log10E": &objects.Float{Value: math.Log10E},
|
||||
"abs": &objects.UserFunction{Name: "abs", Value: FuncAFRF(math.Abs)},
|
||||
"acos": &objects.UserFunction{Name: "acos", Value: FuncAFRF(math.Acos)},
|
||||
"acosh": &objects.UserFunction{Name: "acosh", Value: FuncAFRF(math.Acosh)},
|
||||
"asin": &objects.UserFunction{Name: "asin", Value: FuncAFRF(math.Asin)},
|
||||
"asinh": &objects.UserFunction{Name: "asinh", Value: FuncAFRF(math.Asinh)},
|
||||
"atan": &objects.UserFunction{Name: "atan", Value: FuncAFRF(math.Atan)},
|
||||
"atan2": &objects.UserFunction{Name: "atan2", Value: FuncAFFRF(math.Atan2)},
|
||||
"atanh": &objects.UserFunction{Name: "atanh", Value: FuncAFRF(math.Atanh)},
|
||||
"cbrt": &objects.UserFunction{Name: "cbrt", Value: FuncAFRF(math.Cbrt)},
|
||||
"ceil": &objects.UserFunction{Name: "ceil", Value: FuncAFRF(math.Ceil)},
|
||||
"copysign": &objects.UserFunction{Name: "copysign", Value: FuncAFFRF(math.Copysign)},
|
||||
"cos": &objects.UserFunction{Name: "cos", Value: FuncAFRF(math.Cos)},
|
||||
"cosh": &objects.UserFunction{Name: "cosh", Value: FuncAFRF(math.Cosh)},
|
||||
"dim": &objects.UserFunction{Name: "dim", Value: FuncAFFRF(math.Dim)},
|
||||
"erf": &objects.UserFunction{Name: "erf", Value: FuncAFRF(math.Erf)},
|
||||
"erfc": &objects.UserFunction{Name: "erfc", Value: FuncAFRF(math.Erfc)},
|
||||
"exp": &objects.UserFunction{Name: "exp", Value: FuncAFRF(math.Exp)},
|
||||
"exp2": &objects.UserFunction{Name: "exp2", Value: FuncAFRF(math.Exp2)},
|
||||
"expm1": &objects.UserFunction{Name: "expm1", Value: FuncAFRF(math.Expm1)},
|
||||
"floor": &objects.UserFunction{Name: "floor", Value: FuncAFRF(math.Floor)},
|
||||
"gamma": &objects.UserFunction{Name: "gamma", Value: FuncAFRF(math.Gamma)},
|
||||
"hypot": &objects.UserFunction{Name: "hypot", Value: FuncAFFRF(math.Hypot)},
|
||||
"ilogb": &objects.UserFunction{Name: "ilogb", Value: FuncAFRI(math.Ilogb)},
|
||||
"inf": &objects.UserFunction{Name: "inf", Value: FuncAIRF(math.Inf)},
|
||||
"is_inf": &objects.UserFunction{Name: "is_inf", Value: FuncAFIRB(math.IsInf)},
|
||||
"is_nan": &objects.UserFunction{Name: "is_nan", Value: FuncAFRB(math.IsNaN)},
|
||||
"j0": &objects.UserFunction{Name: "j0", Value: FuncAFRF(math.J0)},
|
||||
"j1": &objects.UserFunction{Name: "j1", Value: FuncAFRF(math.J1)},
|
||||
"jn": &objects.UserFunction{Name: "jn", Value: FuncAIFRF(math.Jn)},
|
||||
"ldexp": &objects.UserFunction{Name: "ldexp", Value: FuncAFIRF(math.Ldexp)},
|
||||
"log": &objects.UserFunction{Name: "log", Value: FuncAFRF(math.Log)},
|
||||
"log10": &objects.UserFunction{Name: "log10", Value: FuncAFRF(math.Log10)},
|
||||
"log1p": &objects.UserFunction{Name: "log1p", Value: FuncAFRF(math.Log1p)},
|
||||
"log2": &objects.UserFunction{Name: "log2", Value: FuncAFRF(math.Log2)},
|
||||
"logb": &objects.UserFunction{Name: "logb", Value: FuncAFRF(math.Logb)},
|
||||
"max": &objects.UserFunction{Name: "max", Value: FuncAFFRF(math.Max)},
|
||||
"min": &objects.UserFunction{Name: "min", Value: FuncAFFRF(math.Min)},
|
||||
"mod": &objects.UserFunction{Name: "mod", Value: FuncAFFRF(math.Mod)},
|
||||
"nan": &objects.UserFunction{Name: "nan", Value: FuncARF(math.NaN)},
|
||||
"nextafter": &objects.UserFunction{Name: "nextafter", Value: FuncAFFRF(math.Nextafter)},
|
||||
"pow": &objects.UserFunction{Name: "pow", Value: FuncAFFRF(math.Pow)},
|
||||
"pow10": &objects.UserFunction{Name: "pow10", Value: FuncAIRF(math.Pow10)},
|
||||
"remainder": &objects.UserFunction{Name: "remainder", Value: FuncAFFRF(math.Remainder)},
|
||||
"signbit": &objects.UserFunction{Name: "signbit", Value: FuncAFRB(math.Signbit)},
|
||||
"sin": &objects.UserFunction{Name: "sin", Value: FuncAFRF(math.Sin)},
|
||||
"sinh": &objects.UserFunction{Name: "sinh", Value: FuncAFRF(math.Sinh)},
|
||||
"sqrt": &objects.UserFunction{Name: "sqrt", Value: FuncAFRF(math.Sqrt)},
|
||||
"tan": &objects.UserFunction{Name: "tan", Value: FuncAFRF(math.Tan)},
|
||||
"tanh": &objects.UserFunction{Name: "tanh", Value: FuncAFRF(math.Tanh)},
|
||||
"trunc": &objects.UserFunction{Name: "trunc", Value: FuncAFRF(math.Trunc)},
|
||||
"y0": &objects.UserFunction{Name: "y0", Value: FuncAFRF(math.Y0)},
|
||||
"y1": &objects.UserFunction{Name: "y1", Value: FuncAFRF(math.Y1)},
|
||||
"yn": &objects.UserFunction{Name: "yn", Value: FuncAIFRF(math.Yn)},
|
||||
}
|
437
stdlib/os.go
Normal file
437
stdlib/os.go
Normal file
|
@ -0,0 +1,437 @@
|
|||
package stdlib
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"os/exec"
|
||||
|
||||
"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_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(io.SeekStart)},
|
||||
"seek_cur": &objects.Int{Value: int64(io.SeekCurrent)},
|
||||
"seek_end": &objects.Int{Value: int64(io.SeekEnd)},
|
||||
"args": &objects.UserFunction{Value: osArgs}, // args() => array(string)
|
||||
"chdir": &objects.UserFunction{Name: "chdir", Value: FuncASRE(os.Chdir)}, // chdir(dir string) => error
|
||||
"chmod": osFuncASFmRE(os.Chmod), // chmod(name string, mode int) => error
|
||||
"chown": &objects.UserFunction{Name: "chown", Value: FuncASIIRE(os.Chown)}, // chown(name string, uid int, gid int) => error
|
||||
"clearenv": &objects.UserFunction{Name: "clearenv", Value: FuncAR(os.Clearenv)}, // clearenv()
|
||||
"environ": &objects.UserFunction{Name: "environ", Value: FuncARSs(os.Environ)}, // environ() => array(string)
|
||||
"exit": &objects.UserFunction{Name: "exit", Value: FuncAIR(os.Exit)}, // exit(code int)
|
||||
"expand_env": &objects.UserFunction{Name: "expand_env", Value: FuncASRS(os.ExpandEnv)}, // expand_env(s string) => string
|
||||
"getegid": &objects.UserFunction{Name: "getegid", Value: FuncARI(os.Getegid)}, // getegid() => int
|
||||
"getenv": &objects.UserFunction{Name: "getenv", Value: FuncASRS(os.Getenv)}, // getenv(s string) => string
|
||||
"geteuid": &objects.UserFunction{Name: "geteuid", Value: FuncARI(os.Geteuid)}, // geteuid() => int
|
||||
"getgid": &objects.UserFunction{Name: "getgid", Value: FuncARI(os.Getgid)}, // getgid() => int
|
||||
"getgroups": &objects.UserFunction{Name: "getgroups", Value: FuncARIsE(os.Getgroups)}, // getgroups() => array(string)/error
|
||||
"getpagesize": &objects.UserFunction{Name: "getpagesize", Value: FuncARI(os.Getpagesize)}, // getpagesize() => int
|
||||
"getpid": &objects.UserFunction{Name: "getpid", Value: FuncARI(os.Getpid)}, // getpid() => int
|
||||
"getppid": &objects.UserFunction{Name: "getppid", Value: FuncARI(os.Getppid)}, // getppid() => int
|
||||
"getuid": &objects.UserFunction{Name: "getuid", Value: FuncARI(os.Getuid)}, // getuid() => int
|
||||
"getwd": &objects.UserFunction{Name: "getwd", Value: FuncARSE(os.Getwd)}, // getwd() => string/error
|
||||
"hostname": &objects.UserFunction{Name: "hostname", Value: FuncARSE(os.Hostname)}, // hostname() => string/error
|
||||
"lchown": &objects.UserFunction{Name: "lchown", Value: FuncASIIRE(os.Lchown)}, // lchown(name string, uid int, gid int) => error
|
||||
"link": &objects.UserFunction{Name: "link", Value: FuncASSRE(os.Link)}, // link(oldname string, newname string) => error
|
||||
"lookup_env": &objects.UserFunction{Value: osLookupEnv}, // lookup_env(key string) => string/false
|
||||
"mkdir": osFuncASFmRE(os.Mkdir), // mkdir(name string, perm int) => error
|
||||
"mkdir_all": osFuncASFmRE(os.MkdirAll), // mkdir_all(name string, perm int) => error
|
||||
"readlink": &objects.UserFunction{Name: "readlink", Value: FuncASRSE(os.Readlink)}, // readlink(name string) => string/error
|
||||
"remove": &objects.UserFunction{Name: "remove", Value: FuncASRE(os.Remove)}, // remove(name string) => error
|
||||
"remove_all": &objects.UserFunction{Name: "remove_all", Value: FuncASRE(os.RemoveAll)}, // remove_all(name string) => error
|
||||
"rename": &objects.UserFunction{Name: "rename", Value: FuncASSRE(os.Rename)}, // rename(oldpath string, newpath string) => error
|
||||
"setenv": &objects.UserFunction{Name: "setenv", Value: FuncASSRE(os.Setenv)}, // setenv(key string, value string) => error
|
||||
"symlink": &objects.UserFunction{Name: "symlink", Value: FuncASSRE(os.Symlink)}, // symlink(oldname string newname string) => error
|
||||
"temp_dir": &objects.UserFunction{Name: "temp_dir", Value: FuncARS(os.TempDir)}, // temp_dir() => string
|
||||
"truncate": &objects.UserFunction{Name: "truncate", Value: FuncASI64RE(os.Truncate)}, // truncate(name string, size int) => error
|
||||
"unsetenv": &objects.UserFunction{Name: "unsetenv", Value: FuncASRE(os.Unsetenv)}, // unsetenv(key string) => error
|
||||
"create": &objects.UserFunction{Value: osCreate}, // create(name string) => imap(file)/error
|
||||
"open": &objects.UserFunction{Value: osOpen}, // open(name string) => imap(file)/error
|
||||
"open_file": &objects.UserFunction{Value: osOpenFile}, // open_file(name string, flag int, perm int) => imap(file)/error
|
||||
"find_process": &objects.UserFunction{Value: osFindProcess}, // find_process(pid int) => imap(process)/error
|
||||
"start_process": &objects.UserFunction{Value: osStartProcess}, // start_process(name string, argv array(string), dir string, env array(string)) => imap(process)/error
|
||||
"exec_look_path": &objects.UserFunction{Name: "exec_look_path", Value: FuncASRSE(exec.LookPath)}, // exec_look_path(file) => string/error
|
||||
"exec": &objects.UserFunction{Value: osExec}, // exec(name, args...) => command
|
||||
"stat": &objects.UserFunction{Value: osStat}, // stat(name) => imap(fileinfo)/error
|
||||
"read_file": &objects.UserFunction{Value: osReadFile}, // readfile(name) => array(byte)/error
|
||||
}
|
||||
|
||||
func osReadFile(args ...objects.Object) (ret objects.Object, err error) {
|
||||
if len(args) != 1 {
|
||||
return nil, objects.ErrWrongNumArguments
|
||||
}
|
||||
|
||||
fname, ok := objects.ToString(args[0])
|
||||
if !ok {
|
||||
return nil, objects.ErrInvalidArgumentType{
|
||||
Name: "first",
|
||||
Expected: "string(compatible)",
|
||||
Found: args[0].TypeName(),
|
||||
}
|
||||
}
|
||||
|
||||
bytes, err := ioutil.ReadFile(fname)
|
||||
if err != nil {
|
||||
return wrapError(err), nil
|
||||
}
|
||||
|
||||
return &objects.Bytes{Value: bytes}, nil
|
||||
}
|
||||
|
||||
func osStat(args ...objects.Object) (ret objects.Object, err error) {
|
||||
if len(args) != 1 {
|
||||
return nil, objects.ErrWrongNumArguments
|
||||
}
|
||||
|
||||
fname, ok := objects.ToString(args[0])
|
||||
if !ok {
|
||||
return nil, objects.ErrInvalidArgumentType{
|
||||
Name: "first",
|
||||
Expected: "string(compatible)",
|
||||
Found: args[0].TypeName(),
|
||||
}
|
||||
}
|
||||
|
||||
stat, err := os.Stat(fname)
|
||||
if err != nil {
|
||||
return wrapError(err), nil
|
||||
}
|
||||
|
||||
fstat := &objects.ImmutableMap{
|
||||
Value: map[string]objects.Object{
|
||||
"name": &objects.String{Value: stat.Name()},
|
||||
"mtime": &objects.Time{Value: stat.ModTime()},
|
||||
"size": &objects.Int{Value: stat.Size()},
|
||||
"mode": &objects.Int{Value: int64(stat.Mode())},
|
||||
},
|
||||
}
|
||||
|
||||
if stat.IsDir() {
|
||||
fstat.Value["directory"] = objects.TrueValue
|
||||
} else {
|
||||
fstat.Value["directory"] = objects.FalseValue
|
||||
}
|
||||
|
||||
return fstat, 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.ErrInvalidArgumentType{
|
||||
Name: "first",
|
||||
Expected: "string(compatible)",
|
||||
Found: args[0].TypeName(),
|
||||
}
|
||||
}
|
||||
|
||||
res, err := os.Create(s1)
|
||||
if err != nil {
|
||||
return wrapError(err), nil
|
||||
}
|
||||
|
||||
return makeOSFile(res), nil
|
||||
}
|
||||
|
||||
func osOpen(args ...objects.Object) (objects.Object, error) {
|
||||
if len(args) != 1 {
|
||||
return nil, objects.ErrWrongNumArguments
|
||||
}
|
||||
|
||||
s1, ok := objects.ToString(args[0])
|
||||
if !ok {
|
||||
return nil, objects.ErrInvalidArgumentType{
|
||||
Name: "first",
|
||||
Expected: "string(compatible)",
|
||||
Found: args[0].TypeName(),
|
||||
}
|
||||
}
|
||||
|
||||
res, err := os.Open(s1)
|
||||
if err != nil {
|
||||
return wrapError(err), nil
|
||||
}
|
||||
|
||||
return makeOSFile(res), nil
|
||||
}
|
||||
|
||||
func osOpenFile(args ...objects.Object) (objects.Object, error) {
|
||||
if len(args) != 3 {
|
||||
return nil, objects.ErrWrongNumArguments
|
||||
}
|
||||
|
||||
s1, ok := objects.ToString(args[0])
|
||||
if !ok {
|
||||
return nil, objects.ErrInvalidArgumentType{
|
||||
Name: "first",
|
||||
Expected: "string(compatible)",
|
||||
Found: args[0].TypeName(),
|
||||
}
|
||||
}
|
||||
|
||||
i2, ok := objects.ToInt(args[1])
|
||||
if !ok {
|
||||
return nil, objects.ErrInvalidArgumentType{
|
||||
Name: "second",
|
||||
Expected: "int(compatible)",
|
||||
Found: args[1].TypeName(),
|
||||
}
|
||||
}
|
||||
|
||||
i3, ok := objects.ToInt(args[2])
|
||||
if !ok {
|
||||
return nil, objects.ErrInvalidArgumentType{
|
||||
Name: "third",
|
||||
Expected: "int(compatible)",
|
||||
Found: args[2].TypeName(),
|
||||
}
|
||||
}
|
||||
|
||||
res, err := os.OpenFile(s1, i2, os.FileMode(i3))
|
||||
if err != nil {
|
||||
return wrapError(err), nil
|
||||
}
|
||||
|
||||
return makeOSFile(res), nil
|
||||
}
|
||||
|
||||
func osArgs(args ...objects.Object) (objects.Object, error) {
|
||||
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.ErrInvalidArgumentType{
|
||||
Name: "first",
|
||||
Expected: "string(compatible)",
|
||||
Found: args[0].TypeName(),
|
||||
}
|
||||
}
|
||||
i2, ok := objects.ToInt64(args[1])
|
||||
if !ok {
|
||||
return nil, objects.ErrInvalidArgumentType{
|
||||
Name: "second",
|
||||
Expected: "int(compatible)",
|
||||
Found: args[1].TypeName(),
|
||||
}
|
||||
}
|
||||
|
||||
return wrapError(fn(s1, os.FileMode(i2))), 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.ErrInvalidArgumentType{
|
||||
Name: "first",
|
||||
Expected: "string(compatible)",
|
||||
Found: args[0].TypeName(),
|
||||
}
|
||||
}
|
||||
|
||||
res, ok := os.LookupEnv(s1)
|
||||
if !ok {
|
||||
return objects.FalseValue, nil
|
||||
}
|
||||
|
||||
return &objects.String{Value: res}, nil
|
||||
}
|
||||
|
||||
func osExec(args ...objects.Object) (objects.Object, error) {
|
||||
if len(args) == 0 {
|
||||
return nil, objects.ErrWrongNumArguments
|
||||
}
|
||||
|
||||
name, ok := objects.ToString(args[0])
|
||||
if !ok {
|
||||
return nil, objects.ErrInvalidArgumentType{
|
||||
Name: "first",
|
||||
Expected: "string(compatible)",
|
||||
Found: args[0].TypeName(),
|
||||
}
|
||||
}
|
||||
|
||||
var execArgs []string
|
||||
for idx, arg := range args[1:] {
|
||||
execArg, ok := objects.ToString(arg)
|
||||
if !ok {
|
||||
return nil, objects.ErrInvalidArgumentType{
|
||||
Name: fmt.Sprintf("args[%d]", idx),
|
||||
Expected: "string(compatible)",
|
||||
Found: args[1+idx].TypeName(),
|
||||
}
|
||||
}
|
||||
|
||||
execArgs = append(execArgs, execArg)
|
||||
}
|
||||
|
||||
return makeOSExecCommand(exec.Command(name, execArgs...)), nil
|
||||
}
|
||||
|
||||
func osFindProcess(args ...objects.Object) (objects.Object, error) {
|
||||
if len(args) != 1 {
|
||||
return nil, objects.ErrWrongNumArguments
|
||||
}
|
||||
|
||||
i1, ok := objects.ToInt(args[0])
|
||||
if !ok {
|
||||
return nil, objects.ErrInvalidArgumentType{
|
||||
Name: "first",
|
||||
Expected: "int(compatible)",
|
||||
Found: args[0].TypeName(),
|
||||
}
|
||||
}
|
||||
|
||||
proc, err := os.FindProcess(i1)
|
||||
if err != nil {
|
||||
return wrapError(err), nil
|
||||
}
|
||||
|
||||
return makeOSProcess(proc), nil
|
||||
}
|
||||
|
||||
func osStartProcess(args ...objects.Object) (objects.Object, error) {
|
||||
if len(args) != 4 {
|
||||
return nil, objects.ErrWrongNumArguments
|
||||
}
|
||||
|
||||
name, ok := objects.ToString(args[0])
|
||||
if !ok {
|
||||
return nil, objects.ErrInvalidArgumentType{
|
||||
Name: "first",
|
||||
Expected: "string(compatible)",
|
||||
Found: args[0].TypeName(),
|
||||
}
|
||||
}
|
||||
|
||||
var argv []string
|
||||
var err error
|
||||
switch arg1 := args[1].(type) {
|
||||
case *objects.Array:
|
||||
argv, err = stringArray(arg1.Value, "second")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
case *objects.ImmutableArray:
|
||||
argv, err = stringArray(arg1.Value, "second")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
default:
|
||||
return nil, objects.ErrInvalidArgumentType{
|
||||
Name: "second",
|
||||
Expected: "array",
|
||||
Found: arg1.TypeName(),
|
||||
}
|
||||
}
|
||||
|
||||
dir, ok := objects.ToString(args[2])
|
||||
if !ok {
|
||||
return nil, objects.ErrInvalidArgumentType{
|
||||
Name: "third",
|
||||
Expected: "string(compatible)",
|
||||
Found: args[2].TypeName(),
|
||||
}
|
||||
}
|
||||
|
||||
var env []string
|
||||
switch arg3 := args[3].(type) {
|
||||
case *objects.Array:
|
||||
env, err = stringArray(arg3.Value, "fourth")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
case *objects.ImmutableArray:
|
||||
env, err = stringArray(arg3.Value, "fourth")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
default:
|
||||
return nil, objects.ErrInvalidArgumentType{
|
||||
Name: "fourth",
|
||||
Expected: "array",
|
||||
Found: arg3.TypeName(),
|
||||
}
|
||||
}
|
||||
|
||||
proc, err := os.StartProcess(name, argv, &os.ProcAttr{
|
||||
Dir: dir,
|
||||
Env: env,
|
||||
})
|
||||
if err != nil {
|
||||
return wrapError(err), nil
|
||||
}
|
||||
|
||||
return makeOSProcess(proc), nil
|
||||
}
|
||||
|
||||
func stringArray(arr []objects.Object, argName string) ([]string, error) {
|
||||
var sarr []string
|
||||
for idx, elem := range arr {
|
||||
str, ok := elem.(*objects.String)
|
||||
if !ok {
|
||||
return nil, objects.ErrInvalidArgumentType{
|
||||
Name: fmt.Sprintf("%s[%d]", argName, idx),
|
||||
Expected: "string",
|
||||
Found: elem.TypeName(),
|
||||
}
|
||||
}
|
||||
|
||||
sarr = append(sarr, str.Value)
|
||||
}
|
||||
|
||||
return sarr, nil
|
||||
}
|
|
@ -10,15 +10,15 @@ func makeOSExecCommand(cmd *exec.Cmd) *objects.ImmutableMap {
|
|||
return &objects.ImmutableMap{
|
||||
Value: map[string]objects.Object{
|
||||
// combined_output() => bytes/error
|
||||
"combined_output": FuncARYE(cmd.CombinedOutput),
|
||||
"combined_output": &objects.UserFunction{Name: "combined_output", Value: FuncARYE(cmd.CombinedOutput)}, //
|
||||
// output() => bytes/error
|
||||
"output": FuncARYE(cmd.Output),
|
||||
"output": &objects.UserFunction{Name: "output", Value: FuncARYE(cmd.Output)}, //
|
||||
// run() => error
|
||||
"run": FuncARE(cmd.Run),
|
||||
"run": &objects.UserFunction{Name: "run", Value: FuncARE(cmd.Run)}, //
|
||||
// start() => error
|
||||
"start": FuncARE(cmd.Start),
|
||||
"start": &objects.UserFunction{Name: "start", Value: FuncARE(cmd.Start)}, //
|
||||
// wait() => error
|
||||
"wait": FuncARE(cmd.Wait),
|
||||
"wait": &objects.UserFunction{Name: "wait", Value: FuncARE(cmd.Wait)}, //
|
||||
// set_path(path string)
|
||||
"set_path": &objects.UserFunction{
|
||||
Value: func(args ...objects.Object) (ret objects.Object, err error) {
|
||||
|
@ -28,7 +28,11 @@ func makeOSExecCommand(cmd *exec.Cmd) *objects.ImmutableMap {
|
|||
|
||||
s1, ok := objects.ToString(args[0])
|
||||
if !ok {
|
||||
return nil, objects.ErrInvalidTypeConversion
|
||||
return nil, objects.ErrInvalidArgumentType{
|
||||
Name: "first",
|
||||
Expected: "string(compatible)",
|
||||
Found: args[0].TypeName(),
|
||||
}
|
||||
}
|
||||
|
||||
cmd.Path = s1
|
||||
|
@ -45,7 +49,11 @@ func makeOSExecCommand(cmd *exec.Cmd) *objects.ImmutableMap {
|
|||
|
||||
s1, ok := objects.ToString(args[0])
|
||||
if !ok {
|
||||
return nil, objects.ErrInvalidTypeConversion
|
||||
return nil, objects.ErrInvalidArgumentType{
|
||||
Name: "first",
|
||||
Expected: "string(compatible)",
|
||||
Found: args[0].TypeName(),
|
||||
}
|
||||
}
|
||||
|
||||
cmd.Dir = s1
|
||||
|
@ -55,17 +63,33 @@ func makeOSExecCommand(cmd *exec.Cmd) *objects.ImmutableMap {
|
|||
},
|
||||
// set_env(env array(string))
|
||||
"set_env": &objects.UserFunction{
|
||||
Value: func(args ...objects.Object) (ret objects.Object, err error) {
|
||||
Value: func(args ...objects.Object) (objects.Object, error) {
|
||||
if len(args) != 1 {
|
||||
return nil, objects.ErrWrongNumArguments
|
||||
}
|
||||
|
||||
envs, err := stringArray(args[0])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
var env []string
|
||||
var err error
|
||||
switch arg0 := args[0].(type) {
|
||||
case *objects.Array:
|
||||
env, err = stringArray(arg0.Value, "first")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
case *objects.ImmutableArray:
|
||||
env, err = stringArray(arg0.Value, "first")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
default:
|
||||
return nil, objects.ErrInvalidArgumentType{
|
||||
Name: "first",
|
||||
Expected: "array",
|
||||
Found: arg0.TypeName(),
|
||||
}
|
||||
}
|
||||
|
||||
cmd.Env = envs
|
||||
cmd.Env = env
|
||||
|
||||
return objects.UndefinedValue, nil
|
||||
},
|
|
@ -10,23 +10,23 @@ func makeOSFile(file *os.File) *objects.ImmutableMap {
|
|||
return &objects.ImmutableMap{
|
||||
Value: map[string]objects.Object{
|
||||
// chdir() => true/error
|
||||
"chdir": FuncARE(file.Chdir),
|
||||
"chdir": &objects.UserFunction{Name: "chdir", Value: FuncARE(file.Chdir)}, //
|
||||
// chown(uid int, gid int) => true/error
|
||||
"chown": FuncAIIRE(file.Chown),
|
||||
"chown": &objects.UserFunction{Name: "chown", Value: FuncAIIRE(file.Chown)}, //
|
||||
// close() => error
|
||||
"close": FuncARE(file.Close),
|
||||
"close": &objects.UserFunction{Name: "close", Value: FuncARE(file.Close)}, //
|
||||
// name() => string
|
||||
"name": FuncARS(file.Name),
|
||||
"name": &objects.UserFunction{Name: "name", Value: FuncARS(file.Name)}, //
|
||||
// readdirnames(n int) => array(string)/error
|
||||
"readdirnames": FuncAIRSsE(file.Readdirnames),
|
||||
"readdirnames": &objects.UserFunction{Name: "readdirnames", Value: FuncAIRSsE(file.Readdirnames)}, //
|
||||
// sync() => error
|
||||
"sync": FuncARE(file.Sync),
|
||||
"sync": &objects.UserFunction{Name: "sync", Value: FuncARE(file.Sync)}, //
|
||||
// write(bytes) => int/error
|
||||
"write": FuncAYRIE(file.Write),
|
||||
"write": &objects.UserFunction{Name: "write", Value: FuncAYRIE(file.Write)}, //
|
||||
// write(string) => int/error
|
||||
"write_string": FuncASRIE(file.WriteString),
|
||||
"write_string": &objects.UserFunction{Name: "write_string", Value: FuncASRIE(file.WriteString)}, //
|
||||
// read(bytes) => int/error
|
||||
"read": FuncAYRIE(file.Read),
|
||||
"read": &objects.UserFunction{Name: "read", Value: FuncAYRIE(file.Read)}, //
|
||||
// chmod(mode int) => error
|
||||
"chmod": &objects.UserFunction{
|
||||
Value: func(args ...objects.Object) (ret objects.Object, err error) {
|
||||
|
@ -36,7 +36,11 @@ func makeOSFile(file *os.File) *objects.ImmutableMap {
|
|||
|
||||
i1, ok := objects.ToInt64(args[0])
|
||||
if !ok {
|
||||
return nil, objects.ErrInvalidTypeConversion
|
||||
return nil, objects.ErrInvalidArgumentType{
|
||||
Name: "first",
|
||||
Expected: "int(compatible)",
|
||||
Found: args[0].TypeName(),
|
||||
}
|
||||
}
|
||||
|
||||
return wrapError(file.Chmod(os.FileMode(i1))), nil
|
||||
|
@ -51,11 +55,19 @@ func makeOSFile(file *os.File) *objects.ImmutableMap {
|
|||
|
||||
i1, ok := objects.ToInt64(args[0])
|
||||
if !ok {
|
||||
return nil, objects.ErrInvalidTypeConversion
|
||||
return nil, objects.ErrInvalidArgumentType{
|
||||
Name: "first",
|
||||
Expected: "int(compatible)",
|
||||
Found: args[0].TypeName(),
|
||||
}
|
||||
}
|
||||
i2, ok := objects.ToInt(args[1])
|
||||
if !ok {
|
||||
return nil, objects.ErrInvalidTypeConversion
|
||||
return nil, objects.ErrInvalidArgumentType{
|
||||
Name: "second",
|
||||
Expected: "int(compatible)",
|
||||
Found: args[1].TypeName(),
|
||||
}
|
||||
}
|
||||
|
||||
res, err := file.Seek(i1, i2)
|
|
@ -10,10 +10,10 @@ import (
|
|||
func makeOSProcessState(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),
|
||||
"exited": &objects.UserFunction{Name: "exited", Value: FuncARB(state.Exited)}, //
|
||||
"pid": &objects.UserFunction{Name: "pid", Value: FuncARI(state.Pid)}, //
|
||||
"string": &objects.UserFunction{Name: "string", Value: FuncARS(state.String)}, //
|
||||
"success": &objects.UserFunction{Name: "success", Value: FuncARB(state.Success)}, //
|
||||
},
|
||||
}
|
||||
}
|
||||
|
@ -21,8 +21,8 @@ func makeOSProcessState(state *os.ProcessState) *objects.ImmutableMap {
|
|||
func makeOSProcess(proc *os.Process) *objects.ImmutableMap {
|
||||
return &objects.ImmutableMap{
|
||||
Value: map[string]objects.Object{
|
||||
"kill": FuncARE(proc.Kill),
|
||||
"release": FuncARE(proc.Release),
|
||||
"kill": &objects.UserFunction{Name: "kill", Value: FuncARE(proc.Kill)}, //
|
||||
"release": &objects.UserFunction{Name: "release", Value: FuncARE(proc.Release)}, //
|
||||
"signal": &objects.UserFunction{
|
||||
Value: func(args ...objects.Object) (ret objects.Object, err error) {
|
||||
if len(args) != 1 {
|
||||
|
@ -31,7 +31,11 @@ func makeOSProcess(proc *os.Process) *objects.ImmutableMap {
|
|||
|
||||
i1, ok := objects.ToInt64(args[0])
|
||||
if !ok {
|
||||
return nil, objects.ErrInvalidTypeConversion
|
||||
return nil, objects.ErrInvalidArgumentType{
|
||||
Name: "first",
|
||||
Expected: "int(compatible)",
|
||||
Found: args[0].TypeName(),
|
||||
}
|
||||
}
|
||||
|
||||
return wrapError(proc.Signal(syscall.Signal(i1))), nil
|
99
stdlib/rand.go
Normal file
99
stdlib/rand.go
Normal file
|
@ -0,0 +1,99 @@
|
|||
package stdlib
|
||||
|
||||
import (
|
||||
"math/rand"
|
||||
|
||||
"github.com/d5/tengo/objects"
|
||||
)
|
||||
|
||||
var randModule = map[string]objects.Object{
|
||||
"int": &objects.UserFunction{Name: "int", Value: FuncARI64(rand.Int63)},
|
||||
"float": &objects.UserFunction{Name: "float", Value: FuncARF(rand.Float64)},
|
||||
"intn": &objects.UserFunction{Name: "intn", Value: FuncAI64RI64(rand.Int63n)},
|
||||
"exp_float": &objects.UserFunction{Name: "exp_float", Value: FuncARF(rand.ExpFloat64)},
|
||||
"norm_float": &objects.UserFunction{Name: "norm_float", Value: FuncARF(rand.NormFloat64)},
|
||||
"perm": &objects.UserFunction{Name: "perm", Value: FuncAIRIs(rand.Perm)},
|
||||
"seed": &objects.UserFunction{Name: "seed", Value: FuncAI64R(rand.Seed)},
|
||||
"read": &objects.UserFunction{
|
||||
Value: func(args ...objects.Object) (ret objects.Object, err error) {
|
||||
if len(args) != 1 {
|
||||
return nil, objects.ErrWrongNumArguments
|
||||
}
|
||||
|
||||
y1, ok := args[0].(*objects.Bytes)
|
||||
if !ok {
|
||||
return nil, objects.ErrInvalidArgumentType{
|
||||
Name: "first",
|
||||
Expected: "bytes",
|
||||
Found: args[0].TypeName(),
|
||||
}
|
||||
}
|
||||
|
||||
res, err := rand.Read(y1.Value)
|
||||
if err != nil {
|
||||
ret = wrapError(err)
|
||||
return
|
||||
}
|
||||
|
||||
return &objects.Int{Value: int64(res)}, nil
|
||||
},
|
||||
},
|
||||
"rand": &objects.UserFunction{
|
||||
Value: func(args ...objects.Object) (ret objects.Object, err error) {
|
||||
if len(args) != 1 {
|
||||
return nil, objects.ErrWrongNumArguments
|
||||
}
|
||||
|
||||
i1, ok := objects.ToInt64(args[0])
|
||||
if !ok {
|
||||
return nil, objects.ErrInvalidArgumentType{
|
||||
Name: "first",
|
||||
Expected: "int(compatible)",
|
||||
Found: args[0].TypeName(),
|
||||
}
|
||||
}
|
||||
|
||||
src := rand.NewSource(i1)
|
||||
|
||||
return randRand(rand.New(src)), nil
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
func randRand(r *rand.Rand) *objects.ImmutableMap {
|
||||
return &objects.ImmutableMap{
|
||||
Value: map[string]objects.Object{
|
||||
"int": &objects.UserFunction{Name: "int", Value: FuncARI64(r.Int63)},
|
||||
"float": &objects.UserFunction{Name: "float", Value: FuncARF(r.Float64)},
|
||||
"intn": &objects.UserFunction{Name: "intn", Value: FuncAI64RI64(r.Int63n)},
|
||||
"exp_float": &objects.UserFunction{Name: "exp_float", Value: FuncARF(r.ExpFloat64)},
|
||||
"norm_float": &objects.UserFunction{Name: "norm_float", Value: FuncARF(r.NormFloat64)},
|
||||
"perm": &objects.UserFunction{Name: "perm", Value: FuncAIRIs(r.Perm)},
|
||||
"seed": &objects.UserFunction{Name: "seed", Value: FuncAI64R(r.Seed)},
|
||||
"read": &objects.UserFunction{
|
||||
Value: func(args ...objects.Object) (ret objects.Object, err error) {
|
||||
if len(args) != 1 {
|
||||
return nil, objects.ErrWrongNumArguments
|
||||
}
|
||||
|
||||
y1, ok := args[0].(*objects.Bytes)
|
||||
if !ok {
|
||||
return nil, objects.ErrInvalidArgumentType{
|
||||
Name: "first",
|
||||
Expected: "bytes",
|
||||
Found: args[0].TypeName(),
|
||||
}
|
||||
}
|
||||
|
||||
res, err := r.Read(y1.Value)
|
||||
if err != nil {
|
||||
ret = wrapError(err)
|
||||
return
|
||||
}
|
||||
|
||||
return &objects.Int{Value: int64(res)}, nil
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
16
stdlib/stdlib.go
Normal file
16
stdlib/stdlib.go
Normal file
|
@ -0,0 +1,16 @@
|
|||
package stdlib
|
||||
|
||||
import "github.com/d5/tengo/objects"
|
||||
|
||||
// Modules contain the standard modules.
|
||||
var Modules = map[string]*objects.Object{
|
||||
"math": objectPtr(&objects.ImmutableMap{Value: mathModule}),
|
||||
"os": objectPtr(&objects.ImmutableMap{Value: osModule}),
|
||||
"text": objectPtr(&objects.ImmutableMap{Value: textModule}),
|
||||
"times": objectPtr(&objects.ImmutableMap{Value: timesModule}),
|
||||
"rand": objectPtr(&objects.ImmutableMap{Value: randModule}),
|
||||
}
|
||||
|
||||
func objectPtr(o objects.Object) *objects.Object {
|
||||
return &o
|
||||
}
|
|
@ -6,8 +6,8 @@ import (
|
|||
"time"
|
||||
|
||||
"github.com/d5/tengo/assert"
|
||||
"github.com/d5/tengo/compiler/stdlib"
|
||||
"github.com/d5/tengo/objects"
|
||||
"github.com/d5/tengo/stdlib"
|
||||
)
|
||||
|
||||
type ARR = []interface{}
|
||||
|
@ -66,7 +66,7 @@ func module(t *testing.T, moduleName string) callres {
|
|||
return callres{t: t, e: fmt.Errorf("module not found: %s", moduleName)}
|
||||
}
|
||||
|
||||
return callres{t: t, o: mod}
|
||||
return callres{t: t, o: (*mod).(*objects.ImmutableMap)}
|
||||
}
|
||||
|
||||
func object(v interface{}) objects.Object {
|
585
stdlib/text.go
Normal file
585
stdlib/text.go
Normal file
|
@ -0,0 +1,585 @@
|
|||
package stdlib
|
||||
|
||||
import (
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/d5/tengo/objects"
|
||||
)
|
||||
|
||||
var textModule = map[string]objects.Object{
|
||||
"re_match": &objects.UserFunction{Value: textREMatch}, // re_match(pattern, text) => bool/error
|
||||
"re_find": &objects.UserFunction{Value: textREFind}, // re_find(pattern, text, count) => [[{text:,begin:,end:}]]/undefined
|
||||
"re_replace": &objects.UserFunction{Value: textREReplace}, // re_replace(pattern, text, repl) => string/error
|
||||
"re_split": &objects.UserFunction{Value: textRESplit}, // re_split(pattern, text, count) => [string]/error
|
||||
"re_compile": &objects.UserFunction{Value: textRECompile}, // re_compile(pattern) => Regexp/error
|
||||
"compare": &objects.UserFunction{Name: "compare", Value: FuncASSRI(strings.Compare)}, // compare(a, b) => int
|
||||
"contains": &objects.UserFunction{Name: "contains", Value: FuncASSRB(strings.Contains)}, // contains(s, substr) => bool
|
||||
"contains_any": &objects.UserFunction{Name: "contains_any", Value: FuncASSRB(strings.ContainsAny)}, // contains_any(s, chars) => bool
|
||||
"count": &objects.UserFunction{Name: "count", Value: FuncASSRI(strings.Count)}, // count(s, substr) => int
|
||||
"equal_fold": &objects.UserFunction{Name: "equal_fold", Value: FuncASSRB(strings.EqualFold)}, // "equal_fold(s, t) => bool
|
||||
"fields": &objects.UserFunction{Name: "fields", Value: FuncASRSs(strings.Fields)}, // fields(s) => [string]
|
||||
"has_prefix": &objects.UserFunction{Name: "has_prefix", Value: FuncASSRB(strings.HasPrefix)}, // has_prefix(s, prefix) => bool
|
||||
"has_suffix": &objects.UserFunction{Name: "has_suffix", Value: FuncASSRB(strings.HasSuffix)}, // has_suffix(s, suffix) => bool
|
||||
"index": &objects.UserFunction{Name: "index", Value: FuncASSRI(strings.Index)}, // index(s, substr) => int
|
||||
"index_any": &objects.UserFunction{Name: "index_any", Value: FuncASSRI(strings.IndexAny)}, // index_any(s, chars) => int
|
||||
"join": &objects.UserFunction{Name: "join", Value: FuncASsSRS(strings.Join)}, // join(arr, sep) => string
|
||||
"last_index": &objects.UserFunction{Name: "last_index", Value: FuncASSRI(strings.LastIndex)}, // last_index(s, substr) => int
|
||||
"last_index_any": &objects.UserFunction{Name: "last_index_any", Value: FuncASSRI(strings.LastIndexAny)}, // last_index_any(s, chars) => int
|
||||
"repeat": &objects.UserFunction{Name: "repeat", Value: FuncASIRS(strings.Repeat)}, // repeat(s, count) => string
|
||||
"replace": &objects.UserFunction{Value: textReplace}, // replace(s, old, new, n) => string
|
||||
"split": &objects.UserFunction{Name: "split", Value: FuncASSRSs(strings.Split)}, // split(s, sep) => [string]
|
||||
"split_after": &objects.UserFunction{Name: "split_after", Value: FuncASSRSs(strings.SplitAfter)}, // split_after(s, sep) => [string]
|
||||
"split_after_n": &objects.UserFunction{Name: "split_after_n", Value: FuncASSIRSs(strings.SplitAfterN)}, // split_after_n(s, sep, n) => [string]
|
||||
"split_n": &objects.UserFunction{Name: "split_n", Value: FuncASSIRSs(strings.SplitN)}, // split_n(s, sep, n) => [string]
|
||||
"title": &objects.UserFunction{Name: "title", Value: FuncASRS(strings.Title)}, // title(s) => string
|
||||
"to_lower": &objects.UserFunction{Name: "to_lower", Value: FuncASRS(strings.ToLower)}, // to_lower(s) => string
|
||||
"to_title": &objects.UserFunction{Name: "to_title", Value: FuncASRS(strings.ToTitle)}, // to_title(s) => string
|
||||
"to_upper": &objects.UserFunction{Name: "to_upper", Value: FuncASRS(strings.ToUpper)}, // to_upper(s) => string
|
||||
"trim_left": &objects.UserFunction{Name: "trim_left", Value: FuncASSRS(strings.TrimLeft)}, // trim_left(s, cutset) => string
|
||||
"trim_prefix": &objects.UserFunction{Name: "trim_prefix", Value: FuncASSRS(strings.TrimPrefix)}, // trim_prefix(s, prefix) => string
|
||||
"trim_right": &objects.UserFunction{Name: "trim_right", Value: FuncASSRS(strings.TrimRight)}, // trim_right(s, cutset) => string
|
||||
"trim_space": &objects.UserFunction{Name: "trim_space", Value: FuncASRS(strings.TrimSpace)}, // trim_space(s) => string
|
||||
"trim_suffix": &objects.UserFunction{Name: "trim_suffix", Value: FuncASSRS(strings.TrimSuffix)}, // trim_suffix(s, suffix) => string
|
||||
"atoi": &objects.UserFunction{Name: "atoi", Value: FuncASRIE(strconv.Atoi)}, // atoi(str) => int/error
|
||||
"format_bool": &objects.UserFunction{Value: textFormatBool}, // format_bool(b) => string
|
||||
"format_float": &objects.UserFunction{Value: textFormatFloat}, // format_float(f, fmt, prec, bits) => string
|
||||
"format_int": &objects.UserFunction{Value: textFormatInt}, // format_int(i, base) => string
|
||||
"itoa": &objects.UserFunction{Name: "itoa", Value: FuncAIRS(strconv.Itoa)}, // itoa(i) => string
|
||||
"parse_bool": &objects.UserFunction{Value: textParseBool}, // parse_bool(str) => bool/error
|
||||
"parse_float": &objects.UserFunction{Value: textParseFloat}, // parse_float(str, bits) => float/error
|
||||
"parse_int": &objects.UserFunction{Value: textParseInt}, // parse_int(str, base, bits) => int/error
|
||||
"quote": &objects.UserFunction{Name: "quote", Value: FuncASRS(strconv.Quote)}, // quote(str) => string
|
||||
"unquote": &objects.UserFunction{Name: "unquote", Value: FuncASRSE(strconv.Unquote)}, // unquote(str) => string/error
|
||||
}
|
||||
|
||||
func textREMatch(args ...objects.Object) (ret objects.Object, err error) {
|
||||
if len(args) != 2 {
|
||||
err = objects.ErrWrongNumArguments
|
||||
return
|
||||
}
|
||||
|
||||
s1, ok := objects.ToString(args[0])
|
||||
if !ok {
|
||||
err = objects.ErrInvalidArgumentType{
|
||||
Name: "first",
|
||||
Expected: "string(compatible)",
|
||||
Found: args[0].TypeName(),
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
s2, ok := objects.ToString(args[1])
|
||||
if !ok {
|
||||
err = objects.ErrInvalidArgumentType{
|
||||
Name: "second",
|
||||
Expected: "string(compatible)",
|
||||
Found: args[1].TypeName(),
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
matched, err := regexp.MatchString(s1, s2)
|
||||
if err != nil {
|
||||
ret = wrapError(err)
|
||||
return
|
||||
}
|
||||
|
||||
if matched {
|
||||
ret = objects.TrueValue
|
||||
} else {
|
||||
ret = objects.FalseValue
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func textREFind(args ...objects.Object) (ret objects.Object, err error) {
|
||||
numArgs := len(args)
|
||||
if numArgs != 2 && numArgs != 3 {
|
||||
err = objects.ErrWrongNumArguments
|
||||
return
|
||||
}
|
||||
|
||||
s1, ok := objects.ToString(args[0])
|
||||
if !ok {
|
||||
err = objects.ErrInvalidArgumentType{
|
||||
Name: "first",
|
||||
Expected: "string(compatible)",
|
||||
Found: args[0].TypeName(),
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
re, err := regexp.Compile(s1)
|
||||
if err != nil {
|
||||
ret = wrapError(err)
|
||||
return
|
||||
}
|
||||
|
||||
s2, ok := objects.ToString(args[1])
|
||||
if !ok {
|
||||
err = objects.ErrInvalidArgumentType{
|
||||
Name: "second",
|
||||
Expected: "string(compatible)",
|
||||
Found: args[1].TypeName(),
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
if numArgs < 3 {
|
||||
m := re.FindStringSubmatchIndex(s2)
|
||||
if m == nil {
|
||||
ret = objects.UndefinedValue
|
||||
return
|
||||
}
|
||||
|
||||
arr := &objects.Array{}
|
||||
for i := 0; i < len(m); i += 2 {
|
||||
arr.Value = append(arr.Value, &objects.ImmutableMap{Value: map[string]objects.Object{
|
||||
"text": &objects.String{Value: s2[m[i]:m[i+1]]},
|
||||
"begin": &objects.Int{Value: int64(m[i])},
|
||||
"end": &objects.Int{Value: int64(m[i+1])},
|
||||
}})
|
||||
}
|
||||
|
||||
ret = &objects.Array{Value: []objects.Object{arr}}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
i3, ok := objects.ToInt(args[2])
|
||||
if !ok {
|
||||
err = objects.ErrInvalidArgumentType{
|
||||
Name: "third",
|
||||
Expected: "int(compatible)",
|
||||
Found: args[2].TypeName(),
|
||||
}
|
||||
return
|
||||
}
|
||||
m := re.FindAllStringSubmatchIndex(s2, i3)
|
||||
if m == nil {
|
||||
ret = objects.UndefinedValue
|
||||
return
|
||||
}
|
||||
|
||||
arr := &objects.Array{}
|
||||
for _, m := range m {
|
||||
subMatch := &objects.Array{}
|
||||
for i := 0; i < len(m); i += 2 {
|
||||
subMatch.Value = append(subMatch.Value, &objects.ImmutableMap{Value: map[string]objects.Object{
|
||||
"text": &objects.String{Value: s2[m[i]:m[i+1]]},
|
||||
"begin": &objects.Int{Value: int64(m[i])},
|
||||
"end": &objects.Int{Value: int64(m[i+1])},
|
||||
}})
|
||||
}
|
||||
|
||||
arr.Value = append(arr.Value, subMatch)
|
||||
}
|
||||
|
||||
ret = arr
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func textREReplace(args ...objects.Object) (ret objects.Object, err error) {
|
||||
if len(args) != 3 {
|
||||
err = objects.ErrWrongNumArguments
|
||||
return
|
||||
}
|
||||
|
||||
s1, ok := objects.ToString(args[0])
|
||||
if !ok {
|
||||
err = objects.ErrInvalidArgumentType{
|
||||
Name: "first",
|
||||
Expected: "string(compatible)",
|
||||
Found: args[0].TypeName(),
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
s2, ok := objects.ToString(args[1])
|
||||
if !ok {
|
||||
err = objects.ErrInvalidArgumentType{
|
||||
Name: "second",
|
||||
Expected: "string(compatible)",
|
||||
Found: args[1].TypeName(),
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
s3, ok := objects.ToString(args[2])
|
||||
if !ok {
|
||||
err = objects.ErrInvalidArgumentType{
|
||||
Name: "third",
|
||||
Expected: "string(compatible)",
|
||||
Found: args[2].TypeName(),
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
re, err := regexp.Compile(s1)
|
||||
if err != nil {
|
||||
ret = wrapError(err)
|
||||
} else {
|
||||
ret = &objects.String{Value: re.ReplaceAllString(s2, s3)}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func textRESplit(args ...objects.Object) (ret objects.Object, err error) {
|
||||
numArgs := len(args)
|
||||
if numArgs != 2 && numArgs != 3 {
|
||||
err = objects.ErrWrongNumArguments
|
||||
return
|
||||
}
|
||||
|
||||
s1, ok := objects.ToString(args[0])
|
||||
if !ok {
|
||||
err = objects.ErrInvalidArgumentType{
|
||||
Name: "first",
|
||||
Expected: "string(compatible)",
|
||||
Found: args[0].TypeName(),
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
s2, ok := objects.ToString(args[1])
|
||||
if !ok {
|
||||
err = objects.ErrInvalidArgumentType{
|
||||
Name: "second",
|
||||
Expected: "string(compatible)",
|
||||
Found: args[1].TypeName(),
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
var i3 = -1
|
||||
if numArgs > 2 {
|
||||
i3, ok = objects.ToInt(args[2])
|
||||
if !ok {
|
||||
err = objects.ErrInvalidArgumentType{
|
||||
Name: "third",
|
||||
Expected: "int(compatible)",
|
||||
Found: args[2].TypeName(),
|
||||
}
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
re, err := regexp.Compile(s1)
|
||||
if err != nil {
|
||||
ret = wrapError(err)
|
||||
return
|
||||
}
|
||||
|
||||
arr := &objects.Array{}
|
||||
for _, s := range re.Split(s2, i3) {
|
||||
arr.Value = append(arr.Value, &objects.String{Value: s})
|
||||
}
|
||||
|
||||
ret = arr
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func textRECompile(args ...objects.Object) (ret objects.Object, err error) {
|
||||
if len(args) != 1 {
|
||||
err = objects.ErrWrongNumArguments
|
||||
return
|
||||
}
|
||||
|
||||
s1, ok := objects.ToString(args[0])
|
||||
if !ok {
|
||||
err = objects.ErrInvalidArgumentType{
|
||||
Name: "first",
|
||||
Expected: "string(compatible)",
|
||||
Found: args[0].TypeName(),
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
re, err := regexp.Compile(s1)
|
||||
if err != nil {
|
||||
ret = wrapError(err)
|
||||
} else {
|
||||
ret = makeTextRegexp(re)
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func textReplace(args ...objects.Object) (ret objects.Object, err error) {
|
||||
if len(args) != 4 {
|
||||
err = objects.ErrWrongNumArguments
|
||||
return
|
||||
}
|
||||
|
||||
s1, ok := objects.ToString(args[0])
|
||||
if !ok {
|
||||
err = objects.ErrInvalidArgumentType{
|
||||
Name: "first",
|
||||
Expected: "string(compatible)",
|
||||
Found: args[0].TypeName(),
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
s2, ok := objects.ToString(args[1])
|
||||
if !ok {
|
||||
err = objects.ErrInvalidArgumentType{
|
||||
Name: "second",
|
||||
Expected: "string(compatible)",
|
||||
Found: args[1].TypeName(),
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
s3, ok := objects.ToString(args[2])
|
||||
if !ok {
|
||||
err = objects.ErrInvalidArgumentType{
|
||||
Name: "third",
|
||||
Expected: "string(compatible)",
|
||||
Found: args[2].TypeName(),
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
i4, ok := objects.ToInt(args[3])
|
||||
if !ok {
|
||||
err = objects.ErrInvalidArgumentType{
|
||||
Name: "fourth",
|
||||
Expected: "int(compatible)",
|
||||
Found: args[3].TypeName(),
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
ret = &objects.String{Value: strings.Replace(s1, s2, s3, i4)}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func textFormatBool(args ...objects.Object) (ret objects.Object, err error) {
|
||||
if len(args) != 1 {
|
||||
err = objects.ErrWrongNumArguments
|
||||
return
|
||||
}
|
||||
|
||||
b1, ok := args[0].(*objects.Bool)
|
||||
if !ok {
|
||||
err = objects.ErrInvalidArgumentType{
|
||||
Name: "first",
|
||||
Expected: "bool",
|
||||
Found: args[0].TypeName(),
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
if b1 == objects.TrueValue {
|
||||
ret = &objects.String{Value: "true"}
|
||||
} else {
|
||||
ret = &objects.String{Value: "false"}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func textFormatFloat(args ...objects.Object) (ret objects.Object, err error) {
|
||||
if len(args) != 4 {
|
||||
err = objects.ErrWrongNumArguments
|
||||
return
|
||||
}
|
||||
|
||||
f1, ok := args[0].(*objects.Float)
|
||||
if !ok {
|
||||
err = objects.ErrInvalidArgumentType{
|
||||
Name: "first",
|
||||
Expected: "float",
|
||||
Found: args[0].TypeName(),
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
s2, ok := objects.ToString(args[1])
|
||||
if !ok {
|
||||
err = objects.ErrInvalidArgumentType{
|
||||
Name: "second",
|
||||
Expected: "string(compatible)",
|
||||
Found: args[1].TypeName(),
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
i3, ok := objects.ToInt(args[2])
|
||||
if !ok {
|
||||
err = objects.ErrInvalidArgumentType{
|
||||
Name: "third",
|
||||
Expected: "int(compatible)",
|
||||
Found: args[2].TypeName(),
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
i4, ok := objects.ToInt(args[3])
|
||||
if !ok {
|
||||
err = objects.ErrInvalidArgumentType{
|
||||
Name: "fourth",
|
||||
Expected: "int(compatible)",
|
||||
Found: args[3].TypeName(),
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
ret = &objects.String{Value: strconv.FormatFloat(f1.Value, s2[0], i3, i4)}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func textFormatInt(args ...objects.Object) (ret objects.Object, err error) {
|
||||
if len(args) != 2 {
|
||||
err = objects.ErrWrongNumArguments
|
||||
return
|
||||
}
|
||||
|
||||
i1, ok := args[0].(*objects.Int)
|
||||
if !ok {
|
||||
err = objects.ErrInvalidArgumentType{
|
||||
Name: "first",
|
||||
Expected: "int",
|
||||
Found: args[0].TypeName(),
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
i2, ok := objects.ToInt(args[1])
|
||||
if !ok {
|
||||
err = objects.ErrInvalidArgumentType{
|
||||
Name: "second",
|
||||
Expected: "int(compatible)",
|
||||
Found: args[1].TypeName(),
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
ret = &objects.String{Value: strconv.FormatInt(i1.Value, i2)}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func textParseBool(args ...objects.Object) (ret objects.Object, err error) {
|
||||
if len(args) != 1 {
|
||||
err = objects.ErrWrongNumArguments
|
||||
return
|
||||
}
|
||||
|
||||
s1, ok := args[0].(*objects.String)
|
||||
if !ok {
|
||||
err = objects.ErrInvalidArgumentType{
|
||||
Name: "first",
|
||||
Expected: "string",
|
||||
Found: args[0].TypeName(),
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
parsed, err := strconv.ParseBool(s1.Value)
|
||||
if err != nil {
|
||||
ret = wrapError(err)
|
||||
return
|
||||
}
|
||||
|
||||
if parsed {
|
||||
ret = objects.TrueValue
|
||||
} else {
|
||||
ret = objects.FalseValue
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func textParseFloat(args ...objects.Object) (ret objects.Object, err error) {
|
||||
if len(args) != 2 {
|
||||
err = objects.ErrWrongNumArguments
|
||||
return
|
||||
}
|
||||
|
||||
s1, ok := args[0].(*objects.String)
|
||||
if !ok {
|
||||
err = objects.ErrInvalidArgumentType{
|
||||
Name: "first",
|
||||
Expected: "string",
|
||||
Found: args[0].TypeName(),
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
i2, ok := objects.ToInt(args[1])
|
||||
if !ok {
|
||||
err = objects.ErrInvalidArgumentType{
|
||||
Name: "second",
|
||||
Expected: "int(compatible)",
|
||||
Found: args[1].TypeName(),
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
parsed, err := strconv.ParseFloat(s1.Value, i2)
|
||||
if err != nil {
|
||||
ret = wrapError(err)
|
||||
return
|
||||
}
|
||||
|
||||
ret = &objects.Float{Value: parsed}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func textParseInt(args ...objects.Object) (ret objects.Object, err error) {
|
||||
if len(args) != 3 {
|
||||
err = objects.ErrWrongNumArguments
|
||||
return
|
||||
}
|
||||
|
||||
s1, ok := args[0].(*objects.String)
|
||||
if !ok {
|
||||
err = objects.ErrInvalidArgumentType{
|
||||
Name: "first",
|
||||
Expected: "string",
|
||||
Found: args[0].TypeName(),
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
i2, ok := objects.ToInt(args[1])
|
||||
if !ok {
|
||||
err = objects.ErrInvalidArgumentType{
|
||||
Name: "second",
|
||||
Expected: "int(compatible)",
|
||||
Found: args[1].TypeName(),
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
i3, ok := objects.ToInt(args[2])
|
||||
if !ok {
|
||||
err = objects.ErrInvalidArgumentType{
|
||||
Name: "third",
|
||||
Expected: "int(compatible)",
|
||||
Found: args[2].TypeName(),
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
parsed, err := strconv.ParseInt(s1.Value, i2, i3)
|
||||
if err != nil {
|
||||
ret = wrapError(err)
|
||||
return
|
||||
}
|
||||
|
||||
ret = &objects.Int{Value: parsed}
|
||||
|
||||
return
|
||||
}
|
|
@ -19,7 +19,11 @@ func makeTextRegexp(re *regexp.Regexp) *objects.ImmutableMap {
|
|||
|
||||
s1, ok := objects.ToString(args[0])
|
||||
if !ok {
|
||||
err = objects.ErrInvalidTypeConversion
|
||||
err = objects.ErrInvalidArgumentType{
|
||||
Name: "first",
|
||||
Expected: "string(compatible)",
|
||||
Found: args[0].TypeName(),
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -45,7 +49,11 @@ func makeTextRegexp(re *regexp.Regexp) *objects.ImmutableMap {
|
|||
|
||||
s1, ok := objects.ToString(args[0])
|
||||
if !ok {
|
||||
err = objects.ErrInvalidTypeConversion
|
||||
err = objects.ErrInvalidArgumentType{
|
||||
Name: "first",
|
||||
Expected: "string(compatible)",
|
||||
Found: args[0].TypeName(),
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -72,7 +80,11 @@ func makeTextRegexp(re *regexp.Regexp) *objects.ImmutableMap {
|
|||
|
||||
i2, ok := objects.ToInt(args[1])
|
||||
if !ok {
|
||||
err = objects.ErrInvalidTypeConversion
|
||||
err = objects.ErrInvalidArgumentType{
|
||||
Name: "second",
|
||||
Expected: "int(compatible)",
|
||||
Found: args[1].TypeName(),
|
||||
}
|
||||
return
|
||||
}
|
||||
m := re.FindAllStringSubmatchIndex(s1, i2)
|
||||
|
@ -111,13 +123,21 @@ func makeTextRegexp(re *regexp.Regexp) *objects.ImmutableMap {
|
|||
|
||||
s1, ok := objects.ToString(args[0])
|
||||
if !ok {
|
||||
err = objects.ErrInvalidTypeConversion
|
||||
err = objects.ErrInvalidArgumentType{
|
||||
Name: "first",
|
||||
Expected: "string(compatible)",
|
||||
Found: args[0].TypeName(),
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
s2, ok := objects.ToString(args[1])
|
||||
if !ok {
|
||||
err = objects.ErrInvalidTypeConversion
|
||||
err = objects.ErrInvalidArgumentType{
|
||||
Name: "second",
|
||||
Expected: "string(compatible)",
|
||||
Found: args[1].TypeName(),
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -139,7 +159,11 @@ func makeTextRegexp(re *regexp.Regexp) *objects.ImmutableMap {
|
|||
|
||||
s1, ok := objects.ToString(args[0])
|
||||
if !ok {
|
||||
err = objects.ErrInvalidTypeConversion
|
||||
err = objects.ErrInvalidArgumentType{
|
||||
Name: "first",
|
||||
Expected: "string(compatible)",
|
||||
Found: args[0].TypeName(),
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -147,7 +171,11 @@ func makeTextRegexp(re *regexp.Regexp) *objects.ImmutableMap {
|
|||
if numArgs > 1 {
|
||||
i2, ok = objects.ToInt(args[1])
|
||||
if !ok {
|
||||
err = objects.ErrInvalidTypeConversion
|
||||
err = objects.ErrInvalidArgumentType{
|
||||
Name: "second",
|
||||
Expected: "int(compatible)",
|
||||
Found: args[1].TypeName(),
|
||||
}
|
||||
return
|
||||
}
|
||||
}
|
|
@ -40,41 +40,41 @@ var timesModule = map[string]objects.Object{
|
|||
"october": &objects.Int{Value: int64(time.October)},
|
||||
"november": &objects.Int{Value: int64(time.November)},
|
||||
"december": &objects.Int{Value: int64(time.December)},
|
||||
"sleep": &objects.UserFunction{Value: timesSleep}, // sleep(int)
|
||||
"parse_duration": &objects.UserFunction{Value: timesParseDuration}, // parse_duration(str) => int
|
||||
"since": &objects.UserFunction{Value: timesSince}, // since(time) => int
|
||||
"until": &objects.UserFunction{Value: timesUntil}, // until(time) => int
|
||||
"duration_hours": &objects.UserFunction{Value: timesDurationHours}, // duration_hours(int) => float
|
||||
"duration_minutes": &objects.UserFunction{Value: timesDurationMinutes}, // duration_minutes(int) => float
|
||||
"duration_nanoseconds": &objects.UserFunction{Value: timesDurationNanoseconds}, // duration_nanoseconds(int) => int
|
||||
"duration_seconds": &objects.UserFunction{Value: timesDurationSeconds}, // duration_seconds(int) => float
|
||||
"duration_string": &objects.UserFunction{Value: timesDurationString}, // duration_string(int) => string
|
||||
"month_string": &objects.UserFunction{Value: timesMonthString}, // month_string(int) => string
|
||||
"date": &objects.UserFunction{Value: timesDate}, // date(year, month, day, hour, min, sec, nsec) => time
|
||||
"now": &objects.UserFunction{Value: timesNow}, // now() => time
|
||||
"parse": &objects.UserFunction{Value: timesParse}, // parse(format, str) => time
|
||||
"unix": &objects.UserFunction{Value: timesUnix}, // unix(sec, nsec) => time
|
||||
"add": &objects.UserFunction{Value: timesAdd}, // add(time, int) => time
|
||||
"add_date": &objects.UserFunction{Value: timesAddDate}, // add_date(time, years, months, days) => time
|
||||
"sub": &objects.UserFunction{Value: timesSub}, // sub(t time, u time) => int
|
||||
"after": &objects.UserFunction{Value: timesAfter}, // after(t time, u time) => bool
|
||||
"before": &objects.UserFunction{Value: timesBefore}, // before(t time, u time) => bool
|
||||
"time_year": &objects.UserFunction{Value: timesTimeYear}, // time_year(time) => int
|
||||
"time_month": &objects.UserFunction{Value: timesTimeMonth}, // time_month(time) => int
|
||||
"time_day": &objects.UserFunction{Value: timesTimeDay}, // time_day(time) => int
|
||||
"time_weekday": &objects.UserFunction{Value: timesTimeWeekday}, // time_weekday(time) => int
|
||||
"time_hour": &objects.UserFunction{Value: timesTimeHour}, // time_hour(time) => int
|
||||
"time_minute": &objects.UserFunction{Value: timesTimeMinute}, // time_minute(time) => int
|
||||
"time_second": &objects.UserFunction{Value: timesTimeSecond}, // time_second(time) => int
|
||||
"time_nanosecond": &objects.UserFunction{Value: timesTimeNanosecond}, // time_nanosecond(time) => int
|
||||
"time_unix": &objects.UserFunction{Value: timesTimeUnix}, // time_unix(time) => int
|
||||
"time_unix_nano": &objects.UserFunction{Value: timesTimeUnixNano}, // time_unix_nano(time) => int
|
||||
"time_format": &objects.UserFunction{Value: timesTimeFormat}, // time_format(time, format) => string
|
||||
"time_location": &objects.UserFunction{Value: timesTimeLocation}, // time_location(time) => string
|
||||
"time_string": &objects.UserFunction{Value: timesTimeString}, // time_string(time) => string
|
||||
"is_zero": &objects.UserFunction{Value: timesIsZero}, // is_zero(time) => bool
|
||||
"to_local": &objects.UserFunction{Value: timesToLocal}, // to_local(time) => time
|
||||
"to_utc": &objects.UserFunction{Value: timesToUTC}, // to_utc(time) => time
|
||||
"sleep": &objects.UserFunction{Name: "sleep", Value: timesSleep}, // sleep(int)
|
||||
"parse_duration": &objects.UserFunction{Name: "parse_duration", Value: timesParseDuration}, // parse_duration(str) => int
|
||||
"since": &objects.UserFunction{Name: "since", Value: timesSince}, // since(time) => int
|
||||
"until": &objects.UserFunction{Name: "until", Value: timesUntil}, // until(time) => int
|
||||
"duration_hours": &objects.UserFunction{Name: "duration_hours", Value: timesDurationHours}, // duration_hours(int) => float
|
||||
"duration_minutes": &objects.UserFunction{Name: "duration_minutes", Value: timesDurationMinutes}, // duration_minutes(int) => float
|
||||
"duration_nanoseconds": &objects.UserFunction{Name: "duration_nanoseconds", Value: timesDurationNanoseconds}, // duration_nanoseconds(int) => int
|
||||
"duration_seconds": &objects.UserFunction{Name: "duration_seconds", Value: timesDurationSeconds}, // duration_seconds(int) => float
|
||||
"duration_string": &objects.UserFunction{Name: "duration_string", Value: timesDurationString}, // duration_string(int) => string
|
||||
"month_string": &objects.UserFunction{Name: "month_string", Value: timesMonthString}, // month_string(int) => string
|
||||
"date": &objects.UserFunction{Name: "date", Value: timesDate}, // date(year, month, day, hour, min, sec, nsec) => time
|
||||
"now": &objects.UserFunction{Name: "now", Value: timesNow}, // now() => time
|
||||
"parse": &objects.UserFunction{Name: "parse", Value: timesParse}, // parse(format, str) => time
|
||||
"unix": &objects.UserFunction{Name: "unix", Value: timesUnix}, // unix(sec, nsec) => time
|
||||
"add": &objects.UserFunction{Name: "add", Value: timesAdd}, // add(time, int) => time
|
||||
"add_date": &objects.UserFunction{Name: "add_date", Value: timesAddDate}, // add_date(time, years, months, days) => time
|
||||
"sub": &objects.UserFunction{Name: "sub", Value: timesSub}, // sub(t time, u time) => int
|
||||
"after": &objects.UserFunction{Name: "after", Value: timesAfter}, // after(t time, u time) => bool
|
||||
"before": &objects.UserFunction{Name: "before", Value: timesBefore}, // before(t time, u time) => bool
|
||||
"time_year": &objects.UserFunction{Name: "time_year", Value: timesTimeYear}, // time_year(time) => int
|
||||
"time_month": &objects.UserFunction{Name: "time_month", Value: timesTimeMonth}, // time_month(time) => int
|
||||
"time_day": &objects.UserFunction{Name: "time_day", Value: timesTimeDay}, // time_day(time) => int
|
||||
"time_weekday": &objects.UserFunction{Name: "time_weekday", Value: timesTimeWeekday}, // time_weekday(time) => int
|
||||
"time_hour": &objects.UserFunction{Name: "time_hour", Value: timesTimeHour}, // time_hour(time) => int
|
||||
"time_minute": &objects.UserFunction{Name: "time_minute", Value: timesTimeMinute}, // time_minute(time) => int
|
||||
"time_second": &objects.UserFunction{Name: "time_second", Value: timesTimeSecond}, // time_second(time) => int
|
||||
"time_nanosecond": &objects.UserFunction{Name: "time_nanosecond", Value: timesTimeNanosecond}, // time_nanosecond(time) => int
|
||||
"time_unix": &objects.UserFunction{Name: "time_unix", Value: timesTimeUnix}, // time_unix(time) => int
|
||||
"time_unix_nano": &objects.UserFunction{Name: "time_unix_nano", Value: timesTimeUnixNano}, // time_unix_nano(time) => int
|
||||
"time_format": &objects.UserFunction{Name: "time_format", Value: timesTimeFormat}, // time_format(time, format) => string
|
||||
"time_location": &objects.UserFunction{Name: "time_location", Value: timesTimeLocation}, // time_location(time) => string
|
||||
"time_string": &objects.UserFunction{Name: "time_string", Value: timesTimeString}, // time_string(time) => string
|
||||
"is_zero": &objects.UserFunction{Name: "is_zero", Value: timesIsZero}, // is_zero(time) => bool
|
||||
"to_local": &objects.UserFunction{Name: "to_local", Value: timesToLocal}, // to_local(time) => time
|
||||
"to_utc": &objects.UserFunction{Name: "to_utc", Value: timesToUTC}, // to_utc(time) => time
|
||||
}
|
||||
|
||||
func timesSleep(args ...objects.Object) (ret objects.Object, err error) {
|
||||
|
@ -85,7 +85,11 @@ func timesSleep(args ...objects.Object) (ret objects.Object, err error) {
|
|||
|
||||
i1, ok := objects.ToInt64(args[0])
|
||||
if !ok {
|
||||
err = objects.ErrInvalidTypeConversion
|
||||
err = objects.ErrInvalidArgumentType{
|
||||
Name: "first",
|
||||
Expected: "int(compatible)",
|
||||
Found: args[0].TypeName(),
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -103,7 +107,11 @@ func timesParseDuration(args ...objects.Object) (ret objects.Object, err error)
|
|||
|
||||
s1, ok := objects.ToString(args[0])
|
||||
if !ok {
|
||||
err = objects.ErrInvalidTypeConversion
|
||||
err = objects.ErrInvalidArgumentType{
|
||||
Name: "first",
|
||||
Expected: "string(compatible)",
|
||||
Found: args[0].TypeName(),
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -126,7 +134,11 @@ func timesSince(args ...objects.Object) (ret objects.Object, err error) {
|
|||
|
||||
t1, ok := objects.ToTime(args[0])
|
||||
if !ok {
|
||||
err = objects.ErrInvalidTypeConversion
|
||||
err = objects.ErrInvalidArgumentType{
|
||||
Name: "first",
|
||||
Expected: "time(compatible)",
|
||||
Found: args[0].TypeName(),
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -143,7 +155,11 @@ func timesUntil(args ...objects.Object) (ret objects.Object, err error) {
|
|||
|
||||
t1, ok := objects.ToTime(args[0])
|
||||
if !ok {
|
||||
err = objects.ErrInvalidTypeConversion
|
||||
err = objects.ErrInvalidArgumentType{
|
||||
Name: "first",
|
||||
Expected: "time(compatible)",
|
||||
Found: args[0].TypeName(),
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -160,7 +176,11 @@ func timesDurationHours(args ...objects.Object) (ret objects.Object, err error)
|
|||
|
||||
i1, ok := objects.ToInt64(args[0])
|
||||
if !ok {
|
||||
err = objects.ErrInvalidTypeConversion
|
||||
err = objects.ErrInvalidArgumentType{
|
||||
Name: "first",
|
||||
Expected: "int(compatible)",
|
||||
Found: args[0].TypeName(),
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -177,7 +197,11 @@ func timesDurationMinutes(args ...objects.Object) (ret objects.Object, err error
|
|||
|
||||
i1, ok := objects.ToInt64(args[0])
|
||||
if !ok {
|
||||
err = objects.ErrInvalidTypeConversion
|
||||
err = objects.ErrInvalidArgumentType{
|
||||
Name: "first",
|
||||
Expected: "int(compatible)",
|
||||
Found: args[0].TypeName(),
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -194,7 +218,11 @@ func timesDurationNanoseconds(args ...objects.Object) (ret objects.Object, err e
|
|||
|
||||
i1, ok := objects.ToInt64(args[0])
|
||||
if !ok {
|
||||
err = objects.ErrInvalidTypeConversion
|
||||
err = objects.ErrInvalidArgumentType{
|
||||
Name: "first",
|
||||
Expected: "int(compatible)",
|
||||
Found: args[0].TypeName(),
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -211,7 +239,11 @@ func timesDurationSeconds(args ...objects.Object) (ret objects.Object, err error
|
|||
|
||||
i1, ok := objects.ToInt64(args[0])
|
||||
if !ok {
|
||||
err = objects.ErrInvalidTypeConversion
|
||||
err = objects.ErrInvalidArgumentType{
|
||||
Name: "first",
|
||||
Expected: "int(compatible)",
|
||||
Found: args[0].TypeName(),
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -228,7 +260,11 @@ func timesDurationString(args ...objects.Object) (ret objects.Object, err error)
|
|||
|
||||
i1, ok := objects.ToInt64(args[0])
|
||||
if !ok {
|
||||
err = objects.ErrInvalidTypeConversion
|
||||
err = objects.ErrInvalidArgumentType{
|
||||
Name: "first",
|
||||
Expected: "int(compatible)",
|
||||
Found: args[0].TypeName(),
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -245,7 +281,11 @@ func timesMonthString(args ...objects.Object) (ret objects.Object, err error) {
|
|||
|
||||
i1, ok := objects.ToInt64(args[0])
|
||||
if !ok {
|
||||
err = objects.ErrInvalidTypeConversion
|
||||
err = objects.ErrInvalidArgumentType{
|
||||
Name: "first",
|
||||
Expected: "int(compatible)",
|
||||
Found: args[0].TypeName(),
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -262,37 +302,65 @@ func timesDate(args ...objects.Object) (ret objects.Object, err error) {
|
|||
|
||||
i1, ok := objects.ToInt(args[0])
|
||||
if !ok {
|
||||
err = objects.ErrInvalidTypeConversion
|
||||
err = objects.ErrInvalidArgumentType{
|
||||
Name: "first",
|
||||
Expected: "int(compatible)",
|
||||
Found: args[0].TypeName(),
|
||||
}
|
||||
return
|
||||
}
|
||||
i2, ok := objects.ToInt(args[1])
|
||||
if !ok {
|
||||
err = objects.ErrInvalidTypeConversion
|
||||
err = objects.ErrInvalidArgumentType{
|
||||
Name: "second",
|
||||
Expected: "int(compatible)",
|
||||
Found: args[1].TypeName(),
|
||||
}
|
||||
return
|
||||
}
|
||||
i3, ok := objects.ToInt(args[2])
|
||||
if !ok {
|
||||
err = objects.ErrInvalidTypeConversion
|
||||
err = objects.ErrInvalidArgumentType{
|
||||
Name: "third",
|
||||
Expected: "int(compatible)",
|
||||
Found: args[2].TypeName(),
|
||||
}
|
||||
return
|
||||
}
|
||||
i4, ok := objects.ToInt(args[3])
|
||||
if !ok {
|
||||
err = objects.ErrInvalidTypeConversion
|
||||
err = objects.ErrInvalidArgumentType{
|
||||
Name: "fourth",
|
||||
Expected: "int(compatible)",
|
||||
Found: args[3].TypeName(),
|
||||
}
|
||||
return
|
||||
}
|
||||
i5, ok := objects.ToInt(args[4])
|
||||
if !ok {
|
||||
err = objects.ErrInvalidTypeConversion
|
||||
err = objects.ErrInvalidArgumentType{
|
||||
Name: "fifth",
|
||||
Expected: "int(compatible)",
|
||||
Found: args[4].TypeName(),
|
||||
}
|
||||
return
|
||||
}
|
||||
i6, ok := objects.ToInt(args[5])
|
||||
if !ok {
|
||||
err = objects.ErrInvalidTypeConversion
|
||||
err = objects.ErrInvalidArgumentType{
|
||||
Name: "sixth",
|
||||
Expected: "int(compatible)",
|
||||
Found: args[5].TypeName(),
|
||||
}
|
||||
return
|
||||
}
|
||||
i7, ok := objects.ToInt(args[6])
|
||||
if !ok {
|
||||
err = objects.ErrInvalidTypeConversion
|
||||
err = objects.ErrInvalidArgumentType{
|
||||
Name: "seventh",
|
||||
Expected: "int(compatible)",
|
||||
Found: args[6].TypeName(),
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -320,13 +388,21 @@ func timesParse(args ...objects.Object) (ret objects.Object, err error) {
|
|||
|
||||
s1, ok := objects.ToString(args[0])
|
||||
if !ok {
|
||||
err = objects.ErrInvalidTypeConversion
|
||||
err = objects.ErrInvalidArgumentType{
|
||||
Name: "first",
|
||||
Expected: "string(compatible)",
|
||||
Found: args[0].TypeName(),
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
s2, ok := objects.ToString(args[1])
|
||||
if !ok {
|
||||
err = objects.ErrInvalidTypeConversion
|
||||
err = objects.ErrInvalidArgumentType{
|
||||
Name: "second",
|
||||
Expected: "string(compatible)",
|
||||
Found: args[1].TypeName(),
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -349,13 +425,21 @@ func timesUnix(args ...objects.Object) (ret objects.Object, err error) {
|
|||
|
||||
i1, ok := objects.ToInt64(args[0])
|
||||
if !ok {
|
||||
err = objects.ErrInvalidTypeConversion
|
||||
err = objects.ErrInvalidArgumentType{
|
||||
Name: "first",
|
||||
Expected: "int(compatible)",
|
||||
Found: args[0].TypeName(),
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
i2, ok := objects.ToInt64(args[1])
|
||||
if !ok {
|
||||
err = objects.ErrInvalidTypeConversion
|
||||
err = objects.ErrInvalidArgumentType{
|
||||
Name: "second",
|
||||
Expected: "int(compatible)",
|
||||
Found: args[1].TypeName(),
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -372,13 +456,21 @@ func timesAdd(args ...objects.Object) (ret objects.Object, err error) {
|
|||
|
||||
t1, ok := objects.ToTime(args[0])
|
||||
if !ok {
|
||||
err = objects.ErrInvalidTypeConversion
|
||||
err = objects.ErrInvalidArgumentType{
|
||||
Name: "first",
|
||||
Expected: "time(compatible)",
|
||||
Found: args[0].TypeName(),
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
i2, ok := objects.ToInt64(args[1])
|
||||
if !ok {
|
||||
err = objects.ErrInvalidTypeConversion
|
||||
err = objects.ErrInvalidArgumentType{
|
||||
Name: "second",
|
||||
Expected: "int(compatible)",
|
||||
Found: args[1].TypeName(),
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -395,13 +487,21 @@ func timesSub(args ...objects.Object) (ret objects.Object, err error) {
|
|||
|
||||
t1, ok := objects.ToTime(args[0])
|
||||
if !ok {
|
||||
err = objects.ErrInvalidTypeConversion
|
||||
err = objects.ErrInvalidArgumentType{
|
||||
Name: "first",
|
||||
Expected: "time(compatible)",
|
||||
Found: args[0].TypeName(),
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
t2, ok := objects.ToTime(args[1])
|
||||
if !ok {
|
||||
err = objects.ErrInvalidTypeConversion
|
||||
err = objects.ErrInvalidArgumentType{
|
||||
Name: "second",
|
||||
Expected: "time(compatible)",
|
||||
Found: args[1].TypeName(),
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -418,25 +518,41 @@ func timesAddDate(args ...objects.Object) (ret objects.Object, err error) {
|
|||
|
||||
t1, ok := objects.ToTime(args[0])
|
||||
if !ok {
|
||||
err = objects.ErrInvalidTypeConversion
|
||||
err = objects.ErrInvalidArgumentType{
|
||||
Name: "first",
|
||||
Expected: "time(compatible)",
|
||||
Found: args[0].TypeName(),
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
i2, ok := objects.ToInt(args[1])
|
||||
if !ok {
|
||||
err = objects.ErrInvalidTypeConversion
|
||||
err = objects.ErrInvalidArgumentType{
|
||||
Name: "second",
|
||||
Expected: "int(compatible)",
|
||||
Found: args[1].TypeName(),
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
i3, ok := objects.ToInt(args[2])
|
||||
if !ok {
|
||||
err = objects.ErrInvalidTypeConversion
|
||||
err = objects.ErrInvalidArgumentType{
|
||||
Name: "third",
|
||||
Expected: "int(compatible)",
|
||||
Found: args[2].TypeName(),
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
i4, ok := objects.ToInt(args[3])
|
||||
if !ok {
|
||||
err = objects.ErrInvalidTypeConversion
|
||||
err = objects.ErrInvalidArgumentType{
|
||||
Name: "fourth",
|
||||
Expected: "int(compatible)",
|
||||
Found: args[3].TypeName(),
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -453,13 +569,21 @@ func timesAfter(args ...objects.Object) (ret objects.Object, err error) {
|
|||
|
||||
t1, ok := objects.ToTime(args[0])
|
||||
if !ok {
|
||||
err = objects.ErrInvalidTypeConversion
|
||||
err = objects.ErrInvalidArgumentType{
|
||||
Name: "first",
|
||||
Expected: "time(compatible)",
|
||||
Found: args[0].TypeName(),
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
t2, ok := objects.ToTime(args[1])
|
||||
if !ok {
|
||||
err = objects.ErrInvalidTypeConversion
|
||||
err = objects.ErrInvalidArgumentType{
|
||||
Name: "second",
|
||||
Expected: "time(compatible)",
|
||||
Found: args[1].TypeName(),
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -480,13 +604,21 @@ func timesBefore(args ...objects.Object) (ret objects.Object, err error) {
|
|||
|
||||
t1, ok := objects.ToTime(args[0])
|
||||
if !ok {
|
||||
err = objects.ErrInvalidTypeConversion
|
||||
err = objects.ErrInvalidArgumentType{
|
||||
Name: "first",
|
||||
Expected: "time(compatible)",
|
||||
Found: args[0].TypeName(),
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
t2, ok := objects.ToTime(args[1])
|
||||
if !ok {
|
||||
err = objects.ErrInvalidTypeConversion
|
||||
err = objects.ErrInvalidArgumentType{
|
||||
Name: "second",
|
||||
Expected: "time(compatible)",
|
||||
Found: args[0].TypeName(),
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -507,7 +639,11 @@ func timesTimeYear(args ...objects.Object) (ret objects.Object, err error) {
|
|||
|
||||
t1, ok := objects.ToTime(args[0])
|
||||
if !ok {
|
||||
err = objects.ErrInvalidTypeConversion
|
||||
err = objects.ErrInvalidArgumentType{
|
||||
Name: "first",
|
||||
Expected: "time(compatible)",
|
||||
Found: args[0].TypeName(),
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -524,7 +660,11 @@ func timesTimeMonth(args ...objects.Object) (ret objects.Object, err error) {
|
|||
|
||||
t1, ok := objects.ToTime(args[0])
|
||||
if !ok {
|
||||
err = objects.ErrInvalidTypeConversion
|
||||
err = objects.ErrInvalidArgumentType{
|
||||
Name: "first",
|
||||
Expected: "time(compatible)",
|
||||
Found: args[0].TypeName(),
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -541,7 +681,11 @@ func timesTimeDay(args ...objects.Object) (ret objects.Object, err error) {
|
|||
|
||||
t1, ok := objects.ToTime(args[0])
|
||||
if !ok {
|
||||
err = objects.ErrInvalidTypeConversion
|
||||
err = objects.ErrInvalidArgumentType{
|
||||
Name: "first",
|
||||
Expected: "time(compatible)",
|
||||
Found: args[0].TypeName(),
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -558,7 +702,11 @@ func timesTimeWeekday(args ...objects.Object) (ret objects.Object, err error) {
|
|||
|
||||
t1, ok := objects.ToTime(args[0])
|
||||
if !ok {
|
||||
err = objects.ErrInvalidTypeConversion
|
||||
err = objects.ErrInvalidArgumentType{
|
||||
Name: "first",
|
||||
Expected: "time(compatible)",
|
||||
Found: args[0].TypeName(),
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -575,7 +723,11 @@ func timesTimeHour(args ...objects.Object) (ret objects.Object, err error) {
|
|||
|
||||
t1, ok := objects.ToTime(args[0])
|
||||
if !ok {
|
||||
err = objects.ErrInvalidTypeConversion
|
||||
err = objects.ErrInvalidArgumentType{
|
||||
Name: "first",
|
||||
Expected: "time(compatible)",
|
||||
Found: args[0].TypeName(),
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -592,7 +744,11 @@ func timesTimeMinute(args ...objects.Object) (ret objects.Object, err error) {
|
|||
|
||||
t1, ok := objects.ToTime(args[0])
|
||||
if !ok {
|
||||
err = objects.ErrInvalidTypeConversion
|
||||
err = objects.ErrInvalidArgumentType{
|
||||
Name: "first",
|
||||
Expected: "time(compatible)",
|
||||
Found: args[0].TypeName(),
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -609,7 +765,11 @@ func timesTimeSecond(args ...objects.Object) (ret objects.Object, err error) {
|
|||
|
||||
t1, ok := objects.ToTime(args[0])
|
||||
if !ok {
|
||||
err = objects.ErrInvalidTypeConversion
|
||||
err = objects.ErrInvalidArgumentType{
|
||||
Name: "first",
|
||||
Expected: "time(compatible)",
|
||||
Found: args[0].TypeName(),
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -626,7 +786,11 @@ func timesTimeNanosecond(args ...objects.Object) (ret objects.Object, err error)
|
|||
|
||||
t1, ok := objects.ToTime(args[0])
|
||||
if !ok {
|
||||
err = objects.ErrInvalidTypeConversion
|
||||
err = objects.ErrInvalidArgumentType{
|
||||
Name: "first",
|
||||
Expected: "time(compatible)",
|
||||
Found: args[0].TypeName(),
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -643,7 +807,11 @@ func timesTimeUnix(args ...objects.Object) (ret objects.Object, err error) {
|
|||
|
||||
t1, ok := objects.ToTime(args[0])
|
||||
if !ok {
|
||||
err = objects.ErrInvalidTypeConversion
|
||||
err = objects.ErrInvalidArgumentType{
|
||||
Name: "first",
|
||||
Expected: "time(compatible)",
|
||||
Found: args[0].TypeName(),
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -660,7 +828,11 @@ func timesTimeUnixNano(args ...objects.Object) (ret objects.Object, err error) {
|
|||
|
||||
t1, ok := objects.ToTime(args[0])
|
||||
if !ok {
|
||||
err = objects.ErrInvalidTypeConversion
|
||||
err = objects.ErrInvalidArgumentType{
|
||||
Name: "first",
|
||||
Expected: "time(compatible)",
|
||||
Found: args[0].TypeName(),
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -677,13 +849,21 @@ func timesTimeFormat(args ...objects.Object) (ret objects.Object, err error) {
|
|||
|
||||
t1, ok := objects.ToTime(args[0])
|
||||
if !ok {
|
||||
err = objects.ErrInvalidTypeConversion
|
||||
err = objects.ErrInvalidArgumentType{
|
||||
Name: "first",
|
||||
Expected: "time(compatible)",
|
||||
Found: args[0].TypeName(),
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
s2, ok := objects.ToString(args[1])
|
||||
if !ok {
|
||||
err = objects.ErrInvalidTypeConversion
|
||||
err = objects.ErrInvalidArgumentType{
|
||||
Name: "second",
|
||||
Expected: "string(compatible)",
|
||||
Found: args[1].TypeName(),
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -700,7 +880,11 @@ func timesIsZero(args ...objects.Object) (ret objects.Object, err error) {
|
|||
|
||||
t1, ok := objects.ToTime(args[0])
|
||||
if !ok {
|
||||
err = objects.ErrInvalidTypeConversion
|
||||
err = objects.ErrInvalidArgumentType{
|
||||
Name: "first",
|
||||
Expected: "time(compatible)",
|
||||
Found: args[0].TypeName(),
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -721,7 +905,11 @@ func timesToLocal(args ...objects.Object) (ret objects.Object, err error) {
|
|||
|
||||
t1, ok := objects.ToTime(args[0])
|
||||
if !ok {
|
||||
err = objects.ErrInvalidTypeConversion
|
||||
err = objects.ErrInvalidArgumentType{
|
||||
Name: "first",
|
||||
Expected: "time(compatible)",
|
||||
Found: args[0].TypeName(),
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -738,7 +926,11 @@ func timesToUTC(args ...objects.Object) (ret objects.Object, err error) {
|
|||
|
||||
t1, ok := objects.ToTime(args[0])
|
||||
if !ok {
|
||||
err = objects.ErrInvalidTypeConversion
|
||||
err = objects.ErrInvalidArgumentType{
|
||||
Name: "first",
|
||||
Expected: "time(compatible)",
|
||||
Found: args[0].TypeName(),
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -755,7 +947,11 @@ func timesTimeLocation(args ...objects.Object) (ret objects.Object, err error) {
|
|||
|
||||
t1, ok := objects.ToTime(args[0])
|
||||
if !ok {
|
||||
err = objects.ErrInvalidTypeConversion
|
||||
err = objects.ErrInvalidArgumentType{
|
||||
Name: "first",
|
||||
Expected: "time(compatible)",
|
||||
Found: args[0].TypeName(),
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -772,7 +968,11 @@ func timesTimeString(args ...objects.Object) (ret objects.Object, err error) {
|
|||
|
||||
t1, ok := objects.ToTime(args[0])
|
||||
if !ok {
|
||||
err = objects.ErrInvalidTypeConversion
|
||||
err = objects.ErrInvalidArgumentType{
|
||||
Name: "first",
|
||||
Expected: "time(compatible)",
|
||||
Found: args[0].TypeName(),
|
||||
}
|
||||
return
|
||||
}
|
||||
|
Loading…
Reference in a new issue