diff --git a/gateway/gateway.go b/gateway/gateway.go index b74a3aaa..e265e62b 100644 --- a/gateway/gateway.go +++ b/gateway/gateway.go @@ -10,8 +10,8 @@ import ( "github.com/42wim/matterbridge/bridge" "github.com/42wim/matterbridge/bridge/config" "github.com/42wim/matterbridge/internal" - "github.com/d5/tengo/script" - "github.com/d5/tengo/stdlib" + "github.com/d5/tengo/v2" + "github.com/d5/tengo/v2/stdlib" lru "github.com/hashicorp/golang-lru" "github.com/matterbridge/emoji" "github.com/sirupsen/logrus" @@ -514,7 +514,7 @@ func modifyMessageTengo(filename string, msg *config.Message) error { if err != nil { return err } - s := script.New(res) + s := tengo.NewScript(res) s.SetImports(stdlib.GetModuleMap(stdlib.AllModuleNames()...)) _ = s.Add("msgText", msg.Text) _ = s.Add("msgUsername", msg.Username) @@ -541,7 +541,7 @@ func (gw *Gateway) modifyUsernameTengo(msg *config.Message, br *bridge.Bridge) ( if err != nil { return "", err } - s := script.New(res) + s := tengo.NewScript(res) s.SetImports(stdlib.GetModuleMap(stdlib.AllModuleNames()...)) _ = s.Add("result", "") _ = s.Add("msgText", msg.Text) @@ -580,7 +580,7 @@ func (gw *Gateway) modifySendMessageTengo(origmsg *config.Message, msg *config.M return err } } - s := script.New(res) + s := tengo.NewScript(res) s.SetImports(stdlib.GetModuleMap(stdlib.AllModuleNames()...)) _ = s.Add("inAccount", origmsg.Account) _ = s.Add("inProtocol", origmsg.Protocol) diff --git a/go.mod b/go.mod index ff07c4a5..d47fa936 100644 --- a/go.mod +++ b/go.mod @@ -7,7 +7,7 @@ require ( github.com/Philipp15b/go-steam v1.0.1-0.20190816133340-b04c5a83c1c0 github.com/Rhymen/go-whatsapp v0.1.0 github.com/bwmarrin/discordgo v0.20.2 - github.com/d5/tengo v1.24.8 + github.com/d5/tengo/v2 v2.0.2 github.com/dfordsoft/golib v0.0.0-20180902042739-76ee6ab99bec github.com/fsnotify/fsnotify v1.4.7 github.com/go-telegram-bot-api/telegram-bot-api v4.6.5-0.20181225215658-ec221ba9ea45+incompatible diff --git a/go.sum b/go.sum index 9963c24d..c356ee32 100644 --- a/go.sum +++ b/go.sum @@ -33,8 +33,8 @@ github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= -github.com/d5/tengo v1.24.8 h1:PRJ+NWt7ae/9sSbIfThOBTkPSvNV+dwYoBAvwfNgNJY= -github.com/d5/tengo v1.24.8/go.mod h1:VhLq8Q2QFhCIJO3NhvM934qOThykMqJi9y9Siqd1ocQ= +github.com/d5/tengo/v2 v2.0.2 h1:3APkPZPc1FExaJoWrN5YzvDqc6GNkQH6ehmCRDmN83I= +github.com/d5/tengo/v2 v2.0.2/go.mod h1:XRGjEs5I9jYIKTxly6HCF8oiiilk5E/RYXOZ5b0DZC8= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= diff --git a/vendor/github.com/d5/tengo/.travis.yml b/vendor/github.com/d5/tengo/.travis.yml deleted file mode 100644 index 57986ae9..00000000 --- a/vendor/github.com/d5/tengo/.travis.yml +++ /dev/null @@ -1,17 +0,0 @@ -language: go - -go: - - "1.12" - -install: - - env GO111MODULE=on go get -u golang.org/x/lint/golint - -script: - - env GO111MODULE=on make test - -deploy: - - provider: script - skip_cleanup: true - script: curl -sL https://git.io/goreleaser | bash - on: - tags: true \ No newline at end of file diff --git a/vendor/github.com/d5/tengo/compiler/ast/array_lit.go b/vendor/github.com/d5/tengo/compiler/ast/array_lit.go deleted file mode 100644 index 9fb4ed67..00000000 --- a/vendor/github.com/d5/tengo/compiler/ast/array_lit.go +++ /dev/null @@ -1,35 +0,0 @@ -package ast - -import ( - "strings" - - "github.com/d5/tengo/compiler/source" -) - -// ArrayLit represents an array literal. -type ArrayLit struct { - Elements []Expr - LBrack source.Pos - RBrack source.Pos -} - -func (e *ArrayLit) exprNode() {} - -// Pos returns the position of first character belonging to the node. -func (e *ArrayLit) Pos() source.Pos { - return e.LBrack -} - -// End returns the position of first character immediately after the node. -func (e *ArrayLit) End() source.Pos { - return e.RBrack + 1 -} - -func (e *ArrayLit) String() string { - var elements []string - for _, m := range e.Elements { - elements = append(elements, m.String()) - } - - return "[" + strings.Join(elements, ", ") + "]" -} diff --git a/vendor/github.com/d5/tengo/compiler/ast/assign_stmt.go b/vendor/github.com/d5/tengo/compiler/ast/assign_stmt.go deleted file mode 100644 index e129114e..00000000 --- a/vendor/github.com/d5/tengo/compiler/ast/assign_stmt.go +++ /dev/null @@ -1,40 +0,0 @@ -package ast - -import ( - "strings" - - "github.com/d5/tengo/compiler/source" - "github.com/d5/tengo/compiler/token" -) - -// AssignStmt represents an assignment statement. -type AssignStmt struct { - LHS []Expr - RHS []Expr - Token token.Token - TokenPos source.Pos -} - -func (s *AssignStmt) stmtNode() {} - -// Pos returns the position of first character belonging to the node. -func (s *AssignStmt) Pos() source.Pos { - return s.LHS[0].Pos() -} - -// End returns the position of first character immediately after the node. -func (s *AssignStmt) End() source.Pos { - return s.RHS[len(s.RHS)-1].End() -} - -func (s *AssignStmt) String() string { - var lhs, rhs []string - for _, e := range s.LHS { - lhs = append(lhs, e.String()) - } - for _, e := range s.RHS { - rhs = append(rhs, e.String()) - } - - return strings.Join(lhs, ", ") + " " + s.Token.String() + " " + strings.Join(rhs, ", ") -} diff --git a/vendor/github.com/d5/tengo/compiler/ast/ast.go b/vendor/github.com/d5/tengo/compiler/ast/ast.go deleted file mode 100644 index 9fd06728..00000000 --- a/vendor/github.com/d5/tengo/compiler/ast/ast.go +++ /dev/null @@ -1,5 +0,0 @@ -package ast - -const ( - nullRep = "" -) diff --git a/vendor/github.com/d5/tengo/compiler/ast/bad_expr.go b/vendor/github.com/d5/tengo/compiler/ast/bad_expr.go deleted file mode 100644 index 771f26fd..00000000 --- a/vendor/github.com/d5/tengo/compiler/ast/bad_expr.go +++ /dev/null @@ -1,25 +0,0 @@ -package ast - -import "github.com/d5/tengo/compiler/source" - -// BadExpr represents a bad expression. -type BadExpr struct { - From source.Pos - To source.Pos -} - -func (e *BadExpr) exprNode() {} - -// Pos returns the position of first character belonging to the node. -func (e *BadExpr) Pos() source.Pos { - return e.From -} - -// End returns the position of first character immediately after the node. -func (e *BadExpr) End() source.Pos { - return e.To -} - -func (e *BadExpr) String() string { - return "" -} diff --git a/vendor/github.com/d5/tengo/compiler/ast/bad_stmt.go b/vendor/github.com/d5/tengo/compiler/ast/bad_stmt.go deleted file mode 100644 index c2d0ae9a..00000000 --- a/vendor/github.com/d5/tengo/compiler/ast/bad_stmt.go +++ /dev/null @@ -1,25 +0,0 @@ -package ast - -import "github.com/d5/tengo/compiler/source" - -// BadStmt represents a bad statement. -type BadStmt struct { - From source.Pos - To source.Pos -} - -func (s *BadStmt) stmtNode() {} - -// Pos returns the position of first character belonging to the node. -func (s *BadStmt) Pos() source.Pos { - return s.From -} - -// End returns the position of first character immediately after the node. -func (s *BadStmt) End() source.Pos { - return s.To -} - -func (s *BadStmt) String() string { - return "" -} diff --git a/vendor/github.com/d5/tengo/compiler/ast/binary_expr.go b/vendor/github.com/d5/tengo/compiler/ast/binary_expr.go deleted file mode 100644 index 0cc5bba8..00000000 --- a/vendor/github.com/d5/tengo/compiler/ast/binary_expr.go +++ /dev/null @@ -1,30 +0,0 @@ -package ast - -import ( - "github.com/d5/tengo/compiler/source" - "github.com/d5/tengo/compiler/token" -) - -// BinaryExpr represents a binary operator expression. -type BinaryExpr struct { - LHS Expr - RHS Expr - Token token.Token - TokenPos source.Pos -} - -func (e *BinaryExpr) exprNode() {} - -// Pos returns the position of first character belonging to the node. -func (e *BinaryExpr) Pos() source.Pos { - return e.LHS.Pos() -} - -// End returns the position of first character immediately after the node. -func (e *BinaryExpr) End() source.Pos { - return e.RHS.End() -} - -func (e *BinaryExpr) String() string { - return "(" + e.LHS.String() + " " + e.Token.String() + " " + e.RHS.String() + ")" -} diff --git a/vendor/github.com/d5/tengo/compiler/ast/block_stmt.go b/vendor/github.com/d5/tengo/compiler/ast/block_stmt.go deleted file mode 100644 index 9bde9fa3..00000000 --- a/vendor/github.com/d5/tengo/compiler/ast/block_stmt.go +++ /dev/null @@ -1,35 +0,0 @@ -package ast - -import ( - "strings" - - "github.com/d5/tengo/compiler/source" -) - -// BlockStmt represents a block statement. -type BlockStmt struct { - Stmts []Stmt - LBrace source.Pos - RBrace source.Pos -} - -func (s *BlockStmt) stmtNode() {} - -// Pos returns the position of first character belonging to the node. -func (s *BlockStmt) Pos() source.Pos { - return s.LBrace -} - -// End returns the position of first character immediately after the node. -func (s *BlockStmt) End() source.Pos { - return s.RBrace + 1 -} - -func (s *BlockStmt) String() string { - var list []string - for _, e := range s.Stmts { - list = append(list, e.String()) - } - - return "{" + strings.Join(list, "; ") + "}" -} diff --git a/vendor/github.com/d5/tengo/compiler/ast/bool_lit.go b/vendor/github.com/d5/tengo/compiler/ast/bool_lit.go deleted file mode 100644 index e667a5c8..00000000 --- a/vendor/github.com/d5/tengo/compiler/ast/bool_lit.go +++ /dev/null @@ -1,26 +0,0 @@ -package ast - -import "github.com/d5/tengo/compiler/source" - -// BoolLit represents a boolean literal. -type BoolLit struct { - Value bool - ValuePos source.Pos - Literal string -} - -func (e *BoolLit) exprNode() {} - -// Pos returns the position of first character belonging to the node. -func (e *BoolLit) Pos() source.Pos { - return e.ValuePos -} - -// End returns the position of first character immediately after the node. -func (e *BoolLit) End() source.Pos { - return source.Pos(int(e.ValuePos) + len(e.Literal)) -} - -func (e *BoolLit) String() string { - return e.Literal -} diff --git a/vendor/github.com/d5/tengo/compiler/ast/branch_stmt.go b/vendor/github.com/d5/tengo/compiler/ast/branch_stmt.go deleted file mode 100644 index f6c7fdea..00000000 --- a/vendor/github.com/d5/tengo/compiler/ast/branch_stmt.go +++ /dev/null @@ -1,38 +0,0 @@ -package ast - -import ( - "github.com/d5/tengo/compiler/source" - "github.com/d5/tengo/compiler/token" -) - -// BranchStmt represents a branch statement. -type BranchStmt struct { - Token token.Token - TokenPos source.Pos - Label *Ident -} - -func (s *BranchStmt) stmtNode() {} - -// Pos returns the position of first character belonging to the node. -func (s *BranchStmt) Pos() source.Pos { - return s.TokenPos -} - -// End returns the position of first character immediately after the node. -func (s *BranchStmt) End() source.Pos { - if s.Label != nil { - return s.Label.End() - } - - return source.Pos(int(s.TokenPos) + len(s.Token.String())) -} - -func (s *BranchStmt) String() string { - var label string - if s.Label != nil { - label = " " + s.Label.Name - } - - return s.Token.String() + label -} diff --git a/vendor/github.com/d5/tengo/compiler/ast/call_expr.go b/vendor/github.com/d5/tengo/compiler/ast/call_expr.go deleted file mode 100644 index 0219d7c9..00000000 --- a/vendor/github.com/d5/tengo/compiler/ast/call_expr.go +++ /dev/null @@ -1,36 +0,0 @@ -package ast - -import ( - "strings" - - "github.com/d5/tengo/compiler/source" -) - -// CallExpr represents a function call expression. -type CallExpr struct { - Func Expr - LParen source.Pos - Args []Expr - RParen source.Pos -} - -func (e *CallExpr) exprNode() {} - -// Pos returns the position of first character belonging to the node. -func (e *CallExpr) Pos() source.Pos { - return e.Func.Pos() -} - -// End returns the position of first character immediately after the node. -func (e *CallExpr) End() source.Pos { - return e.RParen + 1 -} - -func (e *CallExpr) String() string { - var args []string - for _, e := range e.Args { - args = append(args, e.String()) - } - - return e.Func.String() + "(" + strings.Join(args, ", ") + ")" -} diff --git a/vendor/github.com/d5/tengo/compiler/ast/char_lit.go b/vendor/github.com/d5/tengo/compiler/ast/char_lit.go deleted file mode 100644 index 592f8744..00000000 --- a/vendor/github.com/d5/tengo/compiler/ast/char_lit.go +++ /dev/null @@ -1,26 +0,0 @@ -package ast - -import "github.com/d5/tengo/compiler/source" - -// CharLit represents a character literal. -type CharLit struct { - Value rune - ValuePos source.Pos - Literal string -} - -func (e *CharLit) exprNode() {} - -// Pos returns the position of first character belonging to the node. -func (e *CharLit) Pos() source.Pos { - return e.ValuePos -} - -// End returns the position of first character immediately after the node. -func (e *CharLit) End() source.Pos { - return source.Pos(int(e.ValuePos) + len(e.Literal)) -} - -func (e *CharLit) String() string { - return e.Literal -} diff --git a/vendor/github.com/d5/tengo/compiler/ast/cond_expr.go b/vendor/github.com/d5/tengo/compiler/ast/cond_expr.go deleted file mode 100644 index bb1db849..00000000 --- a/vendor/github.com/d5/tengo/compiler/ast/cond_expr.go +++ /dev/null @@ -1,30 +0,0 @@ -package ast - -import ( - "github.com/d5/tengo/compiler/source" -) - -// CondExpr represents a ternary conditional expression. -type CondExpr struct { - Cond Expr - True Expr - False Expr - QuestionPos source.Pos - ColonPos source.Pos -} - -func (e *CondExpr) exprNode() {} - -// Pos returns the position of first character belonging to the node. -func (e *CondExpr) Pos() source.Pos { - return e.Cond.Pos() -} - -// End returns the position of first character immediately after the node. -func (e *CondExpr) End() source.Pos { - return e.False.End() -} - -func (e *CondExpr) String() string { - return "(" + e.Cond.String() + " ? " + e.True.String() + " : " + e.False.String() + ")" -} diff --git a/vendor/github.com/d5/tengo/compiler/ast/empty_stmt.go b/vendor/github.com/d5/tengo/compiler/ast/empty_stmt.go deleted file mode 100644 index a2ac6ffe..00000000 --- a/vendor/github.com/d5/tengo/compiler/ast/empty_stmt.go +++ /dev/null @@ -1,29 +0,0 @@ -package ast - -import "github.com/d5/tengo/compiler/source" - -// EmptyStmt represents an empty statement. -type EmptyStmt struct { - Semicolon source.Pos - Implicit bool -} - -func (s *EmptyStmt) stmtNode() {} - -// Pos returns the position of first character belonging to the node. -func (s *EmptyStmt) Pos() source.Pos { - return s.Semicolon -} - -// End returns the position of first character immediately after the node. -func (s *EmptyStmt) End() source.Pos { - if s.Implicit { - return s.Semicolon - } - - return s.Semicolon + 1 -} - -func (s *EmptyStmt) String() string { - return ";" -} diff --git a/vendor/github.com/d5/tengo/compiler/ast/error_expr.go b/vendor/github.com/d5/tengo/compiler/ast/error_expr.go deleted file mode 100644 index 7ce5667e..00000000 --- a/vendor/github.com/d5/tengo/compiler/ast/error_expr.go +++ /dev/null @@ -1,29 +0,0 @@ -package ast - -import ( - "github.com/d5/tengo/compiler/source" -) - -// ErrorExpr represents an error expression -type ErrorExpr struct { - Expr Expr - ErrorPos source.Pos - LParen source.Pos - RParen source.Pos -} - -func (e *ErrorExpr) exprNode() {} - -// Pos returns the position of first character belonging to the node. -func (e *ErrorExpr) Pos() source.Pos { - return e.ErrorPos -} - -// End returns the position of first character immediately after the node. -func (e *ErrorExpr) End() source.Pos { - return e.RParen -} - -func (e *ErrorExpr) String() string { - return "error(" + e.Expr.String() + ")" -} diff --git a/vendor/github.com/d5/tengo/compiler/ast/export_stmt.go b/vendor/github.com/d5/tengo/compiler/ast/export_stmt.go deleted file mode 100644 index 64eb7606..00000000 --- a/vendor/github.com/d5/tengo/compiler/ast/export_stmt.go +++ /dev/null @@ -1,27 +0,0 @@ -package ast - -import ( - "github.com/d5/tengo/compiler/source" -) - -// ExportStmt represents an export statement. -type ExportStmt struct { - ExportPos source.Pos - Result Expr -} - -func (s *ExportStmt) stmtNode() {} - -// Pos returns the position of first character belonging to the node. -func (s *ExportStmt) Pos() source.Pos { - return s.ExportPos -} - -// End returns the position of first character immediately after the node. -func (s *ExportStmt) End() source.Pos { - return s.Result.End() -} - -func (s *ExportStmt) String() string { - return "export " + s.Result.String() -} diff --git a/vendor/github.com/d5/tengo/compiler/ast/expr.go b/vendor/github.com/d5/tengo/compiler/ast/expr.go deleted file mode 100644 index 764bacec..00000000 --- a/vendor/github.com/d5/tengo/compiler/ast/expr.go +++ /dev/null @@ -1,7 +0,0 @@ -package ast - -// Expr represents an expression node in the AST. -type Expr interface { - Node - exprNode() -} diff --git a/vendor/github.com/d5/tengo/compiler/ast/expr_stmt.go b/vendor/github.com/d5/tengo/compiler/ast/expr_stmt.go deleted file mode 100644 index 095a3ad5..00000000 --- a/vendor/github.com/d5/tengo/compiler/ast/expr_stmt.go +++ /dev/null @@ -1,24 +0,0 @@ -package ast - -import "github.com/d5/tengo/compiler/source" - -// ExprStmt represents an expression statement. -type ExprStmt struct { - Expr Expr -} - -func (s *ExprStmt) stmtNode() {} - -// Pos returns the position of first character belonging to the node. -func (s *ExprStmt) Pos() source.Pos { - return s.Expr.Pos() -} - -// End returns the position of first character immediately after the node. -func (s *ExprStmt) End() source.Pos { - return s.Expr.End() -} - -func (s *ExprStmt) String() string { - return s.Expr.String() -} diff --git a/vendor/github.com/d5/tengo/compiler/ast/float_lit.go b/vendor/github.com/d5/tengo/compiler/ast/float_lit.go deleted file mode 100644 index 670f744b..00000000 --- a/vendor/github.com/d5/tengo/compiler/ast/float_lit.go +++ /dev/null @@ -1,26 +0,0 @@ -package ast - -import "github.com/d5/tengo/compiler/source" - -// FloatLit represents a floating point literal. -type FloatLit struct { - Value float64 - ValuePos source.Pos - Literal string -} - -func (e *FloatLit) exprNode() {} - -// Pos returns the position of first character belonging to the node. -func (e *FloatLit) Pos() source.Pos { - return e.ValuePos -} - -// End returns the position of first character immediately after the node. -func (e *FloatLit) End() source.Pos { - return source.Pos(int(e.ValuePos) + len(e.Literal)) -} - -func (e *FloatLit) String() string { - return e.Literal -} diff --git a/vendor/github.com/d5/tengo/compiler/ast/for_in_stmt.go b/vendor/github.com/d5/tengo/compiler/ast/for_in_stmt.go deleted file mode 100644 index 18020b56..00000000 --- a/vendor/github.com/d5/tengo/compiler/ast/for_in_stmt.go +++ /dev/null @@ -1,32 +0,0 @@ -package ast - -import "github.com/d5/tengo/compiler/source" - -// ForInStmt represents a for-in statement. -type ForInStmt struct { - ForPos source.Pos - Key *Ident - Value *Ident - Iterable Expr - Body *BlockStmt -} - -func (s *ForInStmt) stmtNode() {} - -// Pos returns the position of first character belonging to the node. -func (s *ForInStmt) Pos() source.Pos { - return s.ForPos -} - -// End returns the position of first character immediately after the node. -func (s *ForInStmt) End() source.Pos { - return s.Body.End() -} - -func (s *ForInStmt) String() string { - if s.Value != nil { - return "for " + s.Key.String() + ", " + s.Value.String() + " in " + s.Iterable.String() + " " + s.Body.String() - } - - return "for " + s.Key.String() + " in " + s.Iterable.String() + " " + s.Body.String() -} diff --git a/vendor/github.com/d5/tengo/compiler/ast/for_stmt.go b/vendor/github.com/d5/tengo/compiler/ast/for_stmt.go deleted file mode 100644 index 4b5a0a17..00000000 --- a/vendor/github.com/d5/tengo/compiler/ast/for_stmt.go +++ /dev/null @@ -1,43 +0,0 @@ -package ast - -import "github.com/d5/tengo/compiler/source" - -// ForStmt represents a for statement. -type ForStmt struct { - ForPos source.Pos - Init Stmt - Cond Expr - Post Stmt - Body *BlockStmt -} - -func (s *ForStmt) stmtNode() {} - -// Pos returns the position of first character belonging to the node. -func (s *ForStmt) Pos() source.Pos { - return s.ForPos -} - -// End returns the position of first character immediately after the node. -func (s *ForStmt) End() source.Pos { - return s.Body.End() -} - -func (s *ForStmt) String() string { - var init, cond, post string - if s.Init != nil { - init = s.Init.String() - } - if s.Cond != nil { - cond = s.Cond.String() + " " - } - if s.Post != nil { - post = s.Post.String() - } - - if init != "" || post != "" { - return "for " + init + " ; " + cond + " ; " + post + s.Body.String() - } - - return "for " + cond + s.Body.String() -} diff --git a/vendor/github.com/d5/tengo/compiler/ast/func_lit.go b/vendor/github.com/d5/tengo/compiler/ast/func_lit.go deleted file mode 100644 index 2e90ed2b..00000000 --- a/vendor/github.com/d5/tengo/compiler/ast/func_lit.go +++ /dev/null @@ -1,25 +0,0 @@ -package ast - -import "github.com/d5/tengo/compiler/source" - -// FuncLit represents a function literal. -type FuncLit struct { - Type *FuncType - Body *BlockStmt -} - -func (e *FuncLit) exprNode() {} - -// Pos returns the position of first character belonging to the node. -func (e *FuncLit) Pos() source.Pos { - return e.Type.Pos() -} - -// End returns the position of first character immediately after the node. -func (e *FuncLit) End() source.Pos { - return e.Body.End() -} - -func (e *FuncLit) String() string { - return "func" + e.Type.Params.String() + " " + e.Body.String() -} diff --git a/vendor/github.com/d5/tengo/compiler/ast/func_type.go b/vendor/github.com/d5/tengo/compiler/ast/func_type.go deleted file mode 100644 index 2afaabb1..00000000 --- a/vendor/github.com/d5/tengo/compiler/ast/func_type.go +++ /dev/null @@ -1,25 +0,0 @@ -package ast - -import "github.com/d5/tengo/compiler/source" - -// FuncType represents a function type definition. -type FuncType struct { - FuncPos source.Pos - Params *IdentList -} - -func (e *FuncType) exprNode() {} - -// Pos returns the position of first character belonging to the node. -func (e *FuncType) Pos() source.Pos { - return e.FuncPos -} - -// End returns the position of first character immediately after the node. -func (e *FuncType) End() source.Pos { - return e.Params.End() -} - -func (e *FuncType) String() string { - return "func" + e.Params.String() -} diff --git a/vendor/github.com/d5/tengo/compiler/ast/ident.go b/vendor/github.com/d5/tengo/compiler/ast/ident.go deleted file mode 100644 index 33b7ff76..00000000 --- a/vendor/github.com/d5/tengo/compiler/ast/ident.go +++ /dev/null @@ -1,29 +0,0 @@ -package ast - -import "github.com/d5/tengo/compiler/source" - -// Ident represents an identifier. -type Ident struct { - Name string - NamePos source.Pos -} - -func (e *Ident) exprNode() {} - -// Pos returns the position of first character belonging to the node. -func (e *Ident) Pos() source.Pos { - return e.NamePos -} - -// End returns the position of first character immediately after the node. -func (e *Ident) End() source.Pos { - return source.Pos(int(e.NamePos) + len(e.Name)) -} - -func (e *Ident) String() string { - if e != nil { - return e.Name - } - - return nullRep -} diff --git a/vendor/github.com/d5/tengo/compiler/ast/if_stmt.go b/vendor/github.com/d5/tengo/compiler/ast/if_stmt.go deleted file mode 100644 index b3d65606..00000000 --- a/vendor/github.com/d5/tengo/compiler/ast/if_stmt.go +++ /dev/null @@ -1,40 +0,0 @@ -package ast - -import "github.com/d5/tengo/compiler/source" - -// IfStmt represents an if statement. -type IfStmt struct { - IfPos source.Pos - Init Stmt - Cond Expr - Body *BlockStmt - Else Stmt // else branch; or nil -} - -func (s *IfStmt) stmtNode() {} - -// Pos returns the position of first character belonging to the node. -func (s *IfStmt) Pos() source.Pos { - return s.IfPos -} - -// End returns the position of first character immediately after the node. -func (s *IfStmt) End() source.Pos { - if s.Else != nil { - return s.Else.End() - } - - return s.Body.End() -} - -func (s *IfStmt) String() string { - var initStmt, elseStmt string - if s.Init != nil { - initStmt = s.Init.String() + "; " - } - if s.Else != nil { - elseStmt = " else " + s.Else.String() - } - - return "if " + initStmt + s.Cond.String() + " " + s.Body.String() + elseStmt -} diff --git a/vendor/github.com/d5/tengo/compiler/ast/immutable_expr.go b/vendor/github.com/d5/tengo/compiler/ast/immutable_expr.go deleted file mode 100644 index f9843b50..00000000 --- a/vendor/github.com/d5/tengo/compiler/ast/immutable_expr.go +++ /dev/null @@ -1,29 +0,0 @@ -package ast - -import ( - "github.com/d5/tengo/compiler/source" -) - -// ImmutableExpr represents an immutable expression -type ImmutableExpr struct { - Expr Expr - ErrorPos source.Pos - LParen source.Pos - RParen source.Pos -} - -func (e *ImmutableExpr) exprNode() {} - -// Pos returns the position of first character belonging to the node. -func (e *ImmutableExpr) Pos() source.Pos { - return e.ErrorPos -} - -// End returns the position of first character immediately after the node. -func (e *ImmutableExpr) End() source.Pos { - return e.RParen -} - -func (e *ImmutableExpr) String() string { - return "immutable(" + e.Expr.String() + ")" -} diff --git a/vendor/github.com/d5/tengo/compiler/ast/import_expr.go b/vendor/github.com/d5/tengo/compiler/ast/import_expr.go deleted file mode 100644 index 6eff74a9..00000000 --- a/vendor/github.com/d5/tengo/compiler/ast/import_expr.go +++ /dev/null @@ -1,29 +0,0 @@ -package ast - -import ( - "github.com/d5/tengo/compiler/source" - "github.com/d5/tengo/compiler/token" -) - -// ImportExpr represents an import expression -type ImportExpr struct { - ModuleName string - Token token.Token - TokenPos source.Pos -} - -func (e *ImportExpr) exprNode() {} - -// Pos returns the position of first character belonging to the node. -func (e *ImportExpr) Pos() source.Pos { - return e.TokenPos -} - -// End returns the position of first character immediately after the node. -func (e *ImportExpr) End() source.Pos { - return source.Pos(int(e.TokenPos) + 10 + len(e.ModuleName)) // import("moduleName") -} - -func (e *ImportExpr) String() string { - return `import("` + e.ModuleName + `")"` -} diff --git a/vendor/github.com/d5/tengo/compiler/ast/inc_dec_stmt.go b/vendor/github.com/d5/tengo/compiler/ast/inc_dec_stmt.go deleted file mode 100644 index e4e7f92c..00000000 --- a/vendor/github.com/d5/tengo/compiler/ast/inc_dec_stmt.go +++ /dev/null @@ -1,29 +0,0 @@ -package ast - -import ( - "github.com/d5/tengo/compiler/source" - "github.com/d5/tengo/compiler/token" -) - -// IncDecStmt represents increment or decrement statement. -type IncDecStmt struct { - Expr Expr - Token token.Token - TokenPos source.Pos -} - -func (s *IncDecStmt) stmtNode() {} - -// Pos returns the position of first character belonging to the node. -func (s *IncDecStmt) Pos() source.Pos { - return s.Expr.Pos() -} - -// End returns the position of first character immediately after the node. -func (s *IncDecStmt) End() source.Pos { - return source.Pos(int(s.TokenPos) + 2) -} - -func (s *IncDecStmt) String() string { - return s.Expr.String() + s.Token.String() -} diff --git a/vendor/github.com/d5/tengo/compiler/ast/index_expr.go b/vendor/github.com/d5/tengo/compiler/ast/index_expr.go deleted file mode 100644 index bc0992a3..00000000 --- a/vendor/github.com/d5/tengo/compiler/ast/index_expr.go +++ /dev/null @@ -1,32 +0,0 @@ -package ast - -import "github.com/d5/tengo/compiler/source" - -// IndexExpr represents an index expression. -type IndexExpr struct { - Expr Expr - LBrack source.Pos - Index Expr - RBrack source.Pos -} - -func (e *IndexExpr) exprNode() {} - -// Pos returns the position of first character belonging to the node. -func (e *IndexExpr) Pos() source.Pos { - return e.Expr.Pos() -} - -// End returns the position of first character immediately after the node. -func (e *IndexExpr) End() source.Pos { - return e.RBrack + 1 -} - -func (e *IndexExpr) String() string { - var index string - if e.Index != nil { - index = e.Index.String() - } - - return e.Expr.String() + "[" + index + "]" -} diff --git a/vendor/github.com/d5/tengo/compiler/ast/int_lit.go b/vendor/github.com/d5/tengo/compiler/ast/int_lit.go deleted file mode 100644 index 3e1fd98b..00000000 --- a/vendor/github.com/d5/tengo/compiler/ast/int_lit.go +++ /dev/null @@ -1,26 +0,0 @@ -package ast - -import "github.com/d5/tengo/compiler/source" - -// IntLit represents an integer literal. -type IntLit struct { - Value int64 - ValuePos source.Pos - Literal string -} - -func (e *IntLit) exprNode() {} - -// Pos returns the position of first character belonging to the node. -func (e *IntLit) Pos() source.Pos { - return e.ValuePos -} - -// End returns the position of first character immediately after the node. -func (e *IntLit) End() source.Pos { - return source.Pos(int(e.ValuePos) + len(e.Literal)) -} - -func (e *IntLit) String() string { - return e.Literal -} diff --git a/vendor/github.com/d5/tengo/compiler/ast/map_element_lit.go b/vendor/github.com/d5/tengo/compiler/ast/map_element_lit.go deleted file mode 100644 index 3d7fca9e..00000000 --- a/vendor/github.com/d5/tengo/compiler/ast/map_element_lit.go +++ /dev/null @@ -1,27 +0,0 @@ -package ast - -import "github.com/d5/tengo/compiler/source" - -// MapElementLit represents a map element. -type MapElementLit struct { - Key string - KeyPos source.Pos - ColonPos source.Pos - Value Expr -} - -func (e *MapElementLit) exprNode() {} - -// Pos returns the position of first character belonging to the node. -func (e *MapElementLit) Pos() source.Pos { - return e.KeyPos -} - -// End returns the position of first character immediately after the node. -func (e *MapElementLit) End() source.Pos { - return e.Value.End() -} - -func (e *MapElementLit) String() string { - return e.Key + ": " + e.Value.String() -} diff --git a/vendor/github.com/d5/tengo/compiler/ast/map_lit.go b/vendor/github.com/d5/tengo/compiler/ast/map_lit.go deleted file mode 100644 index a228224d..00000000 --- a/vendor/github.com/d5/tengo/compiler/ast/map_lit.go +++ /dev/null @@ -1,35 +0,0 @@ -package ast - -import ( - "strings" - - "github.com/d5/tengo/compiler/source" -) - -// MapLit represents a map literal. -type MapLit struct { - LBrace source.Pos - Elements []*MapElementLit - RBrace source.Pos -} - -func (e *MapLit) exprNode() {} - -// Pos returns the position of first character belonging to the node. -func (e *MapLit) Pos() source.Pos { - return e.LBrace -} - -// End returns the position of first character immediately after the node. -func (e *MapLit) End() source.Pos { - return e.RBrace + 1 -} - -func (e *MapLit) String() string { - var elements []string - for _, m := range e.Elements { - elements = append(elements, m.String()) - } - - return "{" + strings.Join(elements, ", ") + "}" -} diff --git a/vendor/github.com/d5/tengo/compiler/ast/node.go b/vendor/github.com/d5/tengo/compiler/ast/node.go deleted file mode 100644 index 44677b47..00000000 --- a/vendor/github.com/d5/tengo/compiler/ast/node.go +++ /dev/null @@ -1,13 +0,0 @@ -package ast - -import "github.com/d5/tengo/compiler/source" - -// Node represents a node in the AST. -type Node interface { - // Pos returns the position of first character belonging to the node. - Pos() source.Pos - // End returns the position of first character immediately after the node. - End() source.Pos - // String returns a string representation of the node. - String() string -} diff --git a/vendor/github.com/d5/tengo/compiler/ast/paren_expr.go b/vendor/github.com/d5/tengo/compiler/ast/paren_expr.go deleted file mode 100644 index 8db4ac02..00000000 --- a/vendor/github.com/d5/tengo/compiler/ast/paren_expr.go +++ /dev/null @@ -1,26 +0,0 @@ -package ast - -import "github.com/d5/tengo/compiler/source" - -// ParenExpr represents a parenthesis wrapped expression. -type ParenExpr struct { - Expr Expr - LParen source.Pos - RParen source.Pos -} - -func (e *ParenExpr) exprNode() {} - -// Pos returns the position of first character belonging to the node. -func (e *ParenExpr) Pos() source.Pos { - return e.LParen -} - -// End returns the position of first character immediately after the node. -func (e *ParenExpr) End() source.Pos { - return e.RParen + 1 -} - -func (e *ParenExpr) String() string { - return "(" + e.Expr.String() + ")" -} diff --git a/vendor/github.com/d5/tengo/compiler/ast/return_stmt.go b/vendor/github.com/d5/tengo/compiler/ast/return_stmt.go deleted file mode 100644 index 592d45b8..00000000 --- a/vendor/github.com/d5/tengo/compiler/ast/return_stmt.go +++ /dev/null @@ -1,35 +0,0 @@ -package ast - -import ( - "github.com/d5/tengo/compiler/source" -) - -// ReturnStmt represents a return statement. -type ReturnStmt struct { - ReturnPos source.Pos - Result Expr -} - -func (s *ReturnStmt) stmtNode() {} - -// Pos returns the position of first character belonging to the node. -func (s *ReturnStmt) Pos() source.Pos { - return s.ReturnPos -} - -// End returns the position of first character immediately after the node. -func (s *ReturnStmt) End() source.Pos { - if s.Result != nil { - return s.Result.End() - } - - return s.ReturnPos + 6 -} - -func (s *ReturnStmt) String() string { - if s.Result != nil { - return "return " + s.Result.String() - } - - return "return" -} diff --git a/vendor/github.com/d5/tengo/compiler/ast/selector_expr.go b/vendor/github.com/d5/tengo/compiler/ast/selector_expr.go deleted file mode 100644 index 31d2e6d1..00000000 --- a/vendor/github.com/d5/tengo/compiler/ast/selector_expr.go +++ /dev/null @@ -1,25 +0,0 @@ -package ast - -import "github.com/d5/tengo/compiler/source" - -// SelectorExpr represents a selector expression. -type SelectorExpr struct { - Expr Expr - Sel Expr -} - -func (e *SelectorExpr) exprNode() {} - -// Pos returns the position of first character belonging to the node. -func (e *SelectorExpr) Pos() source.Pos { - return e.Expr.Pos() -} - -// End returns the position of first character immediately after the node. -func (e *SelectorExpr) End() source.Pos { - return e.Sel.End() -} - -func (e *SelectorExpr) String() string { - return e.Expr.String() + "." + e.Sel.String() -} diff --git a/vendor/github.com/d5/tengo/compiler/ast/slice_expr.go b/vendor/github.com/d5/tengo/compiler/ast/slice_expr.go deleted file mode 100644 index e7e2e05b..00000000 --- a/vendor/github.com/d5/tengo/compiler/ast/slice_expr.go +++ /dev/null @@ -1,36 +0,0 @@ -package ast - -import "github.com/d5/tengo/compiler/source" - -// SliceExpr represents a slice expression. -type SliceExpr struct { - Expr Expr - LBrack source.Pos - Low Expr - High Expr - RBrack source.Pos -} - -func (e *SliceExpr) exprNode() {} - -// Pos returns the position of first character belonging to the node. -func (e *SliceExpr) Pos() source.Pos { - return e.Expr.Pos() -} - -// End returns the position of first character immediately after the node. -func (e *SliceExpr) End() source.Pos { - return e.RBrack + 1 -} - -func (e *SliceExpr) String() string { - var low, high string - if e.Low != nil { - low = e.Low.String() - } - if e.High != nil { - high = e.High.String() - } - - return e.Expr.String() + "[" + low + ":" + high + "]" -} diff --git a/vendor/github.com/d5/tengo/compiler/ast/stmt.go b/vendor/github.com/d5/tengo/compiler/ast/stmt.go deleted file mode 100644 index 6b26ba88..00000000 --- a/vendor/github.com/d5/tengo/compiler/ast/stmt.go +++ /dev/null @@ -1,7 +0,0 @@ -package ast - -// Stmt represents a statement in the AST. -type Stmt interface { - Node - stmtNode() -} diff --git a/vendor/github.com/d5/tengo/compiler/ast/string_lit.go b/vendor/github.com/d5/tengo/compiler/ast/string_lit.go deleted file mode 100644 index 2119d34b..00000000 --- a/vendor/github.com/d5/tengo/compiler/ast/string_lit.go +++ /dev/null @@ -1,26 +0,0 @@ -package ast - -import "github.com/d5/tengo/compiler/source" - -// StringLit represents a string literal. -type StringLit struct { - Value string - ValuePos source.Pos - Literal string -} - -func (e *StringLit) exprNode() {} - -// Pos returns the position of first character belonging to the node. -func (e *StringLit) Pos() source.Pos { - return e.ValuePos -} - -// End returns the position of first character immediately after the node. -func (e *StringLit) End() source.Pos { - return source.Pos(int(e.ValuePos) + len(e.Literal)) -} - -func (e *StringLit) String() string { - return e.Literal -} diff --git a/vendor/github.com/d5/tengo/compiler/ast/unary_expr.go b/vendor/github.com/d5/tengo/compiler/ast/unary_expr.go deleted file mode 100644 index 53236146..00000000 --- a/vendor/github.com/d5/tengo/compiler/ast/unary_expr.go +++ /dev/null @@ -1,29 +0,0 @@ -package ast - -import ( - "github.com/d5/tengo/compiler/source" - "github.com/d5/tengo/compiler/token" -) - -// UnaryExpr represents an unary operator expression. -type UnaryExpr struct { - Expr Expr - Token token.Token - TokenPos source.Pos -} - -func (e *UnaryExpr) exprNode() {} - -// Pos returns the position of first character belonging to the node. -func (e *UnaryExpr) Pos() source.Pos { - return e.Expr.Pos() -} - -// End returns the position of first character immediately after the node. -func (e *UnaryExpr) End() source.Pos { - return e.Expr.End() -} - -func (e *UnaryExpr) String() string { - return "(" + e.Token.String() + e.Expr.String() + ")" -} diff --git a/vendor/github.com/d5/tengo/compiler/ast/undefined_lit.go b/vendor/github.com/d5/tengo/compiler/ast/undefined_lit.go deleted file mode 100644 index 8e51b113..00000000 --- a/vendor/github.com/d5/tengo/compiler/ast/undefined_lit.go +++ /dev/null @@ -1,24 +0,0 @@ -package ast - -import "github.com/d5/tengo/compiler/source" - -// UndefinedLit represents an undefined literal. -type UndefinedLit struct { - TokenPos source.Pos -} - -func (e *UndefinedLit) exprNode() {} - -// Pos returns the position of first character belonging to the node. -func (e *UndefinedLit) Pos() source.Pos { - return e.TokenPos -} - -// End returns the position of first character immediately after the node. -func (e *UndefinedLit) End() source.Pos { - return e.TokenPos + 9 // len(undefined) == 9 -} - -func (e *UndefinedLit) String() string { - return "undefined" -} diff --git a/vendor/github.com/d5/tengo/compiler/bytecode.go b/vendor/github.com/d5/tengo/compiler/bytecode.go deleted file mode 100644 index 35f36c0a..00000000 --- a/vendor/github.com/d5/tengo/compiler/bytecode.go +++ /dev/null @@ -1,90 +0,0 @@ -package compiler - -import ( - "encoding/gob" - "fmt" - "io" - "reflect" - - "github.com/d5/tengo/compiler/source" - "github.com/d5/tengo/objects" -) - -// Bytecode is a compiled instructions and constants. -type Bytecode struct { - FileSet *source.FileSet - MainFunction *objects.CompiledFunction - Constants []objects.Object -} - -// Encode writes Bytecode data to the writer. -func (b *Bytecode) Encode(w io.Writer) error { - enc := gob.NewEncoder(w) - - if err := enc.Encode(b.FileSet); err != nil { - return err - } - - if err := enc.Encode(b.MainFunction); err != nil { - return err - } - - // constants - return enc.Encode(b.Constants) -} - -// CountObjects returns the number of objects found in Constants. -func (b *Bytecode) CountObjects() int { - n := 0 - - for _, c := range b.Constants { - n += objects.CountObjects(c) - } - - return n -} - -// FormatInstructions returns human readable string representations of -// compiled instructions. -func (b *Bytecode) FormatInstructions() []string { - return FormatInstructions(b.MainFunction.Instructions, 0) -} - -// FormatConstants returns human readable string representations of -// compiled constants. -func (b *Bytecode) FormatConstants() (output []string) { - for cidx, cn := range b.Constants { - switch cn := cn.(type) { - case *objects.CompiledFunction: - output = append(output, fmt.Sprintf("[% 3d] (Compiled Function|%p)", cidx, &cn)) - for _, l := range FormatInstructions(cn.Instructions, 0) { - output = append(output, fmt.Sprintf(" %s", l)) - } - default: - output = append(output, fmt.Sprintf("[% 3d] %s (%s|%p)", cidx, cn, reflect.TypeOf(cn).Elem().Name(), &cn)) - } - } - - return -} - -func init() { - gob.Register(&source.FileSet{}) - gob.Register(&source.File{}) - gob.Register(&objects.Array{}) - gob.Register(&objects.Bool{}) - gob.Register(&objects.Bytes{}) - gob.Register(&objects.Char{}) - gob.Register(&objects.Closure{}) - gob.Register(&objects.CompiledFunction{}) - 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.String{}) - gob.Register(&objects.Time{}) - gob.Register(&objects.Undefined{}) - gob.Register(&objects.UserFunction{}) -} diff --git a/vendor/github.com/d5/tengo/compiler/bytecode_decode.go b/vendor/github.com/d5/tengo/compiler/bytecode_decode.go deleted file mode 100644 index b3b32a64..00000000 --- a/vendor/github.com/d5/tengo/compiler/bytecode_decode.go +++ /dev/null @@ -1,97 +0,0 @@ -package compiler - -import ( - "encoding/gob" - "fmt" - "io" - - "github.com/d5/tengo/objects" -) - -// Decode reads Bytecode data from the reader. -func (b *Bytecode) Decode(r io.Reader, modules *objects.ModuleMap) error { - if modules == nil { - modules = objects.NewModuleMap() - } - - dec := gob.NewDecoder(r) - - 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 - } - - if err := dec.Decode(&b.Constants); err != nil { - return err - } - for i, v := range b.Constants { - fv, err := fixDecoded(v, modules) - if err != nil { - return err - } - b.Constants[i] = fv - } - - return nil -} - -func fixDecoded(o objects.Object, modules *objects.ModuleMap) (objects.Object, error) { - switch o := o.(type) { - case *objects.Bool: - if o.IsFalsy() { - return objects.FalseValue, nil - } - return objects.TrueValue, nil - case *objects.Undefined: - return objects.UndefinedValue, nil - case *objects.Array: - for i, v := range o.Value { - fv, err := fixDecoded(v, modules) - if err != nil { - return nil, err - } - o.Value[i] = fv - } - case *objects.ImmutableArray: - for i, v := range o.Value { - fv, err := fixDecoded(v, modules) - if err != nil { - return nil, err - } - o.Value[i] = fv - } - case *objects.Map: - for k, v := range o.Value { - fv, err := fixDecoded(v, modules) - if err != nil { - return nil, err - } - o.Value[k] = fv - } - case *objects.ImmutableMap: - modName := moduleName(o) - if mod := modules.GetBuiltinModule(modName); mod != nil { - return mod.AsImmutableMap(modName), nil - } - - for k, v := range o.Value { - // encoding of user function not supported - if _, isUserFunction := v.(*objects.UserFunction); isUserFunction { - return nil, fmt.Errorf("user function not decodable") - } - - fv, err := fixDecoded(v, modules) - if err != nil { - return nil, err - } - o.Value[k] = fv - } - } - - return o, nil -} diff --git a/vendor/github.com/d5/tengo/compiler/bytecode_optimize.go b/vendor/github.com/d5/tengo/compiler/bytecode_optimize.go deleted file mode 100644 index 9526de2e..00000000 --- a/vendor/github.com/d5/tengo/compiler/bytecode_optimize.go +++ /dev/null @@ -1,129 +0,0 @@ -package compiler - -import ( - "fmt" - - "github.com/d5/tengo/objects" -) - -// RemoveDuplicates finds and remove the duplicate values in Constants. -// Note this function mutates Bytecode. -func (b *Bytecode) RemoveDuplicates() { - var deduped []objects.Object - - indexMap := make(map[int]int) // mapping from old constant index to new index - ints := make(map[int64]int) - strings := make(map[string]int) - floats := make(map[float64]int) - chars := make(map[rune]int) - immutableMaps := make(map[string]int) // for modules - - for curIdx, c := range b.Constants { - switch c := c.(type) { - case *objects.CompiledFunction: - // add to deduped list - indexMap[curIdx] = len(deduped) - deduped = append(deduped, c) - case *objects.ImmutableMap: - modName := moduleName(c) - newIdx, ok := immutableMaps[modName] - if modName != "" && ok { - indexMap[curIdx] = newIdx - } else { - newIdx = len(deduped) - immutableMaps[modName] = newIdx - indexMap[curIdx] = newIdx - deduped = append(deduped, c) - } - case *objects.Int: - if newIdx, ok := ints[c.Value]; ok { - indexMap[curIdx] = newIdx - } else { - newIdx = len(deduped) - ints[c.Value] = newIdx - indexMap[curIdx] = newIdx - deduped = append(deduped, c) - } - case *objects.String: - if newIdx, ok := strings[c.Value]; ok { - indexMap[curIdx] = newIdx - } else { - newIdx = len(deduped) - strings[c.Value] = newIdx - indexMap[curIdx] = newIdx - deduped = append(deduped, c) - } - case *objects.Float: - if newIdx, ok := floats[c.Value]; ok { - indexMap[curIdx] = newIdx - } else { - newIdx = len(deduped) - floats[c.Value] = newIdx - indexMap[curIdx] = newIdx - deduped = append(deduped, c) - } - case *objects.Char: - if newIdx, ok := chars[c.Value]; ok { - indexMap[curIdx] = newIdx - } else { - newIdx = len(deduped) - chars[c.Value] = newIdx - indexMap[curIdx] = newIdx - deduped = append(deduped, c) - } - default: - panic(fmt.Errorf("unsupported top-level constant type: %s", c.TypeName())) - } - } - - // replace with de-duplicated constants - b.Constants = deduped - - // update CONST instructions with new indexes - // main function - updateConstIndexes(b.MainFunction.Instructions, indexMap) - // other compiled functions in constants - for _, c := range b.Constants { - switch c := c.(type) { - case *objects.CompiledFunction: - updateConstIndexes(c.Instructions, indexMap) - } - } -} - -func updateConstIndexes(insts []byte, indexMap map[int]int) { - i := 0 - for i < len(insts) { - op := insts[i] - numOperands := OpcodeOperands[op] - _, read := ReadOperands(numOperands, insts[i+1:]) - - switch op { - case OpConstant: - curIdx := int(insts[i+2]) | int(insts[i+1])<<8 - newIdx, ok := indexMap[curIdx] - if !ok { - panic(fmt.Errorf("constant index not found: %d", curIdx)) - } - copy(insts[i:], MakeInstruction(op, newIdx)) - case OpClosure: - curIdx := int(insts[i+2]) | int(insts[i+1])<<8 - numFree := int(insts[i+3]) - newIdx, ok := indexMap[curIdx] - if !ok { - panic(fmt.Errorf("constant index not found: %d", curIdx)) - } - copy(insts[i:], MakeInstruction(op, newIdx, numFree)) - } - - i += 1 + read - } -} - -func moduleName(mod *objects.ImmutableMap) string { - if modName, ok := mod.Value["__module_name__"].(*objects.String); ok { - return modName.Value - } - - return "" -} diff --git a/vendor/github.com/d5/tengo/compiler/compilation_scope.go b/vendor/github.com/d5/tengo/compiler/compilation_scope.go deleted file mode 100644 index 41e7876b..00000000 --- a/vendor/github.com/d5/tengo/compiler/compilation_scope.go +++ /dev/null @@ -1,11 +0,0 @@ -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 - symbolInit map[string]bool - sourceMap map[int]source.Pos -} diff --git a/vendor/github.com/d5/tengo/compiler/compiler.go b/vendor/github.com/d5/tengo/compiler/compiler.go deleted file mode 100644 index 8bde5dc9..00000000 --- a/vendor/github.com/d5/tengo/compiler/compiler.go +++ /dev/null @@ -1,846 +0,0 @@ -package compiler - -import ( - "fmt" - "io" - "io/ioutil" - "path/filepath" - "reflect" - "strings" - - "github.com/d5/tengo" - "github.com/d5/tengo/compiler/ast" - "github.com/d5/tengo/compiler/source" - "github.com/d5/tengo/compiler/token" - "github.com/d5/tengo/objects" -) - -// Compiler compiles the AST into a bytecode. -type Compiler struct { - file *source.File - parent *Compiler - modulePath string - constants []objects.Object - symbolTable *SymbolTable - scopes []CompilationScope - scopeIndex int - modules *objects.ModuleMap - compiledModules map[string]*objects.CompiledFunction - allowFileImport bool - loops []*Loop - loopIndex int - trace io.Writer - indent int -} - -// NewCompiler creates a Compiler. -func NewCompiler(file *source.File, symbolTable *SymbolTable, constants []objects.Object, modules *objects.ModuleMap, trace io.Writer) *Compiler { - mainScope := CompilationScope{ - symbolInit: make(map[string]bool), - sourceMap: make(map[int]source.Pos), - } - - // symbol table - if symbolTable == nil { - symbolTable = NewSymbolTable() - } - - // add builtin functions to the symbol table - for idx, fn := range objects.Builtins { - symbolTable.DefineBuiltin(idx, fn.Name) - } - - // builtin modules - if modules == nil { - modules = objects.NewModuleMap() - } - - return &Compiler{ - file: file, - symbolTable: symbolTable, - constants: constants, - scopes: []CompilationScope{mainScope}, - scopeIndex: 0, - loopIndex: -1, - trace: trace, - modules: modules, - compiledModules: make(map[string]*objects.CompiledFunction), - } -} - -// Compile compiles the AST node. -func (c *Compiler) Compile(node ast.Node) error { - if c.trace != nil { - if node != nil { - defer un(trace(c, fmt.Sprintf("%s (%s)", node.String(), reflect.TypeOf(node).Elem().Name()))) - } else { - defer un(trace(c, "")) - } - } - - switch node := node.(type) { - case *ast.File: - for _, stmt := range node.Stmts { - if err := c.Compile(stmt); err != nil { - return err - } - } - - case *ast.ExprStmt: - if err := c.Compile(node.Expr); err != nil { - return err - } - c.emit(node, OpPop) - - case *ast.IncDecStmt: - op := token.AddAssign - if node.Token == token.Dec { - op = token.SubAssign - } - - 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 { - return err - } - - case *ast.BinaryExpr: - if node.Token == token.LAnd || node.Token == token.LOr { - return c.compileLogical(node) - } - - if node.Token == token.Less { - if err := c.Compile(node.RHS); err != nil { - return err - } - - if err := c.Compile(node.LHS); err != nil { - return err - } - - c.emit(node, OpBinaryOp, int(token.Greater)) - - return nil - } else if node.Token == token.LessEq { - if err := c.Compile(node.RHS); err != nil { - return err - } - if err := c.Compile(node.LHS); err != nil { - return err - } - - c.emit(node, OpBinaryOp, int(token.GreaterEq)) - - return nil - } - - if err := c.Compile(node.LHS); err != nil { - return err - } - if err := c.Compile(node.RHS); err != nil { - return err - } - - switch node.Token { - case token.Add: - c.emit(node, OpBinaryOp, int(token.Add)) - case token.Sub: - c.emit(node, OpBinaryOp, int(token.Sub)) - case token.Mul: - c.emit(node, OpBinaryOp, int(token.Mul)) - case token.Quo: - c.emit(node, OpBinaryOp, int(token.Quo)) - case token.Rem: - c.emit(node, OpBinaryOp, int(token.Rem)) - case token.Greater: - c.emit(node, OpBinaryOp, int(token.Greater)) - case token.GreaterEq: - c.emit(node, OpBinaryOp, int(token.GreaterEq)) - case token.Equal: - c.emit(node, OpEqual) - case token.NotEqual: - c.emit(node, OpNotEqual) - case token.And: - c.emit(node, OpBinaryOp, int(token.And)) - case token.Or: - c.emit(node, OpBinaryOp, int(token.Or)) - case token.Xor: - c.emit(node, OpBinaryOp, int(token.Xor)) - case token.AndNot: - c.emit(node, OpBinaryOp, int(token.AndNot)) - case token.Shl: - c.emit(node, OpBinaryOp, int(token.Shl)) - case token.Shr: - c.emit(node, OpBinaryOp, int(token.Shr)) - default: - return c.errorf(node, "invalid binary operator: %s", node.Token.String()) - } - - case *ast.IntLit: - c.emit(node, OpConstant, c.addConstant(&objects.Int{Value: node.Value})) - - case *ast.FloatLit: - c.emit(node, OpConstant, c.addConstant(&objects.Float{Value: node.Value})) - - case *ast.BoolLit: - if node.Value { - c.emit(node, OpTrue) - } else { - c.emit(node, OpFalse) - } - - case *ast.StringLit: - if len(node.Value) > tengo.MaxStringLen { - return c.error(node, objects.ErrStringLimit) - } - - c.emit(node, OpConstant, c.addConstant(&objects.String{Value: node.Value})) - - case *ast.CharLit: - c.emit(node, OpConstant, c.addConstant(&objects.Char{Value: node.Value})) - - case *ast.UndefinedLit: - c.emit(node, OpNull) - - case *ast.UnaryExpr: - if err := c.Compile(node.Expr); err != nil { - return err - } - - switch node.Token { - case token.Not: - c.emit(node, OpLNot) - case token.Sub: - c.emit(node, OpMinus) - case token.Xor: - c.emit(node, OpBComplement) - case token.Add: - // do nothing? - default: - return c.errorf(node, "invalid unary operator: %s", node.Token.String()) - } - - case *ast.IfStmt: - // open new symbol table for the statement - c.symbolTable = c.symbolTable.Fork(true) - defer func() { - c.symbolTable = c.symbolTable.Parent(false) - }() - - if node.Init != nil { - if err := c.Compile(node.Init); err != nil { - return err - } - } - - if err := c.Compile(node.Cond); err != nil { - return err - } - - // first jump placeholder - jumpPos1 := c.emit(node, OpJumpFalsy, 0) - - if err := c.Compile(node.Body); err != nil { - return err - } - - if node.Else != nil { - // second jump placeholder - jumpPos2 := c.emit(node, OpJump, 0) - - // update first jump offset - curPos := len(c.currentInstructions()) - c.changeOperand(jumpPos1, curPos) - - if err := c.Compile(node.Else); err != nil { - return err - } - - // update second jump offset - curPos = len(c.currentInstructions()) - c.changeOperand(jumpPos2, curPos) - } else { - // update first jump offset - curPos := len(c.currentInstructions()) - c.changeOperand(jumpPos1, curPos) - } - - case *ast.ForStmt: - return c.compileForStmt(node) - - case *ast.ForInStmt: - return c.compileForInStmt(node) - - case *ast.BranchStmt: - if node.Token == token.Break { - curLoop := c.currentLoop() - if curLoop == nil { - return c.errorf(node, "break not allowed outside loop") - } - 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 c.errorf(node, "continue not allowed outside loop") - } - pos := c.emit(node, OpJump, 0) - curLoop.Continues = append(curLoop.Continues, pos) - } else { - panic(fmt.Errorf("invalid branch statement: %s", node.Token.String())) - } - - case *ast.BlockStmt: - if len(node.Stmts) == 0 { - return nil - } - - c.symbolTable = c.symbolTable.Fork(true) - defer func() { - c.symbolTable = c.symbolTable.Parent(false) - }() - - for _, stmt := range node.Stmts { - if err := c.Compile(stmt); err != nil { - return err - } - } - - case *ast.AssignStmt: - 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 c.errorf(node, "unresolved reference '%s'", node.Name) - } - - switch symbol.Scope { - case ScopeGlobal: - c.emit(node, OpGetGlobal, symbol.Index) - case ScopeLocal: - c.emit(node, OpGetLocal, symbol.Index) - case ScopeBuiltin: - c.emit(node, OpGetBuiltin, symbol.Index) - case ScopeFree: - c.emit(node, OpGetFree, symbol.Index) - } - - case *ast.ArrayLit: - for _, elem := range node.Elements { - if err := c.Compile(elem); err != nil { - return err - } - } - - c.emit(node, OpArray, len(node.Elements)) - - case *ast.MapLit: - for _, elt := range node.Elements { - // key - if len(elt.Key) > tengo.MaxStringLen { - return c.error(node, objects.ErrStringLimit) - } - c.emit(node, OpConstant, c.addConstant(&objects.String{Value: elt.Key})) - - // value - if err := c.Compile(elt.Value); err != nil { - return err - } - } - - c.emit(node, OpMap, len(node.Elements)*2) - - case *ast.SelectorExpr: // selector on RHS side - if err := c.Compile(node.Expr); err != nil { - return err - } - - if err := c.Compile(node.Sel); err != nil { - return err - } - - c.emit(node, OpIndex) - - case *ast.IndexExpr: - if err := c.Compile(node.Expr); err != nil { - return err - } - - if err := c.Compile(node.Index); err != nil { - return err - } - - c.emit(node, OpIndex) - - case *ast.SliceExpr: - if err := c.Compile(node.Expr); err != nil { - return err - } - - if node.Low != nil { - if err := c.Compile(node.Low); err != nil { - return err - } - } else { - c.emit(node, OpNull) - } - - if node.High != nil { - if err := c.Compile(node.High); err != nil { - return err - } - } else { - c.emit(node, OpNull) - } - - c.emit(node, OpSliceIndex) - - case *ast.FuncLit: - c.enterScope() - - for _, p := range node.Type.Params.List { - s := c.symbolTable.Define(p.Name) - - // function arguments is not assigned directly. - s.LocalAssigned = true - } - - if err := c.Compile(node.Body); err != nil { - return err - } - - // code optimization - c.optimizeFunc(node) - - freeSymbols := c.symbolTable.FreeSymbols() - numLocals := c.symbolTable.MaxSymbols() - instructions, sourceMap := c.leaveScope() - - for _, s := range freeSymbols { - switch s.Scope { - case ScopeLocal: - if !s.LocalAssigned { - // Here, the closure is capturing a local variable that's not yet assigned its value. - // One example is a local recursive function: - // - // func() { - // foo := func(x) { - // // .. - // return foo(x-1) - // } - // } - // - // which translate into - // - // 0000 GETL 0 - // 0002 CLOSURE ? 1 - // 0006 DEFL 0 - // - // . So the local variable (0) is being captured before it's assigned the value. - // - // Solution is to transform the code into something like this: - // - // func() { - // foo := undefined - // foo = func(x) { - // // .. - // return foo(x-1) - // } - // } - // - // that is equivalent to - // - // 0000 NULL - // 0001 DEFL 0 - // 0003 GETL 0 - // 0005 CLOSURE ? 1 - // 0009 SETL 0 - // - - c.emit(node, OpNull) - c.emit(node, OpDefineLocal, s.Index) - - s.LocalAssigned = true - } - - c.emit(node, OpGetLocalPtr, s.Index) - case ScopeFree: - c.emit(node, OpGetFreePtr, s.Index) - } - } - - compiledFunction := &objects.CompiledFunction{ - Instructions: instructions, - NumLocals: numLocals, - NumParameters: len(node.Type.Params.List), - VarArgs: node.Type.Params.VarArgs, - SourceMap: sourceMap, - } - - if len(freeSymbols) > 0 { - c.emit(node, OpClosure, c.addConstant(compiledFunction), len(freeSymbols)) - } else { - c.emit(node, OpConstant, c.addConstant(compiledFunction)) - } - - case *ast.ReturnStmt: - if c.symbolTable.Parent(true) == nil { - // outside the function - return c.errorf(node, "return not allowed outside function") - } - - if node.Result == nil { - c.emit(node, OpReturn, 0) - } else { - if err := c.Compile(node.Result); err != nil { - return err - } - - c.emit(node, OpReturn, 1) - } - - case *ast.CallExpr: - if err := c.Compile(node.Func); err != nil { - return err - } - - for _, arg := range node.Args { - if err := c.Compile(arg); err != nil { - return err - } - } - - c.emit(node, OpCall, len(node.Args)) - - case *ast.ImportExpr: - if node.ModuleName == "" { - return c.errorf(node, "empty module name") - } - - if mod := c.modules.Get(node.ModuleName); mod != nil { - v, err := mod.Import(node.ModuleName) - if err != nil { - return err - } - - switch v := v.(type) { - case []byte: // module written in Tengo - compiled, err := c.compileModule(node, node.ModuleName, node.ModuleName, v) - if err != nil { - return err - } - c.emit(node, OpConstant, c.addConstant(compiled)) - c.emit(node, OpCall, 0) - case objects.Object: // builtin module - c.emit(node, OpConstant, c.addConstant(v)) - default: - panic(fmt.Errorf("invalid import value type: %T", v)) - } - } else if c.allowFileImport { - moduleName := node.ModuleName - if !strings.HasSuffix(moduleName, ".tengo") { - moduleName += ".tengo" - } - - modulePath, err := filepath.Abs(moduleName) - if err != nil { - return c.errorf(node, "module file path error: %s", err.Error()) - } - - if err := c.checkCyclicImports(node, modulePath); err != nil { - return err - } - - moduleSrc, err := ioutil.ReadFile(moduleName) - if err != nil { - return c.errorf(node, "module file read error: %s", err.Error()) - } - - compiled, err := c.compileModule(node, moduleName, modulePath, moduleSrc) - if err != nil { - return err - } - c.emit(node, OpConstant, c.addConstant(compiled)) - c.emit(node, OpCall, 0) - } else { - return c.errorf(node, "module '%s' not found", node.ModuleName) - } - - case *ast.ExportStmt: - // export statement must be in top-level scope - if c.scopeIndex != 0 { - return c.errorf(node, "export not allowed inside function") - } - - // export statement is simply ignore when compiling non-module code - if c.parent == nil { - break - } - - if err := c.Compile(node.Result); err != nil { - return err - } - - c.emit(node, OpImmutable) - c.emit(node, OpReturn, 1) - - case *ast.ErrorExpr: - if err := c.Compile(node.Expr); err != nil { - return err - } - - c.emit(node, OpError) - - case *ast.ImmutableExpr: - if err := c.Compile(node.Expr); err != nil { - return err - } - - c.emit(node, OpImmutable) - - case *ast.CondExpr: - if err := c.Compile(node.Cond); err != nil { - return err - } - - // first jump placeholder - jumpPos1 := c.emit(node, OpJumpFalsy, 0) - - if err := c.Compile(node.True); err != nil { - return err - } - - // second jump placeholder - jumpPos2 := c.emit(node, OpJump, 0) - - // update first jump offset - curPos := len(c.currentInstructions()) - c.changeOperand(jumpPos1, curPos) - - if err := c.Compile(node.False); err != nil { - return err - } - - // update second jump offset - curPos = len(c.currentInstructions()) - c.changeOperand(jumpPos2, curPos) - } - - return nil -} - -// Bytecode returns a compiled bytecode. -func (c *Compiler) Bytecode() *Bytecode { - return &Bytecode{ - FileSet: c.file.Set(), - MainFunction: &objects.CompiledFunction{ - Instructions: c.currentInstructions(), - SourceMap: c.currentSourceMap(), - }, - Constants: c.constants, - } -} - -// EnableFileImport enables or disables module loading from local files. -// Local file modules are disabled by default. -func (c *Compiler) EnableFileImport(enable bool) { - c.allowFileImport = enable -} - -func (c *Compiler) fork(file *source.File, modulePath string, symbolTable *SymbolTable) *Compiler { - child := NewCompiler(file, symbolTable, nil, c.modules, c.trace) - child.modulePath = modulePath // module file path - child.parent = c // parent to set to current compiler - - return child -} - -func (c *Compiler) error(node ast.Node, err error) error { - return &Error{ - fileSet: c.file.Set(), - node: node, - error: err, - } -} - -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 - return c.parent.addConstant(o) - } - - c.constants = append(c.constants, o) - - if c.trace != nil { - c.printTrace(fmt.Sprintf("CONST %04d %s", len(c.constants)-1, o)) - } - - return len(c.constants) - 1 -} - -func (c *Compiler) addInstruction(b []byte) int { - posNewIns := len(c.currentInstructions()) - - c.scopes[c.scopeIndex].instructions = append(c.currentInstructions(), b...) - - return posNewIns -} - -func (c *Compiler) replaceInstruction(pos int, inst []byte) { - copy(c.currentInstructions()[pos:], inst) - - if c.trace != nil { - c.printTrace(fmt.Sprintf("REPLC %s", - FormatInstructions(c.scopes[c.scopeIndex].instructions[pos:], pos)[0])) - } -} - -func (c *Compiler) changeOperand(opPos int, operand ...int) { - op := Opcode(c.currentInstructions()[opPos]) - inst := MakeInstruction(op, operand...) - - c.replaceInstruction(opPos, inst) -} - -// optimizeFunc performs some code-level optimization for the current function instructions -// it removes unreachable (dead code) instructions and adds "returns" instruction if needed. -func (c *Compiler) optimizeFunc(node ast.Node) { - // any instructions between RETURN and the function end - // or instructions between RETURN and jump target position - // are considered as unreachable. - - // pass 1. identify all jump destinations - dsts := make(map[int]bool) - iterateInstructions(c.scopes[c.scopeIndex].instructions, func(pos int, opcode Opcode, operands []int) bool { - switch opcode { - case OpJump, OpJumpFalsy, OpAndJump, OpOrJump: - dsts[operands[0]] = true - } - - return true - }) - - var newInsts []byte - - // pass 2. eliminate dead code - posMap := make(map[int]int) // old position to new position - var dstIdx int - var deadCode bool - iterateInstructions(c.scopes[c.scopeIndex].instructions, func(pos int, opcode Opcode, operands []int) bool { - switch { - case opcode == OpReturn: - if deadCode { - return true - } - deadCode = true - case dsts[pos]: - dstIdx++ - deadCode = false - case deadCode: - return true - } - - posMap[pos] = len(newInsts) - newInsts = append(newInsts, MakeInstruction(opcode, operands...)...) - return true - }) - - // pass 3. update jump positions - var lastOp Opcode - var appendReturn bool - endPos := len(c.scopes[c.scopeIndex].instructions) - iterateInstructions(newInsts, func(pos int, opcode Opcode, operands []int) bool { - switch opcode { - case OpJump, OpJumpFalsy, OpAndJump, OpOrJump: - newDst, ok := posMap[operands[0]] - if ok { - copy(newInsts[pos:], MakeInstruction(opcode, newDst)) - } else if endPos == operands[0] { - // there's a jump instruction that jumps to the end of function - // compiler should append "return". - appendReturn = true - } else { - panic(fmt.Errorf("invalid jump position: %d", newDst)) - } - } - lastOp = opcode - return true - }) - if lastOp != OpReturn { - appendReturn = true - } - - // pass 4. update source map - newSourceMap := make(map[int]source.Pos) - for pos, srcPos := range c.scopes[c.scopeIndex].sourceMap { - newPos, ok := posMap[pos] - if ok { - newSourceMap[newPos] = srcPos - } - } - - c.scopes[c.scopeIndex].instructions = newInsts - c.scopes[c.scopeIndex].sourceMap = newSourceMap - - // append "return" - if appendReturn { - c.emit(node, OpReturn, 0) - } -} - -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 - - if c.trace != nil { - c.printTrace(fmt.Sprintf("EMIT %s", - FormatInstructions(c.scopes[c.scopeIndex].instructions[pos:], pos)[0])) - } - - return pos -} - -func (c *Compiler) printTrace(a ...interface{}) { - const ( - dots = ". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . " - n = len(dots) - ) - - i := 2 * c.indent - for i > n { - _, _ = fmt.Fprint(c.trace, dots) - i -= n - } - _, _ = fmt.Fprint(c.trace, dots[0:i]) - _, _ = fmt.Fprintln(c.trace, a...) -} - -func trace(c *Compiler, msg string) *Compiler { - c.printTrace(msg, "{") - c.indent++ - - return c -} - -func un(c *Compiler) { - c.indent-- - c.printTrace("}") -} diff --git a/vendor/github.com/d5/tengo/compiler/compiler_assign.go b/vendor/github.com/d5/tengo/compiler/compiler_assign.go deleted file mode 100644 index 59296f6f..00000000 --- a/vendor/github.com/d5/tengo/compiler/compiler_assign.go +++ /dev/null @@ -1,133 +0,0 @@ -package compiler - -import ( - "fmt" - - "github.com/d5/tengo/compiler/ast" - "github.com/d5/tengo/compiler/token" -) - -func (c *Compiler) compileAssign(node ast.Node, lhs, rhs []ast.Expr, op token.Token) error { - numLHS, numRHS := len(lhs), len(rhs) - if numLHS > 1 || numRHS > 1 { - return c.errorf(node, "tuple assignment not allowed") - } - - // resolve and compile left-hand side - ident, selectors := resolveAssignLHS(lhs[0]) - numSel := len(selectors) - - if op == token.Define && numSel > 0 { - // using selector on new variable does not make sense - 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 c.errorf(node, "'%s' redeclared in this block", ident) - } - - symbol = c.symbolTable.Define(ident) - } else { - if !exists { - return c.errorf(node, "unresolved reference '%s'", ident) - } - } - - // +=, -=, *=, /= - if op != token.Assign && op != token.Define { - if err := c.Compile(lhs[0]); err != nil { - return err - } - } - - // compile RHSs - for _, expr := range rhs { - if err := c.Compile(expr); err != nil { - return err - } - } - - switch op { - case token.AddAssign: - c.emit(node, OpBinaryOp, int(token.Add)) - case token.SubAssign: - c.emit(node, OpBinaryOp, int(token.Sub)) - case token.MulAssign: - c.emit(node, OpBinaryOp, int(token.Mul)) - case token.QuoAssign: - c.emit(node, OpBinaryOp, int(token.Quo)) - case token.RemAssign: - c.emit(node, OpBinaryOp, int(token.Rem)) - case token.AndAssign: - c.emit(node, OpBinaryOp, int(token.And)) - case token.OrAssign: - c.emit(node, OpBinaryOp, int(token.Or)) - case token.AndNotAssign: - c.emit(node, OpBinaryOp, int(token.AndNot)) - case token.XorAssign: - c.emit(node, OpBinaryOp, int(token.Xor)) - case token.ShlAssign: - c.emit(node, OpBinaryOp, int(token.Shl)) - case token.ShrAssign: - c.emit(node, OpBinaryOp, int(token.Shr)) - } - - // compile selector expressions (right to left) - for i := numSel - 1; i >= 0; i-- { - if err := c.Compile(selectors[i]); err != nil { - return err - } - } - - switch symbol.Scope { - case ScopeGlobal: - if numSel > 0 { - c.emit(node, OpSetSelGlobal, symbol.Index, numSel) - } else { - c.emit(node, OpSetGlobal, symbol.Index) - } - case ScopeLocal: - if numSel > 0 { - c.emit(node, OpSetSelLocal, symbol.Index, numSel) - } else { - if op == token.Define && !symbol.LocalAssigned { - c.emit(node, OpDefineLocal, symbol.Index) - } else { - c.emit(node, OpSetLocal, symbol.Index) - } - } - - // mark the symbol as local-assigned - symbol.LocalAssigned = true - case ScopeFree: - if numSel > 0 { - c.emit(node, OpSetSelFree, symbol.Index, numSel) - } else { - c.emit(node, OpSetFree, symbol.Index) - } - default: - panic(fmt.Errorf("invalid assignment variable scope: %s", symbol.Scope)) - } - - return nil -} - -func resolveAssignLHS(expr ast.Expr) (name string, selectors []ast.Expr) { - switch term := expr.(type) { - case *ast.SelectorExpr: - name, selectors = resolveAssignLHS(term.Expr) - selectors = append(selectors, term.Sel) - return - - case *ast.IndexExpr: - name, selectors = resolveAssignLHS(term.Expr) - selectors = append(selectors, term.Index) - - case *ast.Ident: - name = term.Name - } - - return -} diff --git a/vendor/github.com/d5/tengo/compiler/compiler_for.go b/vendor/github.com/d5/tengo/compiler/compiler_for.go deleted file mode 100644 index e7b7b5f1..00000000 --- a/vendor/github.com/d5/tengo/compiler/compiler_for.go +++ /dev/null @@ -1,181 +0,0 @@ -package compiler - -import ( - "github.com/d5/tengo/compiler/ast" -) - -func (c *Compiler) compileForStmt(stmt *ast.ForStmt) error { - c.symbolTable = c.symbolTable.Fork(true) - defer func() { - c.symbolTable = c.symbolTable.Parent(false) - }() - - // init statement - if stmt.Init != nil { - if err := c.Compile(stmt.Init); err != nil { - return err - } - } - - // pre-condition position - preCondPos := len(c.currentInstructions()) - - // condition expression - postCondPos := -1 - if stmt.Cond != nil { - if err := c.Compile(stmt.Cond); err != nil { - return err - } - // condition jump position - postCondPos = c.emit(stmt, OpJumpFalsy, 0) - } - - // enter loop - loop := c.enterLoop() - - // body statement - if err := c.Compile(stmt.Body); err != nil { - c.leaveLoop() - return err - } - - c.leaveLoop() - - // post-body position - postBodyPos := len(c.currentInstructions()) - - // post statement - if stmt.Post != nil { - if err := c.Compile(stmt.Post); err != nil { - return err - } - } - - // back to condition - c.emit(stmt, OpJump, preCondPos) - - // post-statement position - postStmtPos := len(c.currentInstructions()) - if postCondPos >= 0 { - c.changeOperand(postCondPos, postStmtPos) - } - - // update all break/continue jump positions - for _, pos := range loop.Breaks { - c.changeOperand(pos, postStmtPos) - } - for _, pos := range loop.Continues { - c.changeOperand(pos, postBodyPos) - } - - return nil -} - -func (c *Compiler) compileForInStmt(stmt *ast.ForInStmt) error { - c.symbolTable = c.symbolTable.Fork(true) - defer func() { - c.symbolTable = c.symbolTable.Parent(false) - }() - - // for-in statement is compiled like following: - // - // for :it := iterator(iterable); :it.next(); { - // k, v := :it.get() // DEFINE operator - // - // ... body ... - // } - // - // ":it" is a local variable but will be conflict with other user variables - // because character ":" is not allowed. - - // init - // :it = iterator(iterable) - itSymbol := c.symbolTable.Define(":it") - if err := c.Compile(stmt.Iterable); err != nil { - return err - } - c.emit(stmt, OpIteratorInit) - if itSymbol.Scope == ScopeGlobal { - c.emit(stmt, OpSetGlobal, itSymbol.Index) - } else { - c.emit(stmt, OpDefineLocal, itSymbol.Index) - } - - // pre-condition position - preCondPos := len(c.currentInstructions()) - - // condition - // :it.HasMore() - if itSymbol.Scope == ScopeGlobal { - c.emit(stmt, OpGetGlobal, itSymbol.Index) - } else { - c.emit(stmt, OpGetLocal, itSymbol.Index) - } - c.emit(stmt, OpIteratorNext) - - // condition jump position - postCondPos := c.emit(stmt, OpJumpFalsy, 0) - - // enter loop - loop := c.enterLoop() - - // assign key variable - if stmt.Key.Name != "_" { - keySymbol := c.symbolTable.Define(stmt.Key.Name) - if itSymbol.Scope == ScopeGlobal { - c.emit(stmt, OpGetGlobal, itSymbol.Index) - } else { - c.emit(stmt, OpGetLocal, itSymbol.Index) - } - c.emit(stmt, OpIteratorKey) - if keySymbol.Scope == ScopeGlobal { - c.emit(stmt, OpSetGlobal, keySymbol.Index) - } else { - c.emit(stmt, OpDefineLocal, keySymbol.Index) - } - } - - // assign value variable - if stmt.Value.Name != "_" { - valueSymbol := c.symbolTable.Define(stmt.Value.Name) - if itSymbol.Scope == ScopeGlobal { - c.emit(stmt, OpGetGlobal, itSymbol.Index) - } else { - c.emit(stmt, OpGetLocal, itSymbol.Index) - } - c.emit(stmt, OpIteratorValue) - if valueSymbol.Scope == ScopeGlobal { - c.emit(stmt, OpSetGlobal, valueSymbol.Index) - } else { - c.emit(stmt, OpDefineLocal, valueSymbol.Index) - } - } - - // body statement - if err := c.Compile(stmt.Body); err != nil { - c.leaveLoop() - return err - } - - c.leaveLoop() - - // post-body position - postBodyPos := len(c.currentInstructions()) - - // back to condition - c.emit(stmt, OpJump, preCondPos) - - // post-statement position - postStmtPos := len(c.currentInstructions()) - c.changeOperand(postCondPos, postStmtPos) - - // update all break/continue jump positions - for _, pos := range loop.Breaks { - c.changeOperand(pos, postStmtPos) - } - for _, pos := range loop.Continues { - c.changeOperand(pos, postBodyPos) - } - - return nil -} diff --git a/vendor/github.com/d5/tengo/compiler/compiler_logical.go b/vendor/github.com/d5/tengo/compiler/compiler_logical.go deleted file mode 100644 index 68c96759..00000000 --- a/vendor/github.com/d5/tengo/compiler/compiler_logical.go +++ /dev/null @@ -1,30 +0,0 @@ -package compiler - -import ( - "github.com/d5/tengo/compiler/ast" - "github.com/d5/tengo/compiler/token" -) - -func (c *Compiler) compileLogical(node *ast.BinaryExpr) error { - // left side term - if err := c.Compile(node.LHS); err != nil { - return err - } - - // jump position - var jumpPos int - if node.Token == token.LAnd { - jumpPos = c.emit(node, OpAndJump, 0) - } else { - jumpPos = c.emit(node, OpOrJump, 0) - } - - // right side term - if err := c.Compile(node.RHS); err != nil { - return err - } - - c.changeOperand(jumpPos, len(c.currentInstructions())) - - return nil -} diff --git a/vendor/github.com/d5/tengo/compiler/compiler_loops.go b/vendor/github.com/d5/tengo/compiler/compiler_loops.go deleted file mode 100644 index 0659ce73..00000000 --- a/vendor/github.com/d5/tengo/compiler/compiler_loops.go +++ /dev/null @@ -1,31 +0,0 @@ -package compiler - -func (c *Compiler) enterLoop() *Loop { - loop := &Loop{} - - c.loops = append(c.loops, loop) - c.loopIndex++ - - if c.trace != nil { - c.printTrace("LOOPE", c.loopIndex) - } - - return loop -} - -func (c *Compiler) leaveLoop() { - if c.trace != nil { - c.printTrace("LOOPL", c.loopIndex) - } - - c.loops = c.loops[:len(c.loops)-1] - c.loopIndex-- -} - -func (c *Compiler) currentLoop() *Loop { - if c.loopIndex >= 0 { - return c.loops[c.loopIndex] - } - - return nil -} diff --git a/vendor/github.com/d5/tengo/compiler/compiler_module.go b/vendor/github.com/d5/tengo/compiler/compiler_module.go deleted file mode 100644 index 8a3671ce..00000000 --- a/vendor/github.com/d5/tengo/compiler/compiler_module.go +++ /dev/null @@ -1,79 +0,0 @@ -package compiler - -import ( - "github.com/d5/tengo/compiler/ast" - "github.com/d5/tengo/compiler/parser" - "github.com/d5/tengo/objects" -) - -func (c *Compiler) checkCyclicImports(node ast.Node, modulePath string) error { - if c.modulePath == modulePath { - return c.errorf(node, "cyclic module import: %s", modulePath) - } else if c.parent != nil { - return c.parent.checkCyclicImports(node, modulePath) - } - - return nil -} - -func (c *Compiler) compileModule(node ast.Node, moduleName, modulePath string, src []byte) (*objects.CompiledFunction, error) { - if err := c.checkCyclicImports(node, modulePath); err != nil { - return nil, err - } - - compiledModule, exists := c.loadCompiledModule(modulePath) - if exists { - return compiledModule, 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 - } - - symbolTable := NewSymbolTable() - - // inherit builtin functions - for _, sym := range c.symbolTable.BuiltinSymbols() { - symbolTable.DefineBuiltin(sym.Index, sym.Name) - } - - // no global scope for the module - symbolTable = symbolTable.Fork(false) - - // compile module - moduleCompiler := c.fork(modFile, modulePath, symbolTable) - if err := moduleCompiler.Compile(file); err != nil { - return nil, err - } - - // code optimization - moduleCompiler.optimizeFunc(node) - - compiledFunc := moduleCompiler.Bytecode().MainFunction - compiledFunc.NumLocals = symbolTable.MaxSymbols() - - c.storeCompiledModule(modulePath, compiledFunc) - - return compiledFunc, nil -} - -func (c *Compiler) loadCompiledModule(modulePath string) (mod *objects.CompiledFunction, ok bool) { - if c.parent != nil { - return c.parent.loadCompiledModule(modulePath) - } - - mod, ok = c.compiledModules[modulePath] - - return -} - -func (c *Compiler) storeCompiledModule(modulePath string, module *objects.CompiledFunction) { - if c.parent != nil { - c.parent.storeCompiledModule(modulePath, module) - } - - c.compiledModules[modulePath] = module -} diff --git a/vendor/github.com/d5/tengo/compiler/compiler_scopes.go b/vendor/github.com/d5/tengo/compiler/compiler_scopes.go deleted file mode 100644 index b63f915a..00000000 --- a/vendor/github.com/d5/tengo/compiler/compiler_scopes.go +++ /dev/null @@ -1,43 +0,0 @@ -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{ - symbolInit: make(map[string]bool), - sourceMap: make(map[int]source.Pos), - } - - c.scopes = append(c.scopes, scope) - c.scopeIndex++ - - c.symbolTable = c.symbolTable.Fork(false) - - if c.trace != nil { - c.printTrace("SCOPE", c.scopeIndex) - } -} - -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-- - - c.symbolTable = c.symbolTable.Parent(true) - - if c.trace != nil { - c.printTrace("SCOPL", c.scopeIndex) - } - - return -} diff --git a/vendor/github.com/d5/tengo/compiler/emitted_instruction.go b/vendor/github.com/d5/tengo/compiler/emitted_instruction.go deleted file mode 100644 index 8572fb0a..00000000 --- a/vendor/github.com/d5/tengo/compiler/emitted_instruction.go +++ /dev/null @@ -1,8 +0,0 @@ -package compiler - -// EmittedInstruction represents an opcode -// with its emitted position. -type EmittedInstruction struct { - Opcode Opcode - Position int -} diff --git a/vendor/github.com/d5/tengo/compiler/error.go b/vendor/github.com/d5/tengo/compiler/error.go deleted file mode 100644 index 6ac33d42..00000000 --- a/vendor/github.com/d5/tengo/compiler/error.go +++ /dev/null @@ -1,20 +0,0 @@ -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("Compile Error: %s\n\tat %s", e.error.Error(), filePos) -} diff --git a/vendor/github.com/d5/tengo/compiler/instructions.go b/vendor/github.com/d5/tengo/compiler/instructions.go deleted file mode 100644 index 14dde1d8..00000000 --- a/vendor/github.com/d5/tengo/compiler/instructions.go +++ /dev/null @@ -1,72 +0,0 @@ -package compiler - -import ( - "fmt" -) - -// MakeInstruction returns a bytecode for an opcode and the operands. -func MakeInstruction(opcode Opcode, operands ...int) []byte { - numOperands := OpcodeOperands[opcode] - - totalLen := 1 - for _, w := range numOperands { - totalLen += w - } - - instruction := make([]byte, totalLen) - instruction[0] = byte(opcode) - - offset := 1 - for i, o := range operands { - width := numOperands[i] - switch width { - case 1: - instruction[offset] = byte(o) - case 2: - n := uint16(o) - instruction[offset] = byte(n >> 8) - instruction[offset+1] = byte(n) - } - offset += width - } - - return instruction -} - -// FormatInstructions returns string representation of -// bytecode instructions. -func FormatInstructions(b []byte, posOffset int) []string { - var out []string - - i := 0 - for i < len(b) { - numOperands := OpcodeOperands[Opcode(b[i])] - operands, read := ReadOperands(numOperands, b[i+1:]) - - switch len(numOperands) { - case 0: - out = append(out, fmt.Sprintf("%04d %-7s", posOffset+i, OpcodeNames[Opcode(b[i])])) - case 1: - out = append(out, fmt.Sprintf("%04d %-7s %-5d", posOffset+i, OpcodeNames[Opcode(b[i])], operands[0])) - case 2: - out = append(out, fmt.Sprintf("%04d %-7s %-5d %-5d", posOffset+i, OpcodeNames[Opcode(b[i])], operands[0], operands[1])) - } - - i += 1 + read - } - - return out -} - -func iterateInstructions(b []byte, fn func(pos int, opcode Opcode, operands []int) bool) { - for i := 0; i < len(b); i++ { - numOperands := OpcodeOperands[Opcode(b[i])] - operands, read := ReadOperands(numOperands, b[i+1:]) - - if !fn(i, b[i], operands) { - break - } - - i += read - } -} diff --git a/vendor/github.com/d5/tengo/compiler/loop.go b/vendor/github.com/d5/tengo/compiler/loop.go deleted file mode 100644 index e27cb096..00000000 --- a/vendor/github.com/d5/tengo/compiler/loop.go +++ /dev/null @@ -1,8 +0,0 @@ -package compiler - -// Loop represents a loop construct that -// the compiler uses to track the current loop. -type Loop struct { - Continues []int - Breaks []int -} diff --git a/vendor/github.com/d5/tengo/compiler/module_loader.go b/vendor/github.com/d5/tengo/compiler/module_loader.go deleted file mode 100644 index b050474d..00000000 --- a/vendor/github.com/d5/tengo/compiler/module_loader.go +++ /dev/null @@ -1,4 +0,0 @@ -package compiler - -// ModuleLoader should take a module name and return the module data. -type ModuleLoader func(moduleName string) ([]byte, error) diff --git a/vendor/github.com/d5/tengo/compiler/parser/error.go b/vendor/github.com/d5/tengo/compiler/parser/error.go deleted file mode 100644 index 80544fbd..00000000 --- a/vendor/github.com/d5/tengo/compiler/parser/error.go +++ /dev/null @@ -1,21 +0,0 @@ -package parser - -import ( - "fmt" - - "github.com/d5/tengo/compiler/source" -) - -// Error represents a parser error. -type Error struct { - Pos source.FilePos - Msg string -} - -func (e Error) Error() string { - if e.Pos.Filename != "" || e.Pos.IsValid() { - return fmt.Sprintf("Parse Error: %s\n\tat %s", e.Msg, e.Pos) - } - - return fmt.Sprintf("Parse Error: %s", e.Msg) -} diff --git a/vendor/github.com/d5/tengo/compiler/parser/error_list.go b/vendor/github.com/d5/tengo/compiler/parser/error_list.go deleted file mode 100644 index 599eaf7d..00000000 --- a/vendor/github.com/d5/tengo/compiler/parser/error_list.go +++ /dev/null @@ -1,68 +0,0 @@ -package parser - -import ( - "fmt" - "sort" - - "github.com/d5/tengo/compiler/source" -) - -// ErrorList is a collection of parser errors. -type ErrorList []*Error - -// Add adds a new parser error to the collection. -func (p *ErrorList) Add(pos source.FilePos, msg string) { - *p = append(*p, &Error{pos, msg}) -} - -// Len returns the number of elements in the collection. -func (p ErrorList) Len() int { - return len(p) -} - -func (p ErrorList) Swap(i, j int) { - p[i], p[j] = p[j], p[i] -} - -func (p ErrorList) Less(i, j int) bool { - e := &p[i].Pos - f := &p[j].Pos - - if e.Filename != f.Filename { - return e.Filename < f.Filename - } - - if e.Line != f.Line { - return e.Line < f.Line - } - - if e.Column != f.Column { - return e.Column < f.Column - } - - return p[i].Msg < p[j].Msg -} - -// Sort sorts the collection. -func (p ErrorList) Sort() { - sort.Sort(p) -} - -func (p ErrorList) Error() string { - switch len(p) { - case 0: - return "no errors" - case 1: - return p[0].Error() - } - return fmt.Sprintf("%s (and %d more errors)", p[0], len(p)-1) -} - -// Err returns an error. -func (p ErrorList) Err() error { - if len(p) == 0 { - return nil - } - - return p -} diff --git a/vendor/github.com/d5/tengo/compiler/parser/parse_source.go b/vendor/github.com/d5/tengo/compiler/parser/parse_source.go deleted file mode 100644 index 5c71436d..00000000 --- a/vendor/github.com/d5/tengo/compiler/parser/parse_source.go +++ /dev/null @@ -1,17 +0,0 @@ -package parser - -import ( - "io" - - "github.com/d5/tengo/compiler/ast" - "github.com/d5/tengo/compiler/source" -) - -// ParseSource parses source code 'src' and builds an AST. -func ParseSource(filename string, src []byte, trace io.Writer) (res *ast.File, err error) { - fileSet := source.NewFileSet() - file := fileSet.AddFile(filename, -1, len(src)) - - p := NewParser(file, src, trace) - return p.ParseFile() -} diff --git a/vendor/github.com/d5/tengo/compiler/parser/sync.go b/vendor/github.com/d5/tengo/compiler/parser/sync.go deleted file mode 100644 index e68d623a..00000000 --- a/vendor/github.com/d5/tengo/compiler/parser/sync.go +++ /dev/null @@ -1,12 +0,0 @@ -package parser - -import "github.com/d5/tengo/compiler/token" - -var stmtStart = map[token.Token]bool{ - token.Break: true, - token.Continue: true, - token.For: true, - token.If: true, - token.Return: true, - token.Export: true, -} diff --git a/vendor/github.com/d5/tengo/compiler/scanner/error_handler.go b/vendor/github.com/d5/tengo/compiler/scanner/error_handler.go deleted file mode 100644 index 379f0196..00000000 --- a/vendor/github.com/d5/tengo/compiler/scanner/error_handler.go +++ /dev/null @@ -1,6 +0,0 @@ -package scanner - -import "github.com/d5/tengo/compiler/source" - -// ErrorHandler is an error handler for the scanner. -type ErrorHandler func(pos source.FilePos, msg string) diff --git a/vendor/github.com/d5/tengo/compiler/scanner/mode.go b/vendor/github.com/d5/tengo/compiler/scanner/mode.go deleted file mode 100644 index f67ceaf8..00000000 --- a/vendor/github.com/d5/tengo/compiler/scanner/mode.go +++ /dev/null @@ -1,10 +0,0 @@ -package scanner - -// Mode represents a scanner mode. -type Mode int - -// List of scanner modes. -const ( - ScanComments Mode = 1 << iota - DontInsertSemis -) diff --git a/vendor/github.com/d5/tengo/compiler/source/file.go b/vendor/github.com/d5/tengo/compiler/source/file.go deleted file mode 100644 index 9e51c9a4..00000000 --- a/vendor/github.com/d5/tengo/compiler/source/file.go +++ /dev/null @@ -1,110 +0,0 @@ -package source - -// File represents a source file. -type File struct { - // 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 -} - -// Set returns FileSet. -func (f *File) Set() *FileSet { - return f.set -} - -// LineCount returns the current number of lines. -func (f *File) LineCount() int { - return len(f.Lines) -} - -// AddLine adds a new line. -func (f *File) AddLine(offset int) { - if i := len(f.Lines); (i == 0 || f.Lines[i-1] < offset) && offset < f.Size { - f.Lines = append(f.Lines, offset) - } -} - -// LineStart returns the position of the first character in the line. -func (f *File) LineStart(line int) Pos { - if line < 1 { - panic("illegal line number (line numbering starts at 1)") - } - - if line > len(f.Lines) { - panic("illegal line number") - } - - 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 { - panic("illegal file 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 { - panic("illegal Pos value") - } - - 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 { - panic("illegal Pos value") - } - - pos = f.position(p) - } - - return -} - -func (f *File) position(p Pos) (pos FilePos) { - offset := int(p) - f.Base - pos.Offset = offset - pos.Filename, pos.Line, pos.Column = f.unpack(offset) - - return -} - -func (f *File) unpack(offset int) (filename string, line, column int) { - filename = f.Name - if i := searchInts(f.Lines, offset); i >= 0 { - line, column = i+1, offset-f.Lines[i]+1 - } - - return -} - -func searchInts(a []int, x int) int { - // This function body is a manually inlined version of: - // return sort.Search(len(a), func(i int) bool { return a[i] > x }) - 1 - i, j := 0, len(a) - for i < j { - h := i + (j-i)/2 // avoid overflow when computing h - // i ≤ h < j - if a[h] <= x { - i = h + 1 - } else { - j = h - } - } - - return i - 1 -} diff --git a/vendor/github.com/d5/tengo/compiler/source/file_pos.go b/vendor/github.com/d5/tengo/compiler/source/file_pos.go deleted file mode 100644 index 4055fe6d..00000000 --- a/vendor/github.com/d5/tengo/compiler/source/file_pos.go +++ /dev/null @@ -1,47 +0,0 @@ -package source - -import "fmt" - -// FilePos represents a position information in the file. -type FilePos struct { - Filename string // filename, if any - Offset int // offset, starting at 0 - Line int // line number, starting at 1 - Column int // column number, starting at 1 (byte count) -} - -// IsValid returns true if the position is valid. -func (p FilePos) IsValid() bool { - return p.Line > 0 -} - -// String returns a string in one of several forms: -// -// file:line:column valid position with file name -// file:line valid position with file name but no column (column == 0) -// line:column valid position without file name -// line valid position without file name and no column (column == 0) -// file invalid position with file name -// - invalid position without file name -// -func (p FilePos) String() string { - s := p.Filename - - if p.IsValid() { - if s != "" { - s += ":" - } - - s += fmt.Sprintf("%d", p.Line) - - if p.Column != 0 { - s += fmt.Sprintf(":%d", p.Column) - } - } - - if s == "" { - s = "-" - } - - return s -} diff --git a/vendor/github.com/d5/tengo/compiler/source/file_set.go b/vendor/github.com/d5/tengo/compiler/source/file_set.go deleted file mode 100644 index da342364..00000000 --- a/vendor/github.com/d5/tengo/compiler/source/file_set.go +++ /dev/null @@ -1,96 +0,0 @@ -package source - -import ( - "sort" -) - -// FileSet represents a set of source files. -type FileSet struct { - 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 - } -} - -// AddFile adds a new file in the file set. -func (s *FileSet) AddFile(filename string, base, size int) *File { - if base < 0 { - base = s.Base - } - if base < s.Base || size < 0 { - panic("illegal base or size") - } - - f := &File{ - set: s, - Name: filename, - Base: base, - Size: size, - Lines: []int{0}, - } - - base += size + 1 // +1 because EOF also has a position - if base < 0 { - panic("offset overflow (> 2G of source code in file set)") - } - - // add the file to the file set - s.Base = base - s.Files = append(s.Files, f) - s.LastFile = f - - return f -} - -// File returns the file that contains the position p. -// If no such file is found (for instance for p == NoPos), -// the result is nil. -// -func (s *FileSet) File(p Pos) (f *File) { - if p != NoPos { - f = s.file(p) - } - - return -} - -// Position converts a Pos p in the fileset into a FilePos value. -func (s *FileSet) Position(p Pos) (pos FilePos) { - if p != NoPos { - if f := s.file(p); f != nil { - return f.position(p) - } - } - - return -} - -func (s *FileSet) file(p Pos) *File { - // common case: p is in last file - 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] - - // f.base <= int(p) by definition of searchFiles - if int(p) <= f.Base+f.Size { - s.LastFile = f // race is ok - s.last is only a cache - return f - } - } - - return nil -} - -func searchFiles(a []*File, x int) int { - return sort.Search(len(a), func(i int) bool { return a[i].Base > x }) - 1 -} diff --git a/vendor/github.com/d5/tengo/compiler/symbol.go b/vendor/github.com/d5/tengo/compiler/symbol.go deleted file mode 100644 index bcd53234..00000000 --- a/vendor/github.com/d5/tengo/compiler/symbol.go +++ /dev/null @@ -1,9 +0,0 @@ -package compiler - -// Symbol represents a symbol in the symbol table. -type Symbol struct { - Name string - Scope SymbolScope - Index int - LocalAssigned bool // if the local symbol is assigned at least once -} diff --git a/vendor/github.com/d5/tengo/compiler/symbol_scopes.go b/vendor/github.com/d5/tengo/compiler/symbol_scopes.go deleted file mode 100644 index e0c0d94b..00000000 --- a/vendor/github.com/d5/tengo/compiler/symbol_scopes.go +++ /dev/null @@ -1,12 +0,0 @@ -package compiler - -// SymbolScope represents a symbol scope. -type SymbolScope string - -// List of symbol scopes -const ( - ScopeGlobal SymbolScope = "GLOBAL" - ScopeLocal SymbolScope = "LOCAL" - ScopeBuiltin SymbolScope = "BUILTIN" - ScopeFree SymbolScope = "FREE" -) diff --git a/vendor/github.com/d5/tengo/compiler/token/keywords.go b/vendor/github.com/d5/tengo/compiler/token/keywords.go deleted file mode 100644 index fd6e9d0b..00000000 --- a/vendor/github.com/d5/tengo/compiler/token/keywords.go +++ /dev/null @@ -1,19 +0,0 @@ -package token - -var keywords map[string]Token - -func init() { - keywords = make(map[string]Token) - for i := _keywordBeg + 1; i < _keywordEnd; i++ { - keywords[tokens[i]] = i - } -} - -// Lookup returns corresponding keyword if ident is a keyword. -func Lookup(ident string) Token { - if tok, isKeyword := keywords[ident]; isKeyword { - return tok - } - - return Ident -} diff --git a/vendor/github.com/d5/tengo/go.mod b/vendor/github.com/d5/tengo/go.mod deleted file mode 100644 index 8421e273..00000000 --- a/vendor/github.com/d5/tengo/go.mod +++ /dev/null @@ -1,3 +0,0 @@ -module github.com/d5/tengo - -go 1.12 diff --git a/vendor/github.com/d5/tengo/objects/array.go b/vendor/github.com/d5/tengo/objects/array.go deleted file mode 100644 index 1e917c59..00000000 --- a/vendor/github.com/d5/tengo/objects/array.go +++ /dev/null @@ -1,130 +0,0 @@ -package objects - -import ( - "fmt" - "strings" - - "github.com/d5/tengo/compiler/token" -) - -// Array represents an array of objects. -type Array struct { - Value []Object -} - -// TypeName returns the name of the type. -func (o *Array) TypeName() string { - return "array" -} - -func (o *Array) String() string { - var elements []string - for _, e := range o.Value { - elements = append(elements, e.String()) - } - - return fmt.Sprintf("[%s]", strings.Join(elements, ", ")) -} - -// BinaryOp returns another object that is the result of -// a given binary operator and a right-hand side object. -func (o *Array) BinaryOp(op token.Token, rhs Object) (Object, error) { - if rhs, ok := rhs.(*Array); ok { - switch op { - case token.Add: - if len(rhs.Value) == 0 { - return o, nil - } - return &Array{Value: append(o.Value, rhs.Value...)}, nil - } - } - - return nil, ErrInvalidOperator -} - -// Copy returns a copy of the type. -func (o *Array) Copy() Object { - var c []Object - for _, elem := range o.Value { - c = append(c, elem.Copy()) - } - - return &Array{Value: c} -} - -// IsFalsy returns true if the value of the type is falsy. -func (o *Array) IsFalsy() bool { - return len(o.Value) == 0 -} - -// Equals returns true if the value of the type -// is equal to the value of another object. -func (o *Array) Equals(x Object) bool { - var xVal []Object - switch x := x.(type) { - case *Array: - xVal = x.Value - case *ImmutableArray: - xVal = x.Value - default: - return false - } - - if len(o.Value) != len(xVal) { - return false - } - - for i, e := range o.Value { - if !e.Equals(xVal[i]) { - return false - } - } - - return true -} - -// IndexGet returns an element at a given index. -func (o *Array) IndexGet(index Object) (res Object, err error) { - intIdx, ok := index.(*Int) - if !ok { - err = ErrInvalidIndexType - return - } - - idxVal := int(intIdx.Value) - - if idxVal < 0 || idxVal >= len(o.Value) { - res = UndefinedValue - return - } - - res = o.Value[idxVal] - - return -} - -// IndexSet sets an element at a given index. -func (o *Array) IndexSet(index, value Object) (err error) { - intIdx, ok := ToInt(index) - if !ok { - err = ErrInvalidIndexType - return - } - - if intIdx < 0 || intIdx >= len(o.Value) { - err = ErrIndexOutOfBounds - return - } - - o.Value[intIdx] = value - - return nil -} - -// Iterate creates an array iterator. -func (o *Array) Iterate() Iterator { - return &ArrayIterator{ - v: o.Value, - l: len(o.Value), - } -} diff --git a/vendor/github.com/d5/tengo/objects/array_iterator.go b/vendor/github.com/d5/tengo/objects/array_iterator.go deleted file mode 100644 index 204faa41..00000000 --- a/vendor/github.com/d5/tengo/objects/array_iterator.go +++ /dev/null @@ -1,57 +0,0 @@ -package objects - -import "github.com/d5/tengo/compiler/token" - -// ArrayIterator is an iterator for an array. -type ArrayIterator struct { - v []Object - i int - l int -} - -// TypeName returns the name of the type. -func (i *ArrayIterator) TypeName() string { - return "array-iterator" -} - -func (i *ArrayIterator) String() string { - return "" -} - -// BinaryOp returns another object that is the result of -// a given binary operator and a right-hand side object. -func (i *ArrayIterator) BinaryOp(op token.Token, rhs Object) (Object, error) { - return nil, ErrInvalidOperator -} - -// IsFalsy returns true if the value of the type is falsy. -func (i *ArrayIterator) IsFalsy() bool { - return true -} - -// Equals returns true if the value of the type -// is equal to the value of another object. -func (i *ArrayIterator) Equals(Object) bool { - return false -} - -// Copy returns a copy of the type. -func (i *ArrayIterator) Copy() Object { - return &ArrayIterator{v: i.v, i: i.i, l: i.l} -} - -// Next returns true if there are more elements to iterate. -func (i *ArrayIterator) Next() bool { - i.i++ - return i.i <= i.l -} - -// Key returns the key or index value of the current element. -func (i *ArrayIterator) Key() Object { - return &Int{Value: int64(i.i - 1)} -} - -// Value returns the value of the current element. -func (i *ArrayIterator) Value() Object { - return i.v[i.i-1] -} diff --git a/vendor/github.com/d5/tengo/objects/bool.go b/vendor/github.com/d5/tengo/objects/bool.go deleted file mode 100644 index ac9949e4..00000000 --- a/vendor/github.com/d5/tengo/objects/bool.go +++ /dev/null @@ -1,64 +0,0 @@ -package objects - -import ( - "github.com/d5/tengo/compiler/token" -) - -// Bool represents a boolean value. -type Bool struct { - // this is intentionally non-public to force using objects.TrueValue and FalseValue always - value bool -} - -func (o *Bool) String() string { - if o.value { - return "true" - } - - return "false" -} - -// TypeName returns the name of the type. -func (o *Bool) TypeName() string { - return "bool" -} - -// BinaryOp returns another object that is the result of -// a given binary operator and a right-hand side object. -func (o *Bool) BinaryOp(op token.Token, rhs Object) (Object, error) { - return nil, ErrInvalidOperator -} - -// Copy returns a copy of the type. -func (o *Bool) Copy() Object { - return o -} - -// IsFalsy returns true if the value of the type is falsy. -func (o *Bool) IsFalsy() bool { - return !o.value -} - -// Equals returns true if the value of the type -// is equal to the value of another object. -func (o *Bool) Equals(x Object) bool { - return o == x -} - -// GobDecode decodes bool value from input bytes. -func (o *Bool) GobDecode(b []byte) (err error) { - o.value = b[0] == 1 - - return -} - -// GobEncode encodes bool values into bytes. -func (o *Bool) GobEncode() (b []byte, err error) { - if o.value { - b = []byte{1} - } else { - b = []byte{0} - } - - return -} diff --git a/vendor/github.com/d5/tengo/objects/builtin_append.go b/vendor/github.com/d5/tengo/objects/builtin_append.go deleted file mode 100644 index 9fb14b82..00000000 --- a/vendor/github.com/d5/tengo/objects/builtin_append.go +++ /dev/null @@ -1,21 +0,0 @@ -package objects - -// append(arr, items...) -func builtinAppend(args ...Object) (Object, error) { - if len(args) < 2 { - return nil, ErrWrongNumArguments - } - - 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, ErrInvalidArgumentType{ - Name: "first", - Expected: "array", - Found: arg.TypeName(), - } - } -} diff --git a/vendor/github.com/d5/tengo/objects/builtin_convert.go b/vendor/github.com/d5/tengo/objects/builtin_convert.go deleted file mode 100644 index b5f2d05d..00000000 --- a/vendor/github.com/d5/tengo/objects/builtin_convert.go +++ /dev/null @@ -1,169 +0,0 @@ -package objects - -import "github.com/d5/tengo" - -func builtinString(args ...Object) (Object, error) { - argsLen := len(args) - if !(argsLen == 1 || argsLen == 2) { - return nil, ErrWrongNumArguments - } - - if _, ok := args[0].(*String); ok { - return args[0], nil - } - - v, ok := ToString(args[0]) - if ok { - if len(v) > tengo.MaxStringLen { - return nil, ErrStringLimit - } - - return &String{Value: v}, nil - } - - if argsLen == 2 { - return args[1], nil - } - - return UndefinedValue, nil -} - -func builtinInt(args ...Object) (Object, error) { - argsLen := len(args) - if !(argsLen == 1 || argsLen == 2) { - return nil, ErrWrongNumArguments - } - - if _, ok := args[0].(*Int); ok { - return args[0], nil - } - - v, ok := ToInt64(args[0]) - if ok { - return &Int{Value: v}, nil - } - - if argsLen == 2 { - return args[1], nil - } - - return UndefinedValue, nil -} - -func builtinFloat(args ...Object) (Object, error) { - argsLen := len(args) - if !(argsLen == 1 || argsLen == 2) { - return nil, ErrWrongNumArguments - } - - if _, ok := args[0].(*Float); ok { - return args[0], nil - } - - v, ok := ToFloat64(args[0]) - if ok { - return &Float{Value: v}, nil - } - - if argsLen == 2 { - return args[1], nil - } - - return UndefinedValue, nil -} - -func builtinBool(args ...Object) (Object, error) { - if len(args) != 1 { - return nil, ErrWrongNumArguments - } - - if _, ok := args[0].(*Bool); ok { - return args[0], nil - } - - v, ok := ToBool(args[0]) - if ok { - if v { - return TrueValue, nil - } - - return FalseValue, nil - } - - return UndefinedValue, nil -} - -func builtinChar(args ...Object) (Object, error) { - argsLen := len(args) - if !(argsLen == 1 || argsLen == 2) { - return nil, ErrWrongNumArguments - } - - if _, ok := args[0].(*Char); ok { - return args[0], nil - } - - v, ok := ToRune(args[0]) - if ok { - return &Char{Value: v}, nil - } - - if argsLen == 2 { - return args[1], nil - } - - return UndefinedValue, nil -} - -func builtinBytes(args ...Object) (Object, error) { - argsLen := len(args) - if !(argsLen == 1 || argsLen == 2) { - return nil, ErrWrongNumArguments - } - - // bytes(N) => create a new bytes with given size N - if n, ok := args[0].(*Int); ok { - if n.Value > int64(tengo.MaxBytesLen) { - return nil, ErrBytesLimit - } - - return &Bytes{Value: make([]byte, int(n.Value))}, nil - } - - v, ok := ToByteSlice(args[0]) - if ok { - if len(v) > tengo.MaxBytesLen { - return nil, ErrBytesLimit - } - - return &Bytes{Value: v}, nil - } - - if argsLen == 2 { - return args[1], nil - } - - return UndefinedValue, nil -} - -func builtinTime(args ...Object) (Object, error) { - argsLen := len(args) - if !(argsLen == 1 || argsLen == 2) { - return nil, ErrWrongNumArguments - } - - if _, ok := args[0].(*Time); ok { - return args[0], nil - } - - v, ok := ToTime(args[0]) - if ok { - return &Time{Value: v}, nil - } - - if argsLen == 2 { - return args[1], nil - } - - return UndefinedValue, nil -} diff --git a/vendor/github.com/d5/tengo/objects/builtin_copy.go b/vendor/github.com/d5/tengo/objects/builtin_copy.go deleted file mode 100644 index 4b254b2b..00000000 --- a/vendor/github.com/d5/tengo/objects/builtin_copy.go +++ /dev/null @@ -1,9 +0,0 @@ -package objects - -func builtinCopy(args ...Object) (Object, error) { - if len(args) != 1 { - return nil, ErrWrongNumArguments - } - - return args[0].Copy(), nil -} diff --git a/vendor/github.com/d5/tengo/objects/builtin_format.go b/vendor/github.com/d5/tengo/objects/builtin_format.go deleted file mode 100644 index 1f0e75ed..00000000 --- a/vendor/github.com/d5/tengo/objects/builtin_format.go +++ /dev/null @@ -1,27 +0,0 @@ -package objects - -func builtinFormat(args ...Object) (Object, error) { - numArgs := len(args) - if numArgs == 0 { - return nil, ErrWrongNumArguments - } - - format, ok := args[0].(*String) - if !ok { - 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 - } - - s, err := Format(format.Value, args[1:]...) - if err != nil { - return nil, err - } - - return &String{Value: s}, nil -} diff --git a/vendor/github.com/d5/tengo/objects/builtin_function.go b/vendor/github.com/d5/tengo/objects/builtin_function.go deleted file mode 100644 index 1d021617..00000000 --- a/vendor/github.com/d5/tengo/objects/builtin_function.go +++ /dev/null @@ -1,47 +0,0 @@ -package objects - -import ( - "github.com/d5/tengo/compiler/token" -) - -// 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:" + o.Name -} - -func (o *BuiltinFunction) String() string { - return "" -} - -// BinaryOp returns another object that is the result of -// a given binary operator and a right-hand side object. -func (o *BuiltinFunction) BinaryOp(op token.Token, rhs Object) (Object, error) { - return nil, ErrInvalidOperator -} - -// Copy returns a copy of the type. -func (o *BuiltinFunction) Copy() Object { - return &BuiltinFunction{Value: o.Value} -} - -// IsFalsy returns true if the value of the type is falsy. -func (o *BuiltinFunction) IsFalsy() bool { - return false -} - -// Equals returns true if the value of the type -// is equal to the value of another object. -func (o *BuiltinFunction) Equals(x Object) bool { - return false -} - -// Call executes a builtin function. -func (o *BuiltinFunction) Call(args ...Object) (Object, error) { - return o.Value(args...) -} diff --git a/vendor/github.com/d5/tengo/objects/builtin_len.go b/vendor/github.com/d5/tengo/objects/builtin_len.go deleted file mode 100644 index 39fbedd8..00000000 --- a/vendor/github.com/d5/tengo/objects/builtin_len.go +++ /dev/null @@ -1,29 +0,0 @@ -package objects - -// len(obj object) => int -func builtinLen(args ...Object) (Object, error) { - if len(args) != 1 { - return nil, ErrWrongNumArguments - } - - switch arg := args[0].(type) { - case *Array: - return &Int{Value: int64(len(arg.Value))}, nil - case *ImmutableArray: - return &Int{Value: int64(len(arg.Value))}, nil - case *String: - return &Int{Value: int64(len(arg.Value))}, nil - case *Bytes: - return &Int{Value: int64(len(arg.Value))}, nil - case *Map: - return &Int{Value: int64(len(arg.Value))}, nil - case *ImmutableMap: - return &Int{Value: int64(len(arg.Value))}, nil - default: - return nil, ErrInvalidArgumentType{ - Name: "first", - Expected: "array/string/bytes/map", - Found: arg.TypeName(), - } - } -} diff --git a/vendor/github.com/d5/tengo/objects/builtin_module.go b/vendor/github.com/d5/tengo/objects/builtin_module.go deleted file mode 100644 index 0ad1d99d..00000000 --- a/vendor/github.com/d5/tengo/objects/builtin_module.go +++ /dev/null @@ -1,23 +0,0 @@ -package objects - -// BuiltinModule is an importable module that's written in Go. -type BuiltinModule struct { - Attrs map[string]Object -} - -// Import returns an immutable map for the module. -func (m *BuiltinModule) Import(moduleName string) (interface{}, error) { - return m.AsImmutableMap(moduleName), nil -} - -// AsImmutableMap converts builtin module into an immutable map. -func (m *BuiltinModule) AsImmutableMap(moduleName string) *ImmutableMap { - attrs := make(map[string]Object, len(m.Attrs)) - for k, v := range m.Attrs { - attrs[k] = v.Copy() - } - - attrs["__module_name__"] = &String{Value: moduleName} - - return &ImmutableMap{Value: attrs} -} diff --git a/vendor/github.com/d5/tengo/objects/builtin_type.go b/vendor/github.com/d5/tengo/objects/builtin_type.go deleted file mode 100644 index 376c26bb..00000000 --- a/vendor/github.com/d5/tengo/objects/builtin_type.go +++ /dev/null @@ -1,9 +0,0 @@ -package objects - -func builtinTypeName(args ...Object) (Object, error) { - if len(args) != 1 { - return nil, ErrWrongNumArguments - } - - return &String{Value: args[0].TypeName()}, nil -} diff --git a/vendor/github.com/d5/tengo/objects/builtin_type_checks.go b/vendor/github.com/d5/tengo/objects/builtin_type_checks.go deleted file mode 100644 index d1e8471d..00000000 --- a/vendor/github.com/d5/tengo/objects/builtin_type_checks.go +++ /dev/null @@ -1,195 +0,0 @@ -package objects - -func builtinIsString(args ...Object) (Object, error) { - if len(args) != 1 { - return nil, ErrWrongNumArguments - } - - if _, ok := args[0].(*String); ok { - return TrueValue, nil - } - - return FalseValue, nil -} - -func builtinIsInt(args ...Object) (Object, error) { - if len(args) != 1 { - return nil, ErrWrongNumArguments - } - - if _, ok := args[0].(*Int); ok { - return TrueValue, nil - } - - return FalseValue, nil -} - -func builtinIsFloat(args ...Object) (Object, error) { - if len(args) != 1 { - return nil, ErrWrongNumArguments - } - - if _, ok := args[0].(*Float); ok { - return TrueValue, nil - } - - return FalseValue, nil -} - -func builtinIsBool(args ...Object) (Object, error) { - if len(args) != 1 { - return nil, ErrWrongNumArguments - } - - if _, ok := args[0].(*Bool); ok { - return TrueValue, nil - } - - return FalseValue, nil -} - -func builtinIsChar(args ...Object) (Object, error) { - if len(args) != 1 { - return nil, ErrWrongNumArguments - } - - if _, ok := args[0].(*Char); ok { - return TrueValue, nil - } - - return FalseValue, nil -} - -func builtinIsBytes(args ...Object) (Object, error) { - if len(args) != 1 { - return nil, ErrWrongNumArguments - } - - if _, ok := args[0].(*Bytes); ok { - return TrueValue, nil - } - - return FalseValue, nil -} - -func builtinIsArray(args ...Object) (Object, error) { - if len(args) != 1 { - return nil, ErrWrongNumArguments - } - - if _, ok := args[0].(*Array); ok { - return TrueValue, nil - } - - return FalseValue, nil -} - -func builtinIsImmutableArray(args ...Object) (Object, error) { - if len(args) != 1 { - return nil, ErrWrongNumArguments - } - - if _, ok := args[0].(*ImmutableArray); ok { - return TrueValue, nil - } - - return FalseValue, nil -} - -func builtinIsMap(args ...Object) (Object, error) { - if len(args) != 1 { - return nil, ErrWrongNumArguments - } - - if _, ok := args[0].(*Map); ok { - return TrueValue, nil - } - - return FalseValue, nil -} - -func builtinIsImmutableMap(args ...Object) (Object, error) { - if len(args) != 1 { - return nil, ErrWrongNumArguments - } - - if _, ok := args[0].(*ImmutableMap); ok { - return TrueValue, nil - } - - return FalseValue, nil -} - -func builtinIsTime(args ...Object) (Object, error) { - if len(args) != 1 { - return nil, ErrWrongNumArguments - } - - if _, ok := args[0].(*Time); ok { - return TrueValue, nil - } - - return FalseValue, nil -} - -func builtinIsError(args ...Object) (Object, error) { - if len(args) != 1 { - return nil, ErrWrongNumArguments - } - - if _, ok := args[0].(*Error); ok { - return TrueValue, nil - } - - return FalseValue, nil -} - -func builtinIsUndefined(args ...Object) (Object, error) { - if len(args) != 1 { - return nil, ErrWrongNumArguments - } - - if args[0] == UndefinedValue { - return TrueValue, nil - } - - return FalseValue, nil -} - -func builtinIsFunction(args ...Object) (Object, error) { - if len(args) != 1 { - return nil, ErrWrongNumArguments - } - - switch args[0].(type) { - case *CompiledFunction, *Closure: - return TrueValue, nil - } - - return FalseValue, nil -} - -func builtinIsCallable(args ...Object) (Object, error) { - if len(args) != 1 { - return nil, ErrWrongNumArguments - } - - switch args[0].(type) { - case *CompiledFunction, *Closure, Callable: // BuiltinFunction is Callable - return TrueValue, nil - } - - return FalseValue, nil -} - -func builtinIsIterable(args ...Object) (Object, error) { - if len(args) != 1 { - return nil, ErrWrongNumArguments - } - - if _, ok := args[0].(Iterable); ok { - return TrueValue, nil - } - - return FalseValue, nil -} diff --git a/vendor/github.com/d5/tengo/objects/builtins.go b/vendor/github.com/d5/tengo/objects/builtins.go deleted file mode 100644 index 773636ec..00000000 --- a/vendor/github.com/d5/tengo/objects/builtins.go +++ /dev/null @@ -1,118 +0,0 @@ -package objects - -// Builtins contains all default builtin functions. -// Use GetBuiltinFunctions instead of accessing Builtins directly. -var Builtins = []*BuiltinFunction{ - { - Name: "len", - Value: builtinLen, - }, - { - Name: "copy", - Value: builtinCopy, - }, - { - Name: "append", - Value: builtinAppend, - }, - { - Name: "string", - Value: builtinString, - }, - { - Name: "int", - Value: builtinInt, - }, - { - Name: "bool", - Value: builtinBool, - }, - { - Name: "float", - Value: builtinFloat, - }, - { - Name: "char", - Value: builtinChar, - }, - { - Name: "bytes", - Value: builtinBytes, - }, - { - Name: "time", - Value: builtinTime, - }, - { - Name: "is_int", - Value: builtinIsInt, - }, - { - Name: "is_float", - Value: builtinIsFloat, - }, - { - Name: "is_string", - Value: builtinIsString, - }, - { - Name: "is_bool", - Value: builtinIsBool, - }, - { - Name: "is_char", - Value: builtinIsChar, - }, - { - Name: "is_bytes", - Value: builtinIsBytes, - }, - { - Name: "is_array", - Value: builtinIsArray, - }, - { - Name: "is_immutable_array", - Value: builtinIsImmutableArray, - }, - { - Name: "is_map", - Value: builtinIsMap, - }, - { - Name: "is_immutable_map", - Value: builtinIsImmutableMap, - }, - { - Name: "is_iterable", - Value: builtinIsIterable, - }, - { - Name: "is_time", - Value: builtinIsTime, - }, - { - Name: "is_error", - Value: builtinIsError, - }, - { - Name: "is_undefined", - Value: builtinIsUndefined, - }, - { - Name: "is_function", - Value: builtinIsFunction, - }, - { - Name: "is_callable", - Value: builtinIsCallable, - }, - { - Name: "type_name", - Value: builtinTypeName, - }, - { - Name: "format", - Value: builtinFormat, - }, -} diff --git a/vendor/github.com/d5/tengo/objects/bytes.go b/vendor/github.com/d5/tengo/objects/bytes.go deleted file mode 100644 index 5159c22f..00000000 --- a/vendor/github.com/d5/tengo/objects/bytes.go +++ /dev/null @@ -1,89 +0,0 @@ -package objects - -import ( - "bytes" - - "github.com/d5/tengo" - "github.com/d5/tengo/compiler/token" -) - -// Bytes represents a byte array. -type Bytes struct { - Value []byte -} - -func (o *Bytes) String() string { - return string(o.Value) -} - -// TypeName returns the name of the type. -func (o *Bytes) TypeName() string { - return "bytes" -} - -// BinaryOp returns another object that is the result of -// a given binary operator and a right-hand side object. -func (o *Bytes) BinaryOp(op token.Token, rhs Object) (Object, error) { - switch op { - case token.Add: - switch rhs := rhs.(type) { - case *Bytes: - if len(o.Value)+len(rhs.Value) > tengo.MaxBytesLen { - return nil, ErrBytesLimit - } - - return &Bytes{Value: append(o.Value, rhs.Value...)}, nil - } - } - - return nil, ErrInvalidOperator -} - -// Copy returns a copy of the type. -func (o *Bytes) Copy() Object { - return &Bytes{Value: append([]byte{}, o.Value...)} -} - -// IsFalsy returns true if the value of the type is falsy. -func (o *Bytes) IsFalsy() bool { - return len(o.Value) == 0 -} - -// Equals returns true if the value of the type -// is equal to the value of another object. -func (o *Bytes) Equals(x Object) bool { - t, ok := x.(*Bytes) - if !ok { - return false - } - - return bytes.Equal(o.Value, t.Value) -} - -// IndexGet returns an element (as Int) at a given index. -func (o *Bytes) IndexGet(index Object) (res Object, err error) { - intIdx, ok := index.(*Int) - if !ok { - err = ErrInvalidIndexType - return - } - - idxVal := int(intIdx.Value) - - if idxVal < 0 || idxVal >= len(o.Value) { - res = UndefinedValue - return - } - - res = &Int{Value: int64(o.Value[idxVal])} - - return -} - -// Iterate creates a bytes iterator. -func (o *Bytes) Iterate() Iterator { - return &BytesIterator{ - v: o.Value, - l: len(o.Value), - } -} diff --git a/vendor/github.com/d5/tengo/objects/bytes_iterator.go b/vendor/github.com/d5/tengo/objects/bytes_iterator.go deleted file mode 100644 index 18a36e17..00000000 --- a/vendor/github.com/d5/tengo/objects/bytes_iterator.go +++ /dev/null @@ -1,57 +0,0 @@ -package objects - -import "github.com/d5/tengo/compiler/token" - -// BytesIterator represents an iterator for a string. -type BytesIterator struct { - v []byte - i int - l int -} - -// TypeName returns the name of the type. -func (i *BytesIterator) TypeName() string { - return "bytes-iterator" -} - -func (i *BytesIterator) String() string { - return "" -} - -// BinaryOp returns another object that is the result of -// a given binary operator and a right-hand side object. -func (i *BytesIterator) BinaryOp(op token.Token, rhs Object) (Object, error) { - return nil, ErrInvalidOperator -} - -// IsFalsy returns true if the value of the type is falsy. -func (i *BytesIterator) IsFalsy() bool { - return true -} - -// Equals returns true if the value of the type -// is equal to the value of another object. -func (i *BytesIterator) Equals(Object) bool { - return false -} - -// Copy returns a copy of the type. -func (i *BytesIterator) Copy() Object { - return &BytesIterator{v: i.v, i: i.i, l: i.l} -} - -// Next returns true if there are more elements to iterate. -func (i *BytesIterator) Next() bool { - i.i++ - return i.i <= i.l -} - -// Key returns the key or index value of the current element. -func (i *BytesIterator) Key() Object { - return &Int{Value: int64(i.i - 1)} -} - -// Value returns the value of the current element. -func (i *BytesIterator) Value() Object { - return &Int{Value: int64(i.v[i.i-1])} -} diff --git a/vendor/github.com/d5/tengo/objects/callable.go b/vendor/github.com/d5/tengo/objects/callable.go deleted file mode 100644 index a066e1b9..00000000 --- a/vendor/github.com/d5/tengo/objects/callable.go +++ /dev/null @@ -1,9 +0,0 @@ -package objects - -// Callable represents an object that can be called like a function. -type Callable interface { - // Call should take an arbitrary number of arguments - // and returns a return value and/or an error, - // which the VM will consider as a run-time error. - Call(args ...Object) (ret Object, err error) -} diff --git a/vendor/github.com/d5/tengo/objects/callable_func.go b/vendor/github.com/d5/tengo/objects/callable_func.go deleted file mode 100644 index ad25e65d..00000000 --- a/vendor/github.com/d5/tengo/objects/callable_func.go +++ /dev/null @@ -1,4 +0,0 @@ -package objects - -// CallableFunc is a function signature for the callable functions. -type CallableFunc = func(args ...Object) (ret Object, err error) diff --git a/vendor/github.com/d5/tengo/objects/char.go b/vendor/github.com/d5/tengo/objects/char.go deleted file mode 100644 index 4458bd12..00000000 --- a/vendor/github.com/d5/tengo/objects/char.go +++ /dev/null @@ -1,119 +0,0 @@ -package objects - -import ( - "github.com/d5/tengo/compiler/token" -) - -// Char represents a character value. -type Char struct { - Value rune -} - -func (o *Char) String() string { - return string(o.Value) -} - -// TypeName returns the name of the type. -func (o *Char) TypeName() string { - return "char" -} - -// BinaryOp returns another object that is the result of -// a given binary operator and a right-hand side object. -func (o *Char) BinaryOp(op token.Token, rhs Object) (Object, error) { - switch rhs := rhs.(type) { - case *Char: - switch op { - case token.Add: - r := o.Value + rhs.Value - if r == o.Value { - return o, nil - } - return &Char{Value: r}, nil - case token.Sub: - r := o.Value - rhs.Value - if r == o.Value { - return o, nil - } - return &Char{Value: r}, nil - case token.Less: - if o.Value < rhs.Value { - return TrueValue, nil - } - return FalseValue, nil - case token.Greater: - if o.Value > rhs.Value { - return TrueValue, nil - } - return FalseValue, nil - case token.LessEq: - if o.Value <= rhs.Value { - return TrueValue, nil - } - return FalseValue, nil - case token.GreaterEq: - if o.Value >= rhs.Value { - return TrueValue, nil - } - return FalseValue, nil - } - case *Int: - switch op { - case token.Add: - r := o.Value + rune(rhs.Value) - if r == o.Value { - return o, nil - } - return &Char{Value: r}, nil - case token.Sub: - r := o.Value - rune(rhs.Value) - if r == o.Value { - return o, nil - } - return &Char{Value: r}, nil - case token.Less: - if int64(o.Value) < rhs.Value { - return TrueValue, nil - } - return FalseValue, nil - case token.Greater: - if int64(o.Value) > rhs.Value { - return TrueValue, nil - } - return FalseValue, nil - case token.LessEq: - if int64(o.Value) <= rhs.Value { - return TrueValue, nil - } - return FalseValue, nil - case token.GreaterEq: - if int64(o.Value) >= rhs.Value { - return TrueValue, nil - } - return FalseValue, nil - } - } - - return nil, ErrInvalidOperator -} - -// Copy returns a copy of the type. -func (o *Char) Copy() Object { - return &Char{Value: o.Value} -} - -// IsFalsy returns true if the value of the type is falsy. -func (o *Char) IsFalsy() bool { - return o.Value == 0 -} - -// Equals returns true if the value of the type -// is equal to the value of another object. -func (o *Char) Equals(x Object) bool { - t, ok := x.(*Char) - if !ok { - return false - } - - return o.Value == t.Value -} diff --git a/vendor/github.com/d5/tengo/objects/closure.go b/vendor/github.com/d5/tengo/objects/closure.go deleted file mode 100644 index 06058b23..00000000 --- a/vendor/github.com/d5/tengo/objects/closure.go +++ /dev/null @@ -1,45 +0,0 @@ -package objects - -import ( - "github.com/d5/tengo/compiler/token" -) - -// Closure represents a function closure. -type Closure struct { - Fn *CompiledFunction - Free []*ObjectPtr -} - -// TypeName returns the name of the type. -func (o *Closure) TypeName() string { - return "closure" -} - -func (o *Closure) String() string { - return "" -} - -// BinaryOp returns another object that is the result of -// a given binary operator and a right-hand side object. -func (o *Closure) BinaryOp(op token.Token, rhs Object) (Object, error) { - return nil, ErrInvalidOperator -} - -// Copy returns a copy of the type. -func (o *Closure) Copy() Object { - return &Closure{ - Fn: o.Fn.Copy().(*CompiledFunction), - Free: append([]*ObjectPtr{}, o.Free...), // DO NOT Copy() of elements; these are variable pointers - } -} - -// IsFalsy returns true if the value of the type is falsy. -func (o *Closure) IsFalsy() bool { - return false -} - -// Equals returns true if the value of the type -// is equal to the value of another object. -func (o *Closure) Equals(x Object) bool { - return false -} diff --git a/vendor/github.com/d5/tengo/objects/compiled_function.go b/vendor/github.com/d5/tengo/objects/compiled_function.go deleted file mode 100644 index d42e69ec..00000000 --- a/vendor/github.com/d5/tengo/objects/compiled_function.go +++ /dev/null @@ -1,62 +0,0 @@ -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 // number of local variables (including function parameters) - NumParameters int - VarArgs bool - SourceMap map[int]source.Pos -} - -// TypeName returns the name of the type. -func (o *CompiledFunction) TypeName() string { - return "compiled-function" -} - -func (o *CompiledFunction) String() string { - return "" -} - -// BinaryOp returns another object that is the result of -// a given binary operator and a right-hand side object. -func (o *CompiledFunction) BinaryOp(op token.Token, rhs Object) (Object, error) { - return nil, ErrInvalidOperator -} - -// Copy returns a copy of the type. -func (o *CompiledFunction) Copy() Object { - return &CompiledFunction{ - Instructions: append([]byte{}, o.Instructions...), - NumLocals: o.NumLocals, - NumParameters: o.NumParameters, - VarArgs: o.VarArgs, - } -} - -// IsFalsy returns true if the value of the type is falsy. -func (o *CompiledFunction) IsFalsy() bool { - return false -} - -// Equals returns true if the value of the type -// is equal to the value of another object. -func (o *CompiledFunction) Equals(x Object) bool { - return false -} - -// SourcePos returns the source position of the instruction at ip. -func (o *CompiledFunction) SourcePos(ip int) source.Pos { - for ip >= 0 { - if p, ok := o.SourceMap[ip]; ok { - return p - } - ip-- - } - return source.NoPos -} diff --git a/vendor/github.com/d5/tengo/objects/count_objects.go b/vendor/github.com/d5/tengo/objects/count_objects.go deleted file mode 100644 index 8c482eb3..00000000 --- a/vendor/github.com/d5/tengo/objects/count_objects.go +++ /dev/null @@ -1,31 +0,0 @@ -package objects - -// CountObjects returns the number of objects that a given object o contains. -// For scalar value types, it will always be 1. For compound value types, -// this will include its elements and all of their elements recursively. -func CountObjects(o Object) (c int) { - c = 1 - - switch o := o.(type) { - case *Array: - for _, v := range o.Value { - c += CountObjects(v) - } - case *ImmutableArray: - for _, v := range o.Value { - c += CountObjects(v) - } - case *Map: - for _, v := range o.Value { - c += CountObjects(v) - } - case *ImmutableMap: - for _, v := range o.Value { - c += CountObjects(v) - } - case *Error: - c += CountObjects(o.Value) - } - - return -} diff --git a/vendor/github.com/d5/tengo/objects/error.go b/vendor/github.com/d5/tengo/objects/error.go deleted file mode 100644 index be21de03..00000000 --- a/vendor/github.com/d5/tengo/objects/error.go +++ /dev/null @@ -1,47 +0,0 @@ -package objects - -import ( - "fmt" - - "github.com/d5/tengo/compiler/token" -) - -// Error represents a string value. -type Error struct { - Value Object -} - -// TypeName returns the name of the type. -func (o *Error) TypeName() string { - return "error" -} - -func (o *Error) String() string { - if o.Value != nil { - return fmt.Sprintf("error: %s", o.Value.String()) - } - - return "error" -} - -// BinaryOp returns another object that is the result of -// a given binary operator and a right-hand side object. -func (o *Error) BinaryOp(op token.Token, rhs Object) (Object, error) { - return nil, ErrInvalidOperator -} - -// IsFalsy returns true if the value of the type is falsy. -func (o *Error) IsFalsy() bool { - return true // error is always false. -} - -// Copy returns a copy of the type. -func (o *Error) Copy() Object { - return &Error{Value: o.Value.Copy()} -} - -// Equals returns true if the value of the type -// is equal to the value of another object. -func (o *Error) Equals(x Object) bool { - return o == x // pointer equality -} diff --git a/vendor/github.com/d5/tengo/objects/errors.go b/vendor/github.com/d5/tengo/objects/errors.go deleted file mode 100644 index bcd480a1..00000000 --- a/vendor/github.com/d5/tengo/objects/errors.go +++ /dev/null @@ -1,38 +0,0 @@ -package objects - -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 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") - -// ErrBytesLimit represents an error where the size of bytes value exceeds the limit. -var ErrBytesLimit = errors.New("exceeding bytes size limit") - -// ErrStringLimit represents an error where the size of string value exceeds the limit. -var ErrStringLimit = errors.New("exceeding string size limit") - -// 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) -} diff --git a/vendor/github.com/d5/tengo/objects/float.go b/vendor/github.com/d5/tengo/objects/float.go deleted file mode 100644 index 65997303..00000000 --- a/vendor/github.com/d5/tengo/objects/float.go +++ /dev/null @@ -1,146 +0,0 @@ -package objects - -import ( - "math" - "strconv" - - "github.com/d5/tengo/compiler/token" -) - -// Float represents a floating point number value. -type Float struct { - Value float64 -} - -func (o *Float) String() string { - return strconv.FormatFloat(o.Value, 'f', -1, 64) -} - -// TypeName returns the name of the type. -func (o *Float) TypeName() string { - return "float" -} - -// BinaryOp returns another object that is the result of -// a given binary operator and a right-hand side object. -func (o *Float) BinaryOp(op token.Token, rhs Object) (Object, error) { - switch rhs := rhs.(type) { - case *Float: - switch op { - case token.Add: - r := o.Value + rhs.Value - if r == o.Value { - return o, nil - } - return &Float{Value: r}, nil - case token.Sub: - r := o.Value - rhs.Value - if r == o.Value { - return o, nil - } - return &Float{Value: r}, nil - case token.Mul: - r := o.Value * rhs.Value - if r == o.Value { - return o, nil - } - return &Float{Value: r}, nil - case token.Quo: - r := o.Value / rhs.Value - if r == o.Value { - return o, nil - } - return &Float{Value: r}, nil - case token.Less: - if o.Value < rhs.Value { - return TrueValue, nil - } - return FalseValue, nil - case token.Greater: - if o.Value > rhs.Value { - return TrueValue, nil - } - return FalseValue, nil - case token.LessEq: - if o.Value <= rhs.Value { - return TrueValue, nil - } - return FalseValue, nil - case token.GreaterEq: - if o.Value >= rhs.Value { - return TrueValue, nil - } - return FalseValue, nil - } - case *Int: - switch op { - case token.Add: - r := o.Value + float64(rhs.Value) - if r == o.Value { - return o, nil - } - return &Float{Value: r}, nil - case token.Sub: - r := o.Value - float64(rhs.Value) - if r == o.Value { - return o, nil - } - return &Float{Value: r}, nil - case token.Mul: - r := o.Value * float64(rhs.Value) - if r == o.Value { - return o, nil - } - return &Float{Value: r}, nil - case token.Quo: - r := o.Value / float64(rhs.Value) - if r == o.Value { - return o, nil - } - return &Float{Value: r}, nil - case token.Less: - if o.Value < float64(rhs.Value) { - return TrueValue, nil - } - return FalseValue, nil - case token.Greater: - if o.Value > float64(rhs.Value) { - return TrueValue, nil - } - return FalseValue, nil - case token.LessEq: - if o.Value <= float64(rhs.Value) { - return TrueValue, nil - } - return FalseValue, nil - case token.GreaterEq: - if o.Value >= float64(rhs.Value) { - return TrueValue, nil - } - return FalseValue, nil - } - } - - return nil, ErrInvalidOperator -} - -// Copy returns a copy of the type. -func (o *Float) Copy() Object { - return &Float{Value: o.Value} -} - -// IsFalsy returns true if the value of the type is falsy. -func (o *Float) IsFalsy() bool { - return math.IsNaN(o.Value) -} - -// Equals returns true if the value of the type -// is equal to the value of another object. -func (o *Float) Equals(x Object) bool { - t, ok := x.(*Float) - if !ok { - return false - } - - return o.Value == t.Value -} diff --git a/vendor/github.com/d5/tengo/objects/immutable_array.go b/vendor/github.com/d5/tengo/objects/immutable_array.go deleted file mode 100644 index f3621e29..00000000 --- a/vendor/github.com/d5/tengo/objects/immutable_array.go +++ /dev/null @@ -1,109 +0,0 @@ -package objects - -import ( - "fmt" - "strings" - - "github.com/d5/tengo/compiler/token" -) - -// ImmutableArray represents an immutable array of objects. -type ImmutableArray struct { - Value []Object -} - -// TypeName returns the name of the type. -func (o *ImmutableArray) TypeName() string { - return "immutable-array" -} - -func (o *ImmutableArray) String() string { - var elements []string - for _, e := range o.Value { - elements = append(elements, e.String()) - } - - return fmt.Sprintf("[%s]", strings.Join(elements, ", ")) -} - -// BinaryOp returns another object that is the result of -// a given binary operator and a right-hand side object. -func (o *ImmutableArray) BinaryOp(op token.Token, rhs Object) (Object, error) { - if rhs, ok := rhs.(*ImmutableArray); ok { - switch op { - case token.Add: - return &Array{Value: append(o.Value, rhs.Value...)}, nil - } - } - - return nil, ErrInvalidOperator -} - -// Copy returns a copy of the type. -func (o *ImmutableArray) Copy() Object { - var c []Object - for _, elem := range o.Value { - c = append(c, elem.Copy()) - } - - return &Array{Value: c} -} - -// IsFalsy returns true if the value of the type is falsy. -func (o *ImmutableArray) IsFalsy() bool { - return len(o.Value) == 0 -} - -// Equals returns true if the value of the type -// is equal to the value of another object. -func (o *ImmutableArray) Equals(x Object) bool { - var xVal []Object - switch x := x.(type) { - case *Array: - xVal = x.Value - case *ImmutableArray: - xVal = x.Value - default: - return false - } - - if len(o.Value) != len(xVal) { - return false - } - - for i, e := range o.Value { - if !e.Equals(xVal[i]) { - return false - } - } - - return true -} - -// IndexGet returns an element at a given index. -func (o *ImmutableArray) IndexGet(index Object) (res Object, err error) { - intIdx, ok := index.(*Int) - if !ok { - err = ErrInvalidIndexType - return - } - - idxVal := int(intIdx.Value) - - if idxVal < 0 || idxVal >= len(o.Value) { - res = UndefinedValue - return - } - - res = o.Value[idxVal] - - return -} - -// Iterate creates an array iterator. -func (o *ImmutableArray) Iterate() Iterator { - return &ArrayIterator{ - v: o.Value, - l: len(o.Value), - } -} diff --git a/vendor/github.com/d5/tengo/objects/immutable_map.go b/vendor/github.com/d5/tengo/objects/immutable_map.go deleted file mode 100644 index 8f58701b..00000000 --- a/vendor/github.com/d5/tengo/objects/immutable_map.go +++ /dev/null @@ -1,105 +0,0 @@ -package objects - -import ( - "fmt" - "strings" - - "github.com/d5/tengo/compiler/token" -) - -// ImmutableMap represents an immutable map object. -type ImmutableMap struct { - Value map[string]Object -} - -// TypeName returns the name of the type. -func (o *ImmutableMap) TypeName() string { - return "immutable-map" -} - -func (o *ImmutableMap) String() string { - var pairs []string - for k, v := range o.Value { - pairs = append(pairs, fmt.Sprintf("%s: %s", k, v.String())) - } - - return fmt.Sprintf("{%s}", strings.Join(pairs, ", ")) -} - -// BinaryOp returns another object that is the result of -// a given binary operator and a right-hand side object. -func (o *ImmutableMap) BinaryOp(op token.Token, rhs Object) (Object, error) { - return nil, ErrInvalidOperator -} - -// Copy returns a copy of the type. -func (o *ImmutableMap) Copy() Object { - c := make(map[string]Object) - for k, v := range o.Value { - c[k] = v.Copy() - } - - return &Map{Value: c} -} - -// IsFalsy returns true if the value of the type is falsy. -func (o *ImmutableMap) IsFalsy() bool { - return len(o.Value) == 0 -} - -// IndexGet returns the value for the given key. -func (o *ImmutableMap) IndexGet(index Object) (res Object, err error) { - strIdx, ok := ToString(index) - if !ok { - err = ErrInvalidIndexType - return - } - - val, ok := o.Value[strIdx] - if !ok { - val = UndefinedValue - } - - return val, nil -} - -// Equals returns true if the value of the type -// is equal to the value of another object. -func (o *ImmutableMap) Equals(x Object) bool { - var xVal map[string]Object - switch x := x.(type) { - case *Map: - xVal = x.Value - case *ImmutableMap: - xVal = x.Value - default: - return false - } - - if len(o.Value) != len(xVal) { - return false - } - - for k, v := range o.Value { - tv := xVal[k] - if !v.Equals(tv) { - return false - } - } - - return true -} - -// Iterate creates an immutable map iterator. -func (o *ImmutableMap) Iterate() Iterator { - var keys []string - for k := range o.Value { - keys = append(keys, k) - } - - return &MapIterator{ - v: o.Value, - k: keys, - l: len(keys), - } -} diff --git a/vendor/github.com/d5/tengo/objects/importable.go b/vendor/github.com/d5/tengo/objects/importable.go deleted file mode 100644 index 9fd86ae8..00000000 --- a/vendor/github.com/d5/tengo/objects/importable.go +++ /dev/null @@ -1,7 +0,0 @@ -package objects - -// Importable interface represents importable module instance. -type Importable interface { - // Import should return either an Object or module source code ([]byte). - Import(moduleName string) (interface{}, error) -} diff --git a/vendor/github.com/d5/tengo/objects/index_assignable.go b/vendor/github.com/d5/tengo/objects/index_assignable.go deleted file mode 100644 index a1c6cbff..00000000 --- a/vendor/github.com/d5/tengo/objects/index_assignable.go +++ /dev/null @@ -1,9 +0,0 @@ -package objects - -// IndexAssignable is an object that can take an index and a value -// on the left-hand side of the assignment statement. -type IndexAssignable interface { - // IndexSet should take an index Object and a value Object. - // If an error is returned, it will be treated as a run-time error. - IndexSet(index, value Object) error -} diff --git a/vendor/github.com/d5/tengo/objects/indexable.go b/vendor/github.com/d5/tengo/objects/indexable.go deleted file mode 100644 index bbc81633..00000000 --- a/vendor/github.com/d5/tengo/objects/indexable.go +++ /dev/null @@ -1,9 +0,0 @@ -package objects - -// Indexable is an object that can take an index and return an object. -type Indexable interface { - // IndexGet should take an index Object and return a result Object or an error. - // If error is returned, the runtime will treat it as a run-time error and ignore returned value. - // If nil is returned as value, it will be converted to Undefined value by the runtime. - IndexGet(index Object) (value Object, err error) -} diff --git a/vendor/github.com/d5/tengo/objects/int.go b/vendor/github.com/d5/tengo/objects/int.go deleted file mode 100644 index e902c93a..00000000 --- a/vendor/github.com/d5/tengo/objects/int.go +++ /dev/null @@ -1,198 +0,0 @@ -package objects - -import ( - "strconv" - - "github.com/d5/tengo/compiler/token" -) - -// Int represents an integer value. -type Int struct { - Value int64 -} - -func (o *Int) String() string { - return strconv.FormatInt(o.Value, 10) -} - -// TypeName returns the name of the type. -func (o *Int) TypeName() string { - return "int" -} - -// BinaryOp returns another object that is the result of -// a given binary operator and a right-hand side object. -func (o *Int) BinaryOp(op token.Token, rhs Object) (Object, error) { - switch rhs := rhs.(type) { - case *Int: - switch op { - case token.Add: - r := o.Value + rhs.Value - if r == o.Value { - return o, nil - } - return &Int{Value: r}, nil - case token.Sub: - r := o.Value - rhs.Value - if r == o.Value { - return o, nil - } - return &Int{Value: r}, nil - case token.Mul: - r := o.Value * rhs.Value - if r == o.Value { - return o, nil - } - return &Int{Value: r}, nil - case token.Quo: - r := o.Value / rhs.Value - if r == o.Value { - return o, nil - } - return &Int{Value: r}, nil - case token.Rem: - r := o.Value % rhs.Value - if r == o.Value { - return o, nil - } - return &Int{Value: r}, nil - case token.And: - r := o.Value & rhs.Value - if r == o.Value { - return o, nil - } - return &Int{Value: r}, nil - case token.Or: - r := o.Value | rhs.Value - if r == o.Value { - return o, nil - } - return &Int{Value: r}, nil - case token.Xor: - r := o.Value ^ rhs.Value - if r == o.Value { - return o, nil - } - return &Int{Value: r}, nil - case token.AndNot: - r := o.Value &^ rhs.Value - if r == o.Value { - return o, nil - } - return &Int{Value: r}, nil - case token.Shl: - r := o.Value << uint64(rhs.Value) - if r == o.Value { - return o, nil - } - return &Int{Value: r}, nil - case token.Shr: - r := o.Value >> uint64(rhs.Value) - if r == o.Value { - return o, nil - } - return &Int{Value: r}, nil - case token.Less: - if o.Value < rhs.Value { - return TrueValue, nil - } - return FalseValue, nil - case token.Greater: - if o.Value > rhs.Value { - return TrueValue, nil - } - return FalseValue, nil - case token.LessEq: - if o.Value <= rhs.Value { - return TrueValue, nil - } - return FalseValue, nil - case token.GreaterEq: - if o.Value >= rhs.Value { - return TrueValue, nil - } - return FalseValue, nil - } - case *Float: - switch op { - case token.Add: - return &Float{float64(o.Value) + rhs.Value}, nil - case token.Sub: - return &Float{float64(o.Value) - rhs.Value}, nil - case token.Mul: - return &Float{float64(o.Value) * rhs.Value}, nil - case token.Quo: - return &Float{float64(o.Value) / rhs.Value}, nil - case token.Less: - if float64(o.Value) < rhs.Value { - return TrueValue, nil - } - return FalseValue, nil - case token.Greater: - if float64(o.Value) > rhs.Value { - return TrueValue, nil - } - return FalseValue, nil - case token.LessEq: - if float64(o.Value) <= rhs.Value { - return TrueValue, nil - } - return FalseValue, nil - case token.GreaterEq: - if float64(o.Value) >= rhs.Value { - return TrueValue, nil - } - return FalseValue, nil - } - case *Char: - switch op { - case token.Add: - return &Char{rune(o.Value) + rhs.Value}, nil - case token.Sub: - return &Char{rune(o.Value) - rhs.Value}, nil - case token.Less: - if o.Value < int64(rhs.Value) { - return TrueValue, nil - } - return FalseValue, nil - case token.Greater: - if o.Value > int64(rhs.Value) { - return TrueValue, nil - } - return FalseValue, nil - case token.LessEq: - if o.Value <= int64(rhs.Value) { - return TrueValue, nil - } - return FalseValue, nil - case token.GreaterEq: - if o.Value >= int64(rhs.Value) { - return TrueValue, nil - } - return FalseValue, nil - } - } - - return nil, ErrInvalidOperator -} - -// Copy returns a copy of the type. -func (o *Int) Copy() Object { - return &Int{Value: o.Value} -} - -// IsFalsy returns true if the value of the type is falsy. -func (o *Int) IsFalsy() bool { - return o.Value == 0 -} - -// Equals returns true if the value of the type -// is equal to the value of another object. -func (o *Int) Equals(x Object) bool { - t, ok := x.(*Int) - if !ok { - return false - } - - return o.Value == t.Value -} diff --git a/vendor/github.com/d5/tengo/objects/iterable.go b/vendor/github.com/d5/tengo/objects/iterable.go deleted file mode 100644 index e431d3d7..00000000 --- a/vendor/github.com/d5/tengo/objects/iterable.go +++ /dev/null @@ -1,7 +0,0 @@ -package objects - -// Iterable represents an object that has iterator. -type Iterable interface { - // Iterate should return an Iterator for the type. - Iterate() Iterator -} diff --git a/vendor/github.com/d5/tengo/objects/iterator.go b/vendor/github.com/d5/tengo/objects/iterator.go deleted file mode 100644 index 01522ba5..00000000 --- a/vendor/github.com/d5/tengo/objects/iterator.go +++ /dev/null @@ -1,15 +0,0 @@ -package objects - -// Iterator represents an iterator for underlying data type. -type Iterator interface { - Object - - // Next returns true if there are more elements to iterate. - Next() bool - - // Key returns the key or index value of the current element. - Key() Object - - // Value returns the value of the current element. - Value() Object -} diff --git a/vendor/github.com/d5/tengo/objects/map.go b/vendor/github.com/d5/tengo/objects/map.go deleted file mode 100644 index 9208872c..00000000 --- a/vendor/github.com/d5/tengo/objects/map.go +++ /dev/null @@ -1,118 +0,0 @@ -package objects - -import ( - "fmt" - "strings" - - "github.com/d5/tengo/compiler/token" -) - -// Map represents a map of objects. -type Map struct { - Value map[string]Object -} - -// TypeName returns the name of the type. -func (o *Map) TypeName() string { - return "map" -} - -func (o *Map) String() string { - var pairs []string - for k, v := range o.Value { - pairs = append(pairs, fmt.Sprintf("%s: %s", k, v.String())) - } - - return fmt.Sprintf("{%s}", strings.Join(pairs, ", ")) -} - -// BinaryOp returns another object that is the result of -// a given binary operator and a right-hand side object. -func (o *Map) BinaryOp(op token.Token, rhs Object) (Object, error) { - return nil, ErrInvalidOperator -} - -// Copy returns a copy of the type. -func (o *Map) Copy() Object { - c := make(map[string]Object) - for k, v := range o.Value { - c[k] = v.Copy() - } - - return &Map{Value: c} -} - -// IsFalsy returns true if the value of the type is falsy. -func (o *Map) IsFalsy() bool { - return len(o.Value) == 0 -} - -// Equals returns true if the value of the type -// is equal to the value of another object. -func (o *Map) Equals(x Object) bool { - var xVal map[string]Object - switch x := x.(type) { - case *Map: - xVal = x.Value - case *ImmutableMap: - xVal = x.Value - default: - return false - } - - if len(o.Value) != len(xVal) { - return false - } - - for k, v := range o.Value { - tv := xVal[k] - if !v.Equals(tv) { - return false - } - } - - return true -} - -// IndexGet returns the value for the given key. -func (o *Map) IndexGet(index Object) (res Object, err error) { - strIdx, ok := ToString(index) - if !ok { - err = ErrInvalidIndexType - return - } - - val, ok := o.Value[strIdx] - if !ok { - val = UndefinedValue - } - - return val, nil -} - -// IndexSet sets the value for the given key. -func (o *Map) IndexSet(index, value Object) (err error) { - strIdx, ok := ToString(index) - if !ok { - err = ErrInvalidIndexType - return - } - - o.Value[strIdx] = value - - return nil -} - -// Iterate creates a map iterator. -func (o *Map) Iterate() Iterator { - var keys []string - for k := range o.Value { - keys = append(keys, k) - } - - return &MapIterator{ - v: o.Value, - k: keys, - l: len(keys), - } -} diff --git a/vendor/github.com/d5/tengo/objects/map_iterator.go b/vendor/github.com/d5/tengo/objects/map_iterator.go deleted file mode 100644 index d60dd0e1..00000000 --- a/vendor/github.com/d5/tengo/objects/map_iterator.go +++ /dev/null @@ -1,62 +0,0 @@ -package objects - -import "github.com/d5/tengo/compiler/token" - -// MapIterator represents an iterator for the map. -type MapIterator struct { - v map[string]Object - k []string - i int - l int -} - -// TypeName returns the name of the type. -func (i *MapIterator) TypeName() string { - return "map-iterator" -} - -func (i *MapIterator) String() string { - return "" -} - -// BinaryOp returns another object that is the result of -// a given binary operator and a right-hand side object. -func (i *MapIterator) BinaryOp(op token.Token, rhs Object) (Object, error) { - return nil, ErrInvalidOperator -} - -// IsFalsy returns true if the value of the type is falsy. -func (i *MapIterator) IsFalsy() bool { - return true -} - -// Equals returns true if the value of the type -// is equal to the value of another object. -func (i *MapIterator) Equals(Object) bool { - return false -} - -// Copy returns a copy of the type. -func (i *MapIterator) Copy() Object { - return &MapIterator{v: i.v, k: i.k, i: i.i, l: i.l} -} - -// Next returns true if there are more elements to iterate. -func (i *MapIterator) Next() bool { - i.i++ - return i.i <= i.l -} - -// Key returns the key or index value of the current element. -func (i *MapIterator) Key() Object { - k := i.k[i.i-1] - - return &String{Value: k} -} - -// Value returns the value of the current element. -func (i *MapIterator) Value() Object { - k := i.k[i.i-1] - - return i.v[k] -} diff --git a/vendor/github.com/d5/tengo/objects/object.go b/vendor/github.com/d5/tengo/objects/object.go deleted file mode 100644 index 4c5aa7ae..00000000 --- a/vendor/github.com/d5/tengo/objects/object.go +++ /dev/null @@ -1,30 +0,0 @@ -package objects - -import "github.com/d5/tengo/compiler/token" - -// Object represents an object in the VM. -type Object interface { - // TypeName should return the name of the type. - TypeName() string - - // String should return a string representation of the type's value. - String() string - - // BinaryOp should return another object that is the result of - // a given binary operator and a right-hand side object. - // If BinaryOp returns an error, the VM will treat it as a run-time error. - BinaryOp(op token.Token, rhs Object) (Object, error) - - // IsFalsy should return true if the value of the type - // should be considered as falsy. - IsFalsy() bool - - // Equals should return true if the value of the type - // should be considered as equal to the value of another object. - Equals(another Object) bool - - // Copy should return a copy of the type (and its value). - // Copy function will be used for copy() builtin function - // which is expected to deep-copy the values generally. - Copy() Object -} diff --git a/vendor/github.com/d5/tengo/objects/object_ptr.go b/vendor/github.com/d5/tengo/objects/object_ptr.go deleted file mode 100644 index 2c87c561..00000000 --- a/vendor/github.com/d5/tengo/objects/object_ptr.go +++ /dev/null @@ -1,41 +0,0 @@ -package objects - -import ( - "github.com/d5/tengo/compiler/token" -) - -// ObjectPtr represents a free variable. -type ObjectPtr struct { - Value *Object -} - -func (o *ObjectPtr) String() string { - return "free-var" -} - -// TypeName returns the name of the type. -func (o *ObjectPtr) TypeName() string { - return "" -} - -// BinaryOp returns another object that is the result of -// a given binary operator and a right-hand side object. -func (o *ObjectPtr) BinaryOp(op token.Token, rhs Object) (Object, error) { - return nil, ErrInvalidOperator -} - -// Copy returns a copy of the type. -func (o *ObjectPtr) Copy() Object { - return o -} - -// IsFalsy returns true if the value of the type is falsy. -func (o *ObjectPtr) IsFalsy() bool { - return o.Value == nil -} - -// Equals returns true if the value of the type -// is equal to the value of another object. -func (o *ObjectPtr) Equals(x Object) bool { - return o == x -} diff --git a/vendor/github.com/d5/tengo/objects/objects.go b/vendor/github.com/d5/tengo/objects/objects.go deleted file mode 100644 index f3878b11..00000000 --- a/vendor/github.com/d5/tengo/objects/objects.go +++ /dev/null @@ -1,12 +0,0 @@ -package objects - -var ( - // TrueValue represents a true value. - TrueValue Object = &Bool{value: true} - - // FalseValue represents a false value. - FalseValue Object = &Bool{value: false} - - // UndefinedValue represents an undefined value. - UndefinedValue Object = &Undefined{} -) diff --git a/vendor/github.com/d5/tengo/objects/source_module.go b/vendor/github.com/d5/tengo/objects/source_module.go deleted file mode 100644 index 577fddf2..00000000 --- a/vendor/github.com/d5/tengo/objects/source_module.go +++ /dev/null @@ -1,11 +0,0 @@ -package objects - -// SourceModule is an importable module that's written in Tengo. -type SourceModule struct { - Src []byte -} - -// Import returns a module source code. -func (m *SourceModule) Import(_ string) (interface{}, error) { - return m.Src, nil -} diff --git a/vendor/github.com/d5/tengo/objects/string.go b/vendor/github.com/d5/tengo/objects/string.go deleted file mode 100644 index c25b0502..00000000 --- a/vendor/github.com/d5/tengo/objects/string.go +++ /dev/null @@ -1,103 +0,0 @@ -package objects - -import ( - "strconv" - - "github.com/d5/tengo" - "github.com/d5/tengo/compiler/token" -) - -// String represents a string value. -type String struct { - Value string - runeStr []rune -} - -// TypeName returns the name of the type. -func (o *String) TypeName() string { - return "string" -} - -func (o *String) String() string { - return strconv.Quote(o.Value) -} - -// BinaryOp returns another object that is the result of -// a given binary operator and a right-hand side object. -func (o *String) BinaryOp(op token.Token, rhs Object) (Object, error) { - switch op { - case token.Add: - switch rhs := rhs.(type) { - case *String: - if len(o.Value)+len(rhs.Value) > tengo.MaxStringLen { - return nil, ErrStringLimit - } - return &String{Value: o.Value + rhs.Value}, nil - default: - rhsStr := rhs.String() - if len(o.Value)+len(rhsStr) > tengo.MaxStringLen { - return nil, ErrStringLimit - } - return &String{Value: o.Value + rhsStr}, nil - } - } - - return nil, ErrInvalidOperator -} - -// IsFalsy returns true if the value of the type is falsy. -func (o *String) IsFalsy() bool { - return len(o.Value) == 0 -} - -// Copy returns a copy of the type. -func (o *String) Copy() Object { - return &String{Value: o.Value} -} - -// Equals returns true if the value of the type -// is equal to the value of another object. -func (o *String) Equals(x Object) bool { - t, ok := x.(*String) - if !ok { - return false - } - - return o.Value == t.Value -} - -// IndexGet returns a character at a given index. -func (o *String) IndexGet(index Object) (res Object, err error) { - intIdx, ok := index.(*Int) - if !ok { - err = ErrInvalidIndexType - return - } - - idxVal := int(intIdx.Value) - - if o.runeStr == nil { - o.runeStr = []rune(o.Value) - } - - if idxVal < 0 || idxVal >= len(o.runeStr) { - res = UndefinedValue - return - } - - res = &Char{Value: o.runeStr[idxVal]} - - return -} - -// Iterate creates a string iterator. -func (o *String) Iterate() Iterator { - if o.runeStr == nil { - o.runeStr = []rune(o.Value) - } - - return &StringIterator{ - v: o.runeStr, - l: len(o.runeStr), - } -} diff --git a/vendor/github.com/d5/tengo/objects/string_iterator.go b/vendor/github.com/d5/tengo/objects/string_iterator.go deleted file mode 100644 index 8bc95eb5..00000000 --- a/vendor/github.com/d5/tengo/objects/string_iterator.go +++ /dev/null @@ -1,57 +0,0 @@ -package objects - -import "github.com/d5/tengo/compiler/token" - -// StringIterator represents an iterator for a string. -type StringIterator struct { - v []rune - i int - l int -} - -// TypeName returns the name of the type. -func (i *StringIterator) TypeName() string { - return "string-iterator" -} - -func (i *StringIterator) String() string { - return "" -} - -// BinaryOp returns another object that is the result of -// a given binary operator and a right-hand side object. -func (i *StringIterator) BinaryOp(op token.Token, rhs Object) (Object, error) { - return nil, ErrInvalidOperator -} - -// IsFalsy returns true if the value of the type is falsy. -func (i *StringIterator) IsFalsy() bool { - return true -} - -// Equals returns true if the value of the type -// is equal to the value of another object. -func (i *StringIterator) Equals(Object) bool { - return false -} - -// Copy returns a copy of the type. -func (i *StringIterator) Copy() Object { - return &StringIterator{v: i.v, i: i.i, l: i.l} -} - -// Next returns true if there are more elements to iterate. -func (i *StringIterator) Next() bool { - i.i++ - return i.i <= i.l -} - -// Key returns the key or index value of the current element. -func (i *StringIterator) Key() Object { - return &Int{Value: int64(i.i - 1)} -} - -// Value returns the value of the current element. -func (i *StringIterator) Value() Object { - return &Char{Value: i.v[i.i-1]} -} diff --git a/vendor/github.com/d5/tengo/objects/time.go b/vendor/github.com/d5/tengo/objects/time.go deleted file mode 100644 index 4e783cc8..00000000 --- a/vendor/github.com/d5/tengo/objects/time.go +++ /dev/null @@ -1,89 +0,0 @@ -package objects - -import ( - "time" - - "github.com/d5/tengo/compiler/token" -) - -// Time represents a time value. -type Time struct { - Value time.Time -} - -func (o *Time) String() string { - return o.Value.String() -} - -// TypeName returns the name of the type. -func (o *Time) TypeName() string { - return "time" -} - -// BinaryOp returns another object that is the result of -// a given binary operator and a right-hand side object. -func (o *Time) BinaryOp(op token.Token, rhs Object) (Object, error) { - switch rhs := rhs.(type) { - case *Int: - switch op { - case token.Add: // time + int => time - if rhs.Value == 0 { - return o, nil - } - return &Time{Value: o.Value.Add(time.Duration(rhs.Value))}, nil - case token.Sub: // time - int => time - if rhs.Value == 0 { - return o, nil - } - return &Time{Value: o.Value.Add(time.Duration(-rhs.Value))}, nil - } - case *Time: - switch op { - case token.Sub: // time - time => int (duration) - return &Int{Value: int64(o.Value.Sub(rhs.Value))}, nil - case token.Less: // time < time => bool - if o.Value.Before(rhs.Value) { - return TrueValue, nil - } - return FalseValue, nil - case token.Greater: - if o.Value.After(rhs.Value) { - return TrueValue, nil - } - return FalseValue, nil - case token.LessEq: - if o.Value.Equal(rhs.Value) || o.Value.Before(rhs.Value) { - return TrueValue, nil - } - return FalseValue, nil - case token.GreaterEq: - if o.Value.Equal(rhs.Value) || o.Value.After(rhs.Value) { - return TrueValue, nil - } - return FalseValue, nil - } - } - - return nil, ErrInvalidOperator -} - -// Copy returns a copy of the type. -func (o *Time) Copy() Object { - return &Time{Value: o.Value} -} - -// IsFalsy returns true if the value of the type is falsy. -func (o *Time) IsFalsy() bool { - return o.Value.IsZero() -} - -// Equals returns true if the value of the type -// is equal to the value of another object. -func (o *Time) Equals(x Object) bool { - t, ok := x.(*Time) - if !ok { - return false - } - - return o.Value.Equal(t.Value) -} diff --git a/vendor/github.com/d5/tengo/objects/undefined.go b/vendor/github.com/d5/tengo/objects/undefined.go deleted file mode 100644 index 0fdbc084..00000000 --- a/vendor/github.com/d5/tengo/objects/undefined.go +++ /dev/null @@ -1,62 +0,0 @@ -package objects - -import "github.com/d5/tengo/compiler/token" - -// Undefined represents an undefined value. -type Undefined struct{} - -// TypeName returns the name of the type. -func (o *Undefined) TypeName() string { - return "undefined" -} - -func (o *Undefined) String() string { - return "" -} - -// BinaryOp returns another object that is the result of -// a given binary operator and a right-hand side object. -func (o *Undefined) BinaryOp(op token.Token, rhs Object) (Object, error) { - return nil, ErrInvalidOperator -} - -// Copy returns a copy of the type. -func (o *Undefined) Copy() Object { - return o -} - -// IsFalsy returns true if the value of the type is falsy. -func (o *Undefined) IsFalsy() bool { - return true -} - -// Equals returns true if the value of the type -// is equal to the value of another object. -func (o *Undefined) Equals(x Object) bool { - return o == x -} - -// IndexGet returns an element at a given index. -func (o *Undefined) IndexGet(index Object) (Object, error) { - return UndefinedValue, nil -} - -// Iterate creates a map iterator. -func (o *Undefined) Iterate() Iterator { - return o -} - -// Next returns true if there are more elements to iterate. -func (o *Undefined) Next() bool { - return false -} - -// Key returns the key or index value of the current element. -func (o *Undefined) Key() Object { - return o -} - -// Value returns the value of the current element. -func (o *Undefined) Value() Object { - return o -} diff --git a/vendor/github.com/d5/tengo/objects/user_function.go b/vendor/github.com/d5/tengo/objects/user_function.go deleted file mode 100644 index a896788b..00000000 --- a/vendor/github.com/d5/tengo/objects/user_function.go +++ /dev/null @@ -1,48 +0,0 @@ -package objects - -import ( - "github.com/d5/tengo/compiler/token" -) - -// UserFunction represents a user function. -type UserFunction struct { - Name string - Value CallableFunc - EncodingID string -} - -// TypeName returns the name of the type. -func (o *UserFunction) TypeName() string { - return "user-function:" + o.Name -} - -func (o *UserFunction) String() string { - return "" -} - -// BinaryOp returns another object that is the result of -// a given binary operator and a right-hand side object. -func (o *UserFunction) BinaryOp(op token.Token, rhs Object) (Object, error) { - return nil, ErrInvalidOperator -} - -// Copy returns a copy of the type. -func (o *UserFunction) Copy() Object { - return &UserFunction{Value: o.Value} -} - -// IsFalsy returns true if the value of the type is falsy. -func (o *UserFunction) IsFalsy() bool { - return false -} - -// Equals returns true if the value of the type -// is equal to the value of another object. -func (o *UserFunction) Equals(x Object) bool { - return false -} - -// Call invokes a user function. -func (o *UserFunction) Call(args ...Object) (Object, error) { - return o.Value(args...) -} diff --git a/vendor/github.com/d5/tengo/runtime/errors.go b/vendor/github.com/d5/tengo/runtime/errors.go deleted file mode 100644 index fc7ca0e9..00000000 --- a/vendor/github.com/d5/tengo/runtime/errors.go +++ /dev/null @@ -1,11 +0,0 @@ -package runtime - -import ( - "errors" -) - -// ErrStackOverflow is a stack overflow error. -var ErrStackOverflow = errors.New("stack overflow") - -// ErrObjectAllocLimit is an objects allocation limit error. -var ErrObjectAllocLimit = errors.New("object allocation limit exceeded") diff --git a/vendor/github.com/d5/tengo/runtime/frame.go b/vendor/github.com/d5/tengo/runtime/frame.go deleted file mode 100644 index cbaadbdb..00000000 --- a/vendor/github.com/d5/tengo/runtime/frame.go +++ /dev/null @@ -1,13 +0,0 @@ -package runtime - -import ( - "github.com/d5/tengo/objects" -) - -// Frame represents a function call frame. -type Frame struct { - fn *objects.CompiledFunction - freeVars []*objects.ObjectPtr - ip int - basePointer int -} diff --git a/vendor/github.com/d5/tengo/script/compiled.go b/vendor/github.com/d5/tengo/script/compiled.go deleted file mode 100644 index ce50f498..00000000 --- a/vendor/github.com/d5/tengo/script/compiled.go +++ /dev/null @@ -1,159 +0,0 @@ -package script - -import ( - "context" - "fmt" - "sync" - - "github.com/d5/tengo/compiler" - "github.com/d5/tengo/objects" - "github.com/d5/tengo/runtime" -) - -// Compiled is a compiled instance of the user script. -// Use Script.Compile() to create Compiled object. -type Compiled struct { - globalIndexes map[string]int // global symbol name to index - bytecode *compiler.Bytecode - globals []objects.Object - maxAllocs int64 - lock sync.RWMutex -} - -// Run executes the compiled script in the virtual machine. -func (c *Compiled) Run() error { - c.lock.Lock() - defer c.lock.Unlock() - - v := runtime.NewVM(c.bytecode, c.globals, c.maxAllocs) - - return v.Run() -} - -// RunContext is like Run but includes a context. -func (c *Compiled) RunContext(ctx context.Context) (err error) { - c.lock.Lock() - defer c.lock.Unlock() - - v := runtime.NewVM(c.bytecode, c.globals, c.maxAllocs) - - ch := make(chan error, 1) - - go func() { - ch <- v.Run() - }() - - select { - case <-ctx.Done(): - v.Abort() - <-ch - err = ctx.Err() - case err = <-ch: - } - - return -} - -// Clone creates a new copy of Compiled. -// Cloned copies are safe for concurrent use by multiple goroutines. -func (c *Compiled) Clone() *Compiled { - c.lock.Lock() - defer c.lock.Unlock() - - clone := &Compiled{ - globalIndexes: c.globalIndexes, - bytecode: c.bytecode, - globals: make([]objects.Object, len(c.globals)), - maxAllocs: c.maxAllocs, - } - - // copy global objects - for idx, g := range c.globals { - if g != nil { - clone.globals[idx] = g - } - } - - return clone -} - -// IsDefined returns true if the variable name is defined (has value) before or after the execution. -func (c *Compiled) IsDefined(name string) bool { - c.lock.RLock() - defer c.lock.RUnlock() - - idx, ok := c.globalIndexes[name] - if !ok { - return false - } - - v := c.globals[idx] - if v == nil { - return false - } - - return v != objects.UndefinedValue -} - -// Get returns a variable identified by the name. -func (c *Compiled) Get(name string) *Variable { - c.lock.RLock() - defer c.lock.RUnlock() - - value := objects.UndefinedValue - - if idx, ok := c.globalIndexes[name]; ok { - value = c.globals[idx] - if value == nil { - value = objects.UndefinedValue - } - } - - return &Variable{ - name: name, - value: value, - } -} - -// GetAll returns all the variables that are defined by the compiled script. -func (c *Compiled) GetAll() []*Variable { - c.lock.RLock() - defer c.lock.RUnlock() - - var vars []*Variable - - for name, idx := range c.globalIndexes { - value := c.globals[idx] - if value == nil { - value = objects.UndefinedValue - } - - vars = append(vars, &Variable{ - name: name, - value: value, - }) - } - - return vars -} - -// Set replaces the value of a global variable identified by the name. -// An error will be returned if the name was not defined during compilation. -func (c *Compiled) Set(name string, value interface{}) error { - c.lock.Lock() - defer c.lock.Unlock() - - obj, err := objects.FromInterface(value) - if err != nil { - return err - } - - idx, ok := c.globalIndexes[name] - if !ok { - return fmt.Errorf("'%s' is not defined", name) - } - - c.globals[idx] = obj - - return nil -} diff --git a/vendor/github.com/d5/tengo/script/script.go b/vendor/github.com/d5/tengo/script/script.go deleted file mode 100644 index 2ee67b61..00000000 --- a/vendor/github.com/d5/tengo/script/script.go +++ /dev/null @@ -1,185 +0,0 @@ -package script - -import ( - "context" - "fmt" - - "github.com/d5/tengo/compiler" - "github.com/d5/tengo/compiler/parser" - "github.com/d5/tengo/compiler/source" - "github.com/d5/tengo/objects" - "github.com/d5/tengo/runtime" -) - -// Script can simplify compilation and execution of embedded scripts. -type Script struct { - variables map[string]*Variable - modules *objects.ModuleMap - input []byte - maxAllocs int64 - maxConstObjects int - enableFileImport bool -} - -// New creates a Script instance with an input script. -func New(input []byte) *Script { - return &Script{ - variables: make(map[string]*Variable), - input: input, - maxAllocs: -1, - maxConstObjects: -1, - } -} - -// Add adds a new variable or updates an existing variable to the script. -func (s *Script) Add(name string, value interface{}) error { - obj, err := objects.FromInterface(value) - if err != nil { - return err - } - - s.variables[name] = &Variable{ - name: name, - value: obj, - } - - return nil -} - -// Remove removes (undefines) an existing variable for the script. -// It returns false if the variable name is not defined. -func (s *Script) Remove(name string) bool { - if _, ok := s.variables[name]; !ok { - return false - } - - delete(s.variables, name) - - return true -} - -// SetImports sets import modules. -func (s *Script) SetImports(modules *objects.ModuleMap) { - s.modules = modules -} - -// SetMaxAllocs sets the maximum number of objects allocations during the run time. -// Compiled script will return runtime.ErrObjectAllocLimit error if it exceeds this limit. -func (s *Script) SetMaxAllocs(n int64) { - s.maxAllocs = n -} - -// SetMaxConstObjects sets the maximum number of objects in the compiled constants. -func (s *Script) SetMaxConstObjects(n int) { - s.maxConstObjects = n -} - -// EnableFileImport enables or disables module loading from local files. -// Local file modules are disabled by default. -func (s *Script) EnableFileImport(enable bool) { - s.enableFileImport = enable -} - -// Compile compiles the script with all the defined variables, and, returns Compiled object. -func (s *Script) Compile() (*Compiled, error) { - symbolTable, globals, err := s.prepCompile() - if err != nil { - return nil, err - } - - fileSet := source.NewFileSet() - srcFile := fileSet.AddFile("(main)", -1, len(s.input)) - - p := parser.NewParser(srcFile, s.input, nil) - file, err := p.ParseFile() - if err != nil { - return nil, err - } - - c := compiler.NewCompiler(srcFile, symbolTable, nil, s.modules, nil) - c.EnableFileImport(s.enableFileImport) - if err := c.Compile(file); err != nil { - return nil, err - } - - // reduce globals size - globals = globals[:symbolTable.MaxSymbols()+1] - - // global symbol names to indexes - globalIndexes := make(map[string]int, len(globals)) - for _, name := range symbolTable.Names() { - symbol, _, _ := symbolTable.Resolve(name) - if symbol.Scope == compiler.ScopeGlobal { - globalIndexes[name] = symbol.Index - } - } - - // remove duplicates from constants - bytecode := c.Bytecode() - bytecode.RemoveDuplicates() - - // check the constant objects limit - if s.maxConstObjects >= 0 { - cnt := bytecode.CountObjects() - if cnt > s.maxConstObjects { - return nil, fmt.Errorf("exceeding constant objects limit: %d", cnt) - } - } - - return &Compiled{ - globalIndexes: globalIndexes, - bytecode: bytecode, - globals: globals, - maxAllocs: s.maxAllocs, - }, nil -} - -// Run compiles and runs the scripts. -// Use returned compiled object to access global variables. -func (s *Script) Run() (compiled *Compiled, err error) { - compiled, err = s.Compile() - if err != nil { - return - } - - err = compiled.Run() - - return -} - -// RunContext is like Run but includes a context. -func (s *Script) RunContext(ctx context.Context) (compiled *Compiled, err error) { - compiled, err = s.Compile() - if err != nil { - return - } - - err = compiled.RunContext(ctx) - - return -} - -func (s *Script) prepCompile() (symbolTable *compiler.SymbolTable, globals []objects.Object, err error) { - var names []string - for name := range s.variables { - names = append(names, name) - } - - symbolTable = compiler.NewSymbolTable() - for idx, fn := range objects.Builtins { - symbolTable.DefineBuiltin(idx, fn.Name) - } - - globals = make([]objects.Object, runtime.GlobalsSize) - - for idx, name := range names { - symbol := symbolTable.Define(name) - if symbol.Index != idx { - panic(fmt.Errorf("wrong symbol index: %d != %d", idx, symbol.Index)) - } - - globals[symbol.Index] = s.variables[name].value - } - - return -} diff --git a/vendor/github.com/d5/tengo/stdlib/base64.go b/vendor/github.com/d5/tengo/stdlib/base64.go deleted file mode 100644 index 40a746ce..00000000 --- a/vendor/github.com/d5/tengo/stdlib/base64.go +++ /dev/null @@ -1,20 +0,0 @@ -package stdlib - -import ( - "encoding/base64" - "github.com/d5/tengo/objects" -) - -var base64Module = map[string]objects.Object{ - "encode": &objects.UserFunction{Value: FuncAYRS(base64.StdEncoding.EncodeToString)}, - "decode": &objects.UserFunction{Value: FuncASRYE(base64.StdEncoding.DecodeString)}, - - "raw_encode": &objects.UserFunction{Value: FuncAYRS(base64.RawStdEncoding.EncodeToString)}, - "raw_decode": &objects.UserFunction{Value: FuncASRYE(base64.RawStdEncoding.DecodeString)}, - - "url_encode": &objects.UserFunction{Value: FuncAYRS(base64.URLEncoding.EncodeToString)}, - "url_decode": &objects.UserFunction{Value: FuncASRYE(base64.URLEncoding.DecodeString)}, - - "raw_url_encode": &objects.UserFunction{Value: FuncAYRS(base64.RawURLEncoding.EncodeToString)}, - "raw_url_decode": &objects.UserFunction{Value: FuncASRYE(base64.RawURLEncoding.DecodeString)}, -} diff --git a/vendor/github.com/d5/tengo/stdlib/builtin_modules.go b/vendor/github.com/d5/tengo/stdlib/builtin_modules.go deleted file mode 100644 index 722461b2..00000000 --- a/vendor/github.com/d5/tengo/stdlib/builtin_modules.go +++ /dev/null @@ -1,16 +0,0 @@ -package stdlib - -import "github.com/d5/tengo/objects" - -// BuiltinModules are builtin type standard library modules. -var BuiltinModules = map[string]map[string]objects.Object{ - "math": mathModule, - "os": osModule, - "text": textModule, - "times": timesModule, - "rand": randModule, - "fmt": fmtModule, - "json": jsonModule, - "base64": base64Module, - "hex": hexModule, -} diff --git a/vendor/github.com/d5/tengo/stdlib/errors.go b/vendor/github.com/d5/tengo/stdlib/errors.go deleted file mode 100644 index a2942bb0..00000000 --- a/vendor/github.com/d5/tengo/stdlib/errors.go +++ /dev/null @@ -1,11 +0,0 @@ -package stdlib - -import "github.com/d5/tengo/objects" - -func wrapError(err error) objects.Object { - if err == nil { - return objects.TrueValue - } - - return &objects.Error{Value: &objects.String{Value: err.Error()}} -} diff --git a/vendor/github.com/d5/tengo/stdlib/fmt.go b/vendor/github.com/d5/tengo/stdlib/fmt.go deleted file mode 100644 index b8f64278..00000000 --- a/vendor/github.com/d5/tengo/stdlib/fmt.go +++ /dev/null @@ -1,110 +0,0 @@ -package stdlib - -import ( - "fmt" - - "github.com/d5/tengo" - "github.com/d5/tengo/objects" -) - -var fmtModule = map[string]objects.Object{ - "print": &objects.UserFunction{Name: "print", Value: fmtPrint}, - "printf": &objects.UserFunction{Name: "printf", Value: fmtPrintf}, - "println": &objects.UserFunction{Name: "println", Value: fmtPrintln}, - "sprintf": &objects.UserFunction{Name: "sprintf", Value: fmtSprintf}, -} - -func fmtPrint(args ...objects.Object) (ret objects.Object, err error) { - printArgs, err := getPrintArgs(args...) - if err != nil { - return nil, err - } - - _, _ = fmt.Print(printArgs...) - - return nil, nil -} - -func fmtPrintf(args ...objects.Object) (ret objects.Object, err error) { - numArgs := len(args) - if numArgs == 0 { - return nil, objects.ErrWrongNumArguments - } - - format, ok := args[0].(*objects.String) - if !ok { - return nil, objects.ErrInvalidArgumentType{ - Name: "format", - Expected: "string", - Found: args[0].TypeName(), - } - } - if numArgs == 1 { - fmt.Print(format) - return nil, nil - } - - s, err := objects.Format(format.Value, args[1:]...) - if err != nil { - return nil, err - } - - fmt.Print(s) - - return nil, nil -} - -func fmtPrintln(args ...objects.Object) (ret objects.Object, err error) { - printArgs, err := getPrintArgs(args...) - if err != nil { - return nil, err - } - - printArgs = append(printArgs, "\n") - _, _ = fmt.Print(printArgs...) - - return nil, nil -} - -func fmtSprintf(args ...objects.Object) (ret objects.Object, err error) { - numArgs := len(args) - if numArgs == 0 { - return nil, objects.ErrWrongNumArguments - } - - format, ok := args[0].(*objects.String) - if !ok { - return nil, objects.ErrInvalidArgumentType{ - Name: "format", - Expected: "string", - Found: args[0].TypeName(), - } - } - if numArgs == 1 { - return format, nil // okay to return 'format' directly as String is immutable - } - - s, err := objects.Format(format.Value, args[1:]...) - if err != nil { - return nil, err - } - - return &objects.String{Value: s}, nil -} - -func getPrintArgs(args ...objects.Object) ([]interface{}, error) { - var printArgs []interface{} - l := 0 - for _, arg := range args { - s, _ := objects.ToString(arg) - slen := len(s) - if l+slen > tengo.MaxStringLen { // make sure length does not exceed the limit - return nil, objects.ErrStringLimit - } - l += slen - - printArgs = append(printArgs, s) - } - - return printArgs, nil -} diff --git a/vendor/github.com/d5/tengo/stdlib/func_typedefs.go b/vendor/github.com/d5/tengo/stdlib/func_typedefs.go deleted file mode 100644 index c7bd11fa..00000000 --- a/vendor/github.com/d5/tengo/stdlib/func_typedefs.go +++ /dev/null @@ -1,1178 +0,0 @@ -package stdlib - -import ( - "fmt" - - "github.com/d5/tengo" - "github.com/d5/tengo/objects" -) - -// FuncAR transform a function of 'func()' signature -// into CallableFunc type. -func FuncAR(fn func()) objects.CallableFunc { - return 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 CallableFunc type. -func FuncARI(fn func() int) objects.CallableFunc { - return 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 CallableFunc type. -func FuncARI64(fn func() int64) objects.CallableFunc { - return 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 CallableFunc type. -func FuncAI64RI64(fn func(int64) int64) objects.CallableFunc { - return 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(), - } - } - - return &objects.Int{Value: fn(i1)}, nil - } -} - -// FuncAI64R transform a function of 'func(int64)' signature -// into CallableFunc type. -func FuncAI64R(fn func(int64)) objects.CallableFunc { - return 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(), - } - } - - fn(i1) - - return objects.UndefinedValue, nil - } -} - -// FuncARB transform a function of 'func() bool' signature -// into CallableFunc type. -func FuncARB(fn func() bool) objects.CallableFunc { - return 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 CallableFunc type. -func FuncARE(fn func() error) objects.CallableFunc { - return 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 CallableFunc type. -func FuncARS(fn func() string) objects.CallableFunc { - return func(args ...objects.Object) (ret objects.Object, err error) { - if len(args) != 0 { - return nil, objects.ErrWrongNumArguments - } - - s := fn() - - if len(s) > tengo.MaxStringLen { - return nil, objects.ErrStringLimit - } - - return &objects.String{Value: s}, nil - } -} - -// FuncARSE transform a function of 'func() (string, error)' signature -// into CallableFunc type. -func FuncARSE(fn func() (string, error)) objects.CallableFunc { - return 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 - } - - if len(res) > tengo.MaxStringLen { - return nil, objects.ErrStringLimit - } - - return &objects.String{Value: res}, nil - } -} - -// FuncARYE transform a function of 'func() ([]byte, error)' signature -// into CallableFunc type. -func FuncARYE(fn func() ([]byte, error)) objects.CallableFunc { - return 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 - } - - if len(res) > tengo.MaxBytesLen { - return nil, objects.ErrBytesLimit - } - - return &objects.Bytes{Value: res}, nil - } -} - -// FuncARF transform a function of 'func() float64' signature -// into CallableFunc type. -func FuncARF(fn func() float64) objects.CallableFunc { - return 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 CallableFunc type. -func FuncARSs(fn func() []string) objects.CallableFunc { - return func(args ...objects.Object) (ret objects.Object, err error) { - if len(args) != 0 { - return nil, objects.ErrWrongNumArguments - } - - arr := &objects.Array{} - for _, elem := range fn() { - if len(elem) > tengo.MaxStringLen { - return nil, objects.ErrStringLimit - } - - arr.Value = append(arr.Value, &objects.String{Value: elem}) - } - - return arr, nil - } -} - -// FuncARIsE transform a function of 'func() ([]int, error)' signature -// into CallableFunc type. -func FuncARIsE(fn func() ([]int, error)) objects.CallableFunc { - return 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 CallableFunc type. -func FuncAIRIs(fn func(int) []int) objects.CallableFunc { - return 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.ErrInvalidArgumentType{ - Name: "first", - Expected: "int(compatible)", - Found: args[0].TypeName(), - } - } - - 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 CallableFunc type. -func FuncAFRF(fn func(float64) float64) objects.CallableFunc { - return 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.ErrInvalidArgumentType{ - Name: "first", - Expected: "float(compatible)", - Found: args[0].TypeName(), - } - } - - return &objects.Float{Value: fn(f1)}, nil - } -} - -// FuncAIR transform a function of 'func(int)' signature -// into CallableFunc type. -func FuncAIR(fn func(int)) objects.CallableFunc { - return 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.ErrInvalidArgumentType{ - Name: "first", - Expected: "int(compatible)", - Found: args[0].TypeName(), - } - } - - fn(i1) - - return objects.UndefinedValue, nil - } -} - -// FuncAIRF transform a function of 'func(int) float64' signature -// into CallableFunc type. -func FuncAIRF(fn func(int) float64) objects.CallableFunc { - return 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.ErrInvalidArgumentType{ - Name: "first", - Expected: "int(compatible)", - Found: args[0].TypeName(), - } - } - - return &objects.Float{Value: fn(i1)}, nil - } -} - -// FuncAFRI transform a function of 'func(float64) int' signature -// into CallableFunc type. -func FuncAFRI(fn func(float64) int) objects.CallableFunc { - return 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.ErrInvalidArgumentType{ - Name: "first", - Expected: "float(compatible)", - Found: args[0].TypeName(), - } - } - - return &objects.Int{Value: int64(fn(f1))}, nil - } -} - -// FuncAFFRF transform a function of 'func(float64, float64) float64' signature -// into CallableFunc type. -func FuncAFFRF(fn func(float64, float64) float64) objects.CallableFunc { - return 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.ErrInvalidArgumentType{ - Name: "first", - Expected: "float(compatible)", - Found: args[0].TypeName(), - } - } - - f2, ok := objects.ToFloat64(args[1]) - if !ok { - return nil, objects.ErrInvalidArgumentType{ - Name: "second", - Expected: "float(compatible)", - Found: args[1].TypeName(), - } - } - - return &objects.Float{Value: fn(f1, f2)}, nil - } -} - -// FuncAIFRF transform a function of 'func(int, float64) float64' signature -// into CallableFunc type. -func FuncAIFRF(fn func(int, float64) float64) objects.CallableFunc { - return 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.ErrInvalidArgumentType{ - Name: "first", - Expected: "int(compatible)", - Found: args[0].TypeName(), - } - } - - f2, ok := objects.ToFloat64(args[1]) - if !ok { - return nil, objects.ErrInvalidArgumentType{ - Name: "second", - Expected: "float(compatible)", - Found: args[1].TypeName(), - } - } - - return &objects.Float{Value: fn(i1, f2)}, nil - } -} - -// FuncAFIRF transform a function of 'func(float64, int) float64' signature -// into CallableFunc type. -func FuncAFIRF(fn func(float64, int) float64) objects.CallableFunc { - return 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.ErrInvalidArgumentType{ - Name: "first", - Expected: "float(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(), - } - } - - return &objects.Float{Value: fn(f1, i2)}, nil - } -} - -// FuncAFIRB transform a function of 'func(float64, int) bool' signature -// into CallableFunc type. -func FuncAFIRB(fn func(float64, int) bool) objects.CallableFunc { - return 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.ErrInvalidArgumentType{ - Name: "first", - Expected: "float(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(), - } - } - - if fn(f1, i2) { - return objects.TrueValue, nil - } - - return objects.FalseValue, nil - } -} - -// FuncAFRB transform a function of 'func(float64) bool' signature -// into CallableFunc type. -func FuncAFRB(fn func(float64) bool) objects.CallableFunc { - return 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.ErrInvalidArgumentType{ - Name: "first", - Expected: "float(compatible)", - Found: args[0].TypeName(), - } - } - - if fn(f1) { - return objects.TrueValue, nil - } - - return objects.FalseValue, nil - } -} - -// FuncASRS transform a function of 'func(string) string' signature into CallableFunc type. -// User function will return 'true' if underlying native function returns nil. -func FuncASRS(fn func(string) string) objects.CallableFunc { - return 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.ErrInvalidArgumentType{ - Name: "first", - Expected: "string(compatible)", - Found: args[0].TypeName(), - } - } - - s := fn(s1) - - if len(s) > tengo.MaxStringLen { - return nil, objects.ErrStringLimit - } - - return &objects.String{Value: s}, nil - } -} - -// FuncASRSs transform a function of 'func(string) []string' signature into CallableFunc type. -func FuncASRSs(fn func(string) []string) objects.CallableFunc { - return 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.ErrInvalidArgumentType{ - Name: "first", - Expected: "string(compatible)", - Found: args[0].TypeName(), - } - } - - res := fn(s1) - - arr := &objects.Array{} - for _, elem := range res { - if len(elem) > tengo.MaxStringLen { - return nil, objects.ErrStringLimit - } - - arr.Value = append(arr.Value, &objects.String{Value: elem}) - } - - return arr, nil - } -} - -// FuncASRSE transform a function of 'func(string) (string, error)' signature into CallableFunc type. -// User function will return 'true' if underlying native function returns nil. -func FuncASRSE(fn func(string) (string, error)) objects.CallableFunc { - return 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.ErrInvalidArgumentType{ - Name: "first", - Expected: "string(compatible)", - Found: args[0].TypeName(), - } - } - - res, err := fn(s1) - if err != nil { - return wrapError(err), nil - } - - if len(res) > tengo.MaxStringLen { - return nil, objects.ErrStringLimit - } - - return &objects.String{Value: res}, nil - } -} - -// FuncASRE transform a function of 'func(string) error' signature into CallableFunc type. -// User function will return 'true' if underlying native function returns nil. -func FuncASRE(fn func(string) error) objects.CallableFunc { - return 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.ErrInvalidArgumentType{ - Name: "first", - Expected: "string(compatible)", - Found: args[0].TypeName(), - } - } - - return wrapError(fn(s1)), nil - } -} - -// FuncASSRE transform a function of 'func(string, string) error' signature into CallableFunc type. -// User function will return 'true' if underlying native function returns nil. -func FuncASSRE(fn func(string, string) error) objects.CallableFunc { - return 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(), - } - } - - s2, ok := objects.ToString(args[1]) - if !ok { - return nil, objects.ErrInvalidArgumentType{ - Name: "second", - Expected: "string(compatible)", - Found: args[1].TypeName(), - } - } - - return wrapError(fn(s1, s2)), nil - } -} - -// FuncASSRSs transform a function of 'func(string, string) []string' signature into CallableFunc type. -func FuncASSRSs(fn func(string, string) []string) objects.CallableFunc { - return 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(), - } - } - - s2, ok := objects.ToString(args[1]) - if !ok { - return nil, objects.ErrInvalidArgumentType{ - Name: "first", - Expected: "string(compatible)", - Found: args[1].TypeName(), - } - } - - arr := &objects.Array{} - for _, res := range fn(s1, s2) { - if len(res) > tengo.MaxStringLen { - return nil, objects.ErrStringLimit - } - - arr.Value = append(arr.Value, &objects.String{Value: res}) - } - - return arr, nil - } -} - -// FuncASSIRSs transform a function of 'func(string, string, int) []string' signature into CallableFunc type. -func FuncASSIRSs(fn func(string, string, int) []string) objects.CallableFunc { - return 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.ErrInvalidArgumentType{ - Name: "first", - Expected: "string(compatible)", - Found: args[0].TypeName(), - } - } - - s2, ok := objects.ToString(args[1]) - if !ok { - return nil, objects.ErrInvalidArgumentType{ - Name: "second", - Expected: "string(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(), - } - } - - arr := &objects.Array{} - for _, res := range fn(s1, s2, i3) { - if len(res) > tengo.MaxStringLen { - return nil, objects.ErrStringLimit - } - - arr.Value = append(arr.Value, &objects.String{Value: res}) - } - - return arr, nil - } -} - -// FuncASSRI transform a function of 'func(string, string) int' signature into CallableFunc type. -func FuncASSRI(fn func(string, string) int) objects.CallableFunc { - return 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(), - } - } - - s2, ok := objects.ToString(args[1]) - if !ok { - return nil, objects.ErrInvalidArgumentType{ - Name: "second", - Expected: "string(compatible)", - Found: args[0].TypeName(), - } - } - - return &objects.Int{Value: int64(fn(s1, s2))}, nil - } -} - -// FuncASSRS transform a function of 'func(string, string) string' signature into CallableFunc type. -func FuncASSRS(fn func(string, string) string) objects.CallableFunc { - return 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(), - } - } - - s2, ok := objects.ToString(args[1]) - if !ok { - return nil, objects.ErrInvalidArgumentType{ - Name: "second", - Expected: "string(compatible)", - Found: args[1].TypeName(), - } - } - - s := fn(s1, s2) - - if len(s) > tengo.MaxStringLen { - return nil, objects.ErrStringLimit - } - - return &objects.String{Value: s}, nil - } -} - -// FuncASSRB transform a function of 'func(string, string) bool' signature into CallableFunc type. -func FuncASSRB(fn func(string, string) bool) objects.CallableFunc { - return 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(), - } - } - - s2, ok := objects.ToString(args[1]) - if !ok { - return nil, objects.ErrInvalidArgumentType{ - Name: "second", - Expected: "string(compatible)", - Found: args[1].TypeName(), - } - } - - if fn(s1, s2) { - return objects.TrueValue, nil - } - - return objects.FalseValue, nil - } -} - -// FuncASsSRS transform a function of 'func([]string, string) string' signature into CallableFunc type. -func FuncASsSRS(fn func([]string, string) string) objects.CallableFunc { - return func(args ...objects.Object) (objects.Object, error) { - if len(args) != 2 { - return nil, objects.ErrWrongNumArguments - } - - var ss1 []string - switch arg0 := args[0].(type) { - case *objects.Array: - for idx, a := range arg0.Value { - as, ok := objects.ToString(a) - if !ok { - return nil, objects.ErrInvalidArgumentType{ - Name: fmt.Sprintf("first[%d]", idx), - Expected: "string(compatible)", - Found: a.TypeName(), - } - } - ss1 = append(ss1, as) - } - case *objects.ImmutableArray: - for idx, a := range arg0.Value { - as, ok := objects.ToString(a) - if !ok { - return nil, objects.ErrInvalidArgumentType{ - Name: fmt.Sprintf("first[%d]", idx), - Expected: "string(compatible)", - Found: a.TypeName(), - } - } - ss1 = append(ss1, as) - } - default: - return nil, objects.ErrInvalidArgumentType{ - Name: "first", - Expected: "array", - Found: args[0].TypeName(), - } - } - - s2, ok := objects.ToString(args[1]) - if !ok { - return nil, objects.ErrInvalidArgumentType{ - Name: "second", - Expected: "string(compatible)", - Found: args[1].TypeName(), - } - } - - s := fn(ss1, s2) - if len(s) > tengo.MaxStringLen { - return nil, objects.ErrStringLimit - } - - return &objects.String{Value: s}, nil - } -} - -// FuncASI64RE transform a function of 'func(string, int64) error' signature -// into CallableFunc type. -func FuncASI64RE(fn func(string, int64) error) objects.CallableFunc { - return 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.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, i2)), nil - } -} - -// FuncAIIRE transform a function of 'func(int, int) error' signature -// into CallableFunc type. -func FuncAIIRE(fn func(int, int) error) objects.CallableFunc { - return 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.ErrInvalidArgumentType{ - Name: "first", - Expected: "int(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(), - } - } - - return wrapError(fn(i1, i2)), nil - } -} - -// FuncASIRS transform a function of 'func(string, int) string' signature -// into CallableFunc type. -func FuncASIRS(fn func(string, int) string) objects.CallableFunc { - return 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.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(), - } - } - - s := fn(s1, i2) - - if len(s) > tengo.MaxStringLen { - return nil, objects.ErrStringLimit - } - - return &objects.String{Value: s}, nil - } -} - -// FuncASIIRE transform a function of 'func(string, int, int) error' signature -// into CallableFunc type. -func FuncASIIRE(fn func(string, int, int) error) objects.CallableFunc { - return 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.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(), - } - } - - return wrapError(fn(s1, i2, i3)), nil - } -} - -// FuncAYRIE transform a function of 'func([]byte) (int, error)' signature -// into CallableFunc type. -func FuncAYRIE(fn func([]byte) (int, error)) objects.CallableFunc { - return 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.ErrInvalidArgumentType{ - Name: "first", - Expected: "bytes(compatible)", - Found: args[0].TypeName(), - } - } - - res, err := fn(y1) - if err != nil { - return wrapError(err), nil - } - - return &objects.Int{Value: int64(res)}, nil - } -} - -// FuncAYRS transform a function of 'func([]byte) string' signature -// into CallableFunc type. -func FuncAYRS(fn func([]byte) string) objects.CallableFunc { - return 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.ErrInvalidArgumentType{ - Name: "first", - Expected: "bytes(compatible)", - Found: args[0].TypeName(), - } - } - - res := fn(y1) - - return &objects.String{Value: res}, nil - } -} - -// FuncASRIE transform a function of 'func(string) (int, error)' signature -// into CallableFunc type. -func FuncASRIE(fn func(string) (int, error)) objects.CallableFunc { - return 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.ErrInvalidArgumentType{ - Name: "first", - Expected: "string(compatible)", - Found: args[0].TypeName(), - } - } - - res, err := fn(s1) - if err != nil { - return wrapError(err), nil - } - - return &objects.Int{Value: int64(res)}, nil - } -} - -// FuncASRYE transform a function of 'func(string) ([]byte, error)' signature -// into CallableFunc type. -func FuncASRYE(fn func(string) ([]byte, error)) objects.CallableFunc { - return 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.ErrInvalidArgumentType{ - Name: "first", - Expected: "string(compatible)", - Found: args[0].TypeName(), - } - } - - res, err := fn(s1) - if err != nil { - return wrapError(err), nil - } - - if len(res) > tengo.MaxBytesLen { - return nil, objects.ErrBytesLimit - } - - return &objects.Bytes{Value: res}, nil - } -} - -// FuncAIRSsE transform a function of 'func(int) ([]string, error)' signature -// into CallableFunc type. -func FuncAIRSsE(fn func(int) ([]string, error)) objects.CallableFunc { - return 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.ErrInvalidArgumentType{ - Name: "first", - Expected: "int(compatible)", - Found: args[0].TypeName(), - } - } - - res, err := fn(i1) - if err != nil { - return wrapError(err), nil - } - - arr := &objects.Array{} - for _, r := range res { - if len(r) > tengo.MaxStringLen { - return nil, objects.ErrStringLimit - } - - arr.Value = append(arr.Value, &objects.String{Value: r}) - } - - return arr, nil - } -} - -// FuncAIRS transform a function of 'func(int) string' signature -// into CallableFunc type. -func FuncAIRS(fn func(int) string) objects.CallableFunc { - return 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.ErrInvalidArgumentType{ - Name: "first", - Expected: "int(compatible)", - Found: args[0].TypeName(), - } - } - - s := fn(i1) - - if len(s) > tengo.MaxStringLen { - return nil, objects.ErrStringLimit - } - - return &objects.String{Value: s}, nil - } -} diff --git a/vendor/github.com/d5/tengo/stdlib/hex.go b/vendor/github.com/d5/tengo/stdlib/hex.go deleted file mode 100644 index acc29e6a..00000000 --- a/vendor/github.com/d5/tengo/stdlib/hex.go +++ /dev/null @@ -1,11 +0,0 @@ -package stdlib - -import ( - "encoding/hex" - "github.com/d5/tengo/objects" -) - -var hexModule = map[string]objects.Object{ - "encode": &objects.UserFunction{Value: FuncAYRS(hex.EncodeToString)}, - "decode": &objects.UserFunction{Value: FuncASRYE(hex.DecodeString)}, -} diff --git a/vendor/github.com/d5/tengo/stdlib/json.go b/vendor/github.com/d5/tengo/stdlib/json.go deleted file mode 100644 index f913dc48..00000000 --- a/vendor/github.com/d5/tengo/stdlib/json.go +++ /dev/null @@ -1,126 +0,0 @@ -package stdlib - -import ( - "bytes" - gojson "encoding/json" - - "github.com/d5/tengo/objects" - "github.com/d5/tengo/stdlib/json" -) - -var jsonModule = map[string]objects.Object{ - "decode": &objects.UserFunction{Name: "decode", Value: jsonDecode}, - "encode": &objects.UserFunction{Name: "encode", Value: jsonEncode}, - "indent": &objects.UserFunction{Name: "encode", Value: jsonIndent}, - "html_escape": &objects.UserFunction{Name: "html_escape", Value: jsonHTMLEscape}, -} - -func jsonDecode(args ...objects.Object) (ret objects.Object, err error) { - if len(args) != 1 { - return nil, objects.ErrWrongNumArguments - } - - switch o := args[0].(type) { - case *objects.Bytes: - v, err := json.Decode(o.Value) - if err != nil { - return &objects.Error{Value: &objects.String{Value: err.Error()}}, nil - } - return v, nil - case *objects.String: - v, err := json.Decode([]byte(o.Value)) - if err != nil { - return &objects.Error{Value: &objects.String{Value: err.Error()}}, nil - } - return v, nil - default: - return nil, objects.ErrInvalidArgumentType{ - Name: "first", - Expected: "bytes/string", - Found: args[0].TypeName(), - } - } -} - -func jsonEncode(args ...objects.Object) (ret objects.Object, err error) { - if len(args) != 1 { - return nil, objects.ErrWrongNumArguments - } - - b, err := json.Encode(args[0]) - if err != nil { - return &objects.Error{Value: &objects.String{Value: err.Error()}}, nil - } - - return &objects.Bytes{Value: b}, nil -} - -func jsonIndent(args ...objects.Object) (ret objects.Object, err error) { - if len(args) != 3 { - return nil, objects.ErrWrongNumArguments - } - - prefix, ok := objects.ToString(args[1]) - if !ok { - return nil, objects.ErrInvalidArgumentType{ - Name: "prefix", - Expected: "string(compatible)", - Found: args[1].TypeName(), - } - } - - indent, ok := objects.ToString(args[2]) - if !ok { - return nil, objects.ErrInvalidArgumentType{ - Name: "indent", - Expected: "string(compatible)", - Found: args[2].TypeName(), - } - } - - switch o := args[0].(type) { - case *objects.Bytes: - var dst bytes.Buffer - err := gojson.Indent(&dst, o.Value, prefix, indent) - if err != nil { - return &objects.Error{Value: &objects.String{Value: err.Error()}}, nil - } - return &objects.Bytes{Value: dst.Bytes()}, nil - case *objects.String: - var dst bytes.Buffer - err := gojson.Indent(&dst, []byte(o.Value), prefix, indent) - if err != nil { - return &objects.Error{Value: &objects.String{Value: err.Error()}}, nil - } - return &objects.Bytes{Value: dst.Bytes()}, nil - default: - return nil, objects.ErrInvalidArgumentType{ - Name: "first", - Expected: "bytes/string", - Found: args[0].TypeName(), - } - } -} - -func jsonHTMLEscape(args ...objects.Object) (ret objects.Object, err error) { - if len(args) != 1 { - return nil, objects.ErrWrongNumArguments - } - - switch o := args[0].(type) { - case *objects.Bytes: - var dst bytes.Buffer - gojson.HTMLEscape(&dst, o.Value) - return &objects.Bytes{Value: dst.Bytes()}, nil - case *objects.String: - var dst bytes.Buffer - gojson.HTMLEscape(&dst, []byte(o.Value)) - return &objects.Bytes{Value: dst.Bytes()}, nil - default: - return nil, objects.ErrInvalidArgumentType{ - Name: "first", - Expected: "bytes/string", - Found: args[0].TypeName(), - } - } -} diff --git a/vendor/github.com/d5/tengo/stdlib/math.go b/vendor/github.com/d5/tengo/stdlib/math.go deleted file mode 100644 index 08d82bdf..00000000 --- a/vendor/github.com/d5/tengo/stdlib/math.go +++ /dev/null @@ -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": &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)}, -} diff --git a/vendor/github.com/d5/tengo/stdlib/os.go b/vendor/github.com/d5/tengo/stdlib/os.go deleted file mode 100644 index a7890cc1..00000000 --- a/vendor/github.com/d5/tengo/stdlib/os.go +++ /dev/null @@ -1,492 +0,0 @@ -package stdlib - -import ( - "fmt" - "io" - "io/ioutil" - "os" - "os/exec" - - "github.com/d5/tengo" - "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{Name: "args", Value: osArgs}, // args() => array(string) - "chdir": &objects.UserFunction{Name: "chdir", Value: FuncASRE(os.Chdir)}, // chdir(dir string) => error - "chmod": osFuncASFmRE("chmod", 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: osExpandEnv}, // 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{Name: "lookup_env", Value: osLookupEnv}, // lookup_env(key string) => string/false - "mkdir": osFuncASFmRE("mkdir", os.Mkdir), // mkdir(name string, perm int) => error - "mkdir_all": osFuncASFmRE("mkdir_all", 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{Name: "create", Value: osCreate}, // create(name string) => imap(file)/error - "open": &objects.UserFunction{Name: "open", Value: osOpen}, // open(name string) => imap(file)/error - "open_file": &objects.UserFunction{Name: "open_file", Value: osOpenFile}, // open_file(name string, flag int, perm int) => imap(file)/error - "find_process": &objects.UserFunction{Name: "find_process", Value: osFindProcess}, // find_process(pid int) => imap(process)/error - "start_process": &objects.UserFunction{Name: "start_process", 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{Name: "exec", Value: osExec}, // exec(name, args...) => command - "stat": &objects.UserFunction{Name: "stat", Value: osStat}, // stat(name) => imap(fileinfo)/error - "read_file": &objects.UserFunction{Name: "read_file", 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 - } - - if len(bytes) > tengo.MaxBytesLen { - return nil, objects.ErrBytesLimit - } - - 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 { - if len(osArg) > tengo.MaxStringLen { - return nil, objects.ErrStringLimit - } - - arr.Value = append(arr.Value, &objects.String{Value: osArg}) - } - - return arr, nil -} - -func osFuncASFmRE(name string, fn func(string, os.FileMode) error) *objects.UserFunction { - return &objects.UserFunction{ - Name: name, - 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 - } - - if len(res) > tengo.MaxStringLen { - return nil, objects.ErrStringLimit - } - - return &objects.String{Value: res}, nil -} - -func osExpandEnv(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(), - } - } - - var vlen int - var failed bool - s := os.Expand(s1, func(k string) string { - if failed { - return "" - } - - v := os.Getenv(k) - - // this does not count the other texts that are not being replaced - // but the code checks the final length at the end - vlen += len(v) - if vlen > tengo.MaxStringLen { - failed = true - return "" - } - - return v - }) - - if failed || len(s) > tengo.MaxStringLen { - return nil, objects.ErrStringLimit - } - - return &objects.String{Value: s}, 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 -} diff --git a/vendor/github.com/d5/tengo/stdlib/os_exec.go b/vendor/github.com/d5/tengo/stdlib/os_exec.go deleted file mode 100644 index 5274c36a..00000000 --- a/vendor/github.com/d5/tengo/stdlib/os_exec.go +++ /dev/null @@ -1,113 +0,0 @@ -package stdlib - -import ( - "os/exec" - - "github.com/d5/tengo/objects" -) - -func makeOSExecCommand(cmd *exec.Cmd) *objects.ImmutableMap { - return &objects.ImmutableMap{ - Value: map[string]objects.Object{ - // combined_output() => bytes/error - "combined_output": &objects.UserFunction{Name: "combined_output", Value: FuncARYE(cmd.CombinedOutput)}, // - // output() => bytes/error - "output": &objects.UserFunction{Name: "output", Value: FuncARYE(cmd.Output)}, // - // run() => error - "run": &objects.UserFunction{Name: "run", Value: FuncARE(cmd.Run)}, // - // start() => error - "start": &objects.UserFunction{Name: "start", Value: FuncARE(cmd.Start)}, // - // wait() => error - "wait": &objects.UserFunction{Name: "wait", Value: FuncARE(cmd.Wait)}, // - // set_path(path string) - "set_path": &objects.UserFunction{ - Name: "set_path", - 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.ErrInvalidArgumentType{ - Name: "first", - Expected: "string(compatible)", - Found: args[0].TypeName(), - } - } - - cmd.Path = s1 - - return objects.UndefinedValue, nil - }, - }, - // set_dir(dir string) - "set_dir": &objects.UserFunction{ - Name: "set_dir", - 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.ErrInvalidArgumentType{ - Name: "first", - Expected: "string(compatible)", - Found: args[0].TypeName(), - } - } - - cmd.Dir = s1 - - return objects.UndefinedValue, nil - }, - }, - // set_env(env array(string)) - "set_env": &objects.UserFunction{ - Name: "set_env", - Value: func(args ...objects.Object) (objects.Object, error) { - if len(args) != 1 { - return nil, objects.ErrWrongNumArguments - } - - 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 = env - - return objects.UndefinedValue, nil - }, - }, - // process() => imap(process) - "process": &objects.UserFunction{ - Name: "process", - Value: func(args ...objects.Object) (ret objects.Object, err error) { - if len(args) != 0 { - return nil, objects.ErrWrongNumArguments - } - - return makeOSProcess(cmd.Process), nil - }, - }, - }, - } -} diff --git a/vendor/github.com/d5/tengo/stdlib/os_file.go b/vendor/github.com/d5/tengo/stdlib/os_file.go deleted file mode 100644 index ee9f625a..00000000 --- a/vendor/github.com/d5/tengo/stdlib/os_file.go +++ /dev/null @@ -1,96 +0,0 @@ -package stdlib - -import ( - "os" - - "github.com/d5/tengo/objects" -) - -func makeOSFile(file *os.File) *objects.ImmutableMap { - return &objects.ImmutableMap{ - Value: map[string]objects.Object{ - // chdir() => true/error - "chdir": &objects.UserFunction{Name: "chdir", Value: FuncARE(file.Chdir)}, // - // chown(uid int, gid int) => true/error - "chown": &objects.UserFunction{Name: "chown", Value: FuncAIIRE(file.Chown)}, // - // close() => error - "close": &objects.UserFunction{Name: "close", Value: FuncARE(file.Close)}, // - // name() => string - "name": &objects.UserFunction{Name: "name", Value: FuncARS(file.Name)}, // - // readdirnames(n int) => array(string)/error - "readdirnames": &objects.UserFunction{Name: "readdirnames", Value: FuncAIRSsE(file.Readdirnames)}, // - // sync() => error - "sync": &objects.UserFunction{Name: "sync", Value: FuncARE(file.Sync)}, // - // write(bytes) => int/error - "write": &objects.UserFunction{Name: "write", Value: FuncAYRIE(file.Write)}, // - // write(string) => int/error - "write_string": &objects.UserFunction{Name: "write_string", Value: FuncASRIE(file.WriteString)}, // - // read(bytes) => int/error - "read": &objects.UserFunction{Name: "read", Value: FuncAYRIE(file.Read)}, // - // chmod(mode int) => error - "chmod": &objects.UserFunction{ - Name: "chmod", - 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(), - } - } - - return wrapError(file.Chmod(os.FileMode(i1))), nil - }, - }, - // seek(offset int, whence int) => int/error - "seek": &objects.UserFunction{ - Name: "seek", - Value: func(args ...objects.Object) (ret objects.Object, err error) { - if len(args) != 2 { - return nil, objects.ErrWrongNumArguments - } - - i1, ok := objects.ToInt64(args[0]) - if !ok { - return nil, objects.ErrInvalidArgumentType{ - Name: "first", - Expected: "int(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(), - } - } - - res, err := file.Seek(i1, i2) - if err != nil { - return wrapError(err), nil - } - - return &objects.Int{Value: res}, nil - }, - }, - // stat() => imap(fileinfo)/error - "stat": &objects.UserFunction{ - Name: "start", - Value: func(args ...objects.Object) (ret objects.Object, err error) { - if len(args) != 0 { - return nil, objects.ErrWrongNumArguments - } - - return osStat(&objects.String{Value: file.Name()}) - }, - }, - }, - } -} diff --git a/vendor/github.com/d5/tengo/stdlib/os_process.go b/vendor/github.com/d5/tengo/stdlib/os_process.go deleted file mode 100644 index 801ccdef..00000000 --- a/vendor/github.com/d5/tengo/stdlib/os_process.go +++ /dev/null @@ -1,62 +0,0 @@ -package stdlib - -import ( - "os" - "syscall" - - "github.com/d5/tengo/objects" -) - -func makeOSProcessState(state *os.ProcessState) *objects.ImmutableMap { - return &objects.ImmutableMap{ - Value: map[string]objects.Object{ - "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)}, // - }, - } -} - -func makeOSProcess(proc *os.Process) *objects.ImmutableMap { - return &objects.ImmutableMap{ - Value: map[string]objects.Object{ - "kill": &objects.UserFunction{Name: "kill", Value: FuncARE(proc.Kill)}, // - "release": &objects.UserFunction{Name: "release", Value: FuncARE(proc.Release)}, // - "signal": &objects.UserFunction{ - Name: "signal", - 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(), - } - } - - return wrapError(proc.Signal(syscall.Signal(i1))), nil - }, - }, - "wait": &objects.UserFunction{ - Name: "wait", - Value: func(args ...objects.Object) (ret objects.Object, err error) { - if len(args) != 0 { - return nil, objects.ErrWrongNumArguments - } - - state, err := proc.Wait() - if err != nil { - return wrapError(err), nil - } - - return makeOSProcessState(state), nil - }, - }, - }, - } -} diff --git a/vendor/github.com/d5/tengo/stdlib/rand.go b/vendor/github.com/d5/tengo/stdlib/rand.go deleted file mode 100644 index 6efe1de8..00000000 --- a/vendor/github.com/d5/tengo/stdlib/rand.go +++ /dev/null @@ -1,102 +0,0 @@ -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{ - Name: "read", - 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{ - Name: "rand", - 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{ - Name: "read", - 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 - }, - }, - }, - } -} diff --git a/vendor/github.com/d5/tengo/stdlib/text.go b/vendor/github.com/d5/tengo/stdlib/text.go deleted file mode 100644 index 4b5729ec..00000000 --- a/vendor/github.com/d5/tengo/stdlib/text.go +++ /dev/null @@ -1,930 +0,0 @@ -package stdlib - -import ( - "fmt" - "regexp" - "strconv" - "strings" - "unicode/utf8" - - "github.com/d5/tengo" - "github.com/d5/tengo/objects" -) - -var textModule = map[string]objects.Object{ - "re_match": &objects.UserFunction{Name: "re_match", Value: textREMatch}, // re_match(pattern, text) => bool/error - "re_find": &objects.UserFunction{Name: "re_find", Value: textREFind}, // re_find(pattern, text, count) => [[{text:,begin:,end:}]]/undefined - "re_replace": &objects.UserFunction{Name: "re_replace", Value: textREReplace}, // re_replace(pattern, text, repl) => string/error - "re_split": &objects.UserFunction{Name: "re_split", Value: textRESplit}, // re_split(pattern, text, count) => [string]/error - "re_compile": &objects.UserFunction{Name: "re_compile", 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: textJoin}, // 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: textRepeat}, // repeat(s, count) => string - "replace": &objects.UserFunction{Name: "replace", Value: textReplace}, // replace(s, old, new, n) => string - "substr": &objects.UserFunction{Name: "substr", Value: textSubstring}, // substr(s, lower, upper) => 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 - "pad_left": &objects.UserFunction{Name: "pad_left", Value: textPadLeft}, // pad_left(s, pad_len, pad_with) => string - "pad_right": &objects.UserFunction{Name: "pad_right", Value: textPadRight}, // pad_right(s, pad_len, pad_with) => string - "trim": &objects.UserFunction{Name: "trim", Value: FuncASSRS(strings.Trim)}, // trim(s, cutset) => 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{Name: "format_bool", Value: textFormatBool}, // format_bool(b) => string - "format_float": &objects.UserFunction{Name: "format_float", Value: textFormatFloat}, // format_float(f, fmt, prec, bits) => string - "format_int": &objects.UserFunction{Name: "format_int", Value: textFormatInt}, // format_int(i, base) => string - "itoa": &objects.UserFunction{Name: "itoa", Value: FuncAIRS(strconv.Itoa)}, // itoa(i) => string - "parse_bool": &objects.UserFunction{Name: "parse_bool", Value: textParseBool}, // parse_bool(str) => bool/error - "parse_float": &objects.UserFunction{Name: "parse_float", Value: textParseFloat}, // parse_float(str, bits) => float/error - "parse_int": &objects.UserFunction{Name: "parse_int", 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 { - s, ok := doTextRegexpReplace(re, s2, s3) - if !ok { - return nil, objects.ErrStringLimit - } - - ret = &objects.String{Value: s} - } - - 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 - } - - s, ok := doTextReplace(s1, s2, s3, i4) - if !ok { - err = objects.ErrStringLimit - return - } - - ret = &objects.String{Value: s} - - return -} - -func textSubstring(args ...objects.Object) (ret objects.Object, err error) { - argslen := len(args) - if argslen != 2 && argslen != 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 - } - - i2, ok := objects.ToInt(args[1]) - if !ok { - err = objects.ErrInvalidArgumentType{ - Name: "second", - Expected: "int(compatible)", - Found: args[1].TypeName(), - } - return - } - - strlen := len(s1) - i3 := strlen - if argslen == 3 { - i3, ok = objects.ToInt(args[2]) - if !ok { - err = objects.ErrInvalidArgumentType{ - Name: "third", - Expected: "int(compatible)", - Found: args[2].TypeName(), - } - return - } - } - - if i2 > i3 { - err = objects.ErrInvalidIndexType - return - } - - if i2 < 0 { - i2 = 0 - } else if i2 > strlen { - i2 = strlen - } - - if i3 < 0 { - i3 = 0 - } else if i3 > strlen { - i3 = strlen - } - - ret = &objects.String{Value: s1[i2:i3]} - - return -} - -func textPadLeft(args ...objects.Object) (ret objects.Object, err error) { - argslen := len(args) - if argslen != 2 && argslen != 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 - } - - i2, ok := objects.ToInt(args[1]) - if !ok { - err = objects.ErrInvalidArgumentType{ - Name: "second", - Expected: "int(compatible)", - Found: args[1].TypeName(), - } - return - } - - if i2 > tengo.MaxStringLen { - return nil, objects.ErrStringLimit - } - - sLen := len(s1) - if sLen >= i2 { - ret = &objects.String{Value: s1} - return - } - - s3 := " " - if argslen == 3 { - s3, ok = objects.ToString(args[2]) - if !ok { - err = objects.ErrInvalidArgumentType{ - Name: "third", - Expected: "string(compatible)", - Found: args[2].TypeName(), - } - return - } - } - - padStrLen := len(s3) - if padStrLen == 0 { - ret = &objects.String{Value: s1} - return - } - - padCount := ((i2 - padStrLen) / padStrLen) + 1 - retStr := strings.Repeat(s3, int(padCount)) + s1 - ret = &objects.String{Value: retStr[len(retStr)-i2:]} - - return -} - -func textPadRight(args ...objects.Object) (ret objects.Object, err error) { - argslen := len(args) - if argslen != 2 && argslen != 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 - } - - i2, ok := objects.ToInt(args[1]) - if !ok { - err = objects.ErrInvalidArgumentType{ - Name: "second", - Expected: "int(compatible)", - Found: args[1].TypeName(), - } - return - } - - if i2 > tengo.MaxStringLen { - return nil, objects.ErrStringLimit - } - - sLen := len(s1) - if sLen >= i2 { - ret = &objects.String{Value: s1} - return - } - - s3 := " " - if argslen == 3 { - s3, ok = objects.ToString(args[2]) - if !ok { - err = objects.ErrInvalidArgumentType{ - Name: "third", - Expected: "string(compatible)", - Found: args[2].TypeName(), - } - return - } - } - - padStrLen := len(s3) - if padStrLen == 0 { - ret = &objects.String{Value: s1} - return - } - - padCount := ((i2 - padStrLen) / padStrLen) + 1 - retStr := s1 + strings.Repeat(s3, int(padCount)) - ret = &objects.String{Value: retStr[:i2]} - - return -} - -func textRepeat(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.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(), - } - } - - if len(s1)*i2 > tengo.MaxStringLen { - return nil, objects.ErrStringLimit - } - - return &objects.String{Value: strings.Repeat(s1, i2)}, nil -} - -func textJoin(args ...objects.Object) (ret objects.Object, err error) { - if len(args) != 2 { - return nil, objects.ErrWrongNumArguments - } - - var slen int - var ss1 []string - switch arg0 := args[0].(type) { - case *objects.Array: - for idx, a := range arg0.Value { - as, ok := objects.ToString(a) - if !ok { - return nil, objects.ErrInvalidArgumentType{ - Name: fmt.Sprintf("first[%d]", idx), - Expected: "string(compatible)", - Found: a.TypeName(), - } - } - slen += len(as) - ss1 = append(ss1, as) - } - case *objects.ImmutableArray: - for idx, a := range arg0.Value { - as, ok := objects.ToString(a) - if !ok { - return nil, objects.ErrInvalidArgumentType{ - Name: fmt.Sprintf("first[%d]", idx), - Expected: "string(compatible)", - Found: a.TypeName(), - } - } - slen += len(as) - ss1 = append(ss1, as) - } - default: - return nil, objects.ErrInvalidArgumentType{ - Name: "first", - Expected: "array", - Found: args[0].TypeName(), - } - } - - s2, ok := objects.ToString(args[1]) - if !ok { - return nil, objects.ErrInvalidArgumentType{ - Name: "second", - Expected: "string(compatible)", - Found: args[1].TypeName(), - } - } - - // make sure output length does not exceed the limit - if slen+len(s2)*(len(ss1)-1) > tengo.MaxStringLen { - return nil, objects.ErrStringLimit - } - - return &objects.String{Value: strings.Join(ss1, s2)}, nil -} - -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 -} - -// Modified implementation of strings.Replace -// to limit the maximum length of output string. -func doTextReplace(s, old, new string, n int) (string, bool) { - if old == new || n == 0 { - return s, true // avoid allocation - } - - // Compute number of replacements. - if m := strings.Count(s, old); m == 0 { - return s, true // avoid allocation - } else if n < 0 || m < n { - n = m - } - - // Apply replacements to buffer. - t := make([]byte, len(s)+n*(len(new)-len(old))) - w := 0 - start := 0 - for i := 0; i < n; i++ { - j := start - if len(old) == 0 { - if i > 0 { - _, wid := utf8.DecodeRuneInString(s[start:]) - j += wid - } - } else { - j += strings.Index(s[start:], old) - } - - ssj := s[start:j] - if w+len(ssj)+len(new) > tengo.MaxStringLen { - return "", false - } - - w += copy(t[w:], ssj) - w += copy(t[w:], new) - start = j + len(old) - } - - ss := s[start:] - if w+len(ss) > tengo.MaxStringLen { - return "", false - } - - w += copy(t[w:], ss) - - return string(t[0:w]), true -} diff --git a/vendor/github.com/d5/tengo/stdlib/times.go b/vendor/github.com/d5/tengo/stdlib/times.go deleted file mode 100644 index 111c877a..00000000 --- a/vendor/github.com/d5/tengo/stdlib/times.go +++ /dev/null @@ -1,989 +0,0 @@ -package stdlib - -import ( - "time" - - "github.com/d5/tengo" - "github.com/d5/tengo/objects" -) - -var timesModule = map[string]objects.Object{ - "format_ansic": &objects.String{Value: time.ANSIC}, - "format_unix_date": &objects.String{Value: time.UnixDate}, - "format_ruby_date": &objects.String{Value: time.RubyDate}, - "format_rfc822": &objects.String{Value: time.RFC822}, - "format_rfc822z": &objects.String{Value: time.RFC822Z}, - "format_rfc850": &objects.String{Value: time.RFC850}, - "format_rfc1123": &objects.String{Value: time.RFC1123}, - "format_rfc1123z": &objects.String{Value: time.RFC1123Z}, - "format_rfc3339": &objects.String{Value: time.RFC3339}, - "format_rfc3339_nano": &objects.String{Value: time.RFC3339Nano}, - "format_kitchen": &objects.String{Value: time.Kitchen}, - "format_stamp": &objects.String{Value: time.Stamp}, - "format_stamp_milli": &objects.String{Value: time.StampMilli}, - "format_stamp_micro": &objects.String{Value: time.StampMicro}, - "format_stamp_nano": &objects.String{Value: time.StampNano}, - "nanosecond": &objects.Int{Value: int64(time.Nanosecond)}, - "microsecond": &objects.Int{Value: int64(time.Microsecond)}, - "millisecond": &objects.Int{Value: int64(time.Millisecond)}, - "second": &objects.Int{Value: int64(time.Second)}, - "minute": &objects.Int{Value: int64(time.Minute)}, - "hour": &objects.Int{Value: int64(time.Hour)}, - "january": &objects.Int{Value: int64(time.January)}, - "february": &objects.Int{Value: int64(time.February)}, - "march": &objects.Int{Value: int64(time.March)}, - "april": &objects.Int{Value: int64(time.April)}, - "may": &objects.Int{Value: int64(time.May)}, - "june": &objects.Int{Value: int64(time.June)}, - "july": &objects.Int{Value: int64(time.July)}, - "august": &objects.Int{Value: int64(time.August)}, - "september": &objects.Int{Value: int64(time.September)}, - "october": &objects.Int{Value: int64(time.October)}, - "november": &objects.Int{Value: int64(time.November)}, - "december": &objects.Int{Value: int64(time.December)}, - "sleep": &objects.UserFunction{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) { - if len(args) != 1 { - err = objects.ErrWrongNumArguments - return - } - - i1, ok := objects.ToInt64(args[0]) - if !ok { - err = objects.ErrInvalidArgumentType{ - Name: "first", - Expected: "int(compatible)", - Found: args[0].TypeName(), - } - return - } - - time.Sleep(time.Duration(i1)) - ret = objects.UndefinedValue - - return -} - -func timesParseDuration(args ...objects.Object) (ret objects.Object, err error) { - if len(args) != 1 { - err = objects.ErrWrongNumArguments - return - } - - s1, ok := objects.ToString(args[0]) - if !ok { - err = objects.ErrInvalidArgumentType{ - Name: "first", - Expected: "string(compatible)", - Found: args[0].TypeName(), - } - return - } - - dur, err := time.ParseDuration(s1) - if err != nil { - ret = wrapError(err) - return - } - - ret = &objects.Int{Value: int64(dur)} - - return -} - -func timesSince(args ...objects.Object) (ret objects.Object, err error) { - if len(args) != 1 { - err = objects.ErrWrongNumArguments - return - } - - t1, ok := objects.ToTime(args[0]) - if !ok { - err = objects.ErrInvalidArgumentType{ - Name: "first", - Expected: "time(compatible)", - Found: args[0].TypeName(), - } - return - } - - ret = &objects.Int{Value: int64(time.Since(t1))} - - return -} - -func timesUntil(args ...objects.Object) (ret objects.Object, err error) { - if len(args) != 1 { - err = objects.ErrWrongNumArguments - return - } - - t1, ok := objects.ToTime(args[0]) - if !ok { - err = objects.ErrInvalidArgumentType{ - Name: "first", - Expected: "time(compatible)", - Found: args[0].TypeName(), - } - return - } - - ret = &objects.Int{Value: int64(time.Until(t1))} - - return -} - -func timesDurationHours(args ...objects.Object) (ret objects.Object, err error) { - if len(args) != 1 { - err = objects.ErrWrongNumArguments - return - } - - i1, ok := objects.ToInt64(args[0]) - if !ok { - err = objects.ErrInvalidArgumentType{ - Name: "first", - Expected: "int(compatible)", - Found: args[0].TypeName(), - } - return - } - - ret = &objects.Float{Value: time.Duration(i1).Hours()} - - return -} - -func timesDurationMinutes(args ...objects.Object) (ret objects.Object, err error) { - if len(args) != 1 { - err = objects.ErrWrongNumArguments - return - } - - i1, ok := objects.ToInt64(args[0]) - if !ok { - err = objects.ErrInvalidArgumentType{ - Name: "first", - Expected: "int(compatible)", - Found: args[0].TypeName(), - } - return - } - - ret = &objects.Float{Value: time.Duration(i1).Minutes()} - - return -} - -func timesDurationNanoseconds(args ...objects.Object) (ret objects.Object, err error) { - if len(args) != 1 { - err = objects.ErrWrongNumArguments - return - } - - i1, ok := objects.ToInt64(args[0]) - if !ok { - err = objects.ErrInvalidArgumentType{ - Name: "first", - Expected: "int(compatible)", - Found: args[0].TypeName(), - } - return - } - - ret = &objects.Int{Value: time.Duration(i1).Nanoseconds()} - - return -} - -func timesDurationSeconds(args ...objects.Object) (ret objects.Object, err error) { - if len(args) != 1 { - err = objects.ErrWrongNumArguments - return - } - - i1, ok := objects.ToInt64(args[0]) - if !ok { - err = objects.ErrInvalidArgumentType{ - Name: "first", - Expected: "int(compatible)", - Found: args[0].TypeName(), - } - return - } - - ret = &objects.Float{Value: time.Duration(i1).Seconds()} - - return -} - -func timesDurationString(args ...objects.Object) (ret objects.Object, err error) { - if len(args) != 1 { - err = objects.ErrWrongNumArguments - return - } - - i1, ok := objects.ToInt64(args[0]) - if !ok { - err = objects.ErrInvalidArgumentType{ - Name: "first", - Expected: "int(compatible)", - Found: args[0].TypeName(), - } - return - } - - ret = &objects.String{Value: time.Duration(i1).String()} - - return -} - -func timesMonthString(args ...objects.Object) (ret objects.Object, err error) { - if len(args) != 1 { - err = objects.ErrWrongNumArguments - return - } - - i1, ok := objects.ToInt64(args[0]) - if !ok { - err = objects.ErrInvalidArgumentType{ - Name: "first", - Expected: "int(compatible)", - Found: args[0].TypeName(), - } - return - } - - ret = &objects.String{Value: time.Month(i1).String()} - - return -} - -func timesDate(args ...objects.Object) (ret objects.Object, err error) { - if len(args) != 7 { - err = objects.ErrWrongNumArguments - return - } - - i1, ok := objects.ToInt(args[0]) - if !ok { - err = objects.ErrInvalidArgumentType{ - Name: "first", - Expected: "int(compatible)", - 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 - } - i4, ok := objects.ToInt(args[3]) - if !ok { - err = objects.ErrInvalidArgumentType{ - Name: "fourth", - Expected: "int(compatible)", - Found: args[3].TypeName(), - } - return - } - i5, ok := objects.ToInt(args[4]) - if !ok { - err = objects.ErrInvalidArgumentType{ - Name: "fifth", - Expected: "int(compatible)", - Found: args[4].TypeName(), - } - return - } - i6, ok := objects.ToInt(args[5]) - if !ok { - err = objects.ErrInvalidArgumentType{ - Name: "sixth", - Expected: "int(compatible)", - Found: args[5].TypeName(), - } - return - } - i7, ok := objects.ToInt(args[6]) - if !ok { - err = objects.ErrInvalidArgumentType{ - Name: "seventh", - Expected: "int(compatible)", - Found: args[6].TypeName(), - } - return - } - - ret = &objects.Time{Value: time.Date(i1, time.Month(i2), i3, i4, i5, i6, i7, time.Now().Location())} - - return -} - -func timesNow(args ...objects.Object) (ret objects.Object, err error) { - if len(args) != 0 { - err = objects.ErrWrongNumArguments - return - } - - ret = &objects.Time{Value: time.Now()} - - return -} - -func timesParse(args ...objects.Object) (ret objects.Object, err error) { - if len(args) != 2 { - err = objects.ErrWrongNumArguments - return - } - - s1, ok := objects.ToString(args[0]) - if !ok { - err = objects.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 - } - - parsed, err := time.Parse(s1, s2) - if err != nil { - ret = wrapError(err) - return - } - - ret = &objects.Time{Value: parsed} - - return -} - -func timesUnix(args ...objects.Object) (ret objects.Object, err error) { - if len(args) != 2 { - err = objects.ErrWrongNumArguments - return - } - - i1, ok := objects.ToInt64(args[0]) - if !ok { - err = objects.ErrInvalidArgumentType{ - Name: "first", - Expected: "int(compatible)", - Found: args[0].TypeName(), - } - return - } - - i2, ok := objects.ToInt64(args[1]) - if !ok { - err = objects.ErrInvalidArgumentType{ - Name: "second", - Expected: "int(compatible)", - Found: args[1].TypeName(), - } - return - } - - ret = &objects.Time{Value: time.Unix(i1, i2)} - - return -} - -func timesAdd(args ...objects.Object) (ret objects.Object, err error) { - if len(args) != 2 { - err = objects.ErrWrongNumArguments - return - } - - t1, ok := objects.ToTime(args[0]) - if !ok { - err = objects.ErrInvalidArgumentType{ - Name: "first", - Expected: "time(compatible)", - Found: args[0].TypeName(), - } - return - } - - i2, ok := objects.ToInt64(args[1]) - if !ok { - err = objects.ErrInvalidArgumentType{ - Name: "second", - Expected: "int(compatible)", - Found: args[1].TypeName(), - } - return - } - - ret = &objects.Time{Value: t1.Add(time.Duration(i2))} - - return -} - -func timesSub(args ...objects.Object) (ret objects.Object, err error) { - if len(args) != 2 { - err = objects.ErrWrongNumArguments - return - } - - t1, ok := objects.ToTime(args[0]) - if !ok { - err = objects.ErrInvalidArgumentType{ - Name: "first", - Expected: "time(compatible)", - Found: args[0].TypeName(), - } - return - } - - t2, ok := objects.ToTime(args[1]) - if !ok { - err = objects.ErrInvalidArgumentType{ - Name: "second", - Expected: "time(compatible)", - Found: args[1].TypeName(), - } - return - } - - ret = &objects.Int{Value: int64(t1.Sub(t2))} - - return -} - -func timesAddDate(args ...objects.Object) (ret objects.Object, err error) { - if len(args) != 4 { - err = objects.ErrWrongNumArguments - return - } - - t1, ok := objects.ToTime(args[0]) - if !ok { - err = objects.ErrInvalidArgumentType{ - Name: "first", - Expected: "time(compatible)", - 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 - } - - i4, ok := objects.ToInt(args[3]) - if !ok { - err = objects.ErrInvalidArgumentType{ - Name: "fourth", - Expected: "int(compatible)", - Found: args[3].TypeName(), - } - return - } - - ret = &objects.Time{Value: t1.AddDate(i2, i3, i4)} - - return -} - -func timesAfter(args ...objects.Object) (ret objects.Object, err error) { - if len(args) != 2 { - err = objects.ErrWrongNumArguments - return - } - - t1, ok := objects.ToTime(args[0]) - if !ok { - err = objects.ErrInvalidArgumentType{ - Name: "first", - Expected: "time(compatible)", - Found: args[0].TypeName(), - } - return - } - - t2, ok := objects.ToTime(args[1]) - if !ok { - err = objects.ErrInvalidArgumentType{ - Name: "second", - Expected: "time(compatible)", - Found: args[1].TypeName(), - } - return - } - - if t1.After(t2) { - ret = objects.TrueValue - } else { - ret = objects.FalseValue - } - - return -} - -func timesBefore(args ...objects.Object) (ret objects.Object, err error) { - if len(args) != 2 { - err = objects.ErrWrongNumArguments - return - } - - t1, ok := objects.ToTime(args[0]) - if !ok { - err = objects.ErrInvalidArgumentType{ - Name: "first", - Expected: "time(compatible)", - Found: args[0].TypeName(), - } - return - } - - t2, ok := objects.ToTime(args[1]) - if !ok { - err = objects.ErrInvalidArgumentType{ - Name: "second", - Expected: "time(compatible)", - Found: args[0].TypeName(), - } - return - } - - if t1.Before(t2) { - ret = objects.TrueValue - } else { - ret = objects.FalseValue - } - - return -} - -func timesTimeYear(args ...objects.Object) (ret objects.Object, err error) { - if len(args) != 1 { - err = objects.ErrWrongNumArguments - return - } - - t1, ok := objects.ToTime(args[0]) - if !ok { - err = objects.ErrInvalidArgumentType{ - Name: "first", - Expected: "time(compatible)", - Found: args[0].TypeName(), - } - return - } - - ret = &objects.Int{Value: int64(t1.Year())} - - return -} - -func timesTimeMonth(args ...objects.Object) (ret objects.Object, err error) { - if len(args) != 1 { - err = objects.ErrWrongNumArguments - return - } - - t1, ok := objects.ToTime(args[0]) - if !ok { - err = objects.ErrInvalidArgumentType{ - Name: "first", - Expected: "time(compatible)", - Found: args[0].TypeName(), - } - return - } - - ret = &objects.Int{Value: int64(t1.Month())} - - return -} - -func timesTimeDay(args ...objects.Object) (ret objects.Object, err error) { - if len(args) != 1 { - err = objects.ErrWrongNumArguments - return - } - - t1, ok := objects.ToTime(args[0]) - if !ok { - err = objects.ErrInvalidArgumentType{ - Name: "first", - Expected: "time(compatible)", - Found: args[0].TypeName(), - } - return - } - - ret = &objects.Int{Value: int64(t1.Day())} - - return -} - -func timesTimeWeekday(args ...objects.Object) (ret objects.Object, err error) { - if len(args) != 1 { - err = objects.ErrWrongNumArguments - return - } - - t1, ok := objects.ToTime(args[0]) - if !ok { - err = objects.ErrInvalidArgumentType{ - Name: "first", - Expected: "time(compatible)", - Found: args[0].TypeName(), - } - return - } - - ret = &objects.Int{Value: int64(t1.Weekday())} - - return -} - -func timesTimeHour(args ...objects.Object) (ret objects.Object, err error) { - if len(args) != 1 { - err = objects.ErrWrongNumArguments - return - } - - t1, ok := objects.ToTime(args[0]) - if !ok { - err = objects.ErrInvalidArgumentType{ - Name: "first", - Expected: "time(compatible)", - Found: args[0].TypeName(), - } - return - } - - ret = &objects.Int{Value: int64(t1.Hour())} - - return -} - -func timesTimeMinute(args ...objects.Object) (ret objects.Object, err error) { - if len(args) != 1 { - err = objects.ErrWrongNumArguments - return - } - - t1, ok := objects.ToTime(args[0]) - if !ok { - err = objects.ErrInvalidArgumentType{ - Name: "first", - Expected: "time(compatible)", - Found: args[0].TypeName(), - } - return - } - - ret = &objects.Int{Value: int64(t1.Minute())} - - return -} - -func timesTimeSecond(args ...objects.Object) (ret objects.Object, err error) { - if len(args) != 1 { - err = objects.ErrWrongNumArguments - return - } - - t1, ok := objects.ToTime(args[0]) - if !ok { - err = objects.ErrInvalidArgumentType{ - Name: "first", - Expected: "time(compatible)", - Found: args[0].TypeName(), - } - return - } - - ret = &objects.Int{Value: int64(t1.Second())} - - return -} - -func timesTimeNanosecond(args ...objects.Object) (ret objects.Object, err error) { - if len(args) != 1 { - err = objects.ErrWrongNumArguments - return - } - - t1, ok := objects.ToTime(args[0]) - if !ok { - err = objects.ErrInvalidArgumentType{ - Name: "first", - Expected: "time(compatible)", - Found: args[0].TypeName(), - } - return - } - - ret = &objects.Int{Value: int64(t1.Nanosecond())} - - return -} - -func timesTimeUnix(args ...objects.Object) (ret objects.Object, err error) { - if len(args) != 1 { - err = objects.ErrWrongNumArguments - return - } - - t1, ok := objects.ToTime(args[0]) - if !ok { - err = objects.ErrInvalidArgumentType{ - Name: "first", - Expected: "time(compatible)", - Found: args[0].TypeName(), - } - return - } - - ret = &objects.Int{Value: int64(t1.Unix())} - - return -} - -func timesTimeUnixNano(args ...objects.Object) (ret objects.Object, err error) { - if len(args) != 1 { - err = objects.ErrWrongNumArguments - return - } - - t1, ok := objects.ToTime(args[0]) - if !ok { - err = objects.ErrInvalidArgumentType{ - Name: "first", - Expected: "time(compatible)", - Found: args[0].TypeName(), - } - return - } - - ret = &objects.Int{Value: int64(t1.UnixNano())} - - return -} - -func timesTimeFormat(args ...objects.Object) (ret objects.Object, err error) { - if len(args) != 2 { - err = objects.ErrWrongNumArguments - return - } - - t1, ok := objects.ToTime(args[0]) - if !ok { - err = objects.ErrInvalidArgumentType{ - Name: "first", - Expected: "time(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 - } - - s := t1.Format(s2) - if len(s) > tengo.MaxStringLen { - - return nil, objects.ErrStringLimit - } - - ret = &objects.String{Value: s} - - return -} - -func timesIsZero(args ...objects.Object) (ret objects.Object, err error) { - if len(args) != 1 { - err = objects.ErrWrongNumArguments - return - } - - t1, ok := objects.ToTime(args[0]) - if !ok { - err = objects.ErrInvalidArgumentType{ - Name: "first", - Expected: "time(compatible)", - Found: args[0].TypeName(), - } - return - } - - if t1.IsZero() { - ret = objects.TrueValue - } else { - ret = objects.FalseValue - } - - return -} - -func timesToLocal(args ...objects.Object) (ret objects.Object, err error) { - if len(args) != 1 { - err = objects.ErrWrongNumArguments - return - } - - t1, ok := objects.ToTime(args[0]) - if !ok { - err = objects.ErrInvalidArgumentType{ - Name: "first", - Expected: "time(compatible)", - Found: args[0].TypeName(), - } - return - } - - ret = &objects.Time{Value: t1.Local()} - - return -} - -func timesToUTC(args ...objects.Object) (ret objects.Object, err error) { - if len(args) != 1 { - err = objects.ErrWrongNumArguments - return - } - - t1, ok := objects.ToTime(args[0]) - if !ok { - err = objects.ErrInvalidArgumentType{ - Name: "first", - Expected: "time(compatible)", - Found: args[0].TypeName(), - } - return - } - - ret = &objects.Time{Value: t1.UTC()} - - return -} - -func timesTimeLocation(args ...objects.Object) (ret objects.Object, err error) { - if len(args) != 1 { - err = objects.ErrWrongNumArguments - return - } - - t1, ok := objects.ToTime(args[0]) - if !ok { - err = objects.ErrInvalidArgumentType{ - Name: "first", - Expected: "time(compatible)", - Found: args[0].TypeName(), - } - return - } - - ret = &objects.String{Value: t1.Location().String()} - - return -} - -func timesTimeString(args ...objects.Object) (ret objects.Object, err error) { - if len(args) != 1 { - err = objects.ErrWrongNumArguments - return - } - - t1, ok := objects.ToTime(args[0]) - if !ok { - err = objects.ErrInvalidArgumentType{ - Name: "first", - Expected: "time(compatible)", - Found: args[0].TypeName(), - } - return - } - - ret = &objects.String{Value: t1.String()} - - return -} diff --git a/vendor/github.com/d5/tengo/tengo.go b/vendor/github.com/d5/tengo/tengo.go deleted file mode 100644 index a883bbd7..00000000 --- a/vendor/github.com/d5/tengo/tengo.go +++ /dev/null @@ -1,11 +0,0 @@ -package tengo - -var ( - // MaxStringLen is the maximum byte-length for string value. - // Note this limit applies to all compiler/VM instances in the process. - MaxStringLen = 2147483647 - - // MaxBytesLen is the maximum length for bytes value. - // Note this limit applies to all compiler/VM instances in the process. - MaxBytesLen = 2147483647 -) diff --git a/vendor/github.com/d5/tengo/.gitignore b/vendor/github.com/d5/tengo/v2/.gitignore similarity index 100% rename from vendor/github.com/d5/tengo/.gitignore rename to vendor/github.com/d5/tengo/v2/.gitignore diff --git a/vendor/github.com/d5/tengo/.goreleaser.yml b/vendor/github.com/d5/tengo/v2/.goreleaser.yml similarity index 63% rename from vendor/github.com/d5/tengo/.goreleaser.yml rename to vendor/github.com/d5/tengo/v2/.goreleaser.yml index 9b1dffec..1bd14324 100644 --- a/vendor/github.com/d5/tengo/.goreleaser.yml +++ b/vendor/github.com/d5/tengo/v2/.goreleaser.yml @@ -11,15 +11,6 @@ builds: - darwin - linux - windows - - env: - - CGO_ENABLED=0 - main: ./cmd/tengomin/main.go - id: tengomin - binary: tengomin - goos: - - darwin - - linux - - windows archive: files: - none* diff --git a/vendor/github.com/d5/tengo/LICENSE b/vendor/github.com/d5/tengo/v2/LICENSE similarity index 100% rename from vendor/github.com/d5/tengo/LICENSE rename to vendor/github.com/d5/tengo/v2/LICENSE diff --git a/vendor/github.com/d5/tengo/Makefile b/vendor/github.com/d5/tengo/v2/Makefile similarity index 72% rename from vendor/github.com/d5/tengo/Makefile rename to vendor/github.com/d5/tengo/v2/Makefile index 99daafd1..793bc129 100644 --- a/vendor/github.com/d5/tengo/Makefile +++ b/vendor/github.com/d5/tengo/v2/Makefile @@ -1,13 +1,10 @@ -vet: - go vet ./... - generate: go generate ./... lint: golint -set_exit_status ./... -test: generate vet lint +test: generate lint go test -race -cover ./... fmt: diff --git a/vendor/github.com/d5/tengo/README.md b/vendor/github.com/d5/tengo/v2/README.md similarity index 61% rename from vendor/github.com/d5/tengo/README.md rename to vendor/github.com/d5/tengo/v2/README.md index 6a35cfd1..92277cfe 100644 --- a/vendor/github.com/d5/tengo/README.md +++ b/vendor/github.com/d5/tengo/v2/README.md @@ -1,21 +1,21 @@

- +

# The Tengo Language -[![GoDoc](https://godoc.org/github.com/d5/tengo?status.svg)](https://godoc.org/github.com/d5/tengo/script) +[![GoDoc](https://godoc.org/github.com/d5/tengo?status.svg)](https://godoc.org/github.com/d5/tengo) [![Go Report Card](https://goreportcard.com/badge/github.com/d5/tengo)](https://goreportcard.com/report/github.com/d5/tengo) -[![Build Status](https://travis-ci.org/d5/tengo.svg?branch=master)](https://travis-ci.org/d5/tengo) +[![CircleCI](https://circleci.com/gh/d5/tengo.svg?style=svg)](https://circleci.com/gh/d5/tengo) [![Sourcegraph](https://sourcegraph.com/github.com/d5/tengo/-/badge.svg)](https://sourcegraph.com/github.com/d5/tengo?badge) **Tengo is a small, dynamic, fast, secure script language for Go.** -Tengo is **[fast](#benchmark)** and secure because it's compiled/executed as bytecode on stack-based VM that's written in native Go. +Tengo is **[fast](#benchmark)** and secure because it's compiled/executed as +bytecode on stack-based VM that's written in native Go. ```golang /* The Tengo Language */ - fmt := import("fmt") each := func(seq, fn) { @@ -31,19 +31,24 @@ fmt.println(sum(0, [1, 2, 3])) // "6" fmt.println(sum("", [1, 2, 3])) // "123" ``` -> Run this code in the [Playground](https://tengolang.com/?s=0c8d5d0d88f2795a7093d7f35ae12c3afa17bea3) +> Test this Tengo code in the +> [Tengo Playground](https://tengolang.com/?s=0c8d5d0d88f2795a7093d7f35ae12c3afa17bea3) ## Features -- Simple and highly readable [Syntax](https://github.com/d5/tengo/blob/master/docs/tutorial.md) +- Simple and highly readable + [Syntax](https://github.com/d5/tengo/blob/master/docs/tutorial.md) - Dynamic typing with type coercion - Higher-order functions and closures - Immutable values - - Garbage collection -- [Securely Embeddable](https://github.com/d5/tengo/blob/master/docs/interoperability.md) and [Extensible](https://github.com/d5/tengo/blob/master/docs/objects.md) +- [Securely Embeddable](https://github.com/d5/tengo/blob/master/docs/interoperability.md) + and [Extensible](https://github.com/d5/tengo/blob/master/docs/objects.md) - Compiler/runtime written in native Go _(no external deps or cgo)_ -- Executable as a [standalone](https://github.com/d5/tengo/blob/master/docs/tengo-cli.md) language / REPL -- Use cases: rules engine, [state machine](https://github.com/d5/go-fsm), [gaming](https://github.com/d5/pbr), data pipeline, [transpiler](https://github.com/d5/tengo2lua) +- Executable as a + [standalone](https://github.com/d5/tengo/blob/master/docs/tengo-cli.md) + language / REPL +- Use cases: rules engine, [state machine](https://github.com/d5/go-fsm), + data pipeline, [transpiler](https://github.com/d5/tengo2lua) ## Benchmark @@ -61,16 +66,69 @@ fmt.println(sum("", [1, 2, 3])) // "123" | [otto](https://github.com/robertkrimen/otto) | `68,377ms` | `11ms` | JS Interpreter on Go | | [Anko](https://github.com/mattn/anko) | `92,579ms` | `18ms` | Interpreter on Go | -_* [fib(35)](https://github.com/d5/tengobench/blob/master/code/fib.tengo): Fibonacci(35)_ -_* [fibt(35)](https://github.com/d5/tengobench/blob/master/code/fibtc.tengo): [tail-call](https://en.wikipedia.org/wiki/Tail_call) version of Fibonacci(35)_ +_* [fib(35)](https://github.com/d5/tengobench/blob/master/code/fib.tengo): +Fibonacci(35)_ +_* [fibt(35)](https://github.com/d5/tengobench/blob/master/code/fibtc.tengo): +[tail-call](https://en.wikipedia.org/wiki/Tail_call) version of Fibonacci(35)_ _* **Go** does not read the source code from file, while all other cases do_ _* See [here](https://github.com/d5/tengobench) for commands/codes used_ +## Quick Start + +A simple Go example code that compiles/runs Tengo script code with some input/output values: + +```golang +package main + +import ( + "context" + "fmt" + + "github.com/d5/tengo/v2" +) + +func main() { + // Tengo script code + src := ` +each := func(seq, fn) { + for x in seq { fn(x) } +} + +sum := 0 +mul := 1 +each([a, b, c, d], func(x) { + sum += x + mul *= x +})` + + // create a new Script instance + script := tengo.NewScript([]byte(src)) + + // set values + _ = script.Add("a", 1) + _ = script.Add("b", 9) + _ = script.Add("c", 8) + _ = script.Add("d", 4) + + // run the script + compiled, err := script.RunContext(context.Background()) + if err != nil { + panic(err) + } + + // retrieve values + sum := compiled.Get("sum") + mul := compiled.Get("mul") + fmt.Println(sum, mul) // "22 288" +} +``` + ## References - [Language Syntax](https://github.com/d5/tengo/blob/master/docs/tutorial.md) - [Object Types](https://github.com/d5/tengo/blob/master/docs/objects.md) -- [Runtime Types](https://github.com/d5/tengo/blob/master/docs/runtime-types.md) and [Operators](https://github.com/d5/tengo/blob/master/docs/operators.md) +- [Runtime Types](https://github.com/d5/tengo/blob/master/docs/runtime-types.md) + and [Operators](https://github.com/d5/tengo/blob/master/docs/operators.md) - [Builtin Functions](https://github.com/d5/tengo/blob/master/docs/builtins.md) - [Interoperability](https://github.com/d5/tengo/blob/master/docs/interoperability.md) - [Tengo CLI](https://github.com/d5/tengo/blob/master/docs/tengo-cli.md) diff --git a/vendor/github.com/d5/tengo/v2/builtins.go b/vendor/github.com/d5/tengo/v2/builtins.go new file mode 100644 index 00000000..87f7fc3d --- /dev/null +++ b/vendor/github.com/d5/tengo/v2/builtins.go @@ -0,0 +1,502 @@ +package tengo + +var builtinFuncs = []*BuiltinFunction{ + { + Name: "len", + Value: builtinLen, + }, + { + Name: "copy", + Value: builtinCopy, + }, + { + Name: "append", + Value: builtinAppend, + }, + { + Name: "string", + Value: builtinString, + }, + { + Name: "int", + Value: builtinInt, + }, + { + Name: "bool", + Value: builtinBool, + }, + { + Name: "float", + Value: builtinFloat, + }, + { + Name: "char", + Value: builtinChar, + }, + { + Name: "bytes", + Value: builtinBytes, + }, + { + Name: "time", + Value: builtinTime, + }, + { + Name: "is_int", + Value: builtinIsInt, + }, + { + Name: "is_float", + Value: builtinIsFloat, + }, + { + Name: "is_string", + Value: builtinIsString, + }, + { + Name: "is_bool", + Value: builtinIsBool, + }, + { + Name: "is_char", + Value: builtinIsChar, + }, + { + Name: "is_bytes", + Value: builtinIsBytes, + }, + { + Name: "is_array", + Value: builtinIsArray, + }, + { + Name: "is_immutable_array", + Value: builtinIsImmutableArray, + }, + { + Name: "is_map", + Value: builtinIsMap, + }, + { + Name: "is_immutable_map", + Value: builtinIsImmutableMap, + }, + { + Name: "is_iterable", + Value: builtinIsIterable, + }, + { + Name: "is_time", + Value: builtinIsTime, + }, + { + Name: "is_error", + Value: builtinIsError, + }, + { + Name: "is_undefined", + Value: builtinIsUndefined, + }, + { + Name: "is_function", + Value: builtinIsFunction, + }, + { + Name: "is_callable", + Value: builtinIsCallable, + }, + { + Name: "type_name", + Value: builtinTypeName, + }, + { + Name: "format", + Value: builtinFormat, + }, +} + +// GetAllBuiltinFunctions returns all builtin function objects. +func GetAllBuiltinFunctions() []*BuiltinFunction { + return append([]*BuiltinFunction{}, builtinFuncs...) +} + +func builtinTypeName(args ...Object) (Object, error) { + if len(args) != 1 { + return nil, ErrWrongNumArguments + } + return &String{Value: args[0].TypeName()}, nil +} + +func builtinIsString(args ...Object) (Object, error) { + if len(args) != 1 { + return nil, ErrWrongNumArguments + } + if _, ok := args[0].(*String); ok { + return TrueValue, nil + } + return FalseValue, nil +} + +func builtinIsInt(args ...Object) (Object, error) { + if len(args) != 1 { + return nil, ErrWrongNumArguments + } + if _, ok := args[0].(*Int); ok { + return TrueValue, nil + } + return FalseValue, nil +} + +func builtinIsFloat(args ...Object) (Object, error) { + if len(args) != 1 { + return nil, ErrWrongNumArguments + } + if _, ok := args[0].(*Float); ok { + return TrueValue, nil + } + return FalseValue, nil +} + +func builtinIsBool(args ...Object) (Object, error) { + if len(args) != 1 { + return nil, ErrWrongNumArguments + } + if _, ok := args[0].(*Bool); ok { + return TrueValue, nil + } + return FalseValue, nil +} + +func builtinIsChar(args ...Object) (Object, error) { + if len(args) != 1 { + return nil, ErrWrongNumArguments + } + if _, ok := args[0].(*Char); ok { + return TrueValue, nil + } + return FalseValue, nil +} + +func builtinIsBytes(args ...Object) (Object, error) { + if len(args) != 1 { + return nil, ErrWrongNumArguments + } + if _, ok := args[0].(*Bytes); ok { + return TrueValue, nil + } + return FalseValue, nil +} + +func builtinIsArray(args ...Object) (Object, error) { + if len(args) != 1 { + return nil, ErrWrongNumArguments + } + if _, ok := args[0].(*Array); ok { + return TrueValue, nil + } + return FalseValue, nil +} + +func builtinIsImmutableArray(args ...Object) (Object, error) { + if len(args) != 1 { + return nil, ErrWrongNumArguments + } + if _, ok := args[0].(*ImmutableArray); ok { + return TrueValue, nil + } + return FalseValue, nil +} + +func builtinIsMap(args ...Object) (Object, error) { + if len(args) != 1 { + return nil, ErrWrongNumArguments + } + if _, ok := args[0].(*Map); ok { + return TrueValue, nil + } + return FalseValue, nil +} + +func builtinIsImmutableMap(args ...Object) (Object, error) { + if len(args) != 1 { + return nil, ErrWrongNumArguments + } + if _, ok := args[0].(*ImmutableMap); ok { + return TrueValue, nil + } + return FalseValue, nil +} + +func builtinIsTime(args ...Object) (Object, error) { + if len(args) != 1 { + return nil, ErrWrongNumArguments + } + if _, ok := args[0].(*Time); ok { + return TrueValue, nil + } + return FalseValue, nil +} + +func builtinIsError(args ...Object) (Object, error) { + if len(args) != 1 { + return nil, ErrWrongNumArguments + } + if _, ok := args[0].(*Error); ok { + return TrueValue, nil + } + return FalseValue, nil +} + +func builtinIsUndefined(args ...Object) (Object, error) { + if len(args) != 1 { + return nil, ErrWrongNumArguments + } + if args[0] == UndefinedValue { + return TrueValue, nil + } + return FalseValue, nil +} + +func builtinIsFunction(args ...Object) (Object, error) { + if len(args) != 1 { + return nil, ErrWrongNumArguments + } + switch args[0].(type) { + case *CompiledFunction: + return TrueValue, nil + } + return FalseValue, nil +} + +func builtinIsCallable(args ...Object) (Object, error) { + if len(args) != 1 { + return nil, ErrWrongNumArguments + } + if args[0].CanCall() { + return TrueValue, nil + } + return FalseValue, nil +} + +func builtinIsIterable(args ...Object) (Object, error) { + if len(args) != 1 { + return nil, ErrWrongNumArguments + } + if args[0].CanIterate() { + return TrueValue, nil + } + return FalseValue, nil +} + +// len(obj object) => int +func builtinLen(args ...Object) (Object, error) { + if len(args) != 1 { + return nil, ErrWrongNumArguments + } + switch arg := args[0].(type) { + case *Array: + return &Int{Value: int64(len(arg.Value))}, nil + case *ImmutableArray: + return &Int{Value: int64(len(arg.Value))}, nil + case *String: + return &Int{Value: int64(len(arg.Value))}, nil + case *Bytes: + return &Int{Value: int64(len(arg.Value))}, nil + case *Map: + return &Int{Value: int64(len(arg.Value))}, nil + case *ImmutableMap: + return &Int{Value: int64(len(arg.Value))}, nil + default: + return nil, ErrInvalidArgumentType{ + Name: "first", + Expected: "array/string/bytes/map", + Found: arg.TypeName(), + } + } +} + +func builtinFormat(args ...Object) (Object, error) { + numArgs := len(args) + if numArgs == 0 { + return nil, ErrWrongNumArguments + } + format, ok := args[0].(*String) + if !ok { + return nil, ErrInvalidArgumentType{ + Name: "format", + Expected: "string", + Found: args[0].TypeName(), + } + } + if numArgs == 1 { + // okay to return 'format' directly as String is immutable + return format, nil + } + s, err := Format(format.Value, args[1:]...) + if err != nil { + return nil, err + } + return &String{Value: s}, nil +} + +func builtinCopy(args ...Object) (Object, error) { + if len(args) != 1 { + return nil, ErrWrongNumArguments + } + return args[0].Copy(), nil +} + +func builtinString(args ...Object) (Object, error) { + argsLen := len(args) + if !(argsLen == 1 || argsLen == 2) { + return nil, ErrWrongNumArguments + } + if _, ok := args[0].(*String); ok { + return args[0], nil + } + v, ok := ToString(args[0]) + if ok { + if len(v) > MaxStringLen { + return nil, ErrStringLimit + } + return &String{Value: v}, nil + } + if argsLen == 2 { + return args[1], nil + } + return UndefinedValue, nil +} + +func builtinInt(args ...Object) (Object, error) { + argsLen := len(args) + if !(argsLen == 1 || argsLen == 2) { + return nil, ErrWrongNumArguments + } + if _, ok := args[0].(*Int); ok { + return args[0], nil + } + v, ok := ToInt64(args[0]) + if ok { + return &Int{Value: v}, nil + } + if argsLen == 2 { + return args[1], nil + } + return UndefinedValue, nil +} + +func builtinFloat(args ...Object) (Object, error) { + argsLen := len(args) + if !(argsLen == 1 || argsLen == 2) { + return nil, ErrWrongNumArguments + } + if _, ok := args[0].(*Float); ok { + return args[0], nil + } + v, ok := ToFloat64(args[0]) + if ok { + return &Float{Value: v}, nil + } + if argsLen == 2 { + return args[1], nil + } + return UndefinedValue, nil +} + +func builtinBool(args ...Object) (Object, error) { + if len(args) != 1 { + return nil, ErrWrongNumArguments + } + if _, ok := args[0].(*Bool); ok { + return args[0], nil + } + v, ok := ToBool(args[0]) + if ok { + if v { + return TrueValue, nil + } + return FalseValue, nil + } + return UndefinedValue, nil +} + +func builtinChar(args ...Object) (Object, error) { + argsLen := len(args) + if !(argsLen == 1 || argsLen == 2) { + return nil, ErrWrongNumArguments + } + if _, ok := args[0].(*Char); ok { + return args[0], nil + } + v, ok := ToRune(args[0]) + if ok { + return &Char{Value: v}, nil + } + if argsLen == 2 { + return args[1], nil + } + return UndefinedValue, nil +} + +func builtinBytes(args ...Object) (Object, error) { + argsLen := len(args) + if !(argsLen == 1 || argsLen == 2) { + return nil, ErrWrongNumArguments + } + + // bytes(N) => create a new bytes with given size N + if n, ok := args[0].(*Int); ok { + if n.Value > int64(MaxBytesLen) { + return nil, ErrBytesLimit + } + return &Bytes{Value: make([]byte, int(n.Value))}, nil + } + v, ok := ToByteSlice(args[0]) + if ok { + if len(v) > MaxBytesLen { + return nil, ErrBytesLimit + } + return &Bytes{Value: v}, nil + } + if argsLen == 2 { + return args[1], nil + } + return UndefinedValue, nil +} + +func builtinTime(args ...Object) (Object, error) { + argsLen := len(args) + if !(argsLen == 1 || argsLen == 2) { + return nil, ErrWrongNumArguments + } + if _, ok := args[0].(*Time); ok { + return args[0], nil + } + v, ok := ToTime(args[0]) + if ok { + return &Time{Value: v}, nil + } + if argsLen == 2 { + return args[1], nil + } + return UndefinedValue, nil +} + +// append(arr, items...) +func builtinAppend(args ...Object) (Object, error) { + if len(args) < 2 { + return nil, ErrWrongNumArguments + } + 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, ErrInvalidArgumentType{ + Name: "first", + Expected: "array", + Found: arg.TypeName(), + } + } +} diff --git a/vendor/github.com/d5/tengo/v2/bytecode.go b/vendor/github.com/d5/tengo/v2/bytecode.go new file mode 100644 index 00000000..cfd0d0b5 --- /dev/null +++ b/vendor/github.com/d5/tengo/v2/bytecode.go @@ -0,0 +1,292 @@ +package tengo + +import ( + "encoding/gob" + "fmt" + "io" + "reflect" + + "github.com/d5/tengo/v2/parser" +) + +// Bytecode is a compiled instructions and constants. +type Bytecode struct { + FileSet *parser.SourceFileSet + MainFunction *CompiledFunction + Constants []Object +} + +// Encode writes Bytecode data to the writer. +func (b *Bytecode) Encode(w io.Writer) error { + enc := gob.NewEncoder(w) + if err := enc.Encode(b.FileSet); err != nil { + return err + } + if err := enc.Encode(b.MainFunction); err != nil { + return err + } + return enc.Encode(b.Constants) +} + +// CountObjects returns the number of objects found in Constants. +func (b *Bytecode) CountObjects() int { + n := 0 + for _, c := range b.Constants { + n += CountObjects(c) + } + return n +} + +// FormatInstructions returns human readable string representations of +// compiled instructions. +func (b *Bytecode) FormatInstructions() []string { + return FormatInstructions(b.MainFunction.Instructions, 0) +} + +// FormatConstants returns human readable string representations of +// compiled constants. +func (b *Bytecode) FormatConstants() (output []string) { + for cidx, cn := range b.Constants { + switch cn := cn.(type) { + case *CompiledFunction: + output = append(output, fmt.Sprintf( + "[% 3d] (Compiled Function|%p)", cidx, &cn)) + for _, l := range FormatInstructions(cn.Instructions, 0) { + output = append(output, fmt.Sprintf(" %s", l)) + } + default: + output = append(output, fmt.Sprintf("[% 3d] %s (%s|%p)", + cidx, cn, reflect.TypeOf(cn).Elem().Name(), &cn)) + } + } + return +} + +// Decode reads Bytecode data from the reader. +func (b *Bytecode) Decode(r io.Reader, modules *ModuleMap) error { + if modules == nil { + modules = NewModuleMap() + } + + dec := gob.NewDecoder(r) + 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 + } + if err := dec.Decode(&b.Constants); err != nil { + return err + } + for i, v := range b.Constants { + fv, err := fixDecodedObject(v, modules) + if err != nil { + return err + } + b.Constants[i] = fv + } + return nil +} + +// RemoveDuplicates finds and remove the duplicate values in Constants. +// Note this function mutates Bytecode. +func (b *Bytecode) RemoveDuplicates() { + var deduped []Object + + indexMap := make(map[int]int) // mapping from old constant index to new index + ints := make(map[int64]int) + strings := make(map[string]int) + floats := make(map[float64]int) + chars := make(map[rune]int) + immutableMaps := make(map[string]int) // for modules + + for curIdx, c := range b.Constants { + switch c := c.(type) { + case *CompiledFunction: + // add to deduped list + indexMap[curIdx] = len(deduped) + deduped = append(deduped, c) + case *ImmutableMap: + modName := inferModuleName(c) + newIdx, ok := immutableMaps[modName] + if modName != "" && ok { + indexMap[curIdx] = newIdx + } else { + newIdx = len(deduped) + immutableMaps[modName] = newIdx + indexMap[curIdx] = newIdx + deduped = append(deduped, c) + } + case *Int: + if newIdx, ok := ints[c.Value]; ok { + indexMap[curIdx] = newIdx + } else { + newIdx = len(deduped) + ints[c.Value] = newIdx + indexMap[curIdx] = newIdx + deduped = append(deduped, c) + } + case *String: + if newIdx, ok := strings[c.Value]; ok { + indexMap[curIdx] = newIdx + } else { + newIdx = len(deduped) + strings[c.Value] = newIdx + indexMap[curIdx] = newIdx + deduped = append(deduped, c) + } + case *Float: + if newIdx, ok := floats[c.Value]; ok { + indexMap[curIdx] = newIdx + } else { + newIdx = len(deduped) + floats[c.Value] = newIdx + indexMap[curIdx] = newIdx + deduped = append(deduped, c) + } + case *Char: + if newIdx, ok := chars[c.Value]; ok { + indexMap[curIdx] = newIdx + } else { + newIdx = len(deduped) + chars[c.Value] = newIdx + indexMap[curIdx] = newIdx + deduped = append(deduped, c) + } + default: + panic(fmt.Errorf("unsupported top-level constant type: %s", + c.TypeName())) + } + } + + // replace with de-duplicated constants + b.Constants = deduped + + // update CONST instructions with new indexes + // main function + updateConstIndexes(b.MainFunction.Instructions, indexMap) + // other compiled functions in constants + for _, c := range b.Constants { + switch c := c.(type) { + case *CompiledFunction: + updateConstIndexes(c.Instructions, indexMap) + } + } +} + +func fixDecodedObject( + o Object, + modules *ModuleMap, +) (Object, error) { + switch o := o.(type) { + case *Bool: + if o.IsFalsy() { + return FalseValue, nil + } + return TrueValue, nil + case *Undefined: + return UndefinedValue, nil + case *Array: + for i, v := range o.Value { + fv, err := fixDecodedObject(v, modules) + if err != nil { + return nil, err + } + o.Value[i] = fv + } + case *ImmutableArray: + for i, v := range o.Value { + fv, err := fixDecodedObject(v, modules) + if err != nil { + return nil, err + } + o.Value[i] = fv + } + case *Map: + for k, v := range o.Value { + fv, err := fixDecodedObject(v, modules) + if err != nil { + return nil, err + } + o.Value[k] = fv + } + case *ImmutableMap: + modName := inferModuleName(o) + if mod := modules.GetBuiltinModule(modName); mod != nil { + return mod.AsImmutableMap(modName), nil + } + + for k, v := range o.Value { + // encoding of user function not supported + if _, isUserFunction := v.(*UserFunction); isUserFunction { + return nil, fmt.Errorf("user function not decodable") + } + + fv, err := fixDecodedObject(v, modules) + if err != nil { + return nil, err + } + o.Value[k] = fv + } + } + return o, nil +} + +func updateConstIndexes(insts []byte, indexMap map[int]int) { + i := 0 + for i < len(insts) { + op := insts[i] + numOperands := parser.OpcodeOperands[op] + _, read := parser.ReadOperands(numOperands, insts[i+1:]) + + switch op { + case parser.OpConstant: + curIdx := int(insts[i+2]) | int(insts[i+1])<<8 + newIdx, ok := indexMap[curIdx] + if !ok { + panic(fmt.Errorf("constant index not found: %d", curIdx)) + } + copy(insts[i:], MakeInstruction(op, newIdx)) + case parser.OpClosure: + curIdx := int(insts[i+2]) | int(insts[i+1])<<8 + numFree := int(insts[i+3]) + newIdx, ok := indexMap[curIdx] + if !ok { + panic(fmt.Errorf("constant index not found: %d", curIdx)) + } + copy(insts[i:], MakeInstruction(op, newIdx, numFree)) + } + + i += 1 + read + } +} + +func inferModuleName(mod *ImmutableMap) string { + if modName, ok := mod.Value["__module_name__"].(*String); ok { + return modName.Value + } + return "" +} + +func init() { + gob.Register(&parser.SourceFileSet{}) + gob.Register(&parser.SourceFile{}) + gob.Register(&Array{}) + gob.Register(&Bool{}) + gob.Register(&Bytes{}) + gob.Register(&Char{}) + gob.Register(&CompiledFunction{}) + gob.Register(&Error{}) + gob.Register(&Float{}) + gob.Register(&ImmutableArray{}) + gob.Register(&ImmutableMap{}) + gob.Register(&Int{}) + gob.Register(&Map{}) + gob.Register(&String{}) + gob.Register(&Time{}) + gob.Register(&Undefined{}) + gob.Register(&UserFunction{}) +} diff --git a/vendor/github.com/d5/tengo/v2/compiler.go b/vendor/github.com/d5/tengo/v2/compiler.go new file mode 100644 index 00000000..eb686ed6 --- /dev/null +++ b/vendor/github.com/d5/tengo/v2/compiler.go @@ -0,0 +1,1312 @@ +package tengo + +import ( + "fmt" + "io" + "io/ioutil" + "path/filepath" + "reflect" + "strings" + + "github.com/d5/tengo/v2/parser" + "github.com/d5/tengo/v2/token" +) + +// compilationScope represents a compiled instructions and the last two +// instructions that were emitted. +type compilationScope struct { + Instructions []byte + SymbolInit map[string]bool + SourceMap map[int]parser.Pos +} + +// loop represents a loop construct that the compiler uses to track the current +// loop. +type loop struct { + Continues []int + Breaks []int +} + +// CompilerError represents a compiler error. +type CompilerError struct { + FileSet *parser.SourceFileSet + Node parser.Node + Err error +} + +func (e *CompilerError) Error() string { + filePos := e.FileSet.Position(e.Node.Pos()) + return fmt.Sprintf("Compile Error: %s\n\tat %s", e.Err.Error(), filePos) +} + +// Compiler compiles the AST into a bytecode. +type Compiler struct { + file *parser.SourceFile + parent *Compiler + modulePath string + constants []Object + symbolTable *SymbolTable + scopes []compilationScope + scopeIndex int + modules *ModuleMap + compiledModules map[string]*CompiledFunction + allowFileImport bool + loops []*loop + loopIndex int + trace io.Writer + indent int +} + +// NewCompiler creates a Compiler. +func NewCompiler( + file *parser.SourceFile, + symbolTable *SymbolTable, + constants []Object, + modules *ModuleMap, + trace io.Writer, +) *Compiler { + mainScope := compilationScope{ + SymbolInit: make(map[string]bool), + SourceMap: make(map[int]parser.Pos), + } + + // symbol table + if symbolTable == nil { + symbolTable = NewSymbolTable() + } + + // add builtin functions to the symbol table + for idx, fn := range builtinFuncs { + symbolTable.DefineBuiltin(idx, fn.Name) + } + + // builtin modules + if modules == nil { + modules = NewModuleMap() + } + + return &Compiler{ + file: file, + symbolTable: symbolTable, + constants: constants, + scopes: []compilationScope{mainScope}, + scopeIndex: 0, + loopIndex: -1, + trace: trace, + modules: modules, + compiledModules: make(map[string]*CompiledFunction), + } +} + +// Compile compiles the AST node. +func (c *Compiler) Compile(node parser.Node) error { + if c.trace != nil { + if node != nil { + defer untracec(tracec(c, fmt.Sprintf("%s (%s)", + node.String(), reflect.TypeOf(node).Elem().Name()))) + } else { + defer untracec(tracec(c, "")) + } + } + + switch node := node.(type) { + case *parser.File: + for _, stmt := range node.Stmts { + if err := c.Compile(stmt); err != nil { + return err + } + } + case *parser.ExprStmt: + if err := c.Compile(node.Expr); err != nil { + return err + } + c.emit(node, parser.OpPop) + case *parser.IncDecStmt: + op := token.AddAssign + if node.Token == token.Dec { + op = token.SubAssign + } + return c.compileAssign(node, []parser.Expr{node.Expr}, + []parser.Expr{&parser.IntLit{Value: 1}}, op) + case *parser.ParenExpr: + if err := c.Compile(node.Expr); err != nil { + return err + } + case *parser.BinaryExpr: + if node.Token == token.LAnd || node.Token == token.LOr { + return c.compileLogical(node) + } + if node.Token == token.Less { + if err := c.Compile(node.RHS); err != nil { + return err + } + if err := c.Compile(node.LHS); err != nil { + return err + } + c.emit(node, parser.OpBinaryOp, int(token.Greater)) + return nil + } else if node.Token == token.LessEq { + if err := c.Compile(node.RHS); err != nil { + return err + } + if err := c.Compile(node.LHS); err != nil { + return err + } + c.emit(node, parser.OpBinaryOp, int(token.GreaterEq)) + return nil + } + if err := c.Compile(node.LHS); err != nil { + return err + } + if err := c.Compile(node.RHS); err != nil { + return err + } + + switch node.Token { + case token.Add: + c.emit(node, parser.OpBinaryOp, int(token.Add)) + case token.Sub: + c.emit(node, parser.OpBinaryOp, int(token.Sub)) + case token.Mul: + c.emit(node, parser.OpBinaryOp, int(token.Mul)) + case token.Quo: + c.emit(node, parser.OpBinaryOp, int(token.Quo)) + case token.Rem: + c.emit(node, parser.OpBinaryOp, int(token.Rem)) + case token.Greater: + c.emit(node, parser.OpBinaryOp, int(token.Greater)) + case token.GreaterEq: + c.emit(node, parser.OpBinaryOp, int(token.GreaterEq)) + case token.Equal: + c.emit(node, parser.OpEqual) + case token.NotEqual: + c.emit(node, parser.OpNotEqual) + case token.And: + c.emit(node, parser.OpBinaryOp, int(token.And)) + case token.Or: + c.emit(node, parser.OpBinaryOp, int(token.Or)) + case token.Xor: + c.emit(node, parser.OpBinaryOp, int(token.Xor)) + case token.AndNot: + c.emit(node, parser.OpBinaryOp, int(token.AndNot)) + case token.Shl: + c.emit(node, parser.OpBinaryOp, int(token.Shl)) + case token.Shr: + c.emit(node, parser.OpBinaryOp, int(token.Shr)) + default: + return c.errorf(node, "invalid binary operator: %s", + node.Token.String()) + } + case *parser.IntLit: + c.emit(node, parser.OpConstant, + c.addConstant(&Int{Value: node.Value})) + case *parser.FloatLit: + c.emit(node, parser.OpConstant, + c.addConstant(&Float{Value: node.Value})) + case *parser.BoolLit: + if node.Value { + c.emit(node, parser.OpTrue) + } else { + c.emit(node, parser.OpFalse) + } + case *parser.StringLit: + if len(node.Value) > MaxStringLen { + return c.error(node, ErrStringLimit) + } + c.emit(node, parser.OpConstant, + c.addConstant(&String{Value: node.Value})) + case *parser.CharLit: + c.emit(node, parser.OpConstant, + c.addConstant(&Char{Value: node.Value})) + case *parser.UndefinedLit: + c.emit(node, parser.OpNull) + case *parser.UnaryExpr: + if err := c.Compile(node.Expr); err != nil { + return err + } + + switch node.Token { + case token.Not: + c.emit(node, parser.OpLNot) + case token.Sub: + c.emit(node, parser.OpMinus) + case token.Xor: + c.emit(node, parser.OpBComplement) + case token.Add: + // do nothing? + default: + return c.errorf(node, + "invalid unary operator: %s", node.Token.String()) + } + case *parser.IfStmt: + // open new symbol table for the statement + c.symbolTable = c.symbolTable.Fork(true) + defer func() { + c.symbolTable = c.symbolTable.Parent(false) + }() + + if node.Init != nil { + if err := c.Compile(node.Init); err != nil { + return err + } + } + if err := c.Compile(node.Cond); err != nil { + return err + } + + // first jump placeholder + jumpPos1 := c.emit(node, parser.OpJumpFalsy, 0) + if err := c.Compile(node.Body); err != nil { + return err + } + if node.Else != nil { + // second jump placeholder + jumpPos2 := c.emit(node, parser.OpJump, 0) + + // update first jump offset + curPos := len(c.currentInstructions()) + c.changeOperand(jumpPos1, curPos) + if err := c.Compile(node.Else); err != nil { + return err + } + + // update second jump offset + curPos = len(c.currentInstructions()) + c.changeOperand(jumpPos2, curPos) + } else { + // update first jump offset + curPos := len(c.currentInstructions()) + c.changeOperand(jumpPos1, curPos) + } + case *parser.ForStmt: + return c.compileForStmt(node) + case *parser.ForInStmt: + return c.compileForInStmt(node) + case *parser.BranchStmt: + if node.Token == token.Break { + curLoop := c.currentLoop() + if curLoop == nil { + return c.errorf(node, "break not allowed outside loop") + } + pos := c.emit(node, parser.OpJump, 0) + curLoop.Breaks = append(curLoop.Breaks, pos) + } else if node.Token == token.Continue { + curLoop := c.currentLoop() + if curLoop == nil { + return c.errorf(node, "continue not allowed outside loop") + } + pos := c.emit(node, parser.OpJump, 0) + curLoop.Continues = append(curLoop.Continues, pos) + } else { + panic(fmt.Errorf("invalid branch statement: %s", + node.Token.String())) + } + case *parser.BlockStmt: + if len(node.Stmts) == 0 { + return nil + } + + c.symbolTable = c.symbolTable.Fork(true) + defer func() { + c.symbolTable = c.symbolTable.Parent(false) + }() + + for _, stmt := range node.Stmts { + if err := c.Compile(stmt); err != nil { + return err + } + } + case *parser.AssignStmt: + err := c.compileAssign(node, node.LHS, node.RHS, node.Token) + if err != nil { + return err + } + case *parser.Ident: + symbol, _, ok := c.symbolTable.Resolve(node.Name) + if !ok { + return c.errorf(node, "unresolved reference '%s'", node.Name) + } + + switch symbol.Scope { + case ScopeGlobal: + c.emit(node, parser.OpGetGlobal, symbol.Index) + case ScopeLocal: + c.emit(node, parser.OpGetLocal, symbol.Index) + case ScopeBuiltin: + c.emit(node, parser.OpGetBuiltin, symbol.Index) + case ScopeFree: + c.emit(node, parser.OpGetFree, symbol.Index) + } + case *parser.ArrayLit: + for _, elem := range node.Elements { + if err := c.Compile(elem); err != nil { + return err + } + } + c.emit(node, parser.OpArray, len(node.Elements)) + case *parser.MapLit: + for _, elt := range node.Elements { + // key + if len(elt.Key) > MaxStringLen { + return c.error(node, ErrStringLimit) + } + c.emit(node, parser.OpConstant, + c.addConstant(&String{Value: elt.Key})) + + // value + if err := c.Compile(elt.Value); err != nil { + return err + } + } + c.emit(node, parser.OpMap, len(node.Elements)*2) + + case *parser.SelectorExpr: // selector on RHS side + if err := c.Compile(node.Expr); err != nil { + return err + } + if err := c.Compile(node.Sel); err != nil { + return err + } + c.emit(node, parser.OpIndex) + case *parser.IndexExpr: + if err := c.Compile(node.Expr); err != nil { + return err + } + if err := c.Compile(node.Index); err != nil { + return err + } + c.emit(node, parser.OpIndex) + case *parser.SliceExpr: + if err := c.Compile(node.Expr); err != nil { + return err + } + if node.Low != nil { + if err := c.Compile(node.Low); err != nil { + return err + } + } else { + c.emit(node, parser.OpNull) + } + if node.High != nil { + if err := c.Compile(node.High); err != nil { + return err + } + } else { + c.emit(node, parser.OpNull) + } + c.emit(node, parser.OpSliceIndex) + case *parser.FuncLit: + c.enterScope() + + for _, p := range node.Type.Params.List { + s := c.symbolTable.Define(p.Name) + + // function arguments is not assigned directly. + s.LocalAssigned = true + } + + if err := c.Compile(node.Body); err != nil { + return err + } + + // code optimization + c.optimizeFunc(node) + + freeSymbols := c.symbolTable.FreeSymbols() + numLocals := c.symbolTable.MaxSymbols() + instructions, sourceMap := c.leaveScope() + + for _, s := range freeSymbols { + switch s.Scope { + case ScopeLocal: + if !s.LocalAssigned { + // Here, the closure is capturing a local variable that's + // not yet assigned its value. One example is a local + // recursive function: + // + // func() { + // foo := func(x) { + // // .. + // return foo(x-1) + // } + // } + // + // which translate into + // + // 0000 GETL 0 + // 0002 CLOSURE ? 1 + // 0006 DEFL 0 + // + // . So the local variable (0) is being captured before + // it's assigned the value. + // + // Solution is to transform the code into something like + // this: + // + // func() { + // foo := undefined + // foo = func(x) { + // // .. + // return foo(x-1) + // } + // } + // + // that is equivalent to + // + // 0000 NULL + // 0001 DEFL 0 + // 0003 GETL 0 + // 0005 CLOSURE ? 1 + // 0009 SETL 0 + // + c.emit(node, parser.OpNull) + c.emit(node, parser.OpDefineLocal, s.Index) + s.LocalAssigned = true + } + c.emit(node, parser.OpGetLocalPtr, s.Index) + case ScopeFree: + c.emit(node, parser.OpGetFreePtr, s.Index) + } + } + + compiledFunction := &CompiledFunction{ + Instructions: instructions, + NumLocals: numLocals, + NumParameters: len(node.Type.Params.List), + VarArgs: node.Type.Params.VarArgs, + SourceMap: sourceMap, + } + if len(freeSymbols) > 0 { + c.emit(node, parser.OpClosure, + c.addConstant(compiledFunction), len(freeSymbols)) + } else { + c.emit(node, parser.OpConstant, c.addConstant(compiledFunction)) + } + case *parser.ReturnStmt: + if c.symbolTable.Parent(true) == nil { + // outside the function + return c.errorf(node, "return not allowed outside function") + } + + if node.Result == nil { + c.emit(node, parser.OpReturn, 0) + } else { + if err := c.Compile(node.Result); err != nil { + return err + } + c.emit(node, parser.OpReturn, 1) + } + case *parser.CallExpr: + if err := c.Compile(node.Func); err != nil { + return err + } + for _, arg := range node.Args { + if err := c.Compile(arg); err != nil { + return err + } + } + c.emit(node, parser.OpCall, len(node.Args)) + case *parser.ImportExpr: + if node.ModuleName == "" { + return c.errorf(node, "empty module name") + } + + if mod := c.modules.Get(node.ModuleName); mod != nil { + v, err := mod.Import(node.ModuleName) + if err != nil { + return err + } + + switch v := v.(type) { + case []byte: // module written in Tengo + compiled, err := c.compileModule(node, + node.ModuleName, node.ModuleName, v) + if err != nil { + return err + } + c.emit(node, parser.OpConstant, c.addConstant(compiled)) + c.emit(node, parser.OpCall, 0) + case Object: // builtin module + c.emit(node, parser.OpConstant, c.addConstant(v)) + default: + panic(fmt.Errorf("invalid import value type: %T", v)) + } + } else if c.allowFileImport { + moduleName := node.ModuleName + if !strings.HasSuffix(moduleName, ".tengo") { + moduleName += ".tengo" + } + + modulePath, err := filepath.Abs(moduleName) + if err != nil { + return c.errorf(node, "module file path error: %s", + err.Error()) + } + + if err := c.checkCyclicImports(node, modulePath); err != nil { + return err + } + + moduleSrc, err := ioutil.ReadFile(moduleName) + if err != nil { + return c.errorf(node, "module file read error: %s", + err.Error()) + } + + compiled, err := c.compileModule(node, + moduleName, modulePath, moduleSrc) + if err != nil { + return err + } + c.emit(node, parser.OpConstant, c.addConstant(compiled)) + c.emit(node, parser.OpCall, 0) + } else { + return c.errorf(node, "module '%s' not found", node.ModuleName) + } + case *parser.ExportStmt: + // export statement must be in top-level scope + if c.scopeIndex != 0 { + return c.errorf(node, "export not allowed inside function") + } + + // export statement is simply ignore when compiling non-module code + if c.parent == nil { + break + } + if err := c.Compile(node.Result); err != nil { + return err + } + c.emit(node, parser.OpImmutable) + c.emit(node, parser.OpReturn, 1) + case *parser.ErrorExpr: + if err := c.Compile(node.Expr); err != nil { + return err + } + c.emit(node, parser.OpError) + case *parser.ImmutableExpr: + if err := c.Compile(node.Expr); err != nil { + return err + } + c.emit(node, parser.OpImmutable) + case *parser.CondExpr: + if err := c.Compile(node.Cond); err != nil { + return err + } + + // first jump placeholder + jumpPos1 := c.emit(node, parser.OpJumpFalsy, 0) + if err := c.Compile(node.True); err != nil { + return err + } + + // second jump placeholder + jumpPos2 := c.emit(node, parser.OpJump, 0) + + // update first jump offset + curPos := len(c.currentInstructions()) + c.changeOperand(jumpPos1, curPos) + if err := c.Compile(node.False); err != nil { + return err + } + + // update second jump offset + curPos = len(c.currentInstructions()) + c.changeOperand(jumpPos2, curPos) + } + return nil +} + +// Bytecode returns a compiled bytecode. +func (c *Compiler) Bytecode() *Bytecode { + return &Bytecode{ + FileSet: c.file.Set(), + MainFunction: &CompiledFunction{ + Instructions: append(c.currentInstructions(), parser.OpSuspend), + SourceMap: c.currentSourceMap(), + }, + Constants: c.constants, + } +} + +// EnableFileImport enables or disables module loading from local files. +// Local file modules are disabled by default. +func (c *Compiler) EnableFileImport(enable bool) { + c.allowFileImport = enable +} + +func (c *Compiler) compileAssign( + node parser.Node, + lhs, rhs []parser.Expr, + op token.Token, +) error { + numLHS, numRHS := len(lhs), len(rhs) + if numLHS > 1 || numRHS > 1 { + return c.errorf(node, "tuple assignment not allowed") + } + + // resolve and compile left-hand side + ident, selectors := resolveAssignLHS(lhs[0]) + numSel := len(selectors) + + if op == token.Define && numSel > 0 { + // using selector on new variable does not make sense + 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 c.errorf(node, "'%s' redeclared in this block", ident) + } + symbol = c.symbolTable.Define(ident) + } else { + if !exists { + return c.errorf(node, "unresolved reference '%s'", ident) + } + } + + // +=, -=, *=, /= + if op != token.Assign && op != token.Define { + if err := c.Compile(lhs[0]); err != nil { + return err + } + } + + // compile RHSs + for _, expr := range rhs { + if err := c.Compile(expr); err != nil { + return err + } + } + + switch op { + case token.AddAssign: + c.emit(node, parser.OpBinaryOp, int(token.Add)) + case token.SubAssign: + c.emit(node, parser.OpBinaryOp, int(token.Sub)) + case token.MulAssign: + c.emit(node, parser.OpBinaryOp, int(token.Mul)) + case token.QuoAssign: + c.emit(node, parser.OpBinaryOp, int(token.Quo)) + case token.RemAssign: + c.emit(node, parser.OpBinaryOp, int(token.Rem)) + case token.AndAssign: + c.emit(node, parser.OpBinaryOp, int(token.And)) + case token.OrAssign: + c.emit(node, parser.OpBinaryOp, int(token.Or)) + case token.AndNotAssign: + c.emit(node, parser.OpBinaryOp, int(token.AndNot)) + case token.XorAssign: + c.emit(node, parser.OpBinaryOp, int(token.Xor)) + case token.ShlAssign: + c.emit(node, parser.OpBinaryOp, int(token.Shl)) + case token.ShrAssign: + c.emit(node, parser.OpBinaryOp, int(token.Shr)) + } + + // compile selector expressions (right to left) + for i := numSel - 1; i >= 0; i-- { + if err := c.Compile(selectors[i]); err != nil { + return err + } + } + + switch symbol.Scope { + case ScopeGlobal: + if numSel > 0 { + c.emit(node, parser.OpSetSelGlobal, symbol.Index, numSel) + } else { + c.emit(node, parser.OpSetGlobal, symbol.Index) + } + case ScopeLocal: + if numSel > 0 { + c.emit(node, parser.OpSetSelLocal, symbol.Index, numSel) + } else { + if op == token.Define && !symbol.LocalAssigned { + c.emit(node, parser.OpDefineLocal, symbol.Index) + } else { + c.emit(node, parser.OpSetLocal, symbol.Index) + } + } + + // mark the symbol as local-assigned + symbol.LocalAssigned = true + case ScopeFree: + if numSel > 0 { + c.emit(node, parser.OpSetSelFree, symbol.Index, numSel) + } else { + c.emit(node, parser.OpSetFree, symbol.Index) + } + default: + panic(fmt.Errorf("invalid assignment variable scope: %s", + symbol.Scope)) + } + return nil +} + +func (c *Compiler) compileLogical(node *parser.BinaryExpr) error { + // left side term + if err := c.Compile(node.LHS); err != nil { + return err + } + + // jump position + var jumpPos int + if node.Token == token.LAnd { + jumpPos = c.emit(node, parser.OpAndJump, 0) + } else { + jumpPos = c.emit(node, parser.OpOrJump, 0) + } + + // right side term + if err := c.Compile(node.RHS); err != nil { + return err + } + + c.changeOperand(jumpPos, len(c.currentInstructions())) + return nil +} + +func (c *Compiler) compileForStmt(stmt *parser.ForStmt) error { + c.symbolTable = c.symbolTable.Fork(true) + defer func() { + c.symbolTable = c.symbolTable.Parent(false) + }() + + // init statement + if stmt.Init != nil { + if err := c.Compile(stmt.Init); err != nil { + return err + } + } + + // pre-condition position + preCondPos := len(c.currentInstructions()) + + // condition expression + postCondPos := -1 + if stmt.Cond != nil { + if err := c.Compile(stmt.Cond); err != nil { + return err + } + // condition jump position + postCondPos = c.emit(stmt, parser.OpJumpFalsy, 0) + } + + // enter loop + loop := c.enterLoop() + + // body statement + if err := c.Compile(stmt.Body); err != nil { + c.leaveLoop() + return err + } + + c.leaveLoop() + + // post-body position + postBodyPos := len(c.currentInstructions()) + + // post statement + if stmt.Post != nil { + if err := c.Compile(stmt.Post); err != nil { + return err + } + } + + // back to condition + c.emit(stmt, parser.OpJump, preCondPos) + + // post-statement position + postStmtPos := len(c.currentInstructions()) + if postCondPos >= 0 { + c.changeOperand(postCondPos, postStmtPos) + } + + // update all break/continue jump positions + for _, pos := range loop.Breaks { + c.changeOperand(pos, postStmtPos) + } + for _, pos := range loop.Continues { + c.changeOperand(pos, postBodyPos) + } + return nil +} + +func (c *Compiler) compileForInStmt(stmt *parser.ForInStmt) error { + c.symbolTable = c.symbolTable.Fork(true) + defer func() { + c.symbolTable = c.symbolTable.Parent(false) + }() + + // for-in statement is compiled like following: + // + // for :it := iterator(iterable); :it.next(); { + // k, v := :it.get() // DEFINE operator + // + // ... body ... + // } + // + // ":it" is a local variable but will be conflict with other user variables + // because character ":" is not allowed. + + // init + // :it = iterator(iterable) + itSymbol := c.symbolTable.Define(":it") + if err := c.Compile(stmt.Iterable); err != nil { + return err + } + c.emit(stmt, parser.OpIteratorInit) + if itSymbol.Scope == ScopeGlobal { + c.emit(stmt, parser.OpSetGlobal, itSymbol.Index) + } else { + c.emit(stmt, parser.OpDefineLocal, itSymbol.Index) + } + + // pre-condition position + preCondPos := len(c.currentInstructions()) + + // condition + // :it.HasMore() + if itSymbol.Scope == ScopeGlobal { + c.emit(stmt, parser.OpGetGlobal, itSymbol.Index) + } else { + c.emit(stmt, parser.OpGetLocal, itSymbol.Index) + } + c.emit(stmt, parser.OpIteratorNext) + + // condition jump position + postCondPos := c.emit(stmt, parser.OpJumpFalsy, 0) + + // enter loop + loop := c.enterLoop() + + // assign key variable + if stmt.Key.Name != "_" { + keySymbol := c.symbolTable.Define(stmt.Key.Name) + if itSymbol.Scope == ScopeGlobal { + c.emit(stmt, parser.OpGetGlobal, itSymbol.Index) + } else { + c.emit(stmt, parser.OpGetLocal, itSymbol.Index) + } + c.emit(stmt, parser.OpIteratorKey) + if keySymbol.Scope == ScopeGlobal { + c.emit(stmt, parser.OpSetGlobal, keySymbol.Index) + } else { + c.emit(stmt, parser.OpDefineLocal, keySymbol.Index) + } + } + + // assign value variable + if stmt.Value.Name != "_" { + valueSymbol := c.symbolTable.Define(stmt.Value.Name) + if itSymbol.Scope == ScopeGlobal { + c.emit(stmt, parser.OpGetGlobal, itSymbol.Index) + } else { + c.emit(stmt, parser.OpGetLocal, itSymbol.Index) + } + c.emit(stmt, parser.OpIteratorValue) + if valueSymbol.Scope == ScopeGlobal { + c.emit(stmt, parser.OpSetGlobal, valueSymbol.Index) + } else { + c.emit(stmt, parser.OpDefineLocal, valueSymbol.Index) + } + } + + // body statement + if err := c.Compile(stmt.Body); err != nil { + c.leaveLoop() + return err + } + + c.leaveLoop() + + // post-body position + postBodyPos := len(c.currentInstructions()) + + // back to condition + c.emit(stmt, parser.OpJump, preCondPos) + + // post-statement position + postStmtPos := len(c.currentInstructions()) + c.changeOperand(postCondPos, postStmtPos) + + // update all break/continue jump positions + for _, pos := range loop.Breaks { + c.changeOperand(pos, postStmtPos) + } + for _, pos := range loop.Continues { + c.changeOperand(pos, postBodyPos) + } + return nil +} + +func (c *Compiler) checkCyclicImports( + node parser.Node, + modulePath string, +) error { + if c.modulePath == modulePath { + return c.errorf(node, "cyclic module import: %s", modulePath) + } else if c.parent != nil { + return c.parent.checkCyclicImports(node, modulePath) + } + return nil +} + +func (c *Compiler) compileModule( + node parser.Node, + moduleName, modulePath string, + src []byte, +) (*CompiledFunction, error) { + if err := c.checkCyclicImports(node, modulePath); err != nil { + return nil, err + } + + compiledModule, exists := c.loadCompiledModule(modulePath) + if exists { + return compiledModule, 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 + } + + // inherit builtin functions + symbolTable := NewSymbolTable() + for _, sym := range c.symbolTable.BuiltinSymbols() { + symbolTable.DefineBuiltin(sym.Index, sym.Name) + } + + // no global scope for the module + symbolTable = symbolTable.Fork(false) + + // compile module + moduleCompiler := c.fork(modFile, modulePath, symbolTable) + if err := moduleCompiler.Compile(file); err != nil { + return nil, err + } + + // code optimization + moduleCompiler.optimizeFunc(node) + compiledFunc := moduleCompiler.Bytecode().MainFunction + compiledFunc.NumLocals = symbolTable.MaxSymbols() + c.storeCompiledModule(modulePath, compiledFunc) + return compiledFunc, nil +} + +func (c *Compiler) loadCompiledModule( + modulePath string, +) (mod *CompiledFunction, ok bool) { + if c.parent != nil { + return c.parent.loadCompiledModule(modulePath) + } + mod, ok = c.compiledModules[modulePath] + return +} + +func (c *Compiler) storeCompiledModule( + modulePath string, + module *CompiledFunction, +) { + if c.parent != nil { + c.parent.storeCompiledModule(modulePath, module) + } + c.compiledModules[modulePath] = module +} + +func (c *Compiler) enterLoop() *loop { + loop := &loop{} + c.loops = append(c.loops, loop) + c.loopIndex++ + if c.trace != nil { + c.printTrace("LOOPE", c.loopIndex) + } + return loop +} + +func (c *Compiler) leaveLoop() { + if c.trace != nil { + c.printTrace("LOOPL", c.loopIndex) + } + c.loops = c.loops[:len(c.loops)-1] + c.loopIndex-- +} + +func (c *Compiler) currentLoop() *loop { + if c.loopIndex >= 0 { + return c.loops[c.loopIndex] + } + return nil +} + +func (c *Compiler) currentInstructions() []byte { + return c.scopes[c.scopeIndex].Instructions +} + +func (c *Compiler) currentSourceMap() map[int]parser.Pos { + return c.scopes[c.scopeIndex].SourceMap +} + +func (c *Compiler) enterScope() { + scope := compilationScope{ + SymbolInit: make(map[string]bool), + SourceMap: make(map[int]parser.Pos), + } + c.scopes = append(c.scopes, scope) + c.scopeIndex++ + c.symbolTable = c.symbolTable.Fork(false) + if c.trace != nil { + c.printTrace("SCOPE", c.scopeIndex) + } +} + +func (c *Compiler) leaveScope() ( + instructions []byte, + sourceMap map[int]parser.Pos, +) { + instructions = c.currentInstructions() + sourceMap = c.currentSourceMap() + c.scopes = c.scopes[:len(c.scopes)-1] + c.scopeIndex-- + c.symbolTable = c.symbolTable.Parent(true) + if c.trace != nil { + c.printTrace("SCOPL", c.scopeIndex) + } + return +} + +func (c *Compiler) fork( + file *parser.SourceFile, + modulePath string, + symbolTable *SymbolTable, +) *Compiler { + child := NewCompiler(file, symbolTable, nil, c.modules, c.trace) + child.modulePath = modulePath // module file path + child.parent = c // parent to set to current compiler + return child +} + +func (c *Compiler) error(node parser.Node, err error) error { + return &CompilerError{ + FileSet: c.file.Set(), + Node: node, + Err: err, + } +} + +func (c *Compiler) errorf( + node parser.Node, + format string, + args ...interface{}, +) error { + return &CompilerError{ + FileSet: c.file.Set(), + Node: node, + Err: fmt.Errorf(format, args...), + } +} + +func (c *Compiler) addConstant(o Object) int { + if c.parent != nil { + // module compilers will use their parent's constants array + return c.parent.addConstant(o) + } + c.constants = append(c.constants, o) + if c.trace != nil { + c.printTrace(fmt.Sprintf("CONST %04d %s", len(c.constants)-1, o)) + } + return len(c.constants) - 1 +} + +func (c *Compiler) addInstruction(b []byte) int { + posNewIns := len(c.currentInstructions()) + c.scopes[c.scopeIndex].Instructions = append( + c.currentInstructions(), b...) + return posNewIns +} + +func (c *Compiler) replaceInstruction(pos int, inst []byte) { + copy(c.currentInstructions()[pos:], inst) + if c.trace != nil { + c.printTrace(fmt.Sprintf("REPLC %s", + FormatInstructions( + c.scopes[c.scopeIndex].Instructions[pos:], pos)[0])) + } +} + +func (c *Compiler) changeOperand(opPos int, operand ...int) { + op := c.currentInstructions()[opPos] + inst := MakeInstruction(op, operand...) + c.replaceInstruction(opPos, inst) +} + +// optimizeFunc performs some code-level optimization for the current function +// instructions. It also removes unreachable (dead code) instructions and adds +// "returns" instruction if needed. +func (c *Compiler) optimizeFunc(node parser.Node) { + // any instructions between RETURN and the function end + // or instructions between RETURN and jump target position + // are considered as unreachable. + + // pass 1. identify all jump destinations + dsts := make(map[int]bool) + iterateInstructions(c.scopes[c.scopeIndex].Instructions, + func(pos int, opcode parser.Opcode, operands []int) bool { + switch opcode { + case parser.OpJump, parser.OpJumpFalsy, + parser.OpAndJump, parser.OpOrJump: + dsts[operands[0]] = true + } + return true + }) + + // pass 2. eliminate dead code + var newInsts []byte + posMap := make(map[int]int) // old position to new position + var dstIdx int + var deadCode bool + iterateInstructions(c.scopes[c.scopeIndex].Instructions, + func(pos int, opcode parser.Opcode, operands []int) bool { + switch { + case opcode == parser.OpReturn: + if deadCode { + return true + } + deadCode = true + case dsts[pos]: + dstIdx++ + deadCode = false + case deadCode: + return true + } + posMap[pos] = len(newInsts) + newInsts = append(newInsts, + MakeInstruction(opcode, operands...)...) + return true + }) + + // pass 3. update jump positions + var lastOp parser.Opcode + var appendReturn bool + endPos := len(c.scopes[c.scopeIndex].Instructions) + iterateInstructions(newInsts, + func(pos int, opcode parser.Opcode, operands []int) bool { + switch opcode { + case parser.OpJump, parser.OpJumpFalsy, parser.OpAndJump, + parser.OpOrJump: + newDst, ok := posMap[operands[0]] + if ok { + copy(newInsts[pos:], + MakeInstruction(opcode, newDst)) + } else if endPos == operands[0] { + // there's a jump instruction that jumps to the end of + // function compiler should append "return". + appendReturn = true + } else { + panic(fmt.Errorf("invalid jump position: %d", newDst)) + } + } + lastOp = opcode + return true + }) + if lastOp != parser.OpReturn { + appendReturn = true + } + + // pass 4. update source map + newSourceMap := make(map[int]parser.Pos) + for pos, srcPos := range c.scopes[c.scopeIndex].SourceMap { + newPos, ok := posMap[pos] + if ok { + newSourceMap[newPos] = srcPos + } + } + c.scopes[c.scopeIndex].Instructions = newInsts + c.scopes[c.scopeIndex].SourceMap = newSourceMap + + // append "return" + if appendReturn { + c.emit(node, parser.OpReturn, 0) + } +} + +func (c *Compiler) emit( + node parser.Node, + opcode parser.Opcode, + operands ...int, +) int { + filePos := parser.NoPos + if node != nil { + filePos = node.Pos() + } + + inst := MakeInstruction(opcode, operands...) + pos := c.addInstruction(inst) + c.scopes[c.scopeIndex].SourceMap[pos] = filePos + if c.trace != nil { + c.printTrace(fmt.Sprintf("EMIT %s", + FormatInstructions( + c.scopes[c.scopeIndex].Instructions[pos:], pos)[0])) + } + return pos +} + +func (c *Compiler) printTrace(a ...interface{}) { + const ( + dots = ". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . " + n = len(dots) + ) + + i := 2 * c.indent + for i > n { + _, _ = fmt.Fprint(c.trace, dots) + i -= n + } + _, _ = fmt.Fprint(c.trace, dots[0:i]) + _, _ = fmt.Fprintln(c.trace, a...) +} + +func resolveAssignLHS( + expr parser.Expr, +) (name string, selectors []parser.Expr) { + switch term := expr.(type) { + case *parser.SelectorExpr: + name, selectors = resolveAssignLHS(term.Expr) + selectors = append(selectors, term.Sel) + return + case *parser.IndexExpr: + name, selectors = resolveAssignLHS(term.Expr) + selectors = append(selectors, term.Index) + case *parser.Ident: + name = term.Name + } + return +} + +func iterateInstructions( + b []byte, + fn func(pos int, opcode parser.Opcode, operands []int) bool, +) { + for i := 0; i < len(b); i++ { + numOperands := parser.OpcodeOperands[b[i]] + operands, read := parser.ReadOperands(numOperands, b[i+1:]) + if !fn(i, b[i], operands) { + break + } + i += read + } +} + +func tracec(c *Compiler, msg string) *Compiler { + c.printTrace(msg, "{") + c.indent++ + return c +} + +func untracec(c *Compiler) { + c.indent-- + c.printTrace("}") +} diff --git a/vendor/github.com/d5/tengo/v2/doc.go b/vendor/github.com/d5/tengo/v2/doc.go new file mode 100644 index 00000000..05b47de1 --- /dev/null +++ b/vendor/github.com/d5/tengo/v2/doc.go @@ -0,0 +1,3 @@ +// tengo is a small, dynamic, fast, secure script language for Go. + +package tengo diff --git a/vendor/github.com/d5/tengo/v2/errors.go b/vendor/github.com/d5/tengo/v2/errors.go new file mode 100644 index 00000000..a3fd1f3b --- /dev/null +++ b/vendor/github.com/d5/tengo/v2/errors.go @@ -0,0 +1,64 @@ +package tengo + +import ( + "errors" + "fmt" +) + +var ( + // ErrStackOverflow is a stack overflow error. + ErrStackOverflow = errors.New("stack overflow") + + // ErrObjectAllocLimit is an objects allocation limit error. + ErrObjectAllocLimit = errors.New("object allocation limit exceeded") + + // ErrIndexOutOfBounds is an error where a given index is out of the + // bounds. + ErrIndexOutOfBounds = errors.New("index out of bounds") + + // ErrInvalidIndexType represents an invalid index type. + ErrInvalidIndexType = errors.New("invalid index type") + + // ErrInvalidIndexValueType represents an invalid index value type. + ErrInvalidIndexValueType = errors.New("invalid index value type") + + // ErrInvalidIndexOnError represents an invalid index on error. + ErrInvalidIndexOnError = errors.New("invalid index on error") + + // ErrInvalidOperator represents an error for invalid operator usage. + ErrInvalidOperator = errors.New("invalid operator") + + // ErrWrongNumArguments represents a wrong number of arguments error. + ErrWrongNumArguments = errors.New("wrong number of arguments") + + // ErrBytesLimit represents an error where the size of bytes value exceeds + // the limit. + ErrBytesLimit = errors.New("exceeding bytes size limit") + + // ErrStringLimit represents an error where the size of string value + // exceeds the limit. + ErrStringLimit = errors.New("exceeding string size limit") + + // ErrNotIndexable is an error where an Object is not indexable. + ErrNotIndexable = errors.New("not indexable") + + // ErrNotIndexAssignable is an error where an Object is not index + // assignable. + ErrNotIndexAssignable = errors.New("not index-assignable") + + // ErrNotImplemented is an error where an Object has not implemented a + // required method. + ErrNotImplemented = errors.New("not implemented") +) + +// 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) +} diff --git a/vendor/github.com/d5/tengo/objects/formatter.go b/vendor/github.com/d5/tengo/v2/formatter.go similarity index 91% rename from vendor/github.com/d5/tengo/objects/formatter.go rename to vendor/github.com/d5/tengo/v2/formatter.go index 95d7f6b1..0dbf71c6 100644 --- a/vendor/github.com/d5/tengo/objects/formatter.go +++ b/vendor/github.com/d5/tengo/v2/formatter.go @@ -1,15 +1,13 @@ -package objects +package tengo import ( "strconv" "sync" "unicode/utf8" - - "github.com/d5/tengo" ) -// Strings for use with buffer.WriteString. -// This is less overhead than using buffer.Write with byte arrays. +// Strings for use with fmtbuf.WriteString. This is less overhead than using +// fmtbuf.Write with byte arrays. const ( commaSpaceString = ", " nilParenString = "(nil)" @@ -55,9 +53,9 @@ type fmtFlags struct { } // A formatter is the raw formatter used by Printf etc. -// It prints into a buffer that must be set up separately. +// It prints into a fmtbuf that must be set up separately. type formatter struct { - buf *buffer + buf *fmtbuf fmtFlags @@ -69,13 +67,13 @@ type formatter struct { intbuf [68]byte } -func (f *formatter) clearflags() { +func (f *formatter) clearFlags() { f.fmtFlags = fmtFlags{} } -func (f *formatter) init(buf *buffer) { +func (f *formatter) init(buf *fmtbuf) { f.buf = buf - f.clearflags() + f.clearFlags() } // writePadding generates n bytes of padding. @@ -87,13 +85,13 @@ func (f *formatter) writePadding(n int) { oldLen := len(buf) newLen := oldLen + n - if newLen > tengo.MaxStringLen { + if newLen > MaxStringLen { panic(ErrStringLimit) } // Make enough room for padding. if newLen > cap(buf) { - buf = make(buffer, cap(buf)*2+n) + buf = make(fmtbuf, cap(buf)*2+n) copy(buf, *f.buf) } // Decide which byte the padding should be filled with. @@ -171,10 +169,12 @@ func (f *formatter) fmtUnicode(u uint64) { } } - // Format into buf, ending at buf[i]. Formatting numbers is easier right-to-left. + // Format into buf, ending at buf[i]. Formatting numbers is easier + // right-to-left. i := len(buf) - // For %#U we want to add a space and a quoted character at the end of the buffer. + // For %#U we want to add a space and a quoted character at the end of + // the fmtbuf. if f.sharp && u <= utf8.MaxRune && strconv.IsPrint(rune(u)) { i-- buf[i] = '\'' @@ -214,7 +214,13 @@ func (f *formatter) fmtUnicode(u uint64) { } // fmtInteger formats signed and unsigned integers. -func (f *formatter) fmtInteger(u uint64, base int, isSigned bool, verb rune, digits string) { +func (f *formatter) fmtInteger( + u uint64, + base int, + isSigned bool, + verb rune, + digits string, +) { negative := isSigned && int64(u) < 0 if negative { u = -u @@ -253,9 +259,10 @@ func (f *formatter) fmtInteger(u uint64, base int, isSigned bool, verb rune, dig } } - // Because printing is easier right-to-left: format u into buf, ending at buf[i]. - // We could make things marginally faster by splitting the 32-bit case out - // into a separate block but it's not worth the duplication, so u has 64 bits. + // Because printing is easier right-to-left: format u into buf, ending at + // buf[i]. We could make things marginally faster by splitting the 32-bit + // case out into a separate block but it's not worth the duplication, so + // u has 64 bits. i := len(buf) // Use constants for the division and modulo for more efficient code. // Switch cases ordered by popularity. @@ -357,7 +364,8 @@ func (f *formatter) truncateString(s string) string { return s } -// truncate truncates the byte slice b as a string of the specified precision, if present. +// truncate truncates the byte slice b as a string of the specified precision, +// if present. func (f *formatter) truncate(b []byte) []byte { if f.precPresent { n := f.prec @@ -399,11 +407,13 @@ func (f *formatter) fmtSbx(s string, b []byte, digits string) { if f.precPresent && f.prec < length { length = f.prec } - // Compute width of the encoding taking into account the f.sharp and f.space flag. + // Compute width of the encoding taking into account the f.sharp and + // f.space flag. width := 2 * length if width > 0 { if f.space { - // Each element encoded by two hexadecimals will get a leading 0x or 0X. + // Each element encoded by two hexadecimals will get a leading + // 0x or 0X. if f.sharp { width *= 2 } @@ -423,7 +433,7 @@ func (f *formatter) fmtSbx(s string, b []byte, digits string) { if f.widPresent && f.wid > width && !f.minus { f.writePadding(f.wid - width) } - // Write the encoding directly into the output buffer. + // Write the encoding directly into the output fmtbuf. buf := *f.buf if f.sharp { // Add leading 0x or 0X. @@ -589,8 +599,9 @@ func (f *formatter) fmtFloat(v float64, size int, verb rune, prec int) { } // We want a sign if asked for and if the sign is not positive. if f.plus || num[0] != '+' { - // If we're zero padding to the left we want the sign before the leading zeros. - // Achieve this by writing the sign out and then padding the unsigned number. + // If we're zero padding to the left we want the sign before the + // leading zeros. Achieve this by writing the sign out and then padding + // the unsigned number. if f.zero && f.widPresent && f.wid > len(num) { f.buf.WriteSingleByte(num[0]) f.writePadding(f.wid - len(num)) @@ -600,39 +611,40 @@ func (f *formatter) fmtFloat(v float64, size int, verb rune, prec int) { f.pad(num) return } - // No sign to show and the number is positive; just print the unsigned number. + // No sign to show and the number is positive; just print the unsigned + // number. f.pad(num[1:]) } // Use simple []byte instead of bytes.Buffer to avoid large dependency. -type buffer []byte +type fmtbuf []byte -func (b *buffer) Write(p []byte) { - if len(*b)+len(p) > tengo.MaxStringLen { +func (b *fmtbuf) Write(p []byte) { + if len(*b)+len(p) > MaxStringLen { panic(ErrStringLimit) } *b = append(*b, p...) } -func (b *buffer) WriteString(s string) { - if len(*b)+len(s) > tengo.MaxStringLen { +func (b *fmtbuf) WriteString(s string) { + if len(*b)+len(s) > MaxStringLen { panic(ErrStringLimit) } *b = append(*b, s...) } -func (b *buffer) WriteSingleByte(c byte) { - if len(*b) >= tengo.MaxStringLen { +func (b *fmtbuf) WriteSingleByte(c byte) { + if len(*b) >= MaxStringLen { panic(ErrStringLimit) } *b = append(*b, c) } -func (b *buffer) WriteRune(r rune) { - if len(*b)+utf8.RuneLen(r) > tengo.MaxStringLen { +func (b *fmtbuf) WriteRune(r rune) { + if len(*b)+utf8.RuneLen(r) > MaxStringLen { panic(ErrStringLimit) } @@ -650,9 +662,10 @@ func (b *buffer) WriteRune(r rune) { *b = b2[:n+w] } -// pp is used to store a printer's state and is reused with sync.Pool to avoid allocations. +// pp is used to store a printer's state and is reused with sync.Pool to avoid +// allocations. type pp struct { - buf buffer + buf fmtbuf // arg holds the current item. arg Object @@ -663,10 +676,12 @@ type pp struct { // reordered records whether the format string used argument reordering. reordered bool - // goodArgNum records whether the most recent reordering directive was valid. + // goodArgNum records whether the most recent reordering directive was + // valid. goodArgNum bool - // erroring is set when printing an error string to guard against calling handleMethods. + // erroring is set when printing an error string to guard against calling + // handleMethods. erroring bool } @@ -686,8 +701,8 @@ func newPrinter() *pp { func (p *pp) free() { // Proper usage of a sync.Pool requires each entry to have approximately // the same memory cost. To obtain this property when the stored type - // contains a variably-sized buffer, we add a hard limit on the maximum buffer - // to place back in the pool. + // contains a variably-sized fmtbuf, we add a hard limit on the maximum + // fmtbuf to place back in the pool. // // See https://golang.org/issue/23199 if cap(p.buf) > 64<<10 { @@ -699,9 +714,13 @@ func (p *pp) free() { ppFree.Put(p) } -func (p *pp) Width() (wid int, ok bool) { return p.fmt.wid, p.fmt.widPresent } +func (p *pp) Width() (wid int, ok bool) { + return p.fmt.wid, p.fmt.widPresent +} -func (p *pp) Precision() (prec int, ok bool) { return p.fmt.prec, p.fmt.precPresent } +func (p *pp) Precision() (prec int, ok bool) { + return p.fmt.prec, p.fmt.precPresent +} func (p *pp) Flag(b int) bool { switch b { @@ -750,7 +769,8 @@ func tooLarge(x int) bool { return x > max || x < -max } -// parsenum converts ASCII to integer. num is 0 (and isnum is false) if no number present. +// parsenum converts ASCII to integer. num is 0 (and isnum is false) if no +// number present. func parsenum(s string, start, end int) (num int, isnum bool, newi int) { if start >= end { return 0, false, end @@ -918,7 +938,8 @@ func (p *pp) printArg(arg Object, verb rune) { } // Special processing considerations. - // %T (the value's type) and %p (its address) are special; we always do them first. + // %T (the value's type) and %p (its address) are special; we always do + // them first. switch verb { case 'T': p.fmt.fmtS(arg.TypeName()) @@ -945,7 +966,8 @@ func (p *pp) printArg(arg Object, verb rune) { } } -// intFromArg gets the argNumth element of a. On return, isInt reports whether the argument has integer type. +// intFromArg gets the argNumth element of a. On return, isInt reports whether +// the argument has integer type. func intFromArg(a []Object, argNum int) (num int, isInt bool, newArgNum int) { newArgNum = argNum if argNum < len(a) { @@ -980,16 +1002,23 @@ func parseArgNumber(format string) (index int, wid int, ok bool) { if !ok || newi != i { return 0, i + 1, false } - return width - 1, i + 1, true // arg numbers are one-indexed and skip paren. + // arg numbers are one-indexed andskip paren. + return width - 1, i + 1, true } } return 0, 1, false } -// argNumber returns the next argument to evaluate, which is either the value of the passed-in -// argNum or the value of the bracketed integer that begins format[i:]. It also returns -// the new value of i, that is, the index of the next byte of the format to process. -func (p *pp) argNumber(argNum int, format string, i int, numArgs int) (newArgNum, newi int, found bool) { +// argNumber returns the next argument to evaluate, which is either the value +// of the passed-in argNum or the value of the bracketed integer that begins +// format[i:]. It also returns the new value of i, that is, the index of the +// next byte of the format to process. +func (p *pp) argNumber( + argNum int, + format string, + i int, + numArgs int, +) (newArgNum, newi int, found bool) { if len(format) <= i || format[i] != '[' { return argNum, i, false } @@ -1048,7 +1077,7 @@ formatLoop: i++ // Do we have flags? - p.fmt.clearflags() + p.fmt.clearFlags() simpleFormat: for ; i < end; i++ { c := format[i] @@ -1056,7 +1085,8 @@ formatLoop: case '#': p.fmt.sharp = true case '0': - p.fmt.zero = !p.fmt.minus // Only allow zero padding to the left. + // Only allow zero padding to the left. + p.fmt.zero = !p.fmt.minus case '+': p.fmt.plus = true case '-': @@ -1081,7 +1111,8 @@ formatLoop: i++ continue formatLoop } - // Format is more complex than simple flags and a verb or is malformed. + // Format is more complex than simple flags and a verb or is + // malformed. break simpleFormat } } @@ -1157,11 +1188,13 @@ formatLoop: i += size switch { - case verb == '%': // Percent does not absorb operands and ignores f.wid and f.prec. + case verb == '%': + // Percent does not absorb operands and ignores f.wid and f.prec. _, _ = p.WriteSingleByte('%') case !p.goodArgNum: p.badArgNum(verb) - case argNum >= len(a): // No argument left over to print for the current verb. + case argNum >= len(a): + // No argument left over to print for the current verb. p.missingArg(verb) case verb == 'v': // Go syntax @@ -1181,7 +1214,7 @@ formatLoop: // out of order, in which case it's too expensive to detect if they've all // been used and arguably OK if they're not. if !p.reordered && argNum < len(a) { - p.fmt.clearflags() + p.fmt.clearFlags() _, _ = p.WriteString(extraString) for i, arg := range a[argNum:] { if i > 0 { @@ -1201,7 +1234,7 @@ formatLoop: return nil } -// Format formats according to a format specifier and returns the resulting string. +// Format is like fmt.Sprintf but using Objects. func Format(format string, a ...Object) (string, error) { p := newPrinter() err := p.doFormat(format, a) diff --git a/vendor/github.com/d5/tengo/v2/go.mod b/vendor/github.com/d5/tengo/v2/go.mod new file mode 100644 index 00000000..732ff142 --- /dev/null +++ b/vendor/github.com/d5/tengo/v2/go.mod @@ -0,0 +1,3 @@ +module github.com/d5/tengo/v2 + +go 1.13 diff --git a/vendor/github.com/d5/tengo/go.sum b/vendor/github.com/d5/tengo/v2/go.sum similarity index 100% rename from vendor/github.com/d5/tengo/go.sum rename to vendor/github.com/d5/tengo/v2/go.sum diff --git a/vendor/github.com/d5/tengo/v2/instructions.go b/vendor/github.com/d5/tengo/v2/instructions.go new file mode 100644 index 00000000..eb1fbf27 --- /dev/null +++ b/vendor/github.com/d5/tengo/v2/instructions.go @@ -0,0 +1,61 @@ +package tengo + +import ( + "fmt" + + "github.com/d5/tengo/v2/parser" +) + +// MakeInstruction returns a bytecode for an opcode and the operands. +func MakeInstruction(opcode parser.Opcode, operands ...int) []byte { + numOperands := parser.OpcodeOperands[opcode] + + totalLen := 1 + for _, w := range numOperands { + totalLen += w + } + + instruction := make([]byte, totalLen) + instruction[0] = opcode + + offset := 1 + for i, o := range operands { + width := numOperands[i] + switch width { + case 1: + instruction[offset] = byte(o) + case 2: + n := uint16(o) + instruction[offset] = byte(n >> 8) + instruction[offset+1] = byte(n) + } + offset += width + } + return instruction +} + +// FormatInstructions returns string representation of bytecode instructions. +func FormatInstructions(b []byte, posOffset int) []string { + var out []string + + i := 0 + for i < len(b) { + numOperands := parser.OpcodeOperands[b[i]] + operands, read := parser.ReadOperands(numOperands, b[i+1:]) + + switch len(numOperands) { + case 0: + out = append(out, fmt.Sprintf("%04d %-7s", + posOffset+i, parser.OpcodeNames[b[i]])) + case 1: + out = append(out, fmt.Sprintf("%04d %-7s %-5d", + posOffset+i, parser.OpcodeNames[b[i]], operands[0])) + case 2: + out = append(out, fmt.Sprintf("%04d %-7s %-5d %-5d", + posOffset+i, parser.OpcodeNames[b[i]], + operands[0], operands[1])) + } + i += 1 + read + } + return out +} diff --git a/vendor/github.com/d5/tengo/v2/iterator.go b/vendor/github.com/d5/tengo/v2/iterator.go new file mode 100644 index 00000000..13adbbab --- /dev/null +++ b/vendor/github.com/d5/tengo/v2/iterator.go @@ -0,0 +1,209 @@ +package tengo + +// Iterator represents an iterator for underlying data type. +type Iterator interface { + Object + + // Next returns true if there are more elements to iterate. + Next() bool + + // Key returns the key or index value of the current element. + Key() Object + + // Value returns the value of the current element. + Value() Object +} + +// ArrayIterator is an iterator for an array. +type ArrayIterator struct { + ObjectImpl + v []Object + i int + l int +} + +// TypeName returns the name of the type. +func (i *ArrayIterator) TypeName() string { + return "array-iterator" +} + +func (i *ArrayIterator) String() string { + return "" +} + +// IsFalsy returns true if the value of the type is falsy. +func (i *ArrayIterator) IsFalsy() bool { + return true +} + +// Equals returns true if the value of the type is equal to the value of +// another object. +func (i *ArrayIterator) Equals(Object) bool { + return false +} + +// Copy returns a copy of the type. +func (i *ArrayIterator) Copy() Object { + return &ArrayIterator{v: i.v, i: i.i, l: i.l} +} + +// Next returns true if there are more elements to iterate. +func (i *ArrayIterator) Next() bool { + i.i++ + return i.i <= i.l +} + +// Key returns the key or index value of the current element. +func (i *ArrayIterator) Key() Object { + return &Int{Value: int64(i.i - 1)} +} + +// Value returns the value of the current element. +func (i *ArrayIterator) Value() Object { + return i.v[i.i-1] +} + +// BytesIterator represents an iterator for a string. +type BytesIterator struct { + ObjectImpl + v []byte + i int + l int +} + +// TypeName returns the name of the type. +func (i *BytesIterator) TypeName() string { + return "bytes-iterator" +} + +func (i *BytesIterator) String() string { + return "" +} + +// Equals returns true if the value of the type is equal to the value of +// another object. +func (i *BytesIterator) Equals(Object) bool { + return false +} + +// Copy returns a copy of the type. +func (i *BytesIterator) Copy() Object { + return &BytesIterator{v: i.v, i: i.i, l: i.l} +} + +// Next returns true if there are more elements to iterate. +func (i *BytesIterator) Next() bool { + i.i++ + return i.i <= i.l +} + +// Key returns the key or index value of the current element. +func (i *BytesIterator) Key() Object { + return &Int{Value: int64(i.i - 1)} +} + +// Value returns the value of the current element. +func (i *BytesIterator) Value() Object { + return &Int{Value: int64(i.v[i.i-1])} +} + +// MapIterator represents an iterator for the map. +type MapIterator struct { + ObjectImpl + v map[string]Object + k []string + i int + l int +} + +// TypeName returns the name of the type. +func (i *MapIterator) TypeName() string { + return "map-iterator" +} + +func (i *MapIterator) String() string { + return "" +} + +// IsFalsy returns true if the value of the type is falsy. +func (i *MapIterator) IsFalsy() bool { + return true +} + +// Equals returns true if the value of the type is equal to the value of +// another object. +func (i *MapIterator) Equals(Object) bool { + return false +} + +// Copy returns a copy of the type. +func (i *MapIterator) Copy() Object { + return &MapIterator{v: i.v, k: i.k, i: i.i, l: i.l} +} + +// Next returns true if there are more elements to iterate. +func (i *MapIterator) Next() bool { + i.i++ + return i.i <= i.l +} + +// Key returns the key or index value of the current element. +func (i *MapIterator) Key() Object { + k := i.k[i.i-1] + return &String{Value: k} +} + +// Value returns the value of the current element. +func (i *MapIterator) Value() Object { + k := i.k[i.i-1] + return i.v[k] +} + +// StringIterator represents an iterator for a string. +type StringIterator struct { + ObjectImpl + v []rune + i int + l int +} + +// TypeName returns the name of the type. +func (i *StringIterator) TypeName() string { + return "string-iterator" +} + +func (i *StringIterator) String() string { + return "" +} + +// IsFalsy returns true if the value of the type is falsy. +func (i *StringIterator) IsFalsy() bool { + return true +} + +// Equals returns true if the value of the type is equal to the value of +// another object. +func (i *StringIterator) Equals(Object) bool { + return false +} + +// Copy returns a copy of the type. +func (i *StringIterator) Copy() Object { + return &StringIterator{v: i.v, i: i.i, l: i.l} +} + +// Next returns true if there are more elements to iterate. +func (i *StringIterator) Next() bool { + i.i++ + return i.i <= i.l +} + +// Key returns the key or index value of the current element. +func (i *StringIterator) Key() Object { + return &Int{Value: int64(i.i - 1)} +} + +// Value returns the value of the current element. +func (i *StringIterator) Value() Object { + return &Char{Value: i.v[i.i-1]} +} diff --git a/vendor/github.com/d5/tengo/objects/module_map.go b/vendor/github.com/d5/tengo/v2/modules.go similarity index 63% rename from vendor/github.com/d5/tengo/objects/module_map.go rename to vendor/github.com/d5/tengo/v2/modules.go index 874b8a2b..c8fcde7f 100644 --- a/vendor/github.com/d5/tengo/objects/module_map.go +++ b/vendor/github.com/d5/tengo/v2/modules.go @@ -1,7 +1,13 @@ -package objects +package tengo -// ModuleMap represents a set of named modules. -// Use NewModuleMap to create a new module map. +// Importable interface represents importable module instance. +type Importable interface { + // Import should return either an Object or module source code ([]byte). + Import(moduleName string) (interface{}, error) +} + +// ModuleMap represents a set of named modules. Use NewModuleMap to create a +// new module map. type ModuleMap struct { m map[string]Importable } @@ -33,21 +39,21 @@ func (m *ModuleMap) Remove(name string) { delete(m.m, name) } -// Get returns an import module identified by name. -// It returns if the name is not found. +// Get returns an import module identified by name. It returns if the name is +// not found. func (m *ModuleMap) Get(name string) Importable { return m.m[name] } -// GetBuiltinModule returns a builtin module identified by name. -// It returns if the name is not found or the module is not a builtin module. +// GetBuiltinModule returns a builtin module identified by name. It returns +// if the name is not found or the module is not a builtin module. func (m *ModuleMap) GetBuiltinModule(name string) *BuiltinModule { mod, _ := m.m[name].(*BuiltinModule) return mod } -// GetSourceModule returns a source module identified by name. -// It returns if the name is not found or the module is not a source module. +// GetSourceModule returns a source module identified by name. It returns if +// the name is not found or the module is not a source module. func (m *ModuleMap) GetSourceModule(name string) *SourceModule { mod, _ := m.m[name].(*SourceModule) return mod @@ -75,3 +81,13 @@ func (m *ModuleMap) AddMap(o *ModuleMap) { m.m[name] = mod } } + +// SourceModule is an importable module that's written in Tengo. +type SourceModule struct { + Src []byte +} + +// Import returns a module source code. +func (m *SourceModule) Import(_ string) (interface{}, error) { + return m.Src, nil +} diff --git a/vendor/github.com/d5/tengo/v2/objects.go b/vendor/github.com/d5/tengo/v2/objects.go new file mode 100644 index 00000000..27c1d493 --- /dev/null +++ b/vendor/github.com/d5/tengo/v2/objects.go @@ -0,0 +1,1581 @@ +package tengo + +import ( + "bytes" + "fmt" + "math" + "strconv" + "strings" + "time" + + "github.com/d5/tengo/v2/parser" + "github.com/d5/tengo/v2/token" +) + +var ( + // TrueValue represents a true value. + TrueValue Object = &Bool{value: true} + + // FalseValue represents a false value. + FalseValue Object = &Bool{value: false} + + // UndefinedValue represents an undefined value. + UndefinedValue Object = &Undefined{} +) + +// Object represents an object in the VM. +type Object interface { + // TypeName should return the name of the type. + TypeName() string + + // String should return a string representation of the type's value. + String() string + + // BinaryOp should return another object that is the result of a given + // binary operator and a right-hand side object. If BinaryOp returns an + // error, the VM will treat it as a run-time error. + BinaryOp(op token.Token, rhs Object) (Object, error) + + // IsFalsy should return true if the value of the type should be considered + // as falsy. + IsFalsy() bool + + // Equals should return true if the value of the type should be considered + // as equal to the value of another object. + Equals(another Object) bool + + // Copy should return a copy of the type (and its value). Copy function + // will be used for copy() builtin function which is expected to deep-copy + // the values generally. + Copy() Object + + // IndexGet should take an index Object and return a result Object or an + // error for indexable objects. Indexable is an object that can take an + // index and return an object. If error is returned, the runtime will treat + // it as a run-time error and ignore returned value. If Object is not + // indexable, ErrNotIndexable should be returned as error. If nil is + // returned as value, it will be converted to UndefinedToken value by the + // runtime. + IndexGet(index Object) (value Object, err error) + + // IndexSet should take an index Object and a value Object for index + // assignable objects. Index assignable is an object that can take an index + // and a value on the left-hand side of the assignment statement. If Object + // is not index assignable, ErrNotIndexAssignable should be returned as + // error. If an error is returned, it will be treated as a run-time error. + IndexSet(index, value Object) error + + // Iterate should return an Iterator for the type. + Iterate() Iterator + + // CanIterate should return whether the Object can be Iterated. + CanIterate() bool + + // Call should take an arbitrary number of arguments and returns a return + // value and/or an error, which the VM will consider as a run-time error. + Call(args ...Object) (ret Object, err error) + + // CanCall should return whether the Object can be Called. + CanCall() bool +} + +// ObjectImpl represents a default Object Implementation. To defined a new +// value type, one can embed ObjectImpl in their type declarations to avoid +// implementing all non-significant methods. TypeName() and String() methods +// still need to be implemented. +type ObjectImpl struct { +} + +// TypeName returns the name of the type. +func (o *ObjectImpl) TypeName() string { + panic(ErrNotImplemented) +} + +func (o *ObjectImpl) String() string { + panic(ErrNotImplemented) +} + +// BinaryOp returns another object that is the result of a given binary +// operator and a right-hand side object. +func (o *ObjectImpl) BinaryOp(_ token.Token, _ Object) (Object, error) { + return nil, ErrInvalidOperator +} + +// Copy returns a copy of the type. +func (o *ObjectImpl) Copy() Object { + return nil +} + +// IsFalsy returns true if the value of the type is falsy. +func (o *ObjectImpl) IsFalsy() bool { + return false +} + +// Equals returns true if the value of the type is equal to the value of +// another object. +func (o *ObjectImpl) Equals(x Object) bool { + return o == x +} + +// IndexGet returns an element at a given index. +func (o *ObjectImpl) IndexGet(_ Object) (res Object, err error) { + return nil, ErrNotIndexable +} + +// IndexSet sets an element at a given index. +func (o *ObjectImpl) IndexSet(_, _ Object) (err error) { + return ErrNotIndexAssignable +} + +// Iterate returns an iterator. +func (o *ObjectImpl) Iterate() Iterator { + return nil +} + +// CanIterate returns whether the Object can be Iterated. +func (o *ObjectImpl) CanIterate() bool { + return false +} + +// Call takes an arbitrary number of arguments and returns a return value +// and/or an error. +func (o *ObjectImpl) Call(_ ...Object) (ret Object, err error) { + return nil, nil +} + +// CanCall returns whether the Object can be Called. +func (o *ObjectImpl) CanCall() bool { + return false +} + +// Array represents an array of objects. +type Array struct { + ObjectImpl + Value []Object +} + +// TypeName returns the name of the type. +func (o *Array) TypeName() string { + return "array" +} + +func (o *Array) String() string { + var elements []string + for _, e := range o.Value { + elements = append(elements, e.String()) + } + return fmt.Sprintf("[%s]", strings.Join(elements, ", ")) +} + +// BinaryOp returns another object that is the result of a given binary +// operator and a right-hand side object. +func (o *Array) BinaryOp(op token.Token, rhs Object) (Object, error) { + if rhs, ok := rhs.(*Array); ok { + switch op { + case token.Add: + if len(rhs.Value) == 0 { + return o, nil + } + return &Array{Value: append(o.Value, rhs.Value...)}, nil + } + } + return nil, ErrInvalidOperator +} + +// Copy returns a copy of the type. +func (o *Array) Copy() Object { + var c []Object + for _, elem := range o.Value { + c = append(c, elem.Copy()) + } + return &Array{Value: c} +} + +// IsFalsy returns true if the value of the type is falsy. +func (o *Array) IsFalsy() bool { + return len(o.Value) == 0 +} + +// Equals returns true if the value of the type is equal to the value of +// another object. +func (o *Array) Equals(x Object) bool { + var xVal []Object + switch x := x.(type) { + case *Array: + xVal = x.Value + case *ImmutableArray: + xVal = x.Value + default: + return false + } + if len(o.Value) != len(xVal) { + return false + } + for i, e := range o.Value { + if !e.Equals(xVal[i]) { + return false + } + } + return true +} + +// IndexGet returns an element at a given index. +func (o *Array) IndexGet(index Object) (res Object, err error) { + intIdx, ok := index.(*Int) + if !ok { + err = ErrInvalidIndexType + return + } + idxVal := int(intIdx.Value) + if idxVal < 0 || idxVal >= len(o.Value) { + res = UndefinedValue + return + } + res = o.Value[idxVal] + return +} + +// IndexSet sets an element at a given index. +func (o *Array) IndexSet(index, value Object) (err error) { + intIdx, ok := ToInt(index) + if !ok { + err = ErrInvalidIndexType + return + } + if intIdx < 0 || intIdx >= len(o.Value) { + err = ErrIndexOutOfBounds + return + } + o.Value[intIdx] = value + return nil +} + +// Iterate creates an array iterator. +func (o *Array) Iterate() Iterator { + return &ArrayIterator{ + v: o.Value, + l: len(o.Value), + } +} + +// CanIterate returns whether the Object can be Iterated. +func (o *Array) CanIterate() bool { + return true +} + +// Bool represents a boolean value. +type Bool struct { + ObjectImpl + + // this is intentionally non-public to force using objects.TrueValue and + // FalseValue always + value bool +} + +func (o *Bool) String() string { + if o.value { + return "true" + } + + return "false" +} + +// TypeName returns the name of the type. +func (o *Bool) TypeName() string { + return "bool" +} + +// Copy returns a copy of the type. +func (o *Bool) Copy() Object { + return o +} + +// IsFalsy returns true if the value of the type is falsy. +func (o *Bool) IsFalsy() bool { + return !o.value +} + +// Equals returns true if the value of the type is equal to the value of +// another object. +func (o *Bool) Equals(x Object) bool { + return o == x +} + +// GobDecode decodes bool value from input bytes. +func (o *Bool) GobDecode(b []byte) (err error) { + o.value = b[0] == 1 + return +} + +// GobEncode encodes bool values into bytes. +func (o *Bool) GobEncode() (b []byte, err error) { + if o.value { + b = []byte{1} + } else { + b = []byte{0} + } + return +} + +// BuiltinFunction represents a builtin function. +type BuiltinFunction struct { + ObjectImpl + Name string + Value CallableFunc +} + +// TypeName returns the name of the type. +func (o *BuiltinFunction) TypeName() string { + return "builtin-function:" + o.Name +} + +func (o *BuiltinFunction) String() string { + return "" +} + +// Copy returns a copy of the type. +func (o *BuiltinFunction) Copy() Object { + return &BuiltinFunction{Value: o.Value} +} + +// Equals returns true if the value of the type is equal to the value of +// another object. +func (o *BuiltinFunction) Equals(_ Object) bool { + return false +} + +// Call executes a builtin function. +func (o *BuiltinFunction) Call(args ...Object) (Object, error) { + return o.Value(args...) +} + +// CanCall returns whether the Object can be Called. +func (o *BuiltinFunction) CanCall() bool { + return true +} + +// BuiltinModule is an importable module that's written in Go. +type BuiltinModule struct { + Attrs map[string]Object +} + +// Import returns an immutable map for the module. +func (m *BuiltinModule) Import(moduleName string) (interface{}, error) { + return m.AsImmutableMap(moduleName), nil +} + +// AsImmutableMap converts builtin module into an immutable map. +func (m *BuiltinModule) AsImmutableMap(moduleName string) *ImmutableMap { + attrs := make(map[string]Object, len(m.Attrs)) + for k, v := range m.Attrs { + attrs[k] = v.Copy() + } + attrs["__module_name__"] = &String{Value: moduleName} + return &ImmutableMap{Value: attrs} +} + +// Bytes represents a byte array. +type Bytes struct { + ObjectImpl + Value []byte +} + +func (o *Bytes) String() string { + return string(o.Value) +} + +// TypeName returns the name of the type. +func (o *Bytes) TypeName() string { + return "bytes" +} + +// BinaryOp returns another object that is the result of a given binary +// operator and a right-hand side object. +func (o *Bytes) BinaryOp(op token.Token, rhs Object) (Object, error) { + switch op { + case token.Add: + switch rhs := rhs.(type) { + case *Bytes: + if len(o.Value)+len(rhs.Value) > MaxBytesLen { + return nil, ErrBytesLimit + } + return &Bytes{Value: append(o.Value, rhs.Value...)}, nil + } + } + return nil, ErrInvalidOperator +} + +// Copy returns a copy of the type. +func (o *Bytes) Copy() Object { + return &Bytes{Value: append([]byte{}, o.Value...)} +} + +// IsFalsy returns true if the value of the type is falsy. +func (o *Bytes) IsFalsy() bool { + return len(o.Value) == 0 +} + +// Equals returns true if the value of the type is equal to the value of +// another object. +func (o *Bytes) Equals(x Object) bool { + t, ok := x.(*Bytes) + if !ok { + return false + } + return bytes.Equal(o.Value, t.Value) +} + +// IndexGet returns an element (as Int) at a given index. +func (o *Bytes) IndexGet(index Object) (res Object, err error) { + intIdx, ok := index.(*Int) + if !ok { + err = ErrInvalidIndexType + return + } + idxVal := int(intIdx.Value) + if idxVal < 0 || idxVal >= len(o.Value) { + res = UndefinedValue + return + } + res = &Int{Value: int64(o.Value[idxVal])} + return +} + +// Iterate creates a bytes iterator. +func (o *Bytes) Iterate() Iterator { + return &BytesIterator{ + v: o.Value, + l: len(o.Value), + } +} + +// CanIterate returns whether the Object can be Iterated. +func (o *Bytes) CanIterate() bool { + return true +} + +// Char represents a character value. +type Char struct { + ObjectImpl + Value rune +} + +func (o *Char) String() string { + return string(o.Value) +} + +// TypeName returns the name of the type. +func (o *Char) TypeName() string { + return "char" +} + +// BinaryOp returns another object that is the result of a given binary +// operator and a right-hand side object. +func (o *Char) BinaryOp(op token.Token, rhs Object) (Object, error) { + switch rhs := rhs.(type) { + case *Char: + switch op { + case token.Add: + r := o.Value + rhs.Value + if r == o.Value { + return o, nil + } + return &Char{Value: r}, nil + case token.Sub: + r := o.Value - rhs.Value + if r == o.Value { + return o, nil + } + return &Char{Value: r}, nil + case token.Less: + if o.Value < rhs.Value { + return TrueValue, nil + } + return FalseValue, nil + case token.Greater: + if o.Value > rhs.Value { + return TrueValue, nil + } + return FalseValue, nil + case token.LessEq: + if o.Value <= rhs.Value { + return TrueValue, nil + } + return FalseValue, nil + case token.GreaterEq: + if o.Value >= rhs.Value { + return TrueValue, nil + } + return FalseValue, nil + } + case *Int: + switch op { + case token.Add: + r := o.Value + rune(rhs.Value) + if r == o.Value { + return o, nil + } + return &Char{Value: r}, nil + case token.Sub: + r := o.Value - rune(rhs.Value) + if r == o.Value { + return o, nil + } + return &Char{Value: r}, nil + case token.Less: + if int64(o.Value) < rhs.Value { + return TrueValue, nil + } + return FalseValue, nil + case token.Greater: + if int64(o.Value) > rhs.Value { + return TrueValue, nil + } + return FalseValue, nil + case token.LessEq: + if int64(o.Value) <= rhs.Value { + return TrueValue, nil + } + return FalseValue, nil + case token.GreaterEq: + if int64(o.Value) >= rhs.Value { + return TrueValue, nil + } + return FalseValue, nil + } + } + return nil, ErrInvalidOperator +} + +// Copy returns a copy of the type. +func (o *Char) Copy() Object { + return &Char{Value: o.Value} +} + +// IsFalsy returns true if the value of the type is falsy. +func (o *Char) IsFalsy() bool { + return o.Value == 0 +} + +// Equals returns true if the value of the type is equal to the value of +// another object. +func (o *Char) Equals(x Object) bool { + t, ok := x.(*Char) + if !ok { + return false + } + return o.Value == t.Value +} + +// CompiledFunction represents a compiled function. +type CompiledFunction struct { + ObjectImpl + Instructions []byte + NumLocals int // number of local variables (including function parameters) + NumParameters int + VarArgs bool + SourceMap map[int]parser.Pos + Free []*ObjectPtr +} + +// TypeName returns the name of the type. +func (o *CompiledFunction) TypeName() string { + return "compiled-function" +} + +func (o *CompiledFunction) String() string { + return "" +} + +// Copy returns a copy of the type. +func (o *CompiledFunction) Copy() Object { + return &CompiledFunction{ + Instructions: append([]byte{}, o.Instructions...), + NumLocals: o.NumLocals, + NumParameters: o.NumParameters, + VarArgs: o.VarArgs, + Free: append([]*ObjectPtr{}, o.Free...), // DO NOT Copy() of elements; these are variable pointers + } +} + +// Equals returns true if the value of the type is equal to the value of +// another object. +func (o *CompiledFunction) Equals(_ Object) bool { + return false +} + +// SourcePos returns the source position of the instruction at ip. +func (o *CompiledFunction) SourcePos(ip int) parser.Pos { + for ip >= 0 { + if p, ok := o.SourceMap[ip]; ok { + return p + } + ip-- + } + return parser.NoPos +} + +// CanCall returns whether the Object can be Called. +func (o *CompiledFunction) CanCall() bool { + return true +} + +// Error represents an error value. +type Error struct { + ObjectImpl + Value Object +} + +// TypeName returns the name of the type. +func (o *Error) TypeName() string { + return "error" +} + +func (o *Error) String() string { + if o.Value != nil { + return fmt.Sprintf("error: %s", o.Value.String()) + } + return "error" +} + +// IsFalsy returns true if the value of the type is falsy. +func (o *Error) IsFalsy() bool { + return true // error is always false. +} + +// Copy returns a copy of the type. +func (o *Error) Copy() Object { + return &Error{Value: o.Value.Copy()} +} + +// Equals returns true if the value of the type is equal to the value of +// another object. +func (o *Error) Equals(x Object) bool { + return o == x // pointer equality +} + +// IndexGet returns an element at a given index. +func (o *Error) IndexGet(index Object) (res Object, err error) { + if strIdx, _ := ToString(index); strIdx != "value" { + err = ErrInvalidIndexOnError + return + } + res = o.Value + return +} + +// Float represents a floating point number value. +type Float struct { + ObjectImpl + Value float64 +} + +func (o *Float) String() string { + return strconv.FormatFloat(o.Value, 'f', -1, 64) +} + +// TypeName returns the name of the type. +func (o *Float) TypeName() string { + return "float" +} + +// BinaryOp returns another object that is the result of a given binary +// operator and a right-hand side object. +func (o *Float) BinaryOp(op token.Token, rhs Object) (Object, error) { + switch rhs := rhs.(type) { + case *Float: + switch op { + case token.Add: + r := o.Value + rhs.Value + if r == o.Value { + return o, nil + } + return &Float{Value: r}, nil + case token.Sub: + r := o.Value - rhs.Value + if r == o.Value { + return o, nil + } + return &Float{Value: r}, nil + case token.Mul: + r := o.Value * rhs.Value + if r == o.Value { + return o, nil + } + return &Float{Value: r}, nil + case token.Quo: + r := o.Value / rhs.Value + if r == o.Value { + return o, nil + } + return &Float{Value: r}, nil + case token.Less: + if o.Value < rhs.Value { + return TrueValue, nil + } + return FalseValue, nil + case token.Greater: + if o.Value > rhs.Value { + return TrueValue, nil + } + return FalseValue, nil + case token.LessEq: + if o.Value <= rhs.Value { + return TrueValue, nil + } + return FalseValue, nil + case token.GreaterEq: + if o.Value >= rhs.Value { + return TrueValue, nil + } + return FalseValue, nil + } + case *Int: + switch op { + case token.Add: + r := o.Value + float64(rhs.Value) + if r == o.Value { + return o, nil + } + return &Float{Value: r}, nil + case token.Sub: + r := o.Value - float64(rhs.Value) + if r == o.Value { + return o, nil + } + return &Float{Value: r}, nil + case token.Mul: + r := o.Value * float64(rhs.Value) + if r == o.Value { + return o, nil + } + return &Float{Value: r}, nil + case token.Quo: + r := o.Value / float64(rhs.Value) + if r == o.Value { + return o, nil + } + return &Float{Value: r}, nil + case token.Less: + if o.Value < float64(rhs.Value) { + return TrueValue, nil + } + return FalseValue, nil + case token.Greater: + if o.Value > float64(rhs.Value) { + return TrueValue, nil + } + return FalseValue, nil + case token.LessEq: + if o.Value <= float64(rhs.Value) { + return TrueValue, nil + } + return FalseValue, nil + case token.GreaterEq: + if o.Value >= float64(rhs.Value) { + return TrueValue, nil + } + return FalseValue, nil + } + } + return nil, ErrInvalidOperator +} + +// Copy returns a copy of the type. +func (o *Float) Copy() Object { + return &Float{Value: o.Value} +} + +// IsFalsy returns true if the value of the type is falsy. +func (o *Float) IsFalsy() bool { + return math.IsNaN(o.Value) +} + +// Equals returns true if the value of the type is equal to the value of +// another object. +func (o *Float) Equals(x Object) bool { + t, ok := x.(*Float) + if !ok { + return false + } + return o.Value == t.Value +} + +// ImmutableArray represents an immutable array of objects. +type ImmutableArray struct { + ObjectImpl + Value []Object +} + +// TypeName returns the name of the type. +func (o *ImmutableArray) TypeName() string { + return "immutable-array" +} + +func (o *ImmutableArray) String() string { + var elements []string + for _, e := range o.Value { + elements = append(elements, e.String()) + } + return fmt.Sprintf("[%s]", strings.Join(elements, ", ")) +} + +// BinaryOp returns another object that is the result of a given binary +// operator and a right-hand side object. +func (o *ImmutableArray) BinaryOp(op token.Token, rhs Object) (Object, error) { + if rhs, ok := rhs.(*ImmutableArray); ok { + switch op { + case token.Add: + return &Array{Value: append(o.Value, rhs.Value...)}, nil + } + } + return nil, ErrInvalidOperator +} + +// Copy returns a copy of the type. +func (o *ImmutableArray) Copy() Object { + var c []Object + for _, elem := range o.Value { + c = append(c, elem.Copy()) + } + return &Array{Value: c} +} + +// IsFalsy returns true if the value of the type is falsy. +func (o *ImmutableArray) IsFalsy() bool { + return len(o.Value) == 0 +} + +// Equals returns true if the value of the type is equal to the value of +// another object. +func (o *ImmutableArray) Equals(x Object) bool { + var xVal []Object + switch x := x.(type) { + case *Array: + xVal = x.Value + case *ImmutableArray: + xVal = x.Value + default: + return false + } + if len(o.Value) != len(xVal) { + return false + } + for i, e := range o.Value { + if !e.Equals(xVal[i]) { + return false + } + } + return true +} + +// IndexGet returns an element at a given index. +func (o *ImmutableArray) IndexGet(index Object) (res Object, err error) { + intIdx, ok := index.(*Int) + if !ok { + err = ErrInvalidIndexType + return + } + idxVal := int(intIdx.Value) + if idxVal < 0 || idxVal >= len(o.Value) { + res = UndefinedValue + return + } + res = o.Value[idxVal] + return +} + +// Iterate creates an array iterator. +func (o *ImmutableArray) Iterate() Iterator { + return &ArrayIterator{ + v: o.Value, + l: len(o.Value), + } +} + +// CanIterate returns whether the Object can be Iterated. +func (o *ImmutableArray) CanIterate() bool { + return true +} + +// ImmutableMap represents an immutable map object. +type ImmutableMap struct { + ObjectImpl + Value map[string]Object +} + +// TypeName returns the name of the type. +func (o *ImmutableMap) TypeName() string { + return "immutable-map" +} + +func (o *ImmutableMap) String() string { + var pairs []string + for k, v := range o.Value { + pairs = append(pairs, fmt.Sprintf("%s: %s", k, v.String())) + } + return fmt.Sprintf("{%s}", strings.Join(pairs, ", ")) +} + +// Copy returns a copy of the type. +func (o *ImmutableMap) Copy() Object { + c := make(map[string]Object) + for k, v := range o.Value { + c[k] = v.Copy() + } + return &Map{Value: c} +} + +// IsFalsy returns true if the value of the type is falsy. +func (o *ImmutableMap) IsFalsy() bool { + return len(o.Value) == 0 +} + +// IndexGet returns the value for the given key. +func (o *ImmutableMap) IndexGet(index Object) (res Object, err error) { + strIdx, ok := ToString(index) + if !ok { + err = ErrInvalidIndexType + return + } + res, ok = o.Value[strIdx] + if !ok { + res = UndefinedValue + } + return +} + +// Equals returns true if the value of the type is equal to the value of +// another object. +func (o *ImmutableMap) Equals(x Object) bool { + var xVal map[string]Object + switch x := x.(type) { + case *Map: + xVal = x.Value + case *ImmutableMap: + xVal = x.Value + default: + return false + } + if len(o.Value) != len(xVal) { + return false + } + for k, v := range o.Value { + tv := xVal[k] + if !v.Equals(tv) { + return false + } + } + return true +} + +// Iterate creates an immutable map iterator. +func (o *ImmutableMap) Iterate() Iterator { + var keys []string + for k := range o.Value { + keys = append(keys, k) + } + return &MapIterator{ + v: o.Value, + k: keys, + l: len(keys), + } +} + +// CanIterate returns whether the Object can be Iterated. +func (o *ImmutableMap) CanIterate() bool { + return true +} + +// Int represents an integer value. +type Int struct { + ObjectImpl + Value int64 +} + +func (o *Int) String() string { + return strconv.FormatInt(o.Value, 10) +} + +// TypeName returns the name of the type. +func (o *Int) TypeName() string { + return "int" +} + +// BinaryOp returns another object that is the result of a given binary +// operator and a right-hand side object. +func (o *Int) BinaryOp(op token.Token, rhs Object) (Object, error) { + switch rhs := rhs.(type) { + case *Int: + switch op { + case token.Add: + r := o.Value + rhs.Value + if r == o.Value { + return o, nil + } + return &Int{Value: r}, nil + case token.Sub: + r := o.Value - rhs.Value + if r == o.Value { + return o, nil + } + return &Int{Value: r}, nil + case token.Mul: + r := o.Value * rhs.Value + if r == o.Value { + return o, nil + } + return &Int{Value: r}, nil + case token.Quo: + r := o.Value / rhs.Value + if r == o.Value { + return o, nil + } + return &Int{Value: r}, nil + case token.Rem: + r := o.Value % rhs.Value + if r == o.Value { + return o, nil + } + return &Int{Value: r}, nil + case token.And: + r := o.Value & rhs.Value + if r == o.Value { + return o, nil + } + return &Int{Value: r}, nil + case token.Or: + r := o.Value | rhs.Value + if r == o.Value { + return o, nil + } + return &Int{Value: r}, nil + case token.Xor: + r := o.Value ^ rhs.Value + if r == o.Value { + return o, nil + } + return &Int{Value: r}, nil + case token.AndNot: + r := o.Value &^ rhs.Value + if r == o.Value { + return o, nil + } + return &Int{Value: r}, nil + case token.Shl: + r := o.Value << uint64(rhs.Value) + if r == o.Value { + return o, nil + } + return &Int{Value: r}, nil + case token.Shr: + r := o.Value >> uint64(rhs.Value) + if r == o.Value { + return o, nil + } + return &Int{Value: r}, nil + case token.Less: + if o.Value < rhs.Value { + return TrueValue, nil + } + return FalseValue, nil + case token.Greater: + if o.Value > rhs.Value { + return TrueValue, nil + } + return FalseValue, nil + case token.LessEq: + if o.Value <= rhs.Value { + return TrueValue, nil + } + return FalseValue, nil + case token.GreaterEq: + if o.Value >= rhs.Value { + return TrueValue, nil + } + return FalseValue, nil + } + case *Float: + switch op { + case token.Add: + return &Float{Value: float64(o.Value) + rhs.Value}, nil + case token.Sub: + return &Float{Value: float64(o.Value) - rhs.Value}, nil + case token.Mul: + return &Float{Value: float64(o.Value) * rhs.Value}, nil + case token.Quo: + return &Float{Value: float64(o.Value) / rhs.Value}, nil + case token.Less: + if float64(o.Value) < rhs.Value { + return TrueValue, nil + } + return FalseValue, nil + case token.Greater: + if float64(o.Value) > rhs.Value { + return TrueValue, nil + } + return FalseValue, nil + case token.LessEq: + if float64(o.Value) <= rhs.Value { + return TrueValue, nil + } + return FalseValue, nil + case token.GreaterEq: + if float64(o.Value) >= rhs.Value { + return TrueValue, nil + } + return FalseValue, nil + } + case *Char: + switch op { + case token.Add: + return &Char{Value: rune(o.Value) + rhs.Value}, nil + case token.Sub: + return &Char{Value: rune(o.Value) - rhs.Value}, nil + case token.Less: + if o.Value < int64(rhs.Value) { + return TrueValue, nil + } + return FalseValue, nil + case token.Greater: + if o.Value > int64(rhs.Value) { + return TrueValue, nil + } + return FalseValue, nil + case token.LessEq: + if o.Value <= int64(rhs.Value) { + return TrueValue, nil + } + return FalseValue, nil + case token.GreaterEq: + if o.Value >= int64(rhs.Value) { + return TrueValue, nil + } + return FalseValue, nil + } + } + return nil, ErrInvalidOperator +} + +// Copy returns a copy of the type. +func (o *Int) Copy() Object { + return &Int{Value: o.Value} +} + +// IsFalsy returns true if the value of the type is falsy. +func (o *Int) IsFalsy() bool { + return o.Value == 0 +} + +// Equals returns true if the value of the type is equal to the value of +// another object. +func (o *Int) Equals(x Object) bool { + t, ok := x.(*Int) + if !ok { + return false + } + return o.Value == t.Value +} + +// Map represents a map of objects. +type Map struct { + ObjectImpl + Value map[string]Object +} + +// TypeName returns the name of the type. +func (o *Map) TypeName() string { + return "map" +} + +func (o *Map) String() string { + var pairs []string + for k, v := range o.Value { + pairs = append(pairs, fmt.Sprintf("%s: %s", k, v.String())) + } + return fmt.Sprintf("{%s}", strings.Join(pairs, ", ")) +} + +// Copy returns a copy of the type. +func (o *Map) Copy() Object { + c := make(map[string]Object) + for k, v := range o.Value { + c[k] = v.Copy() + } + return &Map{Value: c} +} + +// IsFalsy returns true if the value of the type is falsy. +func (o *Map) IsFalsy() bool { + return len(o.Value) == 0 +} + +// Equals returns true if the value of the type is equal to the value of +// another object. +func (o *Map) Equals(x Object) bool { + var xVal map[string]Object + switch x := x.(type) { + case *Map: + xVal = x.Value + case *ImmutableMap: + xVal = x.Value + default: + return false + } + if len(o.Value) != len(xVal) { + return false + } + for k, v := range o.Value { + tv := xVal[k] + if !v.Equals(tv) { + return false + } + } + return true +} + +// IndexGet returns the value for the given key. +func (o *Map) IndexGet(index Object) (res Object, err error) { + strIdx, ok := ToString(index) + if !ok { + err = ErrInvalidIndexType + return + } + res, ok = o.Value[strIdx] + if !ok { + res = UndefinedValue + } + return +} + +// IndexSet sets the value for the given key. +func (o *Map) IndexSet(index, value Object) (err error) { + strIdx, ok := ToString(index) + if !ok { + err = ErrInvalidIndexType + return + } + o.Value[strIdx] = value + return nil +} + +// Iterate creates a map iterator. +func (o *Map) Iterate() Iterator { + var keys []string + for k := range o.Value { + keys = append(keys, k) + } + return &MapIterator{ + v: o.Value, + k: keys, + l: len(keys), + } +} + +// CanIterate returns whether the Object can be Iterated. +func (o *Map) CanIterate() bool { + return true +} + +// ObjectPtr represents a free variable. +type ObjectPtr struct { + ObjectImpl + Value *Object +} + +func (o *ObjectPtr) String() string { + return "free-var" +} + +// TypeName returns the name of the type. +func (o *ObjectPtr) TypeName() string { + return "" +} + +// Copy returns a copy of the type. +func (o *ObjectPtr) Copy() Object { + return o +} + +// IsFalsy returns true if the value of the type is falsy. +func (o *ObjectPtr) IsFalsy() bool { + return o.Value == nil +} + +// Equals returns true if the value of the type is equal to the value of +// another object. +func (o *ObjectPtr) Equals(x Object) bool { + return o == x +} + +// String represents a string value. +type String struct { + ObjectImpl + Value string + runeStr []rune +} + +// TypeName returns the name of the type. +func (o *String) TypeName() string { + return "string" +} + +func (o *String) String() string { + return strconv.Quote(o.Value) +} + +// BinaryOp returns another object that is the result of a given binary +// operator and a right-hand side object. +func (o *String) BinaryOp(op token.Token, rhs Object) (Object, error) { + switch op { + case token.Add: + switch rhs := rhs.(type) { + case *String: + if len(o.Value)+len(rhs.Value) > MaxStringLen { + return nil, ErrStringLimit + } + return &String{Value: o.Value + rhs.Value}, nil + default: + rhsStr := rhs.String() + if len(o.Value)+len(rhsStr) > MaxStringLen { + return nil, ErrStringLimit + } + return &String{Value: o.Value + rhsStr}, nil + } + } + return nil, ErrInvalidOperator +} + +// IsFalsy returns true if the value of the type is falsy. +func (o *String) IsFalsy() bool { + return len(o.Value) == 0 +} + +// Copy returns a copy of the type. +func (o *String) Copy() Object { + return &String{Value: o.Value} +} + +// Equals returns true if the value of the type is equal to the value of +// another object. +func (o *String) Equals(x Object) bool { + t, ok := x.(*String) + if !ok { + return false + } + return o.Value == t.Value +} + +// IndexGet returns a character at a given index. +func (o *String) IndexGet(index Object) (res Object, err error) { + intIdx, ok := index.(*Int) + if !ok { + err = ErrInvalidIndexType + return + } + idxVal := int(intIdx.Value) + if o.runeStr == nil { + o.runeStr = []rune(o.Value) + } + if idxVal < 0 || idxVal >= len(o.runeStr) { + res = UndefinedValue + return + } + res = &Char{Value: o.runeStr[idxVal]} + return +} + +// Iterate creates a string iterator. +func (o *String) Iterate() Iterator { + if o.runeStr == nil { + o.runeStr = []rune(o.Value) + } + return &StringIterator{ + v: o.runeStr, + l: len(o.runeStr), + } +} + +// CanIterate returns whether the Object can be Iterated. +func (o *String) CanIterate() bool { + return true +} + +// Time represents a time value. +type Time struct { + ObjectImpl + Value time.Time +} + +func (o *Time) String() string { + return o.Value.String() +} + +// TypeName returns the name of the type. +func (o *Time) TypeName() string { + return "time" +} + +// BinaryOp returns another object that is the result of a given binary +// operator and a right-hand side object. +func (o *Time) BinaryOp(op token.Token, rhs Object) (Object, error) { + switch rhs := rhs.(type) { + case *Int: + switch op { + case token.Add: // time + int => time + if rhs.Value == 0 { + return o, nil + } + return &Time{Value: o.Value.Add(time.Duration(rhs.Value))}, nil + case token.Sub: // time - int => time + if rhs.Value == 0 { + return o, nil + } + return &Time{Value: o.Value.Add(time.Duration(-rhs.Value))}, nil + } + case *Time: + switch op { + case token.Sub: // time - time => int (duration) + return &Int{Value: int64(o.Value.Sub(rhs.Value))}, nil + case token.Less: // time < time => bool + if o.Value.Before(rhs.Value) { + return TrueValue, nil + } + return FalseValue, nil + case token.Greater: + if o.Value.After(rhs.Value) { + return TrueValue, nil + } + return FalseValue, nil + case token.LessEq: + if o.Value.Equal(rhs.Value) || o.Value.Before(rhs.Value) { + return TrueValue, nil + } + return FalseValue, nil + case token.GreaterEq: + if o.Value.Equal(rhs.Value) || o.Value.After(rhs.Value) { + return TrueValue, nil + } + return FalseValue, nil + } + } + return nil, ErrInvalidOperator +} + +// Copy returns a copy of the type. +func (o *Time) Copy() Object { + return &Time{Value: o.Value} +} + +// IsFalsy returns true if the value of the type is falsy. +func (o *Time) IsFalsy() bool { + return o.Value.IsZero() +} + +// Equals returns true if the value of the type is equal to the value of +// another object. +func (o *Time) Equals(x Object) bool { + t, ok := x.(*Time) + if !ok { + return false + } + return o.Value.Equal(t.Value) +} + +// Undefined represents an undefined value. +type Undefined struct { + ObjectImpl +} + +// TypeName returns the name of the type. +func (o *Undefined) TypeName() string { + return "undefined" +} + +func (o *Undefined) String() string { + return "" +} + +// Copy returns a copy of the type. +func (o *Undefined) Copy() Object { + return o +} + +// IsFalsy returns true if the value of the type is falsy. +func (o *Undefined) IsFalsy() bool { + return true +} + +// Equals returns true if the value of the type is equal to the value of +// another object. +func (o *Undefined) Equals(x Object) bool { + return o == x +} + +// IndexGet returns an element at a given index. +func (o *Undefined) IndexGet(_ Object) (Object, error) { + return UndefinedValue, nil +} + +// Iterate creates a map iterator. +func (o *Undefined) Iterate() Iterator { + return o +} + +// CanIterate returns whether the Object can be Iterated. +func (o *Undefined) CanIterate() bool { + return true +} + +// Next returns true if there are more elements to iterate. +func (o *Undefined) Next() bool { + return false +} + +// Key returns the key or index value of the current element. +func (o *Undefined) Key() Object { + return o +} + +// Value returns the value of the current element. +func (o *Undefined) Value() Object { + return o +} + +// UserFunction represents a user function. +type UserFunction struct { + ObjectImpl + Name string + Value CallableFunc + EncodingID string +} + +// TypeName returns the name of the type. +func (o *UserFunction) TypeName() string { + return "user-function:" + o.Name +} + +func (o *UserFunction) String() string { + return "" +} + +// Copy returns a copy of the type. +func (o *UserFunction) Copy() Object { + return &UserFunction{Value: o.Value} +} + +// Equals returns true if the value of the type is equal to the value of +// another object. +func (o *UserFunction) Equals(_ Object) bool { + return false +} + +// Call invokes a user function. +func (o *UserFunction) Call(args ...Object) (Object, error) { + return o.Value(args...) +} + +// CanCall returns whether the Object can be Called. +func (o *UserFunction) CanCall() bool { + return true +} diff --git a/vendor/github.com/d5/tengo/compiler/ast/ident_list.go b/vendor/github.com/d5/tengo/v2/parser/ast.go similarity index 65% rename from vendor/github.com/d5/tengo/compiler/ast/ident_list.go rename to vendor/github.com/d5/tengo/v2/parser/ast.go index 8dd6d307..8c2f7c07 100644 --- a/vendor/github.com/d5/tengo/compiler/ast/ident_list.go +++ b/vendor/github.com/d5/tengo/v2/parser/ast.go @@ -1,43 +1,51 @@ -package ast +package parser import ( "strings" - - "github.com/d5/tengo/compiler/source" ) +const ( + nullRep = "" +) + +// Node represents a node in the AST. +type Node interface { + // Pos returns the position of first character belonging to the node. + Pos() Pos + // End returns the position of first character immediately after the node. + End() Pos + // String returns a string representation of the node. + String() string +} + // IdentList represents a list of identifiers. type IdentList struct { - LParen source.Pos + LParen Pos VarArgs bool List []*Ident - RParen source.Pos + RParen Pos } // Pos returns the position of first character belonging to the node. -func (n *IdentList) Pos() source.Pos { +func (n *IdentList) Pos() Pos { if n.LParen.IsValid() { return n.LParen } - if len(n.List) > 0 { return n.List[0].Pos() } - - return source.NoPos + return NoPos } // End returns the position of first character immediately after the node. -func (n *IdentList) End() source.Pos { +func (n *IdentList) End() Pos { if n.RParen.IsValid() { return n.RParen + 1 } - if l := len(n.List); l > 0 { return n.List[l-1].End() } - - return source.NoPos + return NoPos } // NumFields returns the number of fields. @@ -45,7 +53,6 @@ func (n *IdentList) NumFields() int { if n == nil { return 0 } - return len(n.List) } @@ -58,6 +65,5 @@ func (n *IdentList) String() string { list = append(list, e.String()) } } - return "(" + strings.Join(list, ", ") + ")" } diff --git a/vendor/github.com/d5/tengo/v2/parser/expr.go b/vendor/github.com/d5/tengo/v2/parser/expr.go new file mode 100644 index 00000000..71e5155b --- /dev/null +++ b/vendor/github.com/d5/tengo/v2/parser/expr.go @@ -0,0 +1,597 @@ +package parser + +import ( + "strings" + + "github.com/d5/tengo/v2/token" +) + +// Expr represents an expression node in the AST. +type Expr interface { + Node + exprNode() +} + +// ArrayLit represents an array literal. +type ArrayLit struct { + Elements []Expr + LBrack Pos + RBrack Pos +} + +func (e *ArrayLit) exprNode() {} + +// Pos returns the position of first character belonging to the node. +func (e *ArrayLit) Pos() Pos { + return e.LBrack +} + +// End returns the position of first character immediately after the node. +func (e *ArrayLit) End() Pos { + return e.RBrack + 1 +} + +func (e *ArrayLit) String() string { + var elements []string + for _, m := range e.Elements { + elements = append(elements, m.String()) + } + return "[" + strings.Join(elements, ", ") + "]" +} + +// BadExpr represents a bad expression. +type BadExpr struct { + From Pos + To Pos +} + +func (e *BadExpr) exprNode() {} + +// Pos returns the position of first character belonging to the node. +func (e *BadExpr) Pos() Pos { + return e.From +} + +// End returns the position of first character immediately after the node. +func (e *BadExpr) End() Pos { + return e.To +} + +func (e *BadExpr) String() string { + return "" +} + +// BinaryExpr represents a binary operator expression. +type BinaryExpr struct { + LHS Expr + RHS Expr + Token token.Token + TokenPos Pos +} + +func (e *BinaryExpr) exprNode() {} + +// Pos returns the position of first character belonging to the node. +func (e *BinaryExpr) Pos() Pos { + return e.LHS.Pos() +} + +// End returns the position of first character immediately after the node. +func (e *BinaryExpr) End() Pos { + return e.RHS.End() +} + +func (e *BinaryExpr) String() string { + return "(" + e.LHS.String() + " " + e.Token.String() + + " " + e.RHS.String() + ")" +} + +// BoolLit represents a boolean literal. +type BoolLit struct { + Value bool + ValuePos Pos + Literal string +} + +func (e *BoolLit) exprNode() {} + +// Pos returns the position of first character belonging to the node. +func (e *BoolLit) Pos() Pos { + return e.ValuePos +} + +// End returns the position of first character immediately after the node. +func (e *BoolLit) End() Pos { + return Pos(int(e.ValuePos) + len(e.Literal)) +} + +func (e *BoolLit) String() string { + return e.Literal +} + +// CallExpr represents a function call expression. +type CallExpr struct { + Func Expr + LParen Pos + Args []Expr + RParen Pos +} + +func (e *CallExpr) exprNode() {} + +// Pos returns the position of first character belonging to the node. +func (e *CallExpr) Pos() Pos { + return e.Func.Pos() +} + +// End returns the position of first character immediately after the node. +func (e *CallExpr) End() Pos { + return e.RParen + 1 +} + +func (e *CallExpr) String() string { + var args []string + for _, e := range e.Args { + args = append(args, e.String()) + } + return e.Func.String() + "(" + strings.Join(args, ", ") + ")" +} + +// CharLit represents a character literal. +type CharLit struct { + Value rune + ValuePos Pos + Literal string +} + +func (e *CharLit) exprNode() {} + +// Pos returns the position of first character belonging to the node. +func (e *CharLit) Pos() Pos { + return e.ValuePos +} + +// End returns the position of first character immediately after the node. +func (e *CharLit) End() Pos { + return Pos(int(e.ValuePos) + len(e.Literal)) +} + +func (e *CharLit) String() string { + return e.Literal +} + +// CondExpr represents a ternary conditional expression. +type CondExpr struct { + Cond Expr + True Expr + False Expr + QuestionPos Pos + ColonPos Pos +} + +func (e *CondExpr) exprNode() {} + +// Pos returns the position of first character belonging to the node. +func (e *CondExpr) Pos() Pos { + return e.Cond.Pos() +} + +// End returns the position of first character immediately after the node. +func (e *CondExpr) End() Pos { + return e.False.End() +} + +func (e *CondExpr) String() string { + return "(" + e.Cond.String() + " ? " + e.True.String() + + " : " + e.False.String() + ")" +} + +// ErrorExpr represents an error expression +type ErrorExpr struct { + Expr Expr + ErrorPos Pos + LParen Pos + RParen Pos +} + +func (e *ErrorExpr) exprNode() {} + +// Pos returns the position of first character belonging to the node. +func (e *ErrorExpr) Pos() Pos { + return e.ErrorPos +} + +// End returns the position of first character immediately after the node. +func (e *ErrorExpr) End() Pos { + return e.RParen +} + +func (e *ErrorExpr) String() string { + return "error(" + e.Expr.String() + ")" +} + +// FloatLit represents a floating point literal. +type FloatLit struct { + Value float64 + ValuePos Pos + Literal string +} + +func (e *FloatLit) exprNode() {} + +// Pos returns the position of first character belonging to the node. +func (e *FloatLit) Pos() Pos { + return e.ValuePos +} + +// End returns the position of first character immediately after the node. +func (e *FloatLit) End() Pos { + return Pos(int(e.ValuePos) + len(e.Literal)) +} + +func (e *FloatLit) String() string { + return e.Literal +} + +// FuncLit represents a function literal. +type FuncLit struct { + Type *FuncType + Body *BlockStmt +} + +func (e *FuncLit) exprNode() {} + +// Pos returns the position of first character belonging to the node. +func (e *FuncLit) Pos() Pos { + return e.Type.Pos() +} + +// End returns the position of first character immediately after the node. +func (e *FuncLit) End() Pos { + return e.Body.End() +} + +func (e *FuncLit) String() string { + return "func" + e.Type.Params.String() + " " + e.Body.String() +} + +// FuncType represents a function type definition. +type FuncType struct { + FuncPos Pos + Params *IdentList +} + +func (e *FuncType) exprNode() {} + +// Pos returns the position of first character belonging to the node. +func (e *FuncType) Pos() Pos { + return e.FuncPos +} + +// End returns the position of first character immediately after the node. +func (e *FuncType) End() Pos { + return e.Params.End() +} + +func (e *FuncType) String() string { + return "func" + e.Params.String() +} + +// Ident represents an identifier. +type Ident struct { + Name string + NamePos Pos +} + +func (e *Ident) exprNode() {} + +// Pos returns the position of first character belonging to the node. +func (e *Ident) Pos() Pos { + return e.NamePos +} + +// End returns the position of first character immediately after the node. +func (e *Ident) End() Pos { + return Pos(int(e.NamePos) + len(e.Name)) +} + +func (e *Ident) String() string { + if e != nil { + return e.Name + } + return nullRep +} + +// ImmutableExpr represents an immutable expression +type ImmutableExpr struct { + Expr Expr + ErrorPos Pos + LParen Pos + RParen Pos +} + +func (e *ImmutableExpr) exprNode() {} + +// Pos returns the position of first character belonging to the node. +func (e *ImmutableExpr) Pos() Pos { + return e.ErrorPos +} + +// End returns the position of first character immediately after the node. +func (e *ImmutableExpr) End() Pos { + return e.RParen +} + +func (e *ImmutableExpr) String() string { + return "immutable(" + e.Expr.String() + ")" +} + +// ImportExpr represents an import expression +type ImportExpr struct { + ModuleName string + Token token.Token + TokenPos Pos +} + +func (e *ImportExpr) exprNode() {} + +// Pos returns the position of first character belonging to the node. +func (e *ImportExpr) Pos() Pos { + return e.TokenPos +} + +// End returns the position of first character immediately after the node. +func (e *ImportExpr) End() Pos { + // import("moduleName") + return Pos(int(e.TokenPos) + 10 + len(e.ModuleName)) +} + +func (e *ImportExpr) String() string { + return `import("` + e.ModuleName + `")"` +} + +// IndexExpr represents an index expression. +type IndexExpr struct { + Expr Expr + LBrack Pos + Index Expr + RBrack Pos +} + +func (e *IndexExpr) exprNode() {} + +// Pos returns the position of first character belonging to the node. +func (e *IndexExpr) Pos() Pos { + return e.Expr.Pos() +} + +// End returns the position of first character immediately after the node. +func (e *IndexExpr) End() Pos { + return e.RBrack + 1 +} + +func (e *IndexExpr) String() string { + var index string + if e.Index != nil { + index = e.Index.String() + } + return e.Expr.String() + "[" + index + "]" +} + +// IntLit represents an integer literal. +type IntLit struct { + Value int64 + ValuePos Pos + Literal string +} + +func (e *IntLit) exprNode() {} + +// Pos returns the position of first character belonging to the node. +func (e *IntLit) Pos() Pos { + return e.ValuePos +} + +// End returns the position of first character immediately after the node. +func (e *IntLit) End() Pos { + return Pos(int(e.ValuePos) + len(e.Literal)) +} + +func (e *IntLit) String() string { + return e.Literal +} + +// MapElementLit represents a map element. +type MapElementLit struct { + Key string + KeyPos Pos + ColonPos Pos + Value Expr +} + +func (e *MapElementLit) exprNode() {} + +// Pos returns the position of first character belonging to the node. +func (e *MapElementLit) Pos() Pos { + return e.KeyPos +} + +// End returns the position of first character immediately after the node. +func (e *MapElementLit) End() Pos { + return e.Value.End() +} + +func (e *MapElementLit) String() string { + return e.Key + ": " + e.Value.String() +} + +// MapLit represents a map literal. +type MapLit struct { + LBrace Pos + Elements []*MapElementLit + RBrace Pos +} + +func (e *MapLit) exprNode() {} + +// Pos returns the position of first character belonging to the node. +func (e *MapLit) Pos() Pos { + return e.LBrace +} + +// End returns the position of first character immediately after the node. +func (e *MapLit) End() Pos { + return e.RBrace + 1 +} + +func (e *MapLit) String() string { + var elements []string + for _, m := range e.Elements { + elements = append(elements, m.String()) + } + return "{" + strings.Join(elements, ", ") + "}" +} + +// ParenExpr represents a parenthesis wrapped expression. +type ParenExpr struct { + Expr Expr + LParen Pos + RParen Pos +} + +func (e *ParenExpr) exprNode() {} + +// Pos returns the position of first character belonging to the node. +func (e *ParenExpr) Pos() Pos { + return e.LParen +} + +// End returns the position of first character immediately after the node. +func (e *ParenExpr) End() Pos { + return e.RParen + 1 +} + +func (e *ParenExpr) String() string { + return "(" + e.Expr.String() + ")" +} + +// SelectorExpr represents a selector expression. +type SelectorExpr struct { + Expr Expr + Sel Expr +} + +func (e *SelectorExpr) exprNode() {} + +// Pos returns the position of first character belonging to the node. +func (e *SelectorExpr) Pos() Pos { + return e.Expr.Pos() +} + +// End returns the position of first character immediately after the node. +func (e *SelectorExpr) End() Pos { + return e.Sel.End() +} + +func (e *SelectorExpr) String() string { + return e.Expr.String() + "." + e.Sel.String() +} + +// SliceExpr represents a slice expression. +type SliceExpr struct { + Expr Expr + LBrack Pos + Low Expr + High Expr + RBrack Pos +} + +func (e *SliceExpr) exprNode() {} + +// Pos returns the position of first character belonging to the node. +func (e *SliceExpr) Pos() Pos { + return e.Expr.Pos() +} + +// End returns the position of first character immediately after the node. +func (e *SliceExpr) End() Pos { + return e.RBrack + 1 +} + +func (e *SliceExpr) String() string { + var low, high string + if e.Low != nil { + low = e.Low.String() + } + if e.High != nil { + high = e.High.String() + } + return e.Expr.String() + "[" + low + ":" + high + "]" +} + +// StringLit represents a string literal. +type StringLit struct { + Value string + ValuePos Pos + Literal string +} + +func (e *StringLit) exprNode() {} + +// Pos returns the position of first character belonging to the node. +func (e *StringLit) Pos() Pos { + return e.ValuePos +} + +// End returns the position of first character immediately after the node. +func (e *StringLit) End() Pos { + return Pos(int(e.ValuePos) + len(e.Literal)) +} + +func (e *StringLit) String() string { + return e.Literal +} + +// UnaryExpr represents an unary operator expression. +type UnaryExpr struct { + Expr Expr + Token token.Token + TokenPos Pos +} + +func (e *UnaryExpr) exprNode() {} + +// Pos returns the position of first character belonging to the node. +func (e *UnaryExpr) Pos() Pos { + return e.Expr.Pos() +} + +// End returns the position of first character immediately after the node. +func (e *UnaryExpr) End() Pos { + return e.Expr.End() +} + +func (e *UnaryExpr) String() string { + return "(" + e.Token.String() + e.Expr.String() + ")" +} + +// UndefinedLit represents an undefined literal. +type UndefinedLit struct { + TokenPos Pos +} + +func (e *UndefinedLit) exprNode() {} + +// Pos returns the position of first character belonging to the node. +func (e *UndefinedLit) Pos() Pos { + return e.TokenPos +} + +// End returns the position of first character immediately after the node. +func (e *UndefinedLit) End() Pos { + return e.TokenPos + 9 // len(undefined) == 9 +} + +func (e *UndefinedLit) String() string { + return "undefined" +} diff --git a/vendor/github.com/d5/tengo/compiler/ast/file.go b/vendor/github.com/d5/tengo/v2/parser/file.go similarity index 62% rename from vendor/github.com/d5/tengo/compiler/ast/file.go rename to vendor/github.com/d5/tengo/v2/parser/file.go index fc18b2d7..7cf50fea 100644 --- a/vendor/github.com/d5/tengo/compiler/ast/file.go +++ b/vendor/github.com/d5/tengo/v2/parser/file.go @@ -1,25 +1,23 @@ -package ast +package parser import ( "strings" - - "github.com/d5/tengo/compiler/source" ) // File represents a file unit. type File struct { - InputFile *source.File + InputFile *SourceFile Stmts []Stmt } // Pos returns the position of first character belonging to the node. -func (n *File) Pos() source.Pos { - return source.Pos(n.InputFile.Base) +func (n *File) Pos() Pos { + return 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) +func (n *File) End() Pos { + return Pos(n.InputFile.Base + n.InputFile.Size) } func (n *File) String() string { @@ -27,6 +25,5 @@ func (n *File) String() string { for _, e := range n.Stmts { stmts = append(stmts, e.String()) } - return strings.Join(stmts, "; ") } diff --git a/vendor/github.com/d5/tengo/compiler/opcodes.go b/vendor/github.com/d5/tengo/v2/parser/opcodes.go similarity index 95% rename from vendor/github.com/d5/tengo/compiler/opcodes.go rename to vendor/github.com/d5/tengo/v2/parser/opcodes.go index d832ee17..a4fbfbaf 100644 --- a/vendor/github.com/d5/tengo/compiler/opcodes.go +++ b/vendor/github.com/d5/tengo/v2/parser/opcodes.go @@ -1,4 +1,4 @@ -package compiler +package parser // Opcode represents a single byte operation code. type Opcode = byte @@ -45,10 +45,11 @@ const ( OpIteratorNext // Iterator next OpIteratorKey // Iterator key OpIteratorValue // Iterator value - OpBinaryOp // Binary Operation + OpBinaryOp // Binary operation + OpSuspend // Suspend VM ) -// OpcodeNames is opcode names. +// OpcodeNames are string representation of opcodes. var OpcodeNames = [...]string{ OpConstant: "CONST", OpPop: "POP", @@ -91,6 +92,7 @@ var OpcodeNames = [...]string{ OpIteratorKey: "ITKEY", OpIteratorValue: "ITVAL", OpBinaryOp: "BINARYOP", + OpSuspend: "SUSPEND", } // OpcodeOperands is the number of operands. @@ -136,6 +138,7 @@ var OpcodeOperands = [...][]int{ OpIteratorKey: {}, OpIteratorValue: {}, OpBinaryOp: {1}, + OpSuspend: {}, } // ReadOperands reads operands from the bytecode. @@ -147,9 +150,7 @@ func ReadOperands(numOperands []int, ins []byte) (operands []int, offset int) { case 2: operands = append(operands, int(ins[offset+1])|int(ins[offset])<<8) } - offset += width } - return } diff --git a/vendor/github.com/d5/tengo/compiler/parser/parser.go b/vendor/github.com/d5/tengo/v2/parser/parser.go similarity index 67% rename from vendor/github.com/d5/tengo/compiler/parser/parser.go rename to vendor/github.com/d5/tengo/v2/parser/parser.go index 27dd48f0..501a9106 100644 --- a/vendor/github.com/d5/tengo/compiler/parser/parser.go +++ b/vendor/github.com/d5/tengo/v2/parser/parser.go @@ -1,63 +1,128 @@ -/* - Parser parses the Tengo source files. - - Parser is a modified version of Go's parser implementation. - - Copyright 2009 The Go Authors. All rights reserved. - Use of this source code is governed by a BSD-style - license that can be found in the LICENSE file. -*/ - package parser import ( "fmt" "io" + "sort" "strconv" - "github.com/d5/tengo/compiler/ast" - "github.com/d5/tengo/compiler/scanner" - "github.com/d5/tengo/compiler/source" - "github.com/d5/tengo/compiler/token" + "github.com/d5/tengo/v2/token" ) type bailout struct{} -// Parser parses the Tengo source files. +var stmtStart = map[token.Token]bool{ + token.Break: true, + token.Continue: true, + token.For: true, + token.If: true, + token.Return: true, + token.Export: true, +} + +// Error represents a parser error. +type Error struct { + Pos SourceFilePos + Msg string +} + +func (e Error) Error() string { + if e.Pos.Filename != "" || e.Pos.IsValid() { + return fmt.Sprintf("Parse Error: %s\n\tat %s", e.Msg, e.Pos) + } + return fmt.Sprintf("Parse Error: %s", e.Msg) +} + +// ErrorList is a collection of parser errors. +type ErrorList []*Error + +// Add adds a new parser error to the collection. +func (p *ErrorList) Add(pos SourceFilePos, msg string) { + *p = append(*p, &Error{pos, msg}) +} + +// Len returns the number of elements in the collection. +func (p ErrorList) Len() int { + return len(p) +} + +func (p ErrorList) Swap(i, j int) { + p[i], p[j] = p[j], p[i] +} + +func (p ErrorList) Less(i, j int) bool { + e := &p[i].Pos + f := &p[j].Pos + + if e.Filename != f.Filename { + return e.Filename < f.Filename + } + if e.Line != f.Line { + return e.Line < f.Line + } + if e.Column != f.Column { + return e.Column < f.Column + } + return p[i].Msg < p[j].Msg +} + +// Sort sorts the collection. +func (p ErrorList) Sort() { + sort.Sort(p) +} + +func (p ErrorList) Error() string { + switch len(p) { + case 0: + return "no errors" + case 1: + return p[0].Error() + } + return fmt.Sprintf("%s (and %d more errors)", p[0], len(p)-1) +} + +// Err returns an error. +func (p ErrorList) Err() error { + if len(p) == 0 { + return nil + } + return p +} + +// Parser parses the Tengo source files. It's based on Go's parser +// implementation. type Parser struct { - file *source.File + file *SourceFile errors ErrorList - scanner *scanner.Scanner - pos source.Pos + scanner *Scanner + pos Pos token token.Token tokenLit string - exprLevel int // < 0: in control clause, >= 0: in expression - syncPos source.Pos // last sync position - syncCount int // number of advance calls without progress + exprLevel int // < 0: in control clause, >= 0: in expression + syncPos Pos // last sync position + syncCount int // number of advance calls without progress trace bool indent int traceOut io.Writer } // NewParser creates a Parser. -func NewParser(file *source.File, src []byte, trace io.Writer) *Parser { +func NewParser(file *SourceFile, src []byte, trace io.Writer) *Parser { p := &Parser{ file: file, trace: trace != nil, traceOut: trace, } - - p.scanner = scanner.NewScanner(p.file, src, func(pos source.FilePos, msg string) { - p.errors.Add(pos, msg) - }, 0) - + p.scanner = NewScanner(p.file, src, + func(pos SourceFilePos, msg string) { + p.errors.Add(pos, msg) + }, 0) p.next() - return p } // ParseFile parses the source and returns an AST file unit. -func (p *Parser) ParseFile() (file *ast.File, err error) { +func (p *Parser) ParseFile() (file *File, err error) { defer func() { if e := recover(); e != nil { if _, ok := e.(bailout); !ok { @@ -70,7 +135,7 @@ func (p *Parser) ParseFile() (file *ast.File, err error) { }() if p.trace { - defer un(trace(p, "File")) + defer untracep(tracep(p, "File")) } if p.errors.Len() > 0 { @@ -82,17 +147,16 @@ func (p *Parser) ParseFile() (file *ast.File, err error) { return nil, p.errors.Err() } - file = &ast.File{ + file = &File{ InputFile: p.file, Stmts: stmts, } - return } -func (p *Parser) parseExpr() ast.Expr { +func (p *Parser) parseExpr() Expr { if p.trace { - defer un(trace(p, "Expression")) + defer untracep(tracep(p, "Expression")) } expr := p.parseBinaryExpr(token.LowestPrec + 1) @@ -101,13 +165,12 @@ func (p *Parser) parseExpr() ast.Expr { if p.token == token.Question { return p.parseCondExpr(expr) } - return expr } -func (p *Parser) parseBinaryExpr(prec1 int) ast.Expr { +func (p *Parser) parseBinaryExpr(prec1 int) Expr { if p.trace { - defer un(trace(p, "BinaryExpression")) + defer untracep(tracep(p, "BinaryExpression")) } x := p.parseUnaryExpr() @@ -122,7 +185,7 @@ func (p *Parser) parseBinaryExpr(prec1 int) ast.Expr { y := p.parseBinaryExpr(prec + 1) - x = &ast.BinaryExpr{ + x = &BinaryExpr{ LHS: x, RHS: y, Token: op, @@ -131,16 +194,13 @@ func (p *Parser) parseBinaryExpr(prec1 int) ast.Expr { } } -func (p *Parser) parseCondExpr(cond ast.Expr) ast.Expr { +func (p *Parser) parseCondExpr(cond Expr) Expr { questionPos := p.expect(token.Question) - trueExpr := p.parseExpr() - colonPos := p.expect(token.Colon) - falseExpr := p.parseExpr() - return &ast.CondExpr{ + return &CondExpr{ Cond: cond, True: trueExpr, False: falseExpr, @@ -149,9 +209,9 @@ func (p *Parser) parseCondExpr(cond ast.Expr) ast.Expr { } } -func (p *Parser) parseUnaryExpr() ast.Expr { +func (p *Parser) parseUnaryExpr() Expr { if p.trace { - defer un(trace(p, "UnaryExpression")) + defer untracep(tracep(p, "UnaryExpression")) } switch p.token { @@ -159,19 +219,18 @@ func (p *Parser) parseUnaryExpr() ast.Expr { pos, op := p.pos, p.token p.next() x := p.parseUnaryExpr() - return &ast.UnaryExpr{ + return &UnaryExpr{ Token: op, TokenPos: pos, Expr: x, } } - return p.parsePrimaryExpr() } -func (p *Parser) parsePrimaryExpr() ast.Expr { +func (p *Parser) parsePrimaryExpr() Expr { if p.trace { - defer un(trace(p, "PrimaryExpression")) + defer untracep(tracep(p, "PrimaryExpression")) } x := p.parseOperand() @@ -189,7 +248,7 @@ L: pos := p.pos p.errorExpected(pos, "selector") p.advance(stmtStart) - return &ast.BadExpr{From: pos, To: p.pos} + return &BadExpr{From: pos, To: p.pos} } case token.LBrack: x = p.parseIndexOrSlice(x) @@ -199,19 +258,18 @@ L: break L } } - return x } -func (p *Parser) parseCall(x ast.Expr) *ast.CallExpr { +func (p *Parser) parseCall(x Expr) *CallExpr { if p.trace { - defer un(trace(p, "Call")) + defer untracep(tracep(p, "Call")) } lparen := p.expect(token.LParen) p.exprLevel++ - var list []ast.Expr + var list []Expr for p.token != token.RParen && p.token != token.EOF { list = append(list, p.parseExpr()) @@ -222,8 +280,7 @@ func (p *Parser) parseCall(x ast.Expr) *ast.CallExpr { p.exprLevel-- rparen := p.expect(token.RParen) - - return &ast.CallExpr{ + return &CallExpr{ Func: x, LParen: lparen, RParen: rparen, @@ -239,26 +296,24 @@ func (p *Parser) expectComma(closing token.Token, want string) bool { p.errorExpected(p.pos, want) return false } - return true } if p.token == token.Semicolon && p.tokenLit == "\n" { p.next() } - return false } -func (p *Parser) parseIndexOrSlice(x ast.Expr) ast.Expr { +func (p *Parser) parseIndexOrSlice(x Expr) Expr { if p.trace { - defer un(trace(p, "IndexOrSlice")) + defer untracep(tracep(p, "IndexOrSlice")) } lbrack := p.expect(token.LBrack) p.exprLevel++ - var index [2]ast.Expr + var index [2]Expr if p.token != token.Colon { index[0] = p.parseExpr() } @@ -277,7 +332,7 @@ func (p *Parser) parseIndexOrSlice(x ast.Expr) ast.Expr { if numColons > 0 { // slice expression - return &ast.SliceExpr{ + return &SliceExpr{ Expr: x, LBrack: lbrack, RBrack: rbrack, @@ -285,8 +340,7 @@ func (p *Parser) parseIndexOrSlice(x ast.Expr) ast.Expr { High: index[1], } } - - return &ast.IndexExpr{ + return &IndexExpr{ Expr: x, LBrack: lbrack, RBrack: rbrack, @@ -294,88 +348,78 @@ func (p *Parser) parseIndexOrSlice(x ast.Expr) ast.Expr { } } -func (p *Parser) parseSelector(x ast.Expr) ast.Expr { +func (p *Parser) parseSelector(x Expr) Expr { if p.trace { - defer un(trace(p, "Selector")) + defer untracep(tracep(p, "Selector")) } sel := p.parseIdent() - - return &ast.SelectorExpr{Expr: x, Sel: &ast.StringLit{ + return &SelectorExpr{Expr: x, Sel: &StringLit{ Value: sel.Name, ValuePos: sel.NamePos, Literal: sel.Name, }} } -func (p *Parser) parseOperand() ast.Expr { +func (p *Parser) parseOperand() Expr { if p.trace { - defer un(trace(p, "Operand")) + defer untracep(tracep(p, "Operand")) } switch p.token { case token.Ident: return p.parseIdent() - case token.Int: v, _ := strconv.ParseInt(p.tokenLit, 10, 64) - x := &ast.IntLit{ + x := &IntLit{ Value: v, ValuePos: p.pos, Literal: p.tokenLit, } p.next() return x - case token.Float: v, _ := strconv.ParseFloat(p.tokenLit, 64) - x := &ast.FloatLit{ + x := &FloatLit{ Value: v, ValuePos: p.pos, Literal: p.tokenLit, } p.next() return x - case token.Char: return p.parseCharLit() - case token.String: v, _ := strconv.Unquote(p.tokenLit) - x := &ast.StringLit{ + x := &StringLit{ Value: v, ValuePos: p.pos, Literal: p.tokenLit, } p.next() return x - case token.True: - x := &ast.BoolLit{ + x := &BoolLit{ Value: true, ValuePos: p.pos, Literal: p.tokenLit, } p.next() return x - case token.False: - x := &ast.BoolLit{ + x := &BoolLit{ Value: false, ValuePos: p.pos, Literal: p.tokenLit, } p.next() return x - case token.Undefined: - x := &ast.UndefinedLit{TokenPos: p.pos} + x := &UndefinedLit{TokenPos: p.pos} p.next() return x - case token.Import: return p.parseImportExpr() - case token.LParen: lparen := p.pos p.next() @@ -383,24 +427,19 @@ func (p *Parser) parseOperand() ast.Expr { x := p.parseExpr() p.exprLevel-- rparen := p.expect(token.RParen) - return &ast.ParenExpr{ + return &ParenExpr{ LParen: lparen, Expr: x, RParen: rparen, } - case token.LBrack: // array literal return p.parseArrayLit() - case token.LBrace: // map literal return p.parseMapLit() - case token.Func: // function literal return p.parseFuncLit() - case token.Error: // error expression return p.parseErrorExpr() - case token.Immutable: // immutable expression return p.parseImmutableExpr() } @@ -408,43 +447,38 @@ func (p *Parser) parseOperand() ast.Expr { pos := p.pos p.errorExpected(pos, "operand") p.advance(stmtStart) - return &ast.BadExpr{From: pos, To: p.pos} + return &BadExpr{From: pos, To: p.pos} } -func (p *Parser) parseImportExpr() ast.Expr { +func (p *Parser) parseImportExpr() Expr { pos := p.pos - p.next() - p.expect(token.LParen) - if p.token != token.String { p.errorExpected(p.pos, "module name") p.advance(stmtStart) - return &ast.BadExpr{From: pos, To: p.pos} + return &BadExpr{From: pos, To: p.pos} } // module name moduleName, _ := strconv.Unquote(p.tokenLit) - - expr := &ast.ImportExpr{ + expr := &ImportExpr{ ModuleName: moduleName, Token: token.Import, TokenPos: pos, } p.next() - p.expect(token.RParen) - return expr } -func (p *Parser) parseCharLit() ast.Expr { +func (p *Parser) parseCharLit() Expr { if n := len(p.tokenLit); n >= 3 { - if code, _, _, err := strconv.UnquoteChar(p.tokenLit[1:n-1], '\''); err == nil { - x := &ast.CharLit{ - Value: rune(code), + code, _, _, err := strconv.UnquoteChar(p.tokenLit[1:n-1], '\'') + if err == nil { + x := &CharLit{ + Value: code, ValuePos: p.pos, Literal: p.tokenLit, } @@ -456,38 +490,36 @@ func (p *Parser) parseCharLit() ast.Expr { pos := p.pos p.error(pos, "illegal char literal") p.next() - return &ast.BadExpr{ + return &BadExpr{ From: pos, To: p.pos, } } -func (p *Parser) parseFuncLit() ast.Expr { +func (p *Parser) parseFuncLit() Expr { if p.trace { - defer un(trace(p, "FuncLit")) + defer untracep(tracep(p, "FuncLit")) } typ := p.parseFuncType() - p.exprLevel++ body := p.parseBody() p.exprLevel-- - - return &ast.FuncLit{ + return &FuncLit{ Type: typ, Body: body, } } -func (p *Parser) parseArrayLit() ast.Expr { +func (p *Parser) parseArrayLit() Expr { if p.trace { - defer un(trace(p, "ArrayLit")) + defer untracep(tracep(p, "ArrayLit")) } lbrack := p.expect(token.LBrack) p.exprLevel++ - var elements []ast.Expr + var elements []Expr for p.token != token.RBrack && p.token != token.EOF { elements = append(elements, p.parseExpr()) @@ -498,95 +530,83 @@ func (p *Parser) parseArrayLit() ast.Expr { p.exprLevel-- rbrack := p.expect(token.RBrack) - - return &ast.ArrayLit{ + return &ArrayLit{ Elements: elements, LBrack: lbrack, RBrack: rbrack, } } -func (p *Parser) parseErrorExpr() ast.Expr { +func (p *Parser) parseErrorExpr() Expr { pos := p.pos p.next() - lparen := p.expect(token.LParen) value := p.parseExpr() rparen := p.expect(token.RParen) - - expr := &ast.ErrorExpr{ + return &ErrorExpr{ ErrorPos: pos, Expr: value, LParen: lparen, RParen: rparen, } - - return expr } -func (p *Parser) parseImmutableExpr() ast.Expr { +func (p *Parser) parseImmutableExpr() Expr { pos := p.pos p.next() - lparen := p.expect(token.LParen) value := p.parseExpr() rparen := p.expect(token.RParen) - - expr := &ast.ImmutableExpr{ + return &ImmutableExpr{ ErrorPos: pos, Expr: value, LParen: lparen, RParen: rparen, } - - return expr } -func (p *Parser) parseFuncType() *ast.FuncType { +func (p *Parser) parseFuncType() *FuncType { if p.trace { - defer un(trace(p, "FuncType")) + defer untracep(tracep(p, "FuncType")) } pos := p.expect(token.Func) params := p.parseIdentList() - - return &ast.FuncType{ + return &FuncType{ FuncPos: pos, Params: params, } } -func (p *Parser) parseBody() *ast.BlockStmt { +func (p *Parser) parseBody() *BlockStmt { if p.trace { - defer un(trace(p, "Body")) + defer untracep(tracep(p, "Body")) } lbrace := p.expect(token.LBrace) list := p.parseStmtList() rbrace := p.expect(token.RBrace) - - return &ast.BlockStmt{ + return &BlockStmt{ LBrace: lbrace, RBrace: rbrace, Stmts: list, } } -func (p *Parser) parseStmtList() (list []ast.Stmt) { +func (p *Parser) parseStmtList() (list []Stmt) { if p.trace { - defer un(trace(p, "StatementList")) + defer untracep(tracep(p, "StatementList")) } for p.token != token.RBrace && p.token != token.EOF { list = append(list, p.parseStmt()) } - return } -func (p *Parser) parseIdent() *ast.Ident { +func (p *Parser) parseIdent() *Ident { pos := p.pos name := "_" @@ -596,19 +616,18 @@ func (p *Parser) parseIdent() *ast.Ident { } else { p.expect(token.Ident) } - - return &ast.Ident{ + return &Ident{ NamePos: pos, Name: name, } } -func (p *Parser) parseIdentList() *ast.IdentList { +func (p *Parser) parseIdentList() *IdentList { if p.trace { - defer un(trace(p, "IdentList")) + defer untracep(tracep(p, "IdentList")) } - var params []*ast.Ident + var params []*Ident lparen := p.expect(token.LParen) isVarArgs := false if p.token != token.RParen { @@ -629,8 +648,7 @@ func (p *Parser) parseIdentList() *ast.IdentList { } rparen := p.expect(token.RParen) - - return &ast.IdentList{ + return &IdentList{ LParen: lparen, RParen: rparen, VarArgs: isVarArgs, @@ -638,16 +656,18 @@ func (p *Parser) parseIdentList() *ast.IdentList { } } -func (p *Parser) parseStmt() (stmt ast.Stmt) { +func (p *Parser) parseStmt() (stmt Stmt) { if p.trace { - defer un(trace(p, "Statement")) + defer untracep(tracep(p, "Statement")) } switch p.token { case // simple statements - token.Func, token.Error, token.Immutable, token.Ident, token.Int, token.Float, token.Char, token.String, token.True, token.False, - token.Undefined, token.Import, token.LParen, token.LBrace, token.LBrack, - token.Add, token.Sub, token.Mul, token.And, token.Xor, token.Not: + token.Func, token.Error, token.Immutable, token.Ident, token.Int, + token.Float, token.Char, token.String, token.True, token.False, + token.Undefined, token.Import, token.LParen, token.LBrace, + token.LBrack, token.Add, token.Sub, token.Mul, token.And, token.Xor, + token.Not: s := p.parseSimpleStmt(false) p.expectSemi() return s @@ -662,23 +682,23 @@ func (p *Parser) parseStmt() (stmt ast.Stmt) { case token.Break, token.Continue: return p.parseBranchStmt(p.token) case token.Semicolon: - s := &ast.EmptyStmt{Semicolon: p.pos, Implicit: p.tokenLit == "\n"} + s := &EmptyStmt{Semicolon: p.pos, Implicit: p.tokenLit == "\n"} p.next() return s case token.RBrace: // semicolon may be omitted before a closing "}" - return &ast.EmptyStmt{Semicolon: p.pos, Implicit: true} + return &EmptyStmt{Semicolon: p.pos, Implicit: true} default: pos := p.pos p.errorExpected(pos, "statement") p.advance(stmtStart) - return &ast.BadStmt{From: pos, To: p.pos} + return &BadStmt{From: pos, To: p.pos} } } -func (p *Parser) parseForStmt() ast.Stmt { +func (p *Parser) parseForStmt() Stmt { if p.trace { - defer un(trace(p, "ForStmt")) + defer untracep(tracep(p, "ForStmt")) } pos := p.expect(token.For) @@ -688,7 +708,7 @@ func (p *Parser) parseForStmt() ast.Stmt { body := p.parseBlockStmt() p.expectSemi() - return &ast.ForStmt{ + return &ForStmt{ ForPos: pos, Body: body, } @@ -697,7 +717,7 @@ func (p *Parser) parseForStmt() ast.Stmt { prevLevel := p.exprLevel p.exprLevel = -1 - var s1 ast.Stmt + var s1 Stmt if p.token != token.Semicolon { // skipping init s1 = p.parseSimpleStmt(true) } @@ -705,7 +725,7 @@ func (p *Parser) parseForStmt() ast.Stmt { // for _ in seq {} or // for value in seq {} or // for key, value in seq {} - if forInStmt, isForIn := s1.(*ast.ForInStmt); isForIn { + if forInStmt, isForIn := s1.(*ForInStmt); isForIn { forInStmt.ForPos = pos p.exprLevel = prevLevel forInStmt.Body = p.parseBlockStmt() @@ -714,7 +734,7 @@ func (p *Parser) parseForStmt() ast.Stmt { } // for init; cond; post {} - var s2, s3 ast.Stmt + var s2, s3 Stmt if p.token == token.Semicolon { p.next() if p.token != token.Semicolon { @@ -734,50 +754,45 @@ func (p *Parser) parseForStmt() ast.Stmt { p.exprLevel = prevLevel body := p.parseBlockStmt() p.expectSemi() - cond := p.makeExpr(s2, "condition expression") - - return &ast.ForStmt{ + return &ForStmt{ ForPos: pos, Init: s1, Cond: cond, Post: s3, Body: body, } - } -func (p *Parser) parseBranchStmt(tok token.Token) ast.Stmt { +func (p *Parser) parseBranchStmt(tok token.Token) Stmt { if p.trace { - defer un(trace(p, "BranchStmt")) + defer untracep(tracep(p, "BranchStmt")) } pos := p.expect(tok) - var label *ast.Ident + var label *Ident if p.token == token.Ident { label = p.parseIdent() } p.expectSemi() - - return &ast.BranchStmt{ + return &BranchStmt{ Token: tok, TokenPos: pos, Label: label, } } -func (p *Parser) parseIfStmt() ast.Stmt { +func (p *Parser) parseIfStmt() Stmt { if p.trace { - defer un(trace(p, "IfStmt")) + defer untracep(tracep(p, "IfStmt")) } pos := p.expect(token.If) - init, cond := p.parseIfHeader() body := p.parseBlockStmt() - var elseStmt ast.Stmt + var elseStmt Stmt if p.token == token.Else { p.next() @@ -789,13 +804,12 @@ func (p *Parser) parseIfStmt() ast.Stmt { p.expectSemi() default: p.errorExpected(p.pos, "if or {") - elseStmt = &ast.BadStmt{From: p.pos, To: p.pos} + elseStmt = &BadStmt{From: p.pos, To: p.pos} } } else { p.expectSemi() } - - return &ast.IfStmt{ + return &IfStmt{ IfPos: pos, Init: init, Cond: cond, @@ -804,40 +818,37 @@ func (p *Parser) parseIfStmt() ast.Stmt { } } -func (p *Parser) parseBlockStmt() *ast.BlockStmt { +func (p *Parser) parseBlockStmt() *BlockStmt { if p.trace { - defer un(trace(p, "BlockStmt")) + defer untracep(tracep(p, "BlockStmt")) } lbrace := p.expect(token.LBrace) list := p.parseStmtList() rbrace := p.expect(token.RBrace) - - return &ast.BlockStmt{ + return &BlockStmt{ LBrace: lbrace, RBrace: rbrace, Stmts: list, } } -func (p *Parser) parseIfHeader() (init ast.Stmt, cond ast.Expr) { +func (p *Parser) parseIfHeader() (init Stmt, cond Expr) { if p.token == token.LBrace { p.error(p.pos, "missing condition in if statement") - cond = &ast.BadExpr{From: p.pos, To: p.pos} + cond = &BadExpr{From: p.pos, To: p.pos} return } outer := p.exprLevel p.exprLevel = -1 - if p.token == token.Semicolon { p.error(p.pos, "missing init in if statement") return } - init = p.parseSimpleStmt(false) - var condStmt ast.Stmt + var condStmt Stmt if p.token == token.LBrace { condStmt = init init = nil @@ -852,75 +863,67 @@ func (p *Parser) parseIfHeader() (init ast.Stmt, cond ast.Expr) { if condStmt != nil { cond = p.makeExpr(condStmt, "boolean expression") } - if cond == nil { - cond = &ast.BadExpr{From: p.pos, To: p.pos} + cond = &BadExpr{From: p.pos, To: p.pos} } - p.exprLevel = outer - return } -func (p *Parser) makeExpr(s ast.Stmt, want string) ast.Expr { +func (p *Parser) makeExpr(s Stmt, want string) Expr { if s == nil { return nil } - if es, isExpr := s.(*ast.ExprStmt); isExpr { + if es, isExpr := s.(*ExprStmt); isExpr { return es.Expr } found := "simple statement" - if _, isAss := s.(*ast.AssignStmt); isAss { + if _, isAss := s.(*AssignStmt); isAss { found = "assignment" } - p.error(s.Pos(), fmt.Sprintf("expected %s, found %s", want, found)) - - return &ast.BadExpr{From: s.Pos(), To: p.safePos(s.End())} + return &BadExpr{From: s.Pos(), To: p.safePos(s.End())} } -func (p *Parser) parseReturnStmt() ast.Stmt { +func (p *Parser) parseReturnStmt() Stmt { if p.trace { - defer un(trace(p, "ReturnStmt")) + defer untracep(tracep(p, "ReturnStmt")) } pos := p.pos p.expect(token.Return) - var x ast.Expr + var x Expr if p.token != token.Semicolon && p.token != token.RBrace { x = p.parseExpr() } p.expectSemi() - - return &ast.ReturnStmt{ + return &ReturnStmt{ ReturnPos: pos, Result: x, } } -func (p *Parser) parseExportStmt() ast.Stmt { +func (p *Parser) parseExportStmt() Stmt { if p.trace { - defer un(trace(p, "ExportStmt")) + defer untracep(tracep(p, "ExportStmt")) } pos := p.pos p.expect(token.Export) - x := p.parseExpr() p.expectSemi() - - return &ast.ExportStmt{ + return &ExportStmt{ ExportPos: pos, Result: x, } } -func (p *Parser) parseSimpleStmt(forIn bool) ast.Stmt { +func (p *Parser) parseSimpleStmt(forIn bool) Stmt { if p.trace { - defer un(trace(p, "SimpleStmt")) + defer untracep(tracep(p, "SimpleStmt")) } x := p.parseExprList() @@ -929,10 +932,8 @@ func (p *Parser) parseSimpleStmt(forIn bool) ast.Stmt { case token.Assign, token.Define: // assignment statement pos, tok := p.pos, p.token p.next() - y := p.parseExprList() - - return &ast.AssignStmt{ + return &AssignStmt{ LHS: x, RHS: y, Token: tok, @@ -941,35 +942,32 @@ func (p *Parser) parseSimpleStmt(forIn bool) ast.Stmt { case token.In: if forIn { p.next() - y := p.parseExpr() - var key, value *ast.Ident + var key, value *Ident var ok bool - switch len(x) { case 1: - key = &ast.Ident{Name: "_", NamePos: x[0].Pos()} + key = &Ident{Name: "_", NamePos: x[0].Pos()} - value, ok = x[0].(*ast.Ident) + value, ok = x[0].(*Ident) if !ok { p.errorExpected(x[0].Pos(), "identifier") - value = &ast.Ident{Name: "_", NamePos: x[0].Pos()} + value = &Ident{Name: "_", NamePos: x[0].Pos()} } case 2: - key, ok = x[0].(*ast.Ident) + key, ok = x[0].(*Ident) if !ok { p.errorExpected(x[0].Pos(), "identifier") - key = &ast.Ident{Name: "_", NamePos: x[0].Pos()} + key = &Ident{Name: "_", NamePos: x[0].Pos()} } - value, ok = x[1].(*ast.Ident) + value, ok = x[1].(*Ident) if !ok { p.errorExpected(x[1].Pos(), "identifier") - value = &ast.Ident{Name: "_", NamePos: x[1].Pos()} + value = &Ident{Name: "_", NamePos: x[1].Pos()} } } - - return &ast.ForInStmt{ + return &ForInStmt{ Key: key, Value: value, Iterable: y, @@ -984,33 +982,30 @@ func (p *Parser) parseSimpleStmt(forIn bool) ast.Stmt { switch p.token { case token.Define, - token.AddAssign, token.SubAssign, token.MulAssign, token.QuoAssign, token.RemAssign, - token.AndAssign, token.OrAssign, token.XorAssign, token.ShlAssign, token.ShrAssign, token.AndNotAssign: + token.AddAssign, token.SubAssign, token.MulAssign, token.QuoAssign, + token.RemAssign, token.AndAssign, token.OrAssign, token.XorAssign, + token.ShlAssign, token.ShrAssign, token.AndNotAssign: pos, tok := p.pos, p.token p.next() - y := p.parseExpr() - - return &ast.AssignStmt{ - LHS: []ast.Expr{x[0]}, - RHS: []ast.Expr{y}, + return &AssignStmt{ + LHS: []Expr{x[0]}, + RHS: []Expr{y}, Token: tok, TokenPos: pos, } case token.Inc, token.Dec: // increment or decrement statement - s := &ast.IncDecStmt{Expr: x[0], Token: p.token, TokenPos: p.pos} + s := &IncDecStmt{Expr: x[0], Token: p.token, TokenPos: p.pos} p.next() return s } - - // expression statement - return &ast.ExprStmt{Expr: x[0]} + return &ExprStmt{Expr: x[0]} } -func (p *Parser) parseExprList() (list []ast.Expr) { +func (p *Parser) parseExprList() (list []Expr) { if p.trace { - defer un(trace(p, "ExpressionList")) + defer untracep(tracep(p, "ExpressionList")) } list = append(list, p.parseExpr()) @@ -1018,18 +1013,16 @@ func (p *Parser) parseExprList() (list []ast.Expr) { p.next() list = append(list, p.parseExpr()) } - return } -func (p *Parser) parseMapElementLit() *ast.MapElementLit { +func (p *Parser) parseMapElementLit() *MapElementLit { if p.trace { - defer un(trace(p, "MapElementLit")) + defer untracep(tracep(p, "MapElementLit")) } pos := p.pos name := "_" - if p.token == token.Ident { name = p.tokenLit } else if p.token == token.String { @@ -1038,13 +1031,10 @@ func (p *Parser) parseMapElementLit() *ast.MapElementLit { } else { p.errorExpected(pos, "map key") } - p.next() - colonPos := p.expect(token.Colon) valueExpr := p.parseExpr() - - return &ast.MapElementLit{ + return &MapElementLit{ Key: name, KeyPos: pos, ColonPos: colonPos, @@ -1052,15 +1042,15 @@ func (p *Parser) parseMapElementLit() *ast.MapElementLit { } } -func (p *Parser) parseMapLit() *ast.MapLit { +func (p *Parser) parseMapLit() *MapLit { if p.trace { - defer un(trace(p, "MapLit")) + defer untracep(tracep(p, "MapLit")) } lbrace := p.expect(token.LBrace) p.exprLevel++ - var elements []*ast.MapElementLit + var elements []*MapElementLit for p.token != token.RBrace && p.token != token.EOF { elements = append(elements, p.parseMapElementLit()) @@ -1071,22 +1061,20 @@ func (p *Parser) parseMapLit() *ast.MapLit { p.exprLevel-- rbrace := p.expect(token.RBrace) - - return &ast.MapLit{ + return &MapLit{ LBrace: lbrace, RBrace: rbrace, Elements: elements, } } -func (p *Parser) expect(token token.Token) source.Pos { +func (p *Parser) expect(token token.Token) Pos { pos := p.pos if p.token != token { p.errorExpected(pos, "'"+token.String()+"'") } p.next() - return pos } @@ -1104,7 +1092,6 @@ func (p *Parser) expectSemi() { p.errorExpected(p.pos, "';'") p.advance(stmtStart) } - } func (p *Parser) advance(to map[token.Token]bool) { @@ -1114,7 +1101,6 @@ func (p *Parser) advance(to map[token.Token]bool) { p.syncCount++ return } - if p.pos > p.syncPos { p.syncPos = p.pos p.syncCount = 0 @@ -1124,7 +1110,7 @@ func (p *Parser) advance(to map[token.Token]bool) { } } -func (p *Parser) error(pos source.Pos, msg string) { +func (p *Parser) error(pos Pos, msg string) { filePos := p.file.Position(pos) n := len(p.errors) @@ -1132,16 +1118,14 @@ func (p *Parser) error(pos source.Pos, msg string) { // discard errors reported on the same line return } - if n > 10 { // too many errors; terminate early panic(bailout{}) } - p.errors.Add(filePos, msg) } -func (p *Parser) errorExpected(pos source.Pos, msg string) { +func (p *Parser) errorExpected(pos Pos, msg string) { msg = "expected " + msg if pos == p.pos { // error happened at the current position: provide more specific @@ -1154,7 +1138,6 @@ func (p *Parser) errorExpected(pos source.Pos, msg string) { msg += ", found '" + p.token.String() + "'" } } - p.error(pos, msg) } @@ -1170,19 +1153,18 @@ func (p *Parser) next() { p.printTrace(s) } } - p.token, p.tokenLit, p.pos = p.scanner.Scan() } func (p *Parser) printTrace(a ...interface{}) { const ( - dots = ". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . " + dots = ". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . " n = len(dots) ) filePos := p.file.Position(p.pos) - _, _ = fmt.Fprintf(p.traceOut, "%5d: %5d:%3d: ", p.pos, filePos.Line, filePos.Column) - + _, _ = fmt.Fprintf(p.traceOut, "%5d: %5d:%3d: ", p.pos, filePos.Line, + filePos.Column) i := 2 * p.indent for i > n { _, _ = fmt.Fprint(p.traceOut, dots) @@ -1192,25 +1174,23 @@ func (p *Parser) printTrace(a ...interface{}) { _, _ = fmt.Fprintln(p.traceOut, a...) } -func (p *Parser) safePos(pos source.Pos) source.Pos { +func (p *Parser) safePos(pos Pos) Pos { fileBase := p.file.Base fileSize := p.file.Size if int(pos) < fileBase || int(pos) > fileBase+fileSize { - return source.Pos(fileBase + fileSize) + return Pos(fileBase + fileSize) } - return pos } -func trace(p *Parser, msg string) *Parser { +func tracep(p *Parser, msg string) *Parser { p.printTrace(msg, "(") p.indent++ - return p } -func un(p *Parser) { +func untracep(p *Parser) { p.indent-- p.printTrace(")") } diff --git a/vendor/github.com/d5/tengo/compiler/source/pos.go b/vendor/github.com/d5/tengo/v2/parser/pos.go similarity index 93% rename from vendor/github.com/d5/tengo/compiler/source/pos.go rename to vendor/github.com/d5/tengo/v2/parser/pos.go index 72128b13..f8d3898c 100644 --- a/vendor/github.com/d5/tengo/compiler/source/pos.go +++ b/vendor/github.com/d5/tengo/v2/parser/pos.go @@ -1,4 +1,4 @@ -package source +package parser // Pos represents a position in the file set. type Pos int diff --git a/vendor/github.com/d5/tengo/compiler/scanner/scanner.go b/vendor/github.com/d5/tengo/v2/parser/scanner.go similarity index 83% rename from vendor/github.com/d5/tengo/compiler/scanner/scanner.go rename to vendor/github.com/d5/tengo/v2/parser/scanner.go index 387cd8ee..f1d820a4 100644 --- a/vendor/github.com/d5/tengo/compiler/scanner/scanner.go +++ b/vendor/github.com/d5/tengo/v2/parser/scanner.go @@ -1,45 +1,53 @@ -/* - Scanner reads the Tengo source text and tokenize them. - - Scanner is a modified version of Go's scanner implementation. - - Copyright 2009 The Go Authors. All rights reserved. - Use of this source code is governed by a BSD-style - license that can be found in the LICENSE file. -*/ - -package scanner +package parser import ( "fmt" "unicode" "unicode/utf8" - "github.com/d5/tengo/compiler/source" - "github.com/d5/tengo/compiler/token" + "github.com/d5/tengo/v2/token" ) // byte order mark const bom = 0xFEFF -// Scanner reads the Tengo source text. +// ScanMode represents a scanner mode. +type ScanMode int + +// List of scanner modes. +const ( + ScanComments ScanMode = 1 << iota + DontInsertSemis +) + +// ScannerErrorHandler is an error handler for the scanner. +type ScannerErrorHandler func(pos SourceFilePos, msg string) + +// Scanner reads the Tengo source text. It's based on Go's scanner +// implementation. type Scanner struct { - file *source.File // source file handle - src []byte // source - ch rune // current character - offset int // character offset - readOffset int // reading offset (position after current character) - lineOffset int // current line offset - insertSemi bool // insert a semicolon before next newline - errorHandler ErrorHandler // error reporting; or nil - errorCount int // number of errors encountered - mode Mode + file *SourceFile // source file handle + src []byte // source + ch rune // current character + offset int // character offset + readOffset int // reading offset (position after current character) + lineOffset int // current line offset + insertSemi bool // insert a semicolon before next newline + errorHandler ScannerErrorHandler // error reporting; or nil + errorCount int // number of errors encountered + mode ScanMode } // NewScanner creates a Scanner. -func NewScanner(file *source.File, src []byte, errorHandler ErrorHandler, mode Mode) *Scanner { +func NewScanner( + file *SourceFile, + src []byte, + errorHandler ScannerErrorHandler, + mode ScanMode, +) *Scanner { if file.Size != len(src) { - panic(fmt.Sprintf("file size (%d) does not match src len (%d)", file.Size, len(src))) + panic(fmt.Sprintf("file size (%d) does not match src len (%d)", + file.Size, len(src))) } s := &Scanner{ @@ -64,7 +72,11 @@ func (s *Scanner) ErrorCount() int { } // Scan returns a token, token literal and its position. -func (s *Scanner) Scan() (tok token.Token, literal string, pos source.Pos) { +func (s *Scanner) Scan() ( + tok token.Token, + literal string, + pos Pos, +) { s.skipWhitespace() pos = s.file.FileSetPos(s.offset) @@ -77,7 +89,8 @@ func (s *Scanner) Scan() (tok token.Token, literal string, pos source.Pos) { literal = s.scanIdentifier() tok = token.Lookup(literal) switch tok { - case token.Ident, token.Break, token.Continue, token.Return, token.Export, token.True, token.False, token.Undefined: + case token.Ident, token.Break, token.Continue, token.Return, + token.Export, token.True, token.False, token.Undefined: insertSemi = true } case '0' <= ch && ch <= '9': @@ -184,9 +197,11 @@ func (s *Scanner) Scan() (tok token.Token, literal string, pos source.Pos) { case '^': tok = s.switch2(token.Xor, token.XorAssign) case '<': - tok = s.switch4(token.Less, token.LessEq, '<', token.Shl, token.ShlAssign) + tok = s.switch4(token.Less, token.LessEq, '<', + token.Shl, token.ShlAssign) case '>': - tok = s.switch4(token.Greater, token.GreaterEq, '>', token.Shr, token.ShrAssign) + tok = s.switch4(token.Greater, token.GreaterEq, '>', + token.Shr, token.ShrAssign) case '=': tok = s.switch2(token.Assign, token.Equal) case '!': @@ -203,18 +218,17 @@ func (s *Scanner) Scan() (tok token.Token, literal string, pos source.Pos) { default: // next reports unexpected BOMs - don't repeat if ch != bom { - s.error(s.file.Offset(pos), fmt.Sprintf("illegal character %#U", ch)) + s.error(s.file.Offset(pos), + fmt.Sprintf("illegal character %#U", ch)) } insertSemi = s.insertSemi // preserve insertSemi info tok = token.Illegal literal = string(ch) } } - if s.mode&DontInsertSemis == 0 { s.insertSemi = insertSemi } - return } @@ -254,7 +268,6 @@ func (s *Scanner) peek() byte { if s.readOffset < len(s.src) { return s.src[s.readOffset] } - return 0 } @@ -262,7 +275,6 @@ func (s *Scanner) error(offset int, msg string) { if s.errorHandler != nil { s.errorHandler(s.file.Position(s.file.FileSetPos(offset)), msg) } - s.errorCount++ } @@ -310,11 +322,9 @@ exit: lit = lit[:len(lit)-1] numCR-- } - if numCR > 0 { lit = StripCR(lit, lit[1] == '*') } - return string(lit) } @@ -358,7 +368,6 @@ func (s *Scanner) findLineEnd() bool { } s.next() // consume '/' } - return false } @@ -367,7 +376,6 @@ func (s *Scanner) scanIdentifier() string { for isLetter(s.ch) || isDigit(s.ch) { s.next() } - return string(s.src[offs:s.offset]) } @@ -377,7 +385,9 @@ func (s *Scanner) scanMantissa(base int) { } } -func (s *Scanner) scanNumber(seenDecimalPoint bool) (tok token.Token, lit string) { +func (s *Scanner) scanNumber( + seenDecimalPoint bool, +) (tok token.Token, lit string) { // digitVal(s.ch) < 10 offs := s.offset tok = token.Int @@ -422,7 +432,6 @@ func (s *Scanner) scanNumber(seenDecimalPoint bool) (tok token.Token, lit string s.error(offs, "illegal octal number") } } - return } @@ -449,7 +458,6 @@ exponent: s.error(offs, "illegal floating-point exponent") } } - return } @@ -486,7 +494,8 @@ func (s *Scanner) scanEscape(quote rune) bool { for n > 0 { d := uint32(digitVal(s.ch)) if d >= base { - msg := fmt.Sprintf("illegal character %#U in escape sequence", s.ch) + msg := fmt.Sprintf( + "illegal character %#U in escape sequence", s.ch) if s.ch < 0 { msg = "escape sequence not terminated" } @@ -502,7 +511,6 @@ func (s *Scanner) scanEscape(quote rune) bool { s.error(offs, "escape sequence is invalid Unicode code point") return false } - return true } @@ -537,7 +545,6 @@ func (s *Scanner) scanRune() string { if valid && n != 1 { s.error(offs, "illegal rune literal") } - return string(s.src[offs:s.offset]) } @@ -558,7 +565,6 @@ func (s *Scanner) scanString() string { s.scanEscape('"') } } - return string(s.src[offs:s.offset]) } @@ -588,30 +594,30 @@ func (s *Scanner) scanRawString() string { if hasCR { lit = StripCR(lit, false) } - return string(lit) } // StripCR removes carriage return characters. func StripCR(b []byte, comment bool) []byte { c := make([]byte, len(b)) - i := 0 for j, ch := range b { - // In a /*-style comment, don't strip \r from *\r/ (incl. sequences of \r from *\r\r...\r/) - // since the resulting */ would terminate the comment too early unless the \r is immediately - // following the opening /* in which case it's ok because /*/ is not closed yet. - if ch != '\r' || comment && i > len("/*") && c[i-1] == '*' && j+1 < len(b) && b[j+1] == '/' { + // In a /*-style comment, don't strip \r from *\r/ (incl. sequences of + // \r from *\r\r...\r/) since the resulting */ would terminate the + // comment too early unless the \r is immediately following the opening + // /* in which case it's ok because /*/ is not closed yet. + if ch != '\r' || comment && i > len("/*") && c[i-1] == '*' && + j+1 < len(b) && b[j+1] == '/' { c[i] = ch i++ } } - return c[:i] } func (s *Scanner) skipWhitespace() { - for s.ch == ' ' || s.ch == '\t' || s.ch == '\n' && !s.insertSemi || s.ch == '\r' { + for s.ch == ' ' || s.ch == '\t' || s.ch == '\n' && !s.insertSemi || + s.ch == '\r' { s.next() } } @@ -621,49 +627,53 @@ func (s *Scanner) switch2(tok0, tok1 token.Token) token.Token { s.next() return tok1 } - return tok0 } -func (s *Scanner) switch3(tok0, tok1 token.Token, ch2 rune, tok2 token.Token) token.Token { +func (s *Scanner) switch3( + tok0, tok1 token.Token, + ch2 rune, + tok2 token.Token, +) token.Token { if s.ch == '=' { s.next() return tok1 } - if s.ch == ch2 { s.next() return tok2 } - return tok0 } -func (s *Scanner) switch4(tok0, tok1 token.Token, ch2 rune, tok2, tok3 token.Token) token.Token { +func (s *Scanner) switch4( + tok0, tok1 token.Token, + ch2 rune, + tok2, tok3 token.Token, +) token.Token { if s.ch == '=' { s.next() return tok1 } - if s.ch == ch2 { s.next() if s.ch == '=' { s.next() return tok3 } - return tok2 } - return tok0 } func isLetter(ch rune) bool { - return 'a' <= ch && ch <= 'z' || 'A' <= ch && ch <= 'Z' || ch == '_' || ch >= utf8.RuneSelf && unicode.IsLetter(ch) + return 'a' <= ch && ch <= 'z' || 'A' <= ch && ch <= 'Z' || ch == '_' || + ch >= utf8.RuneSelf && unicode.IsLetter(ch) } func isDigit(ch rune) bool { - return '0' <= ch && ch <= '9' || ch >= utf8.RuneSelf && unicode.IsDigit(ch) + return '0' <= ch && ch <= '9' || + ch >= utf8.RuneSelf && unicode.IsDigit(ch) } func digitVal(ch rune) int { @@ -675,6 +685,5 @@ func digitVal(ch rune) int { case 'A' <= ch && ch <= 'F': return int(ch - 'A' + 10) } - return 16 // larger than any legal digit val } diff --git a/vendor/github.com/d5/tengo/v2/parser/source_file.go b/vendor/github.com/d5/tengo/v2/parser/source_file.go new file mode 100644 index 00000000..e9f4b0f5 --- /dev/null +++ b/vendor/github.com/d5/tengo/v2/parser/source_file.go @@ -0,0 +1,231 @@ +package parser + +import ( + "fmt" + "sort" +) + +// SourceFilePos represents a position information in the file. +type SourceFilePos struct { + Filename string // filename, if any + Offset int // offset, starting at 0 + Line int // line number, starting at 1 + Column int // column number, starting at 1 (byte count) +} + +// IsValid returns true if the position is valid. +func (p SourceFilePos) IsValid() bool { + return p.Line > 0 +} + +// String returns a string in one of several forms: +// +// file:line:column valid position with file name +// file:line valid position with file name but no column (column == 0) +// line:column valid position without file name +// line valid position without file name and no column (column == 0) +// file invalid position with file name +// - invalid position without file name +// +func (p SourceFilePos) String() string { + s := p.Filename + if p.IsValid() { + if s != "" { + s += ":" + } + s += fmt.Sprintf("%d", p.Line) + if p.Column != 0 { + s += fmt.Sprintf(":%d", p.Column) + } + } + if s == "" { + s = "-" + } + return s +} + +// SourceFileSet represents a set of source files. +type SourceFileSet struct { + Base int // base offset for the next file + Files []*SourceFile // list of files in the order added to the set + LastFile *SourceFile // cache of last file looked up +} + +// NewFileSet creates a new file set. +func NewFileSet() *SourceFileSet { + return &SourceFileSet{ + Base: 1, // 0 == NoPos + } +} + +// AddFile adds a new file in the file set. +func (s *SourceFileSet) AddFile(filename string, base, size int) *SourceFile { + if base < 0 { + base = s.Base + } + if base < s.Base || size < 0 { + panic("illegal base or size") + } + f := &SourceFile{ + set: s, + Name: filename, + Base: base, + Size: size, + Lines: []int{0}, + } + base += size + 1 // +1 because EOF also has a position + if base < 0 { + panic("offset overflow (> 2G of source code in file set)") + } + + // add the file to the file set + s.Base = base + s.Files = append(s.Files, f) + s.LastFile = f + return f +} + +// File returns the file that contains the position p. If no such file is +// found (for instance for p == NoPos), the result is nil. +func (s *SourceFileSet) File(p Pos) (f *SourceFile) { + if p != NoPos { + f = s.file(p) + } + return +} + +// Position converts a SourcePos p in the fileset into a SourceFilePos value. +func (s *SourceFileSet) Position(p Pos) (pos SourceFilePos) { + if p != NoPos { + if f := s.file(p); f != nil { + return f.position(p) + } + } + return +} + +func (s *SourceFileSet) file(p Pos) *SourceFile { + // common case: p is in last file + f := s.LastFile + if 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] + + // f.base <= int(p) by definition of searchFiles + if int(p) <= f.Base+f.Size { + s.LastFile = f // race is ok - s.last is only a cache + return f + } + } + return nil +} + +func searchFiles(a []*SourceFile, x int) int { + return sort.Search(len(a), func(i int) bool { return a[i].Base > x }) - 1 +} + +// SourceFile represents a source file. +type SourceFile struct { + // SourceFile set for the file + set *SourceFileSet + // SourceFile name as provided to AddFile + Name string + // SourcePos value range for this file is [base...base+size] + Base int + // SourceFile 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 +} + +// Set returns SourceFileSet. +func (f *SourceFile) Set() *SourceFileSet { + return f.set +} + +// LineCount returns the current number of lines. +func (f *SourceFile) LineCount() int { + return len(f.Lines) +} + +// AddLine adds a new line. +func (f *SourceFile) AddLine(offset int) { + i := len(f.Lines) + if (i == 0 || f.Lines[i-1] < offset) && offset < f.Size { + f.Lines = append(f.Lines, offset) + } +} + +// LineStart returns the position of the first character in the line. +func (f *SourceFile) LineStart(line int) Pos { + if line < 1 { + panic("illegal line number (line numbering starts at 1)") + } + if line > len(f.Lines) { + panic("illegal line number") + } + return Pos(f.Base + f.Lines[line-1]) +} + +// FileSetPos returns the position in the file set. +func (f *SourceFile) FileSetPos(offset int) Pos { + if offset > f.Size { + panic("illegal file offset") + } + return Pos(f.Base + offset) +} + +// Offset translates the file set position into the file offset. +func (f *SourceFile) Offset(p Pos) int { + if int(p) < f.Base || int(p) > f.Base+f.Size { + panic("illegal SourcePos value") + } + return int(p) - f.Base +} + +// Position translates the file set position into the file position. +func (f *SourceFile) Position(p Pos) (pos SourceFilePos) { + if p != NoPos { + if int(p) < f.Base || int(p) > f.Base+f.Size { + panic("illegal SourcePos value") + } + pos = f.position(p) + } + return +} + +func (f *SourceFile) position(p Pos) (pos SourceFilePos) { + offset := int(p) - f.Base + pos.Offset = offset + pos.Filename, pos.Line, pos.Column = f.unpack(offset) + return +} + +func (f *SourceFile) unpack(offset int) (filename string, line, column int) { + filename = f.Name + if i := searchInts(f.Lines, offset); i >= 0 { + line, column = i+1, offset-f.Lines[i]+1 + } + return +} + +func searchInts(a []int, x int) int { + // This function body is a manually inlined version of: + // return sort.Search(len(a), func(i int) bool { return a[i] > x }) - 1 + i, j := 0, len(a) + for i < j { + h := i + (j-i)/2 // avoid overflow when computing h + // i ≤ h < j + if a[h] <= x { + i = h + 1 + } else { + j = h + } + } + return i - 1 +} diff --git a/vendor/github.com/d5/tengo/v2/parser/stmt.go b/vendor/github.com/d5/tengo/v2/parser/stmt.go new file mode 100644 index 00000000..c0848c48 --- /dev/null +++ b/vendor/github.com/d5/tengo/v2/parser/stmt.go @@ -0,0 +1,349 @@ +package parser + +import ( + "strings" + + "github.com/d5/tengo/v2/token" +) + +// Stmt represents a statement in the AST. +type Stmt interface { + Node + stmtNode() +} + +// AssignStmt represents an assignment statement. +type AssignStmt struct { + LHS []Expr + RHS []Expr + Token token.Token + TokenPos Pos +} + +func (s *AssignStmt) stmtNode() {} + +// Pos returns the position of first character belonging to the node. +func (s *AssignStmt) Pos() Pos { + return s.LHS[0].Pos() +} + +// End returns the position of first character immediately after the node. +func (s *AssignStmt) End() Pos { + return s.RHS[len(s.RHS)-1].End() +} + +func (s *AssignStmt) String() string { + var lhs, rhs []string + for _, e := range s.LHS { + lhs = append(lhs, e.String()) + } + for _, e := range s.RHS { + rhs = append(rhs, e.String()) + } + return strings.Join(lhs, ", ") + " " + s.Token.String() + + " " + strings.Join(rhs, ", ") +} + +// BadStmt represents a bad statement. +type BadStmt struct { + From Pos + To Pos +} + +func (s *BadStmt) stmtNode() {} + +// Pos returns the position of first character belonging to the node. +func (s *BadStmt) Pos() Pos { + return s.From +} + +// End returns the position of first character immediately after the node. +func (s *BadStmt) End() Pos { + return s.To +} + +func (s *BadStmt) String() string { + return "" +} + +// BlockStmt represents a block statement. +type BlockStmt struct { + Stmts []Stmt + LBrace Pos + RBrace Pos +} + +func (s *BlockStmt) stmtNode() {} + +// Pos returns the position of first character belonging to the node. +func (s *BlockStmt) Pos() Pos { + return s.LBrace +} + +// End returns the position of first character immediately after the node. +func (s *BlockStmt) End() Pos { + return s.RBrace + 1 +} + +func (s *BlockStmt) String() string { + var list []string + for _, e := range s.Stmts { + list = append(list, e.String()) + } + return "{" + strings.Join(list, "; ") + "}" +} + +// BranchStmt represents a branch statement. +type BranchStmt struct { + Token token.Token + TokenPos Pos + Label *Ident +} + +func (s *BranchStmt) stmtNode() {} + +// Pos returns the position of first character belonging to the node. +func (s *BranchStmt) Pos() Pos { + return s.TokenPos +} + +// End returns the position of first character immediately after the node. +func (s *BranchStmt) End() Pos { + if s.Label != nil { + return s.Label.End() + } + + return Pos(int(s.TokenPos) + len(s.Token.String())) +} + +func (s *BranchStmt) String() string { + var label string + if s.Label != nil { + label = " " + s.Label.Name + } + return s.Token.String() + label +} + +// EmptyStmt represents an empty statement. +type EmptyStmt struct { + Semicolon Pos + Implicit bool +} + +func (s *EmptyStmt) stmtNode() {} + +// Pos returns the position of first character belonging to the node. +func (s *EmptyStmt) Pos() Pos { + return s.Semicolon +} + +// End returns the position of first character immediately after the node. +func (s *EmptyStmt) End() Pos { + if s.Implicit { + return s.Semicolon + } + return s.Semicolon + 1 +} + +func (s *EmptyStmt) String() string { + return ";" +} + +// ExportStmt represents an export statement. +type ExportStmt struct { + ExportPos Pos + Result Expr +} + +func (s *ExportStmt) stmtNode() {} + +// Pos returns the position of first character belonging to the node. +func (s *ExportStmt) Pos() Pos { + return s.ExportPos +} + +// End returns the position of first character immediately after the node. +func (s *ExportStmt) End() Pos { + return s.Result.End() +} + +func (s *ExportStmt) String() string { + return "export " + s.Result.String() +} + +// ExprStmt represents an expression statement. +type ExprStmt struct { + Expr Expr +} + +func (s *ExprStmt) stmtNode() {} + +// Pos returns the position of first character belonging to the node. +func (s *ExprStmt) Pos() Pos { + return s.Expr.Pos() +} + +// End returns the position of first character immediately after the node. +func (s *ExprStmt) End() Pos { + return s.Expr.End() +} + +func (s *ExprStmt) String() string { + return s.Expr.String() +} + +// ForInStmt represents a for-in statement. +type ForInStmt struct { + ForPos Pos + Key *Ident + Value *Ident + Iterable Expr + Body *BlockStmt +} + +func (s *ForInStmt) stmtNode() {} + +// Pos returns the position of first character belonging to the node. +func (s *ForInStmt) Pos() Pos { + return s.ForPos +} + +// End returns the position of first character immediately after the node. +func (s *ForInStmt) End() Pos { + return s.Body.End() +} + +func (s *ForInStmt) String() string { + if s.Value != nil { + return "for " + s.Key.String() + ", " + s.Value.String() + + " in " + s.Iterable.String() + " " + s.Body.String() + } + return "for " + s.Key.String() + " in " + s.Iterable.String() + + " " + s.Body.String() +} + +// ForStmt represents a for statement. +type ForStmt struct { + ForPos Pos + Init Stmt + Cond Expr + Post Stmt + Body *BlockStmt +} + +func (s *ForStmt) stmtNode() {} + +// Pos returns the position of first character belonging to the node. +func (s *ForStmt) Pos() Pos { + return s.ForPos +} + +// End returns the position of first character immediately after the node. +func (s *ForStmt) End() Pos { + return s.Body.End() +} + +func (s *ForStmt) String() string { + var init, cond, post string + if s.Init != nil { + init = s.Init.String() + } + if s.Cond != nil { + cond = s.Cond.String() + " " + } + if s.Post != nil { + post = s.Post.String() + } + + if init != "" || post != "" { + return "for " + init + " ; " + cond + " ; " + post + s.Body.String() + } + return "for " + cond + s.Body.String() +} + +// IfStmt represents an if statement. +type IfStmt struct { + IfPos Pos + Init Stmt + Cond Expr + Body *BlockStmt + Else Stmt // else branch; or nil +} + +func (s *IfStmt) stmtNode() {} + +// Pos returns the position of first character belonging to the node. +func (s *IfStmt) Pos() Pos { + return s.IfPos +} + +// End returns the position of first character immediately after the node. +func (s *IfStmt) End() Pos { + if s.Else != nil { + return s.Else.End() + } + return s.Body.End() +} + +func (s *IfStmt) String() string { + var initStmt, elseStmt string + if s.Init != nil { + initStmt = s.Init.String() + "; " + } + if s.Else != nil { + elseStmt = " else " + s.Else.String() + } + return "if " + initStmt + s.Cond.String() + " " + + s.Body.String() + elseStmt +} + +// IncDecStmt represents increment or decrement statement. +type IncDecStmt struct { + Expr Expr + Token token.Token + TokenPos Pos +} + +func (s *IncDecStmt) stmtNode() {} + +// Pos returns the position of first character belonging to the node. +func (s *IncDecStmt) Pos() Pos { + return s.Expr.Pos() +} + +// End returns the position of first character immediately after the node. +func (s *IncDecStmt) End() Pos { + return Pos(int(s.TokenPos) + 2) +} + +func (s *IncDecStmt) String() string { + return s.Expr.String() + s.Token.String() +} + +// ReturnStmt represents a return statement. +type ReturnStmt struct { + ReturnPos Pos + Result Expr +} + +func (s *ReturnStmt) stmtNode() {} + +// Pos returns the position of first character belonging to the node. +func (s *ReturnStmt) Pos() Pos { + return s.ReturnPos +} + +// End returns the position of first character immediately after the node. +func (s *ReturnStmt) End() Pos { + if s.Result != nil { + return s.Result.End() + } + return s.ReturnPos + 6 +} + +func (s *ReturnStmt) String() string { + if s.Result != nil { + return "return " + s.Result.String() + } + return "return" +} diff --git a/vendor/github.com/d5/tengo/v2/script.go b/vendor/github.com/d5/tengo/v2/script.go new file mode 100644 index 00000000..906771d9 --- /dev/null +++ b/vendor/github.com/d5/tengo/v2/script.go @@ -0,0 +1,313 @@ +package tengo + +import ( + "context" + "fmt" + "sync" + + "github.com/d5/tengo/v2/parser" +) + +// Script can simplify compilation and execution of embedded scripts. +type Script struct { + variables map[string]*Variable + modules *ModuleMap + input []byte + maxAllocs int64 + maxConstObjects int + enableFileImport bool +} + +// NewScript creates a Script instance with an input script. +func NewScript(input []byte) *Script { + return &Script{ + variables: make(map[string]*Variable), + input: input, + maxAllocs: -1, + maxConstObjects: -1, + } +} + +// Add adds a new variable or updates an existing variable to the script. +func (s *Script) Add(name string, value interface{}) error { + obj, err := FromInterface(value) + if err != nil { + return err + } + s.variables[name] = &Variable{ + name: name, + value: obj, + } + return nil +} + +// Remove removes (undefines) an existing variable for the script. It returns +// false if the variable name is not defined. +func (s *Script) Remove(name string) bool { + if _, ok := s.variables[name]; !ok { + return false + } + delete(s.variables, name) + return true +} + +// SetImports sets import modules. +func (s *Script) SetImports(modules *ModuleMap) { + s.modules = modules +} + +// SetMaxAllocs sets the maximum number of objects allocations during the run +// time. Compiled script will return ErrObjectAllocLimit error if it +// exceeds this limit. +func (s *Script) SetMaxAllocs(n int64) { + s.maxAllocs = n +} + +// SetMaxConstObjects sets the maximum number of objects in the compiled +// constants. +func (s *Script) SetMaxConstObjects(n int) { + s.maxConstObjects = n +} + +// EnableFileImport enables or disables module loading from local files. Local +// file modules are disabled by default. +func (s *Script) EnableFileImport(enable bool) { + s.enableFileImport = enable +} + +// Compile compiles the script with all the defined variables, and, returns +// Compiled object. +func (s *Script) Compile() (*Compiled, error) { + symbolTable, globals, err := s.prepCompile() + if err != nil { + return nil, err + } + + fileSet := parser.NewFileSet() + srcFile := fileSet.AddFile("(main)", -1, len(s.input)) + p := parser.NewParser(srcFile, s.input, nil) + file, err := p.ParseFile() + if err != nil { + return nil, err + } + + c := NewCompiler(srcFile, symbolTable, nil, s.modules, nil) + c.EnableFileImport(s.enableFileImport) + if err := c.Compile(file); err != nil { + return nil, err + } + + // reduce globals size + globals = globals[:symbolTable.MaxSymbols()+1] + + // global symbol names to indexes + globalIndexes := make(map[string]int, len(globals)) + for _, name := range symbolTable.Names() { + symbol, _, _ := symbolTable.Resolve(name) + if symbol.Scope == ScopeGlobal { + globalIndexes[name] = symbol.Index + } + } + + // remove duplicates from constants + bytecode := c.Bytecode() + bytecode.RemoveDuplicates() + + // check the constant objects limit + if s.maxConstObjects >= 0 { + cnt := bytecode.CountObjects() + if cnt > s.maxConstObjects { + return nil, fmt.Errorf("exceeding constant objects limit: %d", cnt) + } + } + return &Compiled{ + globalIndexes: globalIndexes, + bytecode: bytecode, + globals: globals, + maxAllocs: s.maxAllocs, + }, nil +} + +// Run compiles and runs the scripts. Use returned compiled object to access +// global variables. +func (s *Script) Run() (compiled *Compiled, err error) { + compiled, err = s.Compile() + if err != nil { + return + } + err = compiled.Run() + return +} + +// RunContext is like Run but includes a context. +func (s *Script) RunContext( + ctx context.Context, +) (compiled *Compiled, err error) { + compiled, err = s.Compile() + if err != nil { + return + } + err = compiled.RunContext(ctx) + return +} + +func (s *Script) prepCompile() ( + symbolTable *SymbolTable, + globals []Object, + err error, +) { + var names []string + for name := range s.variables { + names = append(names, name) + } + + symbolTable = NewSymbolTable() + for idx, fn := range builtinFuncs { + symbolTable.DefineBuiltin(idx, fn.Name) + } + + globals = make([]Object, GlobalsSize) + + for idx, name := range names { + symbol := symbolTable.Define(name) + if symbol.Index != idx { + panic(fmt.Errorf("wrong symbol index: %d != %d", + idx, symbol.Index)) + } + globals[symbol.Index] = s.variables[name].value + } + return +} + +// Compiled is a compiled instance of the user script. Use Script.Compile() to +// create Compiled object. +type Compiled struct { + globalIndexes map[string]int // global symbol name to index + bytecode *Bytecode + globals []Object + maxAllocs int64 + lock sync.RWMutex +} + +// Run executes the compiled script in the virtual machine. +func (c *Compiled) Run() error { + c.lock.Lock() + defer c.lock.Unlock() + + v := NewVM(c.bytecode, c.globals, c.maxAllocs) + return v.Run() +} + +// RunContext is like Run but includes a context. +func (c *Compiled) RunContext(ctx context.Context) (err error) { + c.lock.Lock() + defer c.lock.Unlock() + + v := NewVM(c.bytecode, c.globals, c.maxAllocs) + ch := make(chan error, 1) + go func() { + ch <- v.Run() + }() + + select { + case <-ctx.Done(): + v.Abort() + <-ch + err = ctx.Err() + case err = <-ch: + } + return +} + +// Clone creates a new copy of Compiled. Cloned copies are safe for concurrent +// use by multiple goroutines. +func (c *Compiled) Clone() *Compiled { + c.lock.Lock() + defer c.lock.Unlock() + + clone := &Compiled{ + globalIndexes: c.globalIndexes, + bytecode: c.bytecode, + globals: make([]Object, len(c.globals)), + maxAllocs: c.maxAllocs, + } + // copy global objects + for idx, g := range c.globals { + if g != nil { + clone.globals[idx] = g + } + } + return clone +} + +// IsDefined returns true if the variable name is defined (has value) before or +// after the execution. +func (c *Compiled) IsDefined(name string) bool { + c.lock.RLock() + defer c.lock.RUnlock() + + idx, ok := c.globalIndexes[name] + if !ok { + return false + } + v := c.globals[idx] + if v == nil { + return false + } + return v != UndefinedValue +} + +// Get returns a variable identified by the name. +func (c *Compiled) Get(name string) *Variable { + c.lock.RLock() + defer c.lock.RUnlock() + + value := UndefinedValue + if idx, ok := c.globalIndexes[name]; ok { + value = c.globals[idx] + if value == nil { + value = UndefinedValue + } + } + return &Variable{ + name: name, + value: value, + } +} + +// GetAll returns all the variables that are defined by the compiled script. +func (c *Compiled) GetAll() []*Variable { + c.lock.RLock() + defer c.lock.RUnlock() + + var vars []*Variable + for name, idx := range c.globalIndexes { + value := c.globals[idx] + if value == nil { + value = UndefinedValue + } + vars = append(vars, &Variable{ + name: name, + value: value, + }) + } + return vars +} + +// Set replaces the value of a global variable identified by the name. An error +// will be returned if the name was not defined during compilation. +func (c *Compiled) Set(name string, value interface{}) error { + c.lock.Lock() + defer c.lock.Unlock() + + obj, err := FromInterface(value) + if err != nil { + return err + } + idx, ok := c.globalIndexes[name] + if !ok { + return fmt.Errorf("'%s' is not defined", name) + } + c.globals[idx] = obj + return nil +} diff --git a/vendor/github.com/d5/tengo/v2/stdlib/base64.go b/vendor/github.com/d5/tengo/v2/stdlib/base64.go new file mode 100644 index 00000000..b4c5b56e --- /dev/null +++ b/vendor/github.com/d5/tengo/v2/stdlib/base64.go @@ -0,0 +1,34 @@ +package stdlib + +import ( + "encoding/base64" + + "github.com/d5/tengo/v2" +) + +var base64Module = map[string]tengo.Object{ + "encode": &tengo.UserFunction{ + Value: FuncAYRS(base64.StdEncoding.EncodeToString), + }, + "decode": &tengo.UserFunction{ + Value: FuncASRYE(base64.StdEncoding.DecodeString), + }, + "raw_encode": &tengo.UserFunction{ + Value: FuncAYRS(base64.RawStdEncoding.EncodeToString), + }, + "raw_decode": &tengo.UserFunction{ + Value: FuncASRYE(base64.RawStdEncoding.DecodeString), + }, + "url_encode": &tengo.UserFunction{ + Value: FuncAYRS(base64.URLEncoding.EncodeToString), + }, + "url_decode": &tengo.UserFunction{ + Value: FuncASRYE(base64.URLEncoding.DecodeString), + }, + "raw_url_encode": &tengo.UserFunction{ + Value: FuncAYRS(base64.RawURLEncoding.EncodeToString), + }, + "raw_url_decode": &tengo.UserFunction{ + Value: FuncASRYE(base64.RawURLEncoding.DecodeString), + }, +} diff --git a/vendor/github.com/d5/tengo/v2/stdlib/builtin_modules.go b/vendor/github.com/d5/tengo/v2/stdlib/builtin_modules.go new file mode 100644 index 00000000..cf0e9621 --- /dev/null +++ b/vendor/github.com/d5/tengo/v2/stdlib/builtin_modules.go @@ -0,0 +1,18 @@ +package stdlib + +import ( + "github.com/d5/tengo/v2" +) + +// BuiltinModules are builtin type standard library modules. +var BuiltinModules = map[string]map[string]tengo.Object{ + "math": mathModule, + "os": osModule, + "text": textModule, + "times": timesModule, + "rand": randModule, + "fmt": fmtModule, + "json": jsonModule, + "base64": base64Module, + "hex": hexModule, +} diff --git a/vendor/github.com/d5/tengo/v2/stdlib/errors.go b/vendor/github.com/d5/tengo/v2/stdlib/errors.go new file mode 100644 index 00000000..ad83c6f8 --- /dev/null +++ b/vendor/github.com/d5/tengo/v2/stdlib/errors.go @@ -0,0 +1,12 @@ +package stdlib + +import ( + "github.com/d5/tengo/v2" +) + +func wrapError(err error) tengo.Object { + if err == nil { + return tengo.TrueValue + } + return &tengo.Error{Value: &tengo.String{Value: err.Error()}} +} diff --git a/vendor/github.com/d5/tengo/v2/stdlib/fmt.go b/vendor/github.com/d5/tengo/v2/stdlib/fmt.go new file mode 100644 index 00000000..9945277f --- /dev/null +++ b/vendor/github.com/d5/tengo/v2/stdlib/fmt.go @@ -0,0 +1,101 @@ +package stdlib + +import ( + "fmt" + + "github.com/d5/tengo/v2" +) + +var fmtModule = map[string]tengo.Object{ + "print": &tengo.UserFunction{Name: "print", Value: fmtPrint}, + "printf": &tengo.UserFunction{Name: "printf", Value: fmtPrintf}, + "println": &tengo.UserFunction{Name: "println", Value: fmtPrintln}, + "sprintf": &tengo.UserFunction{Name: "sprintf", Value: fmtSprintf}, +} + +func fmtPrint(args ...tengo.Object) (ret tengo.Object, err error) { + printArgs, err := getPrintArgs(args...) + if err != nil { + return nil, err + } + _, _ = fmt.Print(printArgs...) + return nil, nil +} + +func fmtPrintf(args ...tengo.Object) (ret tengo.Object, err error) { + numArgs := len(args) + if numArgs == 0 { + return nil, tengo.ErrWrongNumArguments + } + + format, ok := args[0].(*tengo.String) + if !ok { + return nil, tengo.ErrInvalidArgumentType{ + Name: "format", + Expected: "string", + Found: args[0].TypeName(), + } + } + if numArgs == 1 { + fmt.Print(format) + return nil, nil + } + + s, err := tengo.Format(format.Value, args[1:]...) + if err != nil { + return nil, err + } + fmt.Print(s) + return nil, nil +} + +func fmtPrintln(args ...tengo.Object) (ret tengo.Object, err error) { + printArgs, err := getPrintArgs(args...) + if err != nil { + return nil, err + } + printArgs = append(printArgs, "\n") + _, _ = fmt.Print(printArgs...) + return nil, nil +} + +func fmtSprintf(args ...tengo.Object) (ret tengo.Object, err error) { + numArgs := len(args) + if numArgs == 0 { + return nil, tengo.ErrWrongNumArguments + } + + format, ok := args[0].(*tengo.String) + if !ok { + return nil, tengo.ErrInvalidArgumentType{ + Name: "format", + Expected: "string", + Found: args[0].TypeName(), + } + } + if numArgs == 1 { + // okay to return 'format' directly as String is immutable + return format, nil + } + s, err := tengo.Format(format.Value, args[1:]...) + if err != nil { + return nil, err + } + return &tengo.String{Value: s}, nil +} + +func getPrintArgs(args ...tengo.Object) ([]interface{}, error) { + var printArgs []interface{} + l := 0 + for _, arg := range args { + s, _ := tengo.ToString(arg) + slen := len(s) + // make sure length does not exceed the limit + if l+slen > tengo.MaxStringLen { + return nil, tengo.ErrStringLimit + } + l += slen + printArgs = append(printArgs, s) + } + return printArgs, nil +} diff --git a/vendor/github.com/d5/tengo/v2/stdlib/func_typedefs.go b/vendor/github.com/d5/tengo/v2/stdlib/func_typedefs.go new file mode 100644 index 00000000..fdac933c --- /dev/null +++ b/vendor/github.com/d5/tengo/v2/stdlib/func_typedefs.go @@ -0,0 +1,1049 @@ +package stdlib + +import ( + "fmt" + + "github.com/d5/tengo/v2" +) + +// FuncAR transform a function of 'func()' signature into CallableFunc type. +func FuncAR(fn func()) tengo.CallableFunc { + return func(args ...tengo.Object) (ret tengo.Object, err error) { + if len(args) != 0 { + return nil, tengo.ErrWrongNumArguments + } + fn() + return tengo.UndefinedValue, nil + } +} + +// FuncARI transform a function of 'func() int' signature into CallableFunc +// type. +func FuncARI(fn func() int) tengo.CallableFunc { + return func(args ...tengo.Object) (ret tengo.Object, err error) { + if len(args) != 0 { + return nil, tengo.ErrWrongNumArguments + } + return &tengo.Int{Value: int64(fn())}, nil + } +} + +// FuncARI64 transform a function of 'func() int64' signature into CallableFunc +// type. +func FuncARI64(fn func() int64) tengo.CallableFunc { + return func(args ...tengo.Object) (ret tengo.Object, err error) { + if len(args) != 0 { + return nil, tengo.ErrWrongNumArguments + } + return &tengo.Int{Value: fn()}, nil + } +} + +// FuncAI64RI64 transform a function of 'func(int64) int64' signature into +// CallableFunc type. +func FuncAI64RI64(fn func(int64) int64) tengo.CallableFunc { + return func(args ...tengo.Object) (ret tengo.Object, err error) { + if len(args) != 1 { + return nil, tengo.ErrWrongNumArguments + } + + i1, ok := tengo.ToInt64(args[0]) + if !ok { + return nil, tengo.ErrInvalidArgumentType{ + Name: "first", + Expected: "int(compatible)", + Found: args[0].TypeName(), + } + } + return &tengo.Int{Value: fn(i1)}, nil + } +} + +// FuncAI64R transform a function of 'func(int64)' signature into CallableFunc +// type. +func FuncAI64R(fn func(int64)) tengo.CallableFunc { + return func(args ...tengo.Object) (ret tengo.Object, err error) { + if len(args) != 1 { + return nil, tengo.ErrWrongNumArguments + } + + i1, ok := tengo.ToInt64(args[0]) + if !ok { + return nil, tengo.ErrInvalidArgumentType{ + Name: "first", + Expected: "int(compatible)", + Found: args[0].TypeName(), + } + } + fn(i1) + return tengo.UndefinedValue, nil + } +} + +// FuncARB transform a function of 'func() bool' signature into CallableFunc +// type. +func FuncARB(fn func() bool) tengo.CallableFunc { + return func(args ...tengo.Object) (ret tengo.Object, err error) { + if len(args) != 0 { + return nil, tengo.ErrWrongNumArguments + } + if fn() { + return tengo.TrueValue, nil + } + return tengo.FalseValue, nil + } +} + +// FuncARE transform a function of 'func() error' signature into CallableFunc +// type. +func FuncARE(fn func() error) tengo.CallableFunc { + return func(args ...tengo.Object) (ret tengo.Object, err error) { + if len(args) != 0 { + return nil, tengo.ErrWrongNumArguments + } + return wrapError(fn()), nil + } +} + +// FuncARS transform a function of 'func() string' signature into CallableFunc +// type. +func FuncARS(fn func() string) tengo.CallableFunc { + return func(args ...tengo.Object) (ret tengo.Object, err error) { + if len(args) != 0 { + return nil, tengo.ErrWrongNumArguments + } + s := fn() + if len(s) > tengo.MaxStringLen { + return nil, tengo.ErrStringLimit + } + return &tengo.String{Value: s}, nil + } +} + +// FuncARSE transform a function of 'func() (string, error)' signature into +// CallableFunc type. +func FuncARSE(fn func() (string, error)) tengo.CallableFunc { + return func(args ...tengo.Object) (ret tengo.Object, err error) { + if len(args) != 0 { + return nil, tengo.ErrWrongNumArguments + } + res, err := fn() + if err != nil { + return wrapError(err), nil + } + if len(res) > tengo.MaxStringLen { + return nil, tengo.ErrStringLimit + } + return &tengo.String{Value: res}, nil + } +} + +// FuncARYE transform a function of 'func() ([]byte, error)' signature into +// CallableFunc type. +func FuncARYE(fn func() ([]byte, error)) tengo.CallableFunc { + return func(args ...tengo.Object) (ret tengo.Object, err error) { + if len(args) != 0 { + return nil, tengo.ErrWrongNumArguments + } + res, err := fn() + if err != nil { + return wrapError(err), nil + } + if len(res) > tengo.MaxBytesLen { + return nil, tengo.ErrBytesLimit + } + return &tengo.Bytes{Value: res}, nil + } +} + +// FuncARF transform a function of 'func() float64' signature into CallableFunc +// type. +func FuncARF(fn func() float64) tengo.CallableFunc { + return func(args ...tengo.Object) (ret tengo.Object, err error) { + if len(args) != 0 { + return nil, tengo.ErrWrongNumArguments + } + return &tengo.Float{Value: fn()}, nil + } +} + +// FuncARSs transform a function of 'func() []string' signature into +// CallableFunc type. +func FuncARSs(fn func() []string) tengo.CallableFunc { + return func(args ...tengo.Object) (ret tengo.Object, err error) { + if len(args) != 0 { + return nil, tengo.ErrWrongNumArguments + } + arr := &tengo.Array{} + for _, elem := range fn() { + if len(elem) > tengo.MaxStringLen { + return nil, tengo.ErrStringLimit + } + arr.Value = append(arr.Value, &tengo.String{Value: elem}) + } + return arr, nil + } +} + +// FuncARIsE transform a function of 'func() ([]int, error)' signature into +// CallableFunc type. +func FuncARIsE(fn func() ([]int, error)) tengo.CallableFunc { + return func(args ...tengo.Object) (ret tengo.Object, err error) { + if len(args) != 0 { + return nil, tengo.ErrWrongNumArguments + } + res, err := fn() + if err != nil { + return wrapError(err), nil + } + arr := &tengo.Array{} + for _, v := range res { + arr.Value = append(arr.Value, &tengo.Int{Value: int64(v)}) + } + return arr, nil + } +} + +// FuncAIRIs transform a function of 'func(int) []int' signature into +// CallableFunc type. +func FuncAIRIs(fn func(int) []int) tengo.CallableFunc { + return func(args ...tengo.Object) (ret tengo.Object, err error) { + if len(args) != 1 { + return nil, tengo.ErrWrongNumArguments + } + i1, ok := tengo.ToInt(args[0]) + if !ok { + return nil, tengo.ErrInvalidArgumentType{ + Name: "first", + Expected: "int(compatible)", + Found: args[0].TypeName(), + } + } + res := fn(i1) + arr := &tengo.Array{} + for _, v := range res { + arr.Value = append(arr.Value, &tengo.Int{Value: int64(v)}) + } + return arr, nil + } +} + +// FuncAFRF transform a function of 'func(float64) float64' signature into +// CallableFunc type. +func FuncAFRF(fn func(float64) float64) tengo.CallableFunc { + return func(args ...tengo.Object) (ret tengo.Object, err error) { + if len(args) != 1 { + return nil, tengo.ErrWrongNumArguments + } + f1, ok := tengo.ToFloat64(args[0]) + if !ok { + return nil, tengo.ErrInvalidArgumentType{ + Name: "first", + Expected: "float(compatible)", + Found: args[0].TypeName(), + } + } + return &tengo.Float{Value: fn(f1)}, nil + } +} + +// FuncAIR transform a function of 'func(int)' signature into CallableFunc type. +func FuncAIR(fn func(int)) tengo.CallableFunc { + return func(args ...tengo.Object) (ret tengo.Object, err error) { + if len(args) != 1 { + return nil, tengo.ErrWrongNumArguments + } + i1, ok := tengo.ToInt(args[0]) + if !ok { + return nil, tengo.ErrInvalidArgumentType{ + Name: "first", + Expected: "int(compatible)", + Found: args[0].TypeName(), + } + } + fn(i1) + return tengo.UndefinedValue, nil + } +} + +// FuncAIRF transform a function of 'func(int) float64' signature into +// CallableFunc type. +func FuncAIRF(fn func(int) float64) tengo.CallableFunc { + return func(args ...tengo.Object) (ret tengo.Object, err error) { + if len(args) != 1 { + return nil, tengo.ErrWrongNumArguments + } + i1, ok := tengo.ToInt(args[0]) + if !ok { + return nil, tengo.ErrInvalidArgumentType{ + Name: "first", + Expected: "int(compatible)", + Found: args[0].TypeName(), + } + } + return &tengo.Float{Value: fn(i1)}, nil + } +} + +// FuncAFRI transform a function of 'func(float64) int' signature into +// CallableFunc type. +func FuncAFRI(fn func(float64) int) tengo.CallableFunc { + return func(args ...tengo.Object) (ret tengo.Object, err error) { + if len(args) != 1 { + return nil, tengo.ErrWrongNumArguments + } + f1, ok := tengo.ToFloat64(args[0]) + if !ok { + return nil, tengo.ErrInvalidArgumentType{ + Name: "first", + Expected: "float(compatible)", + Found: args[0].TypeName(), + } + } + return &tengo.Int{Value: int64(fn(f1))}, nil + } +} + +// FuncAFFRF transform a function of 'func(float64, float64) float64' signature +// into CallableFunc type. +func FuncAFFRF(fn func(float64, float64) float64) tengo.CallableFunc { + return func(args ...tengo.Object) (ret tengo.Object, err error) { + if len(args) != 2 { + return nil, tengo.ErrWrongNumArguments + } + f1, ok := tengo.ToFloat64(args[0]) + if !ok { + return nil, tengo.ErrInvalidArgumentType{ + Name: "first", + Expected: "float(compatible)", + Found: args[0].TypeName(), + } + } + f2, ok := tengo.ToFloat64(args[1]) + if !ok { + return nil, tengo.ErrInvalidArgumentType{ + Name: "second", + Expected: "float(compatible)", + Found: args[1].TypeName(), + } + } + return &tengo.Float{Value: fn(f1, f2)}, nil + } +} + +// FuncAIFRF transform a function of 'func(int, float64) float64' signature +// into CallableFunc type. +func FuncAIFRF(fn func(int, float64) float64) tengo.CallableFunc { + return func(args ...tengo.Object) (ret tengo.Object, err error) { + if len(args) != 2 { + return nil, tengo.ErrWrongNumArguments + } + i1, ok := tengo.ToInt(args[0]) + if !ok { + return nil, tengo.ErrInvalidArgumentType{ + Name: "first", + Expected: "int(compatible)", + Found: args[0].TypeName(), + } + } + f2, ok := tengo.ToFloat64(args[1]) + if !ok { + return nil, tengo.ErrInvalidArgumentType{ + Name: "second", + Expected: "float(compatible)", + Found: args[1].TypeName(), + } + } + return &tengo.Float{Value: fn(i1, f2)}, nil + } +} + +// FuncAFIRF transform a function of 'func(float64, int) float64' signature +// into CallableFunc type. +func FuncAFIRF(fn func(float64, int) float64) tengo.CallableFunc { + return func(args ...tengo.Object) (ret tengo.Object, err error) { + if len(args) != 2 { + return nil, tengo.ErrWrongNumArguments + } + f1, ok := tengo.ToFloat64(args[0]) + if !ok { + return nil, tengo.ErrInvalidArgumentType{ + Name: "first", + Expected: "float(compatible)", + Found: args[0].TypeName(), + } + } + i2, ok := tengo.ToInt(args[1]) + if !ok { + return nil, tengo.ErrInvalidArgumentType{ + Name: "second", + Expected: "int(compatible)", + Found: args[1].TypeName(), + } + } + return &tengo.Float{Value: fn(f1, i2)}, nil + } +} + +// FuncAFIRB transform a function of 'func(float64, int) bool' signature +// into CallableFunc type. +func FuncAFIRB(fn func(float64, int) bool) tengo.CallableFunc { + return func(args ...tengo.Object) (ret tengo.Object, err error) { + if len(args) != 2 { + return nil, tengo.ErrWrongNumArguments + } + f1, ok := tengo.ToFloat64(args[0]) + if !ok { + return nil, tengo.ErrInvalidArgumentType{ + Name: "first", + Expected: "float(compatible)", + Found: args[0].TypeName(), + } + } + i2, ok := tengo.ToInt(args[1]) + if !ok { + return nil, tengo.ErrInvalidArgumentType{ + Name: "second", + Expected: "int(compatible)", + Found: args[1].TypeName(), + } + } + if fn(f1, i2) { + return tengo.TrueValue, nil + } + return tengo.FalseValue, nil + } +} + +// FuncAFRB transform a function of 'func(float64) bool' signature +// into CallableFunc type. +func FuncAFRB(fn func(float64) bool) tengo.CallableFunc { + return func(args ...tengo.Object) (ret tengo.Object, err error) { + if len(args) != 1 { + return nil, tengo.ErrWrongNumArguments + } + f1, ok := tengo.ToFloat64(args[0]) + if !ok { + return nil, tengo.ErrInvalidArgumentType{ + Name: "first", + Expected: "float(compatible)", + Found: args[0].TypeName(), + } + } + if fn(f1) { + return tengo.TrueValue, nil + } + return tengo.FalseValue, nil + } +} + +// FuncASRS transform a function of 'func(string) string' signature into +// CallableFunc type. User function will return 'true' if underlying native +// function returns nil. +func FuncASRS(fn func(string) string) tengo.CallableFunc { + return func(args ...tengo.Object) (tengo.Object, error) { + if len(args) != 1 { + return nil, tengo.ErrWrongNumArguments + } + s1, ok := tengo.ToString(args[0]) + if !ok { + return nil, tengo.ErrInvalidArgumentType{ + Name: "first", + Expected: "string(compatible)", + Found: args[0].TypeName(), + } + } + s := fn(s1) + if len(s) > tengo.MaxStringLen { + return nil, tengo.ErrStringLimit + } + return &tengo.String{Value: s}, nil + } +} + +// FuncASRSs transform a function of 'func(string) []string' signature into +// CallableFunc type. +func FuncASRSs(fn func(string) []string) tengo.CallableFunc { + return func(args ...tengo.Object) (tengo.Object, error) { + if len(args) != 1 { + return nil, tengo.ErrWrongNumArguments + } + s1, ok := tengo.ToString(args[0]) + if !ok { + return nil, tengo.ErrInvalidArgumentType{ + Name: "first", + Expected: "string(compatible)", + Found: args[0].TypeName(), + } + } + res := fn(s1) + arr := &tengo.Array{} + for _, elem := range res { + if len(elem) > tengo.MaxStringLen { + return nil, tengo.ErrStringLimit + } + arr.Value = append(arr.Value, &tengo.String{Value: elem}) + } + return arr, nil + } +} + +// FuncASRSE transform a function of 'func(string) (string, error)' signature +// into CallableFunc type. User function will return 'true' if underlying +// native function returns nil. +func FuncASRSE(fn func(string) (string, error)) tengo.CallableFunc { + return func(args ...tengo.Object) (tengo.Object, error) { + if len(args) != 1 { + return nil, tengo.ErrWrongNumArguments + } + s1, ok := tengo.ToString(args[0]) + if !ok { + return nil, tengo.ErrInvalidArgumentType{ + Name: "first", + Expected: "string(compatible)", + Found: args[0].TypeName(), + } + } + res, err := fn(s1) + if err != nil { + return wrapError(err), nil + } + if len(res) > tengo.MaxStringLen { + return nil, tengo.ErrStringLimit + } + return &tengo.String{Value: res}, nil + } +} + +// FuncASRE transform a function of 'func(string) error' signature into +// CallableFunc type. User function will return 'true' if underlying native +// function returns nil. +func FuncASRE(fn func(string) error) tengo.CallableFunc { + return func(args ...tengo.Object) (tengo.Object, error) { + if len(args) != 1 { + return nil, tengo.ErrWrongNumArguments + } + s1, ok := tengo.ToString(args[0]) + if !ok { + return nil, tengo.ErrInvalidArgumentType{ + Name: "first", + Expected: "string(compatible)", + Found: args[0].TypeName(), + } + } + return wrapError(fn(s1)), nil + } +} + +// FuncASSRE transform a function of 'func(string, string) error' signature +// into CallableFunc type. User function will return 'true' if underlying +// native function returns nil. +func FuncASSRE(fn func(string, string) error) tengo.CallableFunc { + return func(args ...tengo.Object) (tengo.Object, error) { + if len(args) != 2 { + return nil, tengo.ErrWrongNumArguments + } + s1, ok := tengo.ToString(args[0]) + if !ok { + return nil, tengo.ErrInvalidArgumentType{ + Name: "first", + Expected: "string(compatible)", + Found: args[0].TypeName(), + } + } + s2, ok := tengo.ToString(args[1]) + if !ok { + return nil, tengo.ErrInvalidArgumentType{ + Name: "second", + Expected: "string(compatible)", + Found: args[1].TypeName(), + } + } + return wrapError(fn(s1, s2)), nil + } +} + +// FuncASSRSs transform a function of 'func(string, string) []string' +// signature into CallableFunc type. +func FuncASSRSs(fn func(string, string) []string) tengo.CallableFunc { + return func(args ...tengo.Object) (tengo.Object, error) { + if len(args) != 2 { + return nil, tengo.ErrWrongNumArguments + } + s1, ok := tengo.ToString(args[0]) + if !ok { + return nil, tengo.ErrInvalidArgumentType{ + Name: "first", + Expected: "string(compatible)", + Found: args[0].TypeName(), + } + } + s2, ok := tengo.ToString(args[1]) + if !ok { + return nil, tengo.ErrInvalidArgumentType{ + Name: "first", + Expected: "string(compatible)", + Found: args[1].TypeName(), + } + } + arr := &tengo.Array{} + for _, res := range fn(s1, s2) { + if len(res) > tengo.MaxStringLen { + return nil, tengo.ErrStringLimit + } + arr.Value = append(arr.Value, &tengo.String{Value: res}) + } + return arr, nil + } +} + +// FuncASSIRSs transform a function of 'func(string, string, int) []string' +// signature into CallableFunc type. +func FuncASSIRSs(fn func(string, string, int) []string) tengo.CallableFunc { + return func(args ...tengo.Object) (tengo.Object, error) { + if len(args) != 3 { + return nil, tengo.ErrWrongNumArguments + } + s1, ok := tengo.ToString(args[0]) + if !ok { + return nil, tengo.ErrInvalidArgumentType{ + Name: "first", + Expected: "string(compatible)", + Found: args[0].TypeName(), + } + } + s2, ok := tengo.ToString(args[1]) + if !ok { + return nil, tengo.ErrInvalidArgumentType{ + Name: "second", + Expected: "string(compatible)", + Found: args[1].TypeName(), + } + } + i3, ok := tengo.ToInt(args[2]) + if !ok { + return nil, tengo.ErrInvalidArgumentType{ + Name: "third", + Expected: "int(compatible)", + Found: args[2].TypeName(), + } + } + arr := &tengo.Array{} + for _, res := range fn(s1, s2, i3) { + if len(res) > tengo.MaxStringLen { + return nil, tengo.ErrStringLimit + } + arr.Value = append(arr.Value, &tengo.String{Value: res}) + } + return arr, nil + } +} + +// FuncASSRI transform a function of 'func(string, string) int' signature into +// CallableFunc type. +func FuncASSRI(fn func(string, string) int) tengo.CallableFunc { + return func(args ...tengo.Object) (tengo.Object, error) { + if len(args) != 2 { + return nil, tengo.ErrWrongNumArguments + } + s1, ok := tengo.ToString(args[0]) + if !ok { + return nil, tengo.ErrInvalidArgumentType{ + Name: "first", + Expected: "string(compatible)", + Found: args[0].TypeName(), + } + } + s2, ok := tengo.ToString(args[1]) + if !ok { + return nil, tengo.ErrInvalidArgumentType{ + Name: "second", + Expected: "string(compatible)", + Found: args[0].TypeName(), + } + } + return &tengo.Int{Value: int64(fn(s1, s2))}, nil + } +} + +// FuncASSRS transform a function of 'func(string, string) string' signature +// into CallableFunc type. +func FuncASSRS(fn func(string, string) string) tengo.CallableFunc { + return func(args ...tengo.Object) (tengo.Object, error) { + if len(args) != 2 { + return nil, tengo.ErrWrongNumArguments + } + s1, ok := tengo.ToString(args[0]) + if !ok { + return nil, tengo.ErrInvalidArgumentType{ + Name: "first", + Expected: "string(compatible)", + Found: args[0].TypeName(), + } + } + s2, ok := tengo.ToString(args[1]) + if !ok { + return nil, tengo.ErrInvalidArgumentType{ + Name: "second", + Expected: "string(compatible)", + Found: args[1].TypeName(), + } + } + s := fn(s1, s2) + if len(s) > tengo.MaxStringLen { + return nil, tengo.ErrStringLimit + } + return &tengo.String{Value: s}, nil + } +} + +// FuncASSRB transform a function of 'func(string, string) bool' signature +// into CallableFunc type. +func FuncASSRB(fn func(string, string) bool) tengo.CallableFunc { + return func(args ...tengo.Object) (tengo.Object, error) { + if len(args) != 2 { + return nil, tengo.ErrWrongNumArguments + } + s1, ok := tengo.ToString(args[0]) + if !ok { + return nil, tengo.ErrInvalidArgumentType{ + Name: "first", + Expected: "string(compatible)", + Found: args[0].TypeName(), + } + } + s2, ok := tengo.ToString(args[1]) + if !ok { + return nil, tengo.ErrInvalidArgumentType{ + Name: "second", + Expected: "string(compatible)", + Found: args[1].TypeName(), + } + } + if fn(s1, s2) { + return tengo.TrueValue, nil + } + return tengo.FalseValue, nil + } +} + +// FuncASsSRS transform a function of 'func([]string, string) string' signature +// into CallableFunc type. +func FuncASsSRS(fn func([]string, string) string) tengo.CallableFunc { + return func(args ...tengo.Object) (tengo.Object, error) { + if len(args) != 2 { + return nil, tengo.ErrWrongNumArguments + } + var ss1 []string + switch arg0 := args[0].(type) { + case *tengo.Array: + for idx, a := range arg0.Value { + as, ok := tengo.ToString(a) + if !ok { + return nil, tengo.ErrInvalidArgumentType{ + Name: fmt.Sprintf("first[%d]", idx), + Expected: "string(compatible)", + Found: a.TypeName(), + } + } + ss1 = append(ss1, as) + } + case *tengo.ImmutableArray: + for idx, a := range arg0.Value { + as, ok := tengo.ToString(a) + if !ok { + return nil, tengo.ErrInvalidArgumentType{ + Name: fmt.Sprintf("first[%d]", idx), + Expected: "string(compatible)", + Found: a.TypeName(), + } + } + ss1 = append(ss1, as) + } + default: + return nil, tengo.ErrInvalidArgumentType{ + Name: "first", + Expected: "array", + Found: args[0].TypeName(), + } + } + s2, ok := tengo.ToString(args[1]) + if !ok { + return nil, tengo.ErrInvalidArgumentType{ + Name: "second", + Expected: "string(compatible)", + Found: args[1].TypeName(), + } + } + s := fn(ss1, s2) + if len(s) > tengo.MaxStringLen { + return nil, tengo.ErrStringLimit + } + return &tengo.String{Value: s}, nil + } +} + +// FuncASI64RE transform a function of 'func(string, int64) error' signature +// into CallableFunc type. +func FuncASI64RE(fn func(string, int64) error) tengo.CallableFunc { + return func(args ...tengo.Object) (ret tengo.Object, err error) { + if len(args) != 2 { + return nil, tengo.ErrWrongNumArguments + } + s1, ok := tengo.ToString(args[0]) + if !ok { + return nil, tengo.ErrInvalidArgumentType{ + Name: "first", + Expected: "string(compatible)", + Found: args[0].TypeName(), + } + } + i2, ok := tengo.ToInt64(args[1]) + if !ok { + return nil, tengo.ErrInvalidArgumentType{ + Name: "second", + Expected: "int(compatible)", + Found: args[1].TypeName(), + } + } + return wrapError(fn(s1, i2)), nil + } +} + +// FuncAIIRE transform a function of 'func(int, int) error' signature +// into CallableFunc type. +func FuncAIIRE(fn func(int, int) error) tengo.CallableFunc { + return func(args ...tengo.Object) (ret tengo.Object, err error) { + if len(args) != 2 { + return nil, tengo.ErrWrongNumArguments + } + i1, ok := tengo.ToInt(args[0]) + if !ok { + return nil, tengo.ErrInvalidArgumentType{ + Name: "first", + Expected: "int(compatible)", + Found: args[0].TypeName(), + } + } + i2, ok := tengo.ToInt(args[1]) + if !ok { + return nil, tengo.ErrInvalidArgumentType{ + Name: "second", + Expected: "int(compatible)", + Found: args[1].TypeName(), + } + } + return wrapError(fn(i1, i2)), nil + } +} + +// FuncASIRS transform a function of 'func(string, int) string' signature +// into CallableFunc type. +func FuncASIRS(fn func(string, int) string) tengo.CallableFunc { + return func(args ...tengo.Object) (ret tengo.Object, err error) { + if len(args) != 2 { + return nil, tengo.ErrWrongNumArguments + } + s1, ok := tengo.ToString(args[0]) + if !ok { + return nil, tengo.ErrInvalidArgumentType{ + Name: "first", + Expected: "string(compatible)", + Found: args[0].TypeName(), + } + } + i2, ok := tengo.ToInt(args[1]) + if !ok { + return nil, tengo.ErrInvalidArgumentType{ + Name: "second", + Expected: "int(compatible)", + Found: args[1].TypeName(), + } + } + s := fn(s1, i2) + if len(s) > tengo.MaxStringLen { + return nil, tengo.ErrStringLimit + } + return &tengo.String{Value: s}, nil + } +} + +// FuncASIIRE transform a function of 'func(string, int, int) error' signature +// into CallableFunc type. +func FuncASIIRE(fn func(string, int, int) error) tengo.CallableFunc { + return func(args ...tengo.Object) (ret tengo.Object, err error) { + if len(args) != 3 { + return nil, tengo.ErrWrongNumArguments + } + s1, ok := tengo.ToString(args[0]) + if !ok { + return nil, tengo.ErrInvalidArgumentType{ + Name: "first", + Expected: "string(compatible)", + Found: args[0].TypeName(), + } + } + i2, ok := tengo.ToInt(args[1]) + if !ok { + return nil, tengo.ErrInvalidArgumentType{ + Name: "second", + Expected: "int(compatible)", + Found: args[1].TypeName(), + } + } + i3, ok := tengo.ToInt(args[2]) + if !ok { + return nil, tengo.ErrInvalidArgumentType{ + Name: "third", + Expected: "int(compatible)", + Found: args[2].TypeName(), + } + } + return wrapError(fn(s1, i2, i3)), nil + } +} + +// FuncAYRIE transform a function of 'func([]byte) (int, error)' signature +// into CallableFunc type. +func FuncAYRIE(fn func([]byte) (int, error)) tengo.CallableFunc { + return func(args ...tengo.Object) (ret tengo.Object, err error) { + if len(args) != 1 { + return nil, tengo.ErrWrongNumArguments + } + y1, ok := tengo.ToByteSlice(args[0]) + if !ok { + return nil, tengo.ErrInvalidArgumentType{ + Name: "first", + Expected: "bytes(compatible)", + Found: args[0].TypeName(), + } + } + res, err := fn(y1) + if err != nil { + return wrapError(err), nil + } + return &tengo.Int{Value: int64(res)}, nil + } +} + +// FuncAYRS transform a function of 'func([]byte) string' signature into +// CallableFunc type. +func FuncAYRS(fn func([]byte) string) tengo.CallableFunc { + return func(args ...tengo.Object) (ret tengo.Object, err error) { + if len(args) != 1 { + return nil, tengo.ErrWrongNumArguments + } + y1, ok := tengo.ToByteSlice(args[0]) + if !ok { + return nil, tengo.ErrInvalidArgumentType{ + Name: "first", + Expected: "bytes(compatible)", + Found: args[0].TypeName(), + } + } + res := fn(y1) + return &tengo.String{Value: res}, nil + } +} + +// FuncASRIE transform a function of 'func(string) (int, error)' signature +// into CallableFunc type. +func FuncASRIE(fn func(string) (int, error)) tengo.CallableFunc { + return func(args ...tengo.Object) (ret tengo.Object, err error) { + if len(args) != 1 { + return nil, tengo.ErrWrongNumArguments + } + s1, ok := tengo.ToString(args[0]) + if !ok { + return nil, tengo.ErrInvalidArgumentType{ + Name: "first", + Expected: "string(compatible)", + Found: args[0].TypeName(), + } + } + res, err := fn(s1) + if err != nil { + return wrapError(err), nil + } + return &tengo.Int{Value: int64(res)}, nil + } +} + +// FuncASRYE transform a function of 'func(string) ([]byte, error)' signature +// into CallableFunc type. +func FuncASRYE(fn func(string) ([]byte, error)) tengo.CallableFunc { + return func(args ...tengo.Object) (ret tengo.Object, err error) { + if len(args) != 1 { + return nil, tengo.ErrWrongNumArguments + } + s1, ok := tengo.ToString(args[0]) + if !ok { + return nil, tengo.ErrInvalidArgumentType{ + Name: "first", + Expected: "string(compatible)", + Found: args[0].TypeName(), + } + } + res, err := fn(s1) + if err != nil { + return wrapError(err), nil + } + if len(res) > tengo.MaxBytesLen { + return nil, tengo.ErrBytesLimit + } + return &tengo.Bytes{Value: res}, nil + } +} + +// FuncAIRSsE transform a function of 'func(int) ([]string, error)' signature +// into CallableFunc type. +func FuncAIRSsE(fn func(int) ([]string, error)) tengo.CallableFunc { + return func(args ...tengo.Object) (ret tengo.Object, err error) { + if len(args) != 1 { + return nil, tengo.ErrWrongNumArguments + } + i1, ok := tengo.ToInt(args[0]) + if !ok { + return nil, tengo.ErrInvalidArgumentType{ + Name: "first", + Expected: "int(compatible)", + Found: args[0].TypeName(), + } + } + res, err := fn(i1) + if err != nil { + return wrapError(err), nil + } + arr := &tengo.Array{} + for _, r := range res { + if len(r) > tengo.MaxStringLen { + return nil, tengo.ErrStringLimit + } + arr.Value = append(arr.Value, &tengo.String{Value: r}) + } + return arr, nil + } +} + +// FuncAIRS transform a function of 'func(int) string' signature into +// CallableFunc type. +func FuncAIRS(fn func(int) string) tengo.CallableFunc { + return func(args ...tengo.Object) (ret tengo.Object, err error) { + if len(args) != 1 { + return nil, tengo.ErrWrongNumArguments + } + i1, ok := tengo.ToInt(args[0]) + if !ok { + return nil, tengo.ErrInvalidArgumentType{ + Name: "first", + Expected: "int(compatible)", + Found: args[0].TypeName(), + } + } + s := fn(i1) + if len(s) > tengo.MaxStringLen { + return nil, tengo.ErrStringLimit + } + return &tengo.String{Value: s}, nil + } +} diff --git a/vendor/github.com/d5/tengo/v2/stdlib/hex.go b/vendor/github.com/d5/tengo/v2/stdlib/hex.go new file mode 100644 index 00000000..981da696 --- /dev/null +++ b/vendor/github.com/d5/tengo/v2/stdlib/hex.go @@ -0,0 +1,12 @@ +package stdlib + +import ( + "encoding/hex" + + "github.com/d5/tengo/v2" +) + +var hexModule = map[string]tengo.Object{ + "encode": &tengo.UserFunction{Value: FuncAYRS(hex.EncodeToString)}, + "decode": &tengo.UserFunction{Value: FuncASRYE(hex.DecodeString)}, +} diff --git a/vendor/github.com/d5/tengo/v2/stdlib/json.go b/vendor/github.com/d5/tengo/v2/stdlib/json.go new file mode 100644 index 00000000..be2185db --- /dev/null +++ b/vendor/github.com/d5/tengo/v2/stdlib/json.go @@ -0,0 +1,146 @@ +package stdlib + +import ( + "bytes" + gojson "encoding/json" + + "github.com/d5/tengo/v2" + "github.com/d5/tengo/v2/stdlib/json" +) + +var jsonModule = map[string]tengo.Object{ + "decode": &tengo.UserFunction{ + Name: "decode", + Value: jsonDecode, + }, + "encode": &tengo.UserFunction{ + Name: "encode", + Value: jsonEncode, + }, + "indent": &tengo.UserFunction{ + Name: "encode", + Value: jsonIndent, + }, + "html_escape": &tengo.UserFunction{ + Name: "html_escape", + Value: jsonHTMLEscape, + }, +} + +func jsonDecode(args ...tengo.Object) (ret tengo.Object, err error) { + if len(args) != 1 { + return nil, tengo.ErrWrongNumArguments + } + + switch o := args[0].(type) { + case *tengo.Bytes: + v, err := json.Decode(o.Value) + if err != nil { + return &tengo.Error{ + Value: &tengo.String{Value: err.Error()}, + }, nil + } + return v, nil + case *tengo.String: + v, err := json.Decode([]byte(o.Value)) + if err != nil { + return &tengo.Error{ + Value: &tengo.String{Value: err.Error()}, + }, nil + } + return v, nil + default: + return nil, tengo.ErrInvalidArgumentType{ + Name: "first", + Expected: "bytes/string", + Found: args[0].TypeName(), + } + } +} + +func jsonEncode(args ...tengo.Object) (ret tengo.Object, err error) { + if len(args) != 1 { + return nil, tengo.ErrWrongNumArguments + } + + b, err := json.Encode(args[0]) + if err != nil { + return &tengo.Error{Value: &tengo.String{Value: err.Error()}}, nil + } + + return &tengo.Bytes{Value: b}, nil +} + +func jsonIndent(args ...tengo.Object) (ret tengo.Object, err error) { + if len(args) != 3 { + return nil, tengo.ErrWrongNumArguments + } + + prefix, ok := tengo.ToString(args[1]) + if !ok { + return nil, tengo.ErrInvalidArgumentType{ + Name: "prefix", + Expected: "string(compatible)", + Found: args[1].TypeName(), + } + } + + indent, ok := tengo.ToString(args[2]) + if !ok { + return nil, tengo.ErrInvalidArgumentType{ + Name: "indent", + Expected: "string(compatible)", + Found: args[2].TypeName(), + } + } + + switch o := args[0].(type) { + case *tengo.Bytes: + var dst bytes.Buffer + err := gojson.Indent(&dst, o.Value, prefix, indent) + if err != nil { + return &tengo.Error{ + Value: &tengo.String{Value: err.Error()}, + }, nil + } + return &tengo.Bytes{Value: dst.Bytes()}, nil + case *tengo.String: + var dst bytes.Buffer + err := gojson.Indent(&dst, []byte(o.Value), prefix, indent) + if err != nil { + return &tengo.Error{ + Value: &tengo.String{Value: err.Error()}, + }, nil + } + return &tengo.Bytes{Value: dst.Bytes()}, nil + default: + return nil, tengo.ErrInvalidArgumentType{ + Name: "first", + Expected: "bytes/string", + Found: args[0].TypeName(), + } + } +} + +func jsonHTMLEscape(args ...tengo.Object) (ret tengo.Object, err error) { + if len(args) != 1 { + return nil, tengo.ErrWrongNumArguments + } + + switch o := args[0].(type) { + case *tengo.Bytes: + var dst bytes.Buffer + gojson.HTMLEscape(&dst, o.Value) + return &tengo.Bytes{Value: dst.Bytes()}, nil + case *tengo.String: + var dst bytes.Buffer + gojson.HTMLEscape(&dst, []byte(o.Value)) + return &tengo.Bytes{Value: dst.Bytes()}, nil + default: + return nil, tengo.ErrInvalidArgumentType{ + Name: "first", + Expected: "bytes/string", + Found: args[0].TypeName(), + } + } +} diff --git a/vendor/github.com/d5/tengo/stdlib/json/decode.go b/vendor/github.com/d5/tengo/v2/stdlib/json/decode.go similarity index 88% rename from vendor/github.com/d5/tengo/stdlib/json/decode.go rename to vendor/github.com/d5/tengo/v2/stdlib/json/decode.go index 5a3fe6c7..6d468ef0 100644 --- a/vendor/github.com/d5/tengo/stdlib/json/decode.go +++ b/vendor/github.com/d5/tengo/v2/stdlib/json/decode.go @@ -12,21 +12,19 @@ import ( "unicode/utf16" "unicode/utf8" - "github.com/d5/tengo/objects" + "github.com/d5/tengo/v2" ) // Decode parses the JSON-encoded data and returns the result object. -func Decode(data []byte) (objects.Object, error) { +func Decode(data []byte) (tengo.Object, error) { var d decodeState err := checkValid(data, &d.scan) if err != nil { return nil, err } - d.init(data) d.scan.reset() d.scanWhile(scanSkipSpace) - return d.value() } @@ -80,45 +78,37 @@ func (d *decodeState) scanWhile(op int) { d.opcode = d.scan.eof() } -func (d *decodeState) value() (objects.Object, error) { +func (d *decodeState) value() (tengo.Object, error) { switch d.opcode { default: panic(phasePanicMsg) - case scanBeginArray: o, err := d.array() if err != nil { return nil, err } - d.scanNext() - return o, nil - case scanBeginObject: o, err := d.object() if err != nil { return nil, err } - d.scanNext() - return o, nil - case scanBeginLiteral: return d.literal() } } -func (d *decodeState) array() (objects.Object, error) { - var arr []objects.Object +func (d *decodeState) array() (tengo.Object, error) { + var arr []tengo.Object for { // Look ahead for ] - can only happen on first iteration. d.scanWhile(scanSkipSpace) if d.opcode == scanEndArray { break } - o, err := d.value() if err != nil { return nil, err @@ -136,12 +126,11 @@ func (d *decodeState) array() (objects.Object, error) { panic(phasePanicMsg) } } - - return &objects.Array{Value: arr}, nil + return &tengo.Array{Value: arr}, nil } -func (d *decodeState) object() (objects.Object, error) { - m := make(map[string]objects.Object) +func (d *decodeState) object() (tengo.Object, error) { + m := make(map[string]tengo.Object) for { // Read opening " of string key or closing }. d.scanWhile(scanSkipSpace) @@ -190,11 +179,10 @@ func (d *decodeState) object() (objects.Object, error) { panic(phasePanicMsg) } } - - return &objects.Map{Value: m}, nil + return &tengo.Map{Value: m}, nil } -func (d *decodeState) literal() (objects.Object, error) { +func (d *decodeState) literal() (tengo.Object, error) { // All bytes inside literal return scanContinue op code. start := d.readIndex() d.scanWhile(scanContinue) @@ -203,28 +191,27 @@ func (d *decodeState) literal() (objects.Object, error) { switch c := item[0]; c { case 'n': // null - return objects.UndefinedValue, nil + return tengo.UndefinedValue, nil case 't', 'f': // true, false if c == 't' { - return objects.TrueValue, nil + return tengo.TrueValue, nil } - return objects.FalseValue, nil + return tengo.FalseValue, nil case '"': // string s, ok := unquote(item) if !ok { panic(phasePanicMsg) } - return &objects.String{Value: s}, nil + return &tengo.String{Value: s}, nil default: // number if c != '-' && (c < '0' || c > '9') { panic(phasePanicMsg) } - n, _ := strconv.ParseFloat(string(item), 10) - return &objects.Float{Value: n}, nil + return &tengo.Float{Value: n}, nil } } @@ -265,9 +252,8 @@ func unquoteBytes(s []byte) (t []byte, ok bool) { } s = s[1 : len(s)-1] - // Check for unusual characters. If there are none, - // then no unquoting is needed, so return a slice of the - // original bytes. + // Check for unusual characters. If there are none, then no unquoting is + // needed, so return a slice of the original bytes. r := 0 for r < len(s) { c := s[r] @@ -341,7 +327,8 @@ func unquoteBytes(s []byte) (t []byte, ok bool) { r += 6 if utf16.IsSurrogate(rr) { rr1 := getu4(s[r:]) - if dec := utf16.DecodeRune(rr, rr1); dec != unicode.ReplacementChar { + dec := utf16.DecodeRune(rr, rr1) + if dec != unicode.ReplacementChar { // A valid pair; consume. r += 6 w += utf8.EncodeRune(b[w:], dec) @@ -352,17 +339,14 @@ func unquoteBytes(s []byte) (t []byte, ok bool) { } w += utf8.EncodeRune(b[w:], rr) } - // Quote, control characters are invalid. case c == '"', c < ' ': return - // ASCII case c < utf8.RuneSelf: b[w] = c r++ w++ - // Coerce to well-formed UTF-8. default: rr, size := utf8.DecodeRune(s[r:]) diff --git a/vendor/github.com/d5/tengo/stdlib/json/encode.go b/vendor/github.com/d5/tengo/v2/stdlib/json/encode.go similarity index 88% rename from vendor/github.com/d5/tengo/stdlib/json/encode.go rename to vendor/github.com/d5/tengo/v2/stdlib/json/encode.go index 2b8b17eb..ab7ca6ff 100644 --- a/vendor/github.com/d5/tengo/stdlib/json/encode.go +++ b/vendor/github.com/d5/tengo/v2/stdlib/json/encode.go @@ -12,15 +12,15 @@ import ( "math" "strconv" - "github.com/d5/tengo/objects" + "github.com/d5/tengo/v2" ) // Encode returns the JSON encoding of the object. -func Encode(o objects.Object) ([]byte, error) { +func Encode(o tengo.Object) ([]byte, error) { var b []byte switch o := o.(type) { - case *objects.Array: + case *tengo.Array: b = append(b, '[') len1 := len(o.Value) - 1 for idx, elem := range o.Value { @@ -34,7 +34,7 @@ func Encode(o objects.Object) ([]byte, error) { } } b = append(b, ']') - case *objects.ImmutableArray: + case *tengo.ImmutableArray: b = append(b, '[') len1 := len(o.Value) - 1 for idx, elem := range o.Value { @@ -48,7 +48,7 @@ func Encode(o objects.Object) ([]byte, error) { } } b = append(b, ']') - case *objects.Map: + case *tengo.Map: b = append(b, '{') len1 := len(o.Value) - 1 idx := 0 @@ -66,7 +66,7 @@ func Encode(o objects.Object) ([]byte, error) { idx++ } b = append(b, '}') - case *objects.ImmutableMap: + case *tengo.ImmutableMap: b = append(b, '{') len1 := len(o.Value) - 1 idx := 0 @@ -84,22 +84,22 @@ func Encode(o objects.Object) ([]byte, error) { idx++ } b = append(b, '}') - case *objects.Bool: + case *tengo.Bool: if o.IsFalsy() { b = strconv.AppendBool(b, false) } else { b = strconv.AppendBool(b, true) } - case *objects.Bytes: + case *tengo.Bytes: b = append(b, '"') encodedLen := base64.StdEncoding.EncodedLen(len(o.Value)) dst := make([]byte, encodedLen) base64.StdEncoding.Encode(dst, o.Value) b = append(b, dst...) b = append(b, '"') - case *objects.Char: + case *tengo.Char: b = strconv.AppendInt(b, int64(o.Value), 10) - case *objects.Float: + case *tengo.Float: var y []byte f := o.Value @@ -127,21 +127,20 @@ func Encode(o objects.Object) ([]byte, error) { } b = append(b, y...) - case *objects.Int: + case *tengo.Int: b = strconv.AppendInt(b, o.Value, 10) - case *objects.String: + case *tengo.String: b = strconv.AppendQuote(b, o.Value) - case *objects.Time: + case *tengo.Time: y, err := o.Value.MarshalJSON() if err != nil { return nil, err } b = append(b, y...) - case *objects.Undefined: + case *tengo.Undefined: b = append(b, "null"...) default: // unknown type: ignore } - return b, nil } diff --git a/vendor/github.com/d5/tengo/stdlib/json/scanner.go b/vendor/github.com/d5/tengo/v2/stdlib/json/scanner.go similarity index 98% rename from vendor/github.com/d5/tengo/stdlib/json/scanner.go rename to vendor/github.com/d5/tengo/v2/stdlib/json/scanner.go index 8fc6776d..aed15cf5 100644 --- a/vendor/github.com/d5/tengo/stdlib/json/scanner.go +++ b/vendor/github.com/d5/tengo/v2/stdlib/json/scanner.go @@ -532,14 +532,17 @@ func stateNul(s *scanner, c byte) int { // stateError is the state after reaching a syntax error, // such as after reading `[1}` or `5.1.2`. -func stateError(s *scanner, c byte) int { +func stateError(_ *scanner, _ byte) int { return scanError } // error records an error and switches to the error state. func (s *scanner) error(c byte, context string) int { s.step = stateError - s.err = &SyntaxError{"invalid character " + quoteChar(c) + " " + context, s.bytes} + s.err = &SyntaxError{ + msg: "invalid character " + quoteChar(c) + " " + context, + Offset: s.bytes, + } return scanError } diff --git a/vendor/github.com/d5/tengo/v2/stdlib/math.go b/vendor/github.com/d5/tengo/v2/stdlib/math.go new file mode 100644 index 00000000..633ea09f --- /dev/null +++ b/vendor/github.com/d5/tengo/v2/stdlib/math.go @@ -0,0 +1,233 @@ +package stdlib + +import ( + "math" + + "github.com/d5/tengo/v2" +) + +var mathModule = map[string]tengo.Object{ + "e": &tengo.Float{Value: math.E}, + "pi": &tengo.Float{Value: math.Pi}, + "phi": &tengo.Float{Value: math.Phi}, + "sqrt2": &tengo.Float{Value: math.Sqrt2}, + "sqrtE": &tengo.Float{Value: math.SqrtE}, + "sqrtPi": &tengo.Float{Value: math.SqrtPi}, + "sqrtPhi": &tengo.Float{Value: math.SqrtPhi}, + "ln2": &tengo.Float{Value: math.Ln2}, + "log2E": &tengo.Float{Value: math.Log2E}, + "ln10": &tengo.Float{Value: math.Ln10}, + "log10E": &tengo.Float{Value: math.Log10E}, + "abs": &tengo.UserFunction{ + Name: "abs", + Value: FuncAFRF(math.Abs), + }, + "acos": &tengo.UserFunction{ + Name: "acos", + Value: FuncAFRF(math.Acos), + }, + "acosh": &tengo.UserFunction{ + Name: "acosh", + Value: FuncAFRF(math.Acosh), + }, + "asin": &tengo.UserFunction{ + Name: "asin", + Value: FuncAFRF(math.Asin), + }, + "asinh": &tengo.UserFunction{ + Name: "asinh", + Value: FuncAFRF(math.Asinh), + }, + "atan": &tengo.UserFunction{ + Name: "atan", + Value: FuncAFRF(math.Atan), + }, + "atan2": &tengo.UserFunction{ + Name: "atan2", + Value: FuncAFFRF(math.Atan2), + }, + "atanh": &tengo.UserFunction{ + Name: "atanh", + Value: FuncAFRF(math.Atanh), + }, + "cbrt": &tengo.UserFunction{ + Name: "cbrt", + Value: FuncAFRF(math.Cbrt), + }, + "ceil": &tengo.UserFunction{ + Name: "ceil", + Value: FuncAFRF(math.Ceil), + }, + "copysign": &tengo.UserFunction{ + Name: "copysign", + Value: FuncAFFRF(math.Copysign), + }, + "cos": &tengo.UserFunction{ + Name: "cos", + Value: FuncAFRF(math.Cos), + }, + "cosh": &tengo.UserFunction{ + Name: "cosh", + Value: FuncAFRF(math.Cosh), + }, + "dim": &tengo.UserFunction{ + Name: "dim", + Value: FuncAFFRF(math.Dim), + }, + "erf": &tengo.UserFunction{ + Name: "erf", + Value: FuncAFRF(math.Erf), + }, + "erfc": &tengo.UserFunction{ + Name: "erfc", + Value: FuncAFRF(math.Erfc), + }, + "exp": &tengo.UserFunction{ + Name: "exp", + Value: FuncAFRF(math.Exp), + }, + "exp2": &tengo.UserFunction{ + Name: "exp2", + Value: FuncAFRF(math.Exp2), + }, + "expm1": &tengo.UserFunction{ + Name: "expm1", + Value: FuncAFRF(math.Expm1), + }, + "floor": &tengo.UserFunction{ + Name: "floor", + Value: FuncAFRF(math.Floor), + }, + "gamma": &tengo.UserFunction{ + Name: "gamma", + Value: FuncAFRF(math.Gamma), + }, + "hypot": &tengo.UserFunction{ + Name: "hypot", + Value: FuncAFFRF(math.Hypot), + }, + "ilogb": &tengo.UserFunction{ + Name: "ilogb", + Value: FuncAFRI(math.Ilogb), + }, + "inf": &tengo.UserFunction{ + Name: "inf", + Value: FuncAIRF(math.Inf), + }, + "is_inf": &tengo.UserFunction{ + Name: "is_inf", + Value: FuncAFIRB(math.IsInf), + }, + "is_nan": &tengo.UserFunction{ + Name: "is_nan", + Value: FuncAFRB(math.IsNaN), + }, + "j0": &tengo.UserFunction{ + Name: "j0", + Value: FuncAFRF(math.J0), + }, + "j1": &tengo.UserFunction{ + Name: "j1", + Value: FuncAFRF(math.J1), + }, + "jn": &tengo.UserFunction{ + Name: "jn", + Value: FuncAIFRF(math.Jn), + }, + "ldexp": &tengo.UserFunction{ + Name: "ldexp", + Value: FuncAFIRF(math.Ldexp), + }, + "log": &tengo.UserFunction{ + Name: "log", + Value: FuncAFRF(math.Log), + }, + "log10": &tengo.UserFunction{ + Name: "log10", + Value: FuncAFRF(math.Log10), + }, + "log1p": &tengo.UserFunction{ + Name: "log1p", + Value: FuncAFRF(math.Log1p), + }, + "log2": &tengo.UserFunction{ + Name: "log2", + Value: FuncAFRF(math.Log2), + }, + "logb": &tengo.UserFunction{ + Name: "logb", + Value: FuncAFRF(math.Logb), + }, + "max": &tengo.UserFunction{ + Name: "max", + Value: FuncAFFRF(math.Max), + }, + "min": &tengo.UserFunction{ + Name: "min", + Value: FuncAFFRF(math.Min), + }, + "mod": &tengo.UserFunction{ + Name: "mod", + Value: FuncAFFRF(math.Mod), + }, + "nan": &tengo.UserFunction{ + Name: "nan", + Value: FuncARF(math.NaN), + }, + "nextafter": &tengo.UserFunction{ + Name: "nextafter", + Value: FuncAFFRF(math.Nextafter), + }, + "pow": &tengo.UserFunction{ + Name: "pow", + Value: FuncAFFRF(math.Pow), + }, + "pow10": &tengo.UserFunction{ + Name: "pow10", + Value: FuncAIRF(math.Pow10), + }, + "remainder": &tengo.UserFunction{ + Name: "remainder", + Value: FuncAFFRF(math.Remainder), + }, + "signbit": &tengo.UserFunction{ + Name: "signbit", + Value: FuncAFRB(math.Signbit), + }, + "sin": &tengo.UserFunction{ + Name: "sin", + Value: FuncAFRF(math.Sin), + }, + "sinh": &tengo.UserFunction{ + Name: "sinh", + Value: FuncAFRF(math.Sinh), + }, + "sqrt": &tengo.UserFunction{ + Name: "sqrt", + Value: FuncAFRF(math.Sqrt), + }, + "tan": &tengo.UserFunction{ + Name: "tan", + Value: FuncAFRF(math.Tan), + }, + "tanh": &tengo.UserFunction{ + Name: "tanh", + Value: FuncAFRF(math.Tanh), + }, + "trunc": &tengo.UserFunction{ + Name: "trunc", + Value: FuncAFRF(math.Trunc), + }, + "y0": &tengo.UserFunction{ + Name: "y0", + Value: FuncAFRF(math.Y0), + }, + "y1": &tengo.UserFunction{ + Name: "y1", + Value: FuncAFRF(math.Y1), + }, + "yn": &tengo.UserFunction{ + Name: "yn", + Value: FuncAIFRF(math.Yn), + }, +} diff --git a/vendor/github.com/d5/tengo/v2/stdlib/os.go b/vendor/github.com/d5/tengo/v2/stdlib/os.go new file mode 100644 index 00000000..576bc94b --- /dev/null +++ b/vendor/github.com/d5/tengo/v2/stdlib/os.go @@ -0,0 +1,564 @@ +package stdlib + +import ( + "fmt" + "io" + "io/ioutil" + "os" + "os/exec" + + "github.com/d5/tengo/v2" +) + +var osModule = map[string]tengo.Object{ + "o_rdonly": &tengo.Int{Value: int64(os.O_RDONLY)}, + "o_wronly": &tengo.Int{Value: int64(os.O_WRONLY)}, + "o_rdwr": &tengo.Int{Value: int64(os.O_RDWR)}, + "o_append": &tengo.Int{Value: int64(os.O_APPEND)}, + "o_create": &tengo.Int{Value: int64(os.O_CREATE)}, + "o_excl": &tengo.Int{Value: int64(os.O_EXCL)}, + "o_sync": &tengo.Int{Value: int64(os.O_SYNC)}, + "o_trunc": &tengo.Int{Value: int64(os.O_TRUNC)}, + "mode_dir": &tengo.Int{Value: int64(os.ModeDir)}, + "mode_append": &tengo.Int{Value: int64(os.ModeAppend)}, + "mode_exclusive": &tengo.Int{Value: int64(os.ModeExclusive)}, + "mode_temporary": &tengo.Int{Value: int64(os.ModeTemporary)}, + "mode_symlink": &tengo.Int{Value: int64(os.ModeSymlink)}, + "mode_device": &tengo.Int{Value: int64(os.ModeDevice)}, + "mode_named_pipe": &tengo.Int{Value: int64(os.ModeNamedPipe)}, + "mode_socket": &tengo.Int{Value: int64(os.ModeSocket)}, + "mode_setuid": &tengo.Int{Value: int64(os.ModeSetuid)}, + "mode_setgui": &tengo.Int{Value: int64(os.ModeSetgid)}, + "mode_char_device": &tengo.Int{Value: int64(os.ModeCharDevice)}, + "mode_sticky": &tengo.Int{Value: int64(os.ModeSticky)}, + "mode_type": &tengo.Int{Value: int64(os.ModeType)}, + "mode_perm": &tengo.Int{Value: int64(os.ModePerm)}, + "path_separator": &tengo.Char{Value: os.PathSeparator}, + "path_list_separator": &tengo.Char{Value: os.PathListSeparator}, + "dev_null": &tengo.String{Value: os.DevNull}, + "seek_set": &tengo.Int{Value: int64(io.SeekStart)}, + "seek_cur": &tengo.Int{Value: int64(io.SeekCurrent)}, + "seek_end": &tengo.Int{Value: int64(io.SeekEnd)}, + "args": &tengo.UserFunction{ + Name: "args", + Value: osArgs, + }, // args() => array(string) + "chdir": &tengo.UserFunction{ + Name: "chdir", + Value: FuncASRE(os.Chdir), + }, // chdir(dir string) => error + "chmod": osFuncASFmRE("chmod", os.Chmod), // chmod(name string, mode int) => error + "chown": &tengo.UserFunction{ + Name: "chown", + Value: FuncASIIRE(os.Chown), + }, // chown(name string, uid int, gid int) => error + "clearenv": &tengo.UserFunction{ + Name: "clearenv", + Value: FuncAR(os.Clearenv), + }, // clearenv() + "environ": &tengo.UserFunction{ + Name: "environ", + Value: FuncARSs(os.Environ), + }, // environ() => array(string) + "exit": &tengo.UserFunction{ + Name: "exit", + Value: FuncAIR(os.Exit), + }, // exit(code int) + "expand_env": &tengo.UserFunction{ + Name: "expand_env", + Value: osExpandEnv, + }, // expand_env(s string) => string + "getegid": &tengo.UserFunction{ + Name: "getegid", + Value: FuncARI(os.Getegid), + }, // getegid() => int + "getenv": &tengo.UserFunction{ + Name: "getenv", + Value: FuncASRS(os.Getenv), + }, // getenv(s string) => string + "geteuid": &tengo.UserFunction{ + Name: "geteuid", + Value: FuncARI(os.Geteuid), + }, // geteuid() => int + "getgid": &tengo.UserFunction{ + Name: "getgid", + Value: FuncARI(os.Getgid), + }, // getgid() => int + "getgroups": &tengo.UserFunction{ + Name: "getgroups", + Value: FuncARIsE(os.Getgroups), + }, // getgroups() => array(string)/error + "getpagesize": &tengo.UserFunction{ + Name: "getpagesize", + Value: FuncARI(os.Getpagesize), + }, // getpagesize() => int + "getpid": &tengo.UserFunction{ + Name: "getpid", + Value: FuncARI(os.Getpid), + }, // getpid() => int + "getppid": &tengo.UserFunction{ + Name: "getppid", + Value: FuncARI(os.Getppid), + }, // getppid() => int + "getuid": &tengo.UserFunction{ + Name: "getuid", + Value: FuncARI(os.Getuid), + }, // getuid() => int + "getwd": &tengo.UserFunction{ + Name: "getwd", + Value: FuncARSE(os.Getwd), + }, // getwd() => string/error + "hostname": &tengo.UserFunction{ + Name: "hostname", + Value: FuncARSE(os.Hostname), + }, // hostname() => string/error + "lchown": &tengo.UserFunction{ + Name: "lchown", + Value: FuncASIIRE(os.Lchown), + }, // lchown(name string, uid int, gid int) => error + "link": &tengo.UserFunction{ + Name: "link", + Value: FuncASSRE(os.Link), + }, // link(oldname string, newname string) => error + "lookup_env": &tengo.UserFunction{ + Name: "lookup_env", + Value: osLookupEnv, + }, // lookup_env(key string) => string/false + "mkdir": osFuncASFmRE("mkdir", os.Mkdir), // mkdir(name string, perm int) => error + "mkdir_all": osFuncASFmRE("mkdir_all", os.MkdirAll), // mkdir_all(name string, perm int) => error + "readlink": &tengo.UserFunction{ + Name: "readlink", + Value: FuncASRSE(os.Readlink), + }, // readlink(name string) => string/error + "remove": &tengo.UserFunction{ + Name: "remove", + Value: FuncASRE(os.Remove), + }, // remove(name string) => error + "remove_all": &tengo.UserFunction{ + Name: "remove_all", + Value: FuncASRE(os.RemoveAll), + }, // remove_all(name string) => error + "rename": &tengo.UserFunction{ + Name: "rename", + Value: FuncASSRE(os.Rename), + }, // rename(oldpath string, newpath string) => error + "setenv": &tengo.UserFunction{ + Name: "setenv", + Value: FuncASSRE(os.Setenv), + }, // setenv(key string, value string) => error + "symlink": &tengo.UserFunction{ + Name: "symlink", + Value: FuncASSRE(os.Symlink), + }, // symlink(oldname string newname string) => error + "temp_dir": &tengo.UserFunction{ + Name: "temp_dir", + Value: FuncARS(os.TempDir), + }, // temp_dir() => string + "truncate": &tengo.UserFunction{ + Name: "truncate", + Value: FuncASI64RE(os.Truncate), + }, // truncate(name string, size int) => error + "unsetenv": &tengo.UserFunction{ + Name: "unsetenv", + Value: FuncASRE(os.Unsetenv), + }, // unsetenv(key string) => error + "create": &tengo.UserFunction{ + Name: "create", + Value: osCreate, + }, // create(name string) => imap(file)/error + "open": &tengo.UserFunction{ + Name: "open", + Value: osOpen, + }, // open(name string) => imap(file)/error + "open_file": &tengo.UserFunction{ + Name: "open_file", + Value: osOpenFile, + }, // open_file(name string, flag int, perm int) => imap(file)/error + "find_process": &tengo.UserFunction{ + Name: "find_process", + Value: osFindProcess, + }, // find_process(pid int) => imap(process)/error + "start_process": &tengo.UserFunction{ + Name: "start_process", + Value: osStartProcess, + }, // start_process(name string, argv array(string), dir string, env array(string)) => imap(process)/error + "exec_look_path": &tengo.UserFunction{ + Name: "exec_look_path", + Value: FuncASRSE(exec.LookPath), + }, // exec_look_path(file) => string/error + "exec": &tengo.UserFunction{ + Name: "exec", + Value: osExec, + }, // exec(name, args...) => command + "stat": &tengo.UserFunction{ + Name: "stat", + Value: osStat, + }, // stat(name) => imap(fileinfo)/error + "read_file": &tengo.UserFunction{ + Name: "read_file", + Value: osReadFile, + }, // readfile(name) => array(byte)/error +} + +func osReadFile(args ...tengo.Object) (ret tengo.Object, err error) { + if len(args) != 1 { + return nil, tengo.ErrWrongNumArguments + } + fname, ok := tengo.ToString(args[0]) + if !ok { + return nil, tengo.ErrInvalidArgumentType{ + Name: "first", + Expected: "string(compatible)", + Found: args[0].TypeName(), + } + } + bytes, err := ioutil.ReadFile(fname) + if err != nil { + return wrapError(err), nil + } + if len(bytes) > tengo.MaxBytesLen { + return nil, tengo.ErrBytesLimit + } + return &tengo.Bytes{Value: bytes}, nil +} + +func osStat(args ...tengo.Object) (ret tengo.Object, err error) { + if len(args) != 1 { + return nil, tengo.ErrWrongNumArguments + } + fname, ok := tengo.ToString(args[0]) + if !ok { + return nil, tengo.ErrInvalidArgumentType{ + Name: "first", + Expected: "string(compatible)", + Found: args[0].TypeName(), + } + } + stat, err := os.Stat(fname) + if err != nil { + return wrapError(err), nil + } + fstat := &tengo.ImmutableMap{ + Value: map[string]tengo.Object{ + "name": &tengo.String{Value: stat.Name()}, + "mtime": &tengo.Time{Value: stat.ModTime()}, + "size": &tengo.Int{Value: stat.Size()}, + "mode": &tengo.Int{Value: int64(stat.Mode())}, + }, + } + if stat.IsDir() { + fstat.Value["directory"] = tengo.TrueValue + } else { + fstat.Value["directory"] = tengo.FalseValue + } + return fstat, nil +} + +func osCreate(args ...tengo.Object) (tengo.Object, error) { + if len(args) != 1 { + return nil, tengo.ErrWrongNumArguments + } + s1, ok := tengo.ToString(args[0]) + if !ok { + return nil, tengo.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 ...tengo.Object) (tengo.Object, error) { + if len(args) != 1 { + return nil, tengo.ErrWrongNumArguments + } + s1, ok := tengo.ToString(args[0]) + if !ok { + return nil, tengo.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 ...tengo.Object) (tengo.Object, error) { + if len(args) != 3 { + return nil, tengo.ErrWrongNumArguments + } + s1, ok := tengo.ToString(args[0]) + if !ok { + return nil, tengo.ErrInvalidArgumentType{ + Name: "first", + Expected: "string(compatible)", + Found: args[0].TypeName(), + } + } + i2, ok := tengo.ToInt(args[1]) + if !ok { + return nil, tengo.ErrInvalidArgumentType{ + Name: "second", + Expected: "int(compatible)", + Found: args[1].TypeName(), + } + } + i3, ok := tengo.ToInt(args[2]) + if !ok { + return nil, tengo.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 ...tengo.Object) (tengo.Object, error) { + if len(args) != 0 { + return nil, tengo.ErrWrongNumArguments + } + arr := &tengo.Array{} + for _, osArg := range os.Args { + if len(osArg) > tengo.MaxStringLen { + return nil, tengo.ErrStringLimit + } + arr.Value = append(arr.Value, &tengo.String{Value: osArg}) + } + return arr, nil +} + +func osFuncASFmRE( + name string, + fn func(string, os.FileMode) error, +) *tengo.UserFunction { + return &tengo.UserFunction{ + Name: name, + Value: func(args ...tengo.Object) (tengo.Object, error) { + if len(args) != 2 { + return nil, tengo.ErrWrongNumArguments + } + s1, ok := tengo.ToString(args[0]) + if !ok { + return nil, tengo.ErrInvalidArgumentType{ + Name: "first", + Expected: "string(compatible)", + Found: args[0].TypeName(), + } + } + i2, ok := tengo.ToInt64(args[1]) + if !ok { + return nil, tengo.ErrInvalidArgumentType{ + Name: "second", + Expected: "int(compatible)", + Found: args[1].TypeName(), + } + } + return wrapError(fn(s1, os.FileMode(i2))), nil + }, + } +} + +func osLookupEnv(args ...tengo.Object) (tengo.Object, error) { + if len(args) != 1 { + return nil, tengo.ErrWrongNumArguments + } + s1, ok := tengo.ToString(args[0]) + if !ok { + return nil, tengo.ErrInvalidArgumentType{ + Name: "first", + Expected: "string(compatible)", + Found: args[0].TypeName(), + } + } + res, ok := os.LookupEnv(s1) + if !ok { + return tengo.FalseValue, nil + } + if len(res) > tengo.MaxStringLen { + return nil, tengo.ErrStringLimit + } + return &tengo.String{Value: res}, nil +} + +func osExpandEnv(args ...tengo.Object) (tengo.Object, error) { + if len(args) != 1 { + return nil, tengo.ErrWrongNumArguments + } + s1, ok := tengo.ToString(args[0]) + if !ok { + return nil, tengo.ErrInvalidArgumentType{ + Name: "first", + Expected: "string(compatible)", + Found: args[0].TypeName(), + } + } + var vlen int + var failed bool + s := os.Expand(s1, func(k string) string { + if failed { + return "" + } + v := os.Getenv(k) + + // this does not count the other texts that are not being replaced + // but the code checks the final length at the end + vlen += len(v) + if vlen > tengo.MaxStringLen { + failed = true + return "" + } + return v + }) + if failed || len(s) > tengo.MaxStringLen { + return nil, tengo.ErrStringLimit + } + return &tengo.String{Value: s}, nil +} + +func osExec(args ...tengo.Object) (tengo.Object, error) { + if len(args) == 0 { + return nil, tengo.ErrWrongNumArguments + } + name, ok := tengo.ToString(args[0]) + if !ok { + return nil, tengo.ErrInvalidArgumentType{ + Name: "first", + Expected: "string(compatible)", + Found: args[0].TypeName(), + } + } + var execArgs []string + for idx, arg := range args[1:] { + execArg, ok := tengo.ToString(arg) + if !ok { + return nil, tengo.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 ...tengo.Object) (tengo.Object, error) { + if len(args) != 1 { + return nil, tengo.ErrWrongNumArguments + } + i1, ok := tengo.ToInt(args[0]) + if !ok { + return nil, tengo.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 ...tengo.Object) (tengo.Object, error) { + if len(args) != 4 { + return nil, tengo.ErrWrongNumArguments + } + name, ok := tengo.ToString(args[0]) + if !ok { + return nil, tengo.ErrInvalidArgumentType{ + Name: "first", + Expected: "string(compatible)", + Found: args[0].TypeName(), + } + } + var argv []string + var err error + switch arg1 := args[1].(type) { + case *tengo.Array: + argv, err = stringArray(arg1.Value, "second") + if err != nil { + return nil, err + } + case *tengo.ImmutableArray: + argv, err = stringArray(arg1.Value, "second") + if err != nil { + return nil, err + } + default: + return nil, tengo.ErrInvalidArgumentType{ + Name: "second", + Expected: "array", + Found: arg1.TypeName(), + } + } + + dir, ok := tengo.ToString(args[2]) + if !ok { + return nil, tengo.ErrInvalidArgumentType{ + Name: "third", + Expected: "string(compatible)", + Found: args[2].TypeName(), + } + } + + var env []string + switch arg3 := args[3].(type) { + case *tengo.Array: + env, err = stringArray(arg3.Value, "fourth") + if err != nil { + return nil, err + } + case *tengo.ImmutableArray: + env, err = stringArray(arg3.Value, "fourth") + if err != nil { + return nil, err + } + default: + return nil, tengo.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 []tengo.Object, argName string) ([]string, error) { + var sarr []string + for idx, elem := range arr { + str, ok := elem.(*tengo.String) + if !ok { + return nil, tengo.ErrInvalidArgumentType{ + Name: fmt.Sprintf("%s[%d]", argName, idx), + Expected: "string", + Found: elem.TypeName(), + } + } + sarr = append(sarr, str.Value) + } + return sarr, nil +} diff --git a/vendor/github.com/d5/tengo/v2/stdlib/os_exec.go b/vendor/github.com/d5/tengo/v2/stdlib/os_exec.go new file mode 100644 index 00000000..7ee5c1cd --- /dev/null +++ b/vendor/github.com/d5/tengo/v2/stdlib/os_exec.go @@ -0,0 +1,119 @@ +package stdlib + +import ( + "os/exec" + + "github.com/d5/tengo/v2" +) + +func makeOSExecCommand(cmd *exec.Cmd) *tengo.ImmutableMap { + return &tengo.ImmutableMap{ + Value: map[string]tengo.Object{ + // combined_output() => bytes/error + "combined_output": &tengo.UserFunction{ + Name: "combined_output", + Value: FuncARYE(cmd.CombinedOutput), + }, + // output() => bytes/error + "output": &tengo.UserFunction{ + Name: "output", + Value: FuncARYE(cmd.Output), + }, // + // run() => error + "run": &tengo.UserFunction{ + Name: "run", + Value: FuncARE(cmd.Run), + }, // + // start() => error + "start": &tengo.UserFunction{ + Name: "start", + Value: FuncARE(cmd.Start), + }, // + // wait() => error + "wait": &tengo.UserFunction{ + Name: "wait", + Value: FuncARE(cmd.Wait), + }, // + // set_path(path string) + "set_path": &tengo.UserFunction{ + Name: "set_path", + Value: func(args ...tengo.Object) (tengo.Object, error) { + if len(args) != 1 { + return nil, tengo.ErrWrongNumArguments + } + s1, ok := tengo.ToString(args[0]) + if !ok { + return nil, tengo.ErrInvalidArgumentType{ + Name: "first", + Expected: "string(compatible)", + Found: args[0].TypeName(), + } + } + cmd.Path = s1 + return tengo.UndefinedValue, nil + }, + }, + // set_dir(dir string) + "set_dir": &tengo.UserFunction{ + Name: "set_dir", + Value: func(args ...tengo.Object) (tengo.Object, error) { + if len(args) != 1 { + return nil, tengo.ErrWrongNumArguments + } + s1, ok := tengo.ToString(args[0]) + if !ok { + return nil, tengo.ErrInvalidArgumentType{ + Name: "first", + Expected: "string(compatible)", + Found: args[0].TypeName(), + } + } + cmd.Dir = s1 + return tengo.UndefinedValue, nil + }, + }, + // set_env(env array(string)) + "set_env": &tengo.UserFunction{ + Name: "set_env", + Value: func(args ...tengo.Object) (tengo.Object, error) { + if len(args) != 1 { + return nil, tengo.ErrWrongNumArguments + } + + var env []string + var err error + switch arg0 := args[0].(type) { + case *tengo.Array: + env, err = stringArray(arg0.Value, "first") + if err != nil { + return nil, err + } + case *tengo.ImmutableArray: + env, err = stringArray(arg0.Value, "first") + if err != nil { + return nil, err + } + default: + return nil, tengo.ErrInvalidArgumentType{ + Name: "first", + Expected: "array", + Found: arg0.TypeName(), + } + } + cmd.Env = env + return tengo.UndefinedValue, nil + }, + }, + // process() => imap(process) + "process": &tengo.UserFunction{ + Name: "process", + Value: func(args ...tengo.Object) (tengo.Object, error) { + if len(args) != 0 { + return nil, tengo.ErrWrongNumArguments + } + return makeOSProcess(cmd.Process), nil + }, + }, + }, + } +} diff --git a/vendor/github.com/d5/tengo/v2/stdlib/os_file.go b/vendor/github.com/d5/tengo/v2/stdlib/os_file.go new file mode 100644 index 00000000..4f59b4c4 --- /dev/null +++ b/vendor/github.com/d5/tengo/v2/stdlib/os_file.go @@ -0,0 +1,117 @@ +package stdlib + +import ( + "os" + + "github.com/d5/tengo/v2" +) + +func makeOSFile(file *os.File) *tengo.ImmutableMap { + return &tengo.ImmutableMap{ + Value: map[string]tengo.Object{ + // chdir() => true/error + "chdir": &tengo.UserFunction{ + Name: "chdir", + Value: FuncARE(file.Chdir), + }, // + // chown(uid int, gid int) => true/error + "chown": &tengo.UserFunction{ + Name: "chown", + Value: FuncAIIRE(file.Chown), + }, // + // close() => error + "close": &tengo.UserFunction{ + Name: "close", + Value: FuncARE(file.Close), + }, // + // name() => string + "name": &tengo.UserFunction{ + Name: "name", + Value: FuncARS(file.Name), + }, // + // readdirnames(n int) => array(string)/error + "readdirnames": &tengo.UserFunction{ + Name: "readdirnames", + Value: FuncAIRSsE(file.Readdirnames), + }, // + // sync() => error + "sync": &tengo.UserFunction{ + Name: "sync", + Value: FuncARE(file.Sync), + }, // + // write(bytes) => int/error + "write": &tengo.UserFunction{ + Name: "write", + Value: FuncAYRIE(file.Write), + }, // + // write(string) => int/error + "write_string": &tengo.UserFunction{ + Name: "write_string", + Value: FuncASRIE(file.WriteString), + }, // + // read(bytes) => int/error + "read": &tengo.UserFunction{ + Name: "read", + Value: FuncAYRIE(file.Read), + }, // + // chmod(mode int) => error + "chmod": &tengo.UserFunction{ + Name: "chmod", + Value: func(args ...tengo.Object) (tengo.Object, error) { + if len(args) != 1 { + return nil, tengo.ErrWrongNumArguments + } + i1, ok := tengo.ToInt64(args[0]) + if !ok { + return nil, tengo.ErrInvalidArgumentType{ + Name: "first", + Expected: "int(compatible)", + Found: args[0].TypeName(), + } + } + return wrapError(file.Chmod(os.FileMode(i1))), nil + }, + }, + // seek(offset int, whence int) => int/error + "seek": &tengo.UserFunction{ + Name: "seek", + Value: func(args ...tengo.Object) (tengo.Object, error) { + if len(args) != 2 { + return nil, tengo.ErrWrongNumArguments + } + i1, ok := tengo.ToInt64(args[0]) + if !ok { + return nil, tengo.ErrInvalidArgumentType{ + Name: "first", + Expected: "int(compatible)", + Found: args[0].TypeName(), + } + } + i2, ok := tengo.ToInt(args[1]) + if !ok { + return nil, tengo.ErrInvalidArgumentType{ + Name: "second", + Expected: "int(compatible)", + Found: args[1].TypeName(), + } + } + res, err := file.Seek(i1, i2) + if err != nil { + return wrapError(err), nil + } + return &tengo.Int{Value: res}, nil + }, + }, + // stat() => imap(fileinfo)/error + "stat": &tengo.UserFunction{ + Name: "stat", + Value: func(args ...tengo.Object) (tengo.Object, error) { + if len(args) != 0 { + return nil, tengo.ErrWrongNumArguments + } + return osStat(&tengo.String{Value: file.Name()}) + }, + }, + }, + } +} diff --git a/vendor/github.com/d5/tengo/v2/stdlib/os_process.go b/vendor/github.com/d5/tengo/v2/stdlib/os_process.go new file mode 100644 index 00000000..7fcf27a4 --- /dev/null +++ b/vendor/github.com/d5/tengo/v2/stdlib/os_process.go @@ -0,0 +1,76 @@ +package stdlib + +import ( + "os" + "syscall" + + "github.com/d5/tengo/v2" +) + +func makeOSProcessState(state *os.ProcessState) *tengo.ImmutableMap { + return &tengo.ImmutableMap{ + Value: map[string]tengo.Object{ + "exited": &tengo.UserFunction{ + Name: "exited", + Value: FuncARB(state.Exited), + }, + "pid": &tengo.UserFunction{ + Name: "pid", + Value: FuncARI(state.Pid), + }, + "string": &tengo.UserFunction{ + Name: "string", + Value: FuncARS(state.String), + }, + "success": &tengo.UserFunction{ + Name: "success", + Value: FuncARB(state.Success), + }, + }, + } +} + +func makeOSProcess(proc *os.Process) *tengo.ImmutableMap { + return &tengo.ImmutableMap{ + Value: map[string]tengo.Object{ + "kill": &tengo.UserFunction{ + Name: "kill", + Value: FuncARE(proc.Kill), + }, + "release": &tengo.UserFunction{ + Name: "release", + Value: FuncARE(proc.Release), + }, + "signal": &tengo.UserFunction{ + Name: "signal", + Value: func(args ...tengo.Object) (tengo.Object, error) { + if len(args) != 1 { + return nil, tengo.ErrWrongNumArguments + } + i1, ok := tengo.ToInt64(args[0]) + if !ok { + return nil, tengo.ErrInvalidArgumentType{ + Name: "first", + Expected: "int(compatible)", + Found: args[0].TypeName(), + } + } + return wrapError(proc.Signal(syscall.Signal(i1))), nil + }, + }, + "wait": &tengo.UserFunction{ + Name: "wait", + Value: func(args ...tengo.Object) (tengo.Object, error) { + if len(args) != 0 { + return nil, tengo.ErrWrongNumArguments + } + state, err := proc.Wait() + if err != nil { + return wrapError(err), nil + } + return makeOSProcessState(state), nil + }, + }, + }, + } +} diff --git a/vendor/github.com/d5/tengo/v2/stdlib/rand.go b/vendor/github.com/d5/tengo/v2/stdlib/rand.go new file mode 100644 index 00000000..5d21e1df --- /dev/null +++ b/vendor/github.com/d5/tengo/v2/stdlib/rand.go @@ -0,0 +1,138 @@ +package stdlib + +import ( + "math/rand" + + "github.com/d5/tengo/v2" +) + +var randModule = map[string]tengo.Object{ + "int": &tengo.UserFunction{ + Name: "int", + Value: FuncARI64(rand.Int63), + }, + "float": &tengo.UserFunction{ + Name: "float", + Value: FuncARF(rand.Float64), + }, + "intn": &tengo.UserFunction{ + Name: "intn", + Value: FuncAI64RI64(rand.Int63n), + }, + "exp_float": &tengo.UserFunction{ + Name: "exp_float", + Value: FuncARF(rand.ExpFloat64), + }, + "norm_float": &tengo.UserFunction{ + Name: "norm_float", + Value: FuncARF(rand.NormFloat64), + }, + "perm": &tengo.UserFunction{ + Name: "perm", + Value: FuncAIRIs(rand.Perm), + }, + "seed": &tengo.UserFunction{ + Name: "seed", + Value: FuncAI64R(rand.Seed), + }, + "read": &tengo.UserFunction{ + Name: "read", + Value: func(args ...tengo.Object) (ret tengo.Object, err error) { + if len(args) != 1 { + return nil, tengo.ErrWrongNumArguments + } + y1, ok := args[0].(*tengo.Bytes) + if !ok { + return nil, tengo.ErrInvalidArgumentType{ + Name: "first", + Expected: "bytes", + Found: args[0].TypeName(), + } + } + res, err := rand.Read(y1.Value) + if err != nil { + ret = wrapError(err) + return + } + return &tengo.Int{Value: int64(res)}, nil + }, + }, + "rand": &tengo.UserFunction{ + Name: "rand", + Value: func(args ...tengo.Object) (tengo.Object, error) { + if len(args) != 1 { + return nil, tengo.ErrWrongNumArguments + } + i1, ok := tengo.ToInt64(args[0]) + if !ok { + return nil, tengo.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) *tengo.ImmutableMap { + return &tengo.ImmutableMap{ + Value: map[string]tengo.Object{ + "int": &tengo.UserFunction{ + Name: "int", + Value: FuncARI64(r.Int63), + }, + "float": &tengo.UserFunction{ + Name: "float", + Value: FuncARF(r.Float64), + }, + "intn": &tengo.UserFunction{ + Name: "intn", + Value: FuncAI64RI64(r.Int63n), + }, + "exp_float": &tengo.UserFunction{ + Name: "exp_float", + Value: FuncARF(r.ExpFloat64), + }, + "norm_float": &tengo.UserFunction{ + Name: "norm_float", + Value: FuncARF(r.NormFloat64), + }, + "perm": &tengo.UserFunction{ + Name: "perm", + Value: FuncAIRIs(r.Perm), + }, + "seed": &tengo.UserFunction{ + Name: "seed", + Value: FuncAI64R(r.Seed), + }, + "read": &tengo.UserFunction{ + Name: "read", + Value: func(args ...tengo.Object) ( + ret tengo.Object, + err error, + ) { + if len(args) != 1 { + return nil, tengo.ErrWrongNumArguments + } + y1, ok := args[0].(*tengo.Bytes) + if !ok { + return nil, tengo.ErrInvalidArgumentType{ + Name: "first", + Expected: "bytes", + Found: args[0].TypeName(), + } + } + res, err := r.Read(y1.Value) + if err != nil { + ret = wrapError(err) + return + } + return &tengo.Int{Value: int64(res)}, nil + }, + }, + }, + } +} diff --git a/vendor/github.com/d5/tengo/stdlib/source_modules.go b/vendor/github.com/d5/tengo/v2/stdlib/source_modules.go similarity index 100% rename from vendor/github.com/d5/tengo/stdlib/source_modules.go rename to vendor/github.com/d5/tengo/v2/stdlib/source_modules.go diff --git a/vendor/github.com/d5/tengo/stdlib/srcmod_enum.tengo b/vendor/github.com/d5/tengo/v2/stdlib/srcmod_enum.tengo similarity index 100% rename from vendor/github.com/d5/tengo/stdlib/srcmod_enum.tengo rename to vendor/github.com/d5/tengo/v2/stdlib/srcmod_enum.tengo diff --git a/vendor/github.com/d5/tengo/stdlib/stdlib.go b/vendor/github.com/d5/tengo/v2/stdlib/stdlib.go similarity index 83% rename from vendor/github.com/d5/tengo/stdlib/stdlib.go rename to vendor/github.com/d5/tengo/v2/stdlib/stdlib.go index aad220ee..16c369a0 100644 --- a/vendor/github.com/d5/tengo/stdlib/stdlib.go +++ b/vendor/github.com/d5/tengo/v2/stdlib/stdlib.go @@ -2,7 +2,9 @@ package stdlib //go:generate go run gensrcmods.go -import "github.com/d5/tengo/objects" +import ( + "github.com/d5/tengo/v2" +) // AllModuleNames returns a list of all default module names. func AllModuleNames() []string { @@ -18,9 +20,8 @@ func AllModuleNames() []string { // GetModuleMap returns the module map that includes all modules // for the given module names. -func GetModuleMap(names ...string) *objects.ModuleMap { - modules := objects.NewModuleMap() - +func GetModuleMap(names ...string) *tengo.ModuleMap { + modules := tengo.NewModuleMap() for _, name := range names { if mod := BuiltinModules[name]; mod != nil { modules.AddBuiltinModule(name, mod) @@ -29,6 +30,5 @@ func GetModuleMap(names ...string) *objects.ModuleMap { modules.AddSourceModule(name, []byte(mod)) } } - return modules } diff --git a/vendor/github.com/d5/tengo/v2/stdlib/text.go b/vendor/github.com/d5/tengo/v2/stdlib/text.go new file mode 100644 index 00000000..d7d5d1da --- /dev/null +++ b/vendor/github.com/d5/tengo/v2/stdlib/text.go @@ -0,0 +1,1072 @@ +package stdlib + +import ( + "fmt" + "regexp" + "strconv" + "strings" + "unicode/utf8" + + "github.com/d5/tengo/v2" +) + +var textModule = map[string]tengo.Object{ + "re_match": &tengo.UserFunction{ + Name: "re_match", + Value: textREMatch, + }, // re_match(pattern, text) => bool/error + "re_find": &tengo.UserFunction{ + Name: "re_find", + Value: textREFind, + }, // re_find(pattern, text, count) => [[{text:,begin:,end:}]]/undefined + "re_replace": &tengo.UserFunction{ + Name: "re_replace", + Value: textREReplace, + }, // re_replace(pattern, text, repl) => string/error + "re_split": &tengo.UserFunction{ + Name: "re_split", + Value: textRESplit, + }, // re_split(pattern, text, count) => [string]/error + "re_compile": &tengo.UserFunction{ + Name: "re_compile", + Value: textRECompile, + }, // re_compile(pattern) => Regexp/error + "compare": &tengo.UserFunction{ + Name: "compare", + Value: FuncASSRI(strings.Compare), + }, // compare(a, b) => int + "contains": &tengo.UserFunction{ + Name: "contains", + Value: FuncASSRB(strings.Contains), + }, // contains(s, substr) => bool + "contains_any": &tengo.UserFunction{ + Name: "contains_any", + Value: FuncASSRB(strings.ContainsAny), + }, // contains_any(s, chars) => bool + "count": &tengo.UserFunction{ + Name: "count", + Value: FuncASSRI(strings.Count), + }, // count(s, substr) => int + "equal_fold": &tengo.UserFunction{ + Name: "equal_fold", + Value: FuncASSRB(strings.EqualFold), + }, // "equal_fold(s, t) => bool + "fields": &tengo.UserFunction{ + Name: "fields", + Value: FuncASRSs(strings.Fields), + }, // fields(s) => [string] + "has_prefix": &tengo.UserFunction{ + Name: "has_prefix", + Value: FuncASSRB(strings.HasPrefix), + }, // has_prefix(s, prefix) => bool + "has_suffix": &tengo.UserFunction{ + Name: "has_suffix", + Value: FuncASSRB(strings.HasSuffix), + }, // has_suffix(s, suffix) => bool + "index": &tengo.UserFunction{ + Name: "index", + Value: FuncASSRI(strings.Index), + }, // index(s, substr) => int + "index_any": &tengo.UserFunction{ + Name: "index_any", + Value: FuncASSRI(strings.IndexAny), + }, // index_any(s, chars) => int + "join": &tengo.UserFunction{ + Name: "join", + Value: textJoin, + }, // join(arr, sep) => string + "last_index": &tengo.UserFunction{ + Name: "last_index", + Value: FuncASSRI(strings.LastIndex), + }, // last_index(s, substr) => int + "last_index_any": &tengo.UserFunction{ + Name: "last_index_any", + Value: FuncASSRI(strings.LastIndexAny), + }, // last_index_any(s, chars) => int + "repeat": &tengo.UserFunction{ + Name: "repeat", + Value: textRepeat, + }, // repeat(s, count) => string + "replace": &tengo.UserFunction{ + Name: "replace", + Value: textReplace, + }, // replace(s, old, new, n) => string + "substr": &tengo.UserFunction{ + Name: "substr", + Value: textSubstring, + }, // substr(s, lower, upper) => string + "split": &tengo.UserFunction{ + Name: "split", + Value: FuncASSRSs(strings.Split), + }, // split(s, sep) => [string] + "split_after": &tengo.UserFunction{ + Name: "split_after", + Value: FuncASSRSs(strings.SplitAfter), + }, // split_after(s, sep) => [string] + "split_after_n": &tengo.UserFunction{ + Name: "split_after_n", + Value: FuncASSIRSs(strings.SplitAfterN), + }, // split_after_n(s, sep, n) => [string] + "split_n": &tengo.UserFunction{ + Name: "split_n", + Value: FuncASSIRSs(strings.SplitN), + }, // split_n(s, sep, n) => [string] + "title": &tengo.UserFunction{ + Name: "title", + Value: FuncASRS(strings.Title), + }, // title(s) => string + "to_lower": &tengo.UserFunction{ + Name: "to_lower", + Value: FuncASRS(strings.ToLower), + }, // to_lower(s) => string + "to_title": &tengo.UserFunction{ + Name: "to_title", + Value: FuncASRS(strings.ToTitle), + }, // to_title(s) => string + "to_upper": &tengo.UserFunction{ + Name: "to_upper", + Value: FuncASRS(strings.ToUpper), + }, // to_upper(s) => string + "pad_left": &tengo.UserFunction{ + Name: "pad_left", + Value: textPadLeft, + }, // pad_left(s, pad_len, pad_with) => string + "pad_right": &tengo.UserFunction{ + Name: "pad_right", + Value: textPadRight, + }, // pad_right(s, pad_len, pad_with) => string + "trim": &tengo.UserFunction{ + Name: "trim", + Value: FuncASSRS(strings.Trim), + }, // trim(s, cutset) => string + "trim_left": &tengo.UserFunction{ + Name: "trim_left", + Value: FuncASSRS(strings.TrimLeft), + }, // trim_left(s, cutset) => string + "trim_prefix": &tengo.UserFunction{ + Name: "trim_prefix", + Value: FuncASSRS(strings.TrimPrefix), + }, // trim_prefix(s, prefix) => string + "trim_right": &tengo.UserFunction{ + Name: "trim_right", + Value: FuncASSRS(strings.TrimRight), + }, // trim_right(s, cutset) => string + "trim_space": &tengo.UserFunction{ + Name: "trim_space", + Value: FuncASRS(strings.TrimSpace), + }, // trim_space(s) => string + "trim_suffix": &tengo.UserFunction{ + Name: "trim_suffix", + Value: FuncASSRS(strings.TrimSuffix), + }, // trim_suffix(s, suffix) => string + "atoi": &tengo.UserFunction{ + Name: "atoi", + Value: FuncASRIE(strconv.Atoi), + }, // atoi(str) => int/error + "format_bool": &tengo.UserFunction{ + Name: "format_bool", + Value: textFormatBool, + }, // format_bool(b) => string + "format_float": &tengo.UserFunction{ + Name: "format_float", + Value: textFormatFloat, + }, // format_float(f, fmt, prec, bits) => string + "format_int": &tengo.UserFunction{ + Name: "format_int", + Value: textFormatInt, + }, // format_int(i, base) => string + "itoa": &tengo.UserFunction{ + Name: "itoa", + Value: FuncAIRS(strconv.Itoa), + }, // itoa(i) => string + "parse_bool": &tengo.UserFunction{ + Name: "parse_bool", + Value: textParseBool, + }, // parse_bool(str) => bool/error + "parse_float": &tengo.UserFunction{ + Name: "parse_float", + Value: textParseFloat, + }, // parse_float(str, bits) => float/error + "parse_int": &tengo.UserFunction{ + Name: "parse_int", + Value: textParseInt, + }, // parse_int(str, base, bits) => int/error + "quote": &tengo.UserFunction{ + Name: "quote", + Value: FuncASRS(strconv.Quote), + }, // quote(str) => string + "unquote": &tengo.UserFunction{ + Name: "unquote", + Value: FuncASRSE(strconv.Unquote), + }, // unquote(str) => string/error +} + +func textREMatch(args ...tengo.Object) (ret tengo.Object, err error) { + if len(args) != 2 { + err = tengo.ErrWrongNumArguments + return + } + + s1, ok := tengo.ToString(args[0]) + if !ok { + err = tengo.ErrInvalidArgumentType{ + Name: "first", + Expected: "string(compatible)", + Found: args[0].TypeName(), + } + return + } + + s2, ok := tengo.ToString(args[1]) + if !ok { + err = tengo.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 = tengo.TrueValue + } else { + ret = tengo.FalseValue + } + + return +} + +func textREFind(args ...tengo.Object) (ret tengo.Object, err error) { + numArgs := len(args) + if numArgs != 2 && numArgs != 3 { + err = tengo.ErrWrongNumArguments + return + } + + s1, ok := tengo.ToString(args[0]) + if !ok { + err = tengo.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 := tengo.ToString(args[1]) + if !ok { + err = tengo.ErrInvalidArgumentType{ + Name: "second", + Expected: "string(compatible)", + Found: args[1].TypeName(), + } + return + } + + if numArgs < 3 { + m := re.FindStringSubmatchIndex(s2) + if m == nil { + ret = tengo.UndefinedValue + return + } + + arr := &tengo.Array{} + for i := 0; i < len(m); i += 2 { + arr.Value = append(arr.Value, + &tengo.ImmutableMap{Value: map[string]tengo.Object{ + "text": &tengo.String{Value: s2[m[i]:m[i+1]]}, + "begin": &tengo.Int{Value: int64(m[i])}, + "end": &tengo.Int{Value: int64(m[i+1])}, + }}) + } + + ret = &tengo.Array{Value: []tengo.Object{arr}} + + return + } + + i3, ok := tengo.ToInt(args[2]) + if !ok { + err = tengo.ErrInvalidArgumentType{ + Name: "third", + Expected: "int(compatible)", + Found: args[2].TypeName(), + } + return + } + m := re.FindAllStringSubmatchIndex(s2, i3) + if m == nil { + ret = tengo.UndefinedValue + return + } + + arr := &tengo.Array{} + for _, m := range m { + subMatch := &tengo.Array{} + for i := 0; i < len(m); i += 2 { + subMatch.Value = append(subMatch.Value, + &tengo.ImmutableMap{Value: map[string]tengo.Object{ + "text": &tengo.String{Value: s2[m[i]:m[i+1]]}, + "begin": &tengo.Int{Value: int64(m[i])}, + "end": &tengo.Int{Value: int64(m[i+1])}, + }}) + } + + arr.Value = append(arr.Value, subMatch) + } + + ret = arr + + return +} + +func textREReplace(args ...tengo.Object) (ret tengo.Object, err error) { + if len(args) != 3 { + err = tengo.ErrWrongNumArguments + return + } + + s1, ok := tengo.ToString(args[0]) + if !ok { + err = tengo.ErrInvalidArgumentType{ + Name: "first", + Expected: "string(compatible)", + Found: args[0].TypeName(), + } + return + } + + s2, ok := tengo.ToString(args[1]) + if !ok { + err = tengo.ErrInvalidArgumentType{ + Name: "second", + Expected: "string(compatible)", + Found: args[1].TypeName(), + } + return + } + + s3, ok := tengo.ToString(args[2]) + if !ok { + err = tengo.ErrInvalidArgumentType{ + Name: "third", + Expected: "string(compatible)", + Found: args[2].TypeName(), + } + return + } + + re, err := regexp.Compile(s1) + if err != nil { + ret = wrapError(err) + } else { + s, ok := doTextRegexpReplace(re, s2, s3) + if !ok { + return nil, tengo.ErrStringLimit + } + + ret = &tengo.String{Value: s} + } + + return +} + +func textRESplit(args ...tengo.Object) (ret tengo.Object, err error) { + numArgs := len(args) + if numArgs != 2 && numArgs != 3 { + err = tengo.ErrWrongNumArguments + return + } + + s1, ok := tengo.ToString(args[0]) + if !ok { + err = tengo.ErrInvalidArgumentType{ + Name: "first", + Expected: "string(compatible)", + Found: args[0].TypeName(), + } + return + } + + s2, ok := tengo.ToString(args[1]) + if !ok { + err = tengo.ErrInvalidArgumentType{ + Name: "second", + Expected: "string(compatible)", + Found: args[1].TypeName(), + } + return + } + + var i3 = -1 + if numArgs > 2 { + i3, ok = tengo.ToInt(args[2]) + if !ok { + err = tengo.ErrInvalidArgumentType{ + Name: "third", + Expected: "int(compatible)", + Found: args[2].TypeName(), + } + return + } + } + + re, err := regexp.Compile(s1) + if err != nil { + ret = wrapError(err) + return + } + + arr := &tengo.Array{} + for _, s := range re.Split(s2, i3) { + arr.Value = append(arr.Value, &tengo.String{Value: s}) + } + + ret = arr + + return +} + +func textRECompile(args ...tengo.Object) (ret tengo.Object, err error) { + if len(args) != 1 { + err = tengo.ErrWrongNumArguments + return + } + + s1, ok := tengo.ToString(args[0]) + if !ok { + err = tengo.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 ...tengo.Object) (ret tengo.Object, err error) { + if len(args) != 4 { + err = tengo.ErrWrongNumArguments + return + } + + s1, ok := tengo.ToString(args[0]) + if !ok { + err = tengo.ErrInvalidArgumentType{ + Name: "first", + Expected: "string(compatible)", + Found: args[0].TypeName(), + } + return + } + + s2, ok := tengo.ToString(args[1]) + if !ok { + err = tengo.ErrInvalidArgumentType{ + Name: "second", + Expected: "string(compatible)", + Found: args[1].TypeName(), + } + return + } + + s3, ok := tengo.ToString(args[2]) + if !ok { + err = tengo.ErrInvalidArgumentType{ + Name: "third", + Expected: "string(compatible)", + Found: args[2].TypeName(), + } + return + } + + i4, ok := tengo.ToInt(args[3]) + if !ok { + err = tengo.ErrInvalidArgumentType{ + Name: "fourth", + Expected: "int(compatible)", + Found: args[3].TypeName(), + } + return + } + + s, ok := doTextReplace(s1, s2, s3, i4) + if !ok { + err = tengo.ErrStringLimit + return + } + + ret = &tengo.String{Value: s} + + return +} + +func textSubstring(args ...tengo.Object) (ret tengo.Object, err error) { + argslen := len(args) + if argslen != 2 && argslen != 3 { + err = tengo.ErrWrongNumArguments + return + } + + s1, ok := tengo.ToString(args[0]) + if !ok { + err = tengo.ErrInvalidArgumentType{ + Name: "first", + Expected: "string(compatible)", + Found: args[0].TypeName(), + } + return + } + + i2, ok := tengo.ToInt(args[1]) + if !ok { + err = tengo.ErrInvalidArgumentType{ + Name: "second", + Expected: "int(compatible)", + Found: args[1].TypeName(), + } + return + } + + strlen := len(s1) + i3 := strlen + if argslen == 3 { + i3, ok = tengo.ToInt(args[2]) + if !ok { + err = tengo.ErrInvalidArgumentType{ + Name: "third", + Expected: "int(compatible)", + Found: args[2].TypeName(), + } + return + } + } + + if i2 > i3 { + err = tengo.ErrInvalidIndexType + return + } + + if i2 < 0 { + i2 = 0 + } else if i2 > strlen { + i2 = strlen + } + + if i3 < 0 { + i3 = 0 + } else if i3 > strlen { + i3 = strlen + } + + ret = &tengo.String{Value: s1[i2:i3]} + + return +} + +func textPadLeft(args ...tengo.Object) (ret tengo.Object, err error) { + argslen := len(args) + if argslen != 2 && argslen != 3 { + err = tengo.ErrWrongNumArguments + return + } + + s1, ok := tengo.ToString(args[0]) + if !ok { + err = tengo.ErrInvalidArgumentType{ + Name: "first", + Expected: "string(compatible)", + Found: args[0].TypeName(), + } + return + } + + i2, ok := tengo.ToInt(args[1]) + if !ok { + err = tengo.ErrInvalidArgumentType{ + Name: "second", + Expected: "int(compatible)", + Found: args[1].TypeName(), + } + return + } + + if i2 > tengo.MaxStringLen { + return nil, tengo.ErrStringLimit + } + + sLen := len(s1) + if sLen >= i2 { + ret = &tengo.String{Value: s1} + return + } + + s3 := " " + if argslen == 3 { + s3, ok = tengo.ToString(args[2]) + if !ok { + err = tengo.ErrInvalidArgumentType{ + Name: "third", + Expected: "string(compatible)", + Found: args[2].TypeName(), + } + return + } + } + + padStrLen := len(s3) + if padStrLen == 0 { + ret = &tengo.String{Value: s1} + return + } + + padCount := ((i2 - padStrLen) / padStrLen) + 1 + retStr := strings.Repeat(s3, padCount) + s1 + ret = &tengo.String{Value: retStr[len(retStr)-i2:]} + + return +} + +func textPadRight(args ...tengo.Object) (ret tengo.Object, err error) { + argslen := len(args) + if argslen != 2 && argslen != 3 { + err = tengo.ErrWrongNumArguments + return + } + + s1, ok := tengo.ToString(args[0]) + if !ok { + err = tengo.ErrInvalidArgumentType{ + Name: "first", + Expected: "string(compatible)", + Found: args[0].TypeName(), + } + return + } + + i2, ok := tengo.ToInt(args[1]) + if !ok { + err = tengo.ErrInvalidArgumentType{ + Name: "second", + Expected: "int(compatible)", + Found: args[1].TypeName(), + } + return + } + + if i2 > tengo.MaxStringLen { + return nil, tengo.ErrStringLimit + } + + sLen := len(s1) + if sLen >= i2 { + ret = &tengo.String{Value: s1} + return + } + + s3 := " " + if argslen == 3 { + s3, ok = tengo.ToString(args[2]) + if !ok { + err = tengo.ErrInvalidArgumentType{ + Name: "third", + Expected: "string(compatible)", + Found: args[2].TypeName(), + } + return + } + } + + padStrLen := len(s3) + if padStrLen == 0 { + ret = &tengo.String{Value: s1} + return + } + + padCount := ((i2 - padStrLen) / padStrLen) + 1 + retStr := s1 + strings.Repeat(s3, padCount) + ret = &tengo.String{Value: retStr[:i2]} + + return +} + +func textRepeat(args ...tengo.Object) (ret tengo.Object, err error) { + if len(args) != 2 { + return nil, tengo.ErrWrongNumArguments + } + + s1, ok := tengo.ToString(args[0]) + if !ok { + return nil, tengo.ErrInvalidArgumentType{ + Name: "first", + Expected: "string(compatible)", + Found: args[0].TypeName(), + } + } + + i2, ok := tengo.ToInt(args[1]) + if !ok { + return nil, tengo.ErrInvalidArgumentType{ + Name: "second", + Expected: "int(compatible)", + Found: args[1].TypeName(), + } + } + + if len(s1)*i2 > tengo.MaxStringLen { + return nil, tengo.ErrStringLimit + } + + return &tengo.String{Value: strings.Repeat(s1, i2)}, nil +} + +func textJoin(args ...tengo.Object) (ret tengo.Object, err error) { + if len(args) != 2 { + return nil, tengo.ErrWrongNumArguments + } + + var slen int + var ss1 []string + switch arg0 := args[0].(type) { + case *tengo.Array: + for idx, a := range arg0.Value { + as, ok := tengo.ToString(a) + if !ok { + return nil, tengo.ErrInvalidArgumentType{ + Name: fmt.Sprintf("first[%d]", idx), + Expected: "string(compatible)", + Found: a.TypeName(), + } + } + slen += len(as) + ss1 = append(ss1, as) + } + case *tengo.ImmutableArray: + for idx, a := range arg0.Value { + as, ok := tengo.ToString(a) + if !ok { + return nil, tengo.ErrInvalidArgumentType{ + Name: fmt.Sprintf("first[%d]", idx), + Expected: "string(compatible)", + Found: a.TypeName(), + } + } + slen += len(as) + ss1 = append(ss1, as) + } + default: + return nil, tengo.ErrInvalidArgumentType{ + Name: "first", + Expected: "array", + Found: args[0].TypeName(), + } + } + + s2, ok := tengo.ToString(args[1]) + if !ok { + return nil, tengo.ErrInvalidArgumentType{ + Name: "second", + Expected: "string(compatible)", + Found: args[1].TypeName(), + } + } + + // make sure output length does not exceed the limit + if slen+len(s2)*(len(ss1)-1) > tengo.MaxStringLen { + return nil, tengo.ErrStringLimit + } + + return &tengo.String{Value: strings.Join(ss1, s2)}, nil +} + +func textFormatBool(args ...tengo.Object) (ret tengo.Object, err error) { + if len(args) != 1 { + err = tengo.ErrWrongNumArguments + return + } + + b1, ok := args[0].(*tengo.Bool) + if !ok { + err = tengo.ErrInvalidArgumentType{ + Name: "first", + Expected: "bool", + Found: args[0].TypeName(), + } + return + } + + if b1 == tengo.TrueValue { + ret = &tengo.String{Value: "true"} + } else { + ret = &tengo.String{Value: "false"} + } + + return +} + +func textFormatFloat(args ...tengo.Object) (ret tengo.Object, err error) { + if len(args) != 4 { + err = tengo.ErrWrongNumArguments + return + } + + f1, ok := args[0].(*tengo.Float) + if !ok { + err = tengo.ErrInvalidArgumentType{ + Name: "first", + Expected: "float", + Found: args[0].TypeName(), + } + return + } + + s2, ok := tengo.ToString(args[1]) + if !ok { + err = tengo.ErrInvalidArgumentType{ + Name: "second", + Expected: "string(compatible)", + Found: args[1].TypeName(), + } + return + } + + i3, ok := tengo.ToInt(args[2]) + if !ok { + err = tengo.ErrInvalidArgumentType{ + Name: "third", + Expected: "int(compatible)", + Found: args[2].TypeName(), + } + return + } + + i4, ok := tengo.ToInt(args[3]) + if !ok { + err = tengo.ErrInvalidArgumentType{ + Name: "fourth", + Expected: "int(compatible)", + Found: args[3].TypeName(), + } + return + } + + ret = &tengo.String{Value: strconv.FormatFloat(f1.Value, s2[0], i3, i4)} + + return +} + +func textFormatInt(args ...tengo.Object) (ret tengo.Object, err error) { + if len(args) != 2 { + err = tengo.ErrWrongNumArguments + return + } + + i1, ok := args[0].(*tengo.Int) + if !ok { + err = tengo.ErrInvalidArgumentType{ + Name: "first", + Expected: "int", + Found: args[0].TypeName(), + } + return + } + + i2, ok := tengo.ToInt(args[1]) + if !ok { + err = tengo.ErrInvalidArgumentType{ + Name: "second", + Expected: "int(compatible)", + Found: args[1].TypeName(), + } + return + } + + ret = &tengo.String{Value: strconv.FormatInt(i1.Value, i2)} + + return +} + +func textParseBool(args ...tengo.Object) (ret tengo.Object, err error) { + if len(args) != 1 { + err = tengo.ErrWrongNumArguments + return + } + + s1, ok := args[0].(*tengo.String) + if !ok { + err = tengo.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 = tengo.TrueValue + } else { + ret = tengo.FalseValue + } + + return +} + +func textParseFloat(args ...tengo.Object) (ret tengo.Object, err error) { + if len(args) != 2 { + err = tengo.ErrWrongNumArguments + return + } + + s1, ok := args[0].(*tengo.String) + if !ok { + err = tengo.ErrInvalidArgumentType{ + Name: "first", + Expected: "string", + Found: args[0].TypeName(), + } + return + } + + i2, ok := tengo.ToInt(args[1]) + if !ok { + err = tengo.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 = &tengo.Float{Value: parsed} + + return +} + +func textParseInt(args ...tengo.Object) (ret tengo.Object, err error) { + if len(args) != 3 { + err = tengo.ErrWrongNumArguments + return + } + + s1, ok := args[0].(*tengo.String) + if !ok { + err = tengo.ErrInvalidArgumentType{ + Name: "first", + Expected: "string", + Found: args[0].TypeName(), + } + return + } + + i2, ok := tengo.ToInt(args[1]) + if !ok { + err = tengo.ErrInvalidArgumentType{ + Name: "second", + Expected: "int(compatible)", + Found: args[1].TypeName(), + } + return + } + + i3, ok := tengo.ToInt(args[2]) + if !ok { + err = tengo.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 = &tengo.Int{Value: parsed} + + return +} + +// Modified implementation of strings.Replace +// to limit the maximum length of output string. +func doTextReplace(s, old, new string, n int) (string, bool) { + if old == new || n == 0 { + return s, true // avoid allocation + } + + // Compute number of replacements. + if m := strings.Count(s, old); m == 0 { + return s, true // avoid allocation + } else if n < 0 || m < n { + n = m + } + + // Apply replacements to buffer. + t := make([]byte, len(s)+n*(len(new)-len(old))) + w := 0 + start := 0 + for i := 0; i < n; i++ { + j := start + if len(old) == 0 { + if i > 0 { + _, wid := utf8.DecodeRuneInString(s[start:]) + j += wid + } + } else { + j += strings.Index(s[start:], old) + } + + ssj := s[start:j] + if w+len(ssj)+len(new) > tengo.MaxStringLen { + return "", false + } + + w += copy(t[w:], ssj) + w += copy(t[w:], new) + start = j + len(old) + } + + ss := s[start:] + if w+len(ss) > tengo.MaxStringLen { + return "", false + } + + w += copy(t[w:], ss) + + return string(t[0:w]), true +} diff --git a/vendor/github.com/d5/tengo/stdlib/text_regexp.go b/vendor/github.com/d5/tengo/v2/stdlib/text_regexp.go similarity index 53% rename from vendor/github.com/d5/tengo/stdlib/text_regexp.go rename to vendor/github.com/d5/tengo/v2/stdlib/text_regexp.go index 16f135bf..1a7ecf07 100644 --- a/vendor/github.com/d5/tengo/stdlib/text_regexp.go +++ b/vendor/github.com/d5/tengo/v2/stdlib/text_regexp.go @@ -3,24 +3,26 @@ package stdlib import ( "regexp" - "github.com/d5/tengo" - "github.com/d5/tengo/objects" + "github.com/d5/tengo/v2" ) -func makeTextRegexp(re *regexp.Regexp) *objects.ImmutableMap { - return &objects.ImmutableMap{ - Value: map[string]objects.Object{ +func makeTextRegexp(re *regexp.Regexp) *tengo.ImmutableMap { + return &tengo.ImmutableMap{ + Value: map[string]tengo.Object{ // match(text) => bool - "match": &objects.UserFunction{ - Value: func(args ...objects.Object) (ret objects.Object, err error) { + "match": &tengo.UserFunction{ + Value: func(args ...tengo.Object) ( + ret tengo.Object, + err error, + ) { if len(args) != 1 { - err = objects.ErrWrongNumArguments + err = tengo.ErrWrongNumArguments return } - s1, ok := objects.ToString(args[0]) + s1, ok := tengo.ToString(args[0]) if !ok { - err = objects.ErrInvalidArgumentType{ + err = tengo.ErrInvalidArgumentType{ Name: "first", Expected: "string(compatible)", Found: args[0].TypeName(), @@ -29,9 +31,9 @@ func makeTextRegexp(re *regexp.Regexp) *objects.ImmutableMap { } if re.MatchString(s1) { - ret = objects.TrueValue + ret = tengo.TrueValue } else { - ret = objects.FalseValue + ret = tengo.FalseValue } return @@ -40,17 +42,20 @@ func makeTextRegexp(re *regexp.Regexp) *objects.ImmutableMap { // find(text) => array(array({text:,begin:,end:}))/undefined // find(text, maxCount) => array(array({text:,begin:,end:}))/undefined - "find": &objects.UserFunction{ - Value: func(args ...objects.Object) (ret objects.Object, err error) { + "find": &tengo.UserFunction{ + Value: func(args ...tengo.Object) ( + ret tengo.Object, + err error, + ) { numArgs := len(args) if numArgs != 1 && numArgs != 2 { - err = objects.ErrWrongNumArguments + err = tengo.ErrWrongNumArguments return } - s1, ok := objects.ToString(args[0]) + s1, ok := tengo.ToString(args[0]) if !ok { - err = objects.ErrInvalidArgumentType{ + err = tengo.ErrInvalidArgumentType{ Name: "first", Expected: "string(compatible)", Found: args[0].TypeName(), @@ -61,27 +66,35 @@ func makeTextRegexp(re *regexp.Regexp) *objects.ImmutableMap { if numArgs == 1 { m := re.FindStringSubmatchIndex(s1) if m == nil { - ret = objects.UndefinedValue + ret = tengo.UndefinedValue return } - arr := &objects.Array{} + arr := &tengo.Array{} for i := 0; i < len(m); i += 2 { - arr.Value = append(arr.Value, &objects.ImmutableMap{Value: map[string]objects.Object{ - "text": &objects.String{Value: s1[m[i]:m[i+1]]}, - "begin": &objects.Int{Value: int64(m[i])}, - "end": &objects.Int{Value: int64(m[i+1])}, - }}) + arr.Value = append(arr.Value, + &tengo.ImmutableMap{ + Value: map[string]tengo.Object{ + "text": &tengo.String{ + Value: s1[m[i]:m[i+1]], + }, + "begin": &tengo.Int{ + Value: int64(m[i]), + }, + "end": &tengo.Int{ + Value: int64(m[i+1]), + }, + }}) } - ret = &objects.Array{Value: []objects.Object{arr}} + ret = &tengo.Array{Value: []tengo.Object{arr}} return } - i2, ok := objects.ToInt(args[1]) + i2, ok := tengo.ToInt(args[1]) if !ok { - err = objects.ErrInvalidArgumentType{ + err = tengo.ErrInvalidArgumentType{ Name: "second", Expected: "int(compatible)", Found: args[1].TypeName(), @@ -90,19 +103,27 @@ func makeTextRegexp(re *regexp.Regexp) *objects.ImmutableMap { } m := re.FindAllStringSubmatchIndex(s1, i2) if m == nil { - ret = objects.UndefinedValue + ret = tengo.UndefinedValue return } - arr := &objects.Array{} + arr := &tengo.Array{} for _, m := range m { - subMatch := &objects.Array{} + subMatch := &tengo.Array{} for i := 0; i < len(m); i += 2 { - subMatch.Value = append(subMatch.Value, &objects.ImmutableMap{Value: map[string]objects.Object{ - "text": &objects.String{Value: s1[m[i]:m[i+1]]}, - "begin": &objects.Int{Value: int64(m[i])}, - "end": &objects.Int{Value: int64(m[i+1])}, - }}) + subMatch.Value = append(subMatch.Value, + &tengo.ImmutableMap{ + Value: map[string]tengo.Object{ + "text": &tengo.String{ + Value: s1[m[i]:m[i+1]], + }, + "begin": &tengo.Int{ + Value: int64(m[i]), + }, + "end": &tengo.Int{ + Value: int64(m[i+1]), + }, + }}) } arr.Value = append(arr.Value, subMatch) @@ -115,16 +136,19 @@ func makeTextRegexp(re *regexp.Regexp) *objects.ImmutableMap { }, // replace(src, repl) => string - "replace": &objects.UserFunction{ - Value: func(args ...objects.Object) (ret objects.Object, err error) { + "replace": &tengo.UserFunction{ + Value: func(args ...tengo.Object) ( + ret tengo.Object, + err error, + ) { if len(args) != 2 { - err = objects.ErrWrongNumArguments + err = tengo.ErrWrongNumArguments return } - s1, ok := objects.ToString(args[0]) + s1, ok := tengo.ToString(args[0]) if !ok { - err = objects.ErrInvalidArgumentType{ + err = tengo.ErrInvalidArgumentType{ Name: "first", Expected: "string(compatible)", Found: args[0].TypeName(), @@ -132,9 +156,9 @@ func makeTextRegexp(re *regexp.Regexp) *objects.ImmutableMap { return } - s2, ok := objects.ToString(args[1]) + s2, ok := tengo.ToString(args[1]) if !ok { - err = objects.ErrInvalidArgumentType{ + err = tengo.ErrInvalidArgumentType{ Name: "second", Expected: "string(compatible)", Found: args[1].TypeName(), @@ -144,10 +168,10 @@ func makeTextRegexp(re *regexp.Regexp) *objects.ImmutableMap { s, ok := doTextRegexpReplace(re, s1, s2) if !ok { - return nil, objects.ErrStringLimit + return nil, tengo.ErrStringLimit } - ret = &objects.String{Value: s} + ret = &tengo.String{Value: s} return }, @@ -155,17 +179,20 @@ func makeTextRegexp(re *regexp.Regexp) *objects.ImmutableMap { // split(text) => array(string) // split(text, maxCount) => array(string) - "split": &objects.UserFunction{ - Value: func(args ...objects.Object) (ret objects.Object, err error) { + "split": &tengo.UserFunction{ + Value: func(args ...tengo.Object) ( + ret tengo.Object, + err error, + ) { numArgs := len(args) if numArgs != 1 && numArgs != 2 { - err = objects.ErrWrongNumArguments + err = tengo.ErrWrongNumArguments return } - s1, ok := objects.ToString(args[0]) + s1, ok := tengo.ToString(args[0]) if !ok { - err = objects.ErrInvalidArgumentType{ + err = tengo.ErrInvalidArgumentType{ Name: "first", Expected: "string(compatible)", Found: args[0].TypeName(), @@ -175,9 +202,9 @@ func makeTextRegexp(re *regexp.Regexp) *objects.ImmutableMap { var i2 = -1 if numArgs > 1 { - i2, ok = objects.ToInt(args[1]) + i2, ok = tengo.ToInt(args[1]) if !ok { - err = objects.ErrInvalidArgumentType{ + err = tengo.ErrInvalidArgumentType{ Name: "second", Expected: "int(compatible)", Found: args[1].TypeName(), @@ -186,9 +213,10 @@ func makeTextRegexp(re *regexp.Regexp) *objects.ImmutableMap { } } - arr := &objects.Array{} + arr := &tengo.Array{} for _, s := range re.Split(s1, i2) { - arr.Value = append(arr.Value, &objects.String{Value: s}) + arr.Value = append(arr.Value, + &tengo.String{Value: s}) } ret = arr @@ -204,15 +232,12 @@ func makeTextRegexp(re *regexp.Regexp) *objects.ImmutableMap { func doTextRegexpReplace(re *regexp.Regexp, src, repl string) (string, bool) { idx := 0 out := "" - for _, m := range re.FindAllStringSubmatchIndex(src, -1) { var exp []byte exp = re.ExpandString(exp, repl, src, m) - if len(out)+m[0]-idx+len(exp) > tengo.MaxStringLen { return "", false } - out += src[idx:m[0]] + string(exp) idx = m[1] } @@ -220,9 +245,7 @@ func doTextRegexpReplace(re *regexp.Regexp, src, repl string) (string, bool) { if len(out)+len(src)-idx > tengo.MaxStringLen { return "", false } - out += src[idx:] } - - return string(out), true + return out, true } diff --git a/vendor/github.com/d5/tengo/v2/stdlib/times.go b/vendor/github.com/d5/tengo/v2/stdlib/times.go new file mode 100644 index 00000000..0b6f7bd4 --- /dev/null +++ b/vendor/github.com/d5/tengo/v2/stdlib/times.go @@ -0,0 +1,1135 @@ +package stdlib + +import ( + "time" + + "github.com/d5/tengo/v2" +) + +var timesModule = map[string]tengo.Object{ + "format_ansic": &tengo.String{Value: time.ANSIC}, + "format_unix_date": &tengo.String{Value: time.UnixDate}, + "format_ruby_date": &tengo.String{Value: time.RubyDate}, + "format_rfc822": &tengo.String{Value: time.RFC822}, + "format_rfc822z": &tengo.String{Value: time.RFC822Z}, + "format_rfc850": &tengo.String{Value: time.RFC850}, + "format_rfc1123": &tengo.String{Value: time.RFC1123}, + "format_rfc1123z": &tengo.String{Value: time.RFC1123Z}, + "format_rfc3339": &tengo.String{Value: time.RFC3339}, + "format_rfc3339_nano": &tengo.String{Value: time.RFC3339Nano}, + "format_kitchen": &tengo.String{Value: time.Kitchen}, + "format_stamp": &tengo.String{Value: time.Stamp}, + "format_stamp_milli": &tengo.String{Value: time.StampMilli}, + "format_stamp_micro": &tengo.String{Value: time.StampMicro}, + "format_stamp_nano": &tengo.String{Value: time.StampNano}, + "nanosecond": &tengo.Int{Value: int64(time.Nanosecond)}, + "microsecond": &tengo.Int{Value: int64(time.Microsecond)}, + "millisecond": &tengo.Int{Value: int64(time.Millisecond)}, + "second": &tengo.Int{Value: int64(time.Second)}, + "minute": &tengo.Int{Value: int64(time.Minute)}, + "hour": &tengo.Int{Value: int64(time.Hour)}, + "january": &tengo.Int{Value: int64(time.January)}, + "february": &tengo.Int{Value: int64(time.February)}, + "march": &tengo.Int{Value: int64(time.March)}, + "april": &tengo.Int{Value: int64(time.April)}, + "may": &tengo.Int{Value: int64(time.May)}, + "june": &tengo.Int{Value: int64(time.June)}, + "july": &tengo.Int{Value: int64(time.July)}, + "august": &tengo.Int{Value: int64(time.August)}, + "september": &tengo.Int{Value: int64(time.September)}, + "october": &tengo.Int{Value: int64(time.October)}, + "november": &tengo.Int{Value: int64(time.November)}, + "december": &tengo.Int{Value: int64(time.December)}, + "sleep": &tengo.UserFunction{ + Name: "sleep", + Value: timesSleep, + }, // sleep(int) + "parse_duration": &tengo.UserFunction{ + Name: "parse_duration", + Value: timesParseDuration, + }, // parse_duration(str) => int + "since": &tengo.UserFunction{ + Name: "since", + Value: timesSince, + }, // since(time) => int + "until": &tengo.UserFunction{ + Name: "until", + Value: timesUntil, + }, // until(time) => int + "duration_hours": &tengo.UserFunction{ + Name: "duration_hours", + Value: timesDurationHours, + }, // duration_hours(int) => float + "duration_minutes": &tengo.UserFunction{ + Name: "duration_minutes", + Value: timesDurationMinutes, + }, // duration_minutes(int) => float + "duration_nanoseconds": &tengo.UserFunction{ + Name: "duration_nanoseconds", + Value: timesDurationNanoseconds, + }, // duration_nanoseconds(int) => int + "duration_seconds": &tengo.UserFunction{ + Name: "duration_seconds", + Value: timesDurationSeconds, + }, // duration_seconds(int) => float + "duration_string": &tengo.UserFunction{ + Name: "duration_string", + Value: timesDurationString, + }, // duration_string(int) => string + "month_string": &tengo.UserFunction{ + Name: "month_string", + Value: timesMonthString, + }, // month_string(int) => string + "date": &tengo.UserFunction{ + Name: "date", + Value: timesDate, + }, // date(year, month, day, hour, min, sec, nsec) => time + "now": &tengo.UserFunction{ + Name: "now", + Value: timesNow, + }, // now() => time + "parse": &tengo.UserFunction{ + Name: "parse", + Value: timesParse, + }, // parse(format, str) => time + "unix": &tengo.UserFunction{ + Name: "unix", + Value: timesUnix, + }, // unix(sec, nsec) => time + "add": &tengo.UserFunction{ + Name: "add", + Value: timesAdd, + }, // add(time, int) => time + "add_date": &tengo.UserFunction{ + Name: "add_date", + Value: timesAddDate, + }, // add_date(time, years, months, days) => time + "sub": &tengo.UserFunction{ + Name: "sub", + Value: timesSub, + }, // sub(t time, u time) => int + "after": &tengo.UserFunction{ + Name: "after", + Value: timesAfter, + }, // after(t time, u time) => bool + "before": &tengo.UserFunction{ + Name: "before", + Value: timesBefore, + }, // before(t time, u time) => bool + "time_year": &tengo.UserFunction{ + Name: "time_year", + Value: timesTimeYear, + }, // time_year(time) => int + "time_month": &tengo.UserFunction{ + Name: "time_month", + Value: timesTimeMonth, + }, // time_month(time) => int + "time_day": &tengo.UserFunction{ + Name: "time_day", + Value: timesTimeDay, + }, // time_day(time) => int + "time_weekday": &tengo.UserFunction{ + Name: "time_weekday", + Value: timesTimeWeekday, + }, // time_weekday(time) => int + "time_hour": &tengo.UserFunction{ + Name: "time_hour", + Value: timesTimeHour, + }, // time_hour(time) => int + "time_minute": &tengo.UserFunction{ + Name: "time_minute", + Value: timesTimeMinute, + }, // time_minute(time) => int + "time_second": &tengo.UserFunction{ + Name: "time_second", + Value: timesTimeSecond, + }, // time_second(time) => int + "time_nanosecond": &tengo.UserFunction{ + Name: "time_nanosecond", + Value: timesTimeNanosecond, + }, // time_nanosecond(time) => int + "time_unix": &tengo.UserFunction{ + Name: "time_unix", + Value: timesTimeUnix, + }, // time_unix(time) => int + "time_unix_nano": &tengo.UserFunction{ + Name: "time_unix_nano", + Value: timesTimeUnixNano, + }, // time_unix_nano(time) => int + "time_format": &tengo.UserFunction{ + Name: "time_format", + Value: timesTimeFormat, + }, // time_format(time, format) => string + "time_location": &tengo.UserFunction{ + Name: "time_location", + Value: timesTimeLocation, + }, // time_location(time) => string + "time_string": &tengo.UserFunction{ + Name: "time_string", + Value: timesTimeString, + }, // time_string(time) => string + "is_zero": &tengo.UserFunction{ + Name: "is_zero", + Value: timesIsZero, + }, // is_zero(time) => bool + "to_local": &tengo.UserFunction{ + Name: "to_local", + Value: timesToLocal, + }, // to_local(time) => time + "to_utc": &tengo.UserFunction{ + Name: "to_utc", + Value: timesToUTC, + }, // to_utc(time) => time +} + +func timesSleep(args ...tengo.Object) (ret tengo.Object, err error) { + if len(args) != 1 { + err = tengo.ErrWrongNumArguments + return + } + + i1, ok := tengo.ToInt64(args[0]) + if !ok { + err = tengo.ErrInvalidArgumentType{ + Name: "first", + Expected: "int(compatible)", + Found: args[0].TypeName(), + } + return + } + + time.Sleep(time.Duration(i1)) + ret = tengo.UndefinedValue + + return +} + +func timesParseDuration(args ...tengo.Object) ( + ret tengo.Object, + err error, +) { + if len(args) != 1 { + err = tengo.ErrWrongNumArguments + return + } + + s1, ok := tengo.ToString(args[0]) + if !ok { + err = tengo.ErrInvalidArgumentType{ + Name: "first", + Expected: "string(compatible)", + Found: args[0].TypeName(), + } + return + } + + dur, err := time.ParseDuration(s1) + if err != nil { + ret = wrapError(err) + return + } + + ret = &tengo.Int{Value: int64(dur)} + + return +} + +func timesSince(args ...tengo.Object) ( + ret tengo.Object, + err error, +) { + if len(args) != 1 { + err = tengo.ErrWrongNumArguments + return + } + + t1, ok := tengo.ToTime(args[0]) + if !ok { + err = tengo.ErrInvalidArgumentType{ + Name: "first", + Expected: "time(compatible)", + Found: args[0].TypeName(), + } + return + } + + ret = &tengo.Int{Value: int64(time.Since(t1))} + + return +} + +func timesUntil(args ...tengo.Object) ( + ret tengo.Object, + err error, +) { + if len(args) != 1 { + err = tengo.ErrWrongNumArguments + return + } + + t1, ok := tengo.ToTime(args[0]) + if !ok { + err = tengo.ErrInvalidArgumentType{ + Name: "first", + Expected: "time(compatible)", + Found: args[0].TypeName(), + } + return + } + + ret = &tengo.Int{Value: int64(time.Until(t1))} + + return +} + +func timesDurationHours(args ...tengo.Object) ( + ret tengo.Object, + err error, +) { + if len(args) != 1 { + err = tengo.ErrWrongNumArguments + return + } + + i1, ok := tengo.ToInt64(args[0]) + if !ok { + err = tengo.ErrInvalidArgumentType{ + Name: "first", + Expected: "int(compatible)", + Found: args[0].TypeName(), + } + return + } + + ret = &tengo.Float{Value: time.Duration(i1).Hours()} + + return +} + +func timesDurationMinutes(args ...tengo.Object) ( + ret tengo.Object, + err error, +) { + if len(args) != 1 { + err = tengo.ErrWrongNumArguments + return + } + + i1, ok := tengo.ToInt64(args[0]) + if !ok { + err = tengo.ErrInvalidArgumentType{ + Name: "first", + Expected: "int(compatible)", + Found: args[0].TypeName(), + } + return + } + + ret = &tengo.Float{Value: time.Duration(i1).Minutes()} + + return +} + +func timesDurationNanoseconds(args ...tengo.Object) ( + ret tengo.Object, + err error, +) { + if len(args) != 1 { + err = tengo.ErrWrongNumArguments + return + } + + i1, ok := tengo.ToInt64(args[0]) + if !ok { + err = tengo.ErrInvalidArgumentType{ + Name: "first", + Expected: "int(compatible)", + Found: args[0].TypeName(), + } + return + } + + ret = &tengo.Int{Value: time.Duration(i1).Nanoseconds()} + + return +} + +func timesDurationSeconds(args ...tengo.Object) ( + ret tengo.Object, + err error, +) { + if len(args) != 1 { + err = tengo.ErrWrongNumArguments + return + } + + i1, ok := tengo.ToInt64(args[0]) + if !ok { + err = tengo.ErrInvalidArgumentType{ + Name: "first", + Expected: "int(compatible)", + Found: args[0].TypeName(), + } + return + } + + ret = &tengo.Float{Value: time.Duration(i1).Seconds()} + + return +} + +func timesDurationString(args ...tengo.Object) ( + ret tengo.Object, + err error, +) { + if len(args) != 1 { + err = tengo.ErrWrongNumArguments + return + } + + i1, ok := tengo.ToInt64(args[0]) + if !ok { + err = tengo.ErrInvalidArgumentType{ + Name: "first", + Expected: "int(compatible)", + Found: args[0].TypeName(), + } + return + } + + ret = &tengo.String{Value: time.Duration(i1).String()} + + return +} + +func timesMonthString(args ...tengo.Object) ( + ret tengo.Object, + err error, +) { + if len(args) != 1 { + err = tengo.ErrWrongNumArguments + return + } + + i1, ok := tengo.ToInt64(args[0]) + if !ok { + err = tengo.ErrInvalidArgumentType{ + Name: "first", + Expected: "int(compatible)", + Found: args[0].TypeName(), + } + return + } + + ret = &tengo.String{Value: time.Month(i1).String()} + + return +} + +func timesDate(args ...tengo.Object) ( + ret tengo.Object, + err error, +) { + if len(args) != 7 { + err = tengo.ErrWrongNumArguments + return + } + + i1, ok := tengo.ToInt(args[0]) + if !ok { + err = tengo.ErrInvalidArgumentType{ + Name: "first", + Expected: "int(compatible)", + Found: args[0].TypeName(), + } + return + } + i2, ok := tengo.ToInt(args[1]) + if !ok { + err = tengo.ErrInvalidArgumentType{ + Name: "second", + Expected: "int(compatible)", + Found: args[1].TypeName(), + } + return + } + i3, ok := tengo.ToInt(args[2]) + if !ok { + err = tengo.ErrInvalidArgumentType{ + Name: "third", + Expected: "int(compatible)", + Found: args[2].TypeName(), + } + return + } + i4, ok := tengo.ToInt(args[3]) + if !ok { + err = tengo.ErrInvalidArgumentType{ + Name: "fourth", + Expected: "int(compatible)", + Found: args[3].TypeName(), + } + return + } + i5, ok := tengo.ToInt(args[4]) + if !ok { + err = tengo.ErrInvalidArgumentType{ + Name: "fifth", + Expected: "int(compatible)", + Found: args[4].TypeName(), + } + return + } + i6, ok := tengo.ToInt(args[5]) + if !ok { + err = tengo.ErrInvalidArgumentType{ + Name: "sixth", + Expected: "int(compatible)", + Found: args[5].TypeName(), + } + return + } + i7, ok := tengo.ToInt(args[6]) + if !ok { + err = tengo.ErrInvalidArgumentType{ + Name: "seventh", + Expected: "int(compatible)", + Found: args[6].TypeName(), + } + return + } + + ret = &tengo.Time{ + Value: time.Date(i1, + time.Month(i2), i3, i4, i5, i6, i7, time.Now().Location()), + } + + return +} + +func timesNow(args ...tengo.Object) (ret tengo.Object, err error) { + if len(args) != 0 { + err = tengo.ErrWrongNumArguments + return + } + + ret = &tengo.Time{Value: time.Now()} + + return +} + +func timesParse(args ...tengo.Object) (ret tengo.Object, err error) { + if len(args) != 2 { + err = tengo.ErrWrongNumArguments + return + } + + s1, ok := tengo.ToString(args[0]) + if !ok { + err = tengo.ErrInvalidArgumentType{ + Name: "first", + Expected: "string(compatible)", + Found: args[0].TypeName(), + } + return + } + + s2, ok := tengo.ToString(args[1]) + if !ok { + err = tengo.ErrInvalidArgumentType{ + Name: "second", + Expected: "string(compatible)", + Found: args[1].TypeName(), + } + return + } + + parsed, err := time.Parse(s1, s2) + if err != nil { + ret = wrapError(err) + return + } + + ret = &tengo.Time{Value: parsed} + + return +} + +func timesUnix(args ...tengo.Object) (ret tengo.Object, err error) { + if len(args) != 2 { + err = tengo.ErrWrongNumArguments + return + } + + i1, ok := tengo.ToInt64(args[0]) + if !ok { + err = tengo.ErrInvalidArgumentType{ + Name: "first", + Expected: "int(compatible)", + Found: args[0].TypeName(), + } + return + } + + i2, ok := tengo.ToInt64(args[1]) + if !ok { + err = tengo.ErrInvalidArgumentType{ + Name: "second", + Expected: "int(compatible)", + Found: args[1].TypeName(), + } + return + } + + ret = &tengo.Time{Value: time.Unix(i1, i2)} + + return +} + +func timesAdd(args ...tengo.Object) (ret tengo.Object, err error) { + if len(args) != 2 { + err = tengo.ErrWrongNumArguments + return + } + + t1, ok := tengo.ToTime(args[0]) + if !ok { + err = tengo.ErrInvalidArgumentType{ + Name: "first", + Expected: "time(compatible)", + Found: args[0].TypeName(), + } + return + } + + i2, ok := tengo.ToInt64(args[1]) + if !ok { + err = tengo.ErrInvalidArgumentType{ + Name: "second", + Expected: "int(compatible)", + Found: args[1].TypeName(), + } + return + } + + ret = &tengo.Time{Value: t1.Add(time.Duration(i2))} + + return +} + +func timesSub(args ...tengo.Object) (ret tengo.Object, err error) { + if len(args) != 2 { + err = tengo.ErrWrongNumArguments + return + } + + t1, ok := tengo.ToTime(args[0]) + if !ok { + err = tengo.ErrInvalidArgumentType{ + Name: "first", + Expected: "time(compatible)", + Found: args[0].TypeName(), + } + return + } + + t2, ok := tengo.ToTime(args[1]) + if !ok { + err = tengo.ErrInvalidArgumentType{ + Name: "second", + Expected: "time(compatible)", + Found: args[1].TypeName(), + } + return + } + + ret = &tengo.Int{Value: int64(t1.Sub(t2))} + + return +} + +func timesAddDate(args ...tengo.Object) (ret tengo.Object, err error) { + if len(args) != 4 { + err = tengo.ErrWrongNumArguments + return + } + + t1, ok := tengo.ToTime(args[0]) + if !ok { + err = tengo.ErrInvalidArgumentType{ + Name: "first", + Expected: "time(compatible)", + Found: args[0].TypeName(), + } + return + } + + i2, ok := tengo.ToInt(args[1]) + if !ok { + err = tengo.ErrInvalidArgumentType{ + Name: "second", + Expected: "int(compatible)", + Found: args[1].TypeName(), + } + return + } + + i3, ok := tengo.ToInt(args[2]) + if !ok { + err = tengo.ErrInvalidArgumentType{ + Name: "third", + Expected: "int(compatible)", + Found: args[2].TypeName(), + } + return + } + + i4, ok := tengo.ToInt(args[3]) + if !ok { + err = tengo.ErrInvalidArgumentType{ + Name: "fourth", + Expected: "int(compatible)", + Found: args[3].TypeName(), + } + return + } + + ret = &tengo.Time{Value: t1.AddDate(i2, i3, i4)} + + return +} + +func timesAfter(args ...tengo.Object) (ret tengo.Object, err error) { + if len(args) != 2 { + err = tengo.ErrWrongNumArguments + return + } + + t1, ok := tengo.ToTime(args[0]) + if !ok { + err = tengo.ErrInvalidArgumentType{ + Name: "first", + Expected: "time(compatible)", + Found: args[0].TypeName(), + } + return + } + + t2, ok := tengo.ToTime(args[1]) + if !ok { + err = tengo.ErrInvalidArgumentType{ + Name: "second", + Expected: "time(compatible)", + Found: args[1].TypeName(), + } + return + } + + if t1.After(t2) { + ret = tengo.TrueValue + } else { + ret = tengo.FalseValue + } + + return +} + +func timesBefore(args ...tengo.Object) (ret tengo.Object, err error) { + if len(args) != 2 { + err = tengo.ErrWrongNumArguments + return + } + + t1, ok := tengo.ToTime(args[0]) + if !ok { + err = tengo.ErrInvalidArgumentType{ + Name: "first", + Expected: "time(compatible)", + Found: args[0].TypeName(), + } + return + } + + t2, ok := tengo.ToTime(args[1]) + if !ok { + err = tengo.ErrInvalidArgumentType{ + Name: "second", + Expected: "time(compatible)", + Found: args[0].TypeName(), + } + return + } + + if t1.Before(t2) { + ret = tengo.TrueValue + } else { + ret = tengo.FalseValue + } + + return +} + +func timesTimeYear(args ...tengo.Object) (ret tengo.Object, err error) { + if len(args) != 1 { + err = tengo.ErrWrongNumArguments + return + } + + t1, ok := tengo.ToTime(args[0]) + if !ok { + err = tengo.ErrInvalidArgumentType{ + Name: "first", + Expected: "time(compatible)", + Found: args[0].TypeName(), + } + return + } + + ret = &tengo.Int{Value: int64(t1.Year())} + + return +} + +func timesTimeMonth(args ...tengo.Object) (ret tengo.Object, err error) { + if len(args) != 1 { + err = tengo.ErrWrongNumArguments + return + } + + t1, ok := tengo.ToTime(args[0]) + if !ok { + err = tengo.ErrInvalidArgumentType{ + Name: "first", + Expected: "time(compatible)", + Found: args[0].TypeName(), + } + return + } + + ret = &tengo.Int{Value: int64(t1.Month())} + + return +} + +func timesTimeDay(args ...tengo.Object) (ret tengo.Object, err error) { + if len(args) != 1 { + err = tengo.ErrWrongNumArguments + return + } + + t1, ok := tengo.ToTime(args[0]) + if !ok { + err = tengo.ErrInvalidArgumentType{ + Name: "first", + Expected: "time(compatible)", + Found: args[0].TypeName(), + } + return + } + + ret = &tengo.Int{Value: int64(t1.Day())} + + return +} + +func timesTimeWeekday(args ...tengo.Object) (ret tengo.Object, err error) { + if len(args) != 1 { + err = tengo.ErrWrongNumArguments + return + } + + t1, ok := tengo.ToTime(args[0]) + if !ok { + err = tengo.ErrInvalidArgumentType{ + Name: "first", + Expected: "time(compatible)", + Found: args[0].TypeName(), + } + return + } + + ret = &tengo.Int{Value: int64(t1.Weekday())} + + return +} + +func timesTimeHour(args ...tengo.Object) (ret tengo.Object, err error) { + if len(args) != 1 { + err = tengo.ErrWrongNumArguments + return + } + + t1, ok := tengo.ToTime(args[0]) + if !ok { + err = tengo.ErrInvalidArgumentType{ + Name: "first", + Expected: "time(compatible)", + Found: args[0].TypeName(), + } + return + } + + ret = &tengo.Int{Value: int64(t1.Hour())} + + return +} + +func timesTimeMinute(args ...tengo.Object) (ret tengo.Object, err error) { + if len(args) != 1 { + err = tengo.ErrWrongNumArguments + return + } + + t1, ok := tengo.ToTime(args[0]) + if !ok { + err = tengo.ErrInvalidArgumentType{ + Name: "first", + Expected: "time(compatible)", + Found: args[0].TypeName(), + } + return + } + + ret = &tengo.Int{Value: int64(t1.Minute())} + + return +} + +func timesTimeSecond(args ...tengo.Object) (ret tengo.Object, err error) { + if len(args) != 1 { + err = tengo.ErrWrongNumArguments + return + } + + t1, ok := tengo.ToTime(args[0]) + if !ok { + err = tengo.ErrInvalidArgumentType{ + Name: "first", + Expected: "time(compatible)", + Found: args[0].TypeName(), + } + return + } + + ret = &tengo.Int{Value: int64(t1.Second())} + + return +} + +func timesTimeNanosecond(args ...tengo.Object) ( + ret tengo.Object, + err error, +) { + if len(args) != 1 { + err = tengo.ErrWrongNumArguments + return + } + + t1, ok := tengo.ToTime(args[0]) + if !ok { + err = tengo.ErrInvalidArgumentType{ + Name: "first", + Expected: "time(compatible)", + Found: args[0].TypeName(), + } + return + } + + ret = &tengo.Int{Value: int64(t1.Nanosecond())} + + return +} + +func timesTimeUnix(args ...tengo.Object) (ret tengo.Object, err error) { + if len(args) != 1 { + err = tengo.ErrWrongNumArguments + return + } + + t1, ok := tengo.ToTime(args[0]) + if !ok { + err = tengo.ErrInvalidArgumentType{ + Name: "first", + Expected: "time(compatible)", + Found: args[0].TypeName(), + } + return + } + + ret = &tengo.Int{Value: t1.Unix()} + + return +} + +func timesTimeUnixNano(args ...tengo.Object) ( + ret tengo.Object, + err error, +) { + if len(args) != 1 { + err = tengo.ErrWrongNumArguments + return + } + + t1, ok := tengo.ToTime(args[0]) + if !ok { + err = tengo.ErrInvalidArgumentType{ + Name: "first", + Expected: "time(compatible)", + Found: args[0].TypeName(), + } + return + } + + ret = &tengo.Int{Value: t1.UnixNano()} + + return +} + +func timesTimeFormat(args ...tengo.Object) (ret tengo.Object, err error) { + if len(args) != 2 { + err = tengo.ErrWrongNumArguments + return + } + + t1, ok := tengo.ToTime(args[0]) + if !ok { + err = tengo.ErrInvalidArgumentType{ + Name: "first", + Expected: "time(compatible)", + Found: args[0].TypeName(), + } + return + } + + s2, ok := tengo.ToString(args[1]) + if !ok { + err = tengo.ErrInvalidArgumentType{ + Name: "second", + Expected: "string(compatible)", + Found: args[1].TypeName(), + } + return + } + + s := t1.Format(s2) + if len(s) > tengo.MaxStringLen { + + return nil, tengo.ErrStringLimit + } + + ret = &tengo.String{Value: s} + + return +} + +func timesIsZero(args ...tengo.Object) (ret tengo.Object, err error) { + if len(args) != 1 { + err = tengo.ErrWrongNumArguments + return + } + + t1, ok := tengo.ToTime(args[0]) + if !ok { + err = tengo.ErrInvalidArgumentType{ + Name: "first", + Expected: "time(compatible)", + Found: args[0].TypeName(), + } + return + } + + if t1.IsZero() { + ret = tengo.TrueValue + } else { + ret = tengo.FalseValue + } + + return +} + +func timesToLocal(args ...tengo.Object) (ret tengo.Object, err error) { + if len(args) != 1 { + err = tengo.ErrWrongNumArguments + return + } + + t1, ok := tengo.ToTime(args[0]) + if !ok { + err = tengo.ErrInvalidArgumentType{ + Name: "first", + Expected: "time(compatible)", + Found: args[0].TypeName(), + } + return + } + + ret = &tengo.Time{Value: t1.Local()} + + return +} + +func timesToUTC(args ...tengo.Object) (ret tengo.Object, err error) { + if len(args) != 1 { + err = tengo.ErrWrongNumArguments + return + } + + t1, ok := tengo.ToTime(args[0]) + if !ok { + err = tengo.ErrInvalidArgumentType{ + Name: "first", + Expected: "time(compatible)", + Found: args[0].TypeName(), + } + return + } + + ret = &tengo.Time{Value: t1.UTC()} + + return +} + +func timesTimeLocation(args ...tengo.Object) ( + ret tengo.Object, + err error, +) { + if len(args) != 1 { + err = tengo.ErrWrongNumArguments + return + } + + t1, ok := tengo.ToTime(args[0]) + if !ok { + err = tengo.ErrInvalidArgumentType{ + Name: "first", + Expected: "time(compatible)", + Found: args[0].TypeName(), + } + return + } + + ret = &tengo.String{Value: t1.Location().String()} + + return +} + +func timesTimeString(args ...tengo.Object) (ret tengo.Object, err error) { + if len(args) != 1 { + err = tengo.ErrWrongNumArguments + return + } + + t1, ok := tengo.ToTime(args[0]) + if !ok { + err = tengo.ErrInvalidArgumentType{ + Name: "first", + Expected: "time(compatible)", + Found: args[0].TypeName(), + } + return + } + + ret = &tengo.String{Value: t1.String()} + + return +} diff --git a/vendor/github.com/d5/tengo/compiler/symbol_table.go b/vendor/github.com/d5/tengo/v2/symbol_table.go similarity index 82% rename from vendor/github.com/d5/tengo/compiler/symbol_table.go rename to vendor/github.com/d5/tengo/v2/symbol_table.go index 94c868de..6ae5d7d3 100644 --- a/vendor/github.com/d5/tengo/compiler/symbol_table.go +++ b/vendor/github.com/d5/tengo/v2/symbol_table.go @@ -1,4 +1,23 @@ -package compiler +package tengo + +// SymbolScope represents a symbol scope. +type SymbolScope string + +// List of symbol scopes +const ( + ScopeGlobal SymbolScope = "GLOBAL" + ScopeLocal SymbolScope = "LOCAL" + ScopeBuiltin SymbolScope = "BUILTIN" + ScopeFree SymbolScope = "FREE" +) + +// Symbol represents a symbol in the symbol table. +type Symbol struct { + Name string + Scope SymbolScope + Index int + LocalAssigned bool // if the local symbol is assigned at least once +} // SymbolTable represents a symbol table. type SymbolTable struct { @@ -28,11 +47,8 @@ func (t *SymbolTable) Define(name string) *Symbol { } else { symbol.Scope = ScopeLocal } - t.store[name] = symbol - t.updateMaxDefs(symbol.Index + 1) - return symbol } @@ -47,34 +63,32 @@ func (t *SymbolTable) DefineBuiltin(index int, name string) *Symbol { Index: index, Scope: ScopeBuiltin, } - t.store[name] = symbol - t.builtinSymbols = append(t.builtinSymbols, symbol) - return symbol } // Resolve resolves a symbol with a given name. -func (t *SymbolTable) Resolve(name string) (symbol *Symbol, depth int, ok bool) { +func (t *SymbolTable) Resolve( + name string, +) (symbol *Symbol, depth int, ok bool) { symbol, ok = t.store[name] if !ok && t.parent != nil { symbol, depth, ok = t.parent.Resolve(name) if !ok { return } - depth++ // if symbol is defined in parent table and if it's not global/builtin // then it's free variable. - if !t.block && depth > 0 && symbol.Scope != ScopeGlobal && symbol.Scope != ScopeBuiltin { + if !t.block && depth > 0 && + symbol.Scope != ScopeGlobal && + symbol.Scope != ScopeBuiltin { return t.defineFree(symbol), depth, true } - return } - return } @@ -92,7 +106,6 @@ func (t *SymbolTable) Parent(skipBlock bool) *SymbolTable { if skipBlock && t.block { return t.parent.Parent(skipBlock) } - return t.parent } @@ -111,7 +124,6 @@ func (t *SymbolTable) BuiltinSymbols() []*Symbol { if t.parent != nil { return t.parent.BuiltinSymbols() } - return t.builtinSymbols } @@ -128,7 +140,6 @@ func (t *SymbolTable) nextIndex() int { if t.block { return t.parent.nextIndex() + t.numDefinition } - return t.numDefinition } @@ -136,7 +147,6 @@ func (t *SymbolTable) updateMaxDefs(numDefs int) { if numDefs > t.maxDefinition { t.maxDefinition = numDefs } - if t.block { t.parent.updateMaxDefs(numDefs) } @@ -144,16 +154,12 @@ func (t *SymbolTable) updateMaxDefs(numDefs int) { func (t *SymbolTable) defineFree(original *Symbol) *Symbol { // TODO: should we check duplicates? - t.freeSymbols = append(t.freeSymbols, original) - symbol := &Symbol{ Name: original.Name, Index: len(t.freeSymbols) - 1, Scope: ScopeFree, } - t.store[original.Name] = symbol - return symbol } diff --git a/vendor/github.com/d5/tengo/objects/conversion.go b/vendor/github.com/d5/tengo/v2/tengo.go similarity index 76% rename from vendor/github.com/d5/tengo/objects/conversion.go rename to vendor/github.com/d5/tengo/v2/tengo.go index 27514132..098a1970 100644 --- a/vendor/github.com/d5/tengo/objects/conversion.go +++ b/vendor/github.com/d5/tengo/v2/tengo.go @@ -1,29 +1,75 @@ -package objects +package tengo import ( "errors" "fmt" "strconv" "time" - - "github.com/d5/tengo" ) +var ( + // MaxStringLen is the maximum byte-length for string value. Note this + // limit applies to all compiler/VM instances in the process. + MaxStringLen = 2147483647 + + // MaxBytesLen is the maximum length for bytes value. Note this limit + // applies to all compiler/VM instances in the process. + MaxBytesLen = 2147483647 +) + +const ( + // GlobalsSize is the maximum number of global variables for a VM. + GlobalsSize = 1024 + + // StackSize is the maximum stack size for a VM. + StackSize = 2048 + + // MaxFrames is the maximum number of function frames for a VM. + MaxFrames = 1024 +) + +// CallableFunc is a function signature for the callable functions. +type CallableFunc = func(args ...Object) (ret Object, err error) + +// CountObjects returns the number of objects that a given object o contains. +// For scalar value types, it will always be 1. For compound value types, +// this will include its elements and all of their elements recursively. +func CountObjects(o Object) (c int) { + c = 1 + switch o := o.(type) { + case *Array: + for _, v := range o.Value { + c += CountObjects(v) + } + case *ImmutableArray: + for _, v := range o.Value { + c += CountObjects(v) + } + case *Map: + for _, v := range o.Value { + c += CountObjects(v) + } + case *ImmutableMap: + for _, v := range o.Value { + c += CountObjects(v) + } + case *Error: + c += CountObjects(o.Value) + } + return +} + // ToString will try to convert object o to string value. func ToString(o Object) (v string, ok bool) { if o == UndefinedValue { - //ok = false return } - ok = true - if str, isStr := o.(*String); isStr { v = str.Value } else { v = o.String() } - return } @@ -51,8 +97,6 @@ func ToInt(o Object) (v int, ok bool) { ok = true } } - - //ok = false return } @@ -80,8 +124,6 @@ func ToInt64(o Object) (v int64, ok bool) { ok = true } } - - //ok = false return } @@ -101,8 +143,6 @@ func ToFloat64(o Object) (v float64, ok bool) { ok = true } } - - //ok = false return } @@ -110,7 +150,6 @@ func ToFloat64(o Object) (v float64, ok bool) { func ToBool(o Object) (v bool, ok bool) { ok = true v = !o.IsFalsy() - return } @@ -121,11 +160,9 @@ func ToRune(o Object) (v rune, ok bool) { v = rune(o.Value) ok = true case *Char: - v = rune(o.Value) + v = o.Value ok = true } - - //ok = false return } @@ -139,8 +176,6 @@ func ToByteSlice(o Object) (v []byte, ok bool) { v = []byte(o.Value) ok = true } - - //ok = false return } @@ -154,8 +189,6 @@ func ToTime(o Object) (v time.Time, ok bool) { v = time.Unix(o.Value, 0) ok = true } - - //ok = false return } @@ -203,7 +236,6 @@ func ToInterface(o Object) (res interface{}) { case Object: return o } - return } @@ -213,7 +245,7 @@ func FromInterface(v interface{}) (Object, error) { case nil: return UndefinedValue, nil case string: - if len(v) > tengo.MaxStringLen { + if len(v) > MaxStringLen { return nil, ErrStringLimit } return &String{Value: v}, nil @@ -233,7 +265,7 @@ func FromInterface(v interface{}) (Object, error) { case float64: return &Float{Value: v}, nil case []byte: - if len(v) > tengo.MaxBytesLen { + if len(v) > MaxBytesLen { return nil, ErrBytesLimit } return &Bytes{Value: v}, nil @@ -260,7 +292,6 @@ func FromInterface(v interface{}) (Object, error) { if err != nil { return nil, err } - arr[i] = vo } return &Array{Value: arr}, nil @@ -271,6 +302,5 @@ func FromInterface(v interface{}) (Object, error) { case CallableFunc: return &UserFunction{Value: v}, nil } - return nil, fmt.Errorf("cannot convert to object: %T", v) } diff --git a/vendor/github.com/d5/tengo/compiler/token/tokens.go b/vendor/github.com/d5/tengo/v2/token/token.go similarity index 91% rename from vendor/github.com/d5/tengo/compiler/token/tokens.go rename to vendor/github.com/d5/tengo/v2/token/token.go index b32d36ee..4e6aa80a 100644 --- a/vendor/github.com/d5/tengo/compiler/token/tokens.go +++ b/vendor/github.com/d5/tengo/v2/token/token.go @@ -2,6 +2,8 @@ package token import "strconv" +var keywords map[string]Token + // Token represents a token. type Token int @@ -206,3 +208,18 @@ func (tok Token) IsOperator() bool { func (tok Token) IsKeyword() bool { return _keywordBeg < tok && tok < _keywordEnd } + +// Lookup returns corresponding keyword if ident is a keyword. +func Lookup(ident string) Token { + if tok, isKeyword := keywords[ident]; isKeyword { + return tok + } + return Ident +} + +func init() { + keywords = make(map[string]Token) + for i := _keywordBeg + 1; i < _keywordEnd; i++ { + keywords[tokens[i]] = i + } +} diff --git a/vendor/github.com/d5/tengo/script/variable.go b/vendor/github.com/d5/tengo/v2/variable.go similarity index 51% rename from vendor/github.com/d5/tengo/script/variable.go rename to vendor/github.com/d5/tengo/v2/variable.go index df345115..481b36b8 100644 --- a/vendor/github.com/d5/tengo/script/variable.go +++ b/vendor/github.com/d5/tengo/v2/variable.go @@ -1,24 +1,21 @@ -package script +package tengo import ( "errors" - - "github.com/d5/tengo/objects" ) // Variable is a user-defined variable for the script. type Variable struct { name string - value objects.Object + value Object } // NewVariable creates a Variable. func NewVariable(name string, value interface{}) (*Variable, error) { - obj, err := objects.FromInterface(value) + obj, err := FromInterface(value) if err != nil { return nil, err } - return &Variable{ name: name, value: obj, @@ -32,7 +29,7 @@ func (v *Variable) Name() string { // Value returns an empty interface of the variable value. func (v *Variable) Value() interface{} { - return objects.ToInterface(v.value) + return ToInterface(v.value) } // ValueType returns the name of the value type. @@ -43,107 +40,97 @@ func (v *Variable) ValueType() string { // Int returns int value of the variable value. // It returns 0 if the value is not convertible to int. func (v *Variable) Int() int { - c, _ := objects.ToInt(v.value) - + c, _ := ToInt(v.value) return c } -// Int64 returns int64 value of the variable value. -// It returns 0 if the value is not convertible to int64. +// Int64 returns int64 value of the variable value. It returns 0 if the value +// is not convertible to int64. func (v *Variable) Int64() int64 { - c, _ := objects.ToInt64(v.value) - + c, _ := ToInt64(v.value) return c } -// Float returns float64 value of the variable value. -// It returns 0.0 if the value is not convertible to float64. +// Float returns float64 value of the variable value. It returns 0.0 if the +// value is not convertible to float64. func (v *Variable) Float() float64 { - c, _ := objects.ToFloat64(v.value) - + c, _ := ToFloat64(v.value) return c } -// Char returns rune value of the variable value. -// It returns 0 if the value is not convertible to rune. +// Char returns rune value of the variable value. It returns 0 if the value is +// not convertible to rune. func (v *Variable) Char() rune { - c, _ := objects.ToRune(v.value) - + c, _ := ToRune(v.value) return c } -// Bool returns bool value of the variable value. -// It returns 0 if the value is not convertible to bool. +// Bool returns bool value of the variable value. It returns 0 if the value is +// not convertible to bool. func (v *Variable) Bool() bool { - c, _ := objects.ToBool(v.value) - + c, _ := ToBool(v.value) return c } -// Array returns []interface value of the variable value. -// It returns 0 if the value is not convertible to []interface. +// Array returns []interface value of the variable value. It returns 0 if the +// value is not convertible to []interface. func (v *Variable) Array() []interface{} { switch val := v.value.(type) { - case *objects.Array: + case *Array: var arr []interface{} for _, e := range val.Value { - arr = append(arr, objects.ToInterface(e)) + arr = append(arr, ToInterface(e)) } return arr } - return nil } -// Map returns map[string]interface{} value of the variable value. -// It returns 0 if the value is not convertible to map[string]interface{}. +// Map returns map[string]interface{} value of the variable value. It returns +// 0 if the value is not convertible to map[string]interface{}. func (v *Variable) Map() map[string]interface{} { switch val := v.value.(type) { - case *objects.Map: + case *Map: kv := make(map[string]interface{}) for mk, mv := range val.Value { - kv[mk] = objects.ToInterface(mv) + kv[mk] = ToInterface(mv) } return kv } - return nil } -// String returns string value of the variable value. -// It returns 0 if the value is not convertible to string. +// String returns string value of the variable value. It returns 0 if the value +// is not convertible to string. func (v *Variable) String() string { - c, _ := objects.ToString(v.value) - + c, _ := ToString(v.value) return c } -// Bytes returns a byte slice of the variable value. -// It returns nil if the value is not convertible to byte slice. +// Bytes returns a byte slice of the variable value. It returns nil if the +// value is not convertible to byte slice. func (v *Variable) Bytes() []byte { - c, _ := objects.ToByteSlice(v.value) - + c, _ := ToByteSlice(v.value) return c } -// Error returns an error if the underlying value is error object. -// If not, this returns nil. +// Error returns an error if the underlying value is error object. If not, +// this returns nil. func (v *Variable) Error() error { - err, ok := v.value.(*objects.Error) + err, ok := v.value.(*Error) if ok { return errors.New(err.String()) } - return nil } -// Object returns an underlying Object of the variable value. -// Note that returned Object is a copy of an actual Object used in the script. -func (v *Variable) Object() objects.Object { +// Object returns an underlying Object of the variable value. Note that +// returned Object is a copy of an actual Object used in the script. +func (v *Variable) Object() Object { return v.value } // IsUndefined returns true if the underlying value is undefined. func (v *Variable) IsUndefined() bool { - return v.value == objects.UndefinedValue + return v.value == UndefinedValue } diff --git a/vendor/github.com/d5/tengo/runtime/vm.go b/vendor/github.com/d5/tengo/v2/vm.go similarity index 56% rename from vendor/github.com/d5/tengo/runtime/vm.go rename to vendor/github.com/d5/tengo/v2/vm.go index 07f6e530..783a54a9 100644 --- a/vendor/github.com/d5/tengo/runtime/vm.go +++ b/vendor/github.com/d5/tengo/v2/vm.go @@ -1,36 +1,31 @@ -package runtime +package tengo import ( "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/v2/parser" + "github.com/d5/tengo/v2/token" ) -const ( - // StackSize is the maximum stack size. - StackSize = 2048 - - // GlobalsSize is the maximum number of global variables. - GlobalsSize = 1024 - - // MaxFrames is the maximum number of function frames. - MaxFrames = 1024 -) +// frame represents a function call frame. +type frame struct { + fn *CompiledFunction + freeVars []*ObjectPtr + ip int + basePointer int +} // VM is a virtual machine that executes the bytecode compiled by Compiler. type VM struct { - constants []objects.Object - stack [StackSize]objects.Object + constants []Object + stack [StackSize]Object sp int - globals []objects.Object - fileSet *source.FileSet - frames [MaxFrames]Frame + globals []Object + fileSet *parser.SourceFileSet + frames [MaxFrames]frame framesIndex int - curFrame *Frame + curFrame *frame curInsts []byte ip int aborting int64 @@ -40,11 +35,14 @@ type VM struct { } // NewVM creates a VM. -func NewVM(bytecode *compiler.Bytecode, globals []objects.Object, maxAllocs int64) *VM { +func NewVM( + bytecode *Bytecode, + globals []Object, + maxAllocs int64, +) *VM { if globals == nil { - globals = make([]objects.Object, GlobalsSize) + globals = make([]Object, GlobalsSize) } - v := &VM{ constants: bytecode.Constants, sp: 0, @@ -54,12 +52,10 @@ func NewVM(bytecode *compiler.Bytecode, globals []objects.Object, maxAllocs int6 ip: -1, maxAllocs: maxAllocs, } - v.frames[0].fn = bytecode.MainFunction v.frames[0].ip = -1 v.curFrame = &v.frames[0] v.curInsts = v.curFrame.fn.Instructions - return v } @@ -79,75 +75,52 @@ func (v *VM) Run() (err error) { v.allocs = v.maxAllocs + 1 v.run() - atomic.StoreInt64(&v.aborting, 0) - err = v.err if err != nil { - filePos := v.fileSet.Position(v.curFrame.fn.SourcePos(v.ip - 1)) - err = fmt.Errorf("Runtime Error: %s\n\tat %s", err.Error(), filePos) + filePos := v.fileSet.Position( + v.curFrame.fn.SourcePos(v.ip - 1)) + err = fmt.Errorf("Runtime Error: %s\n\tat %s", + err.Error(), filePos) for v.framesIndex > 1 { v.framesIndex-- v.curFrame = &v.frames[v.framesIndex-1] - - filePos = v.fileSet.Position(v.curFrame.fn.SourcePos(v.curFrame.ip - 1)) + filePos = v.fileSet.Position( + v.curFrame.fn.SourcePos(v.curFrame.ip - 1)) err = fmt.Errorf("%s\n\tat %s", err.Error(), filePos) } return err } - return nil } func (v *VM) run() { - defer func() { - if r := recover(); r != nil { - if v.sp >= StackSize || v.framesIndex >= MaxFrames { - v.err = ErrStackOverflow - return - } - - if v.ip < len(v.curInsts)-1 { - if err, ok := r.(error); ok { - v.err = err - } else { - v.err = fmt.Errorf("panic: %v", r) - } - } - } - }() - for atomic.LoadInt64(&v.aborting) == 0 { v.ip++ switch v.curInsts[v.ip] { - case compiler.OpConstant: + case parser.OpConstant: v.ip += 2 cidx := int(v.curInsts[v.ip]) | int(v.curInsts[v.ip-1])<<8 v.stack[v.sp] = v.constants[cidx] v.sp++ - - case compiler.OpNull: - v.stack[v.sp] = objects.UndefinedValue + case parser.OpNull: + v.stack[v.sp] = UndefinedValue v.sp++ - - case compiler.OpBinaryOp: + case parser.OpBinaryOp: v.ip++ right := v.stack[v.sp-1] left := v.stack[v.sp-2] - tok := token.Token(v.curInsts[v.ip]) res, e := left.BinaryOp(tok, right) if e != nil { v.sp -= 2 - - if e == objects.ErrInvalidOperator { + if e == ErrInvalidOperator { v.err = fmt.Errorf("invalid operation: %s %s %s", left.TypeName(), tok.String(), right.TypeName()) return } - v.err = e return } @@ -160,185 +133,155 @@ func (v *VM) run() { v.stack[v.sp-2] = res v.sp-- - - case compiler.OpEqual: + case parser.OpEqual: right := v.stack[v.sp-1] left := v.stack[v.sp-2] v.sp -= 2 - if left.Equals(right) { - v.stack[v.sp] = objects.TrueValue + v.stack[v.sp] = TrueValue } else { - v.stack[v.sp] = objects.FalseValue + v.stack[v.sp] = FalseValue } v.sp++ - - case compiler.OpNotEqual: + case parser.OpNotEqual: right := v.stack[v.sp-1] left := v.stack[v.sp-2] v.sp -= 2 - if left.Equals(right) { - v.stack[v.sp] = objects.FalseValue + v.stack[v.sp] = FalseValue } else { - v.stack[v.sp] = objects.TrueValue + v.stack[v.sp] = TrueValue } v.sp++ - - case compiler.OpPop: + case parser.OpPop: v.sp-- - - case compiler.OpTrue: - v.stack[v.sp] = objects.TrueValue + case parser.OpTrue: + v.stack[v.sp] = TrueValue v.sp++ - - case compiler.OpFalse: - v.stack[v.sp] = objects.FalseValue + case parser.OpFalse: + v.stack[v.sp] = FalseValue v.sp++ - - case compiler.OpLNot: + case parser.OpLNot: operand := v.stack[v.sp-1] v.sp-- - if operand.IsFalsy() { - v.stack[v.sp] = objects.TrueValue + v.stack[v.sp] = TrueValue } else { - v.stack[v.sp] = objects.FalseValue + v.stack[v.sp] = FalseValue } v.sp++ - - case compiler.OpBComplement: + case parser.OpBComplement: operand := v.stack[v.sp-1] v.sp-- switch x := operand.(type) { - case *objects.Int: - var res objects.Object = &objects.Int{Value: ^x.Value} - + case *Int: + var res Object = &Int{Value: ^x.Value} v.allocs-- if v.allocs == 0 { v.err = ErrObjectAllocLimit return } - v.stack[v.sp] = res v.sp++ default: - v.err = fmt.Errorf("invalid operation: ^%s", operand.TypeName()) + v.err = fmt.Errorf("invalid operation: ^%s", + operand.TypeName()) return } - - case compiler.OpMinus: + case parser.OpMinus: operand := v.stack[v.sp-1] v.sp-- switch x := operand.(type) { - case *objects.Int: - var res objects.Object = &objects.Int{Value: -x.Value} - + case *Int: + var res Object = &Int{Value: -x.Value} v.allocs-- if v.allocs == 0 { v.err = ErrObjectAllocLimit return } - v.stack[v.sp] = res v.sp++ - case *objects.Float: - var res objects.Object = &objects.Float{Value: -x.Value} - + case *Float: + var res Object = &Float{Value: -x.Value} v.allocs-- if v.allocs == 0 { v.err = ErrObjectAllocLimit return } - v.stack[v.sp] = res v.sp++ default: - v.err = fmt.Errorf("invalid operation: -%s", operand.TypeName()) + v.err = fmt.Errorf("invalid operation: -%s", + operand.TypeName()) return } - - case compiler.OpJumpFalsy: + case parser.OpJumpFalsy: v.ip += 2 v.sp-- if v.stack[v.sp].IsFalsy() { pos := int(v.curInsts[v.ip]) | int(v.curInsts[v.ip-1])<<8 v.ip = pos - 1 } - - case compiler.OpAndJump: + case parser.OpAndJump: v.ip += 2 - if v.stack[v.sp-1].IsFalsy() { pos := int(v.curInsts[v.ip]) | int(v.curInsts[v.ip-1])<<8 v.ip = pos - 1 } else { v.sp-- } - - case compiler.OpOrJump: + case parser.OpOrJump: v.ip += 2 - if v.stack[v.sp-1].IsFalsy() { v.sp-- } else { pos := int(v.curInsts[v.ip]) | int(v.curInsts[v.ip-1])<<8 v.ip = pos - 1 } - - case compiler.OpJump: + case parser.OpJump: pos := int(v.curInsts[v.ip+2]) | int(v.curInsts[v.ip+1])<<8 v.ip = pos - 1 - - case compiler.OpSetGlobal: + case parser.OpSetGlobal: v.ip += 2 v.sp-- - globalIndex := int(v.curInsts[v.ip]) | int(v.curInsts[v.ip-1])<<8 v.globals[globalIndex] = v.stack[v.sp] - - case compiler.OpSetSelGlobal: + case parser.OpSetSelGlobal: v.ip += 3 globalIndex := int(v.curInsts[v.ip-1]) | int(v.curInsts[v.ip-2])<<8 numSelectors := int(v.curInsts[v.ip]) // selectors and RHS value - selectors := make([]objects.Object, numSelectors) + selectors := make([]Object, numSelectors) for i := 0; i < numSelectors; i++ { selectors[i] = v.stack[v.sp-numSelectors+i] } - val := v.stack[v.sp-numSelectors-1] v.sp -= numSelectors + 1 - - if e := indexAssign(v.globals[globalIndex], val, selectors); e != nil { + e := indexAssign(v.globals[globalIndex], val, selectors) + if e != nil { v.err = e return } - - case compiler.OpGetGlobal: + case parser.OpGetGlobal: v.ip += 2 globalIndex := int(v.curInsts[v.ip]) | int(v.curInsts[v.ip-1])<<8 - val := v.globals[globalIndex] - v.stack[v.sp] = val v.sp++ - - case compiler.OpArray: + case parser.OpArray: v.ip += 2 numElements := int(v.curInsts[v.ip]) | int(v.curInsts[v.ip-1])<<8 - var elements []objects.Object + var elements []Object for i := v.sp - numElements; i < v.sp; i++ { elements = append(elements, v.stack[i]) } v.sp -= numElements - var arr objects.Object = &objects.Array{Value: elements} - + var arr Object = &Array{Value: elements} v.allocs-- if v.allocs == 0 { v.err = ErrObjectAllocLimit @@ -347,355 +290,260 @@ func (v *VM) run() { v.stack[v.sp] = arr v.sp++ - - case compiler.OpMap: + case parser.OpMap: v.ip += 2 numElements := int(v.curInsts[v.ip]) | int(v.curInsts[v.ip-1])<<8 - - kv := make(map[string]objects.Object) + kv := make(map[string]Object) for i := v.sp - numElements; i < v.sp; i += 2 { key := v.stack[i] value := v.stack[i+1] - kv[key.(*objects.String).Value] = value + kv[key.(*String).Value] = value } v.sp -= numElements - var m objects.Object = &objects.Map{Value: kv} - + var m Object = &Map{Value: kv} v.allocs-- if v.allocs == 0 { v.err = ErrObjectAllocLimit return } - v.stack[v.sp] = m v.sp++ - - case compiler.OpError: + case parser.OpError: value := v.stack[v.sp-1] - - var e objects.Object = &objects.Error{ + var e Object = &Error{ Value: value, } - v.allocs-- if v.allocs == 0 { v.err = ErrObjectAllocLimit return } - v.stack[v.sp-1] = e - - case compiler.OpImmutable: + case parser.OpImmutable: value := v.stack[v.sp-1] - switch value := value.(type) { - case *objects.Array: - var immutableArray objects.Object = &objects.ImmutableArray{ + case *Array: + var immutableArray Object = &ImmutableArray{ Value: value.Value, } - v.allocs-- if v.allocs == 0 { v.err = ErrObjectAllocLimit return } - v.stack[v.sp-1] = immutableArray - case *objects.Map: - var immutableMap objects.Object = &objects.ImmutableMap{ + case *Map: + var immutableMap Object = &ImmutableMap{ Value: value.Value, } - v.allocs-- if v.allocs == 0 { v.err = ErrObjectAllocLimit return } - v.stack[v.sp-1] = immutableMap } - - case compiler.OpIndex: + case parser.OpIndex: index := v.stack[v.sp-1] left := v.stack[v.sp-2] v.sp -= 2 - switch left := left.(type) { - case objects.Indexable: - val, e := left.IndexGet(index) - if e != nil { - - if e == objects.ErrInvalidIndexType { - v.err = fmt.Errorf("invalid index type: %s", index.TypeName()) - return - } - - v.err = e + val, err := left.IndexGet(index) + if err != nil { + if err == ErrNotIndexable { + v.err = fmt.Errorf("not indexable: %s", index.TypeName()) return } - if val == nil { - val = objects.UndefinedValue - } - - v.stack[v.sp] = val - v.sp++ - - case *objects.Error: // e.value - key, ok := index.(*objects.String) - if !ok || key.Value != "value" { - v.err = fmt.Errorf("invalid index on error") + if err == ErrInvalidIndexType { + v.err = fmt.Errorf("invalid index type: %s", + index.TypeName()) return } - - v.stack[v.sp] = left.Value - v.sp++ - - default: - v.err = fmt.Errorf("not indexable: %s", left.TypeName()) + v.err = err return } - - case compiler.OpSliceIndex: + if val == nil { + val = UndefinedValue + } + v.stack[v.sp] = val + v.sp++ + case parser.OpSliceIndex: high := v.stack[v.sp-1] low := v.stack[v.sp-2] left := v.stack[v.sp-3] v.sp -= 3 var lowIdx int64 - if low != objects.UndefinedValue { - if low, ok := low.(*objects.Int); ok { + if low != UndefinedValue { + if low, ok := low.(*Int); ok { lowIdx = low.Value } else { - v.err = fmt.Errorf("invalid slice index type: %s", low.TypeName()) + v.err = fmt.Errorf("invalid slice index type: %s", + low.TypeName()) return } } switch left := left.(type) { - case *objects.Array: + case *Array: numElements := int64(len(left.Value)) var highIdx int64 - if high == objects.UndefinedValue { + if high == UndefinedValue { highIdx = numElements - } else if high, ok := high.(*objects.Int); ok { + } else if high, ok := high.(*Int); ok { highIdx = high.Value } else { - v.err = fmt.Errorf("invalid slice index type: %s", high.TypeName()) + v.err = fmt.Errorf("invalid slice index type: %s", + high.TypeName()) return } - if lowIdx > highIdx { - v.err = fmt.Errorf("invalid slice index: %d > %d", lowIdx, highIdx) + v.err = fmt.Errorf("invalid slice index: %d > %d", + lowIdx, highIdx) return } - if lowIdx < 0 { lowIdx = 0 } else if lowIdx > numElements { lowIdx = numElements } - if highIdx < 0 { highIdx = 0 } else if highIdx > numElements { highIdx = numElements } - - var val objects.Object = &objects.Array{Value: left.Value[lowIdx:highIdx]} - + var val Object = &Array{ + Value: left.Value[lowIdx:highIdx], + } v.allocs-- if v.allocs == 0 { v.err = ErrObjectAllocLimit return } - v.stack[v.sp] = val v.sp++ - - case *objects.ImmutableArray: + case *ImmutableArray: numElements := int64(len(left.Value)) var highIdx int64 - if high == objects.UndefinedValue { + if high == UndefinedValue { highIdx = numElements - } else if high, ok := high.(*objects.Int); ok { + } else if high, ok := high.(*Int); ok { highIdx = high.Value } else { - v.err = fmt.Errorf("invalid slice index type: %s", high.TypeName()) + v.err = fmt.Errorf("invalid slice index type: %s", + high.TypeName()) return } - if lowIdx > highIdx { - v.err = fmt.Errorf("invalid slice index: %d > %d", lowIdx, highIdx) + v.err = fmt.Errorf("invalid slice index: %d > %d", + lowIdx, highIdx) return } - if lowIdx < 0 { lowIdx = 0 } else if lowIdx > numElements { lowIdx = numElements } - if highIdx < 0 { highIdx = 0 } else if highIdx > numElements { highIdx = numElements } - - var val objects.Object = &objects.Array{Value: left.Value[lowIdx:highIdx]} - + var val Object = &Array{ + Value: left.Value[lowIdx:highIdx], + } v.allocs-- if v.allocs == 0 { v.err = ErrObjectAllocLimit return } - v.stack[v.sp] = val v.sp++ - - case *objects.String: + case *String: numElements := int64(len(left.Value)) var highIdx int64 - if high == objects.UndefinedValue { + if high == UndefinedValue { highIdx = numElements - } else if high, ok := high.(*objects.Int); ok { + } else if high, ok := high.(*Int); ok { highIdx = high.Value } else { - v.err = fmt.Errorf("invalid slice index type: %s", high.TypeName()) + v.err = fmt.Errorf("invalid slice index type: %s", + high.TypeName()) return } - if lowIdx > highIdx { - v.err = fmt.Errorf("invalid slice index: %d > %d", lowIdx, highIdx) + v.err = fmt.Errorf("invalid slice index: %d > %d", + lowIdx, highIdx) return } - if lowIdx < 0 { lowIdx = 0 } else if lowIdx > numElements { lowIdx = numElements } - if highIdx < 0 { highIdx = 0 } else if highIdx > numElements { highIdx = numElements } - - var val objects.Object = &objects.String{Value: left.Value[lowIdx:highIdx]} - + var val Object = &String{ + Value: left.Value[lowIdx:highIdx], + } v.allocs-- if v.allocs == 0 { v.err = ErrObjectAllocLimit return } - v.stack[v.sp] = val v.sp++ - - case *objects.Bytes: + case *Bytes: numElements := int64(len(left.Value)) var highIdx int64 - if high == objects.UndefinedValue { + if high == UndefinedValue { highIdx = numElements - } else if high, ok := high.(*objects.Int); ok { + } else if high, ok := high.(*Int); ok { highIdx = high.Value } else { - v.err = fmt.Errorf("invalid slice index type: %s", high.TypeName()) + v.err = fmt.Errorf("invalid slice index type: %s", + high.TypeName()) return } - if lowIdx > highIdx { - v.err = fmt.Errorf("invalid slice index: %d > %d", lowIdx, highIdx) + v.err = fmt.Errorf("invalid slice index: %d > %d", + lowIdx, highIdx) return } - if lowIdx < 0 { lowIdx = 0 } else if lowIdx > numElements { lowIdx = numElements } - if highIdx < 0 { highIdx = 0 } else if highIdx > numElements { highIdx = numElements } - - var val objects.Object = &objects.Bytes{Value: left.Value[lowIdx:highIdx]} - + var val Object = &Bytes{ + Value: left.Value[lowIdx:highIdx], + } v.allocs-- if v.allocs == 0 { v.err = ErrObjectAllocLimit return } - v.stack[v.sp] = val v.sp++ } - - case compiler.OpCall: + case parser.OpCall: numArgs := int(v.curInsts[v.ip+1]) v.ip++ - value := v.stack[v.sp-1-numArgs] - - switch callee := value.(type) { - case *objects.Closure: - if callee.Fn.VarArgs { - // if the closure is variadic, - // roll up all variadic parameters into an array - realArgs := callee.Fn.NumParameters - 1 - varArgs := numArgs - realArgs - if varArgs >= 0 { - numArgs = realArgs + 1 - args := make([]objects.Object, varArgs) - spStart := v.sp - varArgs - for i := spStart; i < v.sp; i++ { - args[i-spStart] = v.stack[i] - } - v.stack[spStart] = &objects.Array{Value: args} - v.sp = spStart + 1 - } - } - - if numArgs != callee.Fn.NumParameters { - if callee.Fn.VarArgs { - v.err = fmt.Errorf("wrong number of arguments: want>=%d, got=%d", - callee.Fn.NumParameters-1, numArgs) - } else { - v.err = fmt.Errorf("wrong number of arguments: want=%d, got=%d", - callee.Fn.NumParameters, numArgs) - } - return - } - - // test if it's tail-call - if callee.Fn == v.curFrame.fn { // recursion - nextOp := v.curInsts[v.ip+1] - if nextOp == compiler.OpReturn || - (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 - } - } - - // 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.framesIndex++ - v.sp = v.sp - numArgs + callee.Fn.NumLocals - - case *objects.CompiledFunction: + if !value.CanCall() { + v.err = fmt.Errorf("not callable: %s", value.TypeName()) + return + } + if callee, ok := value.(*CompiledFunction); ok { if callee.VarArgs { // if the closure is variadic, // roll up all variadic parameters into an array @@ -703,22 +551,23 @@ func (v *VM) run() { varArgs := numArgs - realArgs if varArgs >= 0 { numArgs = realArgs + 1 - args := make([]objects.Object, varArgs) + args := make([]Object, varArgs) spStart := v.sp - varArgs for i := spStart; i < v.sp; i++ { args[i-spStart] = v.stack[i] } - v.stack[spStart] = &objects.Array{Value: args} + v.stack[spStart] = &Array{Value: args} v.sp = spStart + 1 } } - if numArgs != callee.NumParameters { if callee.VarArgs { - v.err = fmt.Errorf("wrong number of arguments: want>=%d, got=%d", + v.err = fmt.Errorf( + "wrong number of arguments: want>=%d, got=%d", callee.NumParameters-1, numArgs) } else { - v.err = fmt.Errorf("wrong number of arguments: want=%d, got=%d", + v.err = fmt.Errorf( + "wrong number of arguments: want=%d, got=%d", callee.NumParameters, numArgs) } return @@ -727,321 +576,271 @@ func (v *VM) run() { // test if it's tail-call if callee == v.curFrame.fn { // recursion nextOp := v.curInsts[v.ip+1] - if nextOp == compiler.OpReturn || - (nextOp == compiler.OpPop && compiler.OpReturn == v.curInsts[v.ip+2]) { + if nextOp == parser.OpReturn || + (nextOp == parser.OpPop && + parser.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.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 } } + if v.framesIndex >= MaxFrames { + v.err = ErrStackOverflow + return + } // 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.freeVars = callee.Free v.curFrame.basePointer = v.sp - numArgs v.curInsts = callee.Instructions v.ip = -1 v.framesIndex++ v.sp = v.sp - numArgs + callee.NumLocals - - case objects.Callable: - var args []objects.Object + } else { + var args []Object args = append(args, v.stack[v.sp-numArgs:v.sp]...) - - ret, e := callee.Call(args...) + ret, e := value.Call(args...) v.sp -= numArgs + 1 // runtime error if e != nil { - if e == objects.ErrWrongNumArguments { - v.err = fmt.Errorf("wrong number of arguments in call to '%s'", + if e == ErrWrongNumArguments { + v.err = fmt.Errorf( + "wrong number of arguments in call to '%s'", value.TypeName()) return } - - if e, ok := e.(objects.ErrInvalidArgumentType); ok { - v.err = fmt.Errorf("invalid type for argument '%s' in call to '%s': expected %s, found %s", + if e, ok := e.(ErrInvalidArgumentType); ok { + v.err = fmt.Errorf( + "invalid type for argument '%s' in call to '%s': "+ + "expected %s, found %s", e.Name, value.TypeName(), e.Expected, e.Found) return } - v.err = e return } // nil return -> undefined if ret == nil { - ret = objects.UndefinedValue + ret = UndefinedValue } - v.allocs-- if v.allocs == 0 { v.err = ErrObjectAllocLimit return } - v.stack[v.sp] = ret v.sp++ - - default: - v.err = fmt.Errorf("not callable: %s", callee.TypeName()) - return } - - case compiler.OpReturn: + case parser.OpReturn: v.ip++ - var retVal objects.Object + var retVal Object if int(v.curInsts[v.ip]) == 1 { retVal = v.stack[v.sp-1] } else { - retVal = objects.UndefinedValue + retVal = UndefinedValue } //v.sp-- - v.framesIndex-- v.curFrame = &v.frames[v.framesIndex-1] v.curInsts = v.curFrame.fn.Instructions v.ip = v.curFrame.ip - //v.sp = lastFrame.basePointer - 1 v.sp = v.frames[v.framesIndex].basePointer - // skip stack overflow check because (newSP) <= (oldSP) v.stack[v.sp-1] = retVal //v.sp++ - - case compiler.OpDefineLocal: + case parser.OpDefineLocal: v.ip++ localIndex := int(v.curInsts[v.ip]) - sp := v.curFrame.basePointer + localIndex // local variables can be mutated by other actions // so always store the copy of popped value val := v.stack[v.sp-1] v.sp-- - v.stack[sp] = val - - case compiler.OpSetLocal: + case parser.OpSetLocal: localIndex := int(v.curInsts[v.ip+1]) v.ip++ - sp := v.curFrame.basePointer + localIndex - // update pointee of v.stack[sp] instead of replacing the pointer itself. - // this is needed because there can be free variables referencing the same local variables. + // update pointee of v.stack[sp] instead of replacing the pointer + // itself. this is needed because there can be free variables + // referencing the same local variables. val := v.stack[v.sp-1] v.sp-- - - if obj, ok := v.stack[sp].(*objects.ObjectPtr); ok { + if obj, ok := v.stack[sp].(*ObjectPtr); ok { *obj.Value = val val = obj } v.stack[sp] = val // also use a copy of popped value - - case compiler.OpSetSelLocal: + case parser.OpSetSelLocal: localIndex := int(v.curInsts[v.ip+1]) numSelectors := int(v.curInsts[v.ip+2]) v.ip += 2 // selectors and RHS value - selectors := make([]objects.Object, numSelectors) + selectors := make([]Object, numSelectors) for i := 0; i < numSelectors; i++ { selectors[i] = v.stack[v.sp-numSelectors+i] } - val := v.stack[v.sp-numSelectors-1] v.sp -= numSelectors + 1 - dst := v.stack[v.curFrame.basePointer+localIndex] - if obj, ok := dst.(*objects.ObjectPtr); ok { + if obj, ok := dst.(*ObjectPtr); ok { dst = *obj.Value } - if e := indexAssign(dst, val, selectors); e != nil { v.err = e return } - - case compiler.OpGetLocal: + case parser.OpGetLocal: v.ip++ localIndex := int(v.curInsts[v.ip]) - val := v.stack[v.curFrame.basePointer+localIndex] - - if obj, ok := val.(*objects.ObjectPtr); ok { + if obj, ok := val.(*ObjectPtr); ok { val = *obj.Value } - v.stack[v.sp] = val v.sp++ - - case compiler.OpGetBuiltin: + case parser.OpGetBuiltin: v.ip++ builtinIndex := int(v.curInsts[v.ip]) - - v.stack[v.sp] = objects.Builtins[builtinIndex] + v.stack[v.sp] = builtinFuncs[builtinIndex] v.sp++ - - case compiler.OpClosure: + case parser.OpClosure: v.ip += 3 constIndex := int(v.curInsts[v.ip-1]) | int(v.curInsts[v.ip-2])<<8 numFree := int(v.curInsts[v.ip]) - - fn, ok := v.constants[constIndex].(*objects.CompiledFunction) + fn, ok := v.constants[constIndex].(*CompiledFunction) if !ok { v.err = fmt.Errorf("not function: %s", fn.TypeName()) return } - - free := make([]*objects.ObjectPtr, numFree) + free := make([]*ObjectPtr, numFree) for i := 0; i < numFree; i++ { switch freeVar := (v.stack[v.sp-numFree+i]).(type) { - case *objects.ObjectPtr: + case *ObjectPtr: free[i] = freeVar default: - free[i] = &objects.ObjectPtr{Value: &v.stack[v.sp-numFree+i]} + free[i] = &ObjectPtr{ + Value: &v.stack[v.sp-numFree+i], + } } } - v.sp -= numFree - - var cl = &objects.Closure{ - Fn: fn, - Free: free, + cl := &CompiledFunction{ + Instructions: fn.Instructions, + NumLocals: fn.NumLocals, + NumParameters: fn.NumParameters, + VarArgs: fn.VarArgs, + Free: free, } - v.allocs-- if v.allocs == 0 { v.err = ErrObjectAllocLimit return } - v.stack[v.sp] = cl v.sp++ - - case compiler.OpGetFreePtr: + case parser.OpGetFreePtr: v.ip++ freeIndex := int(v.curInsts[v.ip]) - val := v.curFrame.freeVars[freeIndex] - v.stack[v.sp] = val v.sp++ - - case compiler.OpGetFree: + case parser.OpGetFree: v.ip++ freeIndex := int(v.curInsts[v.ip]) - val := *v.curFrame.freeVars[freeIndex].Value - v.stack[v.sp] = val v.sp++ - - case compiler.OpSetFree: + case parser.OpSetFree: v.ip++ freeIndex := int(v.curInsts[v.ip]) - *v.curFrame.freeVars[freeIndex].Value = v.stack[v.sp-1] - v.sp-- - - case compiler.OpGetLocalPtr: + case parser.OpGetLocalPtr: v.ip++ localIndex := int(v.curInsts[v.ip]) - sp := v.curFrame.basePointer + localIndex val := v.stack[sp] - - var freeVar *objects.ObjectPtr - if obj, ok := val.(*objects.ObjectPtr); ok { + var freeVar *ObjectPtr + if obj, ok := val.(*ObjectPtr); ok { freeVar = obj } else { - freeVar = &objects.ObjectPtr{Value: &val} + freeVar = &ObjectPtr{Value: &val} v.stack[sp] = freeVar } - v.stack[v.sp] = freeVar v.sp++ - - case compiler.OpSetSelFree: + case parser.OpSetSelFree: v.ip += 2 freeIndex := int(v.curInsts[v.ip-1]) numSelectors := int(v.curInsts[v.ip]) // selectors and RHS value - selectors := make([]objects.Object, numSelectors) + selectors := make([]Object, numSelectors) for i := 0; i < numSelectors; i++ { selectors[i] = v.stack[v.sp-numSelectors+i] } val := v.stack[v.sp-numSelectors-1] v.sp -= numSelectors + 1 - - if e := indexAssign(*v.curFrame.freeVars[freeIndex].Value, val, selectors); e != nil { + e := indexAssign(*v.curFrame.freeVars[freeIndex].Value, + val, selectors) + if e != nil { v.err = e return } - - case compiler.OpIteratorInit: - var iterator objects.Object - + case parser.OpIteratorInit: + var iterator Object dst := v.stack[v.sp-1] v.sp-- - - iterable, ok := dst.(objects.Iterable) - if !ok { + if !dst.CanIterate() { v.err = fmt.Errorf("not iterable: %s", dst.TypeName()) return } - - iterator = iterable.Iterate() - + iterator = dst.Iterate() v.allocs-- if v.allocs == 0 { v.err = ErrObjectAllocLimit return } - v.stack[v.sp] = iterator v.sp++ - - case compiler.OpIteratorNext: + case parser.OpIteratorNext: iterator := v.stack[v.sp-1] v.sp-- - - hasMore := iterator.(objects.Iterator).Next() - + hasMore := iterator.(Iterator).Next() if hasMore { - v.stack[v.sp] = objects.TrueValue + v.stack[v.sp] = TrueValue } else { - v.stack[v.sp] = objects.FalseValue + v.stack[v.sp] = FalseValue } v.sp++ - - case compiler.OpIteratorKey: + case parser.OpIteratorKey: iterator := v.stack[v.sp-1] v.sp-- - - val := iterator.(objects.Iterator).Key() - + val := iterator.(Iterator).Key() v.stack[v.sp] = val v.sp++ - - case compiler.OpIteratorValue: + case parser.OpIteratorValue: iterator := v.stack[v.sp-1] v.sp-- - - val := iterator.(objects.Iterator).Value() - + val := iterator.(Iterator).Value() v.stack[v.sp] = val v.sp++ - + case parser.OpSuspend: + return default: v.err = fmt.Errorf("unknown opcode: %d", v.curInsts[v.ip]) return @@ -1054,39 +853,31 @@ func (v *VM) IsStackEmpty() bool { return v.sp == 0 } -func indexAssign(dst, src objects.Object, selectors []objects.Object) error { +func indexAssign(dst, src Object, selectors []Object) error { numSel := len(selectors) - for sidx := numSel - 1; sidx > 0; sidx-- { - indexable, ok := dst.(objects.Indexable) - if !ok { - return fmt.Errorf("not indexable: %s", dst.TypeName()) - } - - next, err := indexable.IndexGet(selectors[sidx]) + next, err := dst.IndexGet(selectors[sidx]) if err != nil { - if err == objects.ErrInvalidIndexType { - return fmt.Errorf("invalid index type: %s", selectors[sidx].TypeName()) + if err == ErrNotIndexable { + return fmt.Errorf("not indexable: %s", dst.TypeName()) + } + if err == ErrInvalidIndexType { + return fmt.Errorf("invalid index type: %s", + selectors[sidx].TypeName()) } - return err } - dst = next } - indexAssignable, ok := dst.(objects.IndexAssignable) - if !ok { - return fmt.Errorf("not index-assignable: %s", dst.TypeName()) - } - - if err := indexAssignable.IndexSet(selectors[0], src); err != nil { - if err == objects.ErrInvalidIndexValueType { + if err := dst.IndexSet(selectors[0], src); err != nil { + if err == ErrNotIndexAssignable { + return fmt.Errorf("not index-assignable: %s", dst.TypeName()) + } + if err == ErrInvalidIndexValueType { return fmt.Errorf("invaid index value type: %s", src.TypeName()) } - return err } - return nil } diff --git a/vendor/modules.txt b/vendor/modules.txt index 22bb538c..32b59b59 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -25,19 +25,12 @@ github.com/Rhymen/go-whatsapp/crypto/curve25519 github.com/Rhymen/go-whatsapp/crypto/hkdf # github.com/bwmarrin/discordgo v0.20.2 => github.com/matterbridge/discordgo v0.18.1-0.20200109173909-ed873362fa43 github.com/bwmarrin/discordgo -# github.com/d5/tengo v1.24.8 -github.com/d5/tengo -github.com/d5/tengo/compiler -github.com/d5/tengo/compiler/ast -github.com/d5/tengo/compiler/parser -github.com/d5/tengo/compiler/scanner -github.com/d5/tengo/compiler/source -github.com/d5/tengo/compiler/token -github.com/d5/tengo/objects -github.com/d5/tengo/runtime -github.com/d5/tengo/script -github.com/d5/tengo/stdlib -github.com/d5/tengo/stdlib/json +# github.com/d5/tengo/v2 v2.0.2 +github.com/d5/tengo/v2 +github.com/d5/tengo/v2/parser +github.com/d5/tengo/v2/stdlib +github.com/d5/tengo/v2/stdlib/json +github.com/d5/tengo/v2/token # github.com/davecgh/go-spew v1.1.1 github.com/davecgh/go-spew/spew # github.com/dfordsoft/golib v0.0.0-20180902042739-76ee6ab99bec