// Copyright 2013 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// Package constant implements Values representing untyped // Go constants and their corresponding operations. // // A special Unknown value may be used when a value // is unknown due to an error. Operations on unknown // values produce unknown values unless specified // otherwise.
package constant import ( ) //go:generate stringer -type Kind // Kind specifies the kind of value represented by a [Value]. type Kind int const ( // unknown values Unknown Kind = iota // non-numeric values Bool String // numeric values Int Float Complex ) // A Value represents the value of a Go constant. type Value interface { // Kind returns the value kind. Kind() Kind // String returns a short, quoted (human-readable) form of the value. // For numeric values, the result may be an approximation; // for String values the result may be a shortened string. // Use ExactString for a string representing a value exactly. String() string // ExactString returns an exact, quoted (human-readable) form of the value. // If the Value is of Kind String, use StringVal to obtain the unquoted string. ExactString() string // Prevent external implementations. implementsValue() } // ---------------------------------------------------------------------------- // Implementations // Maximum supported mantissa precision. // The spec requires at least 256 bits; typical implementations use 512 bits. const prec = 512 // TODO(gri) Consider storing "error" information in an unknownVal so clients // can provide better error messages. For instance, if a number is // too large (incl. infinity), that could be recorded in unknownVal. // See also #20583 and #42695 for use cases. // Representation of values: // // Values of Int and Float Kind have two different representations each: int64Val // and intVal, and ratVal and floatVal. When possible, the "smaller", respectively // more precise (for Floats) representation is chosen. However, once a Float value // is represented as a floatVal, any subsequent results remain floatVals (unless // explicitly converted); i.e., no attempt is made to convert a floatVal back into // a ratVal. The reasoning is that all representations but floatVal are mathematically // exact, but once that precision is lost (by moving to floatVal), moving back to // a different representation implies a precision that's not actually there. type ( unknownVal struct{} boolVal bool stringVal struct { // Lazy value: either a string (l,r==nil) or an addition (l,r!=nil). mu sync.Mutex s string l, r *stringVal } int64Val int64 // Int values representable as an int64 intVal struct{ val *big.Int } // Int values not representable as an int64 ratVal struct{ val *big.Rat } // Float values representable as a fraction floatVal struct{ val *big.Float } // Float values not representable as a fraction complexVal struct{ re, im Value } ) func (unknownVal) () Kind { return Unknown } func (boolVal) () Kind { return Bool } func (*stringVal) () Kind { return String } func (int64Val) () Kind { return Int } func (intVal) () Kind { return Int } func (ratVal) () Kind { return Float } func (floatVal) () Kind { return Float } func (complexVal) () Kind { return Complex } func (unknownVal) () string { return "unknown" } func ( boolVal) () string { return strconv.FormatBool(bool()) } // String returns a possibly shortened quoted form of the String value. func ( *stringVal) () string { const = 72 // a reasonable length := strconv.Quote(.string()) if utf8.RuneCountInString() > { // The string without the enclosing quotes is greater than maxLen-2 runes // long. Remove the last 3 runes (including the closing '"') by keeping // only the first maxLen-3 runes; then add "...". := 0 for := 0; < -3; ++ { , := utf8.DecodeRuneInString([:]) += } = [:] + "..." } return } // string constructs and returns the actual string literal value. // If x represents an addition, then it rewrites x to be a single // string, to speed future calls. This lazy construction avoids // building different string values for all subpieces of a large // concatenation. See golang.org/issue/23348. func ( *stringVal) () string { .mu.Lock() if .l != nil { .s = strings.Join(reverse(.appendReverse(nil)), "") .l = nil .r = nil } := .s .mu.Unlock() return } // reverse reverses x in place and returns it. func reverse( []string) []string { := len() for := 0; + < ; ++ { [], [-1-] = [-1-], [] } return } // appendReverse appends to list all of x's subpieces, but in reverse, // and returns the result. Appending the reversal allows processing // the right side in a recursive call and the left side in a loop. // Because a chain like a + b + c + d + e is actually represented // as ((((a + b) + c) + d) + e), the left-side loop avoids deep recursion. // x must be locked. func ( *stringVal) ( []string) []string { := for .r != nil { .r.mu.Lock() = .r.() .r.mu.Unlock() := .l if != { .mu.Unlock() } .mu.Lock() = } := .s if != { .mu.Unlock() } return append(, ) } func ( int64Val) () string { return strconv.FormatInt(int64(), 10) } func ( intVal) () string { return .val.String() } func ( ratVal) () string { return rtof().String() } // String returns a decimal approximation of the Float value. func ( floatVal) () string { := .val // Don't try to convert infinities (will not terminate). if .IsInf() { return .String() } // Use exact fmt formatting if in float64 range (common case): // proceed if f doesn't underflow to 0 or overflow to inf. if , := .Float64(); .Sign() == 0 == ( == 0) && !math.IsInf(, 0) { := fmt.Sprintf("%.6g", ) if !.IsInt() && strings.IndexByte(, '.') < 0 { // f is not an integer, but its string representation // doesn't reflect that. Use more digits. See issue 56220. = fmt.Sprintf("%g", ) } return } // Out of float64 range. Do approximate manual to decimal // conversion to avoid precise but possibly slow Float // formatting. // f = mant * 2**exp var big.Float := .MantExp(&) // 0.5 <= |mant| < 1.0 // approximate float64 mantissa m and decimal exponent d // f ~ m * 10**d , := .Float64() // 0.5 <= |m| < 1.0 := float64() * (math.Ln2 / math.Ln10) // log_10(2) // adjust m for truncated (integer) decimal exponent e := int64() *= math.Pow(10, -float64()) // ensure 1 <= |m| < 10 switch := math.Abs(); { case < 1-0.5e-6: // The %.6g format below rounds m to 5 digits after the // decimal point. Make sure that m*10 < 10 even after // rounding up: m*10 + 0.5e-5 < 10 => m < 1 - 0.5e6. *= 10 -- case >= 10: /= 10 ++ } return fmt.Sprintf("%.6ge%+d", , ) } func ( complexVal) () string { return fmt.Sprintf("(%s + %si)", .re, .im) } func ( unknownVal) () string { return .String() } func ( boolVal) () string { return .String() } func ( *stringVal) () string { return strconv.Quote(.string()) } func ( int64Val) () string { return .String() } func ( intVal) () string { return .String() } func ( ratVal) () string { := .val if .IsInt() { return .Num().String() } return .String() } func ( floatVal) () string { return .val.Text('p', 0) } func ( complexVal) () string { return fmt.Sprintf("(%s + %si)", .re.ExactString(), .im.ExactString()) } func (unknownVal) () {} func (boolVal) () {} func (*stringVal) () {} func (int64Val) () {} func (ratVal) () {} func (intVal) () {} func (floatVal) () {} func (complexVal) () {} func newInt() *big.Int { return new(big.Int) } func newRat() *big.Rat { return new(big.Rat) } func newFloat() *big.Float { return new(big.Float).SetPrec(prec) } func i64toi( int64Val) intVal { return intVal{newInt().SetInt64(int64())} } func i64tor( int64Val) ratVal { return ratVal{newRat().SetInt64(int64())} } func i64tof( int64Val) floatVal { return floatVal{newFloat().SetInt64(int64())} } func itor( intVal) ratVal { return ratVal{newRat().SetInt(.val)} } func itof( intVal) floatVal { return floatVal{newFloat().SetInt(.val)} } func rtof( ratVal) floatVal { return floatVal{newFloat().SetRat(.val)} } func vtoc( Value) complexVal { return complexVal{, int64Val(0)} } func makeInt( *big.Int) Value { if .IsInt64() { return int64Val(.Int64()) } return intVal{} } func makeRat( *big.Rat) Value { := .Num() := .Denom() if smallInt() && smallInt() { // ok to remain fraction return ratVal{} } // components too large => switch to float return floatVal{newFloat().SetRat()} } var floatVal0 = floatVal{newFloat()} func makeFloat( *big.Float) Value { // convert -0 if .Sign() == 0 { return floatVal0 } if .IsInf() { return unknownVal{} } // No attempt is made to "go back" to ratVal, even if possible, // to avoid providing the illusion of a mathematically exact // representation. return floatVal{} } func makeComplex(, Value) Value { if .Kind() == Unknown || .Kind() == Unknown { return unknownVal{} } return complexVal{, } } func makeFloatFromLiteral( string) Value { if , := newFloat().SetString(); { if smallFloat() { // ok to use rationals if .Sign() == 0 { // Issue 20228: If the float underflowed to zero, parse just "0". // Otherwise, lit might contain a value with a large negative exponent, // such as -6e-1886451601. As a float, that will underflow to 0, // but it'll take forever to parse as a Rat. = "0" } if , := newRat().SetString(); { return ratVal{} } } // otherwise use floats return makeFloat() } return nil } // Permit fractions with component sizes up to maxExp // before switching to using floating-point numbers. const maxExp = 4 << 10 // smallInt reports whether x would lead to "reasonably"-sized fraction // if converted to a *big.Rat. func smallInt( *big.Int) bool { return .BitLen() < maxExp } // smallFloat64 reports whether x would lead to "reasonably"-sized fraction // if converted to a *big.Rat. func smallFloat64( float64) bool { if math.IsInf(, 0) { return false } , := math.Frexp() return -maxExp < && < maxExp } // smallFloat reports whether x would lead to "reasonably"-sized fraction // if converted to a *big.Rat. func smallFloat( *big.Float) bool { if .IsInf() { return false } := .MantExp(nil) return -maxExp < && < maxExp } // ---------------------------------------------------------------------------- // Factories // MakeUnknown returns the [Unknown] value. func () Value { return unknownVal{} } // MakeBool returns the [Bool] value for b. func ( bool) Value { return boolVal() } // MakeString returns the [String] value for s. func ( string) Value { if == "" { return &emptyString // common case } return &stringVal{s: } } var emptyString stringVal // MakeInt64 returns the [Int] value for x. func ( int64) Value { return int64Val() } // MakeUint64 returns the [Int] value for x. func ( uint64) Value { if < 1<<63 { return int64Val(int64()) } return intVal{newInt().SetUint64()} } // MakeFloat64 returns the [Float] value for x. // If x is -0.0, the result is 0.0. // If x is not finite, the result is an [Unknown]. func ( float64) Value { if math.IsInf(, 0) || math.IsNaN() { return unknownVal{} } if smallFloat64() { return ratVal{newRat().SetFloat64( + 0)} // convert -0 to 0 } return floatVal{newFloat().SetFloat64( + 0)} } // MakeFromLiteral returns the corresponding integer, floating-point, // imaginary, character, or string value for a Go literal string. The // tok value must be one of [token.INT], [token.FLOAT], [token.IMAG], // [token.CHAR], or [token.STRING]. The final argument must be zero. // If the literal string syntax is invalid, the result is an [Unknown]. func ( string, token.Token, uint) Value { if != 0 { panic("MakeFromLiteral called with non-zero last argument") } switch { case token.INT: if , := strconv.ParseInt(, 0, 64); == nil { return int64Val() } if , := newInt().SetString(, 0); { return intVal{} } case token.FLOAT: if := makeFloatFromLiteral(); != nil { return } case token.IMAG: if := len(); > 0 && [-1] == 'i' { if := makeFloatFromLiteral([:-1]); != nil { return makeComplex(int64Val(0), ) } } case token.CHAR: if := len(); >= 2 { if , , , := strconv.UnquoteChar([1:-1], '\''); == nil { return MakeInt64(int64()) } } case token.STRING: if , := strconv.Unquote(); == nil { return MakeString() } default: panic(fmt.Sprintf("%v is not a valid token", )) } return unknownVal{} } // ---------------------------------------------------------------------------- // Accessors // // For unknown arguments the result is the zero value for the respective // accessor type, except for Sign, where the result is 1. // BoolVal returns the Go boolean value of x, which must be a [Bool] or an [Unknown]. // If x is [Unknown], the result is false. func ( Value) bool { switch x := .(type) { case boolVal: return bool() case unknownVal: return false default: panic(fmt.Sprintf("%v not a Bool", )) } } // StringVal returns the Go string value of x, which must be a [String] or an [Unknown]. // If x is [Unknown], the result is "". func ( Value) string { switch x := .(type) { case *stringVal: return .string() case unknownVal: return "" default: panic(fmt.Sprintf("%v not a String", )) } } // Int64Val returns the Go int64 value of x and whether the result is exact; // x must be an [Int] or an [Unknown]. If the result is not exact, its value is undefined. // If x is [Unknown], the result is (0, false). func ( Value) (int64, bool) { switch x := .(type) { case int64Val: return int64(), true case intVal: return .val.Int64(), false // not an int64Val and thus not exact case unknownVal: return 0, false default: panic(fmt.Sprintf("%v not an Int", )) } } // Uint64Val returns the Go uint64 value of x and whether the result is exact; // x must be an [Int] or an [Unknown]. If the result is not exact, its value is undefined. // If x is [Unknown], the result is (0, false). func ( Value) (uint64, bool) { switch x := .(type) { case int64Val: return uint64(), >= 0 case intVal: return .val.Uint64(), .val.IsUint64() case unknownVal: return 0, false default: panic(fmt.Sprintf("%v not an Int", )) } } // Float32Val is like [Float64Val] but for float32 instead of float64. func ( Value) (float32, bool) { switch x := .(type) { case int64Val: := float32() return , int64Val() == case intVal: , := newFloat().SetInt(.val).Float32() return , == big.Exact case ratVal: return .val.Float32() case floatVal: , := .val.Float32() return , == big.Exact case unknownVal: return 0, false default: panic(fmt.Sprintf("%v not a Float", )) } } // Float64Val returns the nearest Go float64 value of x and whether the result is exact; // x must be numeric or an [Unknown], but not [Complex]. For values too small (too close to 0) // to represent as float64, [Float64Val] silently underflows to 0. The result sign always // matches the sign of x, even for 0. // If x is [Unknown], the result is (0, false). func ( Value) (float64, bool) { switch x := .(type) { case int64Val: := float64(int64()) return , int64Val() == case intVal: , := newFloat().SetInt(.val).Float64() return , == big.Exact case ratVal: return .val.Float64() case floatVal: , := .val.Float64() return , == big.Exact case unknownVal: return 0, false default: panic(fmt.Sprintf("%v not a Float", )) } } // Val returns the underlying value for a given constant. Since it returns an // interface, it is up to the caller to type assert the result to the expected // type. The possible dynamic return types are: // // x Kind type of result // ----------------------------------------- // Bool bool // String string // Int int64 or *big.Int // Float *big.Float or *big.Rat // everything else nil func ( Value) any { switch x := .(type) { case boolVal: return bool() case *stringVal: return .string() case int64Val: return int64() case intVal: return .val case ratVal: return .val case floatVal: return .val default: return nil } } // Make returns the [Value] for x. // // type of x result Kind // ---------------------------- // bool Bool // string String // int64 Int // *big.Int Int // *big.Float Float // *big.Rat Float // anything else Unknown func ( any) Value { switch x := .(type) { case bool: return boolVal() case string: return &stringVal{s: } case int64: return int64Val() case *big.Int: return makeInt() case *big.Rat: return makeRat() case *big.Float: return makeFloat() default: return unknownVal{} } } // BitLen returns the number of bits required to represent // the absolute value x in binary representation; x must be an [Int] or an [Unknown]. // If x is [Unknown], the result is 0. func ( Value) int { switch x := .(type) { case int64Val: := uint64() if < 0 { = uint64(-) } return 64 - bits.LeadingZeros64() case intVal: return .val.BitLen() case unknownVal: return 0 default: panic(fmt.Sprintf("%v not an Int", )) } } // Sign returns -1, 0, or 1 depending on whether x < 0, x == 0, or x > 0; // x must be numeric or [Unknown]. For complex values x, the sign is 0 if x == 0, // otherwise it is != 0. If x is [Unknown], the result is 1. func ( Value) int { switch x := .(type) { case int64Val: switch { case < 0: return -1 case > 0: return 1 } return 0 case intVal: return .val.Sign() case ratVal: return .val.Sign() case floatVal: return .val.Sign() case complexVal: return (.re) | (.im) case unknownVal: return 1 // avoid spurious division by zero errors default: panic(fmt.Sprintf("%v not numeric", )) } } // ---------------------------------------------------------------------------- // Support for assembling/disassembling numeric values const ( // Compute the size of a Word in bytes. _m = ^big.Word(0) _log = _m>>8&1 + _m>>16&1 + _m>>32&1 wordSize = 1 << _log ) // Bytes returns the bytes for the absolute value of x in little- // endian binary representation; x must be an [Int]. func ( Value) []byte { var intVal switch x := .(type) { case int64Val: = i64toi() case intVal: = default: panic(fmt.Sprintf("%v not an Int", )) } := .val.Bits() := make([]byte, len()*wordSize) := 0 for , := range { for := 0; < wordSize; ++ { [] = byte() >>= 8 ++ } } // remove leading 0's for > 0 && [-1] == 0 { -- } return [:] } // MakeFromBytes returns the [Int] value given the bytes of its little-endian // binary representation. An empty byte slice argument represents 0. func ( []byte) Value { := make([]big.Word, (len()+(wordSize-1))/wordSize) := 0 var big.Word var uint for , := range { |= big.Word() << if += 8; == wordSize*8 { [] = ++ = 0 = 0 } } // store last word if < len() { [] = ++ } // remove leading 0's for > 0 && [-1] == 0 { -- } return makeInt(newInt().SetBits([:])) } // Num returns the numerator of x; x must be [Int], [Float], or [Unknown]. // If x is [Unknown], or if it is too large or small to represent as a // fraction, the result is [Unknown]. Otherwise the result is an [Int] // with the same sign as x. func ( Value) Value { switch x := .(type) { case int64Val, intVal: return case ratVal: return makeInt(.val.Num()) case floatVal: if smallFloat(.val) { , := .val.Rat(nil) return makeInt(.Num()) } case unknownVal: break default: panic(fmt.Sprintf("%v not Int or Float", )) } return unknownVal{} } // Denom returns the denominator of x; x must be [Int], [Float], or [Unknown]. // If x is [Unknown], or if it is too large or small to represent as a // fraction, the result is [Unknown]. Otherwise the result is an [Int] >= 1. func ( Value) Value { switch x := .(type) { case int64Val, intVal: return int64Val(1) case ratVal: return makeInt(.val.Denom()) case floatVal: if smallFloat(.val) { , := .val.Rat(nil) return makeInt(.Denom()) } case unknownVal: break default: panic(fmt.Sprintf("%v not Int or Float", )) } return unknownVal{} } // MakeImag returns the [Complex] value x*i; // x must be [Int], [Float], or [Unknown]. // If x is [Unknown], the result is [Unknown]. func ( Value) Value { switch .(type) { case unknownVal: return case int64Val, intVal, ratVal, floatVal: return makeComplex(int64Val(0), ) default: panic(fmt.Sprintf("%v not Int or Float", )) } } // Real returns the real part of x, which must be a numeric or unknown value. // If x is [Unknown], the result is [Unknown]. func ( Value) Value { switch x := .(type) { case unknownVal, int64Val, intVal, ratVal, floatVal: return case complexVal: return .re default: panic(fmt.Sprintf("%v not numeric", )) } } // Imag returns the imaginary part of x, which must be a numeric or unknown value. // If x is [Unknown], the result is [Unknown]. func ( Value) Value { switch x := .(type) { case unknownVal: return case int64Val, intVal, ratVal, floatVal: return int64Val(0) case complexVal: return .im default: panic(fmt.Sprintf("%v not numeric", )) } } // ---------------------------------------------------------------------------- // Numeric conversions // ToInt converts x to an [Int] value if x is representable as an [Int]. // Otherwise it returns an [Unknown]. func ( Value) Value { switch x := .(type) { case int64Val, intVal: return case ratVal: if .val.IsInt() { return makeInt(.val.Num()) } case floatVal: // avoid creation of huge integers // (Existing tests require permitting exponents of at least 1024; // allow any value that would also be permissible as a fraction.) if smallFloat(.val) { := newInt() if , := .val.Int(); == big.Exact { return makeInt() } // If we can get an integer by rounding up or down, // assume x is not an integer because of rounding // errors in prior computations. const = 4 // a small number of bits > 0 var big.Float .SetPrec(prec - ) // try rounding down a little .SetMode(big.ToZero) .Set(.val) if , := .Int(); == big.Exact { return makeInt() } // try rounding up a little .SetMode(big.AwayFromZero) .Set(.val) if , := .Int(); == big.Exact { return makeInt() } } case complexVal: if := ToFloat(); .Kind() == Float { return () } } return unknownVal{} } // ToFloat converts x to a [Float] value if x is representable as a [Float]. // Otherwise it returns an [Unknown]. func ( Value) Value { switch x := .(type) { case int64Val: return i64tor() // x is always a small int case intVal: if smallInt(.val) { return itor() } return itof() case ratVal, floatVal: return case complexVal: if Sign(.im) == 0 { return (.re) } } return unknownVal{} } // ToComplex converts x to a [Complex] value if x is representable as a [Complex]. // Otherwise it returns an [Unknown]. func ( Value) Value { switch x := .(type) { case int64Val, intVal, ratVal, floatVal: return vtoc() case complexVal: return } return unknownVal{} } // ---------------------------------------------------------------------------- // Operations // is32bit reports whether x can be represented using 32 bits. func is32bit( int64) bool { const = 32 return -1<<(-1) <= && <= 1<<(-1)-1 } // is63bit reports whether x can be represented using 63 bits. func is63bit( int64) bool { const = 63 return -1<<(-1) <= && <= 1<<(-1)-1 } // UnaryOp returns the result of the unary expression op y. // The operation must be defined for the operand. // If prec > 0 it specifies the ^ (xor) result size in bits. // If y is [Unknown], the result is [Unknown]. func ( token.Token, Value, uint) Value { switch { case token.ADD: switch .(type) { case unknownVal, int64Val, intVal, ratVal, floatVal, complexVal: return } case token.SUB: switch y := .(type) { case unknownVal: return case int64Val: if := -; != { return // no overflow } return makeInt(newInt().Neg(big.NewInt(int64()))) case intVal: return makeInt(newInt().Neg(.val)) case ratVal: return makeRat(newRat().Neg(.val)) case floatVal: return makeFloat(newFloat().Neg(.val)) case complexVal: := (token.SUB, .re, 0) := (token.SUB, .im, 0) return makeComplex(, ) } case token.XOR: := newInt() switch y := .(type) { case unknownVal: return case int64Val: .Not(big.NewInt(int64())) case intVal: .Not(.val) default: goto } // For unsigned types, the result will be negative and // thus "too large": We must limit the result precision // to the type's precision. if > 0 { .AndNot(, newInt().Lsh(big.NewInt(-1), )) // z &^= (-1)<<prec } return makeInt() case token.NOT: switch y := .(type) { case unknownVal: return case boolVal: return ! } } : panic(fmt.Sprintf("invalid unary operation %s%v", , )) } func ord( Value) int { switch .(type) { default: // force invalid value into "x position" in match // (don't panic here so that callers can provide a better error message) return -1 case unknownVal: return 0 case boolVal, *stringVal: return 1 case int64Val: return 2 case intVal: return 3 case ratVal: return 4 case floatVal: return 5 case complexVal: return 6 } } // match returns the matching representation (same type) with the // smallest complexity for two values x and y. If one of them is // numeric, both of them must be numeric. If one of them is Unknown // or invalid (say, nil) both results are that value. func match(, Value) (, Value) { switch , := ord(), ord(); { case < : , = match0(, ) case > : , = match0(, ) } return , } // match0 must only be called by match. // Invariant: ord(x) < ord(y) func match0(, Value) (, Value) { // Prefer to return the original x and y arguments when possible, // to avoid unnecessary heap allocations. switch .(type) { case intVal: switch x1 := .(type) { case int64Val: return i64toi(), } case ratVal: switch x1 := .(type) { case int64Val: return i64tor(), case intVal: return itor(), } case floatVal: switch x1 := .(type) { case int64Val: return i64tof(), case intVal: return itof(), case ratVal: return rtof(), } case complexVal: return vtoc(), } // force unknown and invalid values into "x position" in callers of match // (don't panic here so that callers can provide a better error message) return , } // BinaryOp returns the result of the binary expression x op y. // The operation must be defined for the operands. If one of the // operands is [Unknown], the result is [Unknown]. // BinaryOp doesn't handle comparisons or shifts; use [Compare] // or [Shift] instead. // // To force integer division of [Int] operands, use op == [token.QUO_ASSIGN] // instead of [token.QUO]; the result is guaranteed to be [Int] in this case. // Division by zero leads to a run-time panic. func ( Value, token.Token, Value) Value { , := match(, ) switch x := .(type) { case unknownVal: return case boolVal: := .(boolVal) switch { case token.LAND: return && case token.LOR: return || } case int64Val: := int64() := int64(.(int64Val)) var int64 switch { case token.ADD: if !is63bit() || !is63bit() { return makeInt(newInt().Add(big.NewInt(), big.NewInt())) } = + case token.SUB: if !is63bit() || !is63bit() { return makeInt(newInt().Sub(big.NewInt(), big.NewInt())) } = - case token.MUL: if !is32bit() || !is32bit() { return makeInt(newInt().Mul(big.NewInt(), big.NewInt())) } = * case token.QUO: return makeRat(big.NewRat(, )) case token.QUO_ASSIGN: // force integer division = / case token.REM: = % case token.AND: = & case token.OR: = | case token.XOR: = ^ case token.AND_NOT: = &^ default: goto } return int64Val() case intVal: := .val := .(intVal).val := newInt() switch { case token.ADD: .Add(, ) case token.SUB: .Sub(, ) case token.MUL: .Mul(, ) case token.QUO: return makeRat(newRat().SetFrac(, )) case token.QUO_ASSIGN: // force integer division .Quo(, ) case token.REM: .Rem(, ) case token.AND: .And(, ) case token.OR: .Or(, ) case token.XOR: .Xor(, ) case token.AND_NOT: .AndNot(, ) default: goto } return makeInt() case ratVal: := .val := .(ratVal).val := newRat() switch { case token.ADD: .Add(, ) case token.SUB: .Sub(, ) case token.MUL: .Mul(, ) case token.QUO: .Quo(, ) default: goto } return makeRat() case floatVal: := .val := .(floatVal).val := newFloat() switch { case token.ADD: .Add(, ) case token.SUB: .Sub(, ) case token.MUL: .Mul(, ) case token.QUO: .Quo(, ) default: goto } return makeFloat() case complexVal: := .(complexVal) , := .re, .im , := .re, .im var , Value switch { case token.ADD: // (a+c) + i(b+d) = add(, ) = add(, ) case token.SUB: // (a-c) + i(b-d) = sub(, ) = sub(, ) case token.MUL: // (ac-bd) + i(bc+ad) := mul(, ) := mul(, ) := mul(, ) := mul(, ) = sub(, ) = add(, ) case token.QUO: // (ac+bd)/s + i(bc-ad)/s, with s = cc + dd := mul(, ) := mul(, ) := mul(, ) := mul(, ) := mul(, ) := mul(, ) := add(, ) = add(, ) = quo(, ) = sub(, ) = quo(, ) default: goto } return makeComplex(, ) case *stringVal: if == token.ADD { return &stringVal{l: , r: .(*stringVal)} } } : panic(fmt.Sprintf("invalid binary operation %v %s %v", , , )) } func add(, Value) Value { return BinaryOp(, token.ADD, ) } func sub(, Value) Value { return BinaryOp(, token.SUB, ) } func mul(, Value) Value { return BinaryOp(, token.MUL, ) } func quo(, Value) Value { return BinaryOp(, token.QUO, ) } // Shift returns the result of the shift expression x op s // with op == [token.SHL] or [token.SHR] (<< or >>). x must be // an [Int] or an [Unknown]. If x is [Unknown], the result is x. func ( Value, token.Token, uint) Value { switch x := .(type) { case unknownVal: return case int64Val: if == 0 { return } switch { case token.SHL: := i64toi().val return makeInt(.Lsh(, )) case token.SHR: return >> } case intVal: if == 0 { return } := newInt() switch { case token.SHL: return makeInt(.Lsh(.val, )) case token.SHR: return makeInt(.Rsh(.val, )) } } panic(fmt.Sprintf("invalid shift %v %s %d", , , )) } func cmpZero( int, token.Token) bool { switch { case token.EQL: return == 0 case token.NEQ: return != 0 case token.LSS: return < 0 case token.LEQ: return <= 0 case token.GTR: return > 0 case token.GEQ: return >= 0 } panic(fmt.Sprintf("invalid comparison %v %s 0", , )) } // Compare returns the result of the comparison x op y. // The comparison must be defined for the operands. // If one of the operands is [Unknown], the result is // false. func ( Value, token.Token, Value) bool { , := match(, ) switch x := .(type) { case unknownVal: return false case boolVal: := .(boolVal) switch { case token.EQL: return == case token.NEQ: return != } case int64Val: := .(int64Val) switch { case token.EQL: return == case token.NEQ: return != case token.LSS: return < case token.LEQ: return <= case token.GTR: return > case token.GEQ: return >= } case intVal: return cmpZero(.val.Cmp(.(intVal).val), ) case ratVal: return cmpZero(.val.Cmp(.(ratVal).val), ) case floatVal: return cmpZero(.val.Cmp(.(floatVal).val), ) case complexVal: := .(complexVal) := (.re, token.EQL, .re) := (.im, token.EQL, .im) switch { case token.EQL: return && case token.NEQ: return ! || ! } case *stringVal: := .string() := .(*stringVal).string() switch { case token.EQL: return == case token.NEQ: return != case token.LSS: return < case token.LEQ: return <= case token.GTR: return > case token.GEQ: return >= } } panic(fmt.Sprintf("invalid comparison %v %s %v", , , )) }