// 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 constantimport ()//go:generate stringer -type Kind// Kind specifies the kind of value represented by a [Value].typeKindintconst (// unknown valuesUnknownKind = iota// non-numeric valuesBoolString// numeric valuesIntFloatComplex)// A Value represents the value of a Go constant.typeValueinterface {// 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 { returnUnknown }func (boolVal) () Kind { returnBool }func (*stringVal) () Kind { returnString }func (int64Val) () Kind { returnInt }func (intVal) () Kind { returnInt }func (ratVal) () Kind { returnFloat }func (floatVal) () Kind { returnFloat }func (complexVal) () Kind { returnComplex }func (unknownVal) () string { return"unknown" }func ( boolVal) () string { returnstrconv.FormatBool(bool()) }// String returns a possibly shortened quoted form of the String value.func ( *stringVal) () string {const = 72// a reasonable length := strconv.Quote(.string())ifutf8.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 "...". := 0for := 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() := .lif != { .mu.Unlock() } .mu.Lock() = } := .sif != { .mu.Unlock() }returnappend(, )}func ( int64Val) () string { returnstrconv.FormatInt(int64(), 10) }func ( intVal) () string { return .val.String() }func ( ratVal) () string { returnrtof().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**expvarbig.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| < 10switch := 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 ++ }returnfmt.Sprintf("%.6ge%+d", , )}func ( complexVal) () string { returnfmt.Sprintf("(%s + %si)", .re, .im) }func ( unknownVal) () string { return .String() }func ( boolVal) () string { return .String() }func ( *stringVal) () string { returnstrconv.Quote(.string()) }func ( int64Val) () string { return .String() }func ( intVal) () string { return .String() }func ( ratVal) () string { := .valif .IsInt() {return .Num().String() }return .String()}func ( floatVal) () string { return .val.Text('p', 0) }func ( complexVal) () string {returnfmt.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 { returnnew(big.Int) }func newRat() *big.Rat { returnnew(big.Rat) }func newFloat() *big.Float { returnnew(big.Float).SetPrec(prec) }func i64toi( int64Val) intVal { returnintVal{newInt().SetInt64(int64())} }func i64tor( int64Val) ratVal { returnratVal{newRat().SetInt64(int64())} }func i64tof( int64Val) floatVal { returnfloatVal{newFloat().SetInt64(int64())} }func itor( intVal) ratVal { returnratVal{newRat().SetInt(.val)} }func itof( intVal) floatVal { returnfloatVal{newFloat().SetInt(.val)} }func rtof( ratVal) floatVal { returnfloatVal{newFloat().SetRat(.val)} }func vtoc( Value) complexVal { returncomplexVal{, int64Val(0)} }func makeInt( *big.Int) Value {if .IsInt64() {returnint64Val(.Int64()) }returnintVal{}}func makeRat( *big.Rat) Value { := .Num() := .Denom()ifsmallInt() && smallInt() {// ok to remain fractionreturnratVal{} }// components too large => switch to floatreturnfloatVal{newFloat().SetRat()}}var floatVal0 = floatVal{newFloat()}func makeFloat( *big.Float) Value {// convert -0if .Sign() == 0 {returnfloatVal0 }if .IsInf() {returnunknownVal{} }// No attempt is made to "go back" to ratVal, even if possible, // to avoid providing the illusion of a mathematically exact // representation.returnfloatVal{}}func makeComplex(, Value) Value {if .Kind() == Unknown || .Kind() == Unknown {returnunknownVal{} }returncomplexVal{, }}func makeFloatFromLiteral( string) Value {if , := newFloat().SetString(); {ifsmallFloat() {// ok to use rationalsif .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(); {returnratVal{} } }// otherwise use floatsreturnmakeFloat() }returnnil}// 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 {ifmath.IsInf(, 0) {returnfalse } , := 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() {returnfalse } := .MantExp(nil)return -maxExp < && < maxExp}// ----------------------------------------------------------------------------// Factories// MakeUnknown returns the [Unknown] value.func () Value { returnunknownVal{} }// MakeBool returns the [Bool] value for b.func ( bool) Value { returnboolVal() }// 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 { returnint64Val() }// MakeUint64 returns the [Int] value for x.func ( uint64) Value {if < 1<<63 {returnint64Val(int64()) }returnintVal{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 {ifmath.IsInf(, 0) || math.IsNaN() {returnunknownVal{} }ifsmallFloat64() {returnratVal{newRat().SetFloat64( + 0)} // convert -0 to 0 }returnfloatVal{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 {casetoken.INT:if , := strconv.ParseInt(, 0, 64); == nil {returnint64Val() }if , := newInt().SetString(, 0); {returnintVal{} }casetoken.FLOAT:if := makeFloatFromLiteral(); != nil {return }casetoken.IMAG:if := len(); > 0 && [-1] == 'i' {if := makeFloatFromLiteral([:-1]); != nil {returnmakeComplex(int64Val(0), ) } }casetoken.CHAR:if := len(); >= 2 {if , , , := strconv.UnquoteChar([1:-1], '\''); == nil {returnMakeInt64(int64()) } }casetoken.STRING:if , := strconv.Unquote(); == nil {returnMakeString() }default:panic(fmt.Sprintf("%v is not a valid token", )) }returnunknownVal{}}// ----------------------------------------------------------------------------// 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) {caseboolVal:returnbool()caseunknownVal:returnfalsedefault: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()caseunknownVal: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) {caseint64Val:returnint64(), truecaseintVal:return .val.Int64(), false// not an int64Val and thus not exactcaseunknownVal:return0, falsedefault: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) {caseint64Val:returnuint64(), >= 0caseintVal:return .val.Uint64(), .val.IsUint64()caseunknownVal:return0, falsedefault:panic(fmt.Sprintf("%v not an Int", )) }}// Float32Val is like [Float64Val] but for float32 instead of float64.func ( Value) (float32, bool) {switch x := .(type) {caseint64Val: := float32()return , int64Val() == caseintVal: , := newFloat().SetInt(.val).Float32()return , == big.ExactcaseratVal:return .val.Float32()casefloatVal: , := .val.Float32()return , == big.ExactcaseunknownVal:return0, falsedefault: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) {caseint64Val: := float64(int64())return , int64Val() == caseintVal: , := newFloat().SetInt(.val).Float64()return , == big.ExactcaseratVal:return .val.Float64()casefloatVal: , := .val.Float64()return , == big.ExactcaseunknownVal:return0, falsedefault: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 nilfunc ( Value) any {switch x := .(type) {caseboolVal:returnbool()case *stringVal:return .string()caseint64Val:returnint64()caseintVal:return .valcaseratVal:return .valcasefloatVal:return .valdefault:returnnil }}// 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 Unknownfunc ( any) Value {switch x := .(type) {casebool:returnboolVal()casestring:return &stringVal{s: }caseint64:returnint64Val()case *big.Int:returnmakeInt()case *big.Rat:returnmakeRat()case *big.Float:returnmakeFloat()default:returnunknownVal{} }}// 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) {caseint64Val: := uint64()if < 0 { = uint64(-) }return64 - bits.LeadingZeros64()caseintVal:return .val.BitLen()caseunknownVal:return0default: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) {caseint64Val:switch {case < 0:return -1case > 0:return1 }return0caseintVal:return .val.Sign()caseratVal:return .val.Sign()casefloatVal:return .val.Sign()casecomplexVal:return (.re) | (.im)caseunknownVal:return1// avoid spurious division by zero errorsdefault:panic(fmt.Sprintf("%v not numeric", )) }}// ----------------------------------------------------------------------------// Support for assembling/disassembling numeric valuesconst (// 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 {varintValswitch x := .(type) {caseint64Val: = i64toi()caseintVal: = default:panic(fmt.Sprintf("%v not an Int", )) } := .val.Bits() := make([]byte, len()*wordSize) := 0for , := range {for := 0; < wordSize; ++ { [] = byte() >>= 8 ++ } }// remove leading 0'sfor > 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) := 0varbig.Wordvaruintfor , := range { |= big.Word() << if += 8; == wordSize*8 { [] = ++ = 0 = 0 } }// store last wordif < len() { [] = ++ }// remove leading 0'sfor > 0 && [-1] == 0 { -- }returnmakeInt(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) {caseint64Val, intVal:returncaseratVal:returnmakeInt(.val.Num())casefloatVal:ifsmallFloat(.val) { , := .val.Rat(nil)returnmakeInt(.Num()) }caseunknownVal:breakdefault:panic(fmt.Sprintf("%v not Int or Float", )) }returnunknownVal{}}// 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) {caseint64Val, intVal:returnint64Val(1)caseratVal:returnmakeInt(.val.Denom())casefloatVal:ifsmallFloat(.val) { , := .val.Rat(nil)returnmakeInt(.Denom()) }caseunknownVal:breakdefault:panic(fmt.Sprintf("%v not Int or Float", )) }returnunknownVal{}}// 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) {caseunknownVal:returncaseint64Val, intVal, ratVal, floatVal:returnmakeComplex(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) {caseunknownVal, int64Val, intVal, ratVal, floatVal:returncasecomplexVal:return .redefault: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) {caseunknownVal:returncaseint64Val, intVal, ratVal, floatVal:returnint64Val(0)casecomplexVal:return .imdefault: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) {caseint64Val, intVal:returncaseratVal:if .val.IsInt() {returnmakeInt(.val.Num()) }casefloatVal:// 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.)ifsmallFloat(.val) { := newInt()if , := .val.Int(); == big.Exact {returnmakeInt() }// 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 > 0varbig.Float .SetPrec(prec - )// try rounding down a little .SetMode(big.ToZero) .Set(.val)if , := .Int(); == big.Exact {returnmakeInt() }// try rounding up a little .SetMode(big.AwayFromZero) .Set(.val)if , := .Int(); == big.Exact {returnmakeInt() } }casecomplexVal:if := ToFloat(); .Kind() == Float {return () } }returnunknownVal{}}// 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) {caseint64Val:returni64tor() // x is always a small intcaseintVal:ifsmallInt(.val) {returnitor() }returnitof()caseratVal, floatVal:returncasecomplexVal:ifSign(.im) == 0 {return (.re) } }returnunknownVal{}}// 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) {caseint64Val, intVal, ratVal, floatVal:returnvtoc()casecomplexVal:return }returnunknownVal{}}// ----------------------------------------------------------------------------// Operations// is32bit reports whether x can be represented using 32 bits.func is32bit( int64) bool {const = 32return -1<<(-1) <= && <= 1<<(-1)-1}// is63bit reports whether x can be represented using 63 bits.func is63bit( int64) bool {const = 63return -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 {casetoken.ADD:switch .(type) {caseunknownVal, int64Val, intVal, ratVal, floatVal, complexVal:return }casetoken.SUB:switch y := .(type) {caseunknownVal:returncaseint64Val:if := -; != {return// no overflow }returnmakeInt(newInt().Neg(big.NewInt(int64())))caseintVal:returnmakeInt(newInt().Neg(.val))caseratVal:returnmakeRat(newRat().Neg(.val))casefloatVal:returnmakeFloat(newFloat().Neg(.val))casecomplexVal: := (token.SUB, .re, 0) := (token.SUB, .im, 0)returnmakeComplex(, ) }casetoken.XOR: := newInt()switch y := .(type) {caseunknownVal:returncaseint64Val: .Not(big.NewInt(int64()))caseintVal: .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 }returnmakeInt()casetoken.NOT:switch y := .(type) {caseunknownVal:returncaseboolVal: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 -1caseunknownVal:return0caseboolVal, *stringVal:return1caseint64Val:return2caseintVal:return3caseratVal:return4casefloatVal:return5casecomplexVal:return6 }}// 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) {caseintVal:switch x1 := .(type) {caseint64Val:returni64toi(), }caseratVal:switch x1 := .(type) {caseint64Val:returni64tor(), caseintVal:returnitor(), }casefloatVal:switch x1 := .(type) {caseint64Val:returni64tof(), caseintVal:returnitof(), caseratVal:returnrtof(), }casecomplexVal:returnvtoc(), }// 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) {caseunknownVal:returncaseboolVal: := .(boolVal)switch {casetoken.LAND:return && casetoken.LOR:return || }caseint64Val: := int64() := int64(.(int64Val))varint64switch {casetoken.ADD:if !is63bit() || !is63bit() {returnmakeInt(newInt().Add(big.NewInt(), big.NewInt())) } = + casetoken.SUB:if !is63bit() || !is63bit() {returnmakeInt(newInt().Sub(big.NewInt(), big.NewInt())) } = - casetoken.MUL:if !is32bit() || !is32bit() {returnmakeInt(newInt().Mul(big.NewInt(), big.NewInt())) } = * casetoken.QUO:returnmakeRat(big.NewRat(, ))casetoken.QUO_ASSIGN: // force integer division = / casetoken.REM: = % casetoken.AND: = & casetoken.OR: = | casetoken.XOR: = ^ casetoken.AND_NOT: = &^ default:goto }returnint64Val()caseintVal: := .val := .(intVal).val := newInt()switch {casetoken.ADD: .Add(, )casetoken.SUB: .Sub(, )casetoken.MUL: .Mul(, )casetoken.QUO:returnmakeRat(newRat().SetFrac(, ))casetoken.QUO_ASSIGN: // force integer division .Quo(, )casetoken.REM: .Rem(, )casetoken.AND: .And(, )casetoken.OR: .Or(, )casetoken.XOR: .Xor(, )casetoken.AND_NOT: .AndNot(, )default:goto }returnmakeInt()caseratVal: := .val := .(ratVal).val := newRat()switch {casetoken.ADD: .Add(, )casetoken.SUB: .Sub(, )casetoken.MUL: .Mul(, )casetoken.QUO: .Quo(, )default:goto }returnmakeRat()casefloatVal: := .val := .(floatVal).val := newFloat()switch {casetoken.ADD: .Add(, )casetoken.SUB: .Sub(, )casetoken.MUL: .Mul(, )casetoken.QUO: .Quo(, )default:goto }returnmakeFloat()casecomplexVal: := .(complexVal) , := .re, .im , := .re, .imvar , Valueswitch {casetoken.ADD:// (a+c) + i(b+d) = add(, ) = add(, )casetoken.SUB:// (a-c) + i(b-d) = sub(, ) = sub(, )casetoken.MUL:// (ac-bd) + i(bc+ad) := mul(, ) := mul(, ) := mul(, ) := mul(, ) = sub(, ) = add(, )casetoken.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 }returnmakeComplex(, )case *stringVal:if == token.ADD {return &stringVal{l: , r: .(*stringVal)} } }:panic(fmt.Sprintf("invalid binary operation %v %s %v", , , ))}func add(, Value) Value { returnBinaryOp(, token.ADD, ) }func sub(, Value) Value { returnBinaryOp(, token.SUB, ) }func mul(, Value) Value { returnBinaryOp(, token.MUL, ) }func quo(, Value) Value { returnBinaryOp(, 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) {caseunknownVal:returncaseint64Val:if == 0 {return }switch {casetoken.SHL: := i64toi().valreturnmakeInt(.Lsh(, ))casetoken.SHR:return >> }caseintVal:if == 0 {return } := newInt()switch {casetoken.SHL:returnmakeInt(.Lsh(.val, ))casetoken.SHR:returnmakeInt(.Rsh(.val, )) } }panic(fmt.Sprintf("invalid shift %v %s %d", , , ))}func cmpZero( int, token.Token) bool {switch {casetoken.EQL:return == 0casetoken.NEQ:return != 0casetoken.LSS:return < 0casetoken.LEQ:return <= 0casetoken.GTR:return > 0casetoken.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) {caseunknownVal:returnfalsecaseboolVal: := .(boolVal)switch {casetoken.EQL:return == casetoken.NEQ:return != }caseint64Val: := .(int64Val)switch {casetoken.EQL:return == casetoken.NEQ:return != casetoken.LSS:return < casetoken.LEQ:return <= casetoken.GTR:return > casetoken.GEQ:return >= }caseintVal:returncmpZero(.val.Cmp(.(intVal).val), )caseratVal:returncmpZero(.val.Cmp(.(ratVal).val), )casefloatVal:returncmpZero(.val.Cmp(.(floatVal).val), )casecomplexVal: := .(complexVal) := (.re, token.EQL, .re) := (.im, token.EQL, .im)switch {casetoken.EQL:return && casetoken.NEQ:return ! || ! }case *stringVal: := .string() := .(*stringVal).string()switch {casetoken.EQL:return == casetoken.NEQ:return != casetoken.LSS:return < casetoken.LEQ:return <= casetoken.GTR:return > casetoken.GEQ:return >= } }panic(fmt.Sprintf("invalid comparison %v %s %v", , , ))}
The pages are generated with Goldsv0.7.0-preview. (GOOS=linux GOARCH=amd64)
Golds is a Go 101 project developed by Tapir Liu.
PR and bug reports are welcome and can be submitted to the issue list.
Please follow @zigo_101 (reachable from the left QR code) to get the latest news of Golds.