Embedded anko as run.

This commit is contained in:
Andrey Parhomenko 2023-06-04 18:59:42 +03:00
parent 964e9b2303
commit 5251f0fbf4
140 changed files with 21692 additions and 0 deletions

View file

@ -29,6 +29,7 @@ import (
"github.com/surdeus/goblin/src/tool/wc" "github.com/surdeus/goblin/src/tool/wc"
"github.com/surdeus/goblin/src/tool/whoami" "github.com/surdeus/goblin/src/tool/whoami"
"github.com/surdeus/goblin/src/tool/yes" "github.com/surdeus/goblin/src/tool/yes"
"github.com/surdeus/goblin/src/tool/run"
"github.com/surdeus/gomtool/src/mtool" "github.com/surdeus/gomtool/src/mtool"
) )
@ -74,6 +75,11 @@ func main() {
"link files", "link files",
"", "",
}, },
"run": mtool.Tool{
run.Run,
"run anko script",
"",
},
} }
mtool.Main("goblin", tools) mtool.Main("goblin", tools)

8
src/tool/run/.github/FUNDING.yml vendored Normal file
View file

@ -0,0 +1,8 @@
# These are supported funding model platforms
github: mattn # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2]
patreon: mattn # Replace with a single Patreon username
open_collective: mattn # Replace with a single Open Collective username
ko_fi: # Replace with a single Ko-fi username
tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
custom: # Replace with a single custom sponsorship URL

22
src/tool/run/.travis.yml Normal file
View file

@ -0,0 +1,22 @@
language: go
go:
- 1.8.x
- 1.9.x
- 1.10.x
- 1.11.x
- 1.12.x
- 1.13.x
- 1.14.x
env:
secure: "ELC4rD8nn2l5T48WYbTfcbwGGBmNxl7LAu05hgx5AB9/KA+oD3oBKIJkZqD512gJ31Gtyla/hG9QOgU7LikfWdpGuJjVILy01ZqtgP5SSKsrTdlln1D5pK1ZyHJNrEPevb3W5PYn9ahHnjKGtpobXj4/E0sCXfRPH67jv9hffYs="
before_install:
- go get -u github.com/haya14busa/goverage
script:
- goverage -v -coverprofile=coverage.txt -covermode=count ./vm ./env . ./ast/astutil
after_success:
- bash <(curl -s https://codecov.io/bash)

21
src/tool/run/LICENSE Normal file
View file

@ -0,0 +1,21 @@
The MIT License (MIT)
Copyright (c) 2014-2018 Yasuhiro Matsumoto, http://mattn.kaoriya.net <mattn.jp@gmail.com>
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.

155
src/tool/run/README.md Normal file
View file

@ -0,0 +1,155 @@
# Anko
[![GoDoc Reference](https://godoc.org/github.com/surdeus/goblin/src/tool/run/vm?status.svg)](http://godoc.org/github.com/surdeus/goblin/src/tool/run/vm)
[![Build Status](https://travis-ci.org/mattn/anko.svg?branch=master)](https://travis-ci.org/mattn/anko)
[![Financial Contributors on Open Collective](https://opencollective.com/mattn-anko/all/badge.svg?label=financial+contributors)](https://opencollective.com/mattn-anko) [![Coverage](https://codecov.io/gh/mattn/anko/branch/master/graph/badge.svg)](https://codecov.io/gh/mattn/anko)
[![Go Report Card](https://goreportcard.com/badge/github.com/surdeus/goblin/src/tool/run)](https://goreportcard.com/report/github.com/surdeus/goblin/src/tool/run)
Anko is a scriptable interpreter written in Go.
![](https://raw.githubusercontent.com/mattn/anko/master/anko.png)
(Picture licensed under CC BY-SA 3.0, photo by Ocdp)
## Usage Example - Embedded
```go
package main
import (
"fmt"
"log"
"github.com/surdeus/goblin/src/tool/run/env"
"github.com/surdeus/goblin/src/tool/run/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/surdeus/goblin/src/tool/run/vm
## Usage Example - Command Line
### Building
```
go get github.com/surdeus/goblin/src/tool/run
go install github.com/surdeus/goblin/src/tool/run
```
### 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](CONTRIBUTING.md)].
<a href="https://github.com/surdeus/goblin/src/tool/run/graphs/contributors"><img src="https://opencollective.com/mattn-anko/contributors.svg?width=890&button=false" /></a>
### Financial Contributors
Become a financial contributor and help us sustain our community. [[Contribute](https://opencollective.com/mattn-anko/contribute)]
#### Individuals
<a href="https://opencollective.com/mattn-anko"><img src="https://opencollective.com/mattn-anko/individuals.svg?width=890"></a>
#### Organizations
Support this project with your organization. Your logo will show up here with a link to your website. [[Contribute](https://opencollective.com/mattn-anko/contribute)]
<a href="https://opencollective.com/mattn-anko/organization/0/website"><img src="https://opencollective.com/mattn-anko/organization/0/avatar.svg"></a>
<a href="https://opencollective.com/mattn-anko/organization/1/website"><img src="https://opencollective.com/mattn-anko/organization/1/avatar.svg"></a>
<a href="https://opencollective.com/mattn-anko/organization/2/website"><img src="https://opencollective.com/mattn-anko/organization/2/avatar.svg"></a>
<a href="https://opencollective.com/mattn-anko/organization/3/website"><img src="https://opencollective.com/mattn-anko/organization/3/avatar.svg"></a>
<a href="https://opencollective.com/mattn-anko/organization/4/website"><img src="https://opencollective.com/mattn-anko/organization/4/avatar.svg"></a>
<a href="https://opencollective.com/mattn-anko/organization/5/website"><img src="https://opencollective.com/mattn-anko/organization/5/avatar.svg"></a>
<a href="https://opencollective.com/mattn-anko/organization/6/website"><img src="https://opencollective.com/mattn-anko/organization/6/avatar.svg"></a>
<a href="https://opencollective.com/mattn-anko/organization/7/website"><img src="https://opencollective.com/mattn-anko/organization/7/avatar.svg"></a>
<a href="https://opencollective.com/mattn-anko/organization/8/website"><img src="https://opencollective.com/mattn-anko/organization/8/avatar.svg"></a>
<a href="https://opencollective.com/mattn-anko/organization/9/website"><img src="https://opencollective.com/mattn-anko/organization/9/avatar.svg"></a>

View file

@ -0,0 +1,9 @@
#!anko
func(x) {
return func(y) {
x(y)
}
}(func(z) {
println("Yay!", z)
})("hello world")

View file

@ -0,0 +1,13 @@
#!anko
c = make(chan int64)
go func() {
c <- 1
c <- 2
c <- 3
}()
println(<-c)
println(<-c)
println(<-c)

View file

@ -0,0 +1,9 @@
#!anko
var os, runtime = import("os"), import("runtime")
if runtime.GOOS == "windows" {
println(os.Getenv("USERPROFILE"))
} else {
println(os.Getenv("HOME"))
}

View file

@ -0,0 +1,70 @@
#!anko
# declare function
func foo(x){
return x + 1
}
func bar(x ...){
return len(x)
}
# declare variables
x = 1
y = x + 1
# print values
println(x * (y + 2 * x + foo(x) / 2))
# if/else condition
if foo(y) >= 1 {
println("こんにちわ世界")
} else {
println("Hello, World")
}
# array type
a = [1,2,3]
println(a)
println(a[2])
println(len(a))
# map type
m = {"foo": "bar", "bar": "baz"}
for k in keys(m) {
println(m[k])
}
f = func(a) {
println(a)
}
f("あんこ")
f = func(a ...) {
println(a)
}
f("あんこ", "だいすき")
println(1 && 2)
println(bar(1,2,3))
println("foo")
println(toByteSlice("あいう"))
println(toRuneSlice("あいう"))
a = 1
func foo() {
a = 2
}
foo()
println(a)
module Foo {
func bar1() {
println("Foo.bar1")
}
}
println(Foo.bar1())

View file

@ -0,0 +1,7 @@
#!anko
var os, exec = import("os"), import("os/exec")
cmd = exec.Command("ls", "-la")
cmd.Stdout = os.Stdout
cmd.Run()

View file

@ -0,0 +1,15 @@
#!anko
func fib(n) {
a, b = 1, 1
f = []
for i in range(n) {
f += a
b += a
a = b - a
}
return f
}
println(fib(20))

View file

@ -0,0 +1,14 @@
#!anko
func fib(n) {
if n == 1 {
return [1]
} else if n == 2 {
return [1,1]
} else {
t = fib(n-1)
return t + (t[len(t)-1] + t[len(t)-2])
}
}
println(fib(20))

View file

@ -0,0 +1,12 @@
#!anko
for i in [1,2,3,4,5] {
if i == 2 {
continue
}
println(i)
if i > 3 {
break
}
println("foo")
}

View file

@ -0,0 +1,8 @@
#!anko
var http, ioutil = import("net/http"), import("io/ioutil")
r = http.DefaultClient.Get("http://golang.org/")
b, _ = ioutil.ReadAll(r[0].Body)
printf("%s", toString(b))
r[0].Body.Close()

View file

@ -0,0 +1,10 @@
#!anko
module Foo {
func bar1() {
println("Foo.bar1")
return 1
}
}
println(Foo.bar1())

View file

@ -0,0 +1,8 @@
#!anko
var regexp = import("regexp")
for s in regexp.MustCompile(`[\s_]`).Split("foo_bar_baz", -1) {
println(s)
}

View file

@ -0,0 +1,8 @@
#!anko
var http = import("net/http")
http.HandleFunc("/", func(w, r) {
w.Write(toByteSlice("hello world"))
})
http.ListenAndServe(":8080", nil)

View file

@ -0,0 +1,14 @@
#!anko
var os, signal, time = import("os"), import("os/signal"), import("time")
c = make(chan os.Signal, 1)
signal.Notify(c, os.Interrupt)
go func() {
<-c
println("CTRL-C")
os.Exit(0)
}()
d, _ = time.ParseDuration("10s")
time.Sleep(d)

View file

@ -0,0 +1,10 @@
#!anko
a = make([]int64, 5)
for i = 0; i < len(a); i++ {
a[i] = i
}
for i in a {
println(i)
}

View file

@ -0,0 +1,26 @@
#!anko
var os, net, url, ioutil = import("os"), import("net"), import("net/url"), import("io/ioutil");
func connect(uri) {
proxy = os.Getenv("http_proxy")
if proxy != "" {
u, e = url.Parse(proxy)
if e != nil {
return nil, e
}
return net.Dial("tcp", u.Host)
}
return net.Dial("tcp", uri)
}
c, e = connect("www.google.com:80")
if e != nil {
throw e
}
c.Write(toByteSlice("GET http://www.google.com/ HTTP/1.0\r\n\r\n"))
b, e = ioutil.ReadAll(c)
if e != nil {
throw e
}
printf("%s", b)

View file

@ -0,0 +1,123 @@
#!anko
# toInt with ints, floats, strings, bools
println("\ntoInt examples:\n===============")
i = 1<<63 - 1
println("int", i, "toInt:", toInt(i))
i = -1 << 63
println("int", i, "toInt:", toInt(i))
f = 3.141592653589793
println("float", f, "toInt:", toInt(f))
f = 1.797693134862315708145274237317043567981e18
println("float", f, "toInt:", toInt(f))
f = -1.797693134862315708145274237317043567981e18
println("float", f, "toInt:", toInt(f))
s = "4611686018427387904"
println("string", s, "toInt:", toInt(s))
s = "-9223372036854775808"
println("string", s, "toInt:", toInt(s))
s = "3.141592653589793"
println("string", s, "toInt:", toInt(s))
s = "1.797693134862315708145274237317043567981e18"
println("string", s, "toInt:", toInt(s))
s = "-1.797693134862315708145274237317043567981e18"
println("string", s, "toInt:", toInt(s))
s = "1.797693134862315708145274237317043567981e-18"
println("string", s, "toInt:", toInt(s))
b = true
println("bool", b, "toInt:", toInt(b))
b = false
println("bool", b, "toInt:", toInt(b))
println("\ntoFloat examples:\n=================")
i = 1<<63 - 1
println("int", i, "toFloat:", toFloat(i))
i = -1 << 63
println("int", i, "toFloat:", toFloat(i))
s = "4611686018427387904"
println("string", s, "toFloat:", toFloat(s))
s = "-9223372036854775808"
println("string", s, "toFloat:", toFloat(s))
s = "3.141592653589793"
println("string", s, "toFloat:", toFloat(s))
s = "1.797693134862315708145274237317043567981e18"
println("string", s, "toFloat:", toFloat(s))
s = "-1.797693134862315708145274237317043567981e18"
println("string", s, "toFloat:", toFloat(s))
s = "1.797693134862315708145274237317043567981e-18"
println("string", s, "toFloat:", toFloat(s))
b = true
println("bool", b, "toFloat:", toFloat(b))
b = false
println("bool", b, "toFloat:", toFloat(b))
println("\ntoBool examples:\n================")
i = 1
println("int", i, "toBool:", toBool(i))
i = 0
println("int", i, "toBool:", toBool(i))
i = -1
println("int", i, "toBool:", toBool(i))
f = 1.0
println("float", f, "toBool:", toBool(f))
f = 0.000000000001
println("float", f, "toBool:", toBool(f))
f = 0.0
println("float", f, "toBool:", toBool(f))
f = -0.0
println("float", f, "toBool:", toBool(f))
s = "y"
println("string", s, "toBool:", toBool(s))
s = "yEs"
println("string", s, "toBool:", toBool(s))
s = "t"
println("string", s, "toBool:", toBool(s))
s = "TrUe"
println("string", s, "toBool:", toBool(s))
s = "1"
println("string", s, "toBool:", toBool(s))
s = "0"
println("string", s, "toBool:", toBool(s))
s = "f"
println("string", s, "toBool:", toBool(s))
s = "FaLsE"
println("string", s, "toBool:", toBool(s))
s = "foobar"
println("string", s, "toBool:", toBool(s))

View file

@ -0,0 +1,19 @@
#!anko
var http = import("net/http")
try {
http.Do()
} catch {
println("catch!")
} finally {
println("finally!")
}
try {
http.Do()
} catch e {
println("catch!", e)
} finally {
println("finally!")
}

View file

@ -0,0 +1,7 @@
#!anko
var url = import("net/url")
u, _ = url.Parse("http://www.google.com/search?q=こんにちわ世界")
println(u.Path)
println(u.Host)

View file

@ -0,0 +1,15 @@
#!anko
func Z(f) {
return (func(x) {
return f(func(y) {
return x(x)(y)
})
})(func(x) {
return f(func(y) {
return x(x)(y)
})
})
}
println(Z(func(f) { return func(n) { return n == 0 ? 1 : n * f(n - 1) } })(5) == 120)

172
src/tool/run/anko.go Normal file
View file

@ -0,0 +1,172 @@
// +build !appengine
package run
import (
"bufio"
//"flag"
"fmt"
"io"
"io/ioutil"
"os"
"strings"
"github.com/surdeus/goblin/src/tool/run/core"
"github.com/surdeus/goblin/src/tool/run/env"
_ "github.com/surdeus/goblin/src/tool/run/packages"
"github.com/surdeus/goblin/src/tool/run/parser"
"github.com/surdeus/goblin/src/tool/run/vm"
"github.com/surdeus/gomtool/src/mtool"
)
const version = "0.1.8"
var (
flagExecute string
file string
args []string
e *env.Env
flag *mtool.Flags
)
func Run(flagSet *mtool.Flags) {
var exitCode int
flag = flagSet
parseFlags()
setupEnv()
if flagExecute != "" || flag.NArg() > 0 {
exitCode = runNonInteractive()
} else {
exitCode = runInteractive()
}
os.Exit(exitCode)
}
func parseFlags() {
flagVersion := flag.Bool("v", false, "prints out the version and then exits")
flag.StringVar(&flagExecute, "e", "", "execute the Anko code")
flag.Parse()
if *flagVersion {
fmt.Println(version)
os.Exit(0)
}
if flagExecute != "" || flag.NArg() < 1 {
args = flag.Args()
return
}
file = flag.Arg(0)
args = flag.Args()[1:]
}
func setupEnv() {
e = env.NewEnv()
e.Define("args", args)
core.Import(e)
}
func runNonInteractive() int {
var source string
if flagExecute != "" {
source = flagExecute
} else {
sourceBytes, err := ioutil.ReadFile(file)
if err != nil {
fmt.Println("ReadFile error:", err)
return 2
}
source = string(sourceBytes)
}
_, err := vm.Execute(e, nil, source)
if err != nil {
fmt.Println("Execute error:", err)
return 4
}
return 0
}
func runInteractive() int {
var following bool
var source string
scanner := bufio.NewScanner(os.Stdin)
parser.EnableErrorVerbose()
for {
if following {
source += "\n"
fmt.Print(" ")
} else {
fmt.Print("> ")
}
if !scanner.Scan() {
break
}
source += scanner.Text()
if source == "" {
continue
}
if source == "quit()" {
break
}
stmts, err := parser.ParseSrc(source)
if e, ok := err.(*parser.Error); ok {
es := e.Error()
if strings.HasPrefix(es, "syntax error: unexpected") {
if strings.HasPrefix(es, "syntax error: unexpected $end,") {
following = true
continue
}
} else {
if e.Pos.Column == len(source) && !e.Fatal {
fmt.Fprintln(os.Stderr, e)
following = true
continue
}
if e.Error() == "unexpected EOF" {
following = true
continue
}
}
}
following = false
source = ""
var v interface{}
if err == nil {
v, err = vm.Run(e, nil, stmts)
}
if err != nil {
if e, ok := err.(*vm.Error); ok {
fmt.Fprintf(os.Stderr, "%d:%d %s\n", e.Pos.Line, e.Pos.Column, err)
} else if e, ok := err.(*parser.Error); ok {
fmt.Fprintf(os.Stderr, "%d:%d %s\n", e.Pos.Line, e.Pos.Column, err)
} else {
fmt.Fprintln(os.Stderr, err)
}
continue
}
fmt.Printf("%#v\n", v)
}
if err := scanner.Err(); err != nil {
if err != io.EOF {
fmt.Fprintln(os.Stderr, "ReadString error:", err)
return 12
}
}
return 0
}

BIN
src/tool/run/anko.png Normal file

Binary file not shown.

273
src/tool/run/anko_test.go Normal file
View file

@ -0,0 +1,273 @@
// +build !appengine
package run
import (
"bufio"
"io"
"log"
"os"
"os/exec"
"path/filepath"
"runtime"
"strings"
"testing"
"time"
)
var logger *log.Logger
func TestMain(m *testing.M) {
parseFlags()
code := m.Run()
os.Exit(code)
}
func TestRunNonInteractiveFile(t *testing.T) {
_, filename, _, _ := runtime.Caller(0)
testDir := filepath.Join(filepath.Dir(filename), "core", "testdata")
setupEnv()
file = filepath.Join(testDir, "not-found.ank")
exitCode := runNonInteractive()
if exitCode != 2 {
t.Fatalf("exitCode - received: %v - expected: %v", exitCode, 2)
}
file = filepath.Join(testDir, "broken.ank")
exitCode = runNonInteractive()
os.Args = []string{os.Args[0]}
if exitCode != 4 {
t.Fatalf("exitCode - received: %v - expected: %v", exitCode, 4)
}
file = filepath.Join(testDir, "test.ank")
exitCode = runNonInteractive()
os.Args = []string{os.Args[0]}
if exitCode != 0 {
t.Fatalf("exitCode - received: %v - expected: %v", exitCode, 0)
}
file = ""
}
func TestRunNonInteractiveExecute(t *testing.T) {
setupEnv()
flagExecute = "1 + 1"
exitCode := runNonInteractive()
if exitCode != 0 {
t.Fatalf("exitCode - received: %v - expected: %v", exitCode, 0)
}
flagExecute = "1++"
exitCode = runNonInteractive()
if exitCode != 4 {
t.Fatalf("exitCode - received: %v - expected: %v", exitCode, 4)
}
flagExecute = ""
}
type testInteractive struct {
runLines []string
runOutputs []string
runError string
}
func TestRunInteractive(t *testing.T) {
// empty strings in runOutputs will ignore read timeouts
tests := []testInteractive{
{runLines: []string{".."}, runError: "1:1 syntax error on '.' at 1:1"},
{runLines: []string{"1++"}, runError: "1:1 invalid operation"},
{runLines: []string{"var , b = 1, 2"}, runError: "1:7 syntax error: unexpected ','"},
{runLines: []string{"", "1"}, runOutputs: []string{"", "1"}},
{runLines: []string{"1 + 1"}, runOutputs: []string{"2"}},
{runLines: []string{"a = 1", "b = 2", "a + b"}, runOutputs: []string{"1", "2", "3"}},
{runLines: []string{"a = 1", "if a == 1 {", "b = 1", "b = 2", "}", "a"}, runOutputs: []string{"1", "", "", "", "2", "1"}},
{runLines: []string{"a = 1", "for i = 0; i < 2; i++ {", "a++", "}", "a"}, runOutputs: []string{"1", "", "", "<nil>", "3"}},
{runLines: []string{"1 + 1", "// comment 1", "2 + 2 // comment 2", "// 3 + 3"}, runOutputs: []string{"2", "<nil>", "4", "<nil>"}},
}
runInteractiveTests(t, tests)
}
func runInteractiveTests(t *testing.T, tests []testInteractive) {
// create logger
// Note: logger is used for debugging since real stdout cannot be used
logger = log.New(os.Stderr, "", log.Ldate|log.Ltime|log.LUTC|log.Llongfile)
gopath := os.Getenv("GOPATH")
if gopath == "" {
b, err := exec.Command("go", "env", "GOPATH").CombinedOutput()
if err != nil {
t.Fatal(err)
}
gopath = strings.TrimSpace(string(b))
}
filehandle, err := os.OpenFile(filepath.Join(gopath, "bin", "anko_test.log"), os.O_WRONLY|os.O_CREATE|os.O_APPEND, 0666)
if err != nil {
t.Fatal("OpenFile error:", err)
}
defer filehandle.Close()
logger.SetOutput(filehandle)
logger.Print("logger file created")
// defer sending std back to real
realStdin := os.Stdin
realStderr := os.Stderr
realStdout := os.Stdout
defer setStd(realStdin, realStderr, realStdout)
// create pipes
readFromIn, writeToIn, err := os.Pipe()
if err != nil {
t.Fatal("Pipe error:", err)
}
os.Stdin = readFromIn
logger.Print("pipe in created")
readFromErr, writeToErr, err := os.Pipe()
if err != nil {
t.Fatal("Pipe error:", err)
}
os.Stderr = writeToErr
logger.Print("pipe err created")
readFromOut, writeToOut, err := os.Pipe()
if err != nil {
t.Fatal("Pipe error:", err)
}
os.Stdout = writeToOut
logger.Print("pipe out created")
// setup reader
readerErr := bufio.NewReaderSize(readFromErr, 256)
readerOut := bufio.NewReaderSize(readFromOut, 256)
chanQuit := make(chan struct{}, 1)
chanErr := make(chan string, 3)
chanOut := make(chan string, 3)
readTimeout := 10 * time.Millisecond
var dataErr string
var dataOut string
go readerToChan(t, chanQuit, readerErr, chanErr)
go readerToChan(t, chanQuit, readerOut, chanOut)
go runInteractive()
time.Sleep(readTimeout)
// basic read and write to make sure things are working
_, err = writeToIn.WriteString("1\n")
if err != nil {
t.Fatal("Stdin WriteString error:", err)
}
select {
case dataOut = <-chanOut:
if len(dataOut) > 0 && dataOut[0] == '>' {
dataOut = dataOut[1:]
dataOut = strings.TrimSpace(dataOut)
}
if dataOut != "1" {
t.Fatalf("Stdout - received: %v - expected: %v - basic test", dataOut, "1")
}
case dataErr = <-chanErr:
dataErr = strings.TrimSpace(dataErr)
if dataErr != "" {
t.Fatalf("Stderr - received: %v - expected: %v - basic test", dataErr, "")
}
case <-time.After(readTimeout):
t.Fatal("read timeout for basic test")
}
// run tests
logger.Print("running tests start")
for _, test := range tests {
for i, runLine := range test.runLines {
_, err = writeToIn.WriteString(runLine + "\n")
if err != nil {
t.Fatal("Stdin WriteString error:", err)
}
select {
case <-time.After(readTimeout):
if test.runOutputs[i] != "" {
t.Fatalf("read timeout for i: %v - runLines: %v", i, test.runLines)
}
case dataOut = <-chanOut:
for len(dataOut) > 0 && dataOut[0] == '>' {
dataOut = dataOut[1:]
dataOut = strings.TrimSpace(dataOut)
}
if dataOut != test.runOutputs[i] {
t.Fatalf("Stdout - received: %v - expected: %v - i: %v - runLines: %v", dataOut, test.runOutputs[i], i, test.runLines)
}
case dataErr = <-chanErr:
dataErr = strings.TrimSpace(dataErr)
if dataErr != test.runError {
t.Fatalf("Stderr - received: %v - expected: %v - i: %v - runLines: %v", dataErr, test.runError, i, test.runLines)
}
// to clean output from error
_, err = writeToIn.WriteString("1\n")
if err != nil {
t.Fatal("Stdin WriteString error:", err)
}
select {
case dataOut = <-chanOut:
for len(dataOut) > 0 && dataOut[0] == '>' {
dataOut = dataOut[1:]
dataOut = strings.TrimSpace(dataOut)
}
if dataOut != "1" {
t.Fatalf("Stdout - received: %v - expected: %v - i: %v - runLines: %v", dataOut, test.runOutputs[i], i, test.runLines)
}
case <-time.After(readTimeout):
t.Fatalf("read timeout for i: %v - runLines: %v", i, test.runLines)
}
}
}
}
logger.Print("running tests end")
// quit
_, err = writeToIn.WriteString("quit()\n")
if err != nil {
t.Fatal("Stdin WriteString error:", err)
}
logger.Print("quit() sent")
close(chanQuit)
logger.Print("chanQuit closed")
writeToErr.Close()
writeToOut.Close()
logger.Print("pipes closed")
}
func readerToChan(t *testing.T, chanQuit chan struct{}, reader *bufio.Reader, chanOut chan string) {
logger.Print("readerToChan start")
for {
data, err := reader.ReadString('\n')
if err != nil && err != io.EOF {
t.Fatal("readerToChan ReadString error:", err)
}
select {
case <-chanQuit:
logger.Print("readerToChan end")
return
default:
}
chanOut <- data
}
}
func setStd(stdin *os.File, stderr *os.File, stdout *os.File) {
os.Stdin = stdin
os.Stderr = stderr
os.Stdout = stdout
}

38
src/tool/run/ast/ast.go Normal file
View file

@ -0,0 +1,38 @@
package ast
// Token is used in the lexer to split characters into a string called a token
type Token struct {
PosImpl
Tok int
Lit string
}
// TypeKind is the kinds of types
type TypeKind int
const (
// TypeDefault default type
TypeDefault TypeKind = iota
// TypePtr ptr type
TypePtr
// TypeSlice slice type
TypeSlice
// TypeMap map type
TypeMap
// TypeChan chan type
TypeChan
// TypeStructType struct type
TypeStructType
)
// TypeStruct is the type and sub-types
type TypeStruct struct {
Kind TypeKind
Env []string
Name string
Dimensions int
SubType *TypeStruct
Key *TypeStruct
StructNames []string
StructTypes []*TypeStruct
}

View file

@ -0,0 +1,281 @@
// +build !appengine
package astutil
import (
"fmt"
"reflect"
"github.com/surdeus/goblin/src/tool/run/ast"
)
// WalkFunc is used in Walk to walk the AST
type WalkFunc func(interface{}) error
// Walk walks the ASTs associated with a statement list generated by parser.ParseSrc
// each expression and/or statement is passed to the WalkFunc function.
// If the WalkFunc returns an error the walk is aborted and the error is returned
func Walk(stmt ast.Stmt, f WalkFunc) error {
return walkStmt(stmt, f)
}
func walkStmts(stmts []ast.Stmt, f WalkFunc) error {
for _, stmt := range stmts {
if err := walkStmt(stmt, f); err != nil {
return err
}
}
return nil
}
func walkExprs(exprs []ast.Expr, f WalkFunc) error {
for _, exp := range exprs {
if err := walkExpr(exp, f); err != nil {
return err
}
}
return nil
}
func walkStmt(stmt ast.Stmt, f WalkFunc) error {
//short circuit out if there are no functions
if stmt == nil || f == nil {
return nil
}
if err := callFunc(stmt, f); err != nil {
return err
}
switch stmt := stmt.(type) {
case *ast.StmtsStmt:
if err := walkStmts(stmt.Stmts, f); err != nil {
return err
}
case *ast.BreakStmt:
case *ast.ContinueStmt:
case *ast.LetMapItemStmt:
if err := walkExpr(stmt.RHS, f); err != nil {
return err
}
return walkExprs(stmt.LHSS, f)
case *ast.ReturnStmt:
return walkExprs(stmt.Exprs, f)
case *ast.ExprStmt:
return walkExpr(stmt.Expr, f)
case *ast.VarStmt:
return walkExprs(stmt.Exprs, f)
case *ast.LetsStmt:
if err := walkExprs(stmt.RHSS, f); err != nil {
return err
}
return walkExprs(stmt.LHSS, f)
case *ast.IfStmt:
if err := walkExpr(stmt.If, f); err != nil {
return err
}
if err := walkStmt(stmt.Then, f); err != nil {
return err
}
if err := walkStmts(stmt.ElseIf, f); err != nil {
return err
}
if err := walkStmt(stmt.Else, f); err != nil {
return err
}
case *ast.TryStmt:
if err := walkStmt(stmt.Try, f); err != nil {
return err
}
if err := walkStmt(stmt.Catch, f); err != nil {
return err
}
if err := walkStmt(stmt.Finally, f); err != nil {
return err
}
case *ast.LoopStmt:
if err := walkExpr(stmt.Expr, f); err != nil {
return err
}
if err := walkStmt(stmt.Stmt, f); err != nil {
return err
}
case *ast.ForStmt:
if err := walkExpr(stmt.Value, f); err != nil {
return err
}
if err := walkStmt(stmt.Stmt, f); err != nil {
return err
}
case *ast.CForStmt:
if err := walkStmt(stmt.Stmt1, f); err != nil {
return err
}
if err := walkExpr(stmt.Expr2, f); err != nil {
return err
}
if err := walkExpr(stmt.Expr3, f); err != nil {
return err
}
if err := walkStmt(stmt.Stmt, f); err != nil {
return err
}
case *ast.ThrowStmt:
if err := walkExpr(stmt.Expr, f); err != nil {
return err
}
case *ast.ModuleStmt:
if err := walkStmt(stmt.Stmt, f); err != nil {
return err
}
case *ast.SwitchStmt:
if err := walkExpr(stmt.Expr, f); err != nil {
return err
}
for _, switchCaseStmt := range stmt.Cases {
caseStmt := switchCaseStmt.(*ast.SwitchCaseStmt)
if err := walkStmt(caseStmt.Stmt, f); err != nil {
return err
}
}
if err := walkStmt(stmt.Default, f); err != nil {
return err
}
case *ast.GoroutineStmt:
return walkExpr(stmt.Expr, f)
default:
return fmt.Errorf("unknown statement %v", reflect.TypeOf(stmt))
}
return nil
}
func walkExpr(expr ast.Expr, f WalkFunc) error {
//short circuit out if there are no functions
if expr == nil || f == nil {
return nil
}
if err := callFunc(expr, f); err != nil {
return err
}
switch expr := expr.(type) {
case *ast.OpExpr:
return walkOperator(expr.Op, f)
case *ast.LenExpr:
case *ast.LiteralExpr:
case *ast.IdentExpr:
case *ast.MemberExpr:
return walkExpr(expr.Expr, f)
case *ast.ItemExpr:
if err := walkExpr(expr.Item, f); err != nil {
return err
}
return walkExpr(expr.Index, f)
case *ast.SliceExpr:
if err := walkExpr(expr.Item, f); err != nil {
return err
}
if err := walkExpr(expr.Begin, f); err != nil {
return err
}
return walkExpr(expr.End, f)
case *ast.ArrayExpr:
return walkExprs(expr.Exprs, f)
case *ast.MapExpr:
for i := range expr.Keys {
if err := walkExpr(expr.Keys[i], f); err != nil {
return err
}
if err := walkExpr(expr.Values[i], f); err != nil {
return err
}
}
case *ast.DerefExpr:
return walkExpr(expr.Expr, f)
case *ast.AddrExpr:
return walkExpr(expr.Expr, f)
case *ast.UnaryExpr:
return walkExpr(expr.Expr, f)
case *ast.ParenExpr:
return walkExpr(expr.SubExpr, f)
case *ast.FuncExpr:
return walkStmt(expr.Stmt, f)
case *ast.LetsExpr:
if err := walkExprs(expr.LHSS, f); err != nil {
return err
}
return walkExprs(expr.RHSS, f)
case *ast.AnonCallExpr:
if err := walkExpr(expr.Expr, f); err != nil {
return err
}
return walkExpr(&ast.CallExpr{Func: reflect.Value{}, SubExprs: expr.SubExprs, VarArg: expr.VarArg, Go: expr.Go}, f)
case *ast.CallExpr:
return walkExprs(expr.SubExprs, f)
case *ast.TernaryOpExpr:
if err := walkExpr(expr.Expr, f); err != nil {
return err
}
if err := walkExpr(expr.LHS, f); err != nil {
return err
}
return walkExpr(expr.RHS, f)
case *ast.ImportExpr:
return walkExpr(expr.Name, f)
case *ast.MakeExpr:
if err := walkExpr(expr.LenExpr, f); err != nil {
return err
}
return walkExpr(expr.CapExpr, f)
case *ast.ChanExpr:
if err := walkExpr(expr.RHS, f); err != nil {
return err
}
return walkExpr(expr.LHS, f)
case *ast.IncludeExpr:
if err := walkExpr(expr.ItemExpr, f); err != nil {
return err
}
return walkExpr(expr.ListExpr, f)
default:
return fmt.Errorf("unknown expression %v", reflect.TypeOf(expr))
}
return nil
}
func walkOperator(op ast.Operator, f WalkFunc) error {
//short circuit out if there are no functions
if op == nil || f == nil {
return nil
}
if err := callFunc(op, f); err != nil {
return err
}
switch op := op.(type) {
case *ast.BinaryOperator:
if err := walkExpr(op.LHS, f); err != nil {
return err
}
return walkExpr(op.RHS, f)
case *ast.ComparisonOperator:
if err := walkExpr(op.LHS, f); err != nil {
return err
}
return walkExpr(op.RHS, f)
case *ast.AddOperator:
if err := walkExpr(op.LHS, f); err != nil {
return err
}
return walkExpr(op.RHS, f)
case *ast.MultiplyOperator:
if err := walkExpr(op.LHS, f); err != nil {
return err
}
return walkExpr(op.RHS, f)
}
return nil
}
func callFunc(x interface{}, f WalkFunc) error {
if x == nil || f == nil {
return nil
}
return f(x)
}

View file

@ -0,0 +1,239 @@
package astutil
import (
"errors"
"fmt"
"testing"
"github.com/surdeus/goblin/src/tool/run/ast"
"github.com/surdeus/goblin/src/tool/run/parser"
)
const (
goodSrc string = `
var fmt = import("fmt")
a = "1"
b = 2
m = {}
func testA(arg1, arg2, arg3) {
v, ok = m["foo"]
return "A" + arg1 + arg2 + arg3
}
func Main(arg1) {
fmt.Println("enter Main")
b = testA(1, 2, 3) + Tester()
if b == 0 {
fmt.Println("b is 0")
} else if b == 1 {
fmt.Println("b is 1")
} else {
fmt.Println("b is other")
}
switch arg1 {
case 0:
fmt.Println("arg0 is 0")
case 1:
fmt.Println("arg0 is 1")
default:
fmt.Println("arg0 is other")
}
try {
throw "WTF!"
} catch e {
fmt.Println(e)
}
for n = 0; n < 3; n++ {
if n < 2 {
continue
}
fmt.Println(n)
}
for n in [1, 2, 3, 4, 5] {
fmt.Println(n)
if n > 3 {
break
}
}
n = 0
for n < 3 {
n++
}
a = {"foo": "bar"}
a.foo = "baz"
if a["foo"] == "zoo" {
fmt.Println("foo is zoo")
}
fmt.Println(a["foo"] == "zoo" ? "zoo" : "baz")
c = make(chan int64)
go func() {
c <- 1
c <- 2
c <- 3
}()
println(<-c)
println(<-c)
println(<-c)
v = make([]int, 3)
fmt.Println("sizeof v is ", len(v))
x = 1
y = (&x)
*y = 2
fmt.Println(x)
x, y = !x, 2
fmt.Println(x, y)
x = new(string)
fmt.Println(x)
var f = func() {
return "foo"
}
x = f()[0:1]
y = f()[0]
fmt.Println(x == y ? true : false)
}
func Tester() {
return "YES"
}
func testLen() {
return len("test")
}
fmt.Println(Main(1))
`
)
func TestWalk(t *testing.T) {
stmts, err := parser.ParseSrc(goodSrc)
if err != nil {
t.Fatal(err)
}
var mainFound bool
var lenFound bool
err = Walk(stmts, func(e interface{}) error {
switch exp := e.(type) {
case *ast.CallExpr:
switch exp.Name {
case `testA`:
if len(exp.SubExprs) != 3 {
return errors.New("invalid parameter count")
}
case `Main`:
if len(exp.SubExprs) != 1 {
return errors.New("invalid parameter count")
}
case `Tester`:
if len(exp.SubExprs) != 0 {
return errors.New("invalid parameter count")
}
}
case *ast.FuncExpr:
if !mainFound && exp.Name == `Main` {
mainFound = true
} else if mainFound && exp.Name == `Main` {
return errors.New("Main redefined")
}
case *ast.LenExpr:
lenFound = true
}
return nil
})
if err != nil {
t.Fatal(err)
}
if !mainFound {
t.Fatal("Main not found")
}
if !lenFound {
t.Fatal("len not found")
}
}
func Example_astWalk() {
src := `
var fmt = import("fmt")
func TestFunc(arg1, arg2, arg3) {
return (arg1 + arg2) * arg3
}
func Main() {
return TestFunc(1, 2, 3) + BuiltinFuncX(1, 2, 3)
}
fmt.Println(Main())
`
stmts, err := parser.ParseSrc(src)
if err != nil {
fmt.Println("ERROR: ", err)
return
}
var mainFound bool
err = Walk(stmts, func(e interface{}) error {
switch e := e.(type) {
case *ast.CallExpr:
//check if the BuiltinFuncX is getting the right number of args
if e.Name == `BuiltinFuncX` && len(e.SubExprs) != 3 {
return errors.New("invalid number of arguments to BuiltinFuncX")
}
case *ast.FuncExpr:
if !mainFound && e.Name == `Main` {
if len(e.Params) != 0 {
return errors.New("Too many args to main")
}
mainFound = true
} else if mainFound && e.Name == `Main` {
return errors.New("Main redefined")
}
}
return nil
})
if err != nil {
fmt.Println("ERROR: ", err)
return
}
if !mainFound {
fmt.Println("ERROR: Main not found")
}
}
func TestBadCode(t *testing.T) {
var codes = []string{
`const 1 = 2`,
`a["foo"] = 2, 3`,
`if a, 2 {}`,
`if break {}`,
`if a { else }`,
`if a { } else foo { }`,
`try a { } else { }`,
`try { } catch 1, 2 { }`,
`throw 1, 2`,
`for.true`,
`switch { var }`,
`case { var }`,
`v, ok = { "foo": "bar" }[const 1]`,
}
for _, code := range codes {
_, err := parser.ParseSrc(code)
if err == nil {
t.Fatalf("code %q should fail", code)
}
}
}

2
src/tool/run/ast/doc.go Normal file
View file

@ -0,0 +1,2 @@
// Package ast implements abstruct-syntax-tree for anko.
package ast

187
src/tool/run/ast/expr.go Normal file
View file

@ -0,0 +1,187 @@
package ast
import (
"reflect"
)
// Expr provides all of interfaces for expression.
type Expr interface {
Pos
}
// ExprImpl provide commonly implementations for Expr.
type ExprImpl struct {
PosImpl // PosImpl provide Pos() function.
}
// OpExpr provide operator expression.
type OpExpr struct {
ExprImpl
Op Operator
}
// LiteralExpr provide literal expression.
type LiteralExpr struct {
ExprImpl
Literal reflect.Value
}
// ArrayExpr provide Array expression.
type ArrayExpr struct {
ExprImpl
Exprs []Expr
TypeData *TypeStruct
}
// MapExpr provide Map expression.
type MapExpr struct {
ExprImpl
Keys []Expr
Values []Expr
TypeData *TypeStruct
}
// IdentExpr provide identity expression.
type IdentExpr struct {
ExprImpl
Lit string
}
// UnaryExpr provide unary minus expression. ex: -1, ^1, ~1.
type UnaryExpr struct {
ExprImpl
Operator string
Expr Expr
}
// AddrExpr provide referencing address expression.
type AddrExpr struct {
ExprImpl
Expr Expr
}
// DerefExpr provide dereferencing address expression.
type DerefExpr struct {
ExprImpl
Expr Expr
}
// ParenExpr provide parent block expression.
type ParenExpr struct {
ExprImpl
SubExpr Expr
}
// NilCoalescingOpExpr provide if invalid operator expression.
type NilCoalescingOpExpr struct {
ExprImpl
LHS Expr
RHS Expr
}
// TernaryOpExpr provide ternary operator expression.
type TernaryOpExpr struct {
ExprImpl
Expr Expr
LHS Expr
RHS Expr
}
// CallExpr provide calling expression.
type CallExpr struct {
ExprImpl
Func reflect.Value
Name string
SubExprs []Expr
VarArg bool
Go bool
}
// AnonCallExpr provide anonymous calling expression. ex: func(){}().
type AnonCallExpr struct {
ExprImpl
Expr Expr
SubExprs []Expr
VarArg bool
Go bool
}
// MemberExpr provide expression to refer member.
type MemberExpr struct {
ExprImpl
Expr Expr
Name string
}
// ItemExpr provide expression to refer Map/Array item.
type ItemExpr struct {
ExprImpl
Item Expr
Index Expr
}
// SliceExpr provide expression to refer slice of Array.
type SliceExpr struct {
ExprImpl
Item Expr
Begin Expr
End Expr
Cap Expr
}
// FuncExpr provide function expression.
type FuncExpr struct {
ExprImpl
Name string
Stmt Stmt
Params []string
VarArg bool
}
// LetsExpr provide multiple expression of let.
type LetsExpr struct {
ExprImpl
LHSS []Expr
RHSS []Expr
}
// ChanExpr provide chan expression.
type ChanExpr struct {
ExprImpl
LHS Expr
RHS Expr
}
// ImportExpr provide expression to import packages.
type ImportExpr struct {
ExprImpl
Name Expr
}
// MakeExpr provide expression to make instance.
type MakeExpr struct {
ExprImpl
TypeData *TypeStruct
LenExpr Expr
CapExpr Expr
}
// MakeTypeExpr provide expression to make type.
type MakeTypeExpr struct {
ExprImpl
Name string
Type Expr
}
// LenExpr provide expression to get length of array, map, etc.
type LenExpr struct {
ExprImpl
Expr Expr
}
// IncludeExpr provide in expression
type IncludeExpr struct {
ExprImpl
ItemExpr Expr
ListExpr Expr
}

View file

@ -0,0 +1,43 @@
package ast
// Operator provides interfaces for operators.
type Operator interface {
Pos
}
// OperatorImpl provides common implementations for Operator.
type OperatorImpl struct {
PosImpl // PosImpl provide Pos() function.
}
// BinaryOperator provides binary operation.
type BinaryOperator struct {
OperatorImpl
LHS Expr
Operator string
RHS Expr
}
// ComparisonOperator provides comparison operation.
type ComparisonOperator struct {
OperatorImpl
LHS Expr
Operator string
RHS Expr
}
// AddOperator provides add operation.
type AddOperator struct {
OperatorImpl
LHS Expr
Operator string
RHS Expr
}
// MultiplyOperator provides multiply operation.
type MultiplyOperator struct {
OperatorImpl
LHS Expr
Operator string
RHS Expr
}

28
src/tool/run/ast/pos.go Normal file
View file

@ -0,0 +1,28 @@
package ast
// Position provides interface to store code locations.
type Position struct {
Line int
Column int
}
// Pos interface provides two functions to get/set the position for expression or statement.
type Pos interface {
Position() Position
SetPosition(Position)
}
// PosImpl provides commonly implementations for Pos.
type PosImpl struct {
pos Position
}
// Position return the position of the expression or statement.
func (x *PosImpl) Position() Position {
return x.pos
}
// SetPosition is a function to specify position of the expression or statement.
func (x *PosImpl) SetPosition(pos Position) {
x.pos = pos
}

157
src/tool/run/ast/stmt.go Normal file
View file

@ -0,0 +1,157 @@
package ast
// Stmt provides all of interfaces for statement.
type Stmt interface {
Pos
}
// StmtImpl provide commonly implementations for Stmt..
type StmtImpl struct {
PosImpl // PosImpl provide Pos() function.
}
// StmtsStmt provides statements.
type StmtsStmt struct {
StmtImpl
Stmts []Stmt
}
// ExprStmt provide expression statement.
type ExprStmt struct {
StmtImpl
Expr Expr
}
// IfStmt provide "if/else" statement.
type IfStmt struct {
StmtImpl
If Expr
Then Stmt
ElseIf []Stmt // This is array of IfStmt
Else Stmt
}
// TryStmt provide "try/catch/finally" statement.
type TryStmt struct {
StmtImpl
Try Stmt
Var string
Catch Stmt
Finally Stmt
}
// ForStmt provide "for in" expression statement.
type ForStmt struct {
StmtImpl
Vars []string
Value Expr
Stmt Stmt
}
// CForStmt provide C-style "for (;;)" expression statement.
type CForStmt struct {
StmtImpl
Stmt1 Stmt
Expr2 Expr
Expr3 Expr
Stmt Stmt
}
// LoopStmt provide "for expr" expression statement.
type LoopStmt struct {
StmtImpl
Expr Expr
Stmt Stmt
}
// BreakStmt provide "break" expression statement.
type BreakStmt struct {
StmtImpl
}
// ContinueStmt provide "continue" expression statement.
type ContinueStmt struct {
StmtImpl
}
// ReturnStmt provide "return" expression statement.
type ReturnStmt struct {
StmtImpl
Exprs []Expr
}
// ThrowStmt provide "throw" expression statement.
type ThrowStmt struct {
StmtImpl
Expr Expr
}
// ModuleStmt provide "module" expression statement.
type ModuleStmt struct {
StmtImpl
Name string
Stmt Stmt
}
// SwitchStmt provide switch statement.
type SwitchStmt struct {
StmtImpl
Expr Expr
Cases []Stmt
Default Stmt
}
// SwitchCaseStmt provide switch case statement.
type SwitchCaseStmt struct {
StmtImpl
Exprs []Expr
Stmt Stmt
}
// VarStmt provide statement to let variables in current scope.
type VarStmt struct {
StmtImpl
Names []string
Exprs []Expr
}
// LetsStmt provide multiple statement of let.
type LetsStmt struct {
StmtImpl
LHSS []Expr
RHSS []Expr
}
// LetMapItemStmt provide statement of let for map item.
type LetMapItemStmt struct {
StmtImpl
LHSS []Expr
RHS Expr
}
// GoroutineStmt provide statement of groutine.
type GoroutineStmt struct {
StmtImpl
Expr Expr
}
// DeleteStmt provides statement of delete.
type DeleteStmt struct {
ExprImpl
Item Expr
Key Expr
}
// CloseStmt provides statement of close.
type CloseStmt struct {
StmtImpl
Expr Expr
}
// ChanStmt provide chan lets statement.
type ChanStmt struct {
ExprImpl
LHS Expr
OkExpr Expr
RHS Expr
}

View file

@ -0,0 +1,122 @@
package main
import (
"fmt"
"go/ast"
"go/parser"
"go/token"
"log"
"os"
"os/exec"
"path/filepath"
"sort"
"strings"
)
func pkgName(f string) string {
file, err := parser.ParseFile(token.NewFileSet(), f, nil, parser.PackageClauseOnly)
if err != nil || file == nil {
return ""
}
return file.Name.Name
}
func isGoFile(dir os.FileInfo) bool {
return !dir.IsDir() &&
!strings.HasPrefix(dir.Name(), ".") && // ignore .files
filepath.Ext(dir.Name()) == ".go"
}
func isPkgFile(dir os.FileInfo) bool {
return isGoFile(dir) && !strings.HasSuffix(dir.Name(), "_test.go") // ignore test files
}
func parseDir(p string) (map[string]*ast.Package, error) {
isGoFile := func(d os.FileInfo) bool {
return !d.IsDir() && !strings.HasSuffix(d.Name(), "_test.go") && !strings.HasPrefix(d.Name(), "example_")
}
pkgs, err := parser.ParseDir(token.NewFileSet(), p, isGoFile, parser.ParseComments)
if err != nil {
return nil, err
}
return pkgs, nil
}
func main() {
pkg := "flag"
if len(os.Args) == 2 {
pkg = os.Args[1]
}
b, err := exec.Command("go", "env", "GOROOT").CombinedOutput()
if err != nil {
log.Fatal(err)
}
paths := []string{filepath.Join(strings.TrimSpace(string(b)), "src")}
b, err = exec.Command("go", "env", "GOPATH").CombinedOutput()
if err != nil {
log.Fatal(err)
}
for _, p := range strings.Split(strings.TrimSpace(string(b)), string(filepath.ListSeparator)) {
paths = append(paths, filepath.Join(p, "src"))
}
for _, p := range paths {
pp := filepath.Join(p, pkg)
pkgs, err := parseDir(pp)
if err != nil || len(pkgs) == 0 {
continue
}
names := map[string]bool{}
pn := pkg
for _, pp := range pkgs {
pn = pp.Name
for _, f := range pp.Files {
for _, d := range f.Decls {
switch decl := d.(type) {
case *ast.GenDecl:
for _, spec := range decl.Specs {
if vspec, ok := spec.(*ast.ValueSpec); ok {
for _, n := range vspec.Names {
c := n.Name[0]
if c < 'A' || c > 'Z' {
continue
}
names[n.Name] = true
}
}
}
case *ast.FuncDecl:
if decl.Recv != nil {
continue
}
c := decl.Name.Name[0]
if c < 'A' || c > 'Z' {
continue
}
names[decl.Name.Name] = true
}
}
}
}
keys := []string{}
for k := range names {
keys = append(keys, k)
}
sort.Strings(keys)
fmt.Printf(`// Package %s implements %s interface for anko script.
package %s
import (
"%s"
)
func init() {
Packages["%s"] = map[string]interface{}{
`, pn, pkg, pn, pkg, pn)
for _, k := range keys {
fmt.Printf(` "%s": %s.%s,`+"\n", k, pn, k)
}
fmt.Println(` }
}`)
}
}

105
src/tool/run/core/core.go Normal file
View file

@ -0,0 +1,105 @@
// Package core implements core interface for anko script.
package core
import (
"fmt"
"io/ioutil"
"reflect"
"github.com/surdeus/goblin/src/tool/run/env"
"github.com/surdeus/goblin/src/tool/run/parser"
"github.com/surdeus/goblin/src/tool/run/vm"
)
// Import defines core language builtins - keys, range, println, etc.
func Import(e *env.Env) *env.Env {
e.Define("keys", func(v interface{}) []interface{} {
rv := reflect.ValueOf(v)
if rv.Kind() == reflect.Interface {
rv = rv.Elem()
}
mapKeysValue := rv.MapKeys()
mapKeys := make([]interface{}, len(mapKeysValue))
for i := 0; i < len(mapKeysValue); i++ {
mapKeys[i] = mapKeysValue[i].Interface()
}
return mapKeys
})
e.Define("range", func(args ...int64) []int64 {
var start, stop int64
var step int64 = 1
switch len(args) {
case 0:
panic("range expected at least 1 argument, got 0")
case 1:
stop = args[0]
case 2:
start = args[0]
stop = args[1]
case 3:
start = args[0]
stop = args[1]
step = args[2]
if step == 0 {
panic("range argument 3 must not be zero")
}
default:
panic(fmt.Sprintf("range expected at most 3 arguments, got %d", len(args)))
}
arr := []int64{}
for i := start; (step > 0 && i < stop) || (step < 0 && i > stop); i += step {
arr = append(arr, i)
}
return arr
})
e.Define("typeOf", func(v interface{}) string {
return reflect.TypeOf(v).String()
})
e.Define("kindOf", func(v interface{}) string {
typeOf := reflect.TypeOf(v)
if typeOf == nil {
return "nil"
}
return typeOf.Kind().String()
})
e.Define("defined", func(s string) bool {
_, err := e.Get(s)
return err == nil
})
e.Define("load", func(s string) interface{} {
body, err := ioutil.ReadFile(s)
if err != nil {
panic(err)
}
scanner := new(parser.Scanner)
scanner.Init(string(body))
stmts, err := parser.Parse(scanner)
if err != nil {
if pe, ok := err.(*parser.Error); ok {
pe.Filename = s
panic(pe)
}
panic(err)
}
rv, err := vm.Run(e, nil, stmts)
if err != nil {
panic(err)
}
return rv
})
e.Define("print", fmt.Print)
e.Define("println", fmt.Println)
e.Define("printf", fmt.Printf)
ImportToX(e)
return e
}

6
src/tool/run/core/testdata/broken.ank vendored Normal file
View file

@ -0,0 +1,6 @@
#!/usr/bin/env
use strict;
use warnings;
die "Hey! I'm anko";

17
src/tool/run/core/testdata/chan.ank vendored Normal file
View file

@ -0,0 +1,17 @@
c = make(chan int64)
r = []
go func() {
c <- 1
c <- 2
c <- 3
close(c)
}()
for a in c {
r += a
}
is([1,2,3], r, "chan")
nil

149
src/tool/run/core/testdata/core_test.go vendored Normal file
View file

@ -0,0 +1,149 @@
package core
import (
"fmt"
"os"
"reflect"
"strings"
"testing"
"github.com/surdeus/goblin/src/tool/run/packages"
"github.com/surdeus/goblin/src/tool/run/vm"
)
var testCoreEnvSetupFunc = func(t *testing.T, env corelib.Env) { Import(env.(*vm.Env)) }
func init() {
corelib.NewEnv = func() corelib.Env {
return vm.NewEnv()
}
}
func TestKeys(t *testing.T) {
os.Setenv("ANKO_DEBUG", "1")
tests := []testlib.Test{
{Script: `a = {}; b = keys(a)`, RunOutput: []interface{}{}, Output: map[string]interface{}{"a": map[interface{}]interface{}{}}},
{Script: `a = {"a": nil}; b = keys(a)`, RunOutput: []interface{}{"a"}, Output: map[string]interface{}{"a": map[interface{}]interface{}{"a": nil}}},
{Script: `a = {"a": 1}; b = keys(a)`, RunOutput: []interface{}{"a"}, Output: map[string]interface{}{"a": map[interface{}]interface{}{"a": int64(1)}}},
}
testlib.Run(t, tests, &testlib.Options{EnvSetupFunc: &testCoreEnvSetupFunc})
}
func TestKindOf(t *testing.T) {
os.Setenv("ANKO_DEBUG", "1")
tests := []testlib.Test{
{Script: `kindOf(a)`, Input: map[string]interface{}{"a": reflect.Value{}}, RunOutput: "struct", Output: map[string]interface{}{"a": reflect.Value{}}},
{Script: `kindOf(a)`, Input: map[string]interface{}{"a": nil}, RunOutput: "nil", Output: map[string]interface{}{"a": nil}},
{Script: `kindOf(a)`, Input: map[string]interface{}{"a": true}, RunOutput: "bool", Output: map[string]interface{}{"a": true}},
{Script: `kindOf(a)`, Input: map[string]interface{}{"a": int32(1)}, RunOutput: "int32", Output: map[string]interface{}{"a": int32(1)}},
{Script: `kindOf(a)`, Input: map[string]interface{}{"a": int64(1)}, RunOutput: "int64", Output: map[string]interface{}{"a": int64(1)}},
{Script: `kindOf(a)`, Input: map[string]interface{}{"a": float32(1.1)}, RunOutput: "float32", Output: map[string]interface{}{"a": float32(1.1)}},
{Script: `kindOf(a)`, Input: map[string]interface{}{"a": float64(1.1)}, RunOutput: "float64", Output: map[string]interface{}{"a": float64(1.1)}},
{Script: `kindOf(a)`, Input: map[string]interface{}{"a": "a"}, RunOutput: "string", Output: map[string]interface{}{"a": "a"}},
{Script: `kindOf(a)`, Input: map[string]interface{}{"a": 'a'}, RunOutput: "int32", Output: map[string]interface{}{"a": 'a'}},
{Script: `kindOf(a)`, Input: map[string]interface{}{"a": interface{}(nil)}, RunOutput: "nil", Output: map[string]interface{}{"a": interface{}(nil)}},
{Script: `kindOf(a)`, Input: map[string]interface{}{"a": interface{}(true)}, RunOutput: "bool", Output: map[string]interface{}{"a": interface{}(true)}},
{Script: `kindOf(a)`, Input: map[string]interface{}{"a": interface{}(int32(1))}, RunOutput: "int32", Output: map[string]interface{}{"a": interface{}(int32(1))}},
{Script: `kindOf(a)`, Input: map[string]interface{}{"a": interface{}(int64(1))}, RunOutput: "int64", Output: map[string]interface{}{"a": interface{}(int64(1))}},
{Script: `kindOf(a)`, Input: map[string]interface{}{"a": interface{}(float32(1))}, RunOutput: "float32", Output: map[string]interface{}{"a": interface{}(float32(1))}},
{Script: `kindOf(a)`, Input: map[string]interface{}{"a": interface{}(float64(1))}, RunOutput: "float64", Output: map[string]interface{}{"a": interface{}(float64(1))}},
{Script: `kindOf(a)`, Input: map[string]interface{}{"a": interface{}("a")}, RunOutput: "string", Output: map[string]interface{}{"a": interface{}("a")}},
{Script: `kindOf(a)`, Input: map[string]interface{}{"a": []interface{}{}}, RunOutput: "slice", Output: map[string]interface{}{"a": []interface{}{}}},
{Script: `kindOf(a)`, Input: map[string]interface{}{"a": []interface{}{nil}}, RunOutput: "slice", Output: map[string]interface{}{"a": []interface{}{nil}}},
{Script: `kindOf(a)`, Input: map[string]interface{}{"a": map[string]interface{}{}}, RunOutput: "map", Output: map[string]interface{}{"a": map[string]interface{}{}}},
{Script: `kindOf(a)`, Input: map[string]interface{}{"a": map[string]interface{}{"b": "b"}}, RunOutput: "map", Output: map[string]interface{}{"a": map[string]interface{}{"b": "b"}}},
{Script: `a = make(interface); kindOf(a)`, RunOutput: "nil", Output: map[string]interface{}{"a": interface{}(nil)}},
}
testlib.Run(t, tests, &testlib.Options{EnvSetupFunc: &testCoreEnvSetupFunc})
}
func TestRange(t *testing.T) {
os.Setenv("ANKO_DEBUG", "")
tests := []testlib.Test{
// 0 arguments
{Script: `range()`, RunError: fmt.Errorf("range expected at least 1 argument, got 0")},
// 1 arguments(step == 1, start == 0)
{Script: `range(-1)`, RunOutput: []int64{}},
{Script: `range(0)`, RunOutput: []int64{}},
{Script: `range(1)`, RunOutput: []int64{0}},
{Script: `range(2)`, RunOutput: []int64{0, 1}},
{Script: `range(10)`, RunOutput: []int64{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}},
// 2 arguments(step == 1)
{Script: `range(-5,-1)`, RunOutput: []int64{-5, -4, -3, -2}},
{Script: `range(-1,1)`, RunOutput: []int64{-1, 0}},
{Script: `range(1,5)`, RunOutput: []int64{1, 2, 3, 4}},
// 3 arguments
// step == 2
{Script: `range(-5,-1,2)`, RunOutput: []int64{-5, -3}},
{Script: `range(1,5,2)`, RunOutput: []int64{1, 3}},
{Script: `range(-1,5,2)`, RunOutput: []int64{-1, 1, 3}},
// step < 0 and from small to large
{Script: `range(-5,-1,-1)`, RunOutput: []int64{}},
{Script: `range(1,5,-1)`, RunOutput: []int64{}},
{Script: `range(-1,5,-1)`, RunOutput: []int64{}},
// step < 0 and from large to small
{Script: `range(-1,-5,-1)`, RunOutput: []int64{-1, -2, -3, -4}},
{Script: `range(5,1,-1)`, RunOutput: []int64{5, 4, 3, 2}},
{Script: `range(5,-1,-1)`, RunOutput: []int64{5, 4, 3, 2, 1, 0}},
// 4,5 arguments
{Script: `range(1,5,1,1)`, RunError: fmt.Errorf("range expected at most 3 arguments, got 4")},
{Script: `range(1,5,1,1,1)`, RunError: fmt.Errorf("range expected at most 3 arguments, got 5")},
// more 0 test
{Script: `range(0,1,2)`, RunOutput: []int64{0}},
{Script: `range(1,0,2)`, RunOutput: []int64{}},
{Script: `range(1,2,0)`, RunError: fmt.Errorf("range argument 3 must not be zero")},
}
testlib.Run(t, tests, &testlib.Options{EnvSetupFunc: &testCoreEnvSetupFunc})
}
func TestLoad(t *testing.T) {
os.Setenv("ANKO_DEBUG", "")
notFoundRunErrorFunc := func(t *testing.T, err error) {
if err == nil || !strings.HasPrefix(err.Error(), "open testdata/not-found.ank:") {
t.Errorf("load not-found.ank failed - received: %v", err)
}
}
tests := []testlib.Test{
{Script: `load('testdata/test.ank'); X(1)`, RunOutput: int64(2)},
{Script: `load('testdata/not-found.ank'); X(1)`, RunErrorFunc: &notFoundRunErrorFunc},
{Script: `load('testdata/broken.ank'); X(1)`, RunError: fmt.Errorf("syntax error")},
}
testlib.Run(t, tests, &testlib.Options{EnvSetupFunc: &testCoreEnvSetupFunc})
}
func TestAnk(t *testing.T) {
os.Setenv("ANKO_DEBUG", "")
var testEnvSetupFunc = func(t *testing.T, env corelib.Env) {
e := env.(*vm.Env)
Import(e)
packages.DefineImport(e)
}
tests := []testlib.Test{
{Script: `load('testdata/testing.ank'); load('testdata/let.ank')`},
{Script: `load('testdata/testing.ank'); load('testdata/toString.ank')`},
{Script: `load('testdata/testing.ank'); load('testdata/op.ank')`},
{Script: `load('testdata/testing.ank'); load('testdata/func.ank')`},
{Script: `load('testdata/testing.ank'); load('testdata/len.ank')`},
{Script: `load('testdata/testing.ank'); load('testdata/for.ank')`},
{Script: `load('testdata/testing.ank'); load('testdata/switch.ank')`},
{Script: `load('testdata/testing.ank'); load('testdata/if.ank')`},
{Script: `load('testdata/testing.ank'); load('testdata/toBytes.ank')`},
{Script: `load('testdata/testing.ank'); load('testdata/toRunes.ank')`},
{Script: `load('testdata/testing.ank'); load('testdata/chan.ank')`},
}
testlib.Run(t, tests, &testlib.Options{EnvSetupFunc: &testEnvSetupFunc})
}
func TestDefined(t *testing.T) {
os.Setenv("ANKO_DEBUG", "")
tests := []testlib.Test{
{Script: `var a = 1; defined("a")`, RunOutput: true},
{Script: `defined("a")`, RunOutput: false},
{Script: `func(){ var a = 1 }(); defined("a")`, RunOutput: false},
}
testlib.Run(t, tests, &testlib.Options{EnvSetupFunc: &testCoreEnvSetupFunc})
}

3
src/tool/run/core/testdata/error.ank vendored Normal file
View file

@ -0,0 +1,3 @@
func X(a) {
a.notfound = 1
}

75
src/tool/run/core/testdata/for.ank vendored Normal file
View file

@ -0,0 +1,75 @@
x = 0
for a in [1,2,3] {
x += 1
}
is(3, x, "for a in range [1,2,3]")
x = 0
for {
x += 1
if (x > 3) {
break
}
}
is(4, x, "for loop")
func loop_with_return_stmt() {
y = 0
for {
if y == 5 {
return y
}
y++
}
return 1
}
is(5, loop_with_return_stmt(), "loop with return stmt")
func for_with_return_stmt() {
y = 0
for k in range(0, 10) {
if k == 5 {
return y
}
y++
}
return 1
}
is(5, for_with_return_stmt(), "for loop with return stmt")
x = 0
for a = 0; a < 10; a++ {
x++
}
is(10, x, "C-style for loop")
func cstylefor_with_return_stmt() {
y = 0
for i = 0; i < 10; i++ {
if i == 5 {
return y
}
y++
}
return 1
}
is(5, cstylefor_with_return_stmt(), "C-style for loop with return statement")
resp = {
"items": [{
"someData": 2,
}]
}
x = 0
for item in resp.items {
x += item.someData
}
is(2, x, "dereference slice element")
nil

24
src/tool/run/core/testdata/func.ank vendored Normal file
View file

@ -0,0 +1,24 @@
func a() { return 2 }
is(2, a(), "func a() { return 2 }")
func b(x) { return x + 1 }
is(3, b(2), "func b(x) { return x + 1 }")
func c(x) { return x, x + 1 }
is([2,3], c(2), "func c(x) { return x, x + 1 }")
func d(x) { return func() { return x + 1 } }
is(3, d(2)(), "func d(x) { return func() { return x + 1 } }")
var x = func(x) {
return func(y) {
x(y)
}
}(func(z) {
return "Yay! " + z
})("hello world")
is("Yay! hello world", x, "...")
nil

14
src/tool/run/core/testdata/if.ank vendored Normal file
View file

@ -0,0 +1,14 @@
r = -1
if (false) {
r = 1
} else if (false) {
r = 2
} else if (false) {
r = 3
} else {
r = 4
}
is(4, r, "if")
nil

6
src/tool/run/core/testdata/len.ank vendored Normal file
View file

@ -0,0 +1,6 @@
is(3, len("foo"), "len(\"foo\")")
is(0, len(""), "len(\"\")")
is(4, len([1,2,true,["foo"]]), "len([1,2,true,[\"foo\"]])")
nil

32
src/tool/run/core/testdata/let.ank vendored Normal file
View file

@ -0,0 +1,32 @@
a = nil
is(nil, a, "let nil")
a = 1
is(1, a, "let int")
a = 1.2
is(1.2, a, "let float")
a = "foo"
is("foo", a, "let string")
a = nil
is(nil, a, "let nil")
a = true
is(true, a, "let true")
a = false
is(false, a, "let false")
a = [1,2,3]
is([1,2,3], a, "let array")
a = {"foo": "bar", "bar": "baz"}
is({"bar": "baz", "foo": "bar"}, a, "let map")
a = {"foo": "bar", "bar": {"blah": true, "blah!": [1.3e3, true]}}
is({"foo": "bar", "bar": {"blah": true, "blah!": [1.3e3, true]}}, a, "let map deep")
nil

63
src/tool/run/core/testdata/op.ank vendored Normal file
View file

@ -0,0 +1,63 @@
ok(1 > 0, "1 > 0")
ok(1 == 1.0, "1 == 1.0")
is(1 != "1", false, "1 != \"1\"")
ok(1 == 1, "1 == 1")
ok(1.1 == 1.1, "1.1 == 1.1")
ok("1" == "1", "\"1\" == \"1\"")
ok(false != "1", "false != \"1\"")
ok(false != true, "false != true")
ok(false == false, "false == false")
ok(true == true, "true == true")
ok(false == false, "false == false")
ok(nil == nil, "nil == nil")
ok(1 <= 1, "1 <= 1")
ok(1.0 <= 1.0, "1.0 <= 1.0")
is(true, 1 <= 2 ? true : false, "1 == 1 ? true : false")
a = 1; a += 1
is(2, a, "+=")
a = 2; a -= 1
is(1, a, "-=")
a = 2; a *= 2
is(4, a, "*=")
a = 3; a /= 2
is(1.5, a, "/=")
a = 2; a++
is(3, a, "++")
a = 2; a--
is(1, a, "--")
a = 1; a &= 2
is(0, a, "&=")
a = 1; a |= 2
is(3, a, "|=")
a = !3
is(false, a, "!3")
a = !true
is(false, a, "!true")
a = !false
is(true, a, "!false")
a = ^3
is(-4, a, "^3")
a = 3 << 2
is(12, a, "3 << 2")
a = 11 >> 2
is(2, a, "11 >> 2")
nil

32
src/tool/run/core/testdata/sort.ank vendored Normal file
View file

@ -0,0 +1,32 @@
sort = import("sort")
a = make([]int)
b = make([]int)
a += [1, 2, 3]
b += [3, 1, 2]
sort.Ints(b)
is(a, b, "sort.Ints")
a = make([]float64)
b = make([]float64)
a += [1.1, 2.2, 3.3]
b += [3.3, 1.1, 2.2]
sort.Float64s(b)
is(a, b, "sort.Float64s")
a = make([]string)
b = make([]string)
a += ["a", "b", "c", "d"]
b += ["b", "d", "a", "c"]
sort.Strings(b)
is(a, b, "sort.Strings")
if go18later() {
a = [1, 3, 2]
sort.Slice(a, func(i, j) {
return a[i] < a[j]
})
is([1,2,3], a, "sort.Slice")
}
nil

40
src/tool/run/core/testdata/switch.ank vendored Normal file
View file

@ -0,0 +1,40 @@
x = 0
r = -1
switch x {
case 0:
r = 0
case 1:
r = 1
case 2:
r = 2
}
is(0, r, "switch/case")
x = 3
r = -1
switch x {
case 0:
r = 0
case 1:
r = 1
case 2:
r = 2
}
is(-1, r, "switch/case")
x = 3
r = -1
switch x {
case 0:
r = 0
case 1:
r = 1
case 2:
r = 2
default:
r = 3
}
is(3, r, "switch/default")
nil

3
src/tool/run/core/testdata/test.ank vendored Normal file
View file

@ -0,0 +1,3 @@
func X(a) {
return a + 1
}

19
src/tool/run/core/testdata/testing.ank vendored Normal file
View file

@ -0,0 +1,19 @@
var runtime = import("runtime")
var regexp = import("regexp")
func is(expect, got, name) {
if expect != got {
panic("is - received: " + toString(got) + " - expected: " + toString(expect) + " - for: " + toString(name))
}
}
func ok(expect, name) {
if !expect {
panic("ok - received: " + toString(expect) + " - for: " + toString(name))
}
}
func go18later() {
return regexp.MustCompile("^go1\\.(1[0-9]|[8-9])\\.[0-9]$").MatchString(runtime.Version())
}

12
src/tool/run/core/testdata/toBytes.ank vendored Normal file
View file

@ -0,0 +1,12 @@
a = toByteSlice("あいうえお")
b = [227, 129, 130, 227, 129, 132, 227, 129, 134, 227, 129, 136, 227, 129, 138]
x = 0
for i = 0; i < len(a); i++ {
if (a[i] == b[i]) {
x++
}
}
is(x, len(a), "toByteSlice(str)")
nil

12
src/tool/run/core/testdata/toRunes.ank vendored Normal file
View file

@ -0,0 +1,12 @@
a = toRuneSlice("あいうえお")
b = [12354, 12356, 12358, 12360, 12362]
x = 0
for i = 0; i < len(a); i++ {
if (a[i] == b[i]) {
x++
}
}
is(x, len(a), "toRuneSlice(str)")
nil

View file

@ -0,0 +1,8 @@
is("1", toString(1), "toString(int)")
is("1.2", toString(1.2), "toString(float)")
is("true", toString(true), "toString(true)")
is("false", toString(false), "toString(false)")
is("foo", toString("foo"), "toString(\"foo\")")
nil

151
src/tool/run/core/testdata/toX_test.go vendored Normal file
View file

@ -0,0 +1,151 @@
package core
import (
"fmt"
"os"
"testing"
"time"
"github.com/surdeus/goblin/src/tool/run/internal/testlib"
)
func TestToX(t *testing.T) {
os.Setenv("ANKO_DEBUG", "1")
tests := []testlib.Test{
{Script: `toBool(-2)`, RunOutput: false},
{Script: `toBool(-1.5)`, RunOutput: false},
{Script: `toBool(-1)`, RunOutput: false},
{Script: `toBool(-0.4)`, RunOutput: false},
{Script: `toBool(0)`, RunOutput: false},
{Script: `toBool(0.4)`, RunOutput: true},
{Script: `toBool(1)`, RunOutput: true},
{Script: `toBool(1.5)`, RunOutput: true},
{Script: `toBool(2)`, RunOutput: true},
{Script: `toBool(true)`, RunOutput: true},
{Script: `toBool(false)`, RunOutput: false},
{Script: `toBool("true")`, RunOutput: true},
{Script: `toBool("false")`, RunOutput: false},
{Script: `toBool("yes")`, RunOutput: true},
{Script: `toBool("ye")`, RunOutput: false},
{Script: `toBool("y")`, RunOutput: true},
{Script: `toBool("false")`, RunOutput: false},
{Script: `toBool("f")`, RunOutput: false},
{Script: `toBool("")`, RunOutput: false},
{Script: `toBool(nil)`, RunOutput: false},
{Script: `toBool({})`, RunOutput: false},
{Script: `toBool([])`, RunOutput: false},
{Script: `toBool([true])`, RunOutput: false},
{Script: `toBool({"true": "true"})`, RunOutput: false},
{Script: `toString(nil)`, RunOutput: "<nil>"},
{Script: `toString("")`, RunOutput: ""},
{Script: `toString(1)`, RunOutput: "1"},
{Script: `toString(1.2)`, RunOutput: "1.2"},
{Script: `toString(1/3)`, RunOutput: "0.3333333333333333"},
{Script: `toString(false)`, RunOutput: "false"},
{Script: `toString(true)`, RunOutput: "true"},
{Script: `toString({})`, RunOutput: "map[]"},
{Script: `toString({"foo": "bar"})`, RunOutput: "map[foo:bar]"},
{Script: `toString([true,nil])`, RunOutput: "[true <nil>]"},
{Script: `toString(toByteSlice("foo"))`, RunOutput: "foo"},
{Script: `toInt(nil)`, RunOutput: int64(0)},
{Script: `toInt(-2)`, RunOutput: int64(-2)},
{Script: `toInt(-1.4)`, RunOutput: int64(-1)},
{Script: `toInt(-1)`, RunOutput: int64(-1)},
{Script: `toInt(0)`, RunOutput: int64(0)},
{Script: `toInt(1)`, RunOutput: int64(1)},
{Script: `toInt(1.4)`, RunOutput: int64(1)},
{Script: `toInt(1.5)`, RunOutput: int64(1)},
{Script: `toInt(1.9)`, RunOutput: int64(1)},
{Script: `toInt(2)`, RunOutput: int64(2)},
{Script: `toInt(2.1)`, RunOutput: int64(2)},
{Script: `toInt("2")`, RunOutput: int64(2)},
{Script: `toInt("2.1")`, RunOutput: int64(2)},
{Script: `toInt(true)`, RunOutput: int64(1)},
{Script: `toInt(false)`, RunOutput: int64(0)},
{Script: `toInt({})`, RunOutput: int64(0)},
{Script: `toInt([])`, RunOutput: int64(0)},
{Script: `toFloat(nil)`, RunOutput: float64(0.0)},
{Script: `toFloat(-2)`, RunOutput: float64(-2.0)},
{Script: `toFloat(-1.4)`, RunOutput: float64(-1.4)},
{Script: `toFloat(-1)`, RunOutput: float64(-1.0)},
{Script: `toFloat(0)`, RunOutput: float64(0.0)},
{Script: `toFloat(1)`, RunOutput: float64(1.0)},
{Script: `toFloat(1.4)`, RunOutput: float64(1.4)},
{Script: `toFloat(1.5)`, RunOutput: float64(1.5)},
{Script: `toFloat(1.9)`, RunOutput: float64(1.9)},
{Script: `toFloat(2)`, RunOutput: float64(2.0)},
{Script: `toFloat(2.1)`, RunOutput: float64(2.1)},
{Script: `toFloat("2")`, RunOutput: float64(2.0)},
{Script: `toFloat("2.1")`, RunOutput: float64(2.1)},
{Script: `toFloat(true)`, RunOutput: float64(1.0)},
{Script: `toFloat(false)`, RunOutput: float64(0.0)},
{Script: `toFloat({})`, RunOutput: float64(0.0)},
{Script: `toFloat([])`, RunOutput: float64(0.0)},
{Script: `toChar(0x1F431)`, RunOutput: "🐱"},
{Script: `toChar(0)`, RunOutput: "\x00"},
{Script: `toRune("")`, RunOutput: rune(0)},
{Script: `toRune("🐱")`, RunOutput: rune(0x1F431)},
{Script: `toBoolSlice(nil)`, RunOutput: []bool{}},
{Script: `toBoolSlice(1)`, RunError: fmt.Errorf("function wants argument type []interface {} but received type int64")},
{Script: `toBoolSlice(1.2)`, RunError: fmt.Errorf("function wants argument type []interface {} but received type float64")},
{Script: `toBoolSlice(false)`, RunError: fmt.Errorf("function wants argument type []interface {} but received type bool")},
{Script: `toBoolSlice({})`, RunError: fmt.Errorf("function wants argument type []interface {} but received type map[interface {}]interface {}")},
{Script: `toBoolSlice([])`, RunOutput: []bool{}},
{Script: `toBoolSlice([nil])`, RunOutput: []bool{false}},
{Script: `toBoolSlice([1])`, RunOutput: []bool{false}},
{Script: `toBoolSlice([1.1])`, RunOutput: []bool{false}},
{Script: `toBoolSlice([true])`, RunOutput: []bool{true}},
{Script: `toBoolSlice([[]])`, RunOutput: []bool{false}},
{Script: `toBoolSlice([{}])`, RunOutput: []bool{false}},
{Script: `toIntSlice(nil)`, RunOutput: []int64{}},
{Script: `toIntSlice(1)`, RunError: fmt.Errorf("function wants argument type []interface {} but received type int64")},
{Script: `toIntSlice(1.2)`, RunError: fmt.Errorf("function wants argument type []interface {} but received type float64")},
{Script: `toIntSlice(false)`, RunError: fmt.Errorf("function wants argument type []interface {} but received type bool")},
{Script: `toIntSlice({})`, RunError: fmt.Errorf("function wants argument type []interface {} but received type map[interface {}]interface {}")},
{Script: `toIntSlice([])`, RunOutput: []int64{}},
{Script: `toIntSlice([nil])`, RunOutput: []int64{0}},
{Script: `toIntSlice([1])`, RunOutput: []int64{1}},
{Script: `toIntSlice([1.1])`, RunOutput: []int64{1}},
{Script: `toIntSlice([true])`, RunOutput: []int64{0}},
{Script: `toIntSlice([[]])`, RunOutput: []int64{0}},
{Script: `toIntSlice([{}])`, RunOutput: []int64{0}},
{Script: `toFloatSlice(nil)`, RunOutput: []float64{}},
{Script: `toFloatSlice(1)`, RunError: fmt.Errorf("function wants argument type []interface {} but received type int64")},
{Script: `toFloatSlice(1.2)`, RunError: fmt.Errorf("function wants argument type []interface {} but received type float64")},
{Script: `toFloatSlice(false)`, RunError: fmt.Errorf("function wants argument type []interface {} but received type bool")},
{Script: `toFloatSlice({})`, RunError: fmt.Errorf("function wants argument type []interface {} but received type map[interface {}]interface {}")},
{Script: `toFloatSlice([])`, RunOutput: []float64{}},
{Script: `toFloatSlice([nil])`, RunOutput: []float64{0.0}},
{Script: `toFloatSlice([1])`, RunOutput: []float64{1.0}},
{Script: `toFloatSlice([1.1])`, RunOutput: []float64{1.1}},
{Script: `toFloatSlice([true])`, RunOutput: []float64{0.0}},
{Script: `toFloatSlice([[]])`, RunOutput: []float64{0.0}},
{Script: `toFloatSlice([{}])`, RunOutput: []float64{0.0}},
{Script: `toByteSlice(nil)`, RunOutput: []byte{}},
{Script: `toByteSlice([])`, RunError: fmt.Errorf("function wants argument type string but received type []interface {}")},
{Script: `toByteSlice(1)`, RunOutput: []byte{0x01}}, // FIXME?
{Script: `toByteSlice(1.1)`, RunError: fmt.Errorf("function wants argument type string but received type float64")},
{Script: `toByteSlice(true)`, RunError: fmt.Errorf("function wants argument type string but received type bool")},
{Script: `toByteSlice("foo")`, RunOutput: []byte{'f', 'o', 'o'}},
{Script: `toByteSlice("世界")`, RunOutput: []byte{0xe4, 0xb8, 0x96, 0xe7, 0x95, 0x8c}},
{Script: `toRuneSlice(nil)`, RunOutput: []rune{}},
{Script: `toRuneSlice([])`, RunError: fmt.Errorf("function wants argument type string but received type []interface {}")},
{Script: `toRuneSlice(1)`, RunOutput: []rune{0x01}}, // FIXME?
{Script: `toRuneSlice(1.1)`, RunError: fmt.Errorf("function wants argument type string but received type float64")},
{Script: `toRuneSlice(true)`, RunError: fmt.Errorf("function wants argument type string but received type bool")},
{Script: `toRuneSlice("foo")`, RunOutput: []rune{'f', 'o', 'o'}},
{Script: `toRuneSlice("世界")`, RunOutput: []rune{'世', '界'}},
{Script: `toStringSlice([true,false,1])`, RunOutput: []string{"", "", "\x01"}}, // FIXME?
{Script: `toDuration(nil)`, RunOutput: time.Duration(0)},
{Script: `toDuration(0)`, RunOutput: time.Duration(0)},
{Script: `toDuration(true)`, RunError: fmt.Errorf("function wants argument type int64 but received type bool")},
{Script: `toDuration([])`, RunError: fmt.Errorf("function wants argument type int64 but received type []interface {}")},
{Script: `toDuration({})`, RunError: fmt.Errorf("function wants argument type int64 but received type map[interface {}]interface {}")},
{Script: `toDuration("")`, RunError: fmt.Errorf("function wants argument type int64 but received type string")},
{Script: `toDuration("1s")`, RunError: fmt.Errorf("function wants argument type int64 but received type string")}, // TODO
{Script: `toDuration(a)`, Input: map[string]interface{}{"a": int64(time.Duration(123 * time.Minute))}, RunOutput: time.Duration(123 * time.Minute)},
{Script: `toDuration(a)`, Input: map[string]interface{}{"a": float64(time.Duration(123 * time.Minute))}, RunOutput: time.Duration(123 * time.Minute)},
{Script: `toDuration(a)`, Input: map[string]interface{}{"a": time.Duration(123 * time.Minute)}, RunOutput: time.Duration(123 * time.Minute)},
}
testlib.Run(t, tests, &testlib.Options{EnvSetupFunc: &testCoreEnvSetupFunc})
}

171
src/tool/run/core/toX.go Normal file
View file

@ -0,0 +1,171 @@
package core
import (
"fmt"
"reflect"
"strconv"
"strings"
"time"
"github.com/surdeus/goblin/src/tool/run/env"
)
// ImportToX adds all the toX to the env given
func ImportToX(e *env.Env) {
e.Define("toBool", func(v interface{}) bool {
rv := reflect.ValueOf(v)
if !rv.IsValid() {
return false
}
nt := reflect.TypeOf(true)
if rv.Type().ConvertibleTo(nt) {
return rv.Convert(nt).Bool()
}
if rv.Type().ConvertibleTo(reflect.TypeOf(1.0)) && rv.Convert(reflect.TypeOf(1.0)).Float() > 0.0 {
return true
}
if rv.Kind() == reflect.String {
s := strings.ToLower(v.(string))
if s == "y" || s == "yes" {
return true
}
b, err := strconv.ParseBool(s)
if err == nil {
return b
}
}
return false
})
e.Define("toString", func(v interface{}) string {
if b, ok := v.([]byte); ok {
return string(b)
}
return fmt.Sprint(v)
})
e.Define("toInt", func(v interface{}) int64 {
rv := reflect.ValueOf(v)
if !rv.IsValid() {
return 0
}
nt := reflect.TypeOf(1)
if rv.Type().ConvertibleTo(nt) {
return rv.Convert(nt).Int()
}
if rv.Kind() == reflect.String {
i, err := strconv.ParseInt(v.(string), 10, 64)
if err == nil {
return i
}
f, err := strconv.ParseFloat(v.(string), 64)
if err == nil {
return int64(f)
}
}
if rv.Kind() == reflect.Bool {
if v.(bool) {
return 1
}
}
return 0
})
e.Define("toFloat", func(v interface{}) float64 {
rv := reflect.ValueOf(v)
if !rv.IsValid() {
return 0
}
nt := reflect.TypeOf(1.0)
if rv.Type().ConvertibleTo(nt) {
return rv.Convert(nt).Float()
}
if rv.Kind() == reflect.String {
f, err := strconv.ParseFloat(v.(string), 64)
if err == nil {
return f
}
}
if rv.Kind() == reflect.Bool {
if v.(bool) {
return 1.0
}
}
return 0.0
})
e.Define("toChar", func(s rune) string {
return string(s)
})
e.Define("toRune", func(s string) rune {
if len(s) == 0 {
return 0
}
return []rune(s)[0]
})
e.Define("toBoolSlice", func(v []interface{}) []bool {
var result []bool
toSlice(v, &result)
return result
})
e.Define("toStringSlice", func(v []interface{}) []string {
var result []string
toSlice(v, &result)
return result
})
e.Define("toIntSlice", func(v []interface{}) []int64 {
var result []int64
toSlice(v, &result)
return result
})
e.Define("toFloatSlice", func(v []interface{}) []float64 {
var result []float64
toSlice(v, &result)
return result
})
e.Define("toByteSlice", func(s string) []byte {
return []byte(s)
})
e.Define("toRuneSlice", func(s string) []rune {
return []rune(s)
})
e.Define("toDuration", func(v int64) time.Duration {
return time.Duration(v)
})
}
// toSlice takes in a "generic" slice and converts and copies
// it's elements into the typed slice pointed at by ptr.
// Note that this is a costly operation.
func toSlice(from []interface{}, ptr interface{}) {
// Value of the pointer to the target
obj := reflect.Indirect(reflect.ValueOf(ptr))
// We can't just convert from interface{} to whatever the target is (diff memory layout),
// so we need to create a New slice of the proper type and copy the values individually
t := reflect.TypeOf(ptr).Elem()
tt := t.Elem()
slice := reflect.MakeSlice(t, len(from), len(from))
// Copying the data, val is an addressable Pointer of the actual target type
val := reflect.Indirect(reflect.New(tt))
for i := 0; i < len(from); i++ {
v := reflect.ValueOf(from[i])
if v.IsValid() && v.Type().ConvertibleTo(tt) {
val.Set(v.Convert(tt))
} else {
val.Set(reflect.Zero(tt))
}
slice.Index(i).Set(val)
}
// Ok now assign our slice to the target pointer
obj.Set(slice)
}

187
src/tool/run/env/env.go vendored Normal file
View file

@ -0,0 +1,187 @@
package env
import (
"bytes"
"errors"
"fmt"
"reflect"
"sync"
)
type (
// ExternalLookup for Env external lookup of values and types.
ExternalLookup interface {
Get(string) (reflect.Value, error)
Type(string) (reflect.Type, error)
}
// Env is the environment needed for a VM to run in.
Env struct {
rwMutex *sync.RWMutex
parent *Env
values map[string]reflect.Value
types map[string]reflect.Type
externalLookup ExternalLookup
}
)
var (
// Packages is a where packages can be stored so VM import command can be used to import them.
// reflect.Value must be valid or VM may crash.
// For nil must use NilValue.
Packages = make(map[string]map[string]reflect.Value)
// PackageTypes is a where package types can be stored so VM import command can be used to import them
// reflect.Type must be valid or VM may crash.
// For nil type must use NilType.
PackageTypes = make(map[string]map[string]reflect.Type)
// NilType is the reflect.type of nil
NilType = reflect.TypeOf(nil)
// NilValue is the reflect.value of nil
NilValue = reflect.New(reflect.TypeOf((*interface{})(nil)).Elem()).Elem()
basicTypes = map[string]reflect.Type{
"interface": reflect.ValueOf([]interface{}{int64(1)}).Index(0).Type(),
"bool": reflect.TypeOf(true),
"string": reflect.TypeOf("a"),
"int": reflect.TypeOf(int(1)),
"int32": reflect.TypeOf(int32(1)),
"int64": reflect.TypeOf(int64(1)),
"uint": reflect.TypeOf(uint(1)),
"uint32": reflect.TypeOf(uint32(1)),
"uint64": reflect.TypeOf(uint64(1)),
"byte": reflect.TypeOf(byte(1)),
"rune": reflect.TypeOf('a'),
"float32": reflect.TypeOf(float32(1)),
"float64": reflect.TypeOf(float64(1)),
}
// ErrSymbolContainsDot symbol contains .
ErrSymbolContainsDot = errors.New("symbol contains '.'")
)
// NewEnv creates new global scope.
func NewEnv() *Env {
return &Env{
rwMutex: &sync.RWMutex{},
values: make(map[string]reflect.Value),
}
}
// NewEnv creates new child scope.
func (e *Env) NewEnv() *Env {
return &Env{
rwMutex: &sync.RWMutex{},
parent: e,
values: make(map[string]reflect.Value),
}
}
// NewModule creates new child scope and define it as a symbol.
// This is a shortcut for calling e.NewEnv then Define that new Env.
func (e *Env) NewModule(symbol string) (*Env, error) {
module := &Env{
rwMutex: &sync.RWMutex{},
parent: e,
values: make(map[string]reflect.Value),
}
return module, e.Define(symbol, module)
}
// SetExternalLookup sets an external lookup
func (e *Env) SetExternalLookup(externalLookup ExternalLookup) {
e.externalLookup = externalLookup
}
// String returns string of values and types in current scope.
func (e *Env) String() string {
var buffer bytes.Buffer
e.rwMutex.RLock()
if e.parent == nil {
buffer.WriteString("No parent\n")
} else {
buffer.WriteString("Has parent\n")
}
for symbol, value := range e.values {
buffer.WriteString(fmt.Sprintf("%v = %#v\n", symbol, value))
}
for symbol, aType := range e.types {
buffer.WriteString(fmt.Sprintf("%v = %v\n", symbol, aType))
}
e.rwMutex.RUnlock()
return buffer.String()
}
// GetEnvFromPath returns Env from path
func (e *Env) GetEnvFromPath(path []string) (*Env, error) {
if len(path) < 1 {
return e, nil
}
var value reflect.Value
var ok bool
for {
// find starting env
value, ok = e.values[path[0]]
if ok {
e, ok = value.Interface().(*Env)
if ok {
break
}
}
if e.parent == nil {
return nil, fmt.Errorf("no namespace called: %v", path[0])
}
e = e.parent
}
for i := 1; i < len(path); i++ {
// find child env
value, ok = e.values[path[i]]
if ok {
e, ok = value.Interface().(*Env)
if ok {
continue
}
}
return nil, fmt.Errorf("no namespace called: %v", path[i])
}
return e, nil
}
// Copy the Env for current scope
func (e *Env) Copy() *Env {
e.rwMutex.RLock()
copy := Env{
rwMutex: &sync.RWMutex{},
parent: e.parent,
values: make(map[string]reflect.Value, len(e.values)),
externalLookup: e.externalLookup,
}
for name, value := range e.values {
copy.values[name] = value
}
if e.types != nil {
copy.types = make(map[string]reflect.Type, len(e.types))
for name, t := range e.types {
copy.types[name] = t
}
}
e.rwMutex.RUnlock()
return &copy
}
// DeepCopy the Env for current scope and parent scopes.
// Note that each scope is a consistent snapshot but not the whole.
func (e *Env) DeepCopy() *Env {
e = e.Copy()
if e.parent != nil {
e.parent = e.parent.DeepCopy()
}
return e
}

View file

@ -0,0 +1,235 @@
package env
import (
"fmt"
"reflect"
"strings"
"testing"
)
type TestExternalLookup struct {
values map[string]reflect.Value
types map[string]reflect.Type
}
func NewTestExternalLookup() *TestExternalLookup {
return &TestExternalLookup{
values: make(map[string]reflect.Value),
types: make(map[string]reflect.Type),
}
}
func (testExternalLookup *TestExternalLookup) SetValue(symbol string, value interface{}) error {
if strings.Contains(symbol, ".") {
return ErrSymbolContainsDot
}
if value == nil {
testExternalLookup.values[symbol] = NilValue
} else {
testExternalLookup.values[symbol] = reflect.ValueOf(value)
}
return nil
}
func (testExternalLookup *TestExternalLookup) Get(symbol string) (reflect.Value, error) {
if value, ok := testExternalLookup.values[symbol]; ok {
return value, nil
}
return NilValue, fmt.Errorf("undefined symbol '%s'", symbol)
}
func (testExternalLookup *TestExternalLookup) DefineType(symbol string, aType interface{}) error {
if strings.Contains(symbol, ".") {
return ErrSymbolContainsDot
}
var reflectType reflect.Type
if aType == nil {
reflectType = NilType
} else {
var ok bool
reflectType, ok = reflectType.(reflect.Type)
if !ok {
reflectType = reflect.TypeOf(aType)
}
}
testExternalLookup.types[symbol] = reflectType
return nil
}
func (testExternalLookup *TestExternalLookup) Type(symbol string) (reflect.Type, error) {
if value, ok := testExternalLookup.types[symbol]; ok {
return value, nil
}
return NilType, fmt.Errorf("undefined symbol '%s'", symbol)
}
func TestExternalLookupValueAndGet(t *testing.T) {
var err error
var value interface{}
tests := []struct {
testInfo string
varName string
varDefineValue interface{}
varGetValue interface{}
varKind reflect.Kind
defineError error
getError error
}{
{testInfo: "nil", varName: "a", varDefineValue: nil, varGetValue: nil, varKind: reflect.Interface},
{testInfo: "bool", varName: "a", varDefineValue: true, varGetValue: true, varKind: reflect.Bool},
{testInfo: "int16", varName: "a", varDefineValue: int16(1), varGetValue: int16(1), varKind: reflect.Int16},
{testInfo: "int32", varName: "a", varDefineValue: int32(1), varGetValue: int32(1), varKind: reflect.Int32},
{testInfo: "int64", varName: "a", varDefineValue: int64(1), varGetValue: int64(1), varKind: reflect.Int64},
{testInfo: "uint32", varName: "a", varDefineValue: uint32(1), varGetValue: uint32(1), varKind: reflect.Uint32},
{testInfo: "uint64", varName: "a", varDefineValue: uint64(1), varGetValue: uint64(1), varKind: reflect.Uint64},
{testInfo: "float32", varName: "a", varDefineValue: float32(1), varGetValue: float32(1), varKind: reflect.Float32},
{testInfo: "float64", varName: "a", varDefineValue: float64(1), varGetValue: float64(1), varKind: reflect.Float64},
{testInfo: "string", varName: "a", varDefineValue: "a", varGetValue: "a", varKind: reflect.String},
{testInfo: "string with dot", varName: "a.a", varDefineValue: "a", varGetValue: nil, varKind: reflect.String, defineError: ErrSymbolContainsDot, getError: fmt.Errorf("undefined symbol 'a.a'")},
{testInfo: "string with quotes", varName: "a", varDefineValue: `"a"`, varGetValue: `"a"`, varKind: reflect.String},
}
// ExternalLookup set And get
for _, test := range tests {
testExternalLookup := NewTestExternalLookup()
env := NewEnv()
env.SetExternalLookup(testExternalLookup)
err = testExternalLookup.SetValue(test.varName, test.varDefineValue)
if err != nil && test.defineError != nil {
if err.Error() != test.defineError.Error() {
t.Errorf("TestExternalLookupValueAndGet %v - SetValue error - received: %v - expected: %v", test.testInfo, err, test.defineError)
continue
}
} else if err != test.defineError {
t.Errorf("TestExternalLookupValueAndGet %v - SetValue error - received: %v - expected: %v", test.testInfo, err, test.defineError)
continue
}
value, err = env.Get(test.varName)
if err != nil && test.getError != nil {
if err.Error() != test.getError.Error() {
t.Errorf("TestExternalLookupValueAndGet %v - Get error - received: %v - expected: %v", test.testInfo, err, test.getError)
continue
}
} else if err != test.getError {
t.Errorf("TestExternalLookupValueAndGet %v - Get error - received: %v - expected: %v", test.testInfo, err, test.getError)
continue
}
if value != test.varGetValue {
t.Errorf("TestExternalLookupValueAndGet %v - value check - received %#v expected: %#v", test.testInfo, value, test.varGetValue)
}
}
}
func TestExternalLookupTypeAndGet(t *testing.T) {
var err error
var valueType reflect.Type
tests := []struct {
testInfo string
varName string
varDefineValue interface{}
defineError error
typeError error
}{
{testInfo: "nil", varName: "a", varDefineValue: nil},
{testInfo: "bool", varName: "a", varDefineValue: true},
{testInfo: "int16", varName: "a", varDefineValue: int16(1)},
{testInfo: "int32", varName: "a", varDefineValue: int32(1)},
{testInfo: "int64", varName: "a", varDefineValue: int64(1)},
{testInfo: "uint32", varName: "a", varDefineValue: uint32(1)},
{testInfo: "uint64", varName: "a", varDefineValue: uint64(1)},
{testInfo: "float32", varName: "a", varDefineValue: float32(1)},
{testInfo: "float64", varName: "a", varDefineValue: float64(1)},
{testInfo: "string", varName: "a", varDefineValue: "a"},
{testInfo: "string with dot", varName: "a.a", varDefineValue: nil, defineError: ErrSymbolContainsDot, typeError: fmt.Errorf("undefined type 'a.a'")},
}
// DefineType
for _, test := range tests {
testExternalLookup := NewTestExternalLookup()
env := NewEnv()
env.SetExternalLookup(testExternalLookup)
err = testExternalLookup.DefineType(test.varName, test.varDefineValue)
if err != nil && test.defineError != nil {
if err.Error() != test.defineError.Error() {
t.Errorf("TestExternalLookupTypeAndGet %v - DefineType error - received: %v - expected: %v", test.testInfo, err, test.defineError)
continue
}
} else if err != test.defineError {
t.Errorf("TestExternalLookupTypeAndGet %v - DefineType error - received: %v - expected: %v", test.testInfo, err, test.defineError)
continue
}
valueType, err = env.Type(test.varName)
if err != nil && test.typeError != nil {
if err.Error() != test.typeError.Error() {
t.Errorf("TestExternalLookupTypeAndGet %v - Type error - received: %v - expected: %v", test.testInfo, err, test.typeError)
continue
}
} else if err != test.typeError {
t.Errorf("TestExternalLookupTypeAndGet %v - Type error - received: %v - expected: %v", test.testInfo, err, test.typeError)
continue
}
if valueType == nil || test.varDefineValue == nil {
if valueType != reflect.TypeOf(test.varDefineValue) {
t.Errorf("TestExternalLookupTypeAndGet %v - Type check - received: %v - expected: %v", test.testInfo, valueType, reflect.TypeOf(test.varDefineValue))
}
} else if valueType.String() != reflect.TypeOf(test.varDefineValue).String() {
t.Errorf("TestExternalLookupTypeAndGet %v - Type check - received: %v - expected: %v", test.testInfo, valueType, reflect.TypeOf(test.varDefineValue))
}
}
}
func TestExternalLookupAddr(t *testing.T) {
var err error
tests := []struct {
testInfo string
varName string
varDefineValue interface{}
defineError error
addrError error
}{
{testInfo: "nil", varName: "a", varDefineValue: nil, addrError: nil},
{testInfo: "bool", varName: "a", varDefineValue: true, addrError: fmt.Errorf("unaddressable")},
{testInfo: "int64", varName: "a", varDefineValue: int64(1), addrError: fmt.Errorf("unaddressable")},
{testInfo: "float64", varName: "a", varDefineValue: float64(1), addrError: fmt.Errorf("unaddressable")},
{testInfo: "string", varName: "a", varDefineValue: "a", addrError: fmt.Errorf("unaddressable")},
}
for _, test := range tests {
envParent := NewEnv()
testExternalLookup := NewTestExternalLookup()
envParent.SetExternalLookup(testExternalLookup)
envChild := envParent.NewEnv()
err = testExternalLookup.SetValue(test.varName, test.varDefineValue)
if err != nil && test.defineError != nil {
if err.Error() != test.defineError.Error() {
t.Errorf("TestExternalLookupAddr %v - SetValue error - received: %v - expected: %v", test.testInfo, err, test.defineError)
continue
}
} else if err != test.defineError {
t.Errorf("TestExternalLookupAddr %v - SetValue error - received: %v - expected: %v", test.testInfo, err, test.defineError)
continue
}
_, err = envChild.Addr(test.varName)
if err != nil && test.addrError != nil {
if err.Error() != test.addrError.Error() {
t.Errorf("TestExternalLookupAddr %v - Addr error - received: %v - expected: %v", test.testInfo, err, test.addrError)
continue
}
} else if err != test.addrError {
t.Errorf("TestExternalLookupAddr %v - Addr error - received: %v - expected: %v", test.testInfo, err, test.addrError)
continue
}
}
}

83
src/tool/run/env/envTypes.go vendored Normal file
View file

@ -0,0 +1,83 @@
package env
import (
"fmt"
"reflect"
"strings"
)
// DefineType defines type in current scope.
func (e *Env) DefineType(symbol string, aType interface{}) error {
var reflectType reflect.Type
if aType == nil {
reflectType = NilType
} else {
var ok bool
reflectType, ok = aType.(reflect.Type)
if !ok {
reflectType = reflect.TypeOf(aType)
}
}
return e.DefineReflectType(symbol, reflectType)
}
// DefineReflectType defines type in current scope.
func (e *Env) DefineReflectType(symbol string, reflectType reflect.Type) error {
if strings.Contains(symbol, ".") {
return ErrSymbolContainsDot
}
e.rwMutex.Lock()
if e.types == nil {
e.types = make(map[string]reflect.Type)
}
e.types[symbol] = reflectType
e.rwMutex.Unlock()
return nil
}
// DefineGlobalType defines type in global scope.
func (e *Env) DefineGlobalType(symbol string, aType interface{}) error {
for e.parent != nil {
e = e.parent
}
return e.DefineType(symbol, aType)
}
// DefineGlobalReflectType defines type in global scope.
func (e *Env) DefineGlobalReflectType(symbol string, reflectType reflect.Type) error {
for e.parent != nil {
e = e.parent
}
return e.DefineReflectType(symbol, reflectType)
}
// Type returns reflect type from the scope where symbol is frist found.
func (e *Env) Type(symbol string) (reflect.Type, error) {
e.rwMutex.RLock()
reflectType, ok := e.types[symbol]
e.rwMutex.RUnlock()
if ok {
return reflectType, nil
}
if e.externalLookup != nil {
var err error
reflectType, err = e.externalLookup.Type(symbol)
if err == nil {
return reflectType, nil
}
}
if e.parent == nil {
reflectType, ok = basicTypes[symbol]
if ok {
return reflectType, nil
}
return NilType, fmt.Errorf("undefined type '%s'", symbol)
}
return e.parent.Type(symbol)
}

313
src/tool/run/env/envTypes_test.go vendored Normal file
View file

@ -0,0 +1,313 @@
package env
import (
"fmt"
"reflect"
"testing"
)
func TestBasicType(t *testing.T) {
env := NewEnv()
aType, err := env.Type("string")
if err != nil {
t.Fatalf("Type error - %v", err)
}
if aType != reflect.TypeOf("a") {
t.Errorf("Type - received: %v - expected: %v", aType, reflect.TypeOf("a"))
}
aType, err = env.Type("int64")
if err != nil {
t.Fatal("Type error:", err)
}
if aType != reflect.TypeOf(int64(1)) {
t.Errorf("Type - received: %v - expected: %v", aType, reflect.TypeOf(int64(1)))
}
}
func TestDefineType(t *testing.T) {
var err error
var valueType reflect.Type
tests := []struct {
testInfo string
varName string
varDefineValue interface{}
defineError error
typeError error
}{
{testInfo: "nil", varName: "a", varDefineValue: nil},
{testInfo: "bool", varName: "a", varDefineValue: true},
{testInfo: "int16", varName: "a", varDefineValue: int16(1)},
{testInfo: "int32", varName: "a", varDefineValue: int32(1)},
{testInfo: "int64", varName: "a", varDefineValue: int64(1)},
{testInfo: "uint32", varName: "a", varDefineValue: uint32(1)},
{testInfo: "uint64", varName: "a", varDefineValue: uint64(1)},
{testInfo: "float32", varName: "a", varDefineValue: float32(1)},
{testInfo: "float64", varName: "a", varDefineValue: float64(1)},
{testInfo: "string", varName: "a", varDefineValue: "a"},
{testInfo: "string with dot", varName: "a.a", varDefineValue: nil, defineError: ErrSymbolContainsDot, typeError: fmt.Errorf("undefined type 'a.a'")},
}
// DefineType
for _, test := range tests {
env := NewEnv()
err = env.DefineType(test.varName, test.varDefineValue)
if err != nil && test.defineError != nil {
if err.Error() != test.defineError.Error() {
t.Errorf("DefineType %v - Define error - received: %v - expected: %v", test.testInfo, err, test.defineError)
continue
}
} else if err != test.defineError {
t.Errorf("DefineType %v - Define error - received: %v - expected: %v", test.testInfo, err, test.defineError)
continue
}
valueType, err = env.Type(test.varName)
if err != nil && test.typeError != nil {
if err.Error() != test.typeError.Error() {
t.Errorf("DefineType %v - Type error - received: %v - expected: %v", test.testInfo, err, test.typeError)
continue
}
} else if err != test.typeError {
t.Errorf("DefineType %v - Type error - received: %v - expected: %v", test.testInfo, err, test.typeError)
continue
}
if valueType == nil || test.varDefineValue == nil {
if valueType != reflect.TypeOf(test.varDefineValue) {
t.Errorf("DefineType %v - Type check - received: %v - expected: %v", test.testInfo, valueType, reflect.TypeOf(test.varDefineValue))
}
} else if valueType.String() != reflect.TypeOf(test.varDefineValue).String() {
t.Errorf("DefineType %v - Type check - received: %v - expected: %v", test.testInfo, valueType, reflect.TypeOf(test.varDefineValue))
}
}
// DefineType NewEnv
for _, test := range tests {
envParent := NewEnv()
envChild := envParent.NewEnv()
err = envParent.DefineType(test.varName, test.varDefineValue)
if err != nil && test.defineError != nil {
if err.Error() != test.defineError.Error() {
t.Errorf("DefineType NewEnv %v - Define error - received: %v - expected: %v", test.testInfo, err, test.defineError)
continue
}
} else if err != test.defineError {
t.Errorf("DefineType NewEnv %v - Define error - received: %v - expected: %v", test.testInfo, err, test.defineError)
continue
}
valueType, err = envChild.Type(test.varName)
if err != nil && test.typeError != nil {
if err.Error() != test.typeError.Error() {
t.Errorf("DefineType NewEnv %v - Type error - received: %v - expected: %v", test.testInfo, err, test.typeError)
continue
}
} else if err != test.typeError {
t.Errorf("DefineType NewEnv %v - Type error - received: %v - expected: %v", test.testInfo, err, test.typeError)
continue
}
if valueType == nil || test.varDefineValue == nil {
if valueType != reflect.TypeOf(test.varDefineValue) {
t.Errorf("DefineType NewEnv %v - Type check - received: %v - expected: %v", test.testInfo, valueType, reflect.TypeOf(test.varDefineValue))
}
} else if valueType.String() != reflect.TypeOf(test.varDefineValue).String() {
t.Errorf("DefineType NewEnv %v - Type check - received: %v - expected: %v", test.testInfo, valueType, reflect.TypeOf(test.varDefineValue))
}
}
// DefineType NewModule
for _, test := range tests {
envParent := NewEnv()
envChild, err := envParent.NewModule("envChild")
if err != nil {
t.Fatalf("DefineType NewModule %v - NewModule error - received: %v - expected: %v", test.testInfo, err, nil)
}
err = envParent.DefineType(test.varName, test.varDefineValue)
if err != nil && test.defineError != nil {
if err.Error() != test.defineError.Error() {
t.Errorf("DefineType NewModule %v - Define error - received: %v - expected: %v", test.testInfo, err, test.defineError)
continue
}
} else if err != test.defineError {
t.Errorf("DefineType NewModule %v - Define error - received: %v - expected: %v", test.testInfo, err, test.defineError)
continue
}
valueType, err = envChild.Type(test.varName)
if err != nil && test.typeError != nil {
if err.Error() != test.typeError.Error() {
t.Errorf("DefineType NewModule %v - Type error - received: %v - expected: %v", test.testInfo, err, test.typeError)
continue
}
} else if err != test.typeError {
t.Errorf("DefineType NewModule %v - Type error - received: %v - expected: %v", test.testInfo, err, test.typeError)
continue
}
if valueType == nil || test.varDefineValue == nil {
if valueType != reflect.TypeOf(test.varDefineValue) {
t.Errorf("DefineType NewModule %v - Type check - received: %v - expected: %v", test.testInfo, valueType, reflect.TypeOf(test.varDefineValue))
}
} else if valueType.String() != reflect.TypeOf(test.varDefineValue).String() {
t.Errorf("DefineType NewModule %v - Type check - received: %v - expected: %v", test.testInfo, valueType, reflect.TypeOf(test.varDefineValue))
}
}
// DefineGlobalType
for _, test := range tests {
envParent := NewEnv()
envChild := envParent.NewEnv()
err = envChild.DefineGlobalType(test.varName, test.varDefineValue)
if err != nil && test.defineError != nil {
if err.Error() != test.defineError.Error() {
t.Errorf("DefineGlobalType %v - Define error - received: %v - expected: %v", test.testInfo, err, test.defineError)
continue
}
} else if err != test.defineError {
t.Errorf("DefineGlobalType %v - Define error - received: %v - expected: %v", test.testInfo, err, test.defineError)
continue
}
valueType, err = envParent.Type(test.varName)
if err != nil && test.typeError != nil {
if err.Error() != test.typeError.Error() {
t.Errorf("DefineGlobalType %v - Type error - received: %v - expected: %v", test.testInfo, err, test.typeError)
continue
}
} else if err != test.typeError {
t.Errorf("DefineGlobalType %v - Type error - received: %v - expected: %v", test.testInfo, err, test.typeError)
continue
}
if valueType == nil || test.varDefineValue == nil {
if valueType != reflect.TypeOf(test.varDefineValue) {
t.Errorf("DefineGlobalType %v - Type check - received: %v - expected: %v", test.testInfo, valueType, reflect.TypeOf(test.varDefineValue))
}
} else if valueType.String() != reflect.TypeOf(test.varDefineValue).String() {
t.Errorf("DefineGlobalType %v - Type check - received: %v - expected: %v", test.testInfo, valueType, reflect.TypeOf(test.varDefineValue))
}
valueType, err = envChild.Type(test.varName)
if err != nil && test.typeError != nil {
if err.Error() != test.typeError.Error() {
t.Errorf("DefineGlobalType %v - Type error - received: %v - expected: %v", test.testInfo, err, test.typeError)
continue
}
} else if err != test.typeError {
t.Errorf("DefineGlobalType %v - Type error - received: %v - expected: %v", test.testInfo, err, test.typeError)
continue
}
if valueType == nil || test.varDefineValue == nil {
if valueType != reflect.TypeOf(test.varDefineValue) {
t.Errorf("DefineGlobalType %v - Type check - received: %v - expected: %v", test.testInfo, valueType, reflect.TypeOf(test.varDefineValue))
}
} else if valueType.String() != reflect.TypeOf(test.varDefineValue).String() {
t.Errorf("DefineGlobalType %v - Type check - received: %v - expected: %v", test.testInfo, valueType, reflect.TypeOf(test.varDefineValue))
}
}
// DefineGlobalReflectType
for _, test := range tests {
envParent := NewEnv()
envChild := envParent.NewEnv()
err = envChild.DefineGlobalReflectType(test.varName, reflect.TypeOf(test.varDefineValue))
if err != nil && test.defineError != nil {
if err.Error() != test.defineError.Error() {
t.Errorf("DefineGlobalReflectType %v - Define error - received: %v - expected: %v", test.testInfo, err, test.defineError)
continue
}
} else if err != test.defineError {
t.Errorf("DefineGlobalReflectType %v - Define error - received: %v - expected: %v", test.testInfo, err, test.defineError)
continue
}
valueType, err = envParent.Type(test.varName)
if err != nil && test.typeError != nil {
if err.Error() != test.typeError.Error() {
t.Errorf("DefineGlobalReflectType %v - Type error - received: %v - expected: %v", test.testInfo, err, test.typeError)
continue
}
} else if err != test.typeError {
t.Errorf("DefineGlobalReflectType %v - Type error - received: %v - expected: %v", test.testInfo, err, test.typeError)
continue
}
if valueType == nil || test.varDefineValue == nil {
if valueType != reflect.TypeOf(test.varDefineValue) {
t.Errorf("DefineGlobalReflectType %v - Type check - received: %v - expected: %v", test.testInfo, valueType, reflect.TypeOf(test.varDefineValue))
}
} else if valueType.String() != reflect.TypeOf(test.varDefineValue).String() {
t.Errorf("DefineGlobalReflectType %v - Type check - received: %v - expected: %v", test.testInfo, valueType, reflect.TypeOf(test.varDefineValue))
}
valueType, err = envChild.Type(test.varName)
if err != nil && test.typeError != nil {
if err.Error() != test.typeError.Error() {
t.Errorf("DefineGlobalReflectType %v - Type error - received: %v - expected: %v", test.testInfo, err, test.typeError)
continue
}
} else if err != test.typeError {
t.Errorf("DefineGlobalReflectType %v - Type error - received: %v - expected: %v", test.testInfo, err, test.typeError)
continue
}
if valueType == nil || test.varDefineValue == nil {
if valueType != reflect.TypeOf(test.varDefineValue) {
t.Errorf("DefineGlobalReflectType %v - Type check - received: %v - expected: %v", test.testInfo, valueType, reflect.TypeOf(test.varDefineValue))
}
} else if valueType.String() != reflect.TypeOf(test.varDefineValue).String() {
t.Errorf("DefineGlobalReflectType %v - Type check - received: %v - expected: %v", test.testInfo, valueType, reflect.TypeOf(test.varDefineValue))
}
}
}
func TestDefineTypeFail(t *testing.T) {
var err error
tests := []struct {
testInfo string
varName string
varDefineValue interface{}
defineError error
typeError error
}{
{testInfo: "nil", varName: "a", varDefineValue: nil, typeError: fmt.Errorf("undefined type 'a'")},
{testInfo: "bool", varName: "a", varDefineValue: true, typeError: fmt.Errorf("undefined type 'a'")},
{testInfo: "int16", varName: "a", varDefineValue: int16(1), typeError: fmt.Errorf("undefined type 'a'")},
{testInfo: "int32", varName: "a", varDefineValue: int32(1), typeError: fmt.Errorf("undefined type 'a'")},
{testInfo: "int64", varName: "a", varDefineValue: int64(1), typeError: fmt.Errorf("undefined type 'a'")},
{testInfo: "uint32", varName: "a", varDefineValue: uint32(1), typeError: fmt.Errorf("undefined type 'a'")},
{testInfo: "uint64", varName: "a", varDefineValue: uint64(1), typeError: fmt.Errorf("undefined type 'a'")},
{testInfo: "float32", varName: "a", varDefineValue: float32(1), typeError: fmt.Errorf("undefined type 'a'")},
{testInfo: "float64", varName: "a", varDefineValue: float64(1), typeError: fmt.Errorf("undefined type 'a'")},
{testInfo: "string", varName: "a", varDefineValue: "a", typeError: fmt.Errorf("undefined type 'a'")},
}
// DefineTypeFail
for _, test := range tests {
envParent := NewEnv()
envChild := envParent.NewEnv()
err = envChild.DefineType(test.varName, test.varDefineValue)
if err != nil && test.defineError != nil {
if err.Error() != test.defineError.Error() {
t.Errorf("TestDefineTypeFail %v - Define error - received: %v - expected: %v", test.testInfo, err, test.defineError)
continue
}
} else if err != test.defineError {
t.Errorf("TestDefineTypeFail %v - Define error - received: %v - expected: %v", test.testInfo, err, test.defineError)
continue
}
_, err = envParent.Type(test.varName)
if err != nil && test.typeError != nil {
if err.Error() != test.typeError.Error() {
t.Errorf("TestDefineTypeFail %v - Type error - received: %v - expected: %v", test.testInfo, err, test.typeError)
continue
}
} else if err != test.typeError {
t.Errorf("TestDefineTypeFail %v - Type error - received: %v - expected: %v", test.testInfo, err, test.typeError)
}
}
}

161
src/tool/run/env/envValues.go vendored Normal file
View file

@ -0,0 +1,161 @@
package env
import (
"fmt"
"reflect"
"strings"
)
// define
// Define defines/sets interface value to symbol in current scope.
func (e *Env) Define(symbol string, value interface{}) error {
if value == nil {
return e.DefineValue(symbol, NilValue)
}
return e.DefineValue(symbol, reflect.ValueOf(value))
}
// DefineValue defines/sets reflect value to symbol in current scope.
func (e *Env) DefineValue(symbol string, value reflect.Value) error {
if strings.Contains(symbol, ".") {
return ErrSymbolContainsDot
}
e.rwMutex.Lock()
e.values[symbol] = value
e.rwMutex.Unlock()
return nil
}
// DefineGlobal defines/sets interface value to symbol in global scope.
func (e *Env) DefineGlobal(symbol string, value interface{}) error {
for e.parent != nil {
e = e.parent
}
return e.Define(symbol, value)
}
// DefineGlobalValue defines/sets reflect value to symbol in global scope.
func (e *Env) DefineGlobalValue(symbol string, value reflect.Value) error {
for e.parent != nil {
e = e.parent
}
return e.DefineValue(symbol, value)
}
// set
// Set interface value to the scope where symbol is frist found.
func (e *Env) Set(symbol string, value interface{}) error {
if value == nil {
return e.SetValue(symbol, NilValue)
}
return e.SetValue(symbol, reflect.ValueOf(value))
}
// SetValue reflect value to the scope where symbol is frist found.
func (e *Env) SetValue(symbol string, value reflect.Value) error {
e.rwMutex.RLock()
_, ok := e.values[symbol]
e.rwMutex.RUnlock()
if ok {
e.rwMutex.Lock()
e.values[symbol] = value
e.rwMutex.Unlock()
return nil
}
if e.parent == nil {
return fmt.Errorf("undefined symbol '%s'", symbol)
}
return e.parent.SetValue(symbol, value)
}
// get
// Get returns interface value from the scope where symbol is frist found.
func (e *Env) Get(symbol string) (interface{}, error) {
rv, err := e.GetValue(symbol)
return rv.Interface(), err
}
// GetValue returns reflect value from the scope where symbol is frist found.
func (e *Env) GetValue(symbol string) (reflect.Value, error) {
e.rwMutex.RLock()
value, ok := e.values[symbol]
e.rwMutex.RUnlock()
if ok {
return value, nil
}
if e.externalLookup != nil {
var err error
value, err = e.externalLookup.Get(symbol)
if err == nil {
return value, nil
}
}
if e.parent == nil {
return NilValue, fmt.Errorf("undefined symbol '%s'", symbol)
}
return e.parent.GetValue(symbol)
}
// delete
// Delete deletes symbol in current scope.
func (e *Env) Delete(symbol string) {
e.rwMutex.Lock()
delete(e.values, symbol)
e.rwMutex.Unlock()
}
// DeleteGlobal deletes the first matching symbol found in current or parent scope.
func (e *Env) DeleteGlobal(symbol string) {
if e.parent == nil {
e.Delete(symbol)
return
}
e.rwMutex.RLock()
_, ok := e.values[symbol]
e.rwMutex.RUnlock()
if ok {
e.Delete(symbol)
return
}
e.parent.DeleteGlobal(symbol)
}
// Addr
// Addr returns reflect.Addr of value for first matching symbol found in current or parent scope.
func (e *Env) Addr(symbol string) (reflect.Value, error) {
e.rwMutex.RLock()
defer e.rwMutex.RUnlock()
if v, ok := e.values[symbol]; ok {
if v.CanAddr() {
return v.Addr(), nil
}
return NilValue, fmt.Errorf("unaddressable")
}
if e.externalLookup != nil {
v, err := e.externalLookup.Get(symbol)
if err == nil {
if v.CanAddr() {
return v.Addr(), nil
}
return NilValue, fmt.Errorf("unaddressable")
}
}
if e.parent == nil {
return NilValue, fmt.Errorf("undefined symbol '%s'", symbol)
}
return e.parent.Addr(symbol)
}

787
src/tool/run/env/envValues_test.go vendored Normal file
View file

@ -0,0 +1,787 @@
package env
import (
"fmt"
"reflect"
"sync"
"testing"
)
func TestSetError(t *testing.T) {
envParent := NewEnv()
envChild := envParent.NewEnv()
err := envChild.Set("a", "a")
if err == nil {
t.Errorf("Set error - received: %v - expected: %v", err, fmt.Errorf("undefined symbol 'a'"))
} else if err.Error() != "undefined symbol 'a'" {
t.Errorf("Set error - received: %v - expected: %v", err, fmt.Errorf("undefined symbol 'a'"))
}
}
func TestAddrError(t *testing.T) {
envParent := NewEnv()
envChild := envParent.NewEnv()
_, err := envChild.Addr("a")
if err == nil {
t.Errorf("Addr error - received: %v - expected: %v", err, fmt.Errorf("undefined symbol 'a'"))
} else if err.Error() != "undefined symbol 'a'" {
t.Errorf("Addr error - received: %v - expected: %v", err, fmt.Errorf("undefined symbol 'a'"))
}
}
func TestDefineGlobalValue(t *testing.T) {
envParent := NewEnv()
envChild := envParent.NewEnv()
err := envChild.DefineGlobalValue("a", reflect.ValueOf("a"))
if err != nil {
t.Fatal("DefineGlobalValue error:", err)
}
var value interface{}
value, err = envParent.Get("a")
if err != nil {
t.Fatal("Get error:", err)
}
v, ok := value.(string)
if !ok {
t.Fatalf("value - received: %T - expected: %T", value, "a")
}
if v != "a" {
t.Fatalf("value - received: %v - expected: %v", v, "a")
}
}
func TestDefineAndGet(t *testing.T) {
var err error
var value interface{}
tests := []struct {
testInfo string
varName string
varDefineValue interface{}
varGetValue interface{}
varKind reflect.Kind
defineError error
getError error
}{
{testInfo: "nil", varName: "a", varDefineValue: reflect.Value{}, varGetValue: reflect.Value{}, varKind: reflect.Invalid},
{testInfo: "nil", varName: "a", varDefineValue: nil, varGetValue: nil, varKind: reflect.Interface},
{testInfo: "bool", varName: "a", varDefineValue: true, varGetValue: true, varKind: reflect.Bool},
{testInfo: "int16", varName: "a", varDefineValue: int16(1), varGetValue: int16(1), varKind: reflect.Int16},
{testInfo: "int32", varName: "a", varDefineValue: int32(1), varGetValue: int32(1), varKind: reflect.Int32},
{testInfo: "int64", varName: "a", varDefineValue: int64(1), varGetValue: int64(1), varKind: reflect.Int64},
{testInfo: "uint32", varName: "a", varDefineValue: uint32(1), varGetValue: uint32(1), varKind: reflect.Uint32},
{testInfo: "uint64", varName: "a", varDefineValue: uint64(1), varGetValue: uint64(1), varKind: reflect.Uint64},
{testInfo: "float32", varName: "a", varDefineValue: float32(1), varGetValue: float32(1), varKind: reflect.Float32},
{testInfo: "float64", varName: "a", varDefineValue: float64(1), varGetValue: float64(1), varKind: reflect.Float64},
{testInfo: "string", varName: "a", varDefineValue: "a", varGetValue: "a", varKind: reflect.String},
{testInfo: "string with dot", varName: "a.a", varDefineValue: "a", varGetValue: nil, varKind: reflect.Interface, defineError: ErrSymbolContainsDot, getError: fmt.Errorf("undefined symbol 'a.a'")},
{testInfo: "string with quotes", varName: "a", varDefineValue: `"a"`, varGetValue: `"a"`, varKind: reflect.String},
}
// DefineAndGet
for _, test := range tests {
env := NewEnv()
err = env.Define(test.varName, test.varDefineValue)
if err != nil && test.defineError != nil {
if err.Error() != test.defineError.Error() {
t.Errorf("DefineAndGet %v - Define error - received: %v - expected: %v", test.testInfo, err, test.defineError)
continue
}
} else if err != test.defineError {
t.Errorf("DefineAndGet %v - Define error - received: %v - expected: %v", test.testInfo, err, test.defineError)
continue
}
value, err = env.Get(test.varName)
if err != nil && test.getError != nil {
if err.Error() != test.getError.Error() {
t.Errorf("DefineAndGet %v - Get error - received: %v - expected: %v", test.testInfo, err, test.getError)
continue
}
} else if err != test.getError {
t.Errorf("DefineAndGet %v - Get error - received: %v - expected: %v", test.testInfo, err, test.getError)
continue
}
if value != test.varGetValue {
t.Errorf("DefineAndGet %v - value check - received %#v expected: %#v", test.testInfo, value, test.varGetValue)
}
}
// DefineAndGet NewEnv
for _, test := range tests {
envParent := NewEnv()
envChild := envParent.NewEnv()
err = envParent.Define(test.varName, test.varDefineValue)
if err != nil && test.defineError != nil {
if err.Error() != test.defineError.Error() {
t.Errorf("DefineAndGet NewEnv %v - Define error - received: %v - expected: %v", test.testInfo, err, test.defineError)
continue
}
} else if err != test.defineError {
t.Errorf("DefineAndGet NewEnv %v - Define error - received: %v - expected: %v", test.testInfo, err, test.defineError)
continue
}
value, err = envChild.Get(test.varName)
if err != nil && test.getError != nil {
if err.Error() != test.getError.Error() {
t.Errorf("DefineAndGet NewEnv %v - Get error - received: %v - expected: %v", test.testInfo, err, test.getError)
continue
}
} else if err != test.getError {
t.Errorf("DefineAndGet NewEnv %v - Get error - received: %v - expected: %v", test.testInfo, err, test.getError)
continue
}
if value != test.varGetValue {
t.Errorf("DefineAndGet NewEnv %v - value check - received %#v expected: %#v", test.testInfo, value, test.varGetValue)
}
}
// DefineAndGet DefineGlobal
for _, test := range tests {
envParent := NewEnv()
envChild := envParent.NewEnv()
err = envChild.DefineGlobal(test.varName, test.varDefineValue)
if err != nil && test.defineError != nil {
if err.Error() != test.defineError.Error() {
t.Errorf("DefineAndGet DefineGlobal %v - Define error - received: %v - expected: %v", test.testInfo, err, test.defineError)
continue
}
} else if err != test.defineError {
t.Errorf("DefineAndGet DefineGlobal %v - Define error - received: %v - expected: %v", test.testInfo, err, test.defineError)
continue
}
value, err = envParent.Get(test.varName)
if err != nil && test.getError != nil {
if err.Error() != test.getError.Error() {
t.Errorf("DefineAndGet DefineGlobal %v - Get error - received: %v - expected: %v", test.testInfo, err, test.getError)
continue
}
} else if err != test.getError {
t.Errorf("DefineAndGet DefineGlobal %v - Get error - received: %v - expected: %v", test.testInfo, err, test.getError)
continue
}
if value != test.varGetValue {
t.Errorf("DefineAndGet DefineGlobal %v - value check - received %#v expected: %#v", test.testInfo, value, test.varGetValue)
}
}
}
func TestDefineModify(t *testing.T) {
var err error
var value interface{}
tests := []struct {
testInfo string
varName string
varDefineValue interface{}
varGetValue interface{}
varKind reflect.Kind
defineError error
getError error
}{
{testInfo: "nil", varName: "a", varDefineValue: nil, varGetValue: nil, varKind: reflect.Interface},
{testInfo: "bool", varName: "a", varDefineValue: true, varGetValue: true, varKind: reflect.Bool},
{testInfo: "int64", varName: "a", varDefineValue: int64(1), varGetValue: int64(1), varKind: reflect.Int64},
{testInfo: "float64", varName: "a", varDefineValue: float64(1), varGetValue: float64(1), varKind: reflect.Float64},
{testInfo: "string", varName: "a", varDefineValue: "a", varGetValue: "a", varKind: reflect.String},
}
changeTests := []struct {
varDefineValue interface{}
varGetValue interface{}
varKind reflect.Kind
defineError error
getError error
}{
{varDefineValue: nil, varGetValue: nil, varKind: reflect.Interface},
{varDefineValue: "a", varGetValue: "a", varKind: reflect.String},
{varDefineValue: int64(1), varGetValue: int64(1), varKind: reflect.Int64},
{varDefineValue: float64(1), varGetValue: float64(1), varKind: reflect.Float64},
{varDefineValue: true, varGetValue: true, varKind: reflect.Bool},
}
// DefineModify
for _, test := range tests {
env := NewEnv()
err = env.Define(test.varName, test.varDefineValue)
if err != nil && test.defineError != nil {
if err.Error() != test.defineError.Error() {
t.Errorf("DefineModify %v - Define error - received: %v - expected: %v", test.testInfo, err, test.defineError)
continue
}
} else if err != test.defineError {
t.Errorf("DefineModify %v - Define error - received: %v - expected: %v", test.testInfo, err, test.defineError)
continue
}
value, err = env.Get(test.varName)
if err != nil && test.getError != nil {
if err.Error() != test.getError.Error() {
t.Errorf("DefineModify %v - Get error - received: %v - expected: %v", test.testInfo, err, test.getError)
continue
}
} else if err != test.getError {
t.Errorf("DefineModify %v - Get error - received: %v - expected: %v", test.testInfo, err, test.getError)
continue
}
if value != test.varGetValue {
t.Errorf("DefineModify %v - value check - received %#v expected: %#v", test.testInfo, value, test.varGetValue)
}
// DefineModify changeTest
for _, changeTest := range changeTests {
err = env.Set(test.varName, changeTest.varDefineValue)
if err != nil && changeTest.defineError != nil {
if err.Error() != changeTest.defineError.Error() {
t.Errorf("DefineModify changeTest %v - Set error - received: %v - expected: %v", test.testInfo, err, changeTest.defineError)
continue
}
} else if err != changeTest.defineError {
t.Errorf("DefineModify changeTest %v - Set error - received: %v - expected: %v", test.testInfo, err, changeTest.defineError)
continue
}
value, err = env.Get(test.varName)
if err != nil && changeTest.getError != nil {
if err.Error() != changeTest.getError.Error() {
t.Errorf("DefineModify changeTest %v - Get error - received: %v - expected: %v", test.testInfo, err, changeTest.getError)
continue
}
} else if err != changeTest.getError {
t.Errorf("DefineModify changeTest %v - Get error - received: %v - expected: %v", test.testInfo, err, changeTest.getError)
continue
}
if value != changeTest.varGetValue {
t.Errorf("DefineModify changeTest %v - value check - received %#v expected: %#v", test.testInfo, value, changeTest.varGetValue)
}
}
}
// DefineModify envParent
for _, test := range tests {
envParent := NewEnv()
envChild := envParent.NewEnv()
err = envParent.Define(test.varName, test.varDefineValue)
if err != nil && test.defineError != nil {
if err.Error() != test.defineError.Error() {
t.Errorf("DefineModify envParent %v - Define error - received: %v - expected: %v", test.testInfo, err, test.defineError)
continue
}
} else if err != test.defineError {
t.Errorf("DefineModify envParent %v - Define error - received: %v - expected: %v", test.testInfo, err, test.defineError)
continue
}
value, err = envChild.Get(test.varName)
if err != nil && test.getError != nil {
if err.Error() != test.getError.Error() {
t.Errorf("DefineModify envParent %v - Get error - received: %v - expected: %v", test.testInfo, err, test.getError)
continue
}
} else if err != test.getError {
t.Errorf("DefineModify envParent %v - Get error - received: %v - expected: %v", test.testInfo, err, test.getError)
continue
}
if value != test.varGetValue {
t.Errorf("DefineModify envParent %v - value check - received %#v expected: %#v", test.testInfo, value, test.varGetValue)
}
for _, changeTest := range changeTests {
err = envParent.Set(test.varName, changeTest.varDefineValue)
if err != nil && changeTest.defineError != nil {
if err.Error() != changeTest.defineError.Error() {
t.Errorf("DefineModify envParent changeTest %v - Set error - received: %v - expected: %v", test.testInfo, err, changeTest.defineError)
continue
}
} else if err != changeTest.defineError {
t.Errorf("DefineModify envParent changeTest %v - Set error - received: %v - expected: %v", test.testInfo, err, changeTest.defineError)
continue
}
value, err = envChild.Get(test.varName)
if err != nil && changeTest.getError != nil {
if err.Error() != changeTest.getError.Error() {
t.Errorf("DefineModify envParent changeTest %v - Get error - received: %v - expected: %v", test.testInfo, err, changeTest.getError)
continue
}
} else if err != changeTest.getError {
t.Errorf("ChanDefineModify envParent changeTestgeTest %v - Get error - received: %v - expected: %v", test.testInfo, err, changeTest.getError)
continue
}
if value != changeTest.varGetValue {
t.Errorf("DefineModify envParent changeTest %v - value check - received %#v expected: %#v", test.testInfo, value, changeTest.varGetValue)
}
}
}
// DefineModify envChild
for _, test := range tests {
envParent := NewEnv()
envChild := envParent.NewEnv()
err = envParent.Define(test.varName, test.varDefineValue)
if err != nil && test.defineError != nil {
if err.Error() != test.defineError.Error() {
t.Errorf("DefineModify envChild %v - Define error - received: %v - expected: %v", test.testInfo, err, test.defineError)
continue
}
} else if err != test.defineError {
t.Errorf("DefineModify envChild %v - Define error - received: %v - expected: %v", test.testInfo, err, test.defineError)
continue
}
value, err = envChild.Get(test.varName)
if err != nil && test.getError != nil {
if err.Error() != test.getError.Error() {
t.Errorf("DefineModify envChild %v - Get error - received: %v - expected: %v", test.testInfo, err, test.getError)
continue
}
} else if err != test.getError {
t.Errorf("DefineModify envChild %v - Get error - received: %v - expected: %v", test.testInfo, err, test.getError)
continue
}
if value != test.varGetValue {
t.Errorf("DefineModify envChild %v - value check - received %#v expected: %#v", test.testInfo, value, test.varGetValue)
}
for _, changeTest := range changeTests {
err = envChild.Set(test.varName, changeTest.varDefineValue)
if err != nil && changeTest.defineError != nil {
if err.Error() != changeTest.defineError.Error() {
t.Errorf("DefineModify envChild changeTest %v - Set error - received: %v - expected: %v", test.testInfo, err, changeTest.defineError)
continue
}
} else if err != changeTest.defineError {
t.Errorf("DefineModify envChild changeTest %v - Set error - received: %v - expected: %v", test.testInfo, err, changeTest.defineError)
continue
}
value, err = envChild.Get(test.varName)
if err != nil && changeTest.getError != nil {
if err.Error() != changeTest.getError.Error() {
t.Errorf("DefineModify envChild changeTest %v - Get error - received: %v - expected: %v", test.testInfo, err, changeTest.getError)
continue
}
} else if err != changeTest.getError {
t.Errorf("ChanDefineModify envChild changeTestgeTest %v - Get error - received: %v - expected: %v", test.testInfo, err, changeTest.getError)
continue
}
if value != changeTest.varGetValue {
t.Errorf("DefineModify envChild changeTest %v - value check - received %#v expected: %#v", test.testInfo, value, changeTest.varGetValue)
}
}
}
}
func TestAddr(t *testing.T) {
var err error
tests := []struct {
testInfo string
varName string
varDefineValue interface{}
defineError error
addrError error
}{
{testInfo: "nil", varName: "a", varDefineValue: nil, addrError: nil},
{testInfo: "string", varName: "a", varDefineValue: "a", addrError: fmt.Errorf("unaddressable")},
{testInfo: "int64", varName: "a", varDefineValue: int64(1), addrError: fmt.Errorf("unaddressable")},
{testInfo: "float64", varName: "a", varDefineValue: float64(1), addrError: fmt.Errorf("unaddressable")},
{testInfo: "bool", varName: "a", varDefineValue: true, addrError: fmt.Errorf("unaddressable")},
}
// TestAddr
for _, test := range tests {
envParent := NewEnv()
envChild := envParent.NewEnv()
err = envParent.Define(test.varName, test.varDefineValue)
if err != nil && test.defineError != nil {
if err.Error() != test.defineError.Error() {
t.Errorf("TestAddr %v - Define error - received: %v - expected: %v", test.testInfo, err, test.defineError)
continue
}
} else if err != test.defineError {
t.Errorf("TestAddr %v - Define error - received: %v - expected: %v", test.testInfo, err, test.defineError)
continue
}
_, err = envChild.Addr(test.varName)
if err != nil && test.addrError != nil {
if err.Error() != test.addrError.Error() {
t.Errorf("TestAddr %v - Addr error - received: %v - expected: %v", test.testInfo, err, test.addrError)
continue
}
} else if err != test.addrError {
t.Errorf("TestAddr %v - Addr error - received: %v - expected: %v", test.testInfo, err, test.addrError)
continue
}
}
}
func TestDelete(t *testing.T) {
// empty
env := NewEnv()
env.Delete("a")
// add & delete a
env.Define("a", "a")
env.Delete("a")
value, err := env.Get("a")
expectedError := "undefined symbol 'a'"
if err == nil || err.Error() != expectedError {
t.Errorf("Get error - received: %v - expected: %v", err, expectedError)
}
if value != nil {
t.Errorf("Get value - received: %#v - expected: %#v", value, nil)
}
}
func TestDeleteGlobal(t *testing.T) {
// empty
env := NewEnv()
env.DeleteGlobal("a")
// add & delete a
env.Define("a", "a")
env.DeleteGlobal("a")
value, err := env.Get("a")
expectedError := "undefined symbol 'a'"
if err == nil || err.Error() != expectedError {
t.Errorf("Get error - received: %v - expected: %v", err, expectedError)
}
if value != nil {
t.Errorf("Get value - received: %#v - expected: %#v", value, nil)
}
// parent & child, var in child, delete in parent
envChild := env.NewEnv()
envChild.Define("a", "a")
env.DeleteGlobal("a")
value, err = envChild.Get("a")
if err != nil {
t.Errorf("Get error - received: %v - expected: %v", err, nil)
}
if value != "a" {
t.Errorf("Get value - received: %#v - expected: %#v", value, "a")
}
// parent & child, var in child, delete in child
envChild.DeleteGlobal("a")
value, err = envChild.Get("a")
if err == nil || err.Error() != expectedError {
t.Errorf("Get error - received: %v - expected: %v", err, expectedError)
}
if value != nil {
t.Errorf("Get value - received: %#v - expected: %#v", value, nil)
}
// parent & child, var in parent, delete in child
env.Define("a", "a")
envChild.DeleteGlobal("a")
value, err = envChild.Get("a")
if err == nil || err.Error() != expectedError {
t.Errorf("Get error - received: %v - expected: %v", err, expectedError)
}
if value != nil {
t.Errorf("Get value - received: %#v - expected: %#v", value, nil)
}
// parent & child, var in parent, delete in parent
env.Define("a", "a")
env.DeleteGlobal("a")
value, err = envChild.Get("a")
if err == nil || err.Error() != expectedError {
t.Errorf("Get error - received: %v - expected: %v", err, expectedError)
}
if value != nil {
t.Errorf("Get value - received: %#v - expected: %#v", value, nil)
}
}
func TestRaceCreateSameVariable(t *testing.T) {
// Test creating same variable in parallel
waitChan := make(chan struct{}, 1)
var waitGroup sync.WaitGroup
env := NewEnv()
for i := 0; i < 100; i++ {
waitGroup.Add(1)
go func(i int) {
<-waitChan
err := env.Define("a", i)
if err != nil {
t.Errorf("Define error: %v", err)
}
_, err = env.Get("a")
if err != nil {
t.Errorf("Get error: %v", err)
}
waitGroup.Done()
}(i)
}
close(waitChan)
waitGroup.Wait()
_, err := env.Get("a")
if err != nil {
t.Errorf("Get error: %v", err)
}
}
func TestRaceCreateDifferentVariables(t *testing.T) {
// Test creating different variables in parallel
waitChan := make(chan struct{}, 1)
var waitGroup sync.WaitGroup
env := NewEnv()
for i := 0; i < 100; i++ {
waitGroup.Add(1)
go func(i int) {
<-waitChan
err := env.Define(fmt.Sprint(i), i)
if err != nil {
t.Errorf("Define error: %v", err)
}
_, err = env.Get(fmt.Sprint(i))
if err != nil {
t.Errorf("Get error: %v", err)
}
waitGroup.Done()
}(i)
}
close(waitChan)
waitGroup.Wait()
for i := 0; i < 100; i++ {
_, err := env.Get(fmt.Sprint(i))
if err != nil {
t.Errorf("Get error: %v", err)
}
}
}
func TestRaceReadDifferentVariables(t *testing.T) {
// Test reading different variables in parallel
waitChan := make(chan struct{}, 1)
var waitGroup sync.WaitGroup
env := NewEnv()
for i := 0; i < 100; i++ {
err := env.Define(fmt.Sprint(i), i)
if err != nil {
t.Errorf("Define error: %v", err)
}
_, err = env.Get(fmt.Sprint(i))
if err != nil {
t.Errorf("Get error: %v", err)
}
}
for i := 0; i < 100; i++ {
waitGroup.Add(1)
go func(i int) {
<-waitChan
_, err := env.Get(fmt.Sprint(i))
if err != nil {
t.Errorf("Get error: %v", err)
}
waitGroup.Done()
}(i)
}
close(waitChan)
waitGroup.Wait()
}
func TestRaceSetSameVariable(t *testing.T) {
// Test setting same variable in parallel
waitChan := make(chan struct{}, 1)
var waitGroup sync.WaitGroup
env := NewEnv()
err := env.Define("a", 0)
if err != nil {
t.Errorf("Define error: %v", err)
}
_, err = env.Get("a")
if err != nil {
t.Errorf("Get error: %v", err)
}
for i := 0; i < 100; i++ {
waitGroup.Add(1)
go func(i int) {
<-waitChan
err := env.Set("a", i)
if err != nil {
t.Errorf("Set error: %v", err)
}
waitGroup.Done()
}(i)
}
close(waitChan)
waitGroup.Wait()
_, err = env.Get("a")
if err != nil {
t.Errorf("Get error: %v", err)
}
}
func TestRaceSetSameVariableNewEnv(t *testing.T) {
// Test setting same variable in parallel with NewEnv
waitChan := make(chan struct{}, 1)
var waitGroup sync.WaitGroup
env := NewEnv()
err := env.Define("a", 0)
if err != nil {
t.Errorf("Define error: %v", err)
}
_, err = env.Get("a")
if err != nil {
t.Errorf("Get error: %v", err)
}
for i := 0; i < 100; i++ {
waitGroup.Add(1)
go func(i int) {
<-waitChan
env = env.NewEnv().NewEnv()
err := env.Set("a", i)
if err != nil {
t.Errorf("Set error: %v", err)
}
waitGroup.Done()
}(i)
}
}
func TestRaceDefineAndSetSameVariable(t *testing.T) {
// Test defining and setting same variable in parallel
for i := 0; i < 100; i++ {
raceDefineAndSetSameVariable(t)
}
}
func raceDefineAndSetSameVariable(t *testing.T) {
waitChan := make(chan struct{}, 1)
var waitGroup sync.WaitGroup
envParent := NewEnv()
envChild := envParent.NewEnv()
for i := 0; i < 2; i++ {
waitGroup.Add(1)
go func() {
<-waitChan
err := envParent.Set("a", 1)
if err != nil && err.Error() != "undefined symbol 'a'" {
t.Errorf("Set error: %v", err)
}
waitGroup.Done()
}()
waitGroup.Add(1)
go func() {
<-waitChan
err := envParent.Define("a", 2)
if err != nil {
t.Errorf("Define error: %v", err)
}
waitGroup.Done()
}()
waitGroup.Add(1)
go func() {
<-waitChan
err := envChild.Set("a", 3)
if err != nil && err.Error() != "undefined symbol 'a'" {
t.Errorf("Set error: %v", err)
}
waitGroup.Done()
}()
waitGroup.Add(1)
go func() {
<-waitChan
err := envChild.Define("a", 4)
if err != nil {
t.Errorf("Define error: %v", err)
}
waitGroup.Done()
}()
}
close(waitChan)
waitGroup.Wait()
_, err := envParent.Get("a") // value of a could be 1, 2, or 3
if err != nil {
t.Errorf("Get error: %v", err)
}
_, err = envChild.Get("a") // value of a could be 3 or 4
if err != nil {
t.Errorf("Get error: %v", err)
}
}
func BenchmarkDefine(b *testing.B) {
var err error
env := NewEnv()
b.ResetTimer()
for i := 0; i < b.N; i++ {
err := env.Define("a", 1)
if err != nil {
b.Errorf("Set error: %v", err)
}
}
b.StopTimer()
_, err = env.Get("a")
if err != nil {
b.Errorf("Get error: %v", err)
}
}
func BenchmarkSet(b *testing.B) {
env := NewEnv()
err := env.Define("a", 1)
if err != nil {
b.Errorf("Define error: %v", err)
}
b.ResetTimer()
for i := 0; i < b.N; i++ {
err = env.Set("a", 1)
if err != nil {
b.Errorf("Set error: %v", err)
}
}
b.StopTimer()
_, err = env.Get("a")
if err != nil {
b.Errorf("Get error: %v", err)
}
}

394
src/tool/run/env/env_test.go vendored Normal file
View file

@ -0,0 +1,394 @@
package env
import (
"reflect"
"testing"
)
func TestString(t *testing.T) {
t.Parallel()
env := NewEnv()
env.Define("a", "a")
output := env.String()
expected := `No parent
a = "a"
`
if output != expected {
t.Errorf("received: %v - expected: %v", output, expected)
}
env = env.NewEnv()
env.Define("b", "b")
output = env.String()
expected = `Has parent
b = "b"
`
if output != expected {
t.Errorf("received: %v - expected: %v", output, expected)
}
env = NewEnv()
env.Define("c", "c")
env.DefineType("string", "a")
output = env.String()
expected = `No parent
c = "c"
string = string
`
if output != expected {
t.Errorf("received: %v - expected: %v", output, expected)
}
}
func TestGetEnvFromPath(t *testing.T) {
t.Parallel()
env := NewEnv()
a, err := env.NewModule("a")
if err != nil {
t.Fatal("NewModule error:", err)
}
var b *Env
b, err = a.NewModule("b")
if err != nil {
t.Fatal("NewModule error:", err)
}
var c *Env
c, err = b.NewModule("c")
if err != nil {
t.Fatal("NewModule error:", err)
}
err = c.Define("d", "d")
if err != nil {
t.Fatal("Define error:", err)
}
e, err := env.GetEnvFromPath(nil)
if err != nil {
t.Fatal("GetEnvFromPath error:", err)
}
if e == nil {
t.Fatal("GetEnvFromPath e nil")
}
e, err = env.GetEnvFromPath([]string{})
if err != nil {
t.Fatal("GetEnvFromPath error:", err)
}
if e == nil {
t.Fatal("GetEnvFromPath e nil")
}
e, err = env.GetEnvFromPath([]string{"a", "c"})
expected := "no namespace called: c"
if err == nil || err.Error() != expected {
t.Fatalf("GetEnvFromPath error - received: %v - expected: %v", err, expected)
}
if e != nil {
t.Fatal("GetEnvFromPath e not nil")
}
// a.b.c
e, err = env.GetEnvFromPath([]string{"a", "b", "c"})
if err != nil {
t.Fatal("GetEnvFromPath error:", err)
}
if e == nil {
t.Fatal("GetEnvFromPath e nil")
}
var value interface{}
value, err = e.Get("d")
if err != nil {
t.Fatal("Get error:", err)
}
v, ok := value.(string)
if !ok {
t.Fatal("value not string")
}
if v != "d" {
t.Errorf("value - received: %v - expected: %v", v, "d")
}
e, err = a.GetEnvFromPath([]string{"a", "b", "c"})
if err != nil {
t.Fatal("GetEnvFromPath error:", err)
}
if e == nil {
t.Fatal("GetEnvFromPath e nil")
}
value, err = e.Get("d")
if err != nil {
t.Fatal("Get error:", err)
}
v, ok = value.(string)
if !ok {
t.Fatal("value not string")
}
if v != "d" {
t.Errorf("value - received: %v - expected: %v", v, "d")
}
e, err = b.GetEnvFromPath([]string{"a", "b", "c"})
if err != nil {
t.Fatal("GetEnvFromPath error:", err)
}
if e == nil {
t.Fatal("GetEnvFromPath e nil")
}
value, err = e.Get("d")
if err != nil {
t.Fatal("Get error:", err)
}
v, ok = value.(string)
if !ok {
t.Fatal("value not string")
}
if v != "d" {
t.Errorf("value - received: %v - expected: %v", v, "d")
}
e, err = c.GetEnvFromPath([]string{"a", "b", "c"})
if err != nil {
t.Fatal("GetEnvFromPath error:", err)
}
if e == nil {
t.Fatal("GetEnvFromPath e nil")
}
value, err = e.Get("d")
if err != nil {
t.Fatal("Get error:", err)
}
v, ok = value.(string)
if !ok {
t.Fatal("value not string")
}
if v != "d" {
t.Errorf("value - received: %v - expected: %v", v, "d")
}
// b.c
e, err = env.GetEnvFromPath([]string{"b", "c"})
expected = "no namespace called: b"
if err == nil || err.Error() != expected {
t.Fatalf("GetEnvFromPath error - received: %v - expected: %v", err, expected)
}
if e != nil {
t.Fatal("GetEnvFromPath e not nil")
}
e, err = a.GetEnvFromPath([]string{"b", "c"})
if err != nil {
t.Fatal("GetEnvFromPath error:", err)
}
if e == nil {
t.Fatal("GetEnvFromPath e nil")
}
value, err = e.Get("d")
if err != nil {
t.Fatal("Get error:", err)
}
v, ok = value.(string)
if !ok {
t.Fatal("value not string")
}
if v != "d" {
t.Errorf("value - received: %v - expected: %v", v, "d")
}
e, err = b.GetEnvFromPath([]string{"b", "c"})
if err != nil {
t.Fatal("GetEnvFromPath error:", err)
}
if e == nil {
t.Fatal("GetEnvFromPath e nil")
}
value, err = e.Get("d")
if err != nil {
t.Fatal("Get error:", err)
}
v, ok = value.(string)
if !ok {
t.Fatal("value not string")
}
if v != "d" {
t.Errorf("value - received: %v - expected: %v", v, "d")
}
e, err = c.GetEnvFromPath([]string{"b", "c"})
if err != nil {
t.Fatal("GetEnvFromPath error:", err)
}
if e == nil {
t.Fatal("GetEnvFromPath e nil")
}
value, err = e.Get("d")
if err != nil {
t.Fatal("Get error:", err)
}
v, ok = value.(string)
if !ok {
t.Fatal("value not string")
}
if v != "d" {
t.Errorf("value - received: %v - expected: %v", v, "d")
}
// c
e, err = env.GetEnvFromPath([]string{"c"})
expected = "no namespace called: c"
if err == nil || err.Error() != expected {
t.Fatalf("GetEnvFromPath error - received: %v - expected: %v", err, expected)
}
if e != nil {
t.Fatal("GetEnvFromPath e not nil")
}
e, err = b.GetEnvFromPath([]string{"c"})
if err != nil {
t.Fatal("GetEnvFromPath error:", err)
}
if e == nil {
t.Fatal("GetEnvFromPath e nil")
}
value, err = e.Get("d")
if err != nil {
t.Fatal("Get error:", err)
}
v, ok = value.(string)
if !ok {
t.Fatal("value not string")
}
if v != "d" {
t.Errorf("value - received: %v - expected: %v", v, "d")
}
e, err = c.GetEnvFromPath(nil)
if err != nil {
t.Fatal("GetEnvFromPath error:", err)
}
if e == nil {
t.Fatal("GetEnvFromPath e nil")
}
value, err = e.Get("d")
if err != nil {
t.Fatal("Get error:", err)
}
v, ok = value.(string)
if !ok {
t.Fatal("value not string")
}
if v != "d" {
t.Errorf("value - received: %v - expected: %v", v, "d")
}
}
func TestCopy(t *testing.T) {
t.Parallel()
parent := NewEnv()
parent.Define("a", "a")
parent.DefineType("b", []bool{})
child := parent.NewEnv()
child.Define("c", "c")
child.DefineType("d", []int64{})
copy := child.Copy()
if v, e := copy.Get("a"); e != nil || v != "a" {
t.Errorf("copy missing value")
}
if v, e := copy.Type("b"); e != nil || v != reflect.TypeOf([]bool{}) {
t.Errorf("copy missing type")
}
if v, e := copy.Get("c"); e != nil || v != "c" {
t.Errorf("copy missing value")
}
if v, e := copy.Type("d"); e != nil || v != reflect.TypeOf([]int64{}) {
t.Errorf("copy missing type")
}
// TODO: add more get type tests
copy.Set("a", "i")
if v, e := child.Get("a"); e != nil || v != "i" {
t.Errorf("parent was not modified")
}
if v, e := copy.Get("a"); e != nil || v != "i" {
t.Errorf("copy did not get parent value")
}
copy.Set("c", "j")
if v, e := child.Get("c"); e != nil || v != "c" {
t.Errorf("child was not modified")
}
if v, e := copy.Get("c"); e != nil || v != "j" {
t.Errorf("copy child was not modified")
}
child.Set("a", "x")
if v, e := child.Get("a"); e != nil || v != "x" {
t.Errorf("parent was not modified")
}
if v, e := copy.Get("a"); e != nil || v != "x" {
t.Errorf("copy did not get parent value")
}
child.Set("c", "z")
if v, e := child.Get("c"); e != nil || v != "z" {
t.Errorf("child was not modified")
}
if v, e := copy.Get("c"); e != nil || v != "j" {
t.Errorf("copy child was modified")
}
parent.Set("a", "m")
if v, e := child.Get("a"); e != nil || v != "m" {
t.Errorf("parent was not modified")
}
if v, e := copy.Get("a"); e != nil || v != "m" {
t.Errorf("copy did not get parent value")
}
parent.Define("x", "n")
if v, e := child.Get("x"); e != nil || v != "n" {
t.Errorf("child did not get parent value")
}
if v, e := copy.Get("x"); e != nil || v != "n" {
t.Errorf("copy did not get parent value")
}
}
func TestDeepCopy(t *testing.T) {
t.Parallel()
parent := NewEnv()
parent.Define("a", "a")
env := parent.NewEnv()
copy := env.DeepCopy()
// TODO: add more/better tests, like above
if v, e := copy.Get("a"); e != nil || v != "a" {
t.Errorf("copy doesn't retain original values")
}
parent.Set("a", "b")
if v, e := env.Get("a"); e != nil || v != "b" {
t.Errorf("son was not modified")
}
if v, e := copy.Get("a"); e != nil || v != "a" {
t.Errorf("copy got the new value")
}
parent.Set("a", "c")
if v, e := env.Get("a"); e != nil || v != "c" {
t.Errorf("original was not modified")
}
if v, e := copy.Get("a"); e != nil || v != "a" {
t.Errorf("copy was modified")
}
parent.Define("b", "b")
if _, e := copy.Get("b"); e == nil {
t.Errorf("copy parent was modified")
}
}

View file

@ -0,0 +1 @@
au BufNewFile,BufRead *.ank setlocal filetype=anko

View file

@ -0,0 +1,11 @@
if exists("b:did_ftplugin")
finish
endif
let b:did_ftplugin = 1
setlocal comments=s1:#
setlocal commentstring=#\ %s
let b:undo_ftplugin = "setl com< cms<"
" vim:ts=4:sw=4:et

View file

@ -0,0 +1,15 @@
scriptencoding utf-8
function! s:play()
let code = join(getline(1, '$'), "\n")
let res = webapi#http#post("http://play-anko.appspot.com/api/play", {"code": code})
if res.status == "200"
echo iconv(res.content, "utf-8", &encoding)
else
for line in split(res.content, "\n")
echohl Error | echomsg iconv(line, "utf-8", &encoding) | echohl None
endfor
endif
endfunction
command! -buffer PlayAnko call s:play()

View file

@ -0,0 +1,100 @@
if exists("b:current_syntax")
finish
endif
syn case match
syn keyword ankoDirective module
syn keyword ankoDeclaration var
hi def link ankoDirective Statement
hi def link ankoDeclaration Type
syn keyword ankoStatement return break continue throw
syn keyword ankoConditional if else switch try catch finally
syn keyword ankoLabel case default
syn keyword ankoRepeat for range
hi def link ankoStatement Statement
hi def link ankoConditional Conditional
hi def link ankoLabel Label
hi def link ankoRepeat Repeat
syn match ankoDeclaration /\<func\>/
syn match ankoDeclaration /^func\>/
syn keyword ankoCast bytes runes string
hi def link ankoCast Type
syn keyword ankoBuiltins keys len
syn keyword ankoBuiltins println printf print
syn keyword ankoConstants true false nil
hi def link ankoBuiltins Keyword
hi def link ankoConstants Keyword
" Comments; their contents
syn keyword ankoTodo contained TODO FIXME XXX BUG
syn cluster ankoCommentGroup contains=ankoTodo
syn region ankoComment start="#" end="$" contains=@ankoCommentGroup,@Spell
hi def link ankoComment Comment
hi def link ankoTodo Todo
" anko escapes
syn match ankoEscapeOctal display contained "\\[0-7]\{3}"
syn match ankoEscapeC display contained +\\[abfnrtv\\'"]+
syn match ankoEscapeX display contained "\\x\x\{2}"
syn match ankoEscapeU display contained "\\u\x\{4}"
syn match ankoEscapeBigU display contained "\\U\x\{8}"
syn match ankoEscapeError display contained +\\[^0-7xuUabfnrtv\\'"]+
hi def link ankoEscapeOctal ankoSpecialString
hi def link ankoEscapeC ankoSpecialString
hi def link ankoEscapeX ankoSpecialString
hi def link ankoEscapeU ankoSpecialString
hi def link ankoEscapeBigU ankoSpecialString
hi def link ankoSpecialString Special
hi def link ankoEscapeError Error
" Strings and their contents
syn cluster ankoStringGroup contains=ankoEscapeOctal,ankoEscapeC,ankoEscapeX,ankoEscapeU,ankoEscapeBigU,ankoEscapeError
syn region ankoString start=+"+ skip=+\\\\\|\\"+ end=+"+ contains=@ankoStringGroup
syn region ankoRawString start=+`+ end=+`+
hi def link ankoString String
hi def link ankoRawString String
" Characters; their contents
syn cluster ankoCharacterGroup contains=ankoEscapeOctal,ankoEscapeC,ankoEscapeX,ankoEscapeU,ankoEscapeBigU
syn region ankoCharacter start=+'+ skip=+\\\\\|\\'+ end=+'+ contains=@ankoCharacterGroup
hi def link ankoCharacter Character
" Regions
syn region ankoBlock start="{" end="}" transparent fold
syn region ankoParen start='(' end=')' transparent
" Integers
syn match ankoDecimalInt "\<\d\+\([Ee]\d\+\)\?\>"
syn match ankoHexadecimalInt "\<0x\x\+\>"
syn match ankoOctalInt "\<0\o\+\>"
syn match ankoOctalError "\<0\o*[89]\d*\>"
hi def link ankoDecimalInt Integer
hi def link ankoHexadecimalInt Integer
hi def link ankoOctalInt Integer
hi def link Integer Number
" Floating point
syn match ankoFloat "\<\d\+\.\d*\([Ee][-+]\d\+\)\?\>"
syn match ankoFloat "\<\.\d\+\([Ee][-+]\d\+\)\?\>"
syn match ankoFloat "\<\d\+[Ee][-+]\d\+\>"
hi def link ankoFloat Float
hi def link ankoImaginary Number
syn sync minlines=500
let b:current_syntax = "anko"

View file

@ -0,0 +1,129 @@
// +build js,wasm
package main
import (
"fmt"
"html"
"strings"
"syscall/js"
"github.com/surdeus/goblin/src/tool/run/core"
"github.com/surdeus/goblin/src/tool/run/packages"
"github.com/surdeus/goblin/src/tool/run/parser"
"github.com/surdeus/goblin/src/tool/run/vm"
)
var (
result = js.Global.Get("document").Call("getElementById", "result")
input = js.Global.Get("document").Call("getElementById", "input")
)
func writeCommand(s string) {
result.Set("innerHTML", result.Get("innerHTML").String()+"<p class='command'>"+html.EscapeString(s)+"</p>")
result.Set("scrollTop", result.Get("scrollHeight").Int())
}
func writeStdout(s string) {
result.Set("innerHTML", result.Get("innerHTML").String()+"<p class='stdout'>"+html.EscapeString(s)+"</p>")
result.Set("scrollTop", result.Get("scrollHeight").Int())
}
func writeStderr(s string) {
result.Set("innerHTML", result.Get("innerHTML").String()+"<p class='stderr'>"+html.EscapeString(s)+"</p>")
result.Set("scrollTop", result.Get("scrollHeight").Int())
}
func main() {
env := vm.NewEnv()
core.Import(env)
packages.DefineImport(env)
env.Define("print", func(a ...interface{}) {
writeStdout(fmt.Sprint(a...))
})
env.Define("printf", func(a string, b ...interface{}) {
writeStdout(fmt.Sprintf(a, b...))
})
var following bool
var source string
parser.EnableErrorVerbose()
ch := make(chan string)
input.Call("addEventListener", "keypress", js.NewCallback(func(args []js.Value) {
e := args[0]
if e.Get("keyCode").Int() != 13 {
return
}
s := e.Get("target").Get("value").String()
e.Get("target").Set("value", "")
writeCommand(s)
ch <- s
}))
input.Set("disabled", false)
result.Set("innerHTML", "")
go func() {
for {
text, ok := <-ch
if !ok {
break
}
source += text
if source == "" {
continue
}
if source == "quit()" {
break
}
stmts, err := parser.ParseSrc(source)
if e, ok := err.(*parser.Error); ok {
es := e.Error()
if strings.HasPrefix(es, "syntax error: unexpected") {
if strings.HasPrefix(es, "syntax error: unexpected $end,") {
following = true
continue
}
} else {
if e.Pos.Column == len(source) && !e.Fatal {
writeStderr(e.Error())
following = true
continue
}
if e.Error() == "unexpected EOF" {
following = true
continue
}
}
}
following = false
source = ""
var v interface{}
if err == nil {
v, err = vm.Run(stmts, env)
}
if err != nil {
if e, ok := err.(*vm.Error); ok {
writeStderr(fmt.Sprintf("%d:%d %s\n", e.Pos.Line, e.Pos.Column, err))
} else if e, ok := err.(*parser.Error); ok {
writeStderr(fmt.Sprintf("%d:%d %s\n", e.Pos.Line, e.Pos.Column, err))
} else {
writeStderr(err.Error())
}
continue
}
writeStdout(fmt.Sprintf("%#v\n", v))
}
}()
select {}
}

View file

@ -0,0 +1,35 @@
<html>
<head>
<title>Anko WebAssembly</title>
<script src="wasm_exec.js"></script>
<script type="text/javascript">
function fetchAndInstantiate(url, importObject) {
return fetch(url).then(response =>
response.arrayBuffer()
).then(bytes =>
WebAssembly.instantiate(bytes, importObject)
).then(results =>
results.instance
);
}
var go = new Go();
var mod = fetchAndInstantiate("/anko.wasm", go.importObject);
window.onload = function() {
mod.then(function(instance) {
go.run(instance);
});
};
</script>
<style>
#result { width: 100%; height: 300px; overflow-y: scroll; }
#input { width: 100%; }
.stdout { margin: 0px; }
.command { margin: 0px; color: gray; }
.stderr { margin: 0px; color: red; }
</style>
</head>
<body>
<div id="result">loading...</div>
<input id="input" type="text" value="" disabled=true /><br />
</body>
</html>

View file

@ -0,0 +1,381 @@
// Copyright 2018 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.
(() => {
// Map web browser API and Node.js API to a single common API (preferring web standards over Node.js API).
const isNodeJS = typeof process !== "undefined";
if (isNodeJS) {
global.require = require;
global.fs = require("fs");
const nodeCrypto = require("crypto");
global.crypto = {
getRandomValues(b) {
nodeCrypto.randomFillSync(b);
},
};
global.performance = {
now() {
const [sec, nsec] = process.hrtime();
return sec * 1000 + nsec / 1000000;
},
};
const util = require("util");
global.TextEncoder = util.TextEncoder;
global.TextDecoder = util.TextDecoder;
} else {
window.global = window;
let outputBuf = "";
global.fs = {
constants: {},
writeSync(fd, buf) {
outputBuf += decoder.decode(buf);
const nl = outputBuf.lastIndexOf("\n");
if (nl != -1) {
console.log(outputBuf.substr(0, nl));
outputBuf = outputBuf.substr(nl + 1);
}
return buf.length;
},
openSync(path, flags, mode) {
const err = new Error("not implemented");
err.code = "ENOSYS";
throw err;
},
};
}
const encoder = new TextEncoder("utf-8");
const decoder = new TextDecoder("utf-8");
global.Go = class {
constructor() {
this.argv = ["js"];
this.env = {};
this.exit = (code) => {
if (code !== 0) {
console.warn("exit code:", code);
}
};
this._callbackTimeouts = new Map();
this._nextCallbackTimeoutID = 1;
const mem = () => {
// The buffer may change when requesting more memory.
return new DataView(this._inst.exports.mem.buffer);
}
const setInt64 = (addr, v) => {
mem().setUint32(addr + 0, v, true);
mem().setUint32(addr + 4, Math.floor(v / 4294967296), true);
}
const getInt64 = (addr) => {
const low = mem().getUint32(addr + 0, true);
const high = mem().getInt32(addr + 4, true);
return low + high * 4294967296;
}
const loadValue = (addr) => {
const id = mem().getUint32(addr, true);
return this._values[id];
}
const storeValue = (addr, v) => {
if (v === undefined) {
mem().setUint32(addr, 0, true);
return;
}
if (v === null) {
mem().setUint32(addr, 1, true);
return;
}
this._values.push(v);
mem().setUint32(addr, this._values.length - 1, true);
}
const loadSlice = (addr) => {
const array = getInt64(addr + 0);
const len = getInt64(addr + 8);
return new Uint8Array(this._inst.exports.mem.buffer, array, len);
}
const loadSliceOfValues = (addr) => {
const array = getInt64(addr + 0);
const len = getInt64(addr + 8);
const a = new Array(len);
for (let i = 0; i < len; i++) {
const id = mem().getUint32(array + i * 4, true);
a[i] = this._values[id];
}
return a;
}
const loadString = (addr) => {
const saddr = getInt64(addr + 0);
const len = getInt64(addr + 8);
return decoder.decode(new DataView(this._inst.exports.mem.buffer, saddr, len));
}
const timeOrigin = Date.now() - performance.now();
this.importObject = {
go: {
// func wasmExit(code int32)
"runtime.wasmExit": (sp) => {
this.exited = true;
this.exit(mem().getInt32(sp + 8, true));
},
// func wasmWrite(fd uintptr, p unsafe.Pointer, n int32)
"runtime.wasmWrite": (sp) => {
const fd = getInt64(sp + 8);
const p = getInt64(sp + 16);
const n = mem().getInt32(sp + 24, true);
fs.writeSync(fd, new Uint8Array(this._inst.exports.mem.buffer, p, n));
},
// func nanotime() int64
"runtime.nanotime": (sp) => {
setInt64(sp + 8, (timeOrigin + performance.now()) * 1000000);
},
// func walltime() (sec int64, nsec int32)
"runtime.walltime": (sp) => {
const msec = (new Date).getTime();
setInt64(sp + 8, msec / 1000);
mem().setInt32(sp + 16, (msec % 1000) * 1000000, true);
},
// func scheduleCallback(delay int64) int32
"runtime.scheduleCallback": (sp) => {
const id = this._nextCallbackTimeoutID;
this._nextCallbackTimeoutID++;
this._callbackTimeouts.set(id, setTimeout(
() => { this._resolveCallbackPromise(); },
getInt64(sp + 8) + 1, // setTimeout has been seen to fire up to 1 millisecond early
));
mem().setInt32(sp + 16, id, true);
},
// func clearScheduledCallback(id int32)
"runtime.clearScheduledCallback": (sp) => {
const id = mem().getInt32(sp + 8, true);
clearTimeout(this._callbackTimeouts.get(id));
this._callbackTimeouts.delete(id);
},
// func getRandomData(r []byte)
"runtime.getRandomData": (sp) => {
crypto.getRandomValues(loadSlice(sp + 8));
},
// func boolVal(value bool) ref
"syscall/js.boolVal": (sp) => {
storeValue(sp + 16, mem().getUint8(sp + 8) !== 0);
},
// func intVal(value int) ref
"syscall/js.intVal": (sp) => {
storeValue(sp + 16, getInt64(sp + 8));
},
// func floatVal(value float64) ref
"syscall/js.floatVal": (sp) => {
storeValue(sp + 16, mem().getFloat64(sp + 8, true));
},
// func stringVal(value string) ref
"syscall/js.stringVal": (sp) => {
storeValue(sp + 24, loadString(sp + 8));
},
// func valueGet(v ref, p string) ref
"syscall/js.valueGet": (sp) => {
storeValue(sp + 32, Reflect.get(loadValue(sp + 8), loadString(sp + 16)));
},
// func valueSet(v ref, p string, x ref)
"syscall/js.valueSet": (sp) => {
Reflect.set(loadValue(sp + 8), loadString(sp + 16), loadValue(sp + 32));
},
// func valueIndex(v ref, i int) ref
"syscall/js.valueIndex": (sp) => {
storeValue(sp + 24, Reflect.get(loadValue(sp + 8), getInt64(sp + 16)));
},
// valueSetIndex(v ref, i int, x ref)
"syscall/js.valueSetIndex": (sp) => {
Reflect.set(loadValue(sp + 8), getInt64(sp + 16), loadValue(sp + 24));
},
// func valueCall(v ref, m string, args []ref) (ref, bool)
"syscall/js.valueCall": (sp) => {
try {
const v = loadValue(sp + 8);
const m = Reflect.get(v, loadString(sp + 16));
const args = loadSliceOfValues(sp + 32);
storeValue(sp + 56, Reflect.apply(m, v, args));
mem().setUint8(sp + 60, 1);
} catch (err) {
storeValue(sp + 56, err);
mem().setUint8(sp + 60, 0);
}
},
// func valueInvoke(v ref, args []ref) (ref, bool)
"syscall/js.valueInvoke": (sp) => {
try {
const v = loadValue(sp + 8);
const args = loadSliceOfValues(sp + 16);
storeValue(sp + 40, Reflect.apply(v, undefined, args));
mem().setUint8(sp + 44, 1);
} catch (err) {
storeValue(sp + 40, err);
mem().setUint8(sp + 44, 0);
}
},
// func valueNew(v ref, args []ref) (ref, bool)
"syscall/js.valueNew": (sp) => {
try {
const v = loadValue(sp + 8);
const args = loadSliceOfValues(sp + 16);
storeValue(sp + 40, Reflect.construct(v, args));
mem().setUint8(sp + 44, 1);
} catch (err) {
storeValue(sp + 40, err);
mem().setUint8(sp + 44, 0);
}
},
// func valueFloat(v ref) float64
"syscall/js.valueFloat": (sp) => {
mem().setFloat64(sp + 16, parseFloat(loadValue(sp + 8)), true);
},
// func valueInt(v ref) int
"syscall/js.valueInt": (sp) => {
setInt64(sp + 16, parseInt(loadValue(sp + 8)));
},
// func valueBool(v ref) bool
"syscall/js.valueBool": (sp) => {
mem().setUint8(sp + 16, !!loadValue(sp + 8));
},
// func valueLength(v ref) int
"syscall/js.valueLength": (sp) => {
setInt64(sp + 16, parseInt(loadValue(sp + 8).length));
},
// valuePrepareString(v ref) (ref, int)
"syscall/js.valuePrepareString": (sp) => {
const str = encoder.encode(String(loadValue(sp + 8)));
storeValue(sp + 16, str);
setInt64(sp + 24, str.length);
},
// valueLoadString(v ref, b []byte)
"syscall/js.valueLoadString": (sp) => {
const str = loadValue(sp + 8);
loadSlice(sp + 16).set(str);
},
"debug": (value) => {
console.log(value);
},
}
};
}
async run(instance) {
this._inst = instance;
this._values = [ // TODO: garbage collection
undefined,
null,
global,
this._inst.exports.mem,
() => { // resolveCallbackPromise
if (this.exited) {
throw new Error("bad callback: Go program has already exited");
}
setTimeout(this._resolveCallbackPromise, 0); // make sure it is asynchronous
},
];
this.exited = false;
const mem = new DataView(this._inst.exports.mem.buffer)
// Pass command line arguments and environment variables to WebAssembly by writing them to the linear memory.
let offset = 4096;
const strPtr = (str) => {
let ptr = offset;
new Uint8Array(mem.buffer, offset, str.length + 1).set(encoder.encode(str + "\0"));
offset += str.length + (8 - (str.length % 8));
return ptr;
};
const argc = this.argv.length;
const argvPtrs = [];
this.argv.forEach((arg) => {
argvPtrs.push(strPtr(arg));
});
const keys = Object.keys(this.env).sort();
argvPtrs.push(keys.length);
keys.forEach((key) => {
argvPtrs.push(strPtr(`${key}=${this.env[key]}`));
});
const argv = offset;
argvPtrs.forEach((ptr) => {
mem.setUint32(offset, ptr, true);
mem.setUint32(offset + 4, 0, true);
offset += 8;
});
while (true) {
const callbackPromise = new Promise((resolve) => {
this._resolveCallbackPromise = resolve;
});
this._inst.exports.run(argc, argv);
if (this.exited) {
break;
}
await callbackPromise;
}
}
}
if (isNodeJS) {
if (process.argv.length < 3) {
process.stderr.write("usage: go_js_wasm_exec [wasm binary] [arguments]\n");
process.exit(1);
}
const go = new Go();
go.argv = process.argv.slice(2);
go.env = process.env;
go.exit = process.exit;
WebAssembly.instantiate(fs.readFileSync(process.argv[2]), go.importObject).then((result) => {
process.on("exit", () => { // Node.js exits if no callback is pending
if (!go.exited) {
console.error("error: all goroutines asleep and no JavaScript callback pending - deadlock!");
process.exit(1);
}
});
return go.run(result.instance);
}).catch((err) => {
console.error(err);
go.exited = true;
process.exit(1);
});
}
})();

View file

@ -0,0 +1,64 @@
package packages
import (
"bytes"
"reflect"
"github.com/surdeus/goblin/src/tool/run/env"
)
func init() {
env.Packages["bytes"] = map[string]reflect.Value{
"Compare": reflect.ValueOf(bytes.Compare),
"Contains": reflect.ValueOf(bytes.Contains),
"Count": reflect.ValueOf(bytes.Count),
"Equal": reflect.ValueOf(bytes.Equal),
"EqualFold": reflect.ValueOf(bytes.EqualFold),
"Fields": reflect.ValueOf(bytes.Fields),
"FieldsFunc": reflect.ValueOf(bytes.FieldsFunc),
"HasPrefix": reflect.ValueOf(bytes.HasPrefix),
"HasSuffix": reflect.ValueOf(bytes.HasSuffix),
"Index": reflect.ValueOf(bytes.Index),
"IndexAny": reflect.ValueOf(bytes.IndexAny),
"IndexByte": reflect.ValueOf(bytes.IndexByte),
"IndexFunc": reflect.ValueOf(bytes.IndexFunc),
"IndexRune": reflect.ValueOf(bytes.IndexRune),
"Join": reflect.ValueOf(bytes.Join),
"LastIndex": reflect.ValueOf(bytes.LastIndex),
"LastIndexAny": reflect.ValueOf(bytes.LastIndexAny),
"LastIndexByte": reflect.ValueOf(bytes.LastIndexByte),
"LastIndexFunc": reflect.ValueOf(bytes.LastIndexFunc),
"Map": reflect.ValueOf(bytes.Map),
"NewBuffer": reflect.ValueOf(bytes.NewBuffer),
"NewBufferString": reflect.ValueOf(bytes.NewBufferString),
"NewReader": reflect.ValueOf(bytes.NewReader),
"Repeat": reflect.ValueOf(bytes.Repeat),
"Replace": reflect.ValueOf(bytes.Replace),
"Runes": reflect.ValueOf(bytes.Runes),
"Split": reflect.ValueOf(bytes.Split),
"SplitAfter": reflect.ValueOf(bytes.SplitAfter),
"SplitAfterN": reflect.ValueOf(bytes.SplitAfterN),
"SplitN": reflect.ValueOf(bytes.SplitN),
"Title": reflect.ValueOf(bytes.Title),
"ToLower": reflect.ValueOf(bytes.ToLower),
"ToLowerSpecial": reflect.ValueOf(bytes.ToLowerSpecial),
"ToTitle": reflect.ValueOf(bytes.ToTitle),
"ToTitleSpecial": reflect.ValueOf(bytes.ToTitleSpecial),
"ToUpper": reflect.ValueOf(bytes.ToUpper),
"ToUpperSpecial": reflect.ValueOf(bytes.ToUpperSpecial),
"Trim": reflect.ValueOf(bytes.Trim),
"TrimFunc": reflect.ValueOf(bytes.TrimFunc),
"TrimLeft": reflect.ValueOf(bytes.TrimLeft),
"TrimLeftFunc": reflect.ValueOf(bytes.TrimLeftFunc),
"TrimPrefix": reflect.ValueOf(bytes.TrimPrefix),
"TrimRight": reflect.ValueOf(bytes.TrimRight),
"TrimRightFunc": reflect.ValueOf(bytes.TrimRightFunc),
"TrimSpace": reflect.ValueOf(bytes.TrimSpace),
"TrimSuffix": reflect.ValueOf(bytes.TrimSuffix),
}
env.PackageTypes["bytes"] = map[string]reflect.Type{
"Buffer": reflect.TypeOf(bytes.Buffer{}),
"Reader": reflect.TypeOf(bytes.Reader{}),
}
bytesGo17()
}

View file

@ -0,0 +1,14 @@
// +build go1.7
package packages
import (
"bytes"
"reflect"
"github.com/surdeus/goblin/src/tool/run/env"
)
func bytesGo17() {
env.Packages["bytes"]["ContainsRune"] = reflect.ValueOf(bytes.ContainsRune)
}

View file

@ -0,0 +1,5 @@
// +build !go1.7
package packages
func bytesGo17() {}

View file

@ -0,0 +1,15 @@
package packages
import (
"encoding/json"
"reflect"
"github.com/surdeus/goblin/src/tool/run/env"
)
func init() {
env.Packages["encoding/json"] = map[string]reflect.Value{
"Marshal": reflect.ValueOf(json.Marshal),
"Unmarshal": reflect.ValueOf(json.Unmarshal),
}
}

View file

@ -0,0 +1,14 @@
package packages
import (
"errors"
"reflect"
"github.com/surdeus/goblin/src/tool/run/env"
)
func init() {
env.Packages["errors"] = map[string]reflect.Value{
"New": reflect.ValueOf(errors.New),
}
}

View file

@ -0,0 +1,48 @@
package packages
import (
"flag"
"reflect"
"github.com/surdeus/goblin/src/tool/run/env"
)
func init() {
env.Packages["flag"] = map[string]reflect.Value{
"Arg": reflect.ValueOf(flag.Arg),
"Args": reflect.ValueOf(flag.Args),
"Bool": reflect.ValueOf(flag.Bool),
"BoolVar": reflect.ValueOf(flag.BoolVar),
"CommandLine": reflect.ValueOf(flag.CommandLine),
"ContinueOnError": reflect.ValueOf(flag.ContinueOnError),
"Duration": reflect.ValueOf(flag.Duration),
"DurationVar": reflect.ValueOf(flag.DurationVar),
"ErrHelp": reflect.ValueOf(flag.ErrHelp),
"ExitOnError": reflect.ValueOf(flag.ExitOnError),
"Float64": reflect.ValueOf(flag.Float64),
"Float64Var": reflect.ValueOf(flag.Float64Var),
"Int": reflect.ValueOf(flag.Int),
"Int64": reflect.ValueOf(flag.Int64),
"Int64Var": reflect.ValueOf(flag.Int64Var),
"IntVar": reflect.ValueOf(flag.IntVar),
"Lookup": reflect.ValueOf(flag.Lookup),
"NArg": reflect.ValueOf(flag.NArg),
"NFlag": reflect.ValueOf(flag.NFlag),
"NewFlagSet": reflect.ValueOf(flag.NewFlagSet),
"PanicOnError": reflect.ValueOf(flag.PanicOnError),
"Parse": reflect.ValueOf(flag.Parse),
"Parsed": reflect.ValueOf(flag.Parsed),
"PrintDefaults": reflect.ValueOf(flag.PrintDefaults),
"Set": reflect.ValueOf(flag.Set),
"String": reflect.ValueOf(flag.String),
"StringVar": reflect.ValueOf(flag.StringVar),
"Uint": reflect.ValueOf(flag.Uint),
"Uint64": reflect.ValueOf(flag.Uint64),
"Uint64Var": reflect.ValueOf(flag.Uint64Var),
"UintVar": reflect.ValueOf(flag.UintVar),
"Usage": reflect.ValueOf(flag.Usage),
"Var": reflect.ValueOf(flag.Var),
"Visit": reflect.ValueOf(flag.Visit),
"VisitAll": reflect.ValueOf(flag.VisitAll),
}
}

View file

@ -0,0 +1,32 @@
package packages
import (
"fmt"
"reflect"
"github.com/surdeus/goblin/src/tool/run/env"
)
func init() {
env.Packages["fmt"] = map[string]reflect.Value{
"Errorf": reflect.ValueOf(fmt.Errorf),
"Fprint": reflect.ValueOf(fmt.Fprint),
"Fprintf": reflect.ValueOf(fmt.Fprintf),
"Fprintln": reflect.ValueOf(fmt.Fprintln),
"Fscan": reflect.ValueOf(fmt.Fscan),
"Fscanf": reflect.ValueOf(fmt.Fscanf),
"Fscanln": reflect.ValueOf(fmt.Fscanln),
"Print": reflect.ValueOf(fmt.Print),
"Printf": reflect.ValueOf(fmt.Printf),
"Println": reflect.ValueOf(fmt.Println),
"Scan": reflect.ValueOf(fmt.Scan),
"Scanf": reflect.ValueOf(fmt.Scanf),
"Scanln": reflect.ValueOf(fmt.Scanln),
"Sprint": reflect.ValueOf(fmt.Sprint),
"Sprintf": reflect.ValueOf(fmt.Sprintf),
"Sprintln": reflect.ValueOf(fmt.Sprintln),
"Sscan": reflect.ValueOf(fmt.Sscan),
"Sscanf": reflect.ValueOf(fmt.Sscanf),
"Sscanln": reflect.ValueOf(fmt.Sscanln),
}
}

View file

@ -0,0 +1,30 @@
package packages
import (
"io"
"reflect"
"github.com/surdeus/goblin/src/tool/run/env"
)
func init() {
env.Packages["io"] = map[string]reflect.Value{
"Copy": reflect.ValueOf(io.Copy),
"CopyN": reflect.ValueOf(io.CopyN),
"EOF": reflect.ValueOf(io.EOF),
"ErrClosedPipe": reflect.ValueOf(io.ErrClosedPipe),
"ErrNoProgress": reflect.ValueOf(io.ErrNoProgress),
"ErrShortBuffer": reflect.ValueOf(io.ErrShortBuffer),
"ErrShortWrite": reflect.ValueOf(io.ErrShortWrite),
"ErrUnexpectedEOF": reflect.ValueOf(io.ErrUnexpectedEOF),
"LimitReader": reflect.ValueOf(io.LimitReader),
"MultiReader": reflect.ValueOf(io.MultiReader),
"MultiWriter": reflect.ValueOf(io.MultiWriter),
"NewSectionReader": reflect.ValueOf(io.NewSectionReader),
"Pipe": reflect.ValueOf(io.Pipe),
"ReadAtLeast": reflect.ValueOf(io.ReadAtLeast),
"ReadFull": reflect.ValueOf(io.ReadFull),
"TeeReader": reflect.ValueOf(io.TeeReader),
"WriteString": reflect.ValueOf(io.WriteString),
}
}

View file

@ -0,0 +1,17 @@
package packages
import (
"io/ioutil"
"reflect"
"github.com/surdeus/goblin/src/tool/run/env"
)
func init() {
env.Packages["io/ioutil"] = map[string]reflect.Value{
"ReadAll": reflect.ValueOf(ioutil.ReadAll),
"ReadDir": reflect.ValueOf(ioutil.ReadDir),
"ReadFile": reflect.ValueOf(ioutil.ReadFile),
"WriteFile": reflect.ValueOf(ioutil.WriteFile),
}
}

View file

@ -0,0 +1,29 @@
package packages
import (
"log"
"reflect"
"github.com/surdeus/goblin/src/tool/run/env"
)
func init() {
env.Packages["log"] = map[string]reflect.Value{
"Fatal": reflect.ValueOf(log.Fatal),
"Fatalf": reflect.ValueOf(log.Fatalf),
"Fatalln": reflect.ValueOf(log.Fatalln),
"Flags": reflect.ValueOf(log.Flags),
"New": reflect.ValueOf(log.New),
"Output": reflect.ValueOf(log.Output),
"Panic": reflect.ValueOf(log.Panic),
"Panicf": reflect.ValueOf(log.Panicf),
"Panicln": reflect.ValueOf(log.Panicln),
"Prefix": reflect.ValueOf(log.Prefix),
"Print": reflect.ValueOf(log.Print),
"Printf": reflect.ValueOf(log.Printf),
"Println": reflect.ValueOf(log.Println),
"SetFlags": reflect.ValueOf(log.SetFlags),
"SetOutput": reflect.ValueOf(log.SetOutput),
"SetPrefix": reflect.ValueOf(log.SetPrefix),
}
}

View file

@ -0,0 +1,32 @@
package packages
import (
"math/big"
"reflect"
"github.com/surdeus/goblin/src/tool/run/env"
)
func init() {
env.Packages["math/big"] = map[string]reflect.Value{
"Above": reflect.ValueOf(big.Above),
"AwayFromZero": reflect.ValueOf(big.AwayFromZero),
"Below": reflect.ValueOf(big.Below),
"Exact": reflect.ValueOf(big.Exact),
"Jacobi": reflect.ValueOf(big.Jacobi),
"MaxBase": reflect.ValueOf(big.MaxBase),
"MaxExp": reflect.ValueOf(big.MaxExp),
// TODO: https://github.com/surdeus/goblin/src/tool/run/issues/49
// "MaxPrec": reflect.ValueOf(big.MaxPrec),
"MinExp": reflect.ValueOf(big.MinExp),
"NewFloat": reflect.ValueOf(big.NewFloat),
"NewInt": reflect.ValueOf(big.NewInt),
"NewRat": reflect.ValueOf(big.NewRat),
"ParseFloat": reflect.ValueOf(big.ParseFloat),
"ToNearestAway": reflect.ValueOf(big.ToNearestAway),
"ToNearestEven": reflect.ValueOf(big.ToNearestEven),
"ToNegativeInf": reflect.ValueOf(big.ToNegativeInf),
"ToPositiveInf": reflect.ValueOf(big.ToPositiveInf),
"ToZero": reflect.ValueOf(big.ToZero),
}
}

View file

@ -0,0 +1,74 @@
package packages
import (
"math"
"reflect"
"github.com/surdeus/goblin/src/tool/run/env"
)
func init() {
env.Packages["math"] = map[string]reflect.Value{
"Abs": reflect.ValueOf(math.Abs),
"Acos": reflect.ValueOf(math.Acos),
"Acosh": reflect.ValueOf(math.Acosh),
"Asin": reflect.ValueOf(math.Asin),
"Asinh": reflect.ValueOf(math.Asinh),
"Atan": reflect.ValueOf(math.Atan),
"Atan2": reflect.ValueOf(math.Atan2),
"Atanh": reflect.ValueOf(math.Atanh),
"Cbrt": reflect.ValueOf(math.Cbrt),
"Ceil": reflect.ValueOf(math.Ceil),
"Copysign": reflect.ValueOf(math.Copysign),
"Cos": reflect.ValueOf(math.Cos),
"Cosh": reflect.ValueOf(math.Cosh),
"Dim": reflect.ValueOf(math.Dim),
"Erf": reflect.ValueOf(math.Erf),
"Erfc": reflect.ValueOf(math.Erfc),
"Exp": reflect.ValueOf(math.Exp),
"Exp2": reflect.ValueOf(math.Exp2),
"Expm1": reflect.ValueOf(math.Expm1),
"Float32bits": reflect.ValueOf(math.Float32bits),
"Float32frombits": reflect.ValueOf(math.Float32frombits),
"Float64bits": reflect.ValueOf(math.Float64bits),
"Float64frombits": reflect.ValueOf(math.Float64frombits),
"Floor": reflect.ValueOf(math.Floor),
"Frexp": reflect.ValueOf(math.Frexp),
"Gamma": reflect.ValueOf(math.Gamma),
"Hypot": reflect.ValueOf(math.Hypot),
"Ilogb": reflect.ValueOf(math.Ilogb),
"Inf": reflect.ValueOf(math.Inf),
"IsInf": reflect.ValueOf(math.IsInf),
"IsNaN": reflect.ValueOf(math.IsNaN),
"J0": reflect.ValueOf(math.J0),
"J1": reflect.ValueOf(math.J1),
"Jn": reflect.ValueOf(math.Jn),
"Ldexp": reflect.ValueOf(math.Ldexp),
"Lgamma": reflect.ValueOf(math.Lgamma),
"Log": reflect.ValueOf(math.Log),
"Log10": reflect.ValueOf(math.Log10),
"Log1p": reflect.ValueOf(math.Log1p),
"Log2": reflect.ValueOf(math.Log2),
"Logb": reflect.ValueOf(math.Logb),
"Max": reflect.ValueOf(math.Max),
"Min": reflect.ValueOf(math.Min),
"Mod": reflect.ValueOf(math.Mod),
"Modf": reflect.ValueOf(math.Modf),
"NaN": reflect.ValueOf(math.NaN),
"Nextafter": reflect.ValueOf(math.Nextafter),
"Pow": reflect.ValueOf(math.Pow),
"Pow10": reflect.ValueOf(math.Pow10),
"Remainder": reflect.ValueOf(math.Remainder),
"Signbit": reflect.ValueOf(math.Signbit),
"Sin": reflect.ValueOf(math.Sin),
"Sincos": reflect.ValueOf(math.Sincos),
"Sinh": reflect.ValueOf(math.Sinh),
"Sqrt": reflect.ValueOf(math.Sqrt),
"Tan": reflect.ValueOf(math.Tan),
"Tanh": reflect.ValueOf(math.Tanh),
"Trunc": reflect.ValueOf(math.Trunc),
"Y0": reflect.ValueOf(math.Y0),
"Y1": reflect.ValueOf(math.Y1),
"Yn": reflect.ValueOf(math.Yn),
}
}

View file

@ -0,0 +1,26 @@
package packages
import (
"math/rand"
"reflect"
"github.com/surdeus/goblin/src/tool/run/env"
)
func init() {
env.Packages["math/rand"] = map[string]reflect.Value{
"ExpFloat64": reflect.ValueOf(rand.ExpFloat64),
"Float32": reflect.ValueOf(rand.Float32),
"Float64": reflect.ValueOf(rand.Float64),
"Int": reflect.ValueOf(rand.Int),
"Int31": reflect.ValueOf(rand.Int31),
"Int31n": reflect.ValueOf(rand.Int31n),
"Int63": reflect.ValueOf(rand.Int63),
"Int63n": reflect.ValueOf(rand.Int63n),
"Intn": reflect.ValueOf(rand.Intn),
"NormFloat64": reflect.ValueOf(rand.NormFloat64),
"Perm": reflect.ValueOf(rand.Perm),
"Seed": reflect.ValueOf(rand.Seed),
"Uint32": reflect.ValueOf(rand.Uint32),
}
}

View file

@ -0,0 +1,76 @@
// +build !appengine
package packages
import (
"net"
"reflect"
"github.com/surdeus/goblin/src/tool/run/env"
)
func init() {
env.Packages["net"] = map[string]reflect.Value{
"CIDRMask": reflect.ValueOf(net.CIDRMask),
"Dial": reflect.ValueOf(net.Dial),
"DialIP": reflect.ValueOf(net.DialIP),
"DialTCP": reflect.ValueOf(net.DialTCP),
"DialTimeout": reflect.ValueOf(net.DialTimeout),
"DialUDP": reflect.ValueOf(net.DialUDP),
"DialUnix": reflect.ValueOf(net.DialUnix),
"ErrWriteToConnected": reflect.ValueOf(net.ErrWriteToConnected),
"FileConn": reflect.ValueOf(net.FileConn),
"FileListener": reflect.ValueOf(net.FileListener),
"FilePacketConn": reflect.ValueOf(net.FilePacketConn),
"FlagBroadcast": reflect.ValueOf(net.FlagBroadcast),
"FlagLoopback": reflect.ValueOf(net.FlagLoopback),
"FlagMulticast": reflect.ValueOf(net.FlagMulticast),
"FlagPointToPoint": reflect.ValueOf(net.FlagPointToPoint),
"FlagUp": reflect.ValueOf(net.FlagUp),
"IPv4": reflect.ValueOf(net.IPv4),
"IPv4Mask": reflect.ValueOf(net.IPv4Mask),
"IPv4allrouter": reflect.ValueOf(net.IPv4allrouter),
"IPv4allsys": reflect.ValueOf(net.IPv4allsys),
"IPv4bcast": reflect.ValueOf(net.IPv4bcast),
"IPv4len": reflect.ValueOf(net.IPv4len),
"IPv4zero": reflect.ValueOf(net.IPv4zero),
"IPv6interfacelocalallnodes": reflect.ValueOf(net.IPv6interfacelocalallnodes),
"IPv6len": reflect.ValueOf(net.IPv6len),
"IPv6linklocalallnodes": reflect.ValueOf(net.IPv6linklocalallnodes),
"IPv6linklocalallrouters": reflect.ValueOf(net.IPv6linklocalallrouters),
"IPv6loopback": reflect.ValueOf(net.IPv6loopback),
"IPv6unspecified": reflect.ValueOf(net.IPv6unspecified),
"IPv6zero": reflect.ValueOf(net.IPv6zero),
"InterfaceAddrs": reflect.ValueOf(net.InterfaceAddrs),
"InterfaceByIndex": reflect.ValueOf(net.InterfaceByIndex),
"InterfaceByName": reflect.ValueOf(net.InterfaceByName),
"Interfaces": reflect.ValueOf(net.Interfaces),
"JoinHostPort": reflect.ValueOf(net.JoinHostPort),
"Listen": reflect.ValueOf(net.Listen),
"ListenIP": reflect.ValueOf(net.ListenIP),
"ListenMulticastUDP": reflect.ValueOf(net.ListenMulticastUDP),
"ListenPacket": reflect.ValueOf(net.ListenPacket),
"ListenTCP": reflect.ValueOf(net.ListenTCP),
"ListenUDP": reflect.ValueOf(net.ListenUDP),
"ListenUnix": reflect.ValueOf(net.ListenUnix),
"ListenUnixgram": reflect.ValueOf(net.ListenUnixgram),
"LookupAddr": reflect.ValueOf(net.LookupAddr),
"LookupCNAME": reflect.ValueOf(net.LookupCNAME),
"LookupHost": reflect.ValueOf(net.LookupHost),
"LookupIP": reflect.ValueOf(net.LookupIP),
"LookupMX": reflect.ValueOf(net.LookupMX),
"LookupNS": reflect.ValueOf(net.LookupNS),
"LookupPort": reflect.ValueOf(net.LookupPort),
"LookupSRV": reflect.ValueOf(net.LookupSRV),
"LookupTXT": reflect.ValueOf(net.LookupTXT),
"ParseCIDR": reflect.ValueOf(net.ParseCIDR),
"ParseIP": reflect.ValueOf(net.ParseIP),
"ParseMAC": reflect.ValueOf(net.ParseMAC),
"Pipe": reflect.ValueOf(net.Pipe),
"ResolveIPAddr": reflect.ValueOf(net.ResolveIPAddr),
"ResolveTCPAddr": reflect.ValueOf(net.ResolveTCPAddr),
"ResolveUDPAddr": reflect.ValueOf(net.ResolveUDPAddr),
"ResolveUnixAddr": reflect.ValueOf(net.ResolveUnixAddr),
"SplitHostPort": reflect.ValueOf(net.SplitHostPort),
}
}

View file

@ -0,0 +1,17 @@
package packages
import (
"net/http/cookiejar"
"reflect"
"github.com/surdeus/goblin/src/tool/run/env"
)
func init() {
env.Packages["net/http/cookiejar"] = map[string]reflect.Value{
"New": reflect.ValueOf(cookiejar.New),
}
env.PackageTypes["net/http/cookiejar"] = map[string]reflect.Type{
"Options": reflect.TypeOf(cookiejar.Options{}),
}
}

View file

@ -0,0 +1,32 @@
// +build !appengine
package packages
import (
"net/http"
"reflect"
"github.com/surdeus/goblin/src/tool/run/env"
)
func init() {
env.Packages["net/http"] = map[string]reflect.Value{
"DefaultClient": reflect.ValueOf(http.DefaultClient),
"DefaultServeMux": reflect.ValueOf(http.DefaultServeMux),
"DefaultTransport": reflect.ValueOf(http.DefaultTransport),
"Handle": reflect.ValueOf(http.Handle),
"HandleFunc": reflect.ValueOf(http.HandleFunc),
"ListenAndServe": reflect.ValueOf(http.ListenAndServe),
"ListenAndServeTLS": reflect.ValueOf(http.ListenAndServeTLS),
"NewRequest": reflect.ValueOf(http.NewRequest),
"NewServeMux": reflect.ValueOf(http.NewServeMux),
"Serve": reflect.ValueOf(http.Serve),
"SetCookie": reflect.ValueOf(http.SetCookie),
}
env.PackageTypes["net/http"] = map[string]reflect.Type{
"Client": reflect.TypeOf(http.Client{}),
"Cookie": reflect.TypeOf(http.Cookie{}),
"Request": reflect.TypeOf(http.Request{}),
"Response": reflect.TypeOf(http.Response{}),
}
}

View file

@ -0,0 +1,27 @@
// +build !appengine
package packages
import (
"net/url"
"reflect"
"github.com/surdeus/goblin/src/tool/run/env"
)
func init() {
env.Packages["net/url"] = map[string]reflect.Value{
"QueryEscape": reflect.ValueOf(url.QueryEscape),
"QueryUnescape": reflect.ValueOf(url.QueryUnescape),
"Parse": reflect.ValueOf(url.Parse),
"ParseRequestURI": reflect.ValueOf(url.ParseRequestURI),
"User": reflect.ValueOf(url.User),
"UserPassword": reflect.ValueOf(url.UserPassword),
"ParseQuery": reflect.ValueOf(url.ParseQuery),
}
env.PackageTypes["net/url"] = map[string]reflect.Type{
"Error": reflect.TypeOf(url.Error{}),
"URL": reflect.TypeOf(url.URL{}),
"Values": reflect.TypeOf(url.Values{}),
}
}

View file

@ -0,0 +1,16 @@
package packages
import (
"os/exec"
"reflect"
"github.com/surdeus/goblin/src/tool/run/env"
)
func init() {
env.Packages["os/exec"] = map[string]reflect.Value{
"ErrNotFound": reflect.ValueOf(exec.ErrNotFound),
"LookPath": reflect.ValueOf(exec.LookPath),
"Command": reflect.ValueOf(exec.Command),
}
}

102
src/tool/run/packages/os.go Normal file
View file

@ -0,0 +1,102 @@
package packages
import (
"os"
"reflect"
"github.com/surdeus/goblin/src/tool/run/env"
)
func init() {
env.Packages["os"] = map[string]reflect.Value{
"Args": reflect.ValueOf(os.Args),
"Chdir": reflect.ValueOf(os.Chdir),
"Chmod": reflect.ValueOf(os.Chmod),
"Chown": reflect.ValueOf(os.Chown),
"Chtimes": reflect.ValueOf(os.Chtimes),
"Clearenv": reflect.ValueOf(os.Clearenv),
"Create": reflect.ValueOf(os.Create),
"DevNull": reflect.ValueOf(os.DevNull),
"Environ": reflect.ValueOf(os.Environ),
"ErrExist": reflect.ValueOf(os.ErrExist),
"ErrInvalid": reflect.ValueOf(os.ErrInvalid),
"ErrNotExist": reflect.ValueOf(os.ErrNotExist),
"ErrPermission": reflect.ValueOf(os.ErrPermission),
"Exit": reflect.ValueOf(os.Exit),
"Expand": reflect.ValueOf(os.Expand),
"ExpandEnv": reflect.ValueOf(os.ExpandEnv),
"FindProcess": reflect.ValueOf(os.FindProcess),
"Getegid": reflect.ValueOf(os.Getegid),
"Getenv": reflect.ValueOf(os.Getenv),
"Geteuid": reflect.ValueOf(os.Geteuid),
"Getgid": reflect.ValueOf(os.Getgid),
"Getgroups": reflect.ValueOf(os.Getgroups),
"Getpagesize": reflect.ValueOf(os.Getpagesize),
"Getpid": reflect.ValueOf(os.Getpid),
"Getuid": reflect.ValueOf(os.Getuid),
"Getwd": reflect.ValueOf(os.Getwd),
"Hostname": reflect.ValueOf(os.Hostname),
"Interrupt": reflect.ValueOf(os.Interrupt),
"IsExist": reflect.ValueOf(os.IsExist),
"IsNotExist": reflect.ValueOf(os.IsNotExist),
"IsPathSeparator": reflect.ValueOf(os.IsPathSeparator),
"IsPermission": reflect.ValueOf(os.IsPermission),
"Kill": reflect.ValueOf(os.Kill),
"Lchown": reflect.ValueOf(os.Lchown),
"Link": reflect.ValueOf(os.Link),
"Lstat": reflect.ValueOf(os.Lstat),
"Mkdir": reflect.ValueOf(os.Mkdir),
"MkdirAll": reflect.ValueOf(os.MkdirAll),
"ModeAppend": reflect.ValueOf(os.ModeAppend),
"ModeCharDevice": reflect.ValueOf(os.ModeCharDevice),
"ModeDevice": reflect.ValueOf(os.ModeDevice),
"ModeDir": reflect.ValueOf(os.ModeDir),
"ModeExclusive": reflect.ValueOf(os.ModeExclusive),
"ModeNamedPipe": reflect.ValueOf(os.ModeNamedPipe),
"ModePerm": reflect.ValueOf(os.ModePerm),
"ModeSetgid": reflect.ValueOf(os.ModeSetgid),
"ModeSetuid": reflect.ValueOf(os.ModeSetuid),
"ModeSocket": reflect.ValueOf(os.ModeSocket),
"ModeSticky": reflect.ValueOf(os.ModeSticky),
"ModeSymlink": reflect.ValueOf(os.ModeSymlink),
"ModeTemporary": reflect.ValueOf(os.ModeTemporary),
"ModeType": reflect.ValueOf(os.ModeType),
"NewFile": reflect.ValueOf(os.NewFile),
"NewSyscallError": reflect.ValueOf(os.NewSyscallError),
"O_APPEND": reflect.ValueOf(os.O_APPEND),
"O_CREATE": reflect.ValueOf(os.O_CREATE),
"O_EXCL": reflect.ValueOf(os.O_EXCL),
"O_RDONLY": reflect.ValueOf(os.O_RDONLY),
"O_RDWR": reflect.ValueOf(os.O_RDWR),
"O_SYNC": reflect.ValueOf(os.O_SYNC),
"O_TRUNC": reflect.ValueOf(os.O_TRUNC),
"O_WRONLY": reflect.ValueOf(os.O_WRONLY),
"Open": reflect.ValueOf(os.Open),
"OpenFile": reflect.ValueOf(os.OpenFile),
"PathListSeparator": reflect.ValueOf(os.PathListSeparator),
"PathSeparator": reflect.ValueOf(os.PathSeparator),
"Pipe": reflect.ValueOf(os.Pipe),
"Readlink": reflect.ValueOf(os.Readlink),
"Remove": reflect.ValueOf(os.Remove),
"RemoveAll": reflect.ValueOf(os.RemoveAll),
"Rename": reflect.ValueOf(os.Rename),
"SEEK_CUR": reflect.ValueOf(os.SEEK_CUR),
"SEEK_END": reflect.ValueOf(os.SEEK_END),
"SEEK_SET": reflect.ValueOf(os.SEEK_SET),
"SameFile": reflect.ValueOf(os.SameFile),
"Setenv": reflect.ValueOf(os.Setenv),
"StartProcess": reflect.ValueOf(os.StartProcess),
"Stat": reflect.ValueOf(os.Stat),
"Stderr": reflect.ValueOf(os.Stderr),
"Stdin": reflect.ValueOf(os.Stdin),
"Stdout": reflect.ValueOf(os.Stdout),
"Symlink": reflect.ValueOf(os.Symlink),
"TempDir": reflect.ValueOf(os.TempDir),
"Truncate": reflect.ValueOf(os.Truncate),
}
var signal os.Signal
env.PackageTypes["os"] = map[string]reflect.Type{
"Signal": reflect.TypeOf(&signal).Elem(),
}
osNotAppEngine()
}

View file

@ -0,0 +1,15 @@
package packages
import (
"os/signal"
"reflect"
"github.com/surdeus/goblin/src/tool/run/env"
)
func init() {
env.Packages["os/signal"] = map[string]reflect.Value{
"Notify": reflect.ValueOf(signal.Notify),
"Stop": reflect.ValueOf(signal.Stop),
}
}

View file

@ -0,0 +1,5 @@
// +build appengine
package packages
func osNotAppEngine() {}

View file

@ -0,0 +1,14 @@
// +build !appengine
package packages
import (
"os"
"reflect"
"github.com/surdeus/goblin/src/tool/run/env"
)
func osNotAppEngine() {
env.Packages["os"]["Getppid"] = reflect.ValueOf(os.Getppid)
}

View file

@ -0,0 +1,30 @@
package packages
import (
"path/filepath"
"reflect"
"github.com/surdeus/goblin/src/tool/run/env"
)
func init() {
env.Packages["path/filepath"] = map[string]reflect.Value{
"Abs": reflect.ValueOf(filepath.Abs),
"Base": reflect.ValueOf(filepath.Base),
"Clean": reflect.ValueOf(filepath.Clean),
"Dir": reflect.ValueOf(filepath.Dir),
"EvalSymlinks": reflect.ValueOf(filepath.EvalSymlinks),
"Ext": reflect.ValueOf(filepath.Ext),
"FromSlash": reflect.ValueOf(filepath.FromSlash),
"Glob": reflect.ValueOf(filepath.Glob),
"HasPrefix": reflect.ValueOf(filepath.HasPrefix),
"IsAbs": reflect.ValueOf(filepath.IsAbs),
"Join": reflect.ValueOf(filepath.Join),
"Match": reflect.ValueOf(filepath.Match),
"Rel": reflect.ValueOf(filepath.Rel),
"Split": reflect.ValueOf(filepath.Split),
"SplitList": reflect.ValueOf(filepath.SplitList),
"ToSlash": reflect.ValueOf(filepath.ToSlash),
"VolumeName": reflect.ValueOf(filepath.VolumeName),
}
}

View file

@ -0,0 +1,22 @@
package packages
import (
"path"
"reflect"
"github.com/surdeus/goblin/src/tool/run/env"
)
func init() {
env.Packages["path"] = map[string]reflect.Value{
"Base": reflect.ValueOf(path.Base),
"Clean": reflect.ValueOf(path.Clean),
"Dir": reflect.ValueOf(path.Dir),
"ErrBadPattern": reflect.ValueOf(path.ErrBadPattern),
"Ext": reflect.ValueOf(path.Ext),
"IsAbs": reflect.ValueOf(path.IsAbs),
"Join": reflect.ValueOf(path.Join),
"Match": reflect.ValueOf(path.Match),
"Split": reflect.ValueOf(path.Split),
}
}

View file

@ -0,0 +1,21 @@
package packages
import (
"reflect"
"regexp"
"github.com/surdeus/goblin/src/tool/run/env"
)
func init() {
env.Packages["regexp"] = map[string]reflect.Value{
"Match": reflect.ValueOf(regexp.Match),
"MatchReader": reflect.ValueOf(regexp.MatchReader),
"MatchString": reflect.ValueOf(regexp.MatchString),
"QuoteMeta": reflect.ValueOf(regexp.QuoteMeta),
"Compile": reflect.ValueOf(regexp.Compile),
"CompilePOSIX": reflect.ValueOf(regexp.CompilePOSIX),
"MustCompile": reflect.ValueOf(regexp.MustCompile),
"MustCompilePOSIX": reflect.ValueOf(regexp.MustCompilePOSIX),
}
}

View file

@ -0,0 +1,19 @@
package packages
import (
"reflect"
"runtime"
"github.com/surdeus/goblin/src/tool/run/env"
)
func init() {
env.Packages["runtime"] = map[string]reflect.Value{
"GC": reflect.ValueOf(runtime.GC),
"GOARCH": reflect.ValueOf(runtime.GOARCH),
"GOMAXPROCS": reflect.ValueOf(runtime.GOMAXPROCS),
"GOOS": reflect.ValueOf(runtime.GOOS),
"GOROOT": reflect.ValueOf(runtime.GOROOT),
"Version": reflect.ValueOf(runtime.Version),
}
}

View file

@ -0,0 +1,44 @@
package packages
import (
"reflect"
"sort"
"github.com/surdeus/goblin/src/tool/run/env"
)
// SortFuncsStruct provides functions to be used with Sort
type SortFuncsStruct struct {
LenFunc func() int
LessFunc func(i, j int) bool
SwapFunc func(i, j int)
}
func (s SortFuncsStruct) Len() int { return s.LenFunc() }
func (s SortFuncsStruct) Less(i, j int) bool { return s.LessFunc(i, j) }
func (s SortFuncsStruct) Swap(i, j int) { s.SwapFunc(i, j) }
func init() {
env.Packages["sort"] = map[string]reflect.Value{
"Float64s": reflect.ValueOf(sort.Float64s),
"Float64sAreSorted": reflect.ValueOf(sort.Float64sAreSorted),
"Ints": reflect.ValueOf(sort.Ints),
"IntsAreSorted": reflect.ValueOf(sort.IntsAreSorted),
"IsSorted": reflect.ValueOf(sort.IsSorted),
"Search": reflect.ValueOf(sort.Search),
"SearchFloat64s": reflect.ValueOf(sort.SearchFloat64s),
"SearchInts": reflect.ValueOf(sort.SearchInts),
"SearchStrings": reflect.ValueOf(sort.SearchStrings),
"Sort": reflect.ValueOf(sort.Sort),
"Stable": reflect.ValueOf(sort.Stable),
"Strings": reflect.ValueOf(sort.Strings),
"StringsAreSorted": reflect.ValueOf(sort.StringsAreSorted),
}
env.PackageTypes["sort"] = map[string]reflect.Type{
"Float64Slice": reflect.TypeOf(sort.Float64Slice{}),
"IntSlice": reflect.TypeOf(sort.IntSlice{}),
"StringSlice": reflect.TypeOf(sort.StringSlice{}),
"SortFuncsStruct": reflect.TypeOf(&SortFuncsStruct{}),
}
sortGo18()
}

View file

@ -0,0 +1,16 @@
// +build go1.8
package packages
import (
"reflect"
"sort"
"github.com/surdeus/goblin/src/tool/run/env"
)
func sortGo18() {
env.Packages["sort"]["Slice"] = reflect.ValueOf(sort.Slice)
env.Packages["sort"]["SliceIsSorted"] = reflect.ValueOf(sort.SliceIsSorted)
env.Packages["sort"]["SliceStable"] = reflect.ValueOf(sort.SliceStable)
}

View file

@ -0,0 +1,5 @@
// +build !go1.8
package packages
func sortGo18() {}

View file

@ -0,0 +1,23 @@
package packages
import (
"reflect"
"strconv"
"github.com/surdeus/goblin/src/tool/run/env"
)
func init() {
env.Packages["strconv"] = map[string]reflect.Value{
"FormatBool": reflect.ValueOf(strconv.FormatBool),
"FormatFloat": reflect.ValueOf(strconv.FormatFloat),
"FormatInt": reflect.ValueOf(strconv.FormatInt),
"FormatUint": reflect.ValueOf(strconv.FormatUint),
"ParseBool": reflect.ValueOf(strconv.ParseBool),
"ParseFloat": reflect.ValueOf(strconv.ParseFloat),
"ParseInt": reflect.ValueOf(strconv.ParseInt),
"ParseUint": reflect.ValueOf(strconv.ParseUint),
"Atoi": reflect.ValueOf(strconv.Atoi),
"Itoa": reflect.ValueOf(strconv.Itoa),
}
}

Some files were not shown because too many files have changed in this diff Show more