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 * place_shifts.go 12 * 13 * Created on May 17, 2017 14 * Author Massimiliano Ghilardi 15 */ 16 17package fast 18 19import ( 20 "go/token" 21 r "reflect" 22 23 "github.com/cosmos72/gomacro/base/reflect" 24) 25 26 27:import ( 28 "fmt" 29 "go/ast" 30 "go/token" 31 r "reflect" 32) 33 34 35:func upcasefirstbyte(str string) string { 36 if len(str) > 0 && str[0] >= 'a' && str[0] <= 'z' { 37 bytes := []byte(str) 38 bytes[0] -= 'a' - 'A' 39 return string(bytes) 40 } 41 return str 42} 43 44:func makeupcase(node ast.Node, name string) ast.Node { 45 // go/ast.SelectorExpr requires the foo in x.foo to be an *ast.Ident, cannot unquote there 46 kind := ~"{~,node . foo} 47 kind.Sel = &ast.Ident{Name: upcasefirstbyte(name)} 48 return kind 49} 50 51:func makekind(typ ast.Node) ast.Node { 52 name := EvalType(typ).Name() 53 return makeupcase(~'r, name) 54} 55 56:func fsetplace(opnode, typ, expr, roundup ast.Node) ast.Node { 57 // the return type of Eval() and EvalType() varies. better check early. 58 var t r.Type = EvalType(typ) 59 var get, set ast.Node 60 var shift *ast.BinaryExpr = ~"{result >> ~,expr} 61 op := Eval(opnode).(token.Token) 62 shift.Op = op 63 signed := false 64 65 switch t.Kind() { 66 case r.Int, r.Int8, r.Int16, r.Int32, r.Int64: 67 get = ~"{lhs.Int()} 68 set = ~"{lhs.SetInt(~,shift)} 69 signed = true 70 case r.Uint, r.Uint8, r.Uint16, r.Uint32, r.Uint64, r.Uintptr: 71 get = ~"{lhs.Uint()} 72 set = ~"{lhs.SetUint(~,shift)} 73 default: 74 panic("invalid operator " + op.String() + "= on " + t.String()) 75 } 76 77 if signed && Eval(roundup) == true { 78 return ~"{ 79 // used to optimize division by constant-power-of-two 80 ret = func(env *Env) (Stmt, *Env) { 81 lhs := lhsfun(env) 82 result := ~,get 83 if result < 0 { 84 result += roundup 85 } 86 ~,set 87 env.IP++ 88 return env.Code[env.IP], env 89 } 90 } 91 } 92 return ~"{ 93 ret = func(env *Env) (Stmt, *Env) { 94 lhs := lhsfun(env) 95 result := ~,get 96 ~,set 97 env.IP++ 98 return env.Code[env.IP], env 99 } 100 } 101} 102 103:func fsetmap(opnode, typ, expr, option ast.Node) ast.Node { 104 // the return type of Eval() and EvalType() varies. better check early. 105 var t r.Type = EvalType(typ) 106 var get ast.Node 107 var shift *ast.BinaryExpr = ~"{result >> ~,expr} 108 op := Eval(opnode).(token.Token) 109 shift.Op = op 110 signed := false 111 112 switch t.Kind() { 113 case r.Int, r.Int8, r.Int16, r.Int32, r.Int64: 114 get = ~"{lhs.MapIndex(key).Int()} 115 signed = true 116 case r.Uint, r.Uint8, r.Uint16, r.Uint32, r.Uint64, r.Uintptr: 117 get = ~"{lhs.MapIndex(key).Uint()} 118 default: 119 panic("invalid operator " + op.String() + "= on " + t.String()) 120 } 121 122 var opt interface{} = Eval(option) 123 if signed && opt == true { 124 return ~"{ 125 // used to optimize division by constant-power-of-two 126 ret = func(env *Env) (Stmt, *Env) { 127 lhs := lhsfun(env) 128 key := keyfun(env) 129 result := ~,get 130 if result < 0 { 131 result += roundup 132 } 133 v := r.ValueOf(~,shift) 134 if v.Type() != rt { 135 v = convert(v, rt) 136 } 137 lhs.SetMapIndex(key, v) 138 env.IP++ 139 return env.Code[env.IP], env 140 } 141 } 142 } else { 143 return ~"{ 144 ret = func(env *Env) (Stmt, *Env) { 145 lhs := lhsfun(env) 146 key := keyfun(env) 147 result := ~,get 148 v := r.ValueOf(~,shift) 149 if v.Type() != rt { 150 v = convert(v, rt) 151 } 152 lhs.SetMapIndex(key, v) 153 env.IP++ 154 return env.Code[env.IP], env 155 } 156 } 157 } 158} 159 160:macro setplace_const(opnode, typ, option ast.Node) ast.Node { 161 return fsetplace(opnode, typ, ~'val, option) 162} 163 164:macro setplace_expr(opnode, typ ast.Node) ast.Node { 165 return fsetplace(opnode, typ, ~'{fun(env)}, nil) 166} 167 168:macro setmap_const(opnode, typ, option ast.Node) ast.Node { 169 return fsetmap(opnode, typ, ~'val, option) 170} 171 172:macro setmap_expr(opnode, typ ast.Node) ast.Node { 173 return fsetmap(opnode, typ, ~'{fun(env)}, ~'0) 174} 175 176 177:func list_types(typelist []ast.Stmt) []ast.Node { 178 rets := make([]ast.Node, 0, len(typelist)) 179 for _, typ := range typelist { 180 t := EvalType(typ) 181 if t == nil { 182 rets = append(rets, ~'nil) 183 } else if t.Kind() == r.Int { 184 rets = append(rets, ~'int, ~'int8, ~'int16, ~'int32, ~'int64) 185 } else if t.Kind() == r.Uint { 186 rets = append(rets, ~'uint, ~'uint8, ~'uint16, ~'uint32, ~'uint64, ~'uintptr) 187 } else if t.Kind() == r.Float64 { 188 rets = append(rets, ~'float32, ~'float64) 189 } else if t.Kind() == r.Complex128 { 190 rets = append(rets, ~'complex64, ~'complex128) 191 } else { 192 rets = append(rets, typ) 193 } 194 } 195 return rets 196} 197 198:macro setplaces_const(opnode, types, roundup ast.Node) ast.Node { 199 // separate cases for int8, uint16... not needed 200 typelist := types.(*ast.BlockStmt).List 201 caselist := make([]ast.Stmt, len(typelist)) 202 mapcaselist := make([]ast.Stmt, len(typelist)) 203 for i, typ := range typelist { 204 kind := makekind(typ) 205 caselist[i] = ~"{case ~,kind: 206 setplace_const; ~,opnode; ~,typ; ~,roundup 207 } 208 mapcaselist[i] = ~"{case ~,kind: 209 setmap_const; ~,opnode; ~,typ; ~,roundup 210 } 211 } 212 var conv ast.Node 213 var isroundup interface{} = Eval(roundup) 214 if isroundup == true { 215 conv = ~'shift 216 opnode = ~'{token.QUO} // for error messages 217 } else { 218 conv = ~'{r.ValueOf(val).Uint()} 219 } 220 221 return ~"{ 222 var ret Stmt 223 lhsfun := place.Fun 224 keyfun := place.MapKey 225 val := ~,conv 226 227 t := place.Type 228 rt := t.ReflectType() 229 cat := reflect.Category(t.Kind()) 230 if keyfun == nil { 231 switch cat { 232 ~,@caselist 233 } 234 } else { 235 switch cat { 236 ~,@mapcaselist 237 } 238 } 239 if ret == nil { 240 c.Errorf("invalid operator %s= on <%v>", ~,opnode, place.Type) 241 } 242 return ret 243 } 244} 245 246:macro setplaces_expr(opnode, ltyp, rtypes ast.Node) ast.Node { 247 rtypelist := list_types(rtypes.(*ast.BlockStmt).List) 248 249 caselist := make([]ast.Stmt, len(rtypelist)) 250 for i, rtyp := range rtypelist { 251 caselist[i] = ~"{~typecase func(*Env) ~,rtyp: 252 setplace_expr; ~,opnode; ~,ltyp 253 } 254 } 255 return ~"{ 256 switch fun := fun.(type) { 257 ~,@caselist 258 } 259 } 260} 261 262:macro setmaps_expr(opnode, ltyp, rtypes ast.Node) ast.Node { 263 rtypelist := list_types(rtypes.(*ast.BlockStmt).List) 264 265 caselist := make([]ast.Stmt, len(rtypelist)) 266 for i, rtyp := range rtypelist { 267 caselist[i] = ~"{~typecase func(*Env) ~,rtyp: 268 setmap_expr; ~,opnode; ~,ltyp 269 } 270 } 271 return ~"{ 272 switch fun := fun.(type) { 273 ~,@caselist 274 } 275 } 276} 277 278:macro setplacess_expr(opnode, ltypes, rtypes ast.Node) ast.Node { 279 ltypelist := ltypes.(*ast.BlockStmt).List 280 281 caselist := make([]ast.Stmt, len(ltypelist)) 282 mapcaselist := make([]ast.Stmt, len(ltypelist)) 283 for i, ltyp := range ltypelist { 284 kind := makekind(ltyp) 285 caselist[i] = ~"{case ~,kind: 286 setplaces_expr; ~,opnode; ~,ltyp; ~,rtypes 287 } 288 mapcaselist[i] = ~"{case ~,kind: 289 setmaps_expr; ~,opnode; ~,ltyp; ~,rtypes 290 } 291 } 292 return ~"{ 293 var ret Stmt 294 lhsfun := place.Fun 295 keyfun := place.MapKey 296 t := place.Type 297 rt := t.ReflectType() 298 cat := reflect.Category(t.Kind()) 299 if keyfun == nil { 300 switch cat { 301 ~,@caselist 302 } 303 } else { 304 switch cat { 305 ~,@mapcaselist 306 } 307 } 308 if ret == nil { 309 c.Errorf("invalid operator %s= on <%v>", ~,opnode, place.Type) 310 } 311 return ret 312 } 313} 314 315// placeShlConst compiles 'place <<= constant' 316func (c *Comp) placeShlConst(place *Place, val I) Stmt { 317 if isLiteralNumber(val, 0) { 318 return c.placeForSideEffects(place) 319 } 320 setplaces_const; token.SHL; {int; uint}; false 321} 322 323// placeShlExpr compiles 'place <<= expression' 324func (c *Comp) placeShlExpr(place *Place, fun I) Stmt { 325 setplacess_expr; token.SHL; {int; uint}; {uint} 326} 327 328// placeShrConst compiles 'place >>= constant' 329func (c *Comp) placeShrConst(place *Place, val I) Stmt { 330 if isLiteralNumber(val, 0) { 331 return c.placeForSideEffects(place) 332 } 333 setplaces_const; token.SHR; {int; uint}; false 334} 335 336// placeShrExpr compiles 'place >>= expression' 337func (c *Comp) placeShrExpr(place *Place, fun I) Stmt { 338 setplacess_expr; token.SHR; {int; uint}; {uint} 339} 340 341// placeQuoPow2 compiles 'place /= constant-power-of-two' 342func (c *Comp) placeQuoPow2(place *Place, val I) Stmt { 343 if isLiteralNumber(val, 0) { 344 c.Errorf("division by %v <%v>", val, r.TypeOf(val)) 345 return nil 346 } else if isLiteralNumber(val, 1) { 347 return c.placeForSideEffects(place) 348 } 349 350 ypositive := true 351 yv := r.ValueOf(val) 352 ycat := reflect.Category(yv.Kind()) 353 var y uint64 354 switch ycat { 355 case r.Int: 356 sy := yv.Int() 357 if sy < 0 { 358 ypositive = false 359 y = uint64(-sy) 360 } else { 361 y = uint64(sy) 362 } 363 case r.Uint: 364 y = yv.Uint() 365 default: 366 // floating point or complex division 367 return nil 368 } 369 if !isPowerOfTwo(y) { 370 // division by multiplication and shift not implemented... 371 return nil 372 } 373 // attention: xe / (2**n) and xe >> n have different truncation rules for negative xe: 374 // quotient / truncates toward zero 375 // right shift >> truncates toward negative infinity 376 // see quoPow2() in binary_ops.go for more details 377 shift := integerLen(y) - 1 378 379 if !ypositive { 380 return nil // not yet implemented 381 } 382 383 var roundup int64 384 if ycat == r.Int { 385 // fix rounding mode 386 roundup = int64(y-1) 387 } 388 setplaces_const; token.SHR; {int; uint}; true 389} 390 391