From a81295c0affb1de35f2b3f980be9b8da41b33f3e Mon Sep 17 00:00:00 2001 From: Daniel Kang Date: Sat, 12 Jan 2019 22:58:12 -0800 Subject: [PATCH] update README, remove some unnecessary code --- README.md | 130 +++++++++++++++++++++++++++++++++++++++++++- cmd/example/main.go | 20 ------- cmd/tengo/main.go | 36 ++++++++---- tengo.go | 30 ---------- 4 files changed, 154 insertions(+), 62 deletions(-) delete mode 100644 cmd/example/main.go diff --git a/README.md b/README.md index 527a98a..2c0213c 100644 --- a/README.md +++ b/README.md @@ -193,7 +193,7 @@ for k, v in {k1: 1, k2: 2} { // map: key and value ## Tengo in Go -To embed and execute Tengo code in your Go codebase, ... +To execute Tengo code in your Go codebase, you should use **Script**. In the simple use cases, all you need is to do is to create a new Script instance and call its `Script.Run()` function like this: ```golang import "github.com/d5/tengo/script" @@ -216,7 +216,133 @@ func main() { } ``` -... +If you want to compile the source script and execute it multiple times, consider using `Script.Compile()` function that returns `Compiled` instance. + +```golang +import ( + "fmt" + + "github.com/d5/tengo/script" +) + +func main() { + s := script.New([]byte(`a := b + 20`)) + + // define variable 'b' + _ = s.Add("b", 10) + + // compile the source + c, err := s.Compile() + if err != nil { + panic(err) + } + + // run the compiled bytecode + // a compiled bytecode can be executed multiple without re-compiling it + if err := c.Run(); err != nil { + panic(err) + } + + // retrieve value of 'a' + a := c.Get("a") + fmt.Println(a.Int()) +} +``` + +In the example above, a variable `b` is defined by the user using `Script.Add()` function. Then a compiled bytecode (created by `Script.Compile()`) is used to execute the code and get the value of global variables, like `a` in this example. + +If you want to use your own data type (outside Tengo's primitive types), you can create your `struct` that implements `objects.Object` interface _(and `objects.Callable` if you want to make function-like invokable objects)_. + +```golang +import ( + "errors" + "fmt" + + "github.com/d5/tengo/compiler/token" + "github.com/d5/tengo/objects" + "github.com/d5/tengo/script" +) + +type Counter struct { + value int64 +} + +func (o *Counter) TypeName() string { + return "counter" +} + +func (o *Counter) String() string { + return fmt.Sprintf("Counter(%d)", o.value) +} + +func (o *Counter) BinaryOp(op token.Token, rhs objects.Object) (objects.Object, error) { + switch rhs := rhs.(type) { + case *Counter: + switch op { + case token.Add: + return &Counter{value: o.value + rhs.value}, nil + case token.Sub: + return &Counter{value: o.value - rhs.value}, nil + } + case *objects.Int: + switch op { + case token.Add: + return &Counter{value: o.value + rhs.Value}, nil + case token.Sub: + return &Counter{value: o.value - rhs.Value}, nil + } + } + + return nil, errors.New("invalid operator") +} + +func (o *Counter) IsFalsy() bool { + return o.value == 0 +} + +func (o *Counter) Equals(t objects.Object) bool { + if tc, ok := t.(*Counter); ok { + return o.value == tc.value + } + + return false +} + +func (o *Counter) Copy() objects.Object { + return &Counter{value: o.value} +} + +func (o *Counter) Call(args ...objects.Object) (objects.Object, error) { + return &objects.Int{Value: o.value}, nil +} + +var code = []byte(` +arr := [1, 2, 3, 4] +for x in arr { + c1 += x +} +out := c1()`) + +func main() { + s := script.New(code) + + // define variable 'c1' + _ = s.Add("c1", &Counter{value: 5}) + + // compile the source + c, err := s.Run() + if err != nil { + panic(err) + } + + // retrieve value of 'out' + out := c.Get("out") + fmt.Println(out.Int()) // prints "15" ( = 5 + (1 + 2 + 3 + 4) ) +} + +``` + +Alternatively, you can directly create and interact with the parser, compiler and VMs directly. There's no good documentations for them, but, you can look at Script code to see how they work each other. ## Tengo Standalone diff --git a/cmd/example/main.go b/cmd/example/main.go deleted file mode 100644 index 85c0ddb..0000000 --- a/cmd/example/main.go +++ /dev/null @@ -1,20 +0,0 @@ -package main - -import "github.com/d5/tengo/script" - -var code = ` -reduce := func(seq, fn) { - s := 0 - for x in seq { fn(x, s) } - return s -} - -print(reduce([1, 2, 3], func(x, s) { s += x })) -` - -func main() { - s := script.New([]byte(code)) - if _, err := s.Run(); err != nil { - panic(err) - } -} diff --git a/cmd/tengo/main.go b/cmd/tengo/main.go index c3dca29..acf43c2 100644 --- a/cmd/tengo/main.go +++ b/cmd/tengo/main.go @@ -11,7 +11,6 @@ import ( "path/filepath" "strings" - "github.com/d5/tengo" "github.com/d5/tengo/compiler" "github.com/d5/tengo/compiler/ast" "github.com/d5/tengo/compiler/parser" @@ -78,12 +77,12 @@ func main() { func doHelp() { fmt.Println("Usage:") fmt.Println() - fmt.Println(" tengo [flags] [input-file]") + fmt.Println(" tengo [flags] {input-file}") fmt.Println() fmt.Println("Flags:") fmt.Println() - fmt.Println(" -compile/-c compile source file") - fmt.Println(" -o output file") + fmt.Println(" -c/-compile compile the input and produce bytecode file") + fmt.Println(" -o output") fmt.Println() fmt.Println("Examples:") fmt.Println() @@ -91,19 +90,19 @@ func doHelp() { fmt.Println(" : Start Tengo REPL") fmt.Println() fmt.Println(" tengo myapp.tengo") - fmt.Println(" : Compile and execute a Tengo code (myapp.tengo)") + fmt.Println(" : Compile and execute source file (myapp.tengo)") fmt.Println() - fmt.Println(" tengo -c -o myapp myapp.tengo") - fmt.Println(" : Compile a Tengo code (myapp.tengo) into compiled binary (myapp)") + fmt.Println(" tengo -c myapp myapp.tengo") + fmt.Println(" : Compile source file (myapp.tengo) and produce bytecode file (myapp)") fmt.Println() fmt.Println(" tengo myapp") - fmt.Println(" : Execute compiled binary (myapp)") + fmt.Println(" : Execute bytecode file (myapp)") fmt.Println() fmt.Println() } func doCompile(data []byte, inputFile, outputFile string) (err error) { - bytecode, err := tengo.Compile(data, filepath.Base(inputFile)) + bytecode, err := compileSrc(data, filepath.Base(inputFile)) if err != nil { return } @@ -135,7 +134,7 @@ func doCompile(data []byte, inputFile, outputFile string) (err error) { } func doCompileRun(data []byte, inputFile, _ string) (err error) { - bytecode, err := tengo.Compile(data, filepath.Base(inputFile)) + bytecode, err := compileSrc(data, filepath.Base(inputFile)) if err != nil { return } @@ -210,6 +209,23 @@ func doRepl(in io.Reader, out io.Writer) { } } +func compileSrc(src []byte, filename string) (*compiler.Bytecode, error) { + fileSet := source.NewFileSet() + + p := parser.NewParser(fileSet.AddFile(filename, -1, len(src)), src, nil) + file, err := p.ParseFile() + if err != nil { + return nil, err + } + + c := compiler.NewCompiler(nil, nil) + if err := c.Compile(file); err != nil { + return nil, err + } + + return c.Bytecode(), nil +} + func addPrints(file *ast.File) *ast.File { var stmts []ast.Stmt for _, s := range file.Stmts { diff --git a/tengo.go b/tengo.go index 58846e5..f3bf76e 100644 --- a/tengo.go +++ b/tengo.go @@ -1,31 +1 @@ package tengo - -import ( - "github.com/d5/tengo/compiler" - "github.com/d5/tengo/compiler/parser" - "github.com/d5/tengo/compiler/source" - "github.com/d5/tengo/runtime" -) - -func Compile(input []byte, filename string) (*compiler.Bytecode, error) { - fileSet := source.NewFileSet() - - p := parser.NewParser(fileSet.AddFile(filename, -1, len(input)), input, nil) - file, err := p.ParseFile() - if err != nil { - return nil, err - } - - c := compiler.NewCompiler(nil, nil) - if err := c.Compile(file); err != nil { - return nil, err - } - - return c.Bytecode(), nil -} - -func Run(b *compiler.Bytecode) error { - v := runtime.NewVM(b, nil) - - return v.Run() -}