// Copyright 2011 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. // This file implements operations on ideal constants. package types import ( "go/token" "math/big" "strconv" ) // TODO(gri) Consider changing the API so Const is an interface // and operations on consts don't have to type switch. // A Const implements an ideal constant Value. // The zero value z for a Const is not a valid constant value. type Const struct { // representation of constant values: // ideal bool -> bool // ideal int -> *big.Int // ideal float -> *big.Rat // ideal complex -> cmplx // ideal string -> string val interface{} } // Representation of complex values. type cmplx struct { re, im *big.Rat } func assert(cond bool) { if !cond { panic("go/types internal error: assertion failed") } } // MakeConst makes an ideal constant from a literal // token and the corresponding literal string. func MakeConst(tok token.Token, lit string) Const { switch tok { case token.INT: var x big.Int _, ok := x.SetString(lit, 0) assert(ok) return Const{&x} case token.FLOAT: var y big.Rat _, ok := y.SetString(lit) assert(ok) return Const{&y} case token.IMAG: assert(lit[len(lit)-1] == 'i') var im big.Rat _, ok := im.SetString(lit[0 : len(lit)-1]) assert(ok) return Const{cmplx{big.NewRat(0, 1), &im}} case token.CHAR: assert(lit[0] == '\'' && lit[len(lit)-1] == '\'') code, _, _, err := strconv.UnquoteChar(lit[1:len(lit)-1], '\'') assert(err == nil) return Const{big.NewInt(int64(code))} case token.STRING: s, err := strconv.Unquote(lit) assert(err == nil) return Const{s} } panic("unreachable") } // MakeZero returns the zero constant for the given type. func MakeZero(typ *Type) Const { // TODO(gri) fix this return Const{0} } // Match attempts to match the internal constant representations of x and y. // If the attempt is successful, the result is the values of x and y, // if necessary converted to have the same internal representation; otherwise // the results are invalid. func (x Const) Match(y Const) (u, v Const) { switch a := x.val.(type) { case bool: if _, ok := y.val.(bool); ok { u, v = x, y } case *big.Int: switch y.val.(type) { case *big.Int: u, v = x, y case *big.Rat: var z big.Rat z.SetInt(a) u, v = Const{&z}, y case cmplx: var z big.Rat z.SetInt(a) u, v = Const{cmplx{&z, big.NewRat(0, 1)}}, y } case *big.Rat: switch y.val.(type) { case *big.Int: v, u = y.Match(x) case *big.Rat: u, v = x, y case cmplx: u, v = Const{cmplx{a, big.NewRat(0, 0)}}, y } case cmplx: switch y.val.(type) { case *big.Int, *big.Rat: v, u = y.Match(x) case cmplx: u, v = x, y } case string: if _, ok := y.val.(string); ok { u, v = x, y } default: panic("unreachable") } return } // Convert attempts to convert the constant x to a given type. // If the attempt is successful, the result is the new constant; // otherwise the result is invalid. func (x Const) Convert(typ *Type) Const { // TODO(gri) implement this switch x.val.(type) { case bool: case *big.Int: case *big.Rat: case cmplx: case string: } return x } func (x Const) String() string { switch x := x.val.(type) { case bool: if x { return "true" } return "false" case *big.Int: return x.String() case *big.Rat: return x.FloatString(10) // 10 digits of precision after decimal point seems fine case cmplx: // TODO(gri) don't print 0 components return x.re.FloatString(10) + " + " + x.im.FloatString(10) + "i" case string: return x } panic("unreachable") } func (x Const) UnaryOp(op token.Token) Const { panic("unimplemented") } func (x Const) BinaryOp(op token.Token, y Const) Const { var z interface{} switch x := x.val.(type) { case bool: z = binaryBoolOp(x, op, y.val.(bool)) case *big.Int: z = binaryIntOp(x, op, y.val.(*big.Int)) case *big.Rat: z = binaryFloatOp(x, op, y.val.(*big.Rat)) case cmplx: z = binaryCmplxOp(x, op, y.val.(cmplx)) case string: z = binaryStringOp(x, op, y.val.(string)) default: panic("unreachable") } return Const{z} } func binaryBoolOp(x bool, op token.Token, y bool) interface{} { switch op { case token.EQL: return x == y case token.NEQ: return x != y } panic("unreachable") } 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") } func binaryFloatOp(x *big.Rat, op token.Token, y *big.Rat) interface{} { var z big.Rat 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.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 binaryCmplxOp(x cmplx, op token.Token, y cmplx) interface{} { a, b := x.re, x.im c, d := y.re, y.im switch op { case token.ADD: // (a+c) + i(b+d) var re, im big.Rat re.Add(a, c) im.Add(b, d) return cmplx{&re, &im} case token.SUB: // (a-c) + i(b-d) var re, im big.Rat re.Sub(a, c) im.Sub(b, d) return cmplx{&re, &im} 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) var re, im big.Rat re.Sub(&ac, &bd) im.Add(&bc, &ad) return cmplx{&re, &im} 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)) var re, im big.Rat re.Add(&ac, &bd) re.Quo(&re, &s) im.Sub(&bc, &ad) im.Quo(&im, &s) return cmplx{&re, &im} case token.EQL: return a.Cmp(c) == 0 && b.Cmp(d) == 0 case token.NEQ: return a.Cmp(c) != 0 || b.Cmp(d) != 0 } panic("unreachable") } func binaryStringOp(x string, op token.Token, y string) interface{} { switch op { case token.ADD: return x + y case token.EQL: return x == y case token.NEQ: return x != y case token.LSS: return x < y case token.LEQ: return x <= y case token.GTR: return x > y case token.GEQ: return x >= y } panic("unreachable") }