summaryrefslogtreecommitdiffstats
path: root/vendor/github.com/d5/tengo
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/github.com/d5/tengo')
-rw-r--r--vendor/github.com/d5/tengo/README.md1
-rw-r--r--vendor/github.com/d5/tengo/compiler/ast/ident_list.go15
-rw-r--r--vendor/github.com/d5/tengo/compiler/compiler.go1
-rw-r--r--vendor/github.com/d5/tengo/compiler/instructions.go2
-rw-r--r--vendor/github.com/d5/tengo/compiler/parser/parser.go20
-rw-r--r--vendor/github.com/d5/tengo/compiler/symbol_scopes.go6
-rw-r--r--vendor/github.com/d5/tengo/objects/builtin_format.go27
-rw-r--r--vendor/github.com/d5/tengo/objects/builtins.go4
-rw-r--r--vendor/github.com/d5/tengo/objects/bytes.go2
-rw-r--r--vendor/github.com/d5/tengo/objects/compiled_function.go2
-rw-r--r--vendor/github.com/d5/tengo/objects/conversion.go2
-rw-r--r--vendor/github.com/d5/tengo/objects/formatter.go1212
-rw-r--r--vendor/github.com/d5/tengo/objects/map.go4
-rw-r--r--vendor/github.com/d5/tengo/objects/undefined.go20
-rw-r--r--vendor/github.com/d5/tengo/runtime/vm.go63
-rw-r--r--vendor/github.com/d5/tengo/script/script.go9
-rw-r--r--vendor/github.com/d5/tengo/stdlib/fmt.go20
-rw-r--r--vendor/github.com/d5/tengo/stdlib/text.go193
18 files changed, 1555 insertions, 48 deletions
diff --git a/vendor/github.com/d5/tengo/README.md b/vendor/github.com/d5/tengo/README.md
index e68b0f86..6a35cfd1 100644
--- a/vendor/github.com/d5/tengo/README.md
+++ b/vendor/github.com/d5/tengo/README.md
@@ -7,6 +7,7 @@
[![GoDoc](https://godoc.org/github.com/d5/tengo?status.svg)](https://godoc.org/github.com/d5/tengo/script)
[![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)
+[![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.**
diff --git a/vendor/github.com/d5/tengo/compiler/ast/ident_list.go b/vendor/github.com/d5/tengo/compiler/ast/ident_list.go
index ee8f7db2..8dd6d307 100644
--- a/vendor/github.com/d5/tengo/compiler/ast/ident_list.go
+++ b/vendor/github.com/d5/tengo/compiler/ast/ident_list.go
@@ -8,9 +8,10 @@ import (
// IdentList represents a list of identifiers.
type IdentList struct {
- LParen source.Pos
- List []*Ident
- RParen source.Pos
+ LParen source.Pos
+ VarArgs bool
+ List []*Ident
+ RParen source.Pos
}
// Pos returns the position of first character belonging to the node.
@@ -50,8 +51,12 @@ func (n *IdentList) NumFields() int {
func (n *IdentList) String() string {
var list []string
- for _, e := range n.List {
- list = append(list, e.String())
+ for i, e := range n.List {
+ if n.VarArgs && i == len(n.List)-1 {
+ list = append(list, "..."+e.String())
+ } else {
+ list = append(list, e.String())
+ }
}
return "(" + strings.Join(list, ", ") + ")"
diff --git a/vendor/github.com/d5/tengo/compiler/compiler.go b/vendor/github.com/d5/tengo/compiler/compiler.go
index 4a3ec3ad..8bde5dc9 100644
--- a/vendor/github.com/d5/tengo/compiler/compiler.go
+++ b/vendor/github.com/d5/tengo/compiler/compiler.go
@@ -477,6 +477,7 @@ func (c *Compiler) Compile(node ast.Node) error {
Instructions: instructions,
NumLocals: numLocals,
NumParameters: len(node.Type.Params.List),
+ VarArgs: node.Type.Params.VarArgs,
SourceMap: sourceMap,
}
diff --git a/vendor/github.com/d5/tengo/compiler/instructions.go b/vendor/github.com/d5/tengo/compiler/instructions.go
index 80c88d13..14dde1d8 100644
--- a/vendor/github.com/d5/tengo/compiler/instructions.go
+++ b/vendor/github.com/d5/tengo/compiler/instructions.go
@@ -13,7 +13,7 @@ func MakeInstruction(opcode Opcode, operands ...int) []byte {
totalLen += w
}
- instruction := make([]byte, totalLen, totalLen)
+ instruction := make([]byte, totalLen)
instruction[0] = byte(opcode)
offset := 1
diff --git a/vendor/github.com/d5/tengo/compiler/parser/parser.go b/vendor/github.com/d5/tengo/compiler/parser/parser.go
index 1f609ab5..27dd48f0 100644
--- a/vendor/github.com/d5/tengo/compiler/parser/parser.go
+++ b/vendor/github.com/d5/tengo/compiler/parser/parser.go
@@ -610,19 +610,31 @@ func (p *Parser) parseIdentList() *ast.IdentList {
var params []*ast.Ident
lparen := p.expect(token.LParen)
+ isVarArgs := false
if p.token != token.RParen {
+ if p.token == token.Ellipsis {
+ isVarArgs = true
+ p.next()
+ }
+
params = append(params, p.parseIdent())
- for p.token == token.Comma {
+ for !isVarArgs && p.token == token.Comma {
p.next()
+ if p.token == token.Ellipsis {
+ isVarArgs = true
+ p.next()
+ }
params = append(params, p.parseIdent())
}
}
+
rparen := p.expect(token.RParen)
return &ast.IdentList{
- LParen: lparen,
- RParen: rparen,
- List: params,
+ LParen: lparen,
+ RParen: rparen,
+ VarArgs: isVarArgs,
+ List: params,
}
}
diff --git a/vendor/github.com/d5/tengo/compiler/symbol_scopes.go b/vendor/github.com/d5/tengo/compiler/symbol_scopes.go
index 15204b35..e0c0d94b 100644
--- a/vendor/github.com/d5/tengo/compiler/symbol_scopes.go
+++ b/vendor/github.com/d5/tengo/compiler/symbol_scopes.go
@@ -6,7 +6,7 @@ type SymbolScope string
// List of symbol scopes
const (
ScopeGlobal SymbolScope = "GLOBAL"
- ScopeLocal = "LOCAL"
- ScopeBuiltin = "BUILTIN"
- ScopeFree = "FREE"
+ ScopeLocal SymbolScope = "LOCAL"
+ ScopeBuiltin SymbolScope = "BUILTIN"
+ ScopeFree SymbolScope = "FREE"
)
diff --git a/vendor/github.com/d5/tengo/objects/builtin_format.go b/vendor/github.com/d5/tengo/objects/builtin_format.go
new file mode 100644
index 00000000..1f0e75ed
--- /dev/null
+++ b/vendor/github.com/d5/tengo/objects/builtin_format.go
@@ -0,0 +1,27 @@
+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/builtins.go b/vendor/github.com/d5/tengo/objects/builtins.go
index bfd004dd..773636ec 100644
--- a/vendor/github.com/d5/tengo/objects/builtins.go
+++ b/vendor/github.com/d5/tengo/objects/builtins.go
@@ -111,4 +111,8 @@ var Builtins = []*BuiltinFunction{
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
index 6710c7c1..5159c22f 100644
--- a/vendor/github.com/d5/tengo/objects/bytes.go
+++ b/vendor/github.com/d5/tengo/objects/bytes.go
@@ -57,7 +57,7 @@ func (o *Bytes) Equals(x Object) bool {
return false
}
- return bytes.Compare(o.Value, t.Value) == 0
+ return bytes.Equal(o.Value, t.Value)
}
// IndexGet returns an element (as Int) at a given index.
diff --git a/vendor/github.com/d5/tengo/objects/compiled_function.go b/vendor/github.com/d5/tengo/objects/compiled_function.go
index 606e3d90..d42e69ec 100644
--- a/vendor/github.com/d5/tengo/objects/compiled_function.go
+++ b/vendor/github.com/d5/tengo/objects/compiled_function.go
@@ -10,6 +10,7 @@ type CompiledFunction struct {
Instructions []byte
NumLocals int // number of local variables (including function parameters)
NumParameters int
+ VarArgs bool
SourceMap map[int]source.Pos
}
@@ -34,6 +35,7 @@ func (o *CompiledFunction) Copy() Object {
Instructions: append([]byte{}, o.Instructions...),
NumLocals: o.NumLocals,
NumParameters: o.NumParameters,
+ VarArgs: o.VarArgs,
}
}
diff --git a/vendor/github.com/d5/tengo/objects/conversion.go b/vendor/github.com/d5/tengo/objects/conversion.go
index d7cb3a03..27514132 100644
--- a/vendor/github.com/d5/tengo/objects/conversion.go
+++ b/vendor/github.com/d5/tengo/objects/conversion.go
@@ -254,7 +254,7 @@ func FromInterface(v interface{}) (Object, error) {
case []Object:
return &Array{Value: v}, nil
case []interface{}:
- arr := make([]Object, len(v), len(v))
+ arr := make([]Object, len(v))
for i, e := range v {
vo, err := FromInterface(e)
if err != nil {
diff --git a/vendor/github.com/d5/tengo/objects/formatter.go b/vendor/github.com/d5/tengo/objects/formatter.go
new file mode 100644
index 00000000..95d7f6b1
--- /dev/null
+++ b/vendor/github.com/d5/tengo/objects/formatter.go
@@ -0,0 +1,1212 @@
+package objects
+
+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.
+const (
+ commaSpaceString = ", "
+ nilParenString = "(nil)"
+ percentBangString = "%!"
+ missingString = "(MISSING)"
+ badIndexString = "(BADINDEX)"
+ extraString = "%!(EXTRA "
+ badWidthString = "%!(BADWIDTH)"
+ badPrecString = "%!(BADPREC)"
+ noVerbString = "%!(NOVERB)"
+)
+
+const (
+ ldigits = "0123456789abcdefx"
+ udigits = "0123456789ABCDEFX"
+)
+
+const (
+ signed = true
+ unsigned = false
+)
+
+// flags placed in a separate struct for easy clearing.
+type fmtFlags struct {
+ widPresent bool
+ precPresent bool
+ minus bool
+ plus bool
+ sharp bool
+ space bool
+ zero bool
+
+ // For the formats %+v %#v, we set the plusV/sharpV flags
+ // and clear the plus/sharp flags since %+v and %#v are in effect
+ // different, flagless formats set at the top level.
+ plusV bool
+ sharpV bool
+
+ // error-related flags.
+ inDetail bool
+ needNewline bool
+ needColon bool
+}
+
+// A formatter is the raw formatter used by Printf etc.
+// It prints into a buffer that must be set up separately.
+type formatter struct {
+ buf *buffer
+
+ fmtFlags
+
+ wid int // width
+ prec int // precision
+
+ // intbuf is large enough to store %b of an int64 with a sign and
+ // avoids padding at the end of the struct on 32 bit architectures.
+ intbuf [68]byte
+}
+
+func (f *formatter) clearflags() {
+ f.fmtFlags = fmtFlags{}
+}
+
+func (f *formatter) init(buf *buffer) {
+ f.buf = buf
+ f.clearflags()
+}
+
+// writePadding generates n bytes of padding.
+func (f *formatter) writePadding(n int) {
+ if n <= 0 { // No padding bytes needed.
+ return
+ }
+ buf := *f.buf
+ oldLen := len(buf)
+ newLen := oldLen + n
+
+ if newLen > tengo.MaxStringLen {
+ panic(ErrStringLimit)
+ }
+
+ // Make enough room for padding.
+ if newLen > cap(buf) {
+ buf = make(buffer, cap(buf)*2+n)
+ copy(buf, *f.buf)
+ }
+ // Decide which byte the padding should be filled with.
+ padByte := byte(' ')
+ if f.zero {
+ padByte = byte('0')
+ }
+ // Fill padding with padByte.
+ padding := buf[oldLen:newLen]
+ for i := range padding {
+ padding[i] = padByte
+ }
+ *f.buf = buf[:newLen]
+}
+
+// pad appends b to f.buf, padded on left (!f.minus) or right (f.minus).
+func (f *formatter) pad(b []byte) {
+ if !f.widPresent || f.wid == 0 {
+ f.buf.Write(b)
+ return
+ }
+ width := f.wid - utf8.RuneCount(b)
+ if !f.minus {
+ // left padding
+ f.writePadding(width)
+ f.buf.Write(b)
+ } else {
+ // right padding
+ f.buf.Write(b)
+ f.writePadding(width)
+ }
+}
+
+// padString appends s to f.buf, padded on left (!f.minus) or right (f.minus).
+func (f *formatter) padString(s string) {
+ if !f.widPresent || f.wid == 0 {
+ f.buf.WriteString(s)
+ return
+ }
+ width := f.wid - utf8.RuneCountInString(s)
+ if !f.minus {
+ // left padding
+ f.writePadding(width)
+ f.buf.WriteString(s)
+ } else {
+ // right padding
+ f.buf.WriteString(s)
+ f.writePadding(width)
+ }
+}
+
+// fmtBoolean formats a boolean.
+func (f *formatter) fmtBoolean(v bool) {
+ if v {
+ f.padString("true")
+ } else {
+ f.padString("false")
+ }
+}
+
+// fmtUnicode formats a uint64 as "U+0078" or with f.sharp set as "U+0078 'x'".
+func (f *formatter) fmtUnicode(u uint64) {
+ buf := f.intbuf[0:]
+
+ // With default precision set the maximum needed buf length is 18
+ // for formatting -1 with %#U ("U+FFFFFFFFFFFFFFFF") which fits
+ // into the already allocated intbuf with a capacity of 68 bytes.
+ prec := 4
+ if f.precPresent && f.prec > 4 {
+ prec = f.prec
+ // Compute space needed for "U+" , number, " '", character, "'".
+ width := 2 + prec + 2 + utf8.UTFMax + 1
+ if width > len(buf) {
+ buf = make([]byte, width)
+ }
+ }
+
+ // 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.
+ if f.sharp && u <= utf8.MaxRune && strconv.IsPrint(rune(u)) {
+ i--
+ buf[i] = '\''
+ i -= utf8.RuneLen(rune(u))
+ utf8.EncodeRune(buf[i:], rune(u))
+ i--
+ buf[i] = '\''
+ i--
+ buf[i] = ' '
+ }
+ // Format the Unicode code point u as a hexadecimal number.
+ for u >= 16 {
+ i--
+ buf[i] = udigits[u&0xF]
+ prec--
+ u >>= 4
+ }
+ i--
+ buf[i] = udigits[u]
+ prec--
+ // Add zeros in front of the number until requested precision is reached.
+ for prec > 0 {
+ i--
+ buf[i] = '0'
+ prec--
+ }
+ // Add a leading "U+".
+ i--
+ buf[i] = '+'
+ i--
+ buf[i] = 'U'
+
+ oldZero := f.zero
+ f.zero = false
+ f.pad(buf[i:])
+ f.zero = oldZero
+}
+
+// fmtInteger formats signed and unsigned integers.
+func (f *formatter) fmtInteger(u uint64, base int, isSigned bool, verb rune, digits string) {
+ negative := isSigned && int64(u) < 0
+ if negative {
+ u = -u
+ }
+
+ buf := f.intbuf[0:]
+ // The already allocated f.intbuf with a capacity of 68 bytes
+ // is large enough for integer formatting when no precision or width is set.
+ if f.widPresent || f.precPresent {
+ // Account 3 extra bytes for possible addition of a sign and "0x".
+ width := 3 + f.wid + f.prec // wid and prec are always positive.
+ if width > len(buf) {
+ // We're going to need a bigger boat.
+ buf = make([]byte, width)
+ }
+ }
+
+ // Two ways to ask for extra leading zero digits: %.3d or %03d.
+ // If both are specified the f.zero flag is ignored and
+ // padding with spaces is used instead.
+ prec := 0
+ if f.precPresent {
+ prec = f.prec
+ // Precision of 0 and value of 0 means "print nothing" but padding.
+ if prec == 0 && u == 0 {
+ oldZero := f.zero
+ f.zero = false
+ f.writePadding(f.wid)
+ f.zero = oldZero
+ return
+ }
+ } else if f.zero && f.widPresent {
+ prec = f.wid
+ if negative || f.plus || f.space {
+ prec-- // leave room for sign
+ }
+ }
+
+ // 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.
+ switch base {
+ case 10:
+ for u >= 10 {
+ i--
+ next := u / 10
+ buf[i] = byte('0' + u - next*10)
+ u = next
+ }
+ case 16:
+ for u >= 16 {
+ i--
+ buf[i] = digits[u&0xF]
+ u >>= 4
+ }
+ case 8:
+ for u >= 8 {
+ i--
+ buf[i] = byte('0' + u&7)
+ u >>= 3
+ }
+ case 2:
+ for u >= 2 {
+ i--
+ buf[i] = byte('0' + u&1)
+ u >>= 1
+ }
+ default:
+ panic("fmt: unknown base; can't happen")
+ }
+ i--
+ buf[i] = digits[u]
+ for i > 0 && prec > len(buf)-i {
+ i--
+ buf[i] = '0'
+ }
+
+ // Various prefixes: 0x, -, etc.
+ if f.sharp {
+ switch base {
+ case 2:
+ // Add a leading 0b.
+ i--
+ buf[i] = 'b'
+ i--
+ buf[i] = '0'
+ case 8:
+ if buf[i] != '0' {
+ i--
+ buf[i] = '0'
+ }
+ case 16:
+ // Add a leading 0x or 0X.
+ i--
+ buf[i] = digits[16]
+ i--
+ buf[i] = '0'
+ }
+ }
+ if verb == 'O' {
+ i--
+ buf[i] = 'o'
+ i--
+ buf[i] = '0'
+ }
+
+ if negative {
+ i--
+ buf[i] = '-'
+ } else if f.plus {
+ i--
+ buf[i] = '+'
+ } else if f.space {
+ i--
+ buf[i] = ' '
+ }
+
+ // Left padding with zeros has already been handled like precision earlier
+ // or the f.zero flag is ignored due to an explicitly set precision.
+ oldZero := f.zero
+ f.zero = false
+ f.pad(buf[i:])
+ f.zero = oldZero
+}
+
+// truncate truncates the string s to the specified precision, if present.
+func (f *formatter) truncateString(s string) string {
+ if f.precPresent {
+ n := f.prec
+ for i := range s {
+ n--
+ if n < 0 {
+ return s[:i]
+ }
+ }
+ }
+ return s
+}
+
+// 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
+ for i := 0; i < len(b); {
+ n--
+ if n < 0 {
+ return b[:i]
+ }
+ wid := 1
+ if b[i] >= utf8.RuneSelf {
+ _, wid = utf8.DecodeRune(b[i:])
+ }
+ i += wid
+ }
+ }
+ return b
+}
+
+// fmtS formats a string.
+func (f *formatter) fmtS(s string) {
+ s = f.truncateString(s)
+ f.padString(s)
+}
+
+// fmtBs formats the byte slice b as if it was formatted as string with fmtS.
+func (f *formatter) fmtBs(b []byte) {
+ b = f.truncate(b)
+ f.pad(b)
+}
+
+// fmtSbx formats a string or byte slice as a hexadecimal encoding of its bytes.
+func (f *formatter) fmtSbx(s string, b []byte, digits string) {
+ length := len(b)
+ if b == nil {
+ // No byte slice present. Assume string s should be encoded.
+ length = len(s)
+ }
+ // Set length to not process more bytes than the precision demands.
+ if f.precPresent && f.prec < length {
+ length = f.prec
+ }
+ // 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.
+ if f.sharp {
+ width *= 2
+ }
+ // Elements will be separated by a space.
+ width += length - 1
+ } else if f.sharp {
+ // Only a leading 0x or 0X will be added for the whole string.
+ width += 2
+ }
+ } else { // The byte slice or string that should be encoded is empty.
+ if f.widPresent {
+ f.writePadding(f.wid)
+ }
+ return
+ }
+ // Handle padding to the left.
+ if f.widPresent && f.wid > width && !f.minus {
+ f.writePadding(f.wid - width)
+ }
+ // Write the encoding directly into the output buffer.
+ buf := *f.buf
+ if f.sharp {
+ // Add leading 0x or 0X.
+ buf = append(buf, '0', digits[16])
+ }
+ var c byte
+ for i := 0; i < length; i++ {
+ if f.space && i > 0 {
+ // Separate elements with a space.
+ buf = append(buf, ' ')
+ if f.sharp {
+ // Add leading 0x or 0X for each element.
+ buf = append(buf, '0', digits[16])
+ }
+ }
+ if b != nil {
+ c = b[i] // Take a byte from the input byte slice.
+ } else {
+ c = s[i] // Take a byte from the input string.
+ }
+ // Encode each byte as two hexadecimal digits.
+ buf = append(buf, digits[c>>4], digits[c&0xF])
+ }
+ *f.buf = buf
+ // Handle padding to the right.
+ if f.widPresent && f.wid > width && f.minus {
+ f.writePadding(f.wid - width)
+ }
+}
+
+// fmtSx formats a string as a hexadecimal encoding of its bytes.
+func (f *formatter) fmtSx(s, digits string) {
+ f.fmtSbx(s, nil, digits)
+}
+
+// fmtBx formats a byte slice as a hexadecimal encoding of its bytes.
+func (f *formatter) fmtBx(b []byte, digits string) {
+ f.fmtSbx("", b, digits)
+}
+
+// fmtQ formats a string as a double-quoted, escaped Go string constant.
+// If f.sharp is set a raw (backquoted) string may be returned instead
+// if the string does not contain any control characters other than tab.
+func (f *formatter) fmtQ(s string) {
+ s = f.truncateString(s)
+ if f.sharp && strconv.CanBackquote(s) {
+ f.padString("`" + s + "`")
+ return
+ }
+ buf := f.intbuf[:0]
+ if f.plus {
+ f.pad(strconv.AppendQuoteToASCII(buf, s))
+ } else {
+ f.pad(strconv.AppendQuote(buf, s))
+ }
+}
+
+// fmtC formats an integer as a Unicode character.
+// If the character is not valid Unicode, it will print '\ufffd'.
+func (f *formatter) fmtC(c uint64) {
+ r := rune(c)
+ if c > utf8.MaxRune {
+ r = utf8.RuneError
+ }
+ buf := f.intbuf[:0]
+ w := utf8.EncodeRune(buf[:utf8.UTFMax], r)
+ f.pad(buf[:w])
+}
+
+// fmtQc formats an integer as a single-quoted, escaped Go character constant.
+// If the character is not valid Unicode, it will print '\ufffd'.
+func (f *formatter) fmtQc(c uint64) {
+ r := rune(c)
+ if c > utf8.MaxRune {
+ r = utf8.RuneError
+ }
+ buf := f.intbuf[:0]
+ if f.plus {
+ f.pad(strconv.AppendQuoteRuneToASCII(buf, r))
+ } else {
+ f.pad(strconv.AppendQuoteRune(buf, r))
+ }
+}
+
+// fmtFloat formats a float64. It assumes that verb is a valid format specifier
+// for strconv.AppendFloat and therefore fits into a byte.
+func (f *formatter) fmtFloat(v float64, size int, verb rune, prec int) {
+ // Explicit precision in format specifier overrules default precision.
+ if f.precPresent {
+ prec = f.prec
+ }
+ // Format number, reserving space for leading + sign if needed.
+ num := strconv.AppendFloat(f.intbuf[:1], v, byte(verb), prec, size)
+ if num[1] == '-' || num[1] == '+' {
+ num = num[1:]
+ } else {
+ num[0] = '+'
+ }
+ // f.space means to add a leading space instead of a "+" sign unless
+ // the sign is explicitly asked for by f.plus.
+ if f.space && num[0] == '+' && !f.plus {
+ num[0] = ' '
+ }
+ // Special handling for infinities and NaN,
+ // which don't look like a number so shouldn't be padded with zeros.
+ if num[1] == 'I' || num[1] == 'N' {
+ oldZero := f.zero
+ f.zero = false
+ // Remove sign before NaN if not asked for.
+ if num[1] == 'N' && !f.space && !f.plus {
+ num = num[1:]
+ }
+ f.pad(num)
+ f.zero = oldZero
+ return
+ }
+ // The sharp flag forces printing a decimal point for non-binary formats
+ // and retains trailing zeros, which we may need to restore.
+ if f.sharp && verb != 'b' {
+ digits := 0
+ switch verb {
+ case 'v', 'g', 'G', 'x':
+ digits = prec
+ // If no precision is set explicitly use a precision of 6.
+ if digits == -1 {
+ digits = 6
+ }
+ }
+
+ // Buffer pre-allocated with enough room for
+ // exponent notations of the form "e+123" or "p-1023".
+ var tailBuf [6]byte
+ tail := tailBuf[:0]
+
+ hasDecimalPoint := false
+ // Starting from i = 1 to skip sign at num[0].
+ for i := 1; i < len(num); i++ {
+ switch num[i] {
+ case '.':
+ hasDecimalPoint = true
+ case 'p', 'P':
+ tail = append(tail, num[i:]...)
+ num = num[:i]
+ case 'e', 'E':
+ if verb != 'x' && verb != 'X' {
+ tail = append(tail, num[i:]...)
+ num = num[:i]
+ break
+ }
+ fallthrough
+ default:
+ digits--
+ }
+ }
+ if !hasDecimalPoint {
+ num = append(num, '.')
+ }
+ for digits > 0 {
+ num = append(num, '0')
+ digits--
+ }
+ num = append(num, tail...)
+ }
+ // 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 f.zero && f.widPresent && f.wid > len(num) {
+ f.buf.WriteSingleByte(num[0])
+ f.writePadding(f.wid - len(num))
+ f.buf.Write(num[1:])
+ return
+ }
+ f.pad(num)
+ return
+ }
+ // 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
+
+func (b *buffer) Write(p []byte) {
+ if len(*b)+len(p) > tengo.MaxStringLen {
+ panic(ErrStringLimit)
+ }
+
+ *b = append(*b, p...)
+}
+
+func (b *buffer) WriteString(s string) {
+ if len(*b)+len(s) > tengo.MaxStringLen {
+ panic(ErrStringLimit)
+ }
+
+ *b = append(*b, s...)
+}
+
+func (b *buffer) WriteSingleByte(c byte) {
+ if len(*b) >= tengo.MaxStringLen {
+ panic(ErrStringLimit)
+ }
+
+ *b = append(*b, c)
+}
+
+func (b *buffer) WriteRune(r rune) {
+ if len(*b)+utf8.RuneLen(r) > tengo.MaxStringLen {
+ panic(ErrStringLimit)
+ }
+
+ if r < utf8.RuneSelf {
+ *b = append(*b, byte(r))
+ return
+ }
+
+ b2 := *b
+ n := len(b2)
+ for n+utf8.UTFMax > cap(b2) {
+ b2 = append(b2, 0)
+ }
+ w := utf8.EncodeRune(b2[n:n+utf8.UTFMax], r)
+ *b = b2[:n+w]
+}
+
+// pp is used to store a printer's state and is reused with sync.Pool to avoid allocations.
+type pp struct {
+ buf buffer
+
+ // arg holds the current item.
+ arg Object
+
+ // fmt is used to format basic items such as integers or strings.
+ fmt formatter
+
+ // reordered records whether the format string used argument reordering.
+ reordered bool
+
+ // 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 bool
+}
+
+var ppFree = sync.Pool{
+ New: func() interface{} { return new(pp) },
+}
+
+// newPrinter allocates a new pp struct or grabs a cached one.
+func newPrinter() *pp {
+ p := ppFree.Get().(*pp)
+ p.erroring = false
+ p.fmt.init(&p.buf)
+ return p
+}
+
+// free saves used pp structs in ppFree; avoids an allocation per invocation.
+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.
+ //
+ // See https://golang.org/issue/23199
+ if cap(p.buf) > 64<<10 {
+ return
+ }
+
+ p.buf = p.buf[:0]
+ p.arg = nil
+ ppFree.Put(p)
+}
+
+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) Flag(b int) bool {
+ switch b {
+ case '-':
+ return p.fmt.minus
+ case '+':
+ return p.fmt.plus || p.fmt.plusV
+ case '#':
+ return p.fmt.sharp || p.fmt.sharpV
+ case ' ':
+ return p.fmt.space
+ case '0':
+ return p.fmt.zero
+ }
+ return false
+}
+
+// Implement Write so we can call Fprintf on a pp (through State), for
+// recursive use in custom verbs.
+func (p *pp) Write(b []byte) (ret int, err error) {
+ p.buf.Write(b)
+ return len(b), nil
+}
+
+// Implement WriteString so that we can call io.WriteString
+// on a pp (through state), for efficiency.
+func (p *pp) WriteString(s string) (ret int, err error) {
+ p.buf.WriteString(s)
+ return len(s), nil
+}
+
+func (p *pp) WriteRune(r rune) (ret int, err error) {
+ p.buf.WriteRune(r)
+ return utf8.RuneLen(r), nil
+}
+
+func (p *pp) WriteSingleByte(c byte) (ret int, err error) {
+ p.buf.WriteSingleByte(c)
+ return 1, nil
+}
+
+// tooLarge reports whether the magnitude of the integer is
+// too large to be used as a formatting width or precision.
+func tooLarge(x int) bool {
+ const max int = 1e6
+ return x > max || x < -max
+}
+
+// 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
+ }
+ for newi = start; newi < end && '0' <= s[newi] && s[newi] <= '9'; newi++ {
+ if tooLarge(num) {
+ return 0, false, end // Overflow; crazy long number most likely.
+ }
+ num = num*10 + int(s[newi]-'0')
+ isnum = true
+ }
+ return
+}
+
+func (p *pp) badVerb(verb rune) {
+ p.erroring = true
+ _, _ = p.WriteString(percentBangString)
+ _, _ = p.WriteRune(verb)
+ _, _ = p.WriteSingleByte('(')
+ switch {
+ case p.arg != nil:
+ _, _ = p.WriteString(p.arg.String())
+ _, _ = p.WriteSingleByte('=')
+ p.printArg(p.arg, 'v')
+ default:
+ _, _ = p.WriteString(UndefinedValue.String())
+ }
+ _, _ = p.WriteSingleByte(')')
+ p.erroring = false
+}
+
+func (p *pp) fmtBool(v bool, verb rune) {
+ switch verb {
+ case 't', 'v':
+ p.fmt.fmtBoolean(v)
+ default:
+ p.badVerb(verb)
+ }
+}
+
+// fmt0x64 formats a uint64 in hexadecimal and prefixes it with 0x or
+// not, as requested, by temporarily setting the sharp flag.
+func (p *pp) fmt0x64(v uint64, leading0x bool) {
+ sharp := p.fmt.sharp
+ p.fmt.sharp = leading0x
+ p.fmt.fmtInteger(v, 16, unsigned, 'v', ldigits)
+ p.fmt.sharp = sharp
+}
+
+// fmtInteger formats a signed or unsigned integer.
+func (p *pp) fmtInteger(v uint64, isSigned bool, verb rune) {
+ switch verb {
+ case 'v':
+ if p.fmt.sharpV && !isSigned {
+ p.fmt0x64(v, true)
+ } else {
+ p.fmt.fmtInteger(v, 10, isSigned, verb, ldigits)
+ }
+ case 'd':
+ p.fmt.fmtInteger(v, 10, isSigned, verb, ldigits)
+ case 'b':
+ p.fmt.fmtInteger(v, 2, isSigned, verb, ldigits)
+ case 'o', 'O':
+ p.fmt.fmtInteger(v, 8, isSigned, verb, ldigits)
+ case 'x':
+ p.fmt.fmtInteger(v, 16, isSigned, verb, ldigits)
+ case 'X':
+ p.fmt.fmtInteger(v, 16, isSigned, verb, udigits)
+ case 'c':
+ p.fmt.fmtC(v)
+ case 'q':
+ if v <= utf8.MaxRune {
+ p.fmt.fmtQc(v)
+ } else {
+ p.badVerb(verb)
+ }
+ case 'U':
+ p.fmt.fmtUnicode(v)
+ default:
+ p.badVerb(verb)
+ }
+}
+
+// fmtFloat formats a float. The default precision for each verb
+// is specified as last argument in the call to fmt_float.
+func (p *pp) fmtFloat(v float64, size int, verb rune) {
+ switch verb {
+ case 'v':
+ p.fmt.fmtFloat(v, size, 'g', -1)
+ case 'b', 'g', 'G', 'x', 'X':
+ p.fmt.fmtFloat(v, size, verb, -1)
+ case 'f', 'e', 'E':
+ p.fmt.fmtFloat(v, size, verb, 6)
+ case 'F':
+ p.fmt.fmtFloat(v, size, 'f', 6)
+ default:
+ p.badVerb(verb)
+ }
+}
+
+func (p *pp) fmtString(v string, verb rune) {
+ switch verb {
+ case 'v':
+ if p.fmt.sharpV {
+ p.fmt.fmtQ(v)
+ } else {
+ p.fmt.fmtS(v)
+ }
+ case 's':
+ p.fmt.fmtS(v)
+ case 'x':
+ p.fmt.fmtSx(v, ldigits)
+ case 'X':
+ p.fmt.fmtSx(v, udigits)
+ case 'q':
+ p.fmt.fmtQ(v)
+ default:
+ p.badVerb(verb)
+ }
+}
+
+func (p *pp) fmtBytes(v []byte, verb rune, typeString string) {
+ switch verb {
+ case 'v', 'd':
+ if p.fmt.sharpV {
+ _, _ = p.WriteString(typeString)
+ if v == nil {
+ _, _ = p.WriteString(nilParenString)
+ return
+ }
+ _, _ = p.WriteSingleByte('{')
+ for i, c := range v {
+ if i > 0 {
+ _, _ = p.WriteString(commaSpaceString)
+ }
+ p.fmt0x64(uint64(c), true)
+ }
+ _, _ = p.WriteSingleByte('}')
+ } else {
+ _, _ = p.WriteSingleByte('[')
+ for i, c := range v {
+ if i > 0 {
+ _, _ = p.WriteSingleByte(' ')
+ }
+ p.fmt.fmtInteger(uint64(c), 10, unsigned, verb, ldigits)
+ }
+ _, _ = p.WriteSingleByte(']')
+ }
+ case 's':
+ p.fmt.fmtBs(v)
+ case 'x':
+ p.fmt.fmtBx(v, ldigits)
+ case 'X':
+ p.fmt.fmtBx(v, udigits)
+ case 'q':
+ p.fmt.fmtQ(string(v))
+ }
+}
+
+func (p *pp) printArg(arg Object, verb rune) {
+ p.arg = arg
+
+ if arg == nil {
+ arg = UndefinedValue
+ }
+
+ // Special processing considerations.
+ // %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())
+ return
+ case 'v':
+ p.fmt.fmtS(arg.String())
+ return
+ }
+
+ // Some types can be done without reflection.
+ switch f := arg.(type) {
+ case *Bool:
+ p.fmtBool(!f.IsFalsy(), verb)
+ case *Float:
+ p.fmtFloat(f.Value, 64, verb)
+ case *Int:
+ p.fmtInteger(uint64(f.Value), signed, verb)
+ case *String:
+ p.fmtString(f.Value, verb)
+ case *Bytes:
+ p.fmtBytes(f.Value, verb, "[]byte")
+ default:
+ p.fmtString(f.String(), verb)
+ }
+}
+
+// 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) {
+ var num64 int64
+ num64, isInt = ToInt64(a[argNum])
+ num = int(num64)
+ newArgNum = argNum + 1
+ if tooLarge(num) {
+ num = 0
+ isInt = false
+ }
+ }
+ return
+}
+
+// parseArgNumber returns the value of the bracketed number, minus 1
+// (explicit argument numbers are one-indexed but we want zero-indexed).
+// The opening bracket is known to be present at format[0].
+// The returned values are the index, the number of bytes to consume
+// up to the closing paren, if present, and whether the number parsed
+// ok. The bytes to consume will be 1 if no closing paren is present.
+func parseArgNumber(format string) (index int, wid int, ok bool) {
+ // There must be at least 3 bytes: [n].
+ if len(format) < 3 {
+ return 0, 1, false
+ }
+
+ // Find closing bracket.
+ for i := 1; i < len(format); i++ {
+ if format[i] == ']' {
+ width, ok, newi := parsenum(format, 1, i)
+ if !ok || newi != i {
+ return 0, i + 1, false
+ }
+ return width - 1, i + 1, true // arg numbers are one-indexed and skip paren.
+ }
+ }
+ 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) {
+ if len(format) <= i || format[i] != '[' {
+ return argNum, i, false
+ }
+ p.reordered = true
+ index, wid, ok := parseArgNumber(format[i:])
+ if ok && 0 <= index && index < numArgs {
+ return index, i + wid, true
+ }
+ p.goodArgNum = false
+ return argNum, i + wid, ok
+}
+
+func (p *pp) badArgNum(verb rune) {
+ _, _ = p.WriteString(percentBangString)
+ _, _ = p.WriteRune(verb)
+ _, _ = p.WriteString(badIndexString)
+}
+
+func (p *pp) missingArg(verb rune) {
+ _, _ = p.WriteString(percentBangString)
+ _, _ = p.WriteRune(verb)
+ _, _ = p.WriteString(missingString)
+}
+
+func (p *pp) doFormat(format string, a []Object) (err error) {
+ defer func() {
+ if r := recover(); r != nil {
+ if e, ok := r.(error); ok && e == ErrStringLimit {
+ err = e
+ return
+ }
+ panic(r)
+ }
+ }()
+
+ end := len(format)
+ argNum := 0 // we process one argument per non-trivial format
+ afterIndex := false // previous item in format was an index like [3].
+ p.reordered = false
+formatLoop:
+ for i := 0; i < end; {
+ p.goodArgNum = true
+ lasti := i
+ for i < end && format[i] != '%' {
+ i++
+ }
+ if i > lasti {
+ _, _ = p.WriteString(format[lasti:i])
+ }
+ if i >= end {
+ // done processing format string
+ break
+ }
+
+ // Process one verb
+ i++
+
+ // Do we have flags?
+ p.fmt.clearflags()
+ simpleFormat:
+ for ; i < end; i++ {
+ c := format[i]
+ switch c {
+ case '#':
+ p.fmt.sharp = true
+ case '0':
+ p.fmt.zero = !p.fmt.minus // Only allow zero padding to the left.
+ case '+':
+ p.fmt.plus = true
+ case '-':
+ p.fmt.minus = true
+ p.fmt.zero = false // Do not pad with zeros to the right.
+ case ' ':
+ p.fmt.space = true
+ default:
+ // Fast path for common case of ascii lower case simple verbs
+ // without precision or width or argument indices.
+ if 'a' <= c && c <= 'z' && argNum < len(a) {
+ if c == 'v' {
+ // Go syntax
+ p.fmt.sharpV = p.fmt.sharp
+ p.fmt.sharp = false
+ // Struct-field syntax
+ p.fmt.plusV = p.fmt.plus
+ p.fmt.plus = false
+ }
+ p.printArg(a[argNum], rune(c))
+ argNum++
+ i++
+ continue formatLoop
+ }
+ // Format is more complex than simple flags and a verb or is malformed.
+ break simpleFormat
+ }
+ }
+
+ // Do we have an explicit argument index?
+ argNum, i, afterIndex = p.argNumber(argNum, format, i, len(a))
+
+ // Do we have width?
+ if i < end && format[i] == '*' {
+ i++
+ p.fmt.wid, p.fmt.widPresent, argNum = intFromArg(a, argNum)
+
+ if !p.fmt.widPresent {
+ _, _ = p.WriteString(badWidthString)
+ }
+
+ // We have a negative width, so take its value and ensure
+ // that the minus flag is set
+ if p.fmt.wid < 0 {
+ p.fmt.wid = -p.fmt.wid
+ p.fmt.minus = true
+ p.fmt.zero = false // Do not pad with zeros to the right.
+ }
+ afterIndex = false
+ } else {
+ p.fmt.wid, p.fmt.widPresent, i = parsenum(format, i, end)
+ if afterIndex && p.fmt.widPresent { // "%[3]2d"
+ p.goodArgNum = false
+ }
+ }
+
+ // Do we have precision?
+ if i+1 < end && format[i] == '.' {
+ i++
+ if afterIndex { // "%[3].2d"
+ p.goodArgNum = false
+ }
+ argNum, i, afterIndex = p.argNumber(argNum, format, i, len(a))
+ if i < end && format[i] == '*' {
+ i++
+ p.fmt.prec, p.fmt.precPresent, argNum = intFromArg(a, argNum)
+ // Negative precision arguments don't make sense
+ if p.fmt.prec < 0 {
+ p.fmt.prec = 0
+ p.fmt.precPresent = false
+ }
+ if !p.fmt.precPresent {
+ _, _ = p.WriteString(badPrecString)
+ }
+ afterIndex = false
+ } else {
+ p.fmt.prec, p.fmt.precPresent, i = parsenum(format, i, end)
+ if !p.fmt.precPresent {
+ p.fmt.prec = 0
+ p.fmt.precPresent = true
+ }
+ }
+ }
+
+ if !afterIndex {
+ argNum, i, afterIndex = p.argNumber(argNum, format, i, len(a))
+ }
+
+ if i >= end {
+ _, _ = p.WriteString(noVerbString)
+ break
+ }
+
+ verb, size := rune(format[i]), 1
+ if verb >= utf8.RuneSelf {
+ verb, size = utf8.DecodeRuneInString(format[i:])
+ }
+ i += size
+
+ switch {
+ 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.
+ p.missingArg(verb)
+ case verb == 'v':
+ // Go syntax
+ p.fmt.sharpV = p.fmt.sharp
+ p.fmt.sharp = false
+ // Struct-field syntax
+ p.fmt.plusV = p.fmt.plus
+ p.fmt.plus = false
+ fallthrough
+ default:
+ p.printArg(a[argNum], verb)
+ argNum++
+ }
+ }
+
+ // Check for extra arguments unless the call accessed the arguments
+ // 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.WriteString(extraString)
+ for i, arg := range a[argNum:] {
+ if i > 0 {
+ _, _ = p.WriteString(commaSpaceString)
+ }
+ if arg == nil {
+ _, _ = p.WriteString(UndefinedValue.String())
+ } else {
+ _, _ = p.WriteString(arg.TypeName())
+ _, _ = p.WriteSingleByte('=')
+ p.printArg(arg, 'v')
+ }
+ }
+ _, _ = p.WriteSingleByte(')')
+ }
+
+ return nil
+}
+
+// Format formats according to a format specifier and returns the resulting string.
+func Format(format string, a ...Object) (string, error) {
+ p := newPrinter()
+ err := p.doFormat(format, a)
+ s := string(p.buf)
+ p.free()
+
+ return s, err
+}
diff --git a/vendor/github.com/d5/tengo/objects/map.go b/vendor/github.com/d5/tengo/objects/map.go
index c42ffe93..9208872c 100644
--- a/vendor/github.com/d5/tengo/objects/map.go
+++ b/vendor/github.com/d5/tengo/objects/map.go
@@ -76,13 +76,13 @@ func (o *Map) Equals(x Object) bool {
// IndexGet returns the value for the given key.
func (o *Map) IndexGet(index Object) (res Object, err error) {
- strIdx, ok := index.(*String)
+ strIdx, ok := ToString(index)
if !ok {
err = ErrInvalidIndexType
return
}
- val, ok := o.Value[strIdx.Value]
+ val, ok := o.Value[strIdx]
if !ok {
val = UndefinedValue
}
diff --git a/vendor/github.com/d5/tengo/objects/undefined.go b/vendor/github.com/d5/tengo/objects/undefined.go
index 79a380f5..0fdbc084 100644
--- a/vendor/github.com/d5/tengo/objects/undefined.go
+++ b/vendor/github.com/d5/tengo/objects/undefined.go
@@ -40,3 +40,23 @@ func (o *Undefined) Equals(x Object) bool {
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/runtime/vm.go b/vendor/github.com/d5/tengo/runtime/vm.go
index dde52db5..07f6e530 100644
--- a/vendor/github.com/d5/tengo/runtime/vm.go
+++ b/vendor/github.com/d5/tengo/runtime/vm.go
@@ -642,9 +642,31 @@ func (v *VM) run() {
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 {
- v.err = fmt.Errorf("wrong number of arguments: want=%d, got=%d",
- callee.Fn.NumParameters, numArgs)
+ 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
}
@@ -674,9 +696,31 @@ func (v *VM) run() {
v.sp = v.sp - numArgs + callee.Fn.NumLocals
case *objects.CompiledFunction:
+ if callee.VarArgs {
+ // if the closure is variadic,
+ // roll up all variadic parameters into an array
+ realArgs := callee.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.NumParameters {
- v.err = fmt.Errorf("wrong number of arguments: want=%d, got=%d",
- callee.NumParameters, numArgs)
+ if callee.VarArgs {
+ 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",
+ callee.NumParameters, numArgs)
+ }
return
}
@@ -707,9 +751,7 @@ func (v *VM) run() {
case objects.Callable:
var args []objects.Object
- for _, arg := range v.stack[v.sp-numArgs : v.sp] {
- args = append(args, arg)
- }
+ args = append(args, v.stack[v.sp-numArgs:v.sp]...)
ret, e := callee.Call(args...)
v.sp -= numArgs + 1
@@ -817,9 +859,12 @@ func (v *VM) run() {
val := v.stack[v.sp-numSelectors-1]
v.sp -= numSelectors + 1
- sp := v.curFrame.basePointer + localIndex
+ dst := v.stack[v.curFrame.basePointer+localIndex]
+ if obj, ok := dst.(*objects.ObjectPtr); ok {
+ dst = *obj.Value
+ }
- if e := indexAssign(v.stack[sp], val, selectors); e != nil {
+ if e := indexAssign(dst, val, selectors); e != nil {
v.err = e
return
}
diff --git a/vendor/github.com/d5/tengo/script/script.go b/vendor/github.com/d5/tengo/script/script.go
index cdf36713..2ee67b61 100644
--- a/vendor/github.com/d5/tengo/script/script.go
+++ b/vendor/github.com/d5/tengo/script/script.go
@@ -183,12 +183,3 @@ func (s *Script) prepCompile() (symbolTable *compiler.SymbolTable, globals []obj
return
}
-
-func (s *Script) copyVariables() map[string]*Variable {
- vars := make(map[string]*Variable)
- for n, v := range s.variables {
- vars[n] = v
- }
-
- return vars
-}
diff --git a/vendor/github.com/d5/tengo/stdlib/fmt.go b/vendor/github.com/d5/tengo/stdlib/fmt.go
index 9c75fc33..b8f64278 100644
--- a/vendor/github.com/d5/tengo/stdlib/fmt.go
+++ b/vendor/github.com/d5/tengo/stdlib/fmt.go
@@ -44,12 +44,12 @@ func fmtPrintf(args ...objects.Object) (ret objects.Object, err error) {
return nil, nil
}
- formatArgs := make([]interface{}, numArgs-1, numArgs-1)
- for idx, arg := range args[1:] {
- formatArgs[idx] = objects.ToInterface(arg)
+ s, err := objects.Format(format.Value, args[1:]...)
+ if err != nil {
+ return nil, err
}
- fmt.Printf(format.Value, formatArgs...)
+ fmt.Print(s)
return nil, nil
}
@@ -84,15 +84,9 @@ func fmtSprintf(args ...objects.Object) (ret objects.Object, err error) {
return format, nil // okay to return 'format' directly as String is immutable
}
- formatArgs := make([]interface{}, numArgs-1, numArgs-1)
- for idx, arg := range args[1:] {
- formatArgs[idx] = objects.ToInterface(arg)
- }
-
- s := fmt.Sprintf(format.Value, formatArgs...)
-
- if len(s) > tengo.MaxStringLen {
- return nil, objects.ErrStringLimit
+ s, err := objects.Format(format.Value, args[1:]...)
+ if err != nil {
+ return nil, err
}
return &objects.String{Value: s}, nil
diff --git a/vendor/github.com/d5/tengo/stdlib/text.go b/vendor/github.com/d5/tengo/stdlib/text.go
index 9f9770b8..4b5729ec 100644
--- a/vendor/github.com/d5/tengo/stdlib/text.go
+++ b/vendor/github.com/d5/tengo/stdlib/text.go
@@ -32,6 +32,7 @@ var textModule = map[string]objects.Object{
"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]
@@ -40,6 +41,9 @@ var textModule = map[string]objects.Object{
"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
@@ -376,6 +380,195 @@ func textReplace(args ...objects.Object) (ret objects.Object, err error) {
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