Merge pull request #72 from d5/compiledset

replacing values of the compiled script
This commit is contained in:
Daniel 2019-02-01 11:32:07 -08:00 committed by GitHub
commit 0d99cca738
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 70 additions and 1 deletions

View file

@ -70,12 +70,23 @@ func main() {
// retrieve value of 'a'
a := c.Get("a")
fmt.Println(a.Int())
fmt.Println(a.Int()) // prints "30"
// re-run after replacing value of 'b'
if err := c.Set("b", 20); err != nil {
panic(err)
}
if err := c.Run(); err != nil {
panic(err)
}
fmt.Println(c.Get("a").Int()) // prints "40"
}
```
A variable `b` is defined by the user before compilation using [Script.Add](https://godoc.org/github.com/d5/tengo/script#Script.Add) function. Then a compiled bytecode `c` is used to execute the bytecode and get the value of global variables. In this example, the value of global variable `a` is read using [Compiled.Get](https://godoc.org/github.com/d5/tengo/script#Compiled.Get) function. See [documentation](https://godoc.org/github.com/d5/tengo/script#Variable) for the full list of variable value functions.
Value of the global variables can be replaced using [Compiled.Set](https://godoc.org/github.com/d5/tengo/script#Compiled.Set) function. But it will return an error if you try to set the value of un-defined global variables _(e.g. trying to set the value of `x` in the example)_.
### Type Conversion Table
When adding a Variable _([Script.Add](https://godoc.org/github.com/d5/tengo/script#Script.Add))_, Script converts Go values into Tengo values based on the following conversion table.

View file

@ -80,6 +80,15 @@ func (v *VM) Abort() {
// Run starts the execution.
func (v *VM) Run() error {
// reset VM states
v.sp = 0
v.curFrame = &(v.frames[0])
v.curInsts = v.curFrame.fn.Instructions
v.curIPLimit = len(v.curInsts) - 1
v.framesIndex = 1
v.ip = -1
atomic.StoreInt64(&v.aborting, 0)
for v.ip < v.curIPLimit && (atomic.LoadInt64(&v.aborting) == 0) {
v.ip++

View file

@ -2,6 +2,7 @@ package script
import (
"context"
"fmt"
"github.com/d5/tengo/compiler"
"github.com/d5/tengo/objects"
@ -92,3 +93,21 @@ func (c *Compiled) GetAll() []*Variable {
return vars
}
// Set replaces the value of a global variable identified by the name.
// An error will be returned if the name was not defined during compilation.
func (c *Compiled) Set(name string, value interface{}) error {
obj, err := objects.FromInterface(value)
if err != nil {
return err
}
symbol, _, ok := c.symbolTable.Resolve(name)
if !ok || symbol.Scope != compiler.ScopeGlobal {
return fmt.Errorf("'%s' is not defined", name)
}
c.machine.Globals()[symbol.Index] = &obj
return nil
}

View file

@ -47,6 +47,36 @@ func TestCompiled_IsDefined(t *testing.T) {
compiledIsDefined(t, c, "b", false)
}
func TestCompiled_Set(t *testing.T) {
c := compile(t, `a := b`, M{"b": "foo"})
compiledRun(t, c)
compiledGet(t, c, "a", "foo")
// replace value of 'b'
err := c.Set("b", "bar")
assert.NoError(t, err)
compiledRun(t, c)
compiledGet(t, c, "a", "bar")
// try to replace undefined variable
err = c.Set("c", 1984)
assert.Error(t, err) // 'c' is not defined
// case #2
c = compile(t, `
a := func() {
return func() {
return b + 5
}()
}()`, M{"b": 5})
compiledRun(t, c)
compiledGet(t, c, "a", int64(10))
err = c.Set("b", 10)
assert.NoError(t, err)
compiledRun(t, c)
compiledGet(t, c, "a", int64(15))
}
func TestCompiled_RunContext(t *testing.T) {
// machine completes normally
c := compile(t, `a := 5`, nil)