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:
1
vendor/github.com/d5/tengo/v2/.gitignore
generated
vendored
Normal file
1
vendor/github.com/d5/tengo/v2/.gitignore
generated
vendored
Normal file
@ -0,0 +1 @@
|
||||
dist/
|
20
vendor/github.com/d5/tengo/v2/.goreleaser.yml
generated
vendored
Normal file
20
vendor/github.com/d5/tengo/v2/.goreleaser.yml
generated
vendored
Normal 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
21
vendor/github.com/d5/tengo/v2/LICENSE
generated
vendored
Normal 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
11
vendor/github.com/d5/tengo/v2/Makefile
generated
vendored
Normal 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
135
vendor/github.com/d5/tengo/v2/README.md
generated
vendored
Normal 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
|
||||
|
||||
[](https://godoc.org/github.com/d5/tengo)
|
||||
[](https://goreportcard.com/report/github.com/d5/tengo)
|
||||
[](https://circleci.com/gh/d5/tengo)
|
||||
[](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
502
vendor/github.com/d5/tengo/v2/builtins.go
generated
vendored
Normal 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
292
vendor/github.com/d5/tengo/v2/bytecode.go
generated
vendored
Normal 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
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
3
vendor/github.com/d5/tengo/v2/doc.go
generated
vendored
Normal 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
64
vendor/github.com/d5/tengo/v2/errors.go
generated
vendored
Normal 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
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
3
vendor/github.com/d5/tengo/v2/go.mod
generated
vendored
Normal 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
0
vendor/github.com/d5/tengo/v2/go.sum
generated
vendored
Normal file
61
vendor/github.com/d5/tengo/v2/instructions.go
generated
vendored
Normal file
61
vendor/github.com/d5/tengo/v2/instructions.go
generated
vendored
Normal 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
209
vendor/github.com/d5/tengo/v2/iterator.go
generated
vendored
Normal 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
93
vendor/github.com/d5/tengo/v2/modules.go
generated
vendored
Normal 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
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
69
vendor/github.com/d5/tengo/v2/parser/ast.go
generated
vendored
Normal 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
597
vendor/github.com/d5/tengo/v2/parser/expr.go
generated
vendored
Normal 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
29
vendor/github.com/d5/tengo/v2/parser/file.go
generated
vendored
Normal 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
156
vendor/github.com/d5/tengo/v2/parser/opcodes.go
generated
vendored
Normal 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
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
12
vendor/github.com/d5/tengo/v2/parser/pos.go
generated
vendored
Normal 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
689
vendor/github.com/d5/tengo/v2/parser/scanner.go
generated
vendored
Normal 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
231
vendor/github.com/d5/tengo/v2/parser/source_file.go
generated
vendored
Normal 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
349
vendor/github.com/d5/tengo/v2/parser/stmt.go
generated
vendored
Normal 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
313
vendor/github.com/d5/tengo/v2/script.go
generated
vendored
Normal 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
34
vendor/github.com/d5/tengo/v2/stdlib/base64.go
generated
vendored
Normal 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),
|
||||
},
|
||||
}
|
18
vendor/github.com/d5/tengo/v2/stdlib/builtin_modules.go
generated
vendored
Normal file
18
vendor/github.com/d5/tengo/v2/stdlib/builtin_modules.go
generated
vendored
Normal 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
12
vendor/github.com/d5/tengo/v2/stdlib/errors.go
generated
vendored
Normal 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
101
vendor/github.com/d5/tengo/v2/stdlib/fmt.go
generated
vendored
Normal 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
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
12
vendor/github.com/d5/tengo/v2/stdlib/hex.go
generated
vendored
Normal 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
146
vendor/github.com/d5/tengo/v2/stdlib/json.go
generated
vendored
Normal 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
358
vendor/github.com/d5/tengo/v2/stdlib/json/decode.go
generated
vendored
Normal 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
146
vendor/github.com/d5/tengo/v2/stdlib/json/encode.go
generated
vendored
Normal 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
562
vendor/github.com/d5/tengo/v2/stdlib/json/scanner.go
generated
vendored
Normal 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
233
vendor/github.com/d5/tengo/v2/stdlib/math.go
generated
vendored
Normal 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
564
vendor/github.com/d5/tengo/v2/stdlib/os.go
generated
vendored
Normal 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
119
vendor/github.com/d5/tengo/v2/stdlib/os_exec.go
generated
vendored
Normal 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
117
vendor/github.com/d5/tengo/v2/stdlib/os_file.go
generated
vendored
Normal 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
76
vendor/github.com/d5/tengo/v2/stdlib/os_process.go
generated
vendored
Normal 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
138
vendor/github.com/d5/tengo/v2/stdlib/rand.go
generated
vendored
Normal 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
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
8
vendor/github.com/d5/tengo/v2/stdlib/source_modules.go
generated
vendored
Normal file
8
vendor/github.com/d5/tengo/v2/stdlib/source_modules.go
generated
vendored
Normal 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
128
vendor/github.com/d5/tengo/v2/stdlib/srcmod_enum.tengo
generated
vendored
Normal 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
34
vendor/github.com/d5/tengo/v2/stdlib/stdlib.go
generated
vendored
Normal 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
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
251
vendor/github.com/d5/tengo/v2/stdlib/text_regexp.go
generated
vendored
Normal 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
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
165
vendor/github.com/d5/tengo/v2/symbol_table.go
generated
vendored
Normal 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
306
vendor/github.com/d5/tengo/v2/tengo.go
generated
vendored
Normal 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
225
vendor/github.com/d5/tengo/v2/token/token.go
generated
vendored
Normal 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
136
vendor/github.com/d5/tengo/v2/variable.go
generated
vendored
Normal 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
883
vendor/github.com/d5/tengo/v2/vm.go
generated
vendored
Normal 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
|
||||
}
|
Reference in New Issue
Block a user