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