summaryrefslogtreecommitdiffstats
path: root/vendor/github.com/d5/tengo/compiler/compiler_assign.go
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/github.com/d5/tengo/compiler/compiler_assign.go')
-rw-r--r--vendor/github.com/d5/tengo/compiler/compiler_assign.go133
1 files changed, 133 insertions, 0 deletions
diff --git a/vendor/github.com/d5/tengo/compiler/compiler_assign.go b/vendor/github.com/d5/tengo/compiler/compiler_assign.go
new file mode 100644
index 00000000..0e086c83
--- /dev/null
+++ b/vendor/github.com/d5/tengo/compiler/compiler_assign.go
@@ -0,0 +1,133 @@
+package compiler
+
+import (
+ "fmt"
+
+ "github.com/d5/tengo/compiler/ast"
+ "github.com/d5/tengo/compiler/token"
+)
+
+func (c *Compiler) compileAssign(node ast.Node, lhs, rhs []ast.Expr, op token.Token) error {
+ numLHS, numRHS := len(lhs), len(rhs)
+ if numLHS > 1 || numRHS > 1 {
+ return c.errorf(node, "tuple assignment not allowed")
+ }
+
+ // resolve and compile left-hand side
+ ident, selectors := resolveAssignLHS(lhs[0])
+ numSel := len(selectors)
+
+ if op == token.Define && numSel > 0 {
+ // using selector on new variable does not make sense
+ return c.errorf(node, "operator ':=' not allowed with selector")
+ }
+
+ symbol, depth, exists := c.symbolTable.Resolve(ident)
+ if op == token.Define {
+ if depth == 0 && exists {
+ return c.errorf(node, "'%s' redeclared in this block", ident)
+ }
+
+ symbol = c.symbolTable.Define(ident)
+ } else {
+ if !exists {
+ return c.errorf(node, "unresolved reference '%s'", ident)
+ }
+ }
+
+ // +=, -=, *=, /=
+ if op != token.Assign && op != token.Define {
+ if err := c.Compile(lhs[0]); err != nil {
+ return err
+ }
+ }
+
+ // compile RHSs
+ for _, expr := range rhs {
+ if err := c.Compile(expr); err != nil {
+ return err
+ }
+ }
+
+ switch op {
+ case token.AddAssign:
+ c.emit(node, OpAdd)
+ case token.SubAssign:
+ c.emit(node, OpSub)
+ case token.MulAssign:
+ c.emit(node, OpMul)
+ case token.QuoAssign:
+ c.emit(node, OpDiv)
+ case token.RemAssign:
+ c.emit(node, OpRem)
+ case token.AndAssign:
+ c.emit(node, OpBAnd)
+ case token.OrAssign:
+ c.emit(node, OpBOr)
+ case token.AndNotAssign:
+ c.emit(node, OpBAndNot)
+ case token.XorAssign:
+ c.emit(node, OpBXor)
+ case token.ShlAssign:
+ c.emit(node, OpBShiftLeft)
+ case token.ShrAssign:
+ c.emit(node, OpBShiftRight)
+ }
+
+ // compile selector expressions (right to left)
+ for i := numSel - 1; i >= 0; i-- {
+ if err := c.Compile(selectors[i]); err != nil {
+ return err
+ }
+ }
+
+ switch symbol.Scope {
+ case ScopeGlobal:
+ if numSel > 0 {
+ c.emit(node, OpSetSelGlobal, symbol.Index, numSel)
+ } else {
+ c.emit(node, OpSetGlobal, symbol.Index)
+ }
+ case ScopeLocal:
+ if numSel > 0 {
+ c.emit(node, OpSetSelLocal, symbol.Index, numSel)
+ } else {
+ if op == token.Define && !symbol.LocalAssigned {
+ c.emit(node, OpDefineLocal, symbol.Index)
+ } else {
+ c.emit(node, OpSetLocal, symbol.Index)
+ }
+ }
+
+ // mark the symbol as local-assigned
+ symbol.LocalAssigned = true
+ case ScopeFree:
+ if numSel > 0 {
+ c.emit(node, OpSetSelFree, symbol.Index, numSel)
+ } else {
+ c.emit(node, OpSetFree, symbol.Index)
+ }
+ default:
+ panic(fmt.Errorf("invalid assignment variable scope: %s", symbol.Scope))
+ }
+
+ return nil
+}
+
+func resolveAssignLHS(expr ast.Expr) (name string, selectors []ast.Expr) {
+ switch term := expr.(type) {
+ case *ast.SelectorExpr:
+ name, selectors = resolveAssignLHS(term.Expr)
+ selectors = append(selectors, term.Sel)
+ return
+
+ case *ast.IndexExpr:
+ name, selectors = resolveAssignLHS(term.Expr)
+ selectors = append(selectors, term.Index)
+
+ case *ast.Ident:
+ name = term.Name
+ }
+
+ return
+}