blob: 048f63bb7d380ebadb59405a5a42e2b371a979f1 [file] [log] [blame]
// 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")
}