4
0
mirror of https://github.com/cwinfo/matterbridge.git synced 2025-07-03 09:37:44 +00:00

Update to tengo v2 (#976)

This commit is contained in:
Wim
2020-01-09 21:52:19 +01:00
committed by GitHub
parent 0f708daf2d
commit 9d84d6dd64
191 changed files with 11406 additions and 11832 deletions

1
vendor/github.com/d5/tengo/v2/.gitignore generated vendored Normal file
View File

@ -0,0 +1 @@
dist/

20
vendor/github.com/d5/tengo/v2/.goreleaser.yml generated vendored Normal file
View File

@ -0,0 +1,20 @@
env:
- GO111MODULE=on
before:
hooks:
- go mod tidy
builds:
- env:
- CGO_ENABLED=0
main: ./cmd/tengo/main.go
goos:
- darwin
- linux
- windows
archive:
files:
- none*
checksum:
name_template: 'checksums.txt'
changelog:
sort: asc

21
vendor/github.com/d5/tengo/v2/LICENSE generated vendored Normal file
View File

@ -0,0 +1,21 @@
MIT License
Copyright (c) 2019 Daniel Kang
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

11
vendor/github.com/d5/tengo/v2/Makefile generated vendored Normal file
View File

@ -0,0 +1,11 @@
generate:
go generate ./...
lint:
golint -set_exit_status ./...
test: generate lint
go test -race -cover ./...
fmt:
go fmt ./...

135
vendor/github.com/d5/tengo/v2/README.md generated vendored Normal file
View File

@ -0,0 +1,135 @@
<p align="center">
<img src="https://raw.githubusercontent.com/d5/tengolang-share/master/logo_400.png" width="200" height="200">
</p>
# The Tengo Language
[![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)
[![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.
```golang
/* The Tengo Language */
fmt := import("fmt")
each := func(seq, fn) {
for x in seq { fn(x) }
}
sum := func(init, seq) {
each(seq, func(x) { init += x })
return init
}
fmt.println(sum(0, [1, 2, 3])) // "6"
fmt.println(sum("", [1, 2, 3])) // "123"
```
> 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)
- Dynamic typing with type coercion
- Higher-order functions and closures
- Immutable values
- [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),
data pipeline, [transpiler](https://github.com/d5/tengo2lua)
## Benchmark
| | fib(35) | fibt(35) | Type |
| :--- | ---: | ---: | :---: |
| Go | `48ms` | `3ms` | Go (native) |
| [**Tengo**](https://github.com/d5/tengo) | `2,349ms` | `5ms` | VM on Go |
| Lua | `1,416ms` | `3ms` | Lua (native) |
| [go-lua](https://github.com/Shopify/go-lua) | `4,402ms` | `5ms` | Lua VM on Go |
| [GopherLua](https://github.com/yuin/gopher-lua) | `4,023ms` | `5ms` | Lua VM on Go |
| Python | `2,588ms` | `26ms` | Python (native) |
| [starlark-go](https://github.com/google/starlark-go) | `11,126ms` | `6ms` | Python-like Interpreter on Go |
| [gpython](https://github.com/go-python/gpython) | `15,035ms` | `4ms` | Python Interpreter on Go |
| [goja](https://github.com/dop251/goja) | `5,089ms` | `5ms` | JS VM on Go |
| [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)_
_* **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)
- [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)
- [Standard Library](https://github.com/d5/tengo/blob/master/docs/stdlib.md)

502
vendor/github.com/d5/tengo/v2/builtins.go generated vendored Normal file
View File

@ -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(),
}
}
}

292
vendor/github.com/d5/tengo/v2/bytecode.go generated vendored Normal file
View File

@ -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{})
}

1312
vendor/github.com/d5/tengo/v2/compiler.go generated vendored Normal file

File diff suppressed because it is too large Load Diff

3
vendor/github.com/d5/tengo/v2/doc.go generated vendored Normal file
View File

@ -0,0 +1,3 @@
// tengo is a small, dynamic, fast, secure script language for Go.
package tengo

64
vendor/github.com/d5/tengo/v2/errors.go generated vendored Normal file
View File

@ -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)
}

1245
vendor/github.com/d5/tengo/v2/formatter.go generated vendored Normal file

File diff suppressed because it is too large Load Diff

3
vendor/github.com/d5/tengo/v2/go.mod generated vendored Normal file
View File

@ -0,0 +1,3 @@
module github.com/d5/tengo/v2
go 1.13

0
vendor/github.com/d5/tengo/v2/go.sum generated vendored Normal file
View File

61
vendor/github.com/d5/tengo/v2/instructions.go generated vendored Normal file
View File

@ -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
}

209
vendor/github.com/d5/tengo/v2/iterator.go generated vendored Normal file
View File

@ -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 "<array-iterator>"
}
// 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 "<bytes-iterator>"
}
// 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 "<map-iterator>"
}
// 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 "<string-iterator>"
}
// 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]}
}

93
vendor/github.com/d5/tengo/v2/modules.go generated vendored Normal file
View File

@ -0,0 +1,93 @@
package tengo
// 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
}
// NewModuleMap creates a new module map.
func NewModuleMap() *ModuleMap {
return &ModuleMap{
m: make(map[string]Importable),
}
}
// Add adds an import module.
func (m *ModuleMap) Add(name string, module Importable) {
m.m[name] = module
}
// AddBuiltinModule adds a builtin module.
func (m *ModuleMap) AddBuiltinModule(name string, attrs map[string]Object) {
m.m[name] = &BuiltinModule{Attrs: attrs}
}
// AddSourceModule adds a source module.
func (m *ModuleMap) AddSourceModule(name string, src []byte) {
m.m[name] = &SourceModule{Src: src}
}
// Remove removes a named module.
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.
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.
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.
func (m *ModuleMap) GetSourceModule(name string) *SourceModule {
mod, _ := m.m[name].(*SourceModule)
return mod
}
// Copy creates a copy of the module map.
func (m *ModuleMap) Copy() *ModuleMap {
c := &ModuleMap{
m: make(map[string]Importable),
}
for name, mod := range m.m {
c.m[name] = mod
}
return c
}
// Len returns the number of named modules.
func (m *ModuleMap) Len() int {
return len(m.m)
}
// AddMap adds named modules from another module map.
func (m *ModuleMap) AddMap(o *ModuleMap) {
for name, mod := range o.m {
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
}

1581
vendor/github.com/d5/tengo/v2/objects.go generated vendored Normal file

File diff suppressed because it is too large Load Diff

69
vendor/github.com/d5/tengo/v2/parser/ast.go generated vendored Normal file
View File

@ -0,0 +1,69 @@
package parser
import (
"strings"
)
const (
nullRep = "<null>"
)
// 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 Pos
VarArgs bool
List []*Ident
RParen Pos
}
// Pos returns the position of first character belonging to the node.
func (n *IdentList) Pos() Pos {
if n.LParen.IsValid() {
return n.LParen
}
if len(n.List) > 0 {
return n.List[0].Pos()
}
return NoPos
}
// End returns the position of first character immediately after the node.
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 NoPos
}
// NumFields returns the number of fields.
func (n *IdentList) NumFields() int {
if n == nil {
return 0
}
return len(n.List)
}
func (n *IdentList) String() string {
var list []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, ", ") + ")"
}

597
vendor/github.com/d5/tengo/v2/parser/expr.go generated vendored Normal file
View File

@ -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 "<bad expression>"
}
// 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"
}

29
vendor/github.com/d5/tengo/v2/parser/file.go generated vendored Normal file
View File

@ -0,0 +1,29 @@
package parser
import (
"strings"
)
// File represents a file unit.
type File struct {
InputFile *SourceFile
Stmts []Stmt
}
// Pos returns the position of first character belonging to the node.
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() Pos {
return Pos(n.InputFile.Base + n.InputFile.Size)
}
func (n *File) String() string {
var stmts []string
for _, e := range n.Stmts {
stmts = append(stmts, e.String())
}
return strings.Join(stmts, "; ")
}

156
vendor/github.com/d5/tengo/v2/parser/opcodes.go generated vendored Normal file
View File

@ -0,0 +1,156 @@
package parser
// Opcode represents a single byte operation code.
type Opcode = byte
// List of opcodes
const (
OpConstant Opcode = iota // Load constant
OpBComplement // bitwise complement
OpPop // Pop
OpTrue // Push true
OpFalse // Push false
OpEqual // Equal ==
OpNotEqual // Not equal !=
OpMinus // Minus -
OpLNot // Logical not !
OpJumpFalsy // Jump if falsy
OpAndJump // Logical AND jump
OpOrJump // Logical OR jump
OpJump // Jump
OpNull // Push null
OpArray // Array object
OpMap // Map object
OpError // Error object
OpImmutable // Immutable object
OpIndex // Index operation
OpSliceIndex // Slice operation
OpCall // Call function
OpReturn // Return
OpGetGlobal // Get global variable
OpSetGlobal // Set global variable
OpSetSelGlobal // Set global variable using selectors
OpGetLocal // Get local variable
OpSetLocal // Set local variable
OpDefineLocal // Define local variable
OpSetSelLocal // Set local variable using selectors
OpGetFreePtr // Get free variable pointer object
OpGetFree // Get free variables
OpSetFree // Set free variables
OpGetLocalPtr // Get local variable as a pointer
OpSetSelFree // Set free variables using selectors
OpGetBuiltin // Get builtin function
OpClosure // Push closure
OpIteratorInit // Iterator init
OpIteratorNext // Iterator next
OpIteratorKey // Iterator key
OpIteratorValue // Iterator value
OpBinaryOp // Binary operation
OpSuspend // Suspend VM
)
// OpcodeNames are string representation of opcodes.
var OpcodeNames = [...]string{
OpConstant: "CONST",
OpPop: "POP",
OpTrue: "TRUE",
OpFalse: "FALSE",
OpBComplement: "NEG",
OpEqual: "EQL",
OpNotEqual: "NEQ",
OpMinus: "NEG",
OpLNot: "NOT",
OpJumpFalsy: "JMPF",
OpAndJump: "ANDJMP",
OpOrJump: "ORJMP",
OpJump: "JMP",
OpNull: "NULL",
OpGetGlobal: "GETG",
OpSetGlobal: "SETG",
OpSetSelGlobal: "SETSG",
OpArray: "ARR",
OpMap: "MAP",
OpError: "ERROR",
OpImmutable: "IMMUT",
OpIndex: "INDEX",
OpSliceIndex: "SLICE",
OpCall: "CALL",
OpReturn: "RET",
OpGetLocal: "GETL",
OpSetLocal: "SETL",
OpDefineLocal: "DEFL",
OpSetSelLocal: "SETSL",
OpGetBuiltin: "BUILTIN",
OpClosure: "CLOSURE",
OpGetFreePtr: "GETFP",
OpGetFree: "GETF",
OpSetFree: "SETF",
OpGetLocalPtr: "GETLP",
OpSetSelFree: "SETSF",
OpIteratorInit: "ITER",
OpIteratorNext: "ITNXT",
OpIteratorKey: "ITKEY",
OpIteratorValue: "ITVAL",
OpBinaryOp: "BINARYOP",
OpSuspend: "SUSPEND",
}
// OpcodeOperands is the number of operands.
var OpcodeOperands = [...][]int{
OpConstant: {2},
OpPop: {},
OpTrue: {},
OpFalse: {},
OpBComplement: {},
OpEqual: {},
OpNotEqual: {},
OpMinus: {},
OpLNot: {},
OpJumpFalsy: {2},
OpAndJump: {2},
OpOrJump: {2},
OpJump: {2},
OpNull: {},
OpGetGlobal: {2},
OpSetGlobal: {2},
OpSetSelGlobal: {2, 1},
OpArray: {2},
OpMap: {2},
OpError: {},
OpImmutable: {},
OpIndex: {},
OpSliceIndex: {},
OpCall: {1},
OpReturn: {1},
OpGetLocal: {1},
OpSetLocal: {1},
OpDefineLocal: {1},
OpSetSelLocal: {1, 1},
OpGetBuiltin: {1},
OpClosure: {2, 1},
OpGetFreePtr: {1},
OpGetFree: {1},
OpSetFree: {1},
OpGetLocalPtr: {1},
OpSetSelFree: {1, 1},
OpIteratorInit: {},
OpIteratorNext: {},
OpIteratorKey: {},
OpIteratorValue: {},
OpBinaryOp: {1},
OpSuspend: {},
}
// ReadOperands reads operands from the bytecode.
func ReadOperands(numOperands []int, ins []byte) (operands []int, offset int) {
for _, width := range numOperands {
switch width {
case 1:
operands = append(operands, int(ins[offset]))
case 2:
operands = append(operands, int(ins[offset+1])|int(ins[offset])<<8)
}
offset += width
}
return
}

1196
vendor/github.com/d5/tengo/v2/parser/parser.go generated vendored Normal file

File diff suppressed because it is too large Load Diff

12
vendor/github.com/d5/tengo/v2/parser/pos.go generated vendored Normal file
View File

@ -0,0 +1,12 @@
package parser
// Pos represents a position in the file set.
type Pos int
// NoPos represents an invalid position.
const NoPos Pos = 0
// IsValid returns true if the position is valid.
func (p Pos) IsValid() bool {
return p != NoPos
}

689
vendor/github.com/d5/tengo/v2/parser/scanner.go generated vendored Normal file
View File

@ -0,0 +1,689 @@
package parser
import (
"fmt"
"unicode"
"unicode/utf8"
"github.com/d5/tengo/v2/token"
)
// byte order mark
const bom = 0xFEFF
// 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 *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 *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)))
}
s := &Scanner{
file: file,
src: src,
errorHandler: errorHandler,
ch: ' ',
mode: mode,
}
s.next()
if s.ch == bom {
s.next() // ignore BOM at file beginning
}
return s
}
// ErrorCount returns the number of errors.
func (s *Scanner) ErrorCount() int {
return s.errorCount
}
// Scan returns a token, token literal and its position.
func (s *Scanner) Scan() (
tok token.Token,
literal string,
pos Pos,
) {
s.skipWhitespace()
pos = s.file.FileSetPos(s.offset)
insertSemi := false
// determine token value
switch ch := s.ch; {
case isLetter(ch):
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:
insertSemi = true
}
case '0' <= ch && ch <= '9':
insertSemi = true
tok, literal = s.scanNumber(false)
default:
s.next() // always make progress
switch ch {
case -1: // EOF
if s.insertSemi {
s.insertSemi = false // EOF consumed
return token.Semicolon, "\n", pos
}
tok = token.EOF
case '\n':
// we only reach here if s.insertSemi was set in the first place
s.insertSemi = false // newline consumed
return token.Semicolon, "\n", pos
case '"':
insertSemi = true
tok = token.String
literal = s.scanString()
case '\'':
insertSemi = true
tok = token.Char
literal = s.scanRune()
case '`':
insertSemi = true
tok = token.String
literal = s.scanRawString()
case ':':
tok = s.switch2(token.Colon, token.Define)
case '.':
if '0' <= s.ch && s.ch <= '9' {
insertSemi = true
tok, literal = s.scanNumber(true)
} else {
tok = token.Period
if s.ch == '.' && s.peek() == '.' {
s.next()
s.next() // consume last '.'
tok = token.Ellipsis
}
}
case ',':
tok = token.Comma
case '?':
tok = token.Question
case ';':
tok = token.Semicolon
literal = ";"
case '(':
tok = token.LParen
case ')':
insertSemi = true
tok = token.RParen
case '[':
tok = token.LBrack
case ']':
insertSemi = true
tok = token.RBrack
case '{':
tok = token.LBrace
case '}':
insertSemi = true
tok = token.RBrace
case '+':
tok = s.switch3(token.Add, token.AddAssign, '+', token.Inc)
if tok == token.Inc {
insertSemi = true
}
case '-':
tok = s.switch3(token.Sub, token.SubAssign, '-', token.Dec)
if tok == token.Dec {
insertSemi = true
}
case '*':
tok = s.switch2(token.Mul, token.MulAssign)
case '/':
if s.ch == '/' || s.ch == '*' {
// comment
if s.insertSemi && s.findLineEnd() {
// reset position to the beginning of the comment
s.ch = '/'
s.offset = s.file.Offset(pos)
s.readOffset = s.offset + 1
s.insertSemi = false // newline consumed
return token.Semicolon, "\n", pos
}
comment := s.scanComment()
if s.mode&ScanComments == 0 {
// skip comment
s.insertSemi = false // newline consumed
return s.Scan()
}
tok = token.Comment
literal = comment
} else {
tok = s.switch2(token.Quo, token.QuoAssign)
}
case '%':
tok = s.switch2(token.Rem, token.RemAssign)
case '^':
tok = s.switch2(token.Xor, token.XorAssign)
case '<':
tok = s.switch4(token.Less, token.LessEq, '<',
token.Shl, token.ShlAssign)
case '>':
tok = s.switch4(token.Greater, token.GreaterEq, '>',
token.Shr, token.ShrAssign)
case '=':
tok = s.switch2(token.Assign, token.Equal)
case '!':
tok = s.switch2(token.Not, token.NotEqual)
case '&':
if s.ch == '^' {
s.next()
tok = s.switch2(token.AndNot, token.AndNotAssign)
} else {
tok = s.switch3(token.And, token.AndAssign, '&', token.LAnd)
}
case '|':
tok = s.switch3(token.Or, token.OrAssign, '|', token.LOr)
default:
// next reports unexpected BOMs - don't repeat
if ch != bom {
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
}
func (s *Scanner) next() {
if s.readOffset < len(s.src) {
s.offset = s.readOffset
if s.ch == '\n' {
s.lineOffset = s.offset
s.file.AddLine(s.offset)
}
r, w := rune(s.src[s.readOffset]), 1
switch {
case r == 0:
s.error(s.offset, "illegal character NUL")
case r >= utf8.RuneSelf:
// not ASCII
r, w = utf8.DecodeRune(s.src[s.readOffset:])
if r == utf8.RuneError && w == 1 {
s.error(s.offset, "illegal UTF-8 encoding")
} else if r == bom && s.offset > 0 {
s.error(s.offset, "illegal byte order mark")
}
}
s.readOffset += w
s.ch = r
} else {
s.offset = len(s.src)
if s.ch == '\n' {
s.lineOffset = s.offset
s.file.AddLine(s.offset)
}
s.ch = -1 // eof
}
}
func (s *Scanner) peek() byte {
if s.readOffset < len(s.src) {
return s.src[s.readOffset]
}
return 0
}
func (s *Scanner) error(offset int, msg string) {
if s.errorHandler != nil {
s.errorHandler(s.file.Position(s.file.FileSetPos(offset)), msg)
}
s.errorCount++
}
func (s *Scanner) scanComment() string {
// initial '/' already consumed; s.ch == '/' || s.ch == '*'
offs := s.offset - 1 // position of initial '/'
var numCR int
if s.ch == '/' {
//-style comment
// (the final '\n' is not considered part of the comment)
s.next()
for s.ch != '\n' && s.ch >= 0 {
if s.ch == '\r' {
numCR++
}
s.next()
}
goto exit
}
/*-style comment */
s.next()
for s.ch >= 0 {
ch := s.ch
if ch == '\r' {
numCR++
}
s.next()
if ch == '*' && s.ch == '/' {
s.next()
goto exit
}
}
s.error(offs, "comment not terminated")
exit:
lit := s.src[offs:s.offset]
// On Windows, a (//-comment) line may end in "\r\n".
// Remove the final '\r' before analyzing the text for line directives (matching the compiler).
// Remove any other '\r' afterwards (matching the pre-existing behavior of the scanner).
if numCR > 0 && len(lit) >= 2 && lit[1] == '/' && lit[len(lit)-1] == '\r' {
lit = lit[:len(lit)-1]
numCR--
}
if numCR > 0 {
lit = StripCR(lit, lit[1] == '*')
}
return string(lit)
}
func (s *Scanner) findLineEnd() bool {
// initial '/' already consumed
defer func(offs int) {
// reset scanner state to where it was upon calling findLineEnd
s.ch = '/'
s.offset = offs
s.readOffset = offs + 1
s.next() // consume initial '/' again
}(s.offset - 1)
// read ahead until a newline, EOF, or non-comment tok is found
for s.ch == '/' || s.ch == '*' {
if s.ch == '/' {
//-style comment always contains a newline
return true
}
/*-style comment: look for newline */
s.next()
for s.ch >= 0 {
ch := s.ch
if ch == '\n' {
return true
}
s.next()
if ch == '*' && s.ch == '/' {
s.next()
break
}
}
s.skipWhitespace() // s.insertSemi is set
if s.ch < 0 || s.ch == '\n' {
return true
}
if s.ch != '/' {
// non-comment tok
return false
}
s.next() // consume '/'
}
return false
}
func (s *Scanner) scanIdentifier() string {
offs := s.offset
for isLetter(s.ch) || isDigit(s.ch) {
s.next()
}
return string(s.src[offs:s.offset])
}
func (s *Scanner) scanMantissa(base int) {
for digitVal(s.ch) < base {
s.next()
}
}
func (s *Scanner) scanNumber(
seenDecimalPoint bool,
) (tok token.Token, lit string) {
// digitVal(s.ch) < 10
offs := s.offset
tok = token.Int
defer func() {
lit = string(s.src[offs:s.offset])
}()
if seenDecimalPoint {
offs--
tok = token.Float
s.scanMantissa(10)
goto exponent
}
if s.ch == '0' {
// int or float
offs := s.offset
s.next()
if s.ch == 'x' || s.ch == 'X' {
// hexadecimal int
s.next()
s.scanMantissa(16)
if s.offset-offs <= 2 {
// only scanned "0x" or "0X"
s.error(offs, "illegal hexadecimal number")
}
} else {
// octal int or float
seenDecimalDigit := false
s.scanMantissa(8)
if s.ch == '8' || s.ch == '9' {
// illegal octal int or float
seenDecimalDigit = true
s.scanMantissa(10)
}
if s.ch == '.' || s.ch == 'e' || s.ch == 'E' || s.ch == 'i' {
goto fraction
}
// octal int
if seenDecimalDigit {
s.error(offs, "illegal octal number")
}
}
return
}
// decimal int or float
s.scanMantissa(10)
fraction:
if s.ch == '.' {
tok = token.Float
s.next()
s.scanMantissa(10)
}
exponent:
if s.ch == 'e' || s.ch == 'E' {
tok = token.Float
s.next()
if s.ch == '-' || s.ch == '+' {
s.next()
}
if digitVal(s.ch) < 10 {
s.scanMantissa(10)
} else {
s.error(offs, "illegal floating-point exponent")
}
}
return
}
func (s *Scanner) scanEscape(quote rune) bool {
offs := s.offset
var n int
var base, max uint32
switch s.ch {
case 'a', 'b', 'f', 'n', 'r', 't', 'v', '\\', quote:
s.next()
return true
case '0', '1', '2', '3', '4', '5', '6', '7':
n, base, max = 3, 8, 255
case 'x':
s.next()
n, base, max = 2, 16, 255
case 'u':
s.next()
n, base, max = 4, 16, unicode.MaxRune
case 'U':
s.next()
n, base, max = 8, 16, unicode.MaxRune
default:
msg := "unknown escape sequence"
if s.ch < 0 {
msg = "escape sequence not terminated"
}
s.error(offs, msg)
return false
}
var x uint32
for n > 0 {
d := uint32(digitVal(s.ch))
if d >= base {
msg := fmt.Sprintf(
"illegal character %#U in escape sequence", s.ch)
if s.ch < 0 {
msg = "escape sequence not terminated"
}
s.error(s.offset, msg)
return false
}
x = x*base + d
s.next()
n--
}
if x > max || 0xD800 <= x && x < 0xE000 {
s.error(offs, "escape sequence is invalid Unicode code point")
return false
}
return true
}
func (s *Scanner) scanRune() string {
offs := s.offset - 1 // '\'' opening already consumed
valid := true
n := 0
for {
ch := s.ch
if ch == '\n' || ch < 0 {
// only report error if we don't have one already
if valid {
s.error(offs, "rune literal not terminated")
valid = false
}
break
}
s.next()
if ch == '\'' {
break
}
n++
if ch == '\\' {
if !s.scanEscape('\'') {
valid = false
}
// continue to read to closing quote
}
}
if valid && n != 1 {
s.error(offs, "illegal rune literal")
}
return string(s.src[offs:s.offset])
}
func (s *Scanner) scanString() string {
offs := s.offset - 1 // '"' opening already consumed
for {
ch := s.ch
if ch == '\n' || ch < 0 {
s.error(offs, "string literal not terminated")
break
}
s.next()
if ch == '"' {
break
}
if ch == '\\' {
s.scanEscape('"')
}
}
return string(s.src[offs:s.offset])
}
func (s *Scanner) scanRawString() string {
offs := s.offset - 1 // '`' opening already consumed
hasCR := false
for {
ch := s.ch
if ch < 0 {
s.error(offs, "raw string literal not terminated")
break
}
s.next()
if ch == '`' {
break
}
if ch == '\r' {
hasCR = true
}
}
lit := s.src[offs:s.offset]
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] == '/' {
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' {
s.next()
}
}
func (s *Scanner) switch2(tok0, tok1 token.Token) token.Token {
if s.ch == '=' {
s.next()
return tok1
}
return tok0
}
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 {
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)
}
func isDigit(ch rune) bool {
return '0' <= ch && ch <= '9' ||
ch >= utf8.RuneSelf && unicode.IsDigit(ch)
}
func digitVal(ch rune) int {
switch {
case '0' <= ch && ch <= '9':
return int(ch - '0')
case 'a' <= ch && ch <= 'f':
return int(ch - 'a' + 10)
case 'A' <= ch && ch <= 'F':
return int(ch - 'A' + 10)
}
return 16 // larger than any legal digit val
}

231
vendor/github.com/d5/tengo/v2/parser/source_file.go generated vendored Normal file
View File

@ -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
}

349
vendor/github.com/d5/tengo/v2/parser/stmt.go generated vendored Normal file
View File

@ -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 "<bad statement>"
}
// 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"
}

313
vendor/github.com/d5/tengo/v2/script.go generated vendored Normal file
View File

@ -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
}

34
vendor/github.com/d5/tengo/v2/stdlib/base64.go generated vendored Normal file
View File

@ -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),
},
}

View File

@ -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,
}

12
vendor/github.com/d5/tengo/v2/stdlib/errors.go generated vendored Normal file
View File

@ -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()}}
}

101
vendor/github.com/d5/tengo/v2/stdlib/fmt.go generated vendored Normal file
View File

@ -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
}

1049
vendor/github.com/d5/tengo/v2/stdlib/func_typedefs.go generated vendored Normal file

File diff suppressed because it is too large Load Diff

12
vendor/github.com/d5/tengo/v2/stdlib/hex.go generated vendored Normal file
View File

@ -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)},
}

146
vendor/github.com/d5/tengo/v2/stdlib/json.go generated vendored Normal file
View File

@ -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(),
}
}
}

358
vendor/github.com/d5/tengo/v2/stdlib/json/decode.go generated vendored Normal file
View File

@ -0,0 +1,358 @@
// A modified version of Go's JSON implementation.
// Copyright 2010 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 json
import (
"strconv"
"unicode"
"unicode/utf16"
"unicode/utf8"
"github.com/d5/tengo/v2"
)
// Decode parses the JSON-encoded data and returns the result object.
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()
}
// decodeState represents the state while decoding a JSON value.
type decodeState struct {
data []byte
off int // next read offset in data
opcode int // last read result
scan scanner
}
// readIndex returns the position of the last byte read.
func (d *decodeState) readIndex() int {
return d.off - 1
}
const phasePanicMsg = "JSON decoder out of sync - data changing underfoot?"
func (d *decodeState) init(data []byte) *decodeState {
d.data = data
d.off = 0
return d
}
// scanNext processes the byte at d.data[d.off].
func (d *decodeState) scanNext() {
if d.off < len(d.data) {
d.opcode = d.scan.step(&d.scan, d.data[d.off])
d.off++
} else {
d.opcode = d.scan.eof()
d.off = len(d.data) + 1 // mark processed EOF with len+1
}
}
// scanWhile processes bytes in d.data[d.off:] until it
// receives a scan code not equal to op.
func (d *decodeState) scanWhile(op int) {
s, data, i := &d.scan, d.data, d.off
for i < len(data) {
newOp := s.step(s, data[i])
i++
if newOp != op {
d.opcode = newOp
d.off = i
return
}
}
d.off = len(data) + 1 // mark processed EOF with len+1
d.opcode = d.scan.eof()
}
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() (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
}
arr = append(arr, o)
// Next token must be , or ].
if d.opcode == scanSkipSpace {
d.scanWhile(scanSkipSpace)
}
if d.opcode == scanEndArray {
break
}
if d.opcode != scanArrayValue {
panic(phasePanicMsg)
}
}
return &tengo.Array{Value: arr}, nil
}
func (d *decodeState) object() (tengo.Object, error) {
m := make(map[string]tengo.Object)
for {
// Read opening " of string key or closing }.
d.scanWhile(scanSkipSpace)
if d.opcode == scanEndObject {
// closing } - can only happen on first iteration.
break
}
if d.opcode != scanBeginLiteral {
panic(phasePanicMsg)
}
// Read string key.
start := d.readIndex()
d.scanWhile(scanContinue)
item := d.data[start:d.readIndex()]
key, ok := unquote(item)
if !ok {
panic(phasePanicMsg)
}
// Read : before value.
if d.opcode == scanSkipSpace {
d.scanWhile(scanSkipSpace)
}
if d.opcode != scanObjectKey {
panic(phasePanicMsg)
}
d.scanWhile(scanSkipSpace)
// Read value.
o, err := d.value()
if err != nil {
return nil, err
}
m[key] = o
// Next token must be , or }.
if d.opcode == scanSkipSpace {
d.scanWhile(scanSkipSpace)
}
if d.opcode == scanEndObject {
break
}
if d.opcode != scanObjectValue {
panic(phasePanicMsg)
}
}
return &tengo.Map{Value: m}, nil
}
func (d *decodeState) literal() (tengo.Object, error) {
// All bytes inside literal return scanContinue op code.
start := d.readIndex()
d.scanWhile(scanContinue)
item := d.data[start:d.readIndex()]
switch c := item[0]; c {
case 'n': // null
return tengo.UndefinedValue, nil
case 't', 'f': // true, false
if c == 't' {
return tengo.TrueValue, nil
}
return tengo.FalseValue, nil
case '"': // string
s, ok := unquote(item)
if !ok {
panic(phasePanicMsg)
}
return &tengo.String{Value: s}, nil
default: // number
if c != '-' && (c < '0' || c > '9') {
panic(phasePanicMsg)
}
n, _ := strconv.ParseFloat(string(item), 10)
return &tengo.Float{Value: n}, nil
}
}
// getu4 decodes \uXXXX from the beginning of s, returning the hex value,
// or it returns -1.
func getu4(s []byte) rune {
if len(s) < 6 || s[0] != '\\' || s[1] != 'u' {
return -1
}
var r rune
for _, c := range s[2:6] {
switch {
case '0' <= c && c <= '9':
c = c - '0'
case 'a' <= c && c <= 'f':
c = c - 'a' + 10
case 'A' <= c && c <= 'F':
c = c - 'A' + 10
default:
return -1
}
r = r*16 + rune(c)
}
return r
}
// unquote converts a quoted JSON string literal s into an actual string t.
// The rules are different than for Go, so cannot use strconv.Unquote.
func unquote(s []byte) (t string, ok bool) {
s, ok = unquoteBytes(s)
t = string(s)
return
}
func unquoteBytes(s []byte) (t []byte, ok bool) {
if len(s) < 2 || s[0] != '"' || s[len(s)-1] != '"' {
return
}
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.
r := 0
for r < len(s) {
c := s[r]
if c == '\\' || c == '"' || c < ' ' {
break
}
if c < utf8.RuneSelf {
r++
continue
}
rr, size := utf8.DecodeRune(s[r:])
if rr == utf8.RuneError && size == 1 {
break
}
r += size
}
if r == len(s) {
return s, true
}
b := make([]byte, len(s)+2*utf8.UTFMax)
w := copy(b, s[0:r])
for r < len(s) {
// Out of room? Can only happen if s is full of
// malformed UTF-8 and we're replacing each
// byte with RuneError.
if w >= len(b)-2*utf8.UTFMax {
nb := make([]byte, (len(b)+utf8.UTFMax)*2)
copy(nb, b[0:w])
b = nb
}
switch c := s[r]; {
case c == '\\':
r++
if r >= len(s) {
return
}
switch s[r] {
default:
return
case '"', '\\', '/', '\'':
b[w] = s[r]
r++
w++
case 'b':
b[w] = '\b'
r++
w++
case 'f':
b[w] = '\f'
r++
w++
case 'n':
b[w] = '\n'
r++
w++
case 'r':
b[w] = '\r'
r++
w++
case 't':
b[w] = '\t'
r++
w++
case 'u':
r--
rr := getu4(s[r:])
if rr < 0 {
return
}
r += 6
if utf16.IsSurrogate(rr) {
rr1 := getu4(s[r:])
dec := utf16.DecodeRune(rr, rr1)
if dec != unicode.ReplacementChar {
// A valid pair; consume.
r += 6
w += utf8.EncodeRune(b[w:], dec)
break
}
// Invalid surrogate; fall back to replacement rune.
rr = unicode.ReplacementChar
}
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:])
r += size
w += utf8.EncodeRune(b[w:], rr)
}
}
return b[0:w], true
}

146
vendor/github.com/d5/tengo/v2/stdlib/json/encode.go generated vendored Normal file
View File

@ -0,0 +1,146 @@
// A modified version of Go's JSON implementation.
// Copyright 2010 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 json
import (
"encoding/base64"
"errors"
"math"
"strconv"
"github.com/d5/tengo/v2"
)
// Encode returns the JSON encoding of the object.
func Encode(o tengo.Object) ([]byte, error) {
var b []byte
switch o := o.(type) {
case *tengo.Array:
b = append(b, '[')
len1 := len(o.Value) - 1
for idx, elem := range o.Value {
eb, err := Encode(elem)
if err != nil {
return nil, err
}
b = append(b, eb...)
if idx < len1 {
b = append(b, ',')
}
}
b = append(b, ']')
case *tengo.ImmutableArray:
b = append(b, '[')
len1 := len(o.Value) - 1
for idx, elem := range o.Value {
eb, err := Encode(elem)
if err != nil {
return nil, err
}
b = append(b, eb...)
if idx < len1 {
b = append(b, ',')
}
}
b = append(b, ']')
case *tengo.Map:
b = append(b, '{')
len1 := len(o.Value) - 1
idx := 0
for key, value := range o.Value {
b = strconv.AppendQuote(b, key)
b = append(b, ':')
eb, err := Encode(value)
if err != nil {
return nil, err
}
b = append(b, eb...)
if idx < len1 {
b = append(b, ',')
}
idx++
}
b = append(b, '}')
case *tengo.ImmutableMap:
b = append(b, '{')
len1 := len(o.Value) - 1
idx := 0
for key, value := range o.Value {
b = strconv.AppendQuote(b, key)
b = append(b, ':')
eb, err := Encode(value)
if err != nil {
return nil, err
}
b = append(b, eb...)
if idx < len1 {
b = append(b, ',')
}
idx++
}
b = append(b, '}')
case *tengo.Bool:
if o.IsFalsy() {
b = strconv.AppendBool(b, false)
} else {
b = strconv.AppendBool(b, true)
}
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 *tengo.Char:
b = strconv.AppendInt(b, int64(o.Value), 10)
case *tengo.Float:
var y []byte
f := o.Value
if math.IsInf(f, 0) || math.IsNaN(f) {
return nil, errors.New("unsupported float value")
}
// Convert as if by ES6 number to string conversion.
// This matches most other JSON generators.
abs := math.Abs(f)
fmt := byte('f')
if abs != 0 {
if abs < 1e-6 || abs >= 1e21 {
fmt = 'e'
}
}
y = strconv.AppendFloat(y, f, fmt, -1, 64)
if fmt == 'e' {
// clean up e-09 to e-9
n := len(y)
if n >= 4 && y[n-4] == 'e' && y[n-3] == '-' && y[n-2] == '0' {
y[n-2] = y[n-1]
y = y[:n-1]
}
}
b = append(b, y...)
case *tengo.Int:
b = strconv.AppendInt(b, o.Value, 10)
case *tengo.String:
b = strconv.AppendQuote(b, o.Value)
case *tengo.Time:
y, err := o.Value.MarshalJSON()
if err != nil {
return nil, err
}
b = append(b, y...)
case *tengo.Undefined:
b = append(b, "null"...)
default:
// unknown type: ignore
}
return b, nil
}

562
vendor/github.com/d5/tengo/v2/stdlib/json/scanner.go generated vendored Normal file
View File

@ -0,0 +1,562 @@
// A modified version of Go's JSON implementation.
// Copyright 2010 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 json
import "strconv"
func checkValid(data []byte, scan *scanner) error {
scan.reset()
for _, c := range data {
scan.bytes++
if scan.step(scan, c) == scanError {
return scan.err
}
}
if scan.eof() == scanError {
return scan.err
}
return nil
}
// A SyntaxError is a description of a JSON syntax error.
type SyntaxError struct {
msg string // description of error
Offset int64 // error occurred after reading Offset bytes
}
func (e *SyntaxError) Error() string { return e.msg }
// A scanner is a JSON scanning state machine.
// Callers call scan.reset() and then pass bytes in one at a time
// by calling scan.step(&scan, c) for each byte.
// The return value, referred to as an opcode, tells the
// caller about significant parsing events like beginning
// and ending literals, objects, and arrays, so that the
// caller can follow along if it wishes.
// The return value scanEnd indicates that a single top-level
// JSON value has been completed, *before* the byte that
// just got passed in. (The indication must be delayed in order
// to recognize the end of numbers: is 123 a whole value or
// the beginning of 12345e+6?).
type scanner struct {
// The step is a func to be called to execute the next transition.
// Also tried using an integer constant and a single func
// with a switch, but using the func directly was 10% faster
// on a 64-bit Mac Mini, and it's nicer to read.
step func(*scanner, byte) int
// Reached end of top-level value.
endTop bool
// Stack of what we're in the middle of - array values, object keys, object values.
parseState []int
// Error that happened, if any.
err error
// total bytes consumed, updated by decoder.Decode
bytes int64
}
// These values are returned by the state transition functions
// assigned to scanner.state and the method scanner.eof.
// They give details about the current state of the scan that
// callers might be interested to know about.
// It is okay to ignore the return value of any particular
// call to scanner.state: if one call returns scanError,
// every subsequent call will return scanError too.
const (
// Continue.
scanContinue = iota // uninteresting byte
scanBeginLiteral // end implied by next result != scanContinue
scanBeginObject // begin object
scanObjectKey // just finished object key (string)
scanObjectValue // just finished non-last object value
scanEndObject // end object (implies scanObjectValue if possible)
scanBeginArray // begin array
scanArrayValue // just finished array value
scanEndArray // end array (implies scanArrayValue if possible)
scanSkipSpace // space byte; can skip; known to be last "continue" result
// Stop.
scanEnd // top-level value ended *before* this byte; known to be first "stop" result
scanError // hit an error, scanner.err.
)
// These values are stored in the parseState stack.
// They give the current state of a composite value
// being scanned. If the parser is inside a nested value
// the parseState describes the nested state, outermost at entry 0.
const (
parseObjectKey = iota // parsing object key (before colon)
parseObjectValue // parsing object value (after colon)
parseArrayValue // parsing array value
)
// reset prepares the scanner for use.
// It must be called before calling s.step.
func (s *scanner) reset() {
s.step = stateBeginValue
s.parseState = s.parseState[0:0]
s.err = nil
s.endTop = false
}
// eof tells the scanner that the end of input has been reached.
// It returns a scan status just as s.step does.
func (s *scanner) eof() int {
if s.err != nil {
return scanError
}
if s.endTop {
return scanEnd
}
s.step(s, ' ')
if s.endTop {
return scanEnd
}
if s.err == nil {
s.err = &SyntaxError{"unexpected end of JSON input", s.bytes}
}
return scanError
}
// pushParseState pushes a new parse state p onto the parse stack.
func (s *scanner) pushParseState(p int) {
s.parseState = append(s.parseState, p)
}
// popParseState pops a parse state (already obtained) off the stack
// and updates s.step accordingly.
func (s *scanner) popParseState() {
n := len(s.parseState) - 1
s.parseState = s.parseState[0:n]
if n == 0 {
s.step = stateEndTop
s.endTop = true
} else {
s.step = stateEndValue
}
}
func isSpace(c byte) bool {
return c == ' ' || c == '\t' || c == '\r' || c == '\n'
}
// stateBeginValueOrEmpty is the state after reading `[`.
func stateBeginValueOrEmpty(s *scanner, c byte) int {
if c <= ' ' && isSpace(c) {
return scanSkipSpace
}
if c == ']' {
return stateEndValue(s, c)
}
return stateBeginValue(s, c)
}
// stateBeginValue is the state at the beginning of the input.
func stateBeginValue(s *scanner, c byte) int {
if c <= ' ' && isSpace(c) {
return scanSkipSpace
}
switch c {
case '{':
s.step = stateBeginStringOrEmpty
s.pushParseState(parseObjectKey)
return scanBeginObject
case '[':
s.step = stateBeginValueOrEmpty
s.pushParseState(parseArrayValue)
return scanBeginArray
case '"':
s.step = stateInString
return scanBeginLiteral
case '-':
s.step = stateNeg
return scanBeginLiteral
case '0': // beginning of 0.123
s.step = state0
return scanBeginLiteral
case 't': // beginning of true
s.step = stateT
return scanBeginLiteral
case 'f': // beginning of false
s.step = stateF
return scanBeginLiteral
case 'n': // beginning of null
s.step = stateN
return scanBeginLiteral
}
if '1' <= c && c <= '9' { // beginning of 1234.5
s.step = state1
return scanBeginLiteral
}
return s.error(c, "looking for beginning of value")
}
// stateBeginStringOrEmpty is the state after reading `{`.
func stateBeginStringOrEmpty(s *scanner, c byte) int {
if c <= ' ' && isSpace(c) {
return scanSkipSpace
}
if c == '}' {
n := len(s.parseState)
s.parseState[n-1] = parseObjectValue
return stateEndValue(s, c)
}
return stateBeginString(s, c)
}
// stateBeginString is the state after reading `{"key": value,`.
func stateBeginString(s *scanner, c byte) int {
if c <= ' ' && isSpace(c) {
return scanSkipSpace
}
if c == '"' {
s.step = stateInString
return scanBeginLiteral
}
return s.error(c, "looking for beginning of object key string")
}
// stateEndValue is the state after completing a value,
// such as after reading `{}` or `true` or `["x"`.
func stateEndValue(s *scanner, c byte) int {
n := len(s.parseState)
if n == 0 {
// Completed top-level before the current byte.
s.step = stateEndTop
s.endTop = true
return stateEndTop(s, c)
}
if c <= ' ' && isSpace(c) {
s.step = stateEndValue
return scanSkipSpace
}
ps := s.parseState[n-1]
switch ps {
case parseObjectKey:
if c == ':' {
s.parseState[n-1] = parseObjectValue
s.step = stateBeginValue
return scanObjectKey
}
return s.error(c, "after object key")
case parseObjectValue:
if c == ',' {
s.parseState[n-1] = parseObjectKey
s.step = stateBeginString
return scanObjectValue
}
if c == '}' {
s.popParseState()
return scanEndObject
}
return s.error(c, "after object key:value pair")
case parseArrayValue:
if c == ',' {
s.step = stateBeginValue
return scanArrayValue
}
if c == ']' {
s.popParseState()
return scanEndArray
}
return s.error(c, "after array element")
}
return s.error(c, "")
}
// stateEndTop is the state after finishing the top-level value,
// such as after reading `{}` or `[1,2,3]`.
// Only space characters should be seen now.
func stateEndTop(s *scanner, c byte) int {
if !isSpace(c) {
// Complain about non-space byte on next call.
s.error(c, "after top-level value")
}
return scanEnd
}
// stateInString is the state after reading `"`.
func stateInString(s *scanner, c byte) int {
if c == '"' {
s.step = stateEndValue
return scanContinue
}
if c == '\\' {
s.step = stateInStringEsc
return scanContinue
}
if c < 0x20 {
return s.error(c, "in string literal")
}
return scanContinue
}
// stateInStringEsc is the state after reading `"\` during a quoted string.
func stateInStringEsc(s *scanner, c byte) int {
switch c {
case 'b', 'f', 'n', 'r', 't', '\\', '/', '"':
s.step = stateInString
return scanContinue
case 'u':
s.step = stateInStringEscU
return scanContinue
}
return s.error(c, "in string escape code")
}
// stateInStringEscU is the state after reading `"\u` during a quoted string.
func stateInStringEscU(s *scanner, c byte) int {
if '0' <= c && c <= '9' || 'a' <= c && c <= 'f' || 'A' <= c && c <= 'F' {
s.step = stateInStringEscU1
return scanContinue
}
// numbers
return s.error(c, "in \\u hexadecimal character escape")
}
// stateInStringEscU1 is the state after reading `"\u1` during a quoted string.
func stateInStringEscU1(s *scanner, c byte) int {
if '0' <= c && c <= '9' || 'a' <= c && c <= 'f' || 'A' <= c && c <= 'F' {
s.step = stateInStringEscU12
return scanContinue
}
// numbers
return s.error(c, "in \\u hexadecimal character escape")
}
// stateInStringEscU12 is the state after reading `"\u12` during a quoted string.
func stateInStringEscU12(s *scanner, c byte) int {
if '0' <= c && c <= '9' || 'a' <= c && c <= 'f' || 'A' <= c && c <= 'F' {
s.step = stateInStringEscU123
return scanContinue
}
// numbers
return s.error(c, "in \\u hexadecimal character escape")
}
// stateInStringEscU123 is the state after reading `"\u123` during a quoted string.
func stateInStringEscU123(s *scanner, c byte) int {
if '0' <= c && c <= '9' || 'a' <= c && c <= 'f' || 'A' <= c && c <= 'F' {
s.step = stateInString
return scanContinue
}
// numbers
return s.error(c, "in \\u hexadecimal character escape")
}
// stateNeg is the state after reading `-` during a number.
func stateNeg(s *scanner, c byte) int {
if c == '0' {
s.step = state0
return scanContinue
}
if '1' <= c && c <= '9' {
s.step = state1
return scanContinue
}
return s.error(c, "in numeric literal")
}
// state1 is the state after reading a non-zero integer during a number,
// such as after reading `1` or `100` but not `0`.
func state1(s *scanner, c byte) int {
if '0' <= c && c <= '9' {
s.step = state1
return scanContinue
}
return state0(s, c)
}
// state0 is the state after reading `0` during a number.
func state0(s *scanner, c byte) int {
if c == '.' {
s.step = stateDot
return scanContinue
}
if c == 'e' || c == 'E' {
s.step = stateE
return scanContinue
}
return stateEndValue(s, c)
}
// stateDot is the state after reading the integer and decimal point in a number,
// such as after reading `1.`.
func stateDot(s *scanner, c byte) int {
if '0' <= c && c <= '9' {
s.step = stateDot0
return scanContinue
}
return s.error(c, "after decimal point in numeric literal")
}
// stateDot0 is the state after reading the integer, decimal point, and subsequent
// digits of a number, such as after reading `3.14`.
func stateDot0(s *scanner, c byte) int {
if '0' <= c && c <= '9' {
return scanContinue
}
if c == 'e' || c == 'E' {
s.step = stateE
return scanContinue
}
return stateEndValue(s, c)
}
// stateE is the state after reading the mantissa and e in a number,
// such as after reading `314e` or `0.314e`.
func stateE(s *scanner, c byte) int {
if c == '+' || c == '-' {
s.step = stateESign
return scanContinue
}
return stateESign(s, c)
}
// stateESign is the state after reading the mantissa, e, and sign in a number,
// such as after reading `314e-` or `0.314e+`.
func stateESign(s *scanner, c byte) int {
if '0' <= c && c <= '9' {
s.step = stateE0
return scanContinue
}
return s.error(c, "in exponent of numeric literal")
}
// stateE0 is the state after reading the mantissa, e, optional sign,
// and at least one digit of the exponent in a number,
// such as after reading `314e-2` or `0.314e+1` or `3.14e0`.
func stateE0(s *scanner, c byte) int {
if '0' <= c && c <= '9' {
return scanContinue
}
return stateEndValue(s, c)
}
// stateT is the state after reading `t`.
func stateT(s *scanner, c byte) int {
if c == 'r' {
s.step = stateTr
return scanContinue
}
return s.error(c, "in literal true (expecting 'r')")
}
// stateTr is the state after reading `tr`.
func stateTr(s *scanner, c byte) int {
if c == 'u' {
s.step = stateTru
return scanContinue
}
return s.error(c, "in literal true (expecting 'u')")
}
// stateTru is the state after reading `tru`.
func stateTru(s *scanner, c byte) int {
if c == 'e' {
s.step = stateEndValue
return scanContinue
}
return s.error(c, "in literal true (expecting 'e')")
}
// stateF is the state after reading `f`.
func stateF(s *scanner, c byte) int {
if c == 'a' {
s.step = stateFa
return scanContinue
}
return s.error(c, "in literal false (expecting 'a')")
}
// stateFa is the state after reading `fa`.
func stateFa(s *scanner, c byte) int {
if c == 'l' {
s.step = stateFal
return scanContinue
}
return s.error(c, "in literal false (expecting 'l')")
}
// stateFal is the state after reading `fal`.
func stateFal(s *scanner, c byte) int {
if c == 's' {
s.step = stateFals
return scanContinue
}
return s.error(c, "in literal false (expecting 's')")
}
// stateFals is the state after reading `fals`.
func stateFals(s *scanner, c byte) int {
if c == 'e' {
s.step = stateEndValue
return scanContinue
}
return s.error(c, "in literal false (expecting 'e')")
}
// stateN is the state after reading `n`.
func stateN(s *scanner, c byte) int {
if c == 'u' {
s.step = stateNu
return scanContinue
}
return s.error(c, "in literal null (expecting 'u')")
}
// stateNu is the state after reading `nu`.
func stateNu(s *scanner, c byte) int {
if c == 'l' {
s.step = stateNul
return scanContinue
}
return s.error(c, "in literal null (expecting 'l')")
}
// stateNul is the state after reading `nul`.
func stateNul(s *scanner, c byte) int {
if c == 'l' {
s.step = stateEndValue
return scanContinue
}
return s.error(c, "in literal null (expecting 'l')")
}
// stateError is the state after reaching a syntax error,
// such as after reading `[1}` or `5.1.2`.
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{
msg: "invalid character " + quoteChar(c) + " " + context,
Offset: s.bytes,
}
return scanError
}
// quoteChar formats c as a quoted character literal
func quoteChar(c byte) string {
// special cases - different from quoted strings
if c == '\'' {
return `'\''`
}
if c == '"' {
return `'"'`
}
// use quoted string with different quotation marks
s := strconv.Quote(string(c))
return "'" + s[1:len(s)-1] + "'"
}

233
vendor/github.com/d5/tengo/v2/stdlib/math.go generated vendored Normal file
View File

@ -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),
},
}

564
vendor/github.com/d5/tengo/v2/stdlib/os.go generated vendored Normal file
View File

@ -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
}

119
vendor/github.com/d5/tengo/v2/stdlib/os_exec.go generated vendored Normal file
View File

@ -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
},
},
},
}
}

117
vendor/github.com/d5/tengo/v2/stdlib/os_file.go generated vendored Normal file
View File

@ -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()})
},
},
},
}
}

76
vendor/github.com/d5/tengo/v2/stdlib/os_process.go generated vendored Normal file
View File

@ -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
},
},
},
}
}

138
vendor/github.com/d5/tengo/v2/stdlib/rand.go generated vendored Normal file
View File

@ -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
},
},
},
}
}

View File

@ -0,0 +1,8 @@
// Code generated using gensrcmods.go; DO NOT EDIT.
package stdlib
// SourceModules are source type standard library modules.
var SourceModules = map[string]string{
"enum": "is_enumerable := func(x) {\n return is_array(x) || is_map(x) || is_immutable_array(x) || is_immutable_map(x)\n}\n\nis_array_like := func(x) {\n return is_array(x) || is_immutable_array(x)\n}\n\nexport {\n // all returns true if the given function `fn` evaluates to a truthy value on\n // all of the items in `x`. It returns undefined if `x` is not enumerable.\n all: func(x, fn) {\n if !is_enumerable(x) { return undefined }\n\n for k, v in x {\n if !fn(k, v) { return false }\n }\n\n return true\n },\n // any returns true if the given function `fn` evaluates to a truthy value on\n // any of the items in `x`. It returns undefined if `x` is not enumerable.\n any: func(x, fn) {\n if !is_enumerable(x) { return undefined }\n\n for k, v in x {\n if fn(k, v) { return true }\n }\n\n return false\n },\n // chunk returns an array of elements split into groups the length of size.\n // If `x` can't be split evenly, the final chunk will be the remaining elements.\n // It returns undefined if `x` is not array.\n chunk: func(x, size) {\n if !is_array_like(x) || !size { return undefined }\n\n numElements := len(x)\n if !numElements { return [] }\n\n res := []\n idx := 0\n for idx < numElements {\n res = append(res, x[idx:idx+size])\n idx += size\n }\n\n return res\n },\n // at returns an element at the given index (if `x` is array) or\n // key (if `x` is map). It returns undefined if `x` is not enumerable.\n at: func(x, key) {\n if !is_enumerable(x) { return undefined }\n\n if is_array_like(x) {\n if !is_int(key) { return undefined }\n } else {\n if !is_string(key) { return undefined }\n }\n\n return x[key]\n },\n // each iterates over elements of `x` and invokes `fn` for each element. `fn` is\n // invoked with two arguments: `key` and `value`. `key` is an int index\n // if `x` is array. `key` is a string key if `x` is map. It does not iterate\n // and returns undefined if `x` is not enumerable.\n each: func(x, fn) {\n if !is_enumerable(x) { return undefined }\n\n for k, v in x {\n fn(k, v)\n }\n },\n // filter iterates over elements of `x`, returning an array of all elements `fn`\n // returns truthy for. `fn` is invoked with two arguments: `key` and `value`.\n // `key` is an int index if `x` is array. `key` is a string key if `x` is map.\n // It returns undefined if `x` is not enumerable.\n filter: func(x, fn) {\n if !is_array_like(x) { return undefined }\n\n dst := []\n for k, v in x {\n if fn(k, v) { dst = append(dst, v) }\n }\n\n return dst\n },\n // find iterates over elements of `x`, returning value of the first element `fn`\n // returns truthy for. `fn` is invoked with two arguments: `key` and `value`.\n // `key` is an int index if `x` is array. `key` is a string key if `x` is map.\n // It returns undefined if `x` is not enumerable.\n find: func(x, fn) {\n if !is_enumerable(x) { return undefined }\n\n for k, v in x {\n if fn(k, v) { return v }\n }\n },\n // find_key iterates over elements of `x`, returning key or index of the first\n // element `fn` returns truthy for. `fn` is invoked with two arguments: `key`\n // and `value`. `key` is an int index if `x` is array. `key` is a string key if\n // `x` is map. It returns undefined if `x` is not enumerable.\n find_key: func(x, fn) {\n if !is_enumerable(x) { return undefined }\n\n for k, v in x {\n if fn(k, v) { return k }\n }\n },\n // map creates an array of values by running each element in `x` through `fn`.\n // `fn` is invoked with two arguments: `key` and `value`. `key` is an int index\n // if `x` is array. `key` is a string key if `x` is map. It returns undefined\n // if `x` is not enumerable.\n map: func(x, fn) {\n if !is_enumerable(x) { return undefined }\n\n dst := []\n for k, v in x {\n dst = append(dst, fn(k, v))\n }\n\n return dst\n },\n // key returns the first argument.\n key: func(k, _) { return k },\n // value returns the second argument.\n value: func(_, v) { return v }\n}\n",
}

128
vendor/github.com/d5/tengo/v2/stdlib/srcmod_enum.tengo generated vendored Normal file
View File

@ -0,0 +1,128 @@
is_enumerable := func(x) {
return is_array(x) || is_map(x) || is_immutable_array(x) || is_immutable_map(x)
}
is_array_like := func(x) {
return is_array(x) || is_immutable_array(x)
}
export {
// all returns true if the given function `fn` evaluates to a truthy value on
// all of the items in `x`. It returns undefined if `x` is not enumerable.
all: func(x, fn) {
if !is_enumerable(x) { return undefined }
for k, v in x {
if !fn(k, v) { return false }
}
return true
},
// any returns true if the given function `fn` evaluates to a truthy value on
// any of the items in `x`. It returns undefined if `x` is not enumerable.
any: func(x, fn) {
if !is_enumerable(x) { return undefined }
for k, v in x {
if fn(k, v) { return true }
}
return false
},
// chunk returns an array of elements split into groups the length of size.
// If `x` can't be split evenly, the final chunk will be the remaining elements.
// It returns undefined if `x` is not array.
chunk: func(x, size) {
if !is_array_like(x) || !size { return undefined }
numElements := len(x)
if !numElements { return [] }
res := []
idx := 0
for idx < numElements {
res = append(res, x[idx:idx+size])
idx += size
}
return res
},
// at returns an element at the given index (if `x` is array) or
// key (if `x` is map). It returns undefined if `x` is not enumerable.
at: func(x, key) {
if !is_enumerable(x) { return undefined }
if is_array_like(x) {
if !is_int(key) { return undefined }
} else {
if !is_string(key) { return undefined }
}
return x[key]
},
// each iterates over elements of `x` and invokes `fn` for each element. `fn` is
// invoked with two arguments: `key` and `value`. `key` is an int index
// if `x` is array. `key` is a string key if `x` is map. It does not iterate
// and returns undefined if `x` is not enumerable.
each: func(x, fn) {
if !is_enumerable(x) { return undefined }
for k, v in x {
fn(k, v)
}
},
// filter iterates over elements of `x`, returning an array of all elements `fn`
// returns truthy for. `fn` is invoked with two arguments: `key` and `value`.
// `key` is an int index if `x` is array. `key` is a string key if `x` is map.
// It returns undefined if `x` is not enumerable.
filter: func(x, fn) {
if !is_array_like(x) { return undefined }
dst := []
for k, v in x {
if fn(k, v) { dst = append(dst, v) }
}
return dst
},
// find iterates over elements of `x`, returning value of the first element `fn`
// returns truthy for. `fn` is invoked with two arguments: `key` and `value`.
// `key` is an int index if `x` is array. `key` is a string key if `x` is map.
// It returns undefined if `x` is not enumerable.
find: func(x, fn) {
if !is_enumerable(x) { return undefined }
for k, v in x {
if fn(k, v) { return v }
}
},
// find_key iterates over elements of `x`, returning key or index of the first
// element `fn` returns truthy for. `fn` is invoked with two arguments: `key`
// and `value`. `key` is an int index if `x` is array. `key` is a string key if
// `x` is map. It returns undefined if `x` is not enumerable.
find_key: func(x, fn) {
if !is_enumerable(x) { return undefined }
for k, v in x {
if fn(k, v) { return k }
}
},
// map creates an array of values by running each element in `x` through `fn`.
// `fn` is invoked with two arguments: `key` and `value`. `key` is an int index
// if `x` is array. `key` is a string key if `x` is map. It returns undefined
// if `x` is not enumerable.
map: func(x, fn) {
if !is_enumerable(x) { return undefined }
dst := []
for k, v in x {
dst = append(dst, fn(k, v))
}
return dst
},
// key returns the first argument.
key: func(k, _) { return k },
// value returns the second argument.
value: func(_, v) { return v }
}

34
vendor/github.com/d5/tengo/v2/stdlib/stdlib.go generated vendored Normal file
View File

@ -0,0 +1,34 @@
package stdlib
//go:generate go run gensrcmods.go
import (
"github.com/d5/tengo/v2"
)
// AllModuleNames returns a list of all default module names.
func AllModuleNames() []string {
var names []string
for name := range BuiltinModules {
names = append(names, name)
}
for name := range SourceModules {
names = append(names, name)
}
return names
}
// GetModuleMap returns the module map that includes all modules
// for the given module names.
func GetModuleMap(names ...string) *tengo.ModuleMap {
modules := tengo.NewModuleMap()
for _, name := range names {
if mod := BuiltinModules[name]; mod != nil {
modules.AddBuiltinModule(name, mod)
}
if mod := SourceModules[name]; mod != "" {
modules.AddSourceModule(name, []byte(mod))
}
}
return modules
}

1072
vendor/github.com/d5/tengo/v2/stdlib/text.go generated vendored Normal file

File diff suppressed because it is too large Load Diff

251
vendor/github.com/d5/tengo/v2/stdlib/text_regexp.go generated vendored Normal file
View File

@ -0,0 +1,251 @@
package stdlib
import (
"regexp"
"github.com/d5/tengo/v2"
)
func makeTextRegexp(re *regexp.Regexp) *tengo.ImmutableMap {
return &tengo.ImmutableMap{
Value: map[string]tengo.Object{
// match(text) => bool
"match": &tengo.UserFunction{
Value: func(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
}
if re.MatchString(s1) {
ret = tengo.TrueValue
} else {
ret = tengo.FalseValue
}
return
},
},
// find(text) => array(array({text:,begin:,end:}))/undefined
// find(text, maxCount) => array(array({text:,begin:,end:}))/undefined
"find": &tengo.UserFunction{
Value: func(args ...tengo.Object) (
ret tengo.Object,
err error,
) {
numArgs := len(args)
if numArgs != 1 && numArgs != 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
}
if numArgs == 1 {
m := re.FindStringSubmatchIndex(s1)
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: s1[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
}
i2, ok := tengo.ToInt(args[1])
if !ok {
err = tengo.ErrInvalidArgumentType{
Name: "second",
Expected: "int(compatible)",
Found: args[1].TypeName(),
}
return
}
m := re.FindAllStringSubmatchIndex(s1, i2)
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: 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)
}
ret = arr
return
},
},
// replace(src, repl) => string
"replace": &tengo.UserFunction{
Value: func(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
}
s, ok := doTextRegexpReplace(re, s1, s2)
if !ok {
return nil, tengo.ErrStringLimit
}
ret = &tengo.String{Value: s}
return
},
},
// split(text) => array(string)
// split(text, maxCount) => array(string)
"split": &tengo.UserFunction{
Value: func(args ...tengo.Object) (
ret tengo.Object,
err error,
) {
numArgs := len(args)
if numArgs != 1 && numArgs != 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
}
var i2 = -1
if numArgs > 1 {
i2, ok = tengo.ToInt(args[1])
if !ok {
err = tengo.ErrInvalidArgumentType{
Name: "second",
Expected: "int(compatible)",
Found: args[1].TypeName(),
}
return
}
}
arr := &tengo.Array{}
for _, s := range re.Split(s1, i2) {
arr.Value = append(arr.Value,
&tengo.String{Value: s})
}
ret = arr
return
},
},
},
}
}
// Size-limit checking implementation of regexp.ReplaceAllString.
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]
}
if idx < len(src) {
if len(out)+len(src)-idx > tengo.MaxStringLen {
return "", false
}
out += src[idx:]
}
return out, true
}

1135
vendor/github.com/d5/tengo/v2/stdlib/times.go generated vendored Normal file

File diff suppressed because it is too large Load Diff

165
vendor/github.com/d5/tengo/v2/symbol_table.go generated vendored Normal file
View File

@ -0,0 +1,165 @@
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 {
parent *SymbolTable
block bool
store map[string]*Symbol
numDefinition int
maxDefinition int
freeSymbols []*Symbol
builtinSymbols []*Symbol
}
// NewSymbolTable creates a SymbolTable.
func NewSymbolTable() *SymbolTable {
return &SymbolTable{
store: make(map[string]*Symbol),
}
}
// Define adds a new symbol in the current scope.
func (t *SymbolTable) Define(name string) *Symbol {
symbol := &Symbol{Name: name, Index: t.nextIndex()}
t.numDefinition++
if t.Parent(true) == nil {
symbol.Scope = ScopeGlobal
} else {
symbol.Scope = ScopeLocal
}
t.store[name] = symbol
t.updateMaxDefs(symbol.Index + 1)
return symbol
}
// DefineBuiltin adds a symbol for builtin function.
func (t *SymbolTable) DefineBuiltin(index int, name string) *Symbol {
if t.parent != nil {
return t.parent.DefineBuiltin(index, name)
}
symbol := &Symbol{
Name: name,
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) {
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 {
return t.defineFree(symbol), depth, true
}
return
}
return
}
// Fork creates a new symbol table for a new scope.
func (t *SymbolTable) Fork(block bool) *SymbolTable {
return &SymbolTable{
store: make(map[string]*Symbol),
parent: t,
block: block,
}
}
// Parent returns the outer scope of the current symbol table.
func (t *SymbolTable) Parent(skipBlock bool) *SymbolTable {
if skipBlock && t.block {
return t.parent.Parent(skipBlock)
}
return t.parent
}
// MaxSymbols returns the total number of symbols defined in the scope.
func (t *SymbolTable) MaxSymbols() int {
return t.maxDefinition
}
// FreeSymbols returns free symbols for the scope.
func (t *SymbolTable) FreeSymbols() []*Symbol {
return t.freeSymbols
}
// BuiltinSymbols returns builtin symbols for the scope.
func (t *SymbolTable) BuiltinSymbols() []*Symbol {
if t.parent != nil {
return t.parent.BuiltinSymbols()
}
return t.builtinSymbols
}
// Names returns the name of all the symbols.
func (t *SymbolTable) Names() []string {
var names []string
for name := range t.store {
names = append(names, name)
}
return names
}
func (t *SymbolTable) nextIndex() int {
if t.block {
return t.parent.nextIndex() + t.numDefinition
}
return t.numDefinition
}
func (t *SymbolTable) updateMaxDefs(numDefs int) {
if numDefs > t.maxDefinition {
t.maxDefinition = numDefs
}
if t.block {
t.parent.updateMaxDefs(numDefs)
}
}
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
}

306
vendor/github.com/d5/tengo/v2/tengo.go generated vendored Normal file
View File

@ -0,0 +1,306 @@
package tengo
import (
"errors"
"fmt"
"strconv"
"time"
)
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 {
return
}
ok = true
if str, isStr := o.(*String); isStr {
v = str.Value
} else {
v = o.String()
}
return
}
// ToInt will try to convert object o to int value.
func ToInt(o Object) (v int, ok bool) {
switch o := o.(type) {
case *Int:
v = int(o.Value)
ok = true
case *Float:
v = int(o.Value)
ok = true
case *Char:
v = int(o.Value)
ok = true
case *Bool:
if o == TrueValue {
v = 1
}
ok = true
case *String:
c, err := strconv.ParseInt(o.Value, 10, 64)
if err == nil {
v = int(c)
ok = true
}
}
return
}
// ToInt64 will try to convert object o to int64 value.
func ToInt64(o Object) (v int64, ok bool) {
switch o := o.(type) {
case *Int:
v = o.Value
ok = true
case *Float:
v = int64(o.Value)
ok = true
case *Char:
v = int64(o.Value)
ok = true
case *Bool:
if o == TrueValue {
v = 1
}
ok = true
case *String:
c, err := strconv.ParseInt(o.Value, 10, 64)
if err == nil {
v = c
ok = true
}
}
return
}
// ToFloat64 will try to convert object o to float64 value.
func ToFloat64(o Object) (v float64, ok bool) {
switch o := o.(type) {
case *Int:
v = float64(o.Value)
ok = true
case *Float:
v = o.Value
ok = true
case *String:
c, err := strconv.ParseFloat(o.Value, 64)
if err == nil {
v = c
ok = true
}
}
return
}
// ToBool will try to convert object o to bool value.
func ToBool(o Object) (v bool, ok bool) {
ok = true
v = !o.IsFalsy()
return
}
// ToRune will try to convert object o to rune value.
func ToRune(o Object) (v rune, ok bool) {
switch o := o.(type) {
case *Int:
v = rune(o.Value)
ok = true
case *Char:
v = o.Value
ok = true
}
return
}
// ToByteSlice will try to convert object o to []byte value.
func ToByteSlice(o Object) (v []byte, ok bool) {
switch o := o.(type) {
case *Bytes:
v = o.Value
ok = true
case *String:
v = []byte(o.Value)
ok = true
}
return
}
// ToTime will try to convert object o to time.Time value.
func ToTime(o Object) (v time.Time, ok bool) {
switch o := o.(type) {
case *Time:
v = o.Value
ok = true
case *Int:
v = time.Unix(o.Value, 0)
ok = true
}
return
}
// ToInterface attempts to convert an object o to an interface{} value
func ToInterface(o Object) (res interface{}) {
switch o := o.(type) {
case *Int:
res = o.Value
case *String:
res = o.Value
case *Float:
res = o.Value
case *Bool:
res = o == TrueValue
case *Char:
res = o.Value
case *Bytes:
res = o.Value
case *Array:
res = make([]interface{}, len(o.Value))
for i, val := range o.Value {
res.([]interface{})[i] = ToInterface(val)
}
case *ImmutableArray:
res = make([]interface{}, len(o.Value))
for i, val := range o.Value {
res.([]interface{})[i] = ToInterface(val)
}
case *Map:
res = make(map[string]interface{})
for key, v := range o.Value {
res.(map[string]interface{})[key] = ToInterface(v)
}
case *ImmutableMap:
res = make(map[string]interface{})
for key, v := range o.Value {
res.(map[string]interface{})[key] = ToInterface(v)
}
case *Time:
res = o.Value
case *Error:
res = errors.New(o.String())
case *Undefined:
res = nil
case Object:
return o
}
return
}
// FromInterface will attempt to convert an interface{} v to a Tengo Object
func FromInterface(v interface{}) (Object, error) {
switch v := v.(type) {
case nil:
return UndefinedValue, nil
case string:
if len(v) > MaxStringLen {
return nil, ErrStringLimit
}
return &String{Value: v}, nil
case int64:
return &Int{Value: v}, nil
case int:
return &Int{Value: int64(v)}, nil
case bool:
if v {
return TrueValue, nil
}
return FalseValue, nil
case rune:
return &Char{Value: v}, nil
case byte:
return &Char{Value: rune(v)}, nil
case float64:
return &Float{Value: v}, nil
case []byte:
if len(v) > MaxBytesLen {
return nil, ErrBytesLimit
}
return &Bytes{Value: v}, nil
case error:
return &Error{Value: &String{Value: v.Error()}}, nil
case map[string]Object:
return &Map{Value: v}, nil
case map[string]interface{}:
kv := make(map[string]Object)
for vk, vv := range v {
vo, err := FromInterface(vv)
if err != nil {
return nil, err
}
kv[vk] = vo
}
return &Map{Value: kv}, nil
case []Object:
return &Array{Value: v}, nil
case []interface{}:
arr := make([]Object, len(v))
for i, e := range v {
vo, err := FromInterface(e)
if err != nil {
return nil, err
}
arr[i] = vo
}
return &Array{Value: arr}, nil
case time.Time:
return &Time{Value: v}, nil
case Object:
return v, nil
case CallableFunc:
return &UserFunction{Value: v}, nil
}
return nil, fmt.Errorf("cannot convert to object: %T", v)
}

225
vendor/github.com/d5/tengo/v2/token/token.go generated vendored Normal file
View File

@ -0,0 +1,225 @@
package token
import "strconv"
var keywords map[string]Token
// Token represents a token.
type Token int
// List of tokens
const (
Illegal Token = iota
EOF
Comment
_literalBeg
Ident
Int
Float
Char
String
_literalEnd
_operatorBeg
Add // +
Sub // -
Mul // *
Quo // /
Rem // %
And // &
Or // |
Xor // ^
Shl // <<
Shr // >>
AndNot // &^
AddAssign // +=
SubAssign // -=
MulAssign // *=
QuoAssign // /=
RemAssign // %=
AndAssign // &=
OrAssign // |=
XorAssign // ^=
ShlAssign // <<=
ShrAssign // >>=
AndNotAssign // &^=
LAnd // &&
LOr // ||
Inc // ++
Dec // --
Equal // ==
Less // <
Greater // >
Assign // =
Not // !
NotEqual // !=
LessEq // <=
GreaterEq // >=
Define // :=
Ellipsis // ...
LParen // (
LBrack // [
LBrace // {
Comma // ,
Period // .
RParen // )
RBrack // ]
RBrace // }
Semicolon // ;
Colon // :
Question // ?
_operatorEnd
_keywordBeg
Break
Continue
Else
For
Func
Error
Immutable
If
Return
Export
True
False
In
Undefined
Import
_keywordEnd
)
var tokens = [...]string{
Illegal: "ILLEGAL",
EOF: "EOF",
Comment: "COMMENT",
Ident: "IDENT",
Int: "INT",
Float: "FLOAT",
Char: "CHAR",
String: "STRING",
Add: "+",
Sub: "-",
Mul: "*",
Quo: "/",
Rem: "%",
And: "&",
Or: "|",
Xor: "^",
Shl: "<<",
Shr: ">>",
AndNot: "&^",
AddAssign: "+=",
SubAssign: "-=",
MulAssign: "*=",
QuoAssign: "/=",
RemAssign: "%=",
AndAssign: "&=",
OrAssign: "|=",
XorAssign: "^=",
ShlAssign: "<<=",
ShrAssign: ">>=",
AndNotAssign: "&^=",
LAnd: "&&",
LOr: "||",
Inc: "++",
Dec: "--",
Equal: "==",
Less: "<",
Greater: ">",
Assign: "=",
Not: "!",
NotEqual: "!=",
LessEq: "<=",
GreaterEq: ">=",
Define: ":=",
Ellipsis: "...",
LParen: "(",
LBrack: "[",
LBrace: "{",
Comma: ",",
Period: ".",
RParen: ")",
RBrack: "]",
RBrace: "}",
Semicolon: ";",
Colon: ":",
Question: "?",
Break: "break",
Continue: "continue",
Else: "else",
For: "for",
Func: "func",
Error: "error",
Immutable: "immutable",
If: "if",
Return: "return",
Export: "export",
True: "true",
False: "false",
In: "in",
Undefined: "undefined",
Import: "import",
}
func (tok Token) String() string {
s := ""
if 0 <= tok && tok < Token(len(tokens)) {
s = tokens[tok]
}
if s == "" {
s = "token(" + strconv.Itoa(int(tok)) + ")"
}
return s
}
// LowestPrec represents lowest operator precedence.
const LowestPrec = 0
// Precedence returns the precedence for the operator token.
func (tok Token) Precedence() int {
switch tok {
case LOr:
return 1
case LAnd:
return 2
case Equal, NotEqual, Less, LessEq, Greater, GreaterEq:
return 3
case Add, Sub, Or, Xor:
return 4
case Mul, Quo, Rem, Shl, Shr, And, AndNot:
return 5
}
return LowestPrec
}
// IsLiteral returns true if the token is a literal.
func (tok Token) IsLiteral() bool {
return _literalBeg < tok && tok < _literalEnd
}
// IsOperator returns true if the token is an operator.
func (tok Token) IsOperator() bool {
return _operatorBeg < tok && tok < _operatorEnd
}
// IsKeyword returns true if the token is a keyword.
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
}
}

136
vendor/github.com/d5/tengo/v2/variable.go generated vendored Normal file
View File

@ -0,0 +1,136 @@
package tengo
import (
"errors"
)
// Variable is a user-defined variable for the script.
type Variable struct {
name string
value Object
}
// NewVariable creates a Variable.
func NewVariable(name string, value interface{}) (*Variable, error) {
obj, err := FromInterface(value)
if err != nil {
return nil, err
}
return &Variable{
name: name,
value: obj,
}, nil
}
// Name returns the name of the variable.
func (v *Variable) Name() string {
return v.name
}
// Value returns an empty interface of the variable value.
func (v *Variable) Value() interface{} {
return ToInterface(v.value)
}
// ValueType returns the name of the value type.
func (v *Variable) ValueType() string {
return v.value.TypeName()
}
// 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, _ := ToInt(v.value)
return c
}
// 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, _ := 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.
func (v *Variable) Float() float64 {
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.
func (v *Variable) Char() rune {
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.
func (v *Variable) Bool() bool {
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.
func (v *Variable) Array() []interface{} {
switch val := v.value.(type) {
case *Array:
var arr []interface{}
for _, e := range val.Value {
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{}.
func (v *Variable) Map() map[string]interface{} {
switch val := v.value.(type) {
case *Map:
kv := make(map[string]interface{})
for mk, mv := range val.Value {
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.
func (v *Variable) String() string {
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.
func (v *Variable) Bytes() []byte {
c, _ := ToByteSlice(v.value)
return c
}
// Error returns an error if the underlying value is error object. If not,
// this returns nil.
func (v *Variable) Error() error {
err, ok := v.value.(*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() Object {
return v.value
}
// IsUndefined returns true if the underlying value is undefined.
func (v *Variable) IsUndefined() bool {
return v.value == UndefinedValue
}

883
vendor/github.com/d5/tengo/v2/vm.go generated vendored Normal file
View File

@ -0,0 +1,883 @@
package tengo
import (
"fmt"
"sync/atomic"
"github.com/d5/tengo/v2/parser"
"github.com/d5/tengo/v2/token"
)
// 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 []Object
stack [StackSize]Object
sp int
globals []Object
fileSet *parser.SourceFileSet
frames [MaxFrames]frame
framesIndex int
curFrame *frame
curInsts []byte
ip int
aborting int64
maxAllocs int64
allocs int64
err error
}
// NewVM creates a VM.
func NewVM(
bytecode *Bytecode,
globals []Object,
maxAllocs int64,
) *VM {
if globals == nil {
globals = make([]Object, GlobalsSize)
}
v := &VM{
constants: bytecode.Constants,
sp: 0,
globals: globals,
fileSet: bytecode.FileSet,
framesIndex: 1,
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
}
// Abort aborts the execution.
func (v *VM) Abort() {
atomic.StoreInt64(&v.aborting, 1)
}
// Run starts the execution.
func (v *VM) Run() (err error) {
// reset VM states
v.sp = 0
v.curFrame = &(v.frames[0])
v.curInsts = v.curFrame.fn.Instructions
v.framesIndex = 1
v.ip = -1
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)
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))
err = fmt.Errorf("%s\n\tat %s", err.Error(), filePos)
}
return err
}
return nil
}
func (v *VM) run() {
for atomic.LoadInt64(&v.aborting) == 0 {
v.ip++
switch v.curInsts[v.ip] {
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 parser.OpNull:
v.stack[v.sp] = UndefinedValue
v.sp++
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 == ErrInvalidOperator {
v.err = fmt.Errorf("invalid operation: %s %s %s",
left.TypeName(), tok.String(), right.TypeName())
return
}
v.err = e
return
}
v.allocs--
if v.allocs == 0 {
v.err = ErrObjectAllocLimit
return
}
v.stack[v.sp-2] = res
v.sp--
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] = TrueValue
} else {
v.stack[v.sp] = FalseValue
}
v.sp++
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] = FalseValue
} else {
v.stack[v.sp] = TrueValue
}
v.sp++
case parser.OpPop:
v.sp--
case parser.OpTrue:
v.stack[v.sp] = TrueValue
v.sp++
case parser.OpFalse:
v.stack[v.sp] = FalseValue
v.sp++
case parser.OpLNot:
operand := v.stack[v.sp-1]
v.sp--
if operand.IsFalsy() {
v.stack[v.sp] = TrueValue
} else {
v.stack[v.sp] = FalseValue
}
v.sp++
case parser.OpBComplement:
operand := v.stack[v.sp-1]
v.sp--
switch x := operand.(type) {
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())
return
}
case parser.OpMinus:
operand := v.stack[v.sp-1]
v.sp--
switch x := operand.(type) {
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 *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())
return
}
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 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 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 parser.OpJump:
pos := int(v.curInsts[v.ip+2]) | int(v.curInsts[v.ip+1])<<8
v.ip = pos - 1
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 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([]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
e := indexAssign(v.globals[globalIndex], val, selectors)
if e != nil {
v.err = e
return
}
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 parser.OpArray:
v.ip += 2
numElements := int(v.curInsts[v.ip]) | int(v.curInsts[v.ip-1])<<8
var elements []Object
for i := v.sp - numElements; i < v.sp; i++ {
elements = append(elements, v.stack[i])
}
v.sp -= numElements
var arr Object = &Array{Value: elements}
v.allocs--
if v.allocs == 0 {
v.err = ErrObjectAllocLimit
return
}
v.stack[v.sp] = arr
v.sp++
case parser.OpMap:
v.ip += 2
numElements := int(v.curInsts[v.ip]) | int(v.curInsts[v.ip-1])<<8
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.(*String).Value] = value
}
v.sp -= numElements
var m Object = &Map{Value: kv}
v.allocs--
if v.allocs == 0 {
v.err = ErrObjectAllocLimit
return
}
v.stack[v.sp] = m
v.sp++
case parser.OpError:
value := v.stack[v.sp-1]
var e Object = &Error{
Value: value,
}
v.allocs--
if v.allocs == 0 {
v.err = ErrObjectAllocLimit
return
}
v.stack[v.sp-1] = e
case parser.OpImmutable:
value := v.stack[v.sp-1]
switch value := value.(type) {
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 *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 parser.OpIndex:
index := v.stack[v.sp-1]
left := v.stack[v.sp-2]
v.sp -= 2
val, err := left.IndexGet(index)
if err != nil {
if err == ErrNotIndexable {
v.err = fmt.Errorf("not indexable: %s", index.TypeName())
return
}
if err == ErrInvalidIndexType {
v.err = fmt.Errorf("invalid index type: %s",
index.TypeName())
return
}
v.err = err
return
}
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 != UndefinedValue {
if low, ok := low.(*Int); ok {
lowIdx = low.Value
} else {
v.err = fmt.Errorf("invalid slice index type: %s",
low.TypeName())
return
}
}
switch left := left.(type) {
case *Array:
numElements := int64(len(left.Value))
var highIdx int64
if high == UndefinedValue {
highIdx = numElements
} else if high, ok := high.(*Int); ok {
highIdx = high.Value
} else {
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)
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 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 *ImmutableArray:
numElements := int64(len(left.Value))
var highIdx int64
if high == UndefinedValue {
highIdx = numElements
} else if high, ok := high.(*Int); ok {
highIdx = high.Value
} else {
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)
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 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 *String:
numElements := int64(len(left.Value))
var highIdx int64
if high == UndefinedValue {
highIdx = numElements
} else if high, ok := high.(*Int); ok {
highIdx = high.Value
} else {
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)
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 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 *Bytes:
numElements := int64(len(left.Value))
var highIdx int64
if high == UndefinedValue {
highIdx = numElements
} else if high, ok := high.(*Int); ok {
highIdx = high.Value
} else {
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)
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 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 parser.OpCall:
numArgs := int(v.curInsts[v.ip+1])
v.ip++
value := v.stack[v.sp-1-numArgs]
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
realArgs := callee.NumParameters - 1
varArgs := numArgs - realArgs
if varArgs >= 0 {
numArgs = realArgs + 1
args := make([]Object, varArgs)
spStart := v.sp - varArgs
for i := spStart; i < v.sp; i++ {
args[i-spStart] = v.stack[i]
}
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",
callee.NumParameters-1, numArgs)
} else {
v.err = fmt.Errorf(
"wrong number of arguments: want=%d, got=%d",
callee.NumParameters, numArgs)
}
return
}
// test if it's tail-call
if callee == v.curFrame.fn { // recursion
nextOp := v.curInsts[v.ip+1]
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.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 = callee.Free
v.curFrame.basePointer = v.sp - numArgs
v.curInsts = callee.Instructions
v.ip = -1
v.framesIndex++
v.sp = v.sp - numArgs + callee.NumLocals
} else {
var args []Object
args = append(args, v.stack[v.sp-numArgs:v.sp]...)
ret, e := value.Call(args...)
v.sp -= numArgs + 1
// runtime error
if e != nil {
if e == ErrWrongNumArguments {
v.err = fmt.Errorf(
"wrong number of arguments in call to '%s'",
value.TypeName())
return
}
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 = UndefinedValue
}
v.allocs--
if v.allocs == 0 {
v.err = ErrObjectAllocLimit
return
}
v.stack[v.sp] = ret
v.sp++
}
case parser.OpReturn:
v.ip++
var retVal Object
if int(v.curInsts[v.ip]) == 1 {
retVal = v.stack[v.sp-1]
} else {
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 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 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.
val := v.stack[v.sp-1]
v.sp--
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 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([]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.(*ObjectPtr); ok {
dst = *obj.Value
}
if e := indexAssign(dst, val, selectors); e != nil {
v.err = e
return
}
case parser.OpGetLocal:
v.ip++
localIndex := int(v.curInsts[v.ip])
val := v.stack[v.curFrame.basePointer+localIndex]
if obj, ok := val.(*ObjectPtr); ok {
val = *obj.Value
}
v.stack[v.sp] = val
v.sp++
case parser.OpGetBuiltin:
v.ip++
builtinIndex := int(v.curInsts[v.ip])
v.stack[v.sp] = builtinFuncs[builtinIndex]
v.sp++
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].(*CompiledFunction)
if !ok {
v.err = fmt.Errorf("not function: %s", fn.TypeName())
return
}
free := make([]*ObjectPtr, numFree)
for i := 0; i < numFree; i++ {
switch freeVar := (v.stack[v.sp-numFree+i]).(type) {
case *ObjectPtr:
free[i] = freeVar
default:
free[i] = &ObjectPtr{
Value: &v.stack[v.sp-numFree+i],
}
}
}
v.sp -= numFree
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 parser.OpGetFreePtr:
v.ip++
freeIndex := int(v.curInsts[v.ip])
val := v.curFrame.freeVars[freeIndex]
v.stack[v.sp] = val
v.sp++
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 parser.OpSetFree:
v.ip++
freeIndex := int(v.curInsts[v.ip])
*v.curFrame.freeVars[freeIndex].Value = v.stack[v.sp-1]
v.sp--
case parser.OpGetLocalPtr:
v.ip++
localIndex := int(v.curInsts[v.ip])
sp := v.curFrame.basePointer + localIndex
val := v.stack[sp]
var freeVar *ObjectPtr
if obj, ok := val.(*ObjectPtr); ok {
freeVar = obj
} else {
freeVar = &ObjectPtr{Value: &val}
v.stack[sp] = freeVar
}
v.stack[v.sp] = freeVar
v.sp++
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([]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
e := indexAssign(*v.curFrame.freeVars[freeIndex].Value,
val, selectors)
if e != nil {
v.err = e
return
}
case parser.OpIteratorInit:
var iterator Object
dst := v.stack[v.sp-1]
v.sp--
if !dst.CanIterate() {
v.err = fmt.Errorf("not iterable: %s", dst.TypeName())
return
}
iterator = dst.Iterate()
v.allocs--
if v.allocs == 0 {
v.err = ErrObjectAllocLimit
return
}
v.stack[v.sp] = iterator
v.sp++
case parser.OpIteratorNext:
iterator := v.stack[v.sp-1]
v.sp--
hasMore := iterator.(Iterator).Next()
if hasMore {
v.stack[v.sp] = TrueValue
} else {
v.stack[v.sp] = FalseValue
}
v.sp++
case parser.OpIteratorKey:
iterator := v.stack[v.sp-1]
v.sp--
val := iterator.(Iterator).Key()
v.stack[v.sp] = val
v.sp++
case parser.OpIteratorValue:
iterator := v.stack[v.sp-1]
v.sp--
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
}
}
}
// IsStackEmpty tests if the stack is empty or not.
func (v *VM) IsStackEmpty() bool {
return v.sp == 0
}
func indexAssign(dst, src Object, selectors []Object) error {
numSel := len(selectors)
for sidx := numSel - 1; sidx > 0; sidx-- {
next, err := dst.IndexGet(selectors[sidx])
if err != nil {
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
}
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
}