// UnaryOp returns the result of the unary expression op y. // The operation must be defined for the operand. // If size >= 0 it specifies the ^ (xor) result size in bytes. // If y is Unknown, the result is Unknown. // func UnaryOp(op token.Token, y Value, size int) Value { switch op { case token.ADD: switch y.(type) { case unknownVal, int64Val, intVal, floatVal, complexVal: return y } case token.SUB: switch y := y.(type) { case unknownVal: return y case int64Val: if z := -y; z != y { return z // no overflow } return normInt(new(big.Int).Neg(big.NewInt(int64(y)))) case intVal: return normInt(new(big.Int).Neg(y.val)) case floatVal: return normFloat(new(big.Rat).Neg(y.val)) case complexVal: return normComplex(new(big.Rat).Neg(y.re), new(big.Rat).Neg(y.im)) } case token.XOR: var z big.Int switch y := y.(type) { case unknownVal: return y case int64Val: z.Not(big.NewInt(int64(y))) case intVal: z.Not(y.val) default: goto Error } // For unsigned types, the result will be negative and // thus "too large": We must limit the result size to // the type's size. if size >= 0 { s := uint(size) * 8 z.AndNot(&z, new(big.Int).Lsh(big.NewInt(-1), s)) // z &^= (-1)<<s } return normInt(&z) case token.NOT: switch y := y.(type) { case unknownVal: return y case boolVal: return !y } } Error: panic(fmt.Sprintf("invalid unary operation %s%v", op, y)) }
// unaryOpConst returns the result of the constant evaluation op x where x is of the given type. func unaryOpConst(x interface{}, op token.Token, typ *Basic) interface{} { switch op { case token.ADD: return x // nothing to do case token.SUB: switch x := x.(type) { case int64: if z := -x; z != x { return z // no overflow } // overflow - need to convert to big.Int return normalizeIntConst(new(big.Int).Neg(big.NewInt(x))) case *big.Int: return normalizeIntConst(new(big.Int).Neg(x)) case *big.Rat: return normalizeRatConst(new(big.Rat).Neg(x)) case complex: return newComplex(new(big.Rat).Neg(x.re), new(big.Rat).Neg(x.im)) } case token.XOR: var z big.Int switch x := x.(type) { case int64: z.Not(big.NewInt(x)) case *big.Int: z.Not(x) default: unreachable() } // For unsigned types, the result will be negative and // thus "too large": We must limit the result size to // the type's size. if typ.Info&IsUnsigned != 0 { s := uint(typ.Size) * 8 if s == 0 { // platform-specific type // TODO(gri) this needs to be factored out switch typ.Kind { case Uint: s = intBits case Uintptr: s = ptrBits default: unreachable() } } // z &^= (-1)<<s z.AndNot(&z, new(big.Int).Lsh(big.NewInt(-1), s)) } return normalizeIntConst(&z) case token.NOT: return !x.(bool) } unreachable() return nil }
func binaryIntOp(x *big.Int, op token.Token, y *big.Int) interface{} { var z big.Int switch op { case token.ADD: return z.Add(x, y) case token.SUB: return z.Sub(x, y) case token.MUL: return z.Mul(x, y) case token.QUO: return z.Quo(x, y) case token.REM: return z.Rem(x, y) case token.AND: return z.And(x, y) case token.OR: return z.Or(x, y) case token.XOR: return z.Xor(x, y) case token.AND_NOT: return z.AndNot(x, y) case token.SHL: // The shift length must be uint, or untyped int and // convertible to uint. // TODO 32/64bit if y.BitLen() > 32 { panic("Excessive shift length") } return z.Lsh(x, uint(y.Int64())) case token.SHR: if y.BitLen() > 32 { panic("Excessive shift length") } return z.Rsh(x, uint(y.Int64())) case token.EQL: return x.Cmp(y) == 0 case token.NEQ: return x.Cmp(y) != 0 case token.LSS: return x.Cmp(y) < 0 case token.LEQ: return x.Cmp(y) <= 0 case token.GTR: return x.Cmp(y) > 0 case token.GEQ: return x.Cmp(y) >= 0 } panic("unreachable") }
func bigIntAndNot(oldValues [][]byte, newValues [][]byte, w float64) (result [][]byte) { var sum *big.Int if oldValues != nil { sum = new(big.Int).SetBytes(oldValues[0]) for _, b := range newValues { sum.AndNot(sum, new(big.Int).Mul(common.DecodeBigInt(b), big.NewInt(int64(w)))) } } else { sum = new(big.Int).Mul(new(big.Int).SetBytes(newValues[0]), big.NewInt(int64(w))) for _, b := range newValues[1:] { sum.AndNot(sum, new(big.Int).Mul(common.DecodeBigInt(b), big.NewInt(int64(w)))) } } return [][]byte{sum.Bytes()} }
// unaryOpConst returns the result of the constant evaluation op x where x is of the given type. func unaryOpConst(x interface{}, ctxt *Context, op token.Token, typ *Basic) interface{} { switch op { case token.ADD: return x // nothing to do case token.SUB: switch x := x.(type) { case int64: if z := -x; z != x { return z // no overflow } // overflow - need to convert to big.Int return normalizeIntConst(new(big.Int).Neg(big.NewInt(x))) case *big.Int: return normalizeIntConst(new(big.Int).Neg(x)) case *big.Rat: return normalizeRatConst(new(big.Rat).Neg(x)) case Complex: return newComplex(new(big.Rat).Neg(x.Re), new(big.Rat).Neg(x.Im)) } case token.XOR: var z big.Int switch x := x.(type) { case int64: z.Not(big.NewInt(x)) case *big.Int: z.Not(x) default: unreachable() } // For unsigned types, the result will be negative and // thus "too large": We must limit the result size to // the type's size. if typ.Info&IsUnsigned != 0 { s := uint(ctxt.sizeof(typ)) * 8 z.AndNot(&z, new(big.Int).Lsh(big.NewInt(-1), s)) // z &^= (-1)<<s } return normalizeIntConst(&z) case token.NOT: return !x.(bool) } unreachable() return nil }
func binaryIntOp(x *big.Int, op token.Token, y *big.Int) interface{} { var z big.Int switch op { case token.ADD: return z.Add(x, y) case token.SUB: return z.Sub(x, y) case token.MUL: return z.Mul(x, y) case token.QUO: return z.Quo(x, y) case token.REM: return z.Rem(x, y) case token.AND: return z.And(x, y) case token.OR: return z.Or(x, y) case token.XOR: return z.Xor(x, y) case token.AND_NOT: return z.AndNot(x, y) case token.SHL: panic("unimplemented") case token.SHR: panic("unimplemented") case token.EQL: return x.Cmp(y) == 0 case token.NEQ: return x.Cmp(y) != 0 case token.LSS: return x.Cmp(y) < 0 case token.LEQ: return x.Cmp(y) <= 0 case token.GTR: return x.Cmp(y) > 0 case token.GEQ: return x.Cmp(y) >= 0 } panic("unreachable") }
// binaryOpConst returns the result of the constant evaluation x op y; // both operands must be of the same "kind" (boolean, numeric, or string). // If intDiv is true, division (op == token.QUO) is using integer division // (and the result is guaranteed to be integer) rather than floating-point // division. Division by zero leads to a run-time panic. // func binaryOpConst(x, y interface{}, op token.Token, intDiv bool) interface{} { x, y = matchConst(x, y) switch x := x.(type) { case bool: y := y.(bool) switch op { case token.LAND: return x && y case token.LOR: return x || y default: unreachable() } case int64: y := y.(int64) switch op { case token.ADD: // TODO(gri) can do better than this if is63bit(x) && is63bit(y) { return x + y } return normalizeIntConst(new(big.Int).Add(big.NewInt(x), big.NewInt(y))) case token.SUB: // TODO(gri) can do better than this if is63bit(x) && is63bit(y) { return x - y } return normalizeIntConst(new(big.Int).Sub(big.NewInt(x), big.NewInt(y))) case token.MUL: // TODO(gri) can do better than this if is32bit(x) && is32bit(y) { return x * y } return normalizeIntConst(new(big.Int).Mul(big.NewInt(x), big.NewInt(y))) case token.REM: return x % y case token.QUO: if intDiv { return x / y } return normalizeRatConst(new(big.Rat).SetFrac(big.NewInt(x), big.NewInt(y))) case token.AND: return x & y case token.OR: return x | y case token.XOR: return x ^ y case token.AND_NOT: return x &^ y default: unreachable() } case *big.Int: y := y.(*big.Int) var z big.Int switch op { case token.ADD: z.Add(x, y) case token.SUB: z.Sub(x, y) case token.MUL: z.Mul(x, y) case token.REM: z.Rem(x, y) case token.QUO: if intDiv { z.Quo(x, y) } else { return normalizeRatConst(new(big.Rat).SetFrac(x, y)) } case token.AND: z.And(x, y) case token.OR: z.Or(x, y) case token.XOR: z.Xor(x, y) case token.AND_NOT: z.AndNot(x, y) default: unreachable() } return normalizeIntConst(&z) case *big.Rat: y := y.(*big.Rat) var z big.Rat switch op { case token.ADD: z.Add(x, y) case token.SUB: z.Sub(x, y) case token.MUL: z.Mul(x, y) case token.QUO: z.Quo(x, y) default: unreachable() } return normalizeRatConst(&z) case complex: y := y.(complex) a, b := x.re, x.im c, d := y.re, y.im var re, im big.Rat switch op { case token.ADD: // (a+c) + i(b+d) re.Add(a, c) im.Add(b, d) case token.SUB: // (a-c) + i(b-d) re.Sub(a, c) im.Sub(b, d) case token.MUL: // (ac-bd) + i(bc+ad) var ac, bd, bc, ad big.Rat ac.Mul(a, c) bd.Mul(b, d) bc.Mul(b, c) ad.Mul(a, d) re.Sub(&ac, &bd) im.Add(&bc, &ad) case token.QUO: // (ac+bd)/s + i(bc-ad)/s, with s = cc + dd var ac, bd, bc, ad, s big.Rat ac.Mul(a, c) bd.Mul(b, d) bc.Mul(b, c) ad.Mul(a, d) s.Add(c.Mul(c, c), d.Mul(d, d)) re.Add(&ac, &bd) re.Quo(&re, &s) im.Sub(&bc, &ad) im.Quo(&im, &s) default: unreachable() } return normalizeComplexConst(complex{&re, &im}) case string: if op == token.ADD { return x + y.(string) } } unreachable() return nil }
// Post-order traversal, equivalent to postfix notation. func Eval(node interface{}) (*big.Int, error) { switch nn := node.(type) { case *ast.BinaryExpr: z := new(big.Int) x, xerr := Eval(nn.X) if xerr != nil { return nil, xerr } y, yerr := Eval(nn.Y) if yerr != nil { return nil, yerr } switch nn.Op { case token.ADD: return z.Add(x, y), nil case token.SUB: return z.Sub(x, y), nil case token.MUL: return z.Mul(x, y), nil case token.QUO: if y.Sign() == 0 { // 0 denominator return nil, DivideByZero } return z.Quo(x, y), nil case token.REM: if y.Sign() == 0 { return nil, DivideByZero } return z.Rem(x, y), nil case token.AND: return z.And(x, y), nil case token.OR: return z.Or(x, y), nil case token.XOR: return z.Xor(x, y), nil case token.SHL: if y.Sign() < 0 { // negative shift return nil, NegativeShift } return z.Lsh(x, uint(y.Int64())), nil case token.SHR: if y.Sign() < 0 { return nil, NegativeShift } return z.Rsh(x, uint(y.Int64())), nil case token.AND_NOT: return z.AndNot(x, y), nil default: return nil, UnknownOpErr } case *ast.UnaryExpr: var z *big.Int var err error if z, err = Eval(nn.X); err != nil { return nil, err } switch nn.Op { case token.SUB: // -x return z.Neg(z), nil case token.XOR: // ^x return z.Not(z), nil case token.ADD: // +x (useless) return z, nil } case *ast.BasicLit: z := new(big.Int) switch nn.Kind { case token.INT: z.SetString(nn.Value, 0) return z, nil default: return nil, UnknownLitErr } case *ast.ParenExpr: z, err := Eval(nn.X) if err != nil { return nil, err } return z, nil case *ast.CallExpr: ident, ok := nn.Fun.(*ast.Ident) if !ok { return nil, UnknownTokenErr // quarter to four am; dunno correct error } var f Func f, ok = FuncMap[ident.Name] if !ok { return nil, UnknownFuncErr } var aerr error args := make([]*big.Int, len(nn.Args)) for i, a := range nn.Args { if args[i], aerr = Eval(a); aerr != nil { return nil, aerr } } x, xerr := f(args...) if xerr != nil { return nil, xerr } return x, nil } return nil, UnknownTokenErr }