| // 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 ( |
| "big" |
| "go/token" |
| "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 := 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") |
| } |