Scriptable interpreter written in golang



GoDoc Reference Build Status Financial Contributors on Open Collective Coverage Go Report Card

Anko is a scriptable interpreter written in Go.

(Picture licensed under CC BY-SA 3.0, photo by Ocdp)

Usage Example - Embedded

package main

import (


func main() {
	e := env.NewEnv()

	err := e.Define("println", fmt.Println)
	if err != nil {
		log.Fatalf("Define error: %v\n", err)

	script := `
println("Hello World :)")

	_, err = vm.Execute(e, nil, script)
	if err != nil {
		log.Fatalf("Execute error: %v\n", err)

	// output: Hello World :)

More examples are located in the GoDoc:

Usage Example - Command Line


go get
go install

Running an Anko script file named script.ank

./anko script.ank

Anko Script Quick Start

// declare variables
x = 1
y = x + 1

// print using outside the script defined println function
println(x + y) // 3

// if else statement
if x < 1 || y < 1 {
} else if x < 1 && y < 1 {
} else {
	println(x + y)

// slice
a = []interface{1, 2, 3}
println(a) // [1 2 3]
println(a[0]) // 1

// map
a = map[interface]interface{"x": 1}
println(a) // map[x:1]
a.b = 2
a["c"] = 3
println(a["b"]) // 2
println(a.c) // 3

// struct
a = make(struct {
	A int64,
	B float64
a.A = 4
a.B = 5.5
println(a.A) // 4
println(a.B) // 5.5

// function
func a (x) {
	println(x + 1)
a(5) // 6

Please note that the master branch is not stable

The master branch language and API may change at any time.

To mitigate breaking changes, please use tagged branches. New tagged branches will be created for breaking changes.


Yasuhiro Matsumoto (a.k.a mattn)


Code Contributors

This project exists thanks to all the people who contribute. [Contribute].

Financial Contributors

Become a financial contributor and help us sustain our community. [Contribute]



Support this project with your organization. Your logo will show up here with a link to your website. [Contribute]

  • Add context so we can interrupt channels

    Add context so we can interrupt channels

    Problem: When you have a script that ends with "receive from chan", the script get stuck there forever, and vm.Interrupt(env) is unable to cancel the execution of that script. Example:

    ch = make(chan string)

    Solution: I added an optional (e *Env) SetContext(context.Context) *Env function. When you give a context to this function, it will change how it interrupt the code inside the VM. There is 2 main differences

    • The main loop will check for each statement if there is a context, and if it should exit the loop.
    • The channels will now "select" between the ctx.Done() and the channel, instead of just waiting forever on the ch.Recv()

    If there is no context, none of the above apply, the code execute as it was before.

    The test TestInterruptChannelWithContext will break the current master

    opened by alaingilbert 22
  • Added Copy() function on vm.Env

    Added Copy() function on vm.Env

    I need this function to clone a VM in the NeON build tool. As vm.Env fields are not public, I have to fork Anko project to implement this Copy() function. This would be great if this was integrated in the Anko project.

    opened by c4s4 17
  • **Breaking change for types** plus more

    **Breaking change for types** plus more

    Breaking change for types

    Make arrays last two args now optional Changed type to be an expression Type expression converts to string for type lookup DefineType is now local scope Added DefineReflectType Added DefineGlobalType & DefineGlobalReflectType Added make multidimensional array support Improved multidimensional array support Added AddPackage

    opened by MichaelS11 17
  • panic vm.Error instead of plain string

    panic vm.Error instead of plain string

    convertVMFunctionToType panics strings if any error happens, which blocks invokers form acquiring the position where errors occur. If we could panic the raw vm.Error, the invoker will be able to report the line and column of the error. IDEs and programmers will benefit from the change. Thanks. :)

    opened by Berailitz 14
  • goroutine crash silently

    goroutine crash silently

    Any idea how we could make a crash in a goroutine stop the VM ? eg:

    time = import("time")
    for {
      go func() {
        This should stop the VM
        println("will never print")

    This program will not crash and will just keep running as if everything is fine. It would be nice to stop the VM in such case, and get back the error.

    My workaround right now is to define a Go function

    ctx, cancel := context.WithCancel(context.Background())
    _ = myvm.Define("Go", func(fn func()) {
      go func() {
        defer func() {
          if r := recover(); r != nil {
    myscript := `
    time = import("time")
    for {
      Go(func() {
        This will stop the VM
        println("will never print")
    myvm.ExecuteContext(ctx, myscript)

    But I have to edit the original anko code to prevent the use of the go keyword.

    opened by alaingilbert 14
  • Nil coalescing operator

    Nil coalescing operator

    Added 'if invalid' operator similar to Swift's nil coalescing operator, adapted for scripting. This can be extremely useful in eliminating unnecessarily lengthy code.

    The difference being that instead of checking for nil the operator checks to see if the left side is valid (error, empty slice, empty map or zero value).

    opened by geseq 14
  • channel types and other typing problems

    channel types and other typing problems

    Apologies if this veers a little... philosophical.

    Anko comprehends a bunch of types, but the way things are implemented means you don't have to think about types until you REALLY NEED TO think about types, and then you're stuck.

    I can define a channel of bytes:

    > c = make(chan byte, 1)
    (chan uint8)(0xc420070070)

    Having done that, one might think I could do this:

    > c <- 10
    panic: reflect.Value.Send: value of type int64 is not assignable to type uint8

    Instead, the only thing I've been able to figure out is:

    > a = make([]byte, 1)
    > a[0] = 0xa
    > c <- a[0]
    > <-c

    I thought I might be able to trick it, but no:

    > a = make(byte)
    > typeOf(a)
    > a = 10
    > typeOf(a)

    Basically, this issue is me griping about inconsistencies in Anko's handling of types. You can define typed arrays and channels using make, but you cannot define a scalar or map of a particular type.

    Of course, this might go completely against what @mattn intends for the project, and I'll accept that. In that case, though, it feels like all arrays should be []interface and all chans should be channels of interface.

    opened by floren 14
  • Scoping question

    Scoping question

    I'm not particularly good with dynamic scoping, I've pretty much never used it, so I've got some questions about how I should be writing my code. Basically, any variables I use in functions are now "off-limits" from using elsewhere... here's a simplified example:

    var strconv = import("strconv")
    func parseBase10(s) {
      r, _ = strconv.ParseInt(s, 10, 64)
      return r
    r = ["5", "4", "3"]
    for i = 0; i < len(r); i++ {

    The call to parseBase10 changes r from a slice of strings to an int64. Whoops! I thought I might do this:

     func parseBase10(s) {
      var r, err = strconv.ParseInt(s, 10, 64)
      return r

    But that doesn't work quite right:

    > parseBase10("5")
    []interface {}{5, interface {}(nil)}

    What do I do in this situation? Start using more unique variable names? Or is there a correct way to use 'var' in that sort of multiple assignment situation?

    opened by floren 13
  • 1 line counts as 2

    1 line counts as 2

    Sample script with a syntax error:

    func fib(n) {
      a, b = 1, 1
      f = []
      for i in range n {
        f += a
        b += a
        a = b - a
      return f

    You get this output:

    $ ./anko fib-for.ank
    fib-for.ank:11: syntax error: unexpected IDENT

    The reported line is incorrect, the error is on line 6. The correct line can be found by doing (l.pos.Line-1)/2+1... but that just masks the underlying problem - somewhere a new line is double counted.

    opened by dhiltonp 12
  • Method overriding in custom structs

    Method overriding in custom structs


    I am developing a project in need of function overriding. I already developed some on my fork, but I was wondering if you would be interested in merging my commits to the master branch. Let me show you some examples so you can have a better idea of what I am trying to do:


    type C interface {
      Get(k string) interface{}
    type A struct {
      v int64  
    func (a *A) Get(k string) interface{} {
    func (a *A) Set(v interface{}) {

    I want to call A from the script in a transparent way by using:

    # asume `obj` is `A` type
    v = obj["key"]
    v = 20

    That should call:

    v = obj.Get("key")

    That's already implemented in my fork, and you can try it out with your custom structures. I'll wait for your comments and suggestions.


    opened by dgrr 11
  • panic in LetsStmt

    panic in LetsStmt

    we saw a panic come out of a system that is running scripts in a customer system. I haven't yet been able to get a script sample with the data that can cause the panic, but it happened multiple times. It appears that the code isn't validating the size of the slice.

    The offending piece of code is runInfo.rv = rvs[len(rvs)-1]

    here is the backtrace snippet:

    panic: runtime error: index out of range
    goroutine 85 [running]:
    0xc00002a800, 0xaafe40, 0xc0002ee750, 0xc000220500, 0x600, 0x1,
    0xc00004efa8, 0x6ffc46)
    0xab6dc0, 0xc00002a800, 0xc0002e7200, 0x58e, 0xc00011dda0, 0x0, 0x1,
            /home/user/mygo/src/vendor/ +0x15e
    main.runScript.func1(0xc00000d8c0, 0xab6dc0, 0xc00002a800,
    0xc000216280, 0xc00006eea0)

    Anyone have any ideas on a script snippet that could invoke this piece of the interpreter code? I am still trying to generate a test case.

    opened by traetox 11
  • Methods definition

    Methods definition

    In Go, a method defined on a pointer like this:

    func (u *User) SayHello() {...}

    May be called on a pointer or on an instance:

    user1 := &User{Name: "Michel"}
    user2 := User{Name: "Michel"}

    But this is not the case in Anko, as following example demonstrates:

    package main
    import (
    type User struct {
    	Name string
    func (u *User) SayHello() {
    	fmt.Printf("Hello %s!\n", u.Name)
    func main() {
    	e := env.NewEnv()
    	user := User{Name: "Michel"}
    	err := e.Define("user", user)
    	if err != nil {
    		log.Fatalf("Define error: %v\n", err)
    	_, err = vm.Execute(e, nil, "user.SayHello()")
    	if err != nil {
    		log.Fatalf("Execute error: %v\n", err)

    Which prints:

    $ go run main.go 
    Hello Michel!
    2022/01/12 18:42:13 Execute error: no member named 'SayHello' for struct
    exit status 1
    opened by c4s4 0
  • add GetValueSymbols and GetTypeSymbols

    add GetValueSymbols and GetTypeSymbols

    I've been using anko for my music player and I wanted to implement auto completion. By adding GetValueSymbols and GetTypeSymbols, I will be able to easily fetch symbols of the current scope. This will also help others with the same use case.

    opened by issadarkthing 1
  • when

    when "new" and "make" structure, field will not be zero value.

    test code:

    type FooStruct struct {
    	function  func(string)
    	Pointer   *int
    	Slice     []string
    	Map       map[string]string
    	Channel   chan string
    	Function  func(string)
    	Interface interface{}
    	Transport http.RoundTripper
    	Str2      FooStruct2
    	Str2p     *FooStruct2
    type FooStruct2 struct {
    	Pointer *int
    // Println is used to check structure fields are Zero Value.
    func (f *FooStruct) Println() {
    	fmt.Println("func(unexported):", f.function == nil)
    	fmt.Println("pointer:", f.Pointer == nil)
    	fmt.Println("slice:", f.Slice == nil)
    	fmt.Println("map:", f.Map == nil)
    	fmt.Println("chan:", f.Channel == nil)
    	fmt.Println("func:", f.Function == nil)
    	fmt.Println("interface{}:", f.Interface == nil)
    	fmt.Println("interface:", f.Transport == nil)
    	fmt.Println("str2:", f.Str2.Pointer == nil)
    	fmt.Println("str2p:", f.Str2p == nil)
    func TestAnkoMakeStruct(t *testing.T) {
    	// Zero Value
    	fs1 := new(FooStruct)
    	fs2 := FooStruct{}
    	// some fields not Zero Value
    	e := env.NewEnv()
    	err := e.DefineType("FooStruct", reflect.TypeOf(fs1).Elem())
    	require.NoError(t, err)
    	src := `
    fs1 = new(FooStruct)
    fs2 = make(FooStruct)
    	stmt, err := parser.ParseSrc(src)
    	require.NoError(t, err)
    	_, err = vm.Run(e, nil, stmt)
    	require.NoError(t, err)


    func(unexported): true
    pointer: true
    slice: true
    map: true
    chan: true
    func: true
    interface{}: true
    interface: true
    str2: true
    str2p: true
    func(unexported): true
    pointer: true
    slice: true
    map: true
    chan: true
    func: true
    interface{}: true
    interface: true
    str2: true
    str2p: true
    func(unexported): true
    pointer: true
    slice: false
    map: false
    chan: false
    func: false
    interface{}: true
    interface: true
    str2: true
    str2p: true
    func(unexported): true
    pointer: true
    slice: false
    map: false
    chan: false
    func: false
    interface{}: true
    interface: true
    str2: true
    str2p: true
    slice, map, chan and func will not be set zero value, but pointer is zero value.
    it will occur some package panic like net/http.Client
    // code in src/net/http
    func (c *Client) checkRedirect(req *Request, via []*Request) error {
        fn := c.CheckRedirect
        if fn == nil {       // [error] in anko, this if is unexpected.
            fn = defaultCheckRedirect
        return fn(req, via)
    func TestAnkoMakeHTTPClient(t *testing.T) {
    	e := env.NewEnv()
    	src := `
    http = import("net/http")
    // must 302
    url = ""
    req, err = http.NewRequest(http.MethodGet, url, nil)
    if err != nil {
        return false, err
    client = new(http.Client)
    resp, err = client.Do(req) // will return a nil pointer error
    if err != nil {
        return false, err
    println(client.CheckRedirect == nil) // false
    println(client.CheckRedirect) // invalid function
    // ok
    client.CheckRedirect = nil
    resp, err = client.Do(req)
    if err != nil {
        return false, err
    	stmt, err := parser.ParseSrc(src)
    	require.NoError(t, err)
    	_, err = vm.Run(e, nil, stmt)
    	require.NoError(t, err)

    file anko/vm/vm.go:

    line 394:
    func makeValue(t reflect.Type) (reflect.Value, error) {
    	switch t.Kind() {
    	case reflect.Chan:
    		return reflect.MakeChan(t, 0), nil
    	case reflect.Func:
    		return reflect.MakeFunc(t, nil), nil
    	case reflect.Map:
    		// note creating slice as work around to create map
    		// just doing MakeMap can give incorrect type for defined types
    		value := reflect.MakeSlice(reflect.SliceOf(t), 0, 1)
    		value = reflect.Append(value, reflect.MakeMap(reflect.MapOf(t.Key(), t.Elem())))
    		return value.Index(0), nil
    	case reflect.Ptr:
    		ptrV := reflect.New(t.Elem())
    		v, err := makeValue(t.Elem())
    		if err != nil {
    			return nilValue, err
    		return ptrV, nil
    	case reflect.Slice:
    		return reflect.MakeSlice(t, 0, 0), nil
    	case reflect.Struct:
    		 structV := reflect.New(t).Elem()
    		 for i := 0; i < structV.NumField(); i++ {
                            // here Pointer will be zero value, but other type is not
    		 	if structV.Field(i).Kind() == reflect.Ptr {
    		 	v, err := makeValue(structV.Field(i).Type())
    			if err != nil {
    				return nilValue, err
    			if structV.Field(i).CanSet() {
    		 return structV, nil
    	        // only this code is ok
    		// return reflect.New(t).Elem(), nil
    	return reflect.New(t).Elem(), nil
    opened by For-ACGN 0
  • Crashed by invalid input

    Crashed by invalid input

    Found in fuzz test with go-fuzz

    input: "\ue031"

    package main
    import ""
    func main() {
    	_, err := parser.ParseSrc("\ue031")
    	if err != nil {
    $ go run ./main.go
    signal: killed

    fuzz code

    package parser
    import ""
    func Fuzz(data []byte) int {
    	_, err := parser.ParseSrc(string(data))
    	if err != nil {
    		return 0
    	return 1

    crash log

    program hanged (timeout 10 seconds)
    SIGABRT: abort
    PC=0x45e721 m=0 sigcode=0
    goroutine 0 [idle]:
    runtime.futex(0x5efec8, 0x80, 0x0, 0x0, 0x7f5800000000, 0x0, 0xc000118000, 0x7ffc00000001, 0x7ffcaf2e6e68, 0x40a28f, ...)
    	runtime/sys_linux_amd64.s:567 +0x21
    runtime.futexsleep(0x5efec8, 0x7f5800000000, 0xffffffffffffffff)
    	runtime/os_linux.go:45 +0x46
    	runtime/lock_futex.go:151 +0x9f
    	runtime/proc.go:1834 +0xc0
    	runtime/proc.go:3268 +0x111
    	runtime/asm_amd64.s:318 +0x5b
    goroutine 1 [runnable]:, 0xc00037d8b0, 0xc000164500, 0xe031, 0x33)
    	/home/heijo/go/pkg/mod/[email protected]/parser/parser.go:1042 +0x466*yyParserImpl).Parse(0xc000164500, 0x538220, 0xc00037d8b0, 0x0)
    	/home/heijo/go/pkg/mod/[email protected]/parser/parser.go:1129 +0x161db
    	/home/heijo/go/pkg/mod/[email protected]/parser/parser.go:1078, 0xc000101650, 0x3, 0xc0003b38c8, 0x1)
    	/home/heijo/go/pkg/mod/[email protected]/parser/lexer.go:591 +0xef, 0x3, 0x3, 0x3, 0xc000420e78, 0x3)
    	/home/heijo/go/pkg/mod/[email protected]/parser/lexer.go:612 +0xb9, 0x3, 0x3, 0x3)
    	/home/heijo/ghq/ +0x7d
    go-fuzz-dep.Main(0xc000420f70, 0x1, 0x1)
    	go-fuzz-dep/main.go:36 +0x1ad
    main.main() +0x52
    rax    0xca
    rbx    0x5efd80
    rcx    0x45e723
    rdx    0x0
    rdi    0x5efec8
    rsi    0x80
    rbp    0x7ffcaf2e6e30
    rsp    0x7ffcaf2e6de8
    r8     0x0
    r9     0x0
    r10    0x0
    r11    0x286
    r12    0x5debc0
    r13    0x5ddda0
    r14    0x5d85c0
    r15    0x4
    rip    0x45e721
    rflags 0x286
    cs     0x33
    fs     0x0
    gs     0x0
    exit status 2
    opened by johejo 0
Long-time Golang user&contributor, Google Dev Expert for Go, and author of many Go tools, Vim plugin author. Windows hacker C#/Java/C/C++
gpython is a python interpreter written in go "batteries not included"

gpython gpython is a part re-implementation / part port of the Python 3.4 interpreter to the Go language, "batteries not included". It includes: runti

go-python 659 Dec 28, 2022
A JavaScript interpreter in Go (golang)

otto -- import "" Package otto is a JavaScript parser and interpreter written natively in Go.

Robert Krimen 7.1k Jan 2, 2023
wagon, a WebAssembly-based Go interpreter, for Go.

wagon wagon is a WebAssembly-based interpreter in Go, for Go. As of 2020/05/11 Wagon is in read-only mode, and looking for a maintainer. You may want

Go Interpreter 899 Dec 6, 2022
Yaegi is Another Elegant Go Interpreter

Yaegi is Another Elegant Go Interpreter. It powers executable Go scripts and plugins, in embedded interpreters or interactive shells, on top of the Go

Traefik Labs 5.1k Jan 5, 2023
Lisp Interpreter

golisp Lisp Interpreter Usage $ golisp < foo.lisp Installation $ go get Features Call Go functions. Print random in

mattn 123 Dec 15, 2022
A simple interpreter

类型: 基础类型: 整形,浮点,字符串,布尔,空值(nil) 符合类型: 数组,只读数组(元组),字典 支持语句: if, elif, else, for, foreach, break, continue, return 支持类型定义: class, func 语法格式: 赋值语句---> ```

null 19 Aug 10, 2022
High-performance PHP application server, load-balancer and process manager written in Golang

RoadRunner is an open-source (MIT licensed) high-performance PHP application server, load balancer, and process manager. It supports running as a serv

Spiral Scout 6.9k Dec 30, 2022
A compiler for the ReCT programming language written in Golang

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

null 6 Nov 30, 2022
A Lua 5.3 VM and compiler written in Go.

DCLua - Go Lua Compiler and VM: This is a Lua 5.3 VM and compiler written in Go. This is intended to allow easy embedding into Go programs, with minim

Milo Christiansen 899 Dec 12, 2022
PHP parser written in Go

PHP Parser written in Go This project uses goyacc and ragel tools to create PHP parser. It parses source code into AST. It can be used to write static

Vadym Slizov 899 Dec 25, 2022
An interpreted languages written in Go

Monkey My changes 1. Installation Source Installation go <= 1.11 Source installation go >= 1.12 Binary Releases 1.1 Usage 2 Syntax 2.1 Definitions 2.2

Steve Kemp 193 Jan 8, 2023
Transpiling fortran code to golang code

f4go Example of use > # Install golang > # Compile f4go > go get -u > cd $GOPATH/src/ > g

Konstantin 34 Sep 26, 2022
Golang->Haxe->CPP/CSharp/Java/JavaScript transpiler

TARDIS Go -> Haxe transpiler Haxe -> C++ / C# / Java / JavaScript Project status: a non-working curiosity, development currently on-ice The advent of

TARDIS Go 423 Dec 30, 2022
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

Alex Palaistras 882 Jan 1, 2023
High-performance PHP-to-Golang IPC bridge

High-performance PHP-to-Golang IPC bridge Goridge is high performance PHP-to-Golang codec library which works over native PHP sockets and Golang net/r

Spiral Scout 1.1k Dec 28, 2022
Expression evaluation in golang

Gval Gval (Go eVALuate) provides support for evaluating arbitrary expressions, in particular Go-like expressions. Evaluate Gval can evaluate expressio

null 563 Dec 27, 2022
golang AST matcher

goastch (GO AST matCH) Introduction Inspired by ast matcher. There are four different basic categories of matchers: Node Matchers: Matchers that match

Helloyi He 13 Nov 11, 2022
Arbitrary expression evaluation for golang

govaluate Provides support for evaluating arbitrary C-like artithmetic/string expressions. Why can't you just write these expressions in code? Sometim

George Lester 2.9k Jan 2, 2023
hotbuild - a cross platform hot compilation tool for golang

hotbuild A cross platform hot compilation tool By monitoring the modification of the project directory file, the recompilation and running are automat

wander 189 Dec 12, 2022