Scriptable interpreter written in golang

Related tags

go programming-language
Overview

Anko

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 (
	"fmt"
	"log"

	"github.com/mattn/anko/env"
	"github.com/mattn/anko/vm"
)

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:

https://godoc.org/github.com/mattn/anko/vm

Usage Example - Command Line

Building

go get github.com/mattn/anko
go install github.com/mattn/anko

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 {
	println(x)
} else if x < 1 && y < 1 {
	println(y)
} 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.

Author

Yasuhiro Matsumoto (a.k.a mattn)

Contributors

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]

Individuals

Organizations

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

Issues
  • add generate-package-code

    add generate-package-code

    opened by chyroc 34
  • Add/item in list syntax

    Add/item in list syntax

    opened by chyroc 23
  • 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)
    <-ch
    

    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
  • **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
  • 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
  • Removed _example

    Removed _example

    opened by MichaelS11 16
  • 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() {
        println("before")
        This should stop the VM
        println("will never print")
      }()
      time.Sleep(time.Second)
    }
    

    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 {
            println(r)
            cancel()
          }
        }()
        fn()
      }()
    })
    
    myscript := `
    time = import("time")
    for {
      Go(func() {
        println("before")
        This will stop the VM
        println("will never print")
      })
      time.Sleep(time.Second)
    }`
    
    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
  • 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
  • Master branch doesn't follow releases

    Master branch doesn't follow releases

    Code on master branch is not the same than the one of the latest v0.1.2 release. May I suggest that master branch follow latest releases?

    opened by c4s4 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
  • function with struct

    function with struct

    https://github.com/mattn/anko/issues/72

    opened by houxul 1
  • 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
  • fix #344

    fix #344

    opened by For-ACGN 0
  • 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)
    	fmt.Println()
    }
    
    func TestAnkoMakeStruct(t *testing.T) {
    	// Zero Value
    	fs1 := new(FooStruct)
    	fs1.Println()
    	fs2 := FooStruct{}
    	fs2.Println()
    
    	// some fields not Zero Value
    	e := env.NewEnv()
    	err := e.DefineType("FooStruct", reflect.TypeOf(fs1).Elem())
    	require.NoError(t, err)
    	src := `
    fs1 = new(FooStruct)
    fs1.Println()
    
    fs2 = make(FooStruct)
    fs2.Println()
    `
    	stmt, err := parser.ParseSrc(src)
    	require.NoError(t, err)
    	_, err = vm.Run(e, nil, stmt)
    	require.NoError(t, err)
    }
    

    output:

    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 = "http://example.com/302"
    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
    		}
    		
    		ptrV.Elem().Set(v)
    		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 {
    		 		continue
    		 	}
    		 	v, err := makeValue(structV.Field(i).Type())
    			if err != nil {
    				return nilValue, err
    			}
    			if structV.Field(i).CanSet() {
    				structV.Field(i).Set(v)
    			}
    		 }
    		 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
  • Can a function be created

    Can a function be created

    type A struct{
    B func(i int) int
    }
    

    I want to create a function in the template for A.B Or how to achieve func(a []reflect.Value) []reflect.Value

    opened by 456vv 0
  • Crashed by invalid input

    Crashed by invalid input

    Found in fuzz test with go-fuzz

    input: "\ue031"

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

    fuzz code

    package parser
    
    import "github.com/mattn/anko/parser"
    
    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.notesleep(0x5efec8)
    	runtime/lock_futex.go:151 +0x9f
    runtime.stopm()
    	runtime/proc.go:1834 +0xc0
    runtime.exitsyscall0(0xc000000180)
    	runtime/proc.go:3268 +0x111
    runtime.mcall(0x0)
    	runtime/asm_amd64.s:318 +0x5b
    
    goroutine 1 [runnable]:
    github.com/mattn/anko/parser.yylex1(0x538220, 0xc00037d8b0, 0xc000164500, 0xe031, 0x33)
    	/home/heijo/go/pkg/mod/github.com/mattn/[email protected]/parser/parser.go:1042 +0x466
    github.com/mattn/anko/parser.(*yyParserImpl).Parse(0xc000164500, 0x538220, 0xc00037d8b0, 0x0)
    	/home/heijo/go/pkg/mod/github.com/mattn/[email protected]/parser/parser.go:1129 +0x161db
    github.com/mattn/anko/parser.yyParse(...)
    	/home/heijo/go/pkg/mod/github.com/mattn/[email protected]/parser/parser.go:1078
    github.com/mattn/anko/parser.Parse(0xc000101650, 0xc000101650, 0x3, 0xc0003b38c8, 0x1)
    	/home/heijo/go/pkg/mod/github.com/mattn/[email protected]/parser/lexer.go:591 +0xef
    github.com/mattn/anko/parser.ParseSrc(0xc000420e78, 0x3, 0x3, 0x3, 0xc000420e78, 0x3)
    	/home/heijo/go/pkg/mod/github.com/mattn/[email protected]/parser/lexer.go:612 +0xb9
    github.com/johejo/go-fuzz-contrib/github.com/mattn/anko/parser.Fuzz(0x7f583a827000, 0x3, 0x3, 0x3)
    	/home/heijo/ghq/github.com/johejo/go-fuzz-contrib/github.com/mattn/anko/parser/fuzz.go:6 +0x7d
    go-fuzz-dep.Main(0xc000420f70, 0x1, 0x1)
    	go-fuzz-dep/main.go:36 +0x1ad
    main.main()
    	github.com/johejo/go-fuzz-contrib/github.com/mattn/anko/parser/go.fuzz.main/main.go:15 +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
  • Adding new example script: Find prime numbers

    Adding new example script: Find prime numbers

    Hello @mattn,

    My first PR in this repository: Find prime numbers I will add more examples in future, No problem?

    Regards, Max Base

    opened by BaseMax 2
  • Select statement

    Select statement

    Add select to the language

    examples:

    a = make(chan int64, 1)
    select {
        case <-a:
            println("value received on a")
        default:
            println("default")
    }
    
    a = make(chan int64, 1)
    b = make(chan int64, 1)
    b <- 2
    val = 0
    select {
        case val = <-a:
            println("value " + val + " received on a")
        case val = <-b:
            println("value " + val + " received on b")
    }
    
    opened by alaingilbert 2
  • Instance defined with VmModules is not a new instance

    Instance defined with VmModules is not a new instance

    Anko code

    module User{
    	_age = -1
    	_name = "undefined"
    
    	func SetAge(age) {
    		_age = age
    	}
    
    	func SetName(name) {
    		_name = name
    	}
    }
    
    data = [
    	{
    		"age": 10,
    		"name" : "jack"
    	},
    	{
    		"age": 0,
    		"name" : ""
    	},
    ]
    
    for item in data {
    	user = User
    	if item["age"] != 0 {
    		user.SetAge(item["age"])
    	}
    
    	if item["name"] != "" {
    		user.SetName(item["name"])
    	}
    	
    	println(user._age)
    	println(user._name)
    }
    

    Expected output

    10
    jack
    -1
    undefined
    

    Actual output

    10
    jack
    10
    jack
    
    opened by houxul 24
  • Support Array initializer

    Support Array initializer

    a = []int{0: 0xFF, 10: 0xFF}
    
    opened by mattn 0
Owner
mattn
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++
mattn
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 891 Oct 10, 2021
A BASIC interpreter written in golang.

05 PRINT "Index" 10 PRINT "GOBASIC!" 20 PRINT "Limitations" Arrays Line Numbers IF Statement DATA / READ Statements Builtin Functions Types 30 PRINT "

Steve Kemp 270 Oct 18, 2021
GopherLua: VM and compiler for Lua in Go

GopherLua: VM and compiler for Lua in Go. GopherLua is a Lua5.1 VM and compiler written in Go. GopherLua has a same goal with Lua: Be a scripting lang

Yusuke Inuzuka 4.3k Oct 19, 2021
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 138 Oct 18, 2021
Scriptable interpreter written in golang

Anko Anko is a scriptable interpreter written in Go. (Picture licensed under CC BY-SA 3.0, photo by Ocdp) Usage Example - Embedded package main impor

mattn 1.2k Oct 7, 2021
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

Google 1.4k Oct 15, 2021
A Lua VM in Go

A Lua VM in pure Go go-lua is a port of the Lua 5.2 VM to pure Go. It is compatible with binary files dumped by luac, from the Lua reference implement

Shopify 2.2k Oct 18, 2021
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

daniel 2.5k Oct 17, 2021
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 522 Oct 20, 2021
Lisp Interpreter

golisp Lisp Interpreter Usage $ golisp < foo.lisp Installation $ go get github.com/mattn/golisp/cmd/golisp Features Call Go functions. Print random in

mattn 111 Sep 8, 2021
A JavaScript interpreter in Go (golang)

otto -- import "github.com/robertkrimen/otto" Package otto is a JavaScript parser and interpreter written natively in Go. http://godoc.org/github.com/

Robert Krimen 6.3k Oct 25, 2021
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 412 Oct 7, 2021
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

Google 918 Oct 17, 2021
A parser library for Go

A dead simple parser package for Go V2 Introduction Tutorial Tag syntax Overview Grammar syntax Capturing Capturing boolean value Streaming Lexing Sta

Alec Thomas 2.3k Oct 18, 2021
A simple interpreter

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

null 17 Sep 4, 2020
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 825 Oct 14, 2021
Grumpy is a Python to Go source code transcompiler and runtime.

Grumpy: Go running Python Overview Grumpy is a Python to Go source code transcompiler and runtime that is intended to be a near drop-in replacement fo

Google 10.4k Oct 18, 2021
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 881 Oct 4, 2021
ECMAScript/JavaScript engine in pure Go

goja ECMAScript 5.1(+) implementation in Go. Goja is an implementation of ECMAScript 5.1 in pure Go with emphasis on standard compliance and performan

Dmitry Panov 2.4k Oct 17, 2021