xgo/compiler/compiler_assign.go
Daniel Kang b79fd4f7ef
Fix lint issues (#2)
* addressing golint issues

* fix all lint issues.
2019-01-14 22:24:33 -08:00

151 lines
3.2 KiB
Go

package compiler
import (
"errors"
"fmt"
"github.com/d5/tengo/compiler/ast"
"github.com/d5/tengo/compiler/token"
)
func (c *Compiler) compileAssign(lhs, rhs []ast.Expr, op token.Token) error {
numLHS, numRHS := len(lhs), len(rhs)
if numLHS < numRHS {
// # of LHS must be >= # of RHS
return fmt.Errorf("assigntment count error: %d < %d", numLHS, numRHS)
}
if numLHS > 1 {
// TODO: until we fully implement the tuple assignment
return fmt.Errorf("tuple assignment not implemented")
}
//if numLHS > 1 && op != token.Assign && op != token.Define {
// return fmt.Errorf("invalid operator for tuple assignment: %s", op.String())
//}
// resolve and compile left-hand side
ident, selectors, err := resolveAssignLHS(lhs[0])
if err != nil {
return err
}
numSel := len(selectors)
if op == token.Define && numSel > 0 {
// using selector on new variable does not make sense
return errors.New("cannot use selector with ':='")
}
symbol, depth, exists := c.symbolTable.Resolve(ident)
if op == token.Define {
if depth == 0 && exists {
return fmt.Errorf("'%s' redeclared in this block", ident)
}
symbol = c.symbolTable.Define(ident)
} else {
if !exists {
return fmt.Errorf("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(OpAdd)
case token.SubAssign:
c.emit(OpSub)
case token.MulAssign:
c.emit(OpMul)
case token.QuoAssign:
c.emit(OpDiv)
case token.RemAssign:
c.emit(OpRem)
case token.AndAssign:
c.emit(OpBAnd)
case token.OrAssign:
c.emit(OpBOr)
case token.AndNotAssign:
c.emit(OpBAndNot)
case token.XorAssign:
c.emit(OpBXor)
case token.ShlAssign:
c.emit(OpBShiftLeft)
case token.ShrAssign:
c.emit(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(OpSetSelGlobal, symbol.Index, numSel)
} else {
c.emit(OpSetGlobal, symbol.Index)
}
case ScopeLocal:
if numSel > 0 {
c.emit(OpSetSelLocal, symbol.Index, numSel)
} else {
if op == token.Define {
c.emit(OpDefineLocal, symbol.Index)
} else {
c.emit(OpSetLocal, symbol.Index)
}
}
case ScopeFree:
if numSel > 0 {
c.emit(OpSetSelFree, symbol.Index, numSel)
} else {
c.emit(OpSetFree, symbol.Index)
}
default:
return fmt.Errorf("invalid assignment variable scope: %s", symbol.Scope)
}
return nil
}
func resolveAssignLHS(expr ast.Expr) (name string, selectors []ast.Expr, err error) {
switch term := expr.(type) {
case *ast.SelectorExpr:
name, selectors, err = resolveAssignLHS(term.Expr)
if err != nil {
return
}
selectors = append(selectors, term.Sel)
return
case *ast.IndexExpr:
name, selectors, err = resolveAssignLHS(term.Expr)
if err != nil {
return
}
selectors = append(selectors, term.Index)
case *ast.Ident:
return term.Name, nil, nil
}
return
}