A fast script language for Go

The Tengo Language

GoDoc test Go Report Card

Tengo is a small, dynamic, fast, secure script language for Go.

Tengo is fast and secure because it's compiled/executed as bytecode on stack-based VM that's written in native Go.

/* 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

Features

Benchmark

fib(35) fibt(35) Language (Type)
Tengo 2,931ms 4ms Tengo (VM)
go-lua 4,824ms 4ms Lua (VM)
GopherLua 5,365ms 4ms Lua (VM)
goja 5,533ms 5ms JavaScript (VM)
starlark-go 11,495ms 5ms Starlark (Interpreter)
Yaegi 15,645ms 12ms Yaegi (Interpreter)
gpython 16,322ms 5ms Python (Interpreter)
otto 73,093ms 10ms JavaScript (Interpreter)
Anko 79,809ms 8ms Anko (Interpreter)
- - - -
Go 53ms 3ms Go (Native)
Lua 1,612ms 3ms Lua (Native)
Python 2,632ms 23ms Python 2 (Native)

* fib(35): Fibonacci(35)
* fibt(35): tail-call version of Fibonacci(35)
* Go does not read the source code from file, while all other cases do
* See here for commands/codes used

Quick Start

go get github.com/d5/tengo/v2

A simple Go example code that compiles/runs Tengo script code with some input/output values:

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

♥️ Like writing Go code? Come work at Skool. We're hiring!

Comments
  • new spread feature

    new spread feature

    #265 Docs are still missing but I will add later. Note: After Ellipsis space is allowed in this implementation not to break compatibility. Note: Any expression is allowed after ellipsis but it can return an error at parser level but I want to hear your ideas.

  •  please support range syntax

    please support range syntax

    array define : [1,2,3,4,5] range define: [1..5]

    This syntax is useful in an inline environment to effectively shorten script lengths.

    Some language support it like kotlin, groovy...etc

  • Can tengo module support changing script suffix?

    Can tengo module support changing script suffix?

    vm.Compile() Error {"error": "[uno.mshk]Compile Error: module file read error: open due.tengo: no such file or directory\n\tat (main):1:8"}

    vm.Compile() Error {"error": "[due.mshk]Compile Error: module file read error: open tre.tengo: no such file or directory\n\tat (main):1:8"}

  • Spread last call argument

    Spread last call argument

    • After feedback from #297 (spread in arrays, maps, call args), I removed spread for array and map, only last call argument can be spread.
    • Ellipsis must come after the argument, not before (like Go).
    • Only last argument can be spread (like Go).
    • Call Args and function params match is not mandatory, OpCall takes whatever in the stack after spread and use it as before, no major OpCall changes.
    • Docs are missing, will be added after PR approval.
    • Cost of this is not calculated in terms of performance. Simple benchmarks #265 show +~1% delta.
    • Major version change is not required.
    • This is a draft PR.
  • enable relative imports

    enable relative imports

    After considering what could be effective, I came up with this implementation. Docs and some tests are missing and will be added if it is accepted. It closes #280 . Implementation is based on @d5 comment Notes:

    • checkCyclicImport call removed which was called before compileModule in Compiler for file imports. It is already called in compileModule.
    • I added testdata folder and bunch of tengo scripts under cmd/tengo for testing relative imports with CI (testdata folder may be moved to lib root folder).
    • At Go side, user can set import dir to search importable tengo files which is updated for each module compiler based on file's dir.
    • Absolute paths are used to ensure detecting cyclic imports.
    • Physical file modules can be a symbolic link, they are resolved as well but following symbolic links is limited to max 2 levels (who needs more?).
    • Tengo CLI compiles and runs both compiled and module files without any problem.
    • I have only Linux machine, I dont know if this implementation works well on windows/mac (TODO).

    Comments please @d5 @geseq

  • Enable relative imports

    Enable relative imports

    Importing a module tre.tengo

    fun := func(a) {
      return a
    }
    
    tre := {
      fun: fun
    }
    export tre
    

    in another tengo script due.tengo (both files are in the same folder)

    tre := import(`tre`)
    
    funcy := func(a) {
      return tre.fun(a)
    }
    
    due := {
      funcy: funcy
    }
    export due
    

    throws an error, when tengo is invoked as CLI from another directory, e.g. the parent folder :

    Compile Error: module file read error: open tre.tengo: no such file or directory at due.tengo:2:8

    It works fine however when tengo is invoked from within the folder. This leads me to suspect the import is done relative to the working directory. Relative imports feel more intuitive though, or am I wrong. Is there already a way to import relative to another file?

  • builtin delete for maps and arrays

    builtin delete for maps and arrays

    builtin delete is ready. It works like Go's delete for maps but also works for array types. delete function returns only undefined value or raises runtime error. It mutates given map or array. doc and tests are added.

  • Symbol Table and Closure

    Symbol Table and Closure

    Hi,

    I encountered following behavior at v2.6

    f := undefined
    if true {
        a := 10
        f = func() {
            a = 20
        }
    }
    b := 5
    f()
    fmt := import("fmt")
    fmt.println(b)
    

    Output

    20
    

    Expected

    5
    

    Symbol table is forked before If statement and symbols next index is reset to the state before forking after If statement finished in compiler, because of that variable a and variable b has same symbol index and captured variable in f refers to wrong variable. What do you think?

  • Function re-assignment in another function causes SIGSEGV in Tengo VM

    Function re-assignment in another function causes SIGSEGV in Tengo VM

    The following Tengo code:

    func1 := func(a, b) {
        return a * b
    }
    
    func2 := func() {
        test := test(1, 2)
    }
    
    func2()
    

    While completely bonkers (re-assigning a function), produces a SIGSEGV, which should not happen.

    panic: runtime error: invalid memory address or nil pointer dereference
    [signal SIGSEGV: segmentation violation code=0x1 addr=0x28 pc=0x52eb39]
    
    goroutine 1 [running]:
    github.com/d5/tengo/v2.(*VM).run(0xc000180000)
    	/home/runner/work/tengo/tengo/vm.go:542 +0x2ac9
    github.com/d5/tengo/v2.(*VM).Run(0xc000180000, 0xc000180000, 0x400)
    	/home/runner/work/tengo/tengo/vm.go:77 +0xcf
    main.CompileAndRun(0xc0001026f0, 0xc000152000, 0x5e, 0x25e, 0xc0001163d0, 0xc, 0x0, 0xc000104120)
    	/home/runner/work/tengo/tengo/cmd/tengo/main.go:144 +0x1d0
    main.main()
    	/home/runner/work/tengo/tengo/cmd/tengo/main.go:83 +0x420
    

    I stumbled upon this by happenstance as a result of a typo in my code.

  • Add shebang support (#!)

    Add shebang support (#!)

    To use tengo as scripting language in the terminal, support for shebang (#!) would be beneficial.

    Additional question, what is the proposed file extension for tengo files?

  • spread syntax

    spread syntax

    Spread Syntax

    Now that the language has variadic functions, it would make sense to add something like Go's spread operator (...) to allow using the contents of arrays as variadic arguments.

    However, Tengo uses a stack-based vm. Implementing a spread-like operator would create a re-usable mechanism for "exploding" arrays onto the stack. We can take advantage of this by allowing "exploding" in array literals and at other positions in a function call besides the last.

    I've got most of an implementation complete in the explosions branch of my fork. For syntax and uses, see below.

    Syntax

    Array Literals

    insert/delete:

    x := [1, 2, 3, 4]
    x = [x[:2]..., "hi", x[2:]...] // [1, 2, "hi", 4, 5]
    x = [x[:2]..., x[3:]...] // [1, 2, 4, 5]
    

    concatenate:

    a := [1, 2]
    b := [3, 4]
    c := [5, 6]
    d := [a..., b..., c...] // [1, 2, 3, 4, 5, 6]
    

    mix & match:

    a := [2, 3]
    b := [5, 6]
    c := [1, a..., 4, b...] // [1, 2, 3, 4, 5, 6]
    

    explode custom Iterable implementations:

    iter := make_go_channel_iterator()
    list := [iter...] // [ (all values from the iterator) ]
    

    explode any value of an explodable type (including literals):

    x := [[1, 2]..., 3, [4, 5, 6]...] // [1, 2, 3, 4, 5, 6]
    

    Function Calls

    our example funcs:

    variadic_fn := func(a, b, c, ...args) { /* ... */ }
    normal_fn := func(a, b, c, d) { /* ... */ } 
    

    variadic function calls:

    variadic_fn(1, 2, 3) // a=1 b=2 c=3 args=[]
    variadic_fn(1, 2, 3, [4, 5]...) // a=1 b=2 c=3 args=[4, 5]
    variadic_fn(1, [2, 3]..., [4, 5]..., 6) // a=1 b=2 c=3 args=[4, 5, 6]
    

    normal function calls:

    normal_fn(1, 2, 3) // illegal, missing arguments
    normal_fn(1, 2, 3, [4, 5]...) // illegal, total # of arguments > # of function parameters
    normal_fn(1, [2, 3]..., [4]...) // a=1 b=2 c=3 d=4
    normal_fn([1, 2, 3, 4]...) // a=1, b=2, c=3, d=4
    

    example:

    apply := func(fn, ...applied) {
        return func(...args) {
            return fn(applied..., args...)
        }
    }
    
    apply(normal_fn, 1, 2)(3, 4) // a=1 b=2 c=3 d=4
    
  • Sorting

    Sorting

    I would like sort an array of objects based on a supplied function. Something like sort.Slice()

    sorting := import("sorting") myList := [{x:1}, {x:2},{x:3}] sorting.sort( myList, func( i, j ){ return myList[i].x < myList[j].x }

    The problem is, you can run a CompiledFunc for your UserFunc.

    My workaround was to pass the name of a field to the UserFunc...

    sorting.sort( myList, "x")

  • Better support for TZ locations in the times module

    Better support for TZ locations in the times module

    I've added a location parameter to times.date to allow for creating a timestamp in a specific location. The call now works the same as the equivalent Go function. This parameter is optional so as not to break existing code.

    Along with this I've also added times.in_location(t time, l string), which works similar to func (t Time) In(loc *Location) Time

  • Add Encode/Decode to Compiled object

    Add Encode/Decode to Compiled object

    Rationale:

    Compared to other scripting solutions for golang, tengo has a capability to execute bytecode. However:

    1. Currently, Encode/Decode is implemented only in Bytecode and global variables are not saved. In order to run a script, it needs to be compiled first.
    2. Because of (1), it's not possible to deliver/embed compiled script with host application, if it contains global variable(s).
  • Faster binary operations

    Faster binary operations

    Improves BinaryOp performance significantly

    Before:

    -------------------------------------
    fibonacci(35)
    -------------------------------------
    Result:  9227465
    Go:      42.388955ms
    Parser:  36.718µs
    Compile: 109.024µs
    VM:      2.787647146s
    -------------------------------------
    fibonacci(35) (tail-call #1)
    -------------------------------------
    Result:  9227465
    Go:      42.789713ms
    Parser:  21.297µs
    Compile: 70.948µs
    VM:      2.894222823s
    -------------------------------------
    fibonacci(35) (tail-call #2)
    -------------------------------------
    Result:  9227465
    Go:      256ns
    Parser:  17.864µs
    Compile: 60.804µs
    VM:      14.668µs 
    

    After:

    -------------------------------------
    fibonacci(35)
    -------------------------------------
    Result:  9227465
    Go:      41.610339ms
    Parser:  33.751µs
    Compile: 98.728µs
    VM:      2.252044656s
    -------------------------------------
    fibonacci(35) (tail-call #1)
    -------------------------------------
    Result:  9227465
    Go:      41.661108ms
    Parser:  18.044µs
    Compile: 59.931µs
    VM:      2.700379139s
    -------------------------------------
    fibonacci(35) (tail-call #2)
    -------------------------------------
    Result:  9227465
    Go:      240ns
    Parser:  22.475µs
    Compile: 70.738µs
    VM:      22.121µs
    
Fast, portable, non-Turing complete expression evaluation with gradual typing (Go)

Common Expression Language The Common Expression Language (CEL) is a non-Turing complete language designed for simplicity, speed, safety, and portabil

Dec 24, 2022
Expression evaluation engine for Go: fast, non-Turing complete, dynamic typing, static typing
Expression evaluation engine for Go: fast, non-Turing complete, dynamic typing, static typing

Expr Expr package provides an engine that can compile and evaluate expressions. An expression is a one-liner that returns a value (mostly, but not lim

Dec 30, 2022
Starlark in Go: the Starlark configuration language, implemented in Go

Starlark in Go This is the home of the Starlark in Go project. Starlark in Go is an interpreter for Starlark, implemented in Go. Starlark was formerly

Jan 2, 2023
PHP bindings for the Go programming language (Golang)

PHP bindings for Go This package implements support for executing PHP scripts, exporting Go variables for use in PHP contexts, attaching Go method rec

Jan 1, 2023
Compiler for a small language into x86-64 Assembly

Compiler This project is a small compiler, that compiles my own little language into X86-64 Assembly. It then uses yasm and ld to assemble and link in

Dec 13, 2022
Elvish = Expressive Programming Language + Versatile Interactive Shell

Elvish: Expressive Programming Language + Versatile Interactive Shell Elvish is an expressive programming language and a versatile interactive shell,

Dec 25, 2022
A compiler for the ReCT programming language written in Golang

ReCT-Go-Compiler A compiler for the ReCT programming language written in Golang

Nov 30, 2022
A fast script language for Go
A fast script language for Go

The Tengo Language Tengo is a small, dynamic, fast, secure script language for Go. Tengo is fast and secure because it's compiled/executed as bytecode

Dec 30, 2022
Q Language : A script language for Go
Q Language : A script language for Go

Q Language - A script language for Go 语言特色 与 Go 语言有最好的互操作性。可不进行任何包装即可直接使用 Go 语言的函数、类及其成员变量和方法。 有赖于 Go 语言的互操作性,这门语言直接拥有了一套非常完整且您十分熟悉的标准库,无额外学习成本。 与 Go

Sep 5, 2022
WebWalker - Fast Script To Walk Web for find urls...

WebWalker send http request to url to get all urls in url and send http request to urls and again .... WebWalker can find 10,000 urls in 10 seconds.

Nov 28, 2021
🚀 Platform providing a powerful and fast public script parsing API dedicated to the Skript community.

SkriptMC-Parser is currently a prototype in the early stages of development of a system that allows the Skript community to test their scripts via a public API for potential errors or warnings. This is a quick and easy way to check your scripts without having to set up a Spigot server on your environment.

Mar 3, 2022
Gentee - script programming language for automation. It uses VM and compiler written in Go (Golang).

Gentee script programming language Gentee is a free open source script programming language. The Gentee programming language is designed to create scr

Dec 15, 2022
Gentee - script programming language for automation. It uses VM and compiler written in Go (Golang).

Gentee script programming language Gentee is a free open source script programming language. The Gentee programming language is designed to create scr

Dec 15, 2022
The interpreter for qiitan script. Yet another dialect of the Tengo language.

Qiitan は、Qiita ™️ の SNS である「Qiitadonβ」のマスコット・キャラクターです。 キーたん(Qiitan) @ Qiitadon Qiitan-go は Qiitan のファン・アプリであり、Qiita ™️ とは一切関係がありません。 Qiitan-goalpha キー

Feb 6, 2022
xlsxlang is a tiny toy script programming language. xlsxlang is heavily inspired by Lisp
xlsxlang is a tiny toy script programming language. xlsxlang is heavily inspired by Lisp

xlsxlang Table of Contents 1. Usage 1.1. Examples 2. Installation 3. Supported functions 4. LICENSE xlsxlang is a tiny toy script programming language

Feb 11, 2022
Golang bindings of Sciter: the Embeddable HTML/CSS/script engine for modern UI development
Golang bindings of Sciter: the Embeddable HTML/CSS/script engine for modern UI development

Go bindings for Sciter Check this page for other language bindings (Delphi / D / Go / .NET / Python / Rust). Attention The ownership of project is tra

Dec 23, 2022
Shell script to download and set GO environmental paths to allow multiple versions.
Shell script to download and set GO environmental paths to allow multiple versions.

gobrew gobrew lets you easily switch between multiple versions of go. It is based on rbenv and pyenv. Installation The automatic installer You can ins

Nov 3, 2022
A simple Go script to brute force or parse a password-protected PKCS#12 (PFX/P12) file.
A simple Go script to brute force or parse a password-protected PKCS#12 (PFX/P12) file.

A simple Go script to brute force or parse a password-protected PKCS#12 (PFX/P12) file.

Oct 14, 2022
View the script files in the original Resident Evil 2 / Biohazard 2 as pseudocode
View the script files in the original Resident Evil 2 / Biohazard 2 as pseudocode

Resident Evil 2 Script Viewer About You can view the script files in the original Resident Evil 2 / Biohazard 2 as pseudocode next to the original byt

Jan 20, 2022
流媒体NetFlix解锁检测脚本 / A script used to determine whether your network can watch native Netflix movies or not
流媒体NetFlix解锁检测脚本 / A script used to determine whether your network can watch native Netflix movies or not

netflix-verify 流媒体NetFlix解锁检测脚本,使用Go语言编写 在VPS网络正常的情况下,哪怕是双栈网络也可在几秒内快速完成IPv4/IPv6的解锁判断 鸣谢 感谢 @CoiaPrant 指出对于地域检测更简便的方法 感谢 @XmJwit 解决了IPV6 Only VPS无法下载脚

Dec 29, 2022