1/* 2 * gomacro - A Go interpreter with Lisp-like macros 3 * 4 * Copyright (C) 2017-2019 Massimiliano Ghilardi 5 * 6 * This Source Code Form is subject to the terms of the Mozilla Public 7 * License, v. 2.0. If a copy of the MPL was not distributed with this 8 * file, You can obtain one at http://mozilla.org/MPL/2.0/. 9 * 10 * 11 * val.go 12 * 13 * Created on May 27, 2017 14 * Author Massimiliano Ghilardi 15 */ 16 17package untyped 18 19import ( 20 "fmt" 21 "go/constant" 22 "go/token" 23 "go/types" 24 "math/big" 25 "strings" 26 27 "github.com/cosmos72/gomacro/base/output" 28) 29 30// untyped value 31type Val struct { 32 Kind Kind // default type 33 Val constant.Value 34} 35 36func GoUntypedToKind(gkind types.BasicKind) Kind { 37 var kind Kind 38 switch gkind { 39 case types.UntypedBool: 40 kind = Bool 41 case types.UntypedInt: 42 kind = Int 43 case types.UntypedRune: 44 kind = Rune 45 case types.UntypedFloat: 46 kind = Float 47 case types.UntypedComplex: 48 kind = Complex 49 case types.UntypedString: 50 kind = String 51 case types.UntypedNil: 52 kind = None 53 default: 54 output.Errorf("unsupported types.BasicKind: %v", gkind) 55 } 56 return kind 57} 58 59func (val *Val) Marshal() string { 60 return Marshal(val.Kind, val.Val) 61} 62 63func Marshal(kind Kind, val constant.Value) string { 64 // untyped constants have arbitrary precision... they may overflow integers 65 var s string 66 switch kind { 67 case None: 68 s = "nil" 69 case Bool: 70 if constant.BoolVal(val) { 71 s = "bool:true" 72 } else { 73 s = "bool:false" 74 } 75 case Int: 76 s = fmt.Sprintf("int:%s", val.ExactString()) 77 case Rune: 78 s = fmt.Sprintf("rune:%s", val.ExactString()) 79 case Float: 80 s = fmt.Sprintf("float:%s", val.ExactString()) 81 case Complex: 82 s = fmt.Sprintf("complex:%s:%s", constant.Real(val).ExactString(), constant.Imag(val).ExactString()) 83 case String: 84 s = fmt.Sprintf("string:%s", constant.StringVal(val)) 85 } 86 return s 87} 88 89func UnmarshalVal(marshalled string) *Val { 90 kind, val := Unmarshal(marshalled) 91 return &Val{kind, val} 92} 93 94func Unmarshal(marshalled string) (Kind, constant.Value) { 95 var skind, str string 96 if sep := strings.IndexByte(marshalled, ':'); sep >= 0 { 97 skind = marshalled[:sep] 98 str = marshalled[sep+1:] 99 } else { 100 skind = marshalled 101 } 102 103 var kind Kind 104 var val constant.Value 105 switch skind { 106 case "bool": 107 kind = Bool 108 if str == "true" { 109 val = constant.MakeBool(true) 110 } else { 111 val = constant.MakeBool(false) 112 } 113 case "int": 114 kind = Int 115 val = constant.MakeFromLiteral(str, token.INT, 0) 116 case "rune": 117 kind = Rune 118 val = constant.MakeFromLiteral(str, token.INT, 0) 119 case "float": 120 kind = Float 121 val = unmarshalFloat(str) 122 case "complex": 123 kind = Complex 124 if sep := strings.IndexByte(str, ':'); sep >= 0 { 125 re := unmarshalFloat(str[:sep]) 126 im := unmarshalFloat(str[sep+1:]) 127 val = constant.BinaryOp(constant.ToComplex(re), token.ADD, constant.MakeImag(im)) 128 } else { 129 val = constant.ToComplex(unmarshalFloat(str)) 130 } 131 case "string": 132 kind = String 133 val = constant.MakeString(str) 134 case "nil": 135 kind = None 136 default: 137 kind = None 138 } 139 return kind, val 140} 141 142// generalization of constant.MakeFromLiteral, accepts the fractions generated by 143// constant.Value.ExactString() for floating-point values 144func unmarshalFloat(str string) constant.Value { 145 if sep := strings.IndexByte(str, '/'); sep >= 0 { 146 x := constant.MakeFromLiteral(str[:sep], token.FLOAT, 0) 147 y := constant.MakeFromLiteral(str[sep+1:], token.FLOAT, 0) 148 return constant.BinaryOp(x, token.QUO, y) 149 } 150 return constant.MakeFromLiteral(str, token.FLOAT, 0) 151} 152 153func (lit *Val) BigInt() (*big.Int, error) { 154 val := lit.Val 155 switch lit.Kind { 156 case Int, Rune: 157 if i, ok := constant.Int64Val(val); ok { 158 return big.NewInt(i), nil 159 } 160 if bi, ok := new(big.Int).SetString(val.ExactString(), 10); ok { 161 return bi, nil 162 } 163 } 164 return nil, output.MakeRuntimeError("cannot convert untyped %s to math/big.Int: %v", lit.Kind, lit.Val) 165} 166 167func (lit *Val) BigRat() (*big.Rat, error) { 168 val := lit.Val 169 switch lit.Kind { 170 case Int, Rune: 171 if i, ok := constant.Int64Val(val); ok { 172 return big.NewRat(i, 1), nil 173 } 174 fallthrough 175 case Float: 176 if br, ok := new(big.Rat).SetString(val.ExactString()); ok { 177 return br, nil 178 } 179 } 180 return nil, output.MakeRuntimeError("cannot convert untyped %s to math/big.Rat: %v", lit.Kind, lit.Val) 181} 182