func (tc *typechecker) checkIncDecStatement(node *ast.Assignment) {
	if node.Type != ast.AssignmentIncrement && node.Type != ast.AssignmentDecrement {
		panic(internalError("expected an IncDec statement"))
	}
	lh := tc.checkExpr(node.Lhs[0])
	tc.checkAssignTo(lh, node.Lhs[0])
	if !isNumeric(lh.Type.Kind()) {
		panic(tc.errorf(node, "invalid operation: %s (non-numeric type %s)", node, lh))
	}
	if node.Type == ast.AssignmentIncrement {
		node.Type = ast.AssignmentAddition
	} else {
		node.Type = ast.AssignmentSubtraction
	}
	rhExpr := ast.NewBasicLiteral(nil, ast.IntLiteral, "1")
	rh := tc.checkExpr(rhExpr)
	rh.setValue(lh.Type)
	node.Rhs = append(node.Rhs, rhExpr)
}
checkIncDecStatement checks an IncDec statement.
package compiler
import (
	"reflect"
	"strings"
	"github.com/open2b/scriggo/ast"
)
type typeInfoPair [2]*typeInfo
func (p typeInfoPair) TypeInfo() *typeInfo {
	if p[0] == nil {
		return p[1]
	}
	return p[0]
}
func (tc *typechecker) checkAssignment(node *ast.Assignment) {
	if node.Type != ast.AssignmentSimple {
		panic(internalError("expected an assignment node with operator '='"))
	}
	nodeRhs := node.Rhs
	if len(node.Lhs) != len(nodeRhs) {
		nodeRhs = tc.rebalancedRightSide(node)
	}
	lhs := make([]*typeInfo, len(node.Lhs))
	for i, lhExpr := range node.Lhs {
		if isBlankIdentifier(lhExpr) {
			continue
		}
		var lh *typeInfo
		if ident, ok := lhExpr.(*ast.Identifier); ok {
			lh = tc.checkIdentifier(ident, false)  
		} else {
			lh = tc.checkExpr(lhExpr)
		}
		tc.checkAssignTo(lh, lhExpr)
		lhs[i] = lh
	}
	rhs := make([]typeInfoPair, len(nodeRhs))
	for i, expr := range nodeRhs {
		rhs[i] = tc.checkExpr2(expr, false)
	}
	for i, rh := range rhs {
		for j, ti := range rh {
			if ti == nil {
				continue
			}
			if isBlankIdentifier(node.Lhs[i]) {
				if ti.Nil() {  
					panic(tc.errorf(node.Lhs[i], "use of untyped nil"))
				}
				if ti.Untyped() {
					tc.mustBeAssignableTo(ti, subExpr(nodeRhs[i], j == 0), ti.Type, false, nil)
				}
				continue
			}
			tc.mustBeAssignableTo(ti, subExpr(nodeRhs[i], j == 0), lhs[i].Type, len(node.Lhs) != len(node.Rhs), node.Lhs[i])
		}
		ti := rh.TypeInfo()
		if isBlankIdentifier(node.Lhs[i]) {
			ti.setValue(nil)
			continue
		}
		if ti.Nil() {
			tc.compilation.typeInfos[nodeRhs[i]] = tc.nilOf(lhs[i].Type)
		} else {
			ti.setValue(nil)
		}
	}
}
func (tc *typechecker) checkAssignmentOperation(node *ast.Assignment) {
	if !(ast.AssignmentAddition <= node.Type && node.Type <= ast.AssignmentRightShift) {
		panic(internalError("expected an assignment operation"))
	}
	lh := tc.checkExpr(node.Lhs[0])
	rh := tc.checkExpr(node.Rhs[0])
	tc.checkAssignTo(lh, node.Lhs[0])
	op := operatorFromAssignmentType(node.Type)
	_, err := tc.binaryOp(node.Lhs[0], op, node.Rhs[0])
	if err != nil {
		if err == errDivisionByZero {
			panic(tc.errorf(node, "%s", err))
		}
		panic(tc.errorf(node, "invalid operation: %s (%s)", node, err))
	}
	rh.setValue(lh.Type)
}
func (tc *typechecker) checkBalancedConstantDeclaration(node *ast.Const) {
	if len(node.Lhs) > len(node.Rhs) {
		pos := node.Lhs[0].Pos()
		if len(node.Rhs) > 0 {
			pos = node.Rhs[len(node.Rhs)-1].Pos()
		}
		panic(tc.errorf(pos, "missing value in const declaration"))
	}
	if len(node.Lhs) < len(node.Rhs) {
		panic(tc.errorf(node.Rhs[len(node.Rhs)-1], "extra expression in const declaration"))
	}
}
func (tc *typechecker) checkConstantDeclaration(node *ast.Const) {
	tc.checkBalancedConstantDeclaration(node)
	tc.iota = node.Index
	rhs := make([]typeInfoPair, len(node.Rhs))
	for i, rhExpr := range node.Rhs {
		rh := tc.checkExpr2(rhExpr, false)
		for j, ti := range rh {
			if ti != nil && !ti.IsConstant() {
				if ti.Nil() {
					panic(tc.errorf(node.Lhs[i], "const initializer cannot be nil"))
				}
				panic(tc.errorf(node.Lhs[i], "const initializer %s is not a constant", subExpr(rhExpr, j == 0)))
			}
		}
		rhs[i] = rh
	}
	tc.iota = -1
	var typ *typeInfo
	if node.Type == nil {
		for i, rh := range rhs {
			if rh[0] == nil {
				continue
			}
			if !rh[1].Untyped() {
				tc.mustBeAssignableTo(rh[0], subExpr(node.Rhs[i], true), rh[1].Type, false, nil)
				continue
			}
			if !rh[0].Untyped() {
				panic(tc.errorf(node.Lhs[i], "cannot use typed const %s in untyped const initializer", subExpr(node.Rhs[i], true)))
			}
			if rh[0].Type != rh[1].Type {
				panic(tc.errorf(node.Lhs[i], "mismatched kinds %s and %s in untyped const initializer",
					constantKindName[rh[0].Type.Kind()], constantKindName[rh[1].Type.Kind()]))
			}
		}
	} else {
		typ = tc.checkType(node.Type)
		switch typ.Type.Kind() {
		case reflect.Array, reflect.Chan, reflect.Func,
			reflect.Interface, reflect.Map, reflect.Ptr, reflect.Slice,
			reflect.Struct, reflect.UnsafePointer:
			panic(tc.errorf(node.Lhs[0], "invalid constant type %s", typ))
		}
		for i, rh := range rhs {
			for j, ti := range rh {
				if ti != nil {
					tc.mustBeAssignableTo(ti, subExpr(node.Rhs[i], j == 0), typ.Type, false, nil)
				}
			}
		}
	}
	for i, rh := range rhs {
		if isBlankIdentifier(node.Lhs[i]) {
			continue
		}
		ti := rh.TypeInfo()
		constValue := ti.Constant
		var constType reflect.Type
		if typ == nil {
			constType = ti.Type
		} else {
			constType = typ.Type
		}
		constTi := &typeInfo{Type: constType, Constant: constValue}
		if ti.Untyped() && typ == nil {
			constTi.Properties = propertyUntyped
		}
		tc.assignScope(node.Lhs[i].Name, constTi, node.Lhs[i], nil)
	}
}
func (tc *typechecker) checkGenericAssignmentNode(node *ast.Assignment) {
	switch node.Type {
	case ast.AssignmentDeclaration:
		tc.checkShortVariableDeclaration(node)
	case ast.AssignmentIncrement, ast.AssignmentDecrement:
		tc.checkIncDecStatement(node)
	case ast.AssignmentSimple:
		tc.checkAssignment(node)
	default:
		tc.checkAssignmentOperation(node)
	}
}