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 * binary_relops.go
12 *
13 *  Created on Apr 12, 2017
14 *      Author Massimiliano Ghilardi
15 */
16
17package fast
18
19import (
20	"go/ast"
21	r "reflect"
22
23	// . "github.com/cosmos72/gomacro/base"
24)
25
26:import (
27	"fmt"
28	"go/ast"
29	"go/token"
30	r "reflect"
31)
32
33:func upcasefirstbyte(str string) string {
34	if len(str) > 0 && str[0] >= 'a' && str[0] <= 'z' {
35		bytes := []byte(str)
36		bytes[0] -= 'a' - 'A'
37		return string(bytes)
38	}
39	return str
40}
41
42:func makekind(typ ast.Node) ast.Node {
43	t := EvalType(typ)
44
45	// go/ast.SelectorExpr requires the foo in r.foo to be an *ast.Ident, cannot unquote there
46	kind := ~"{r . foo}
47	kind.Sel = &ast.Ident{Name: upcasefirstbyte(t.Name())}
48	return kind
49}
50
51
52:func convertvalue1(typ, val ast.Node) ast.Node {
53	var t r.Type = EvalType(typ)
54	if t == nil {
55		// keep the result wrapped in a reflect.Value
56		return val
57	}
58	// unwrap the result
59	tname := t.Name()
60	// remove final digits from t.Name()
61	// needed to convert Uint64 -> Uint etc. to calls reflect.Value.{tname}
62	for len(tname) != 0 {
63		ch := tname[len(tname)-1]
64		if ch < '0' || ch > '9' {
65			break
66		}
67		tname = tname[0:len(tname)-1]
68	}
69	if tname == "uintptr" {
70		tname = "uint" // use reflect.Value.Uint()
71	}
72	sel := ~"{~,val . foo} // we modify it destructively
73	sel.Sel = &ast.Ident{Name: upcasefirstbyte(tname)}
74
75	switch t.Kind() {
76	case r.Bool, r.Int64, r.Uint64, r.Float64, r.Complex128, r.String:
77		// result of reflect.Value.{tname} is already the correct type
78		val = ~"{~,sel ()}
79	default:
80		// convert int64, uint64... to the correct type
81		val = ~"{~,typ ( ~,sel () )}
82	}
83	return val
84}
85
86:macro binaryop(opnode, xconst, yconst, typ ast.Node) ast.Node {
87
88	// the return type of Eval() and EvalType() varies. better check early.
89	xc, yc := Eval(xconst).(bool), Eval(yconst).(bool)
90	optoken := Eval(opnode).(token.Token)
91
92	if xc == yc {
93		var expr *ast.BinaryExpr = ~"{x(env) && y(env)} // quasiquote, we modify it destructively
94		expr.Op = optoken
95
96		return ~"{
97			x := x.(func(*Env) ~,typ)
98			y := y.(func(*Env) ~,typ)
99			fun = func(env *Env) bool {
100				return ~,expr
101			}
102		}
103	} else if yc {
104		var expr *ast.BinaryExpr = ~"{x(env) && y} // quasiquote, we modify it destructively
105		expr.Op = optoken
106
107		yconv := convertvalue1(typ, ~'yv)
108		return ~"{
109			x := x.(func(*Env) ~,typ)
110			y := ~,yconv
111			fun = func(env *Env) bool {
112				return ~,expr
113			}
114		}
115	} else {
116		var expr *ast.BinaryExpr = ~"{x && y(env)} // quasiquote, we modify it destructively
117		expr.Op = optoken
118
119		xconv := convertvalue1(typ, ~'xv)
120		return ~"{
121			x := ~,xconv
122			y := y.(func(*Env) ~,typ)
123			fun = func(env *Env) bool {
124				return ~,expr
125			}
126		}
127	}
128}
129
130:macro binaryops(opnode, xconst, yconst, types ast.Node) ast.Node {
131	typelist := types.(*ast.BlockStmt).List
132	caselist := make([]ast.Stmt, 0, len(typelist))
133	foundnil := false
134	for _, typ := range typelist {
135		t := EvalType(typ)
136		if t == nil {
137			caselist = append(caselist, ~"{default: binaryop; ~,opnode; ~,xconst; ~,yconst; ~,typ})
138			foundnil = true
139
140		} else if t.Kind() == r.Int {
141			// shortcut for all int* types
142			for _, typ := range []ast.Expr{~'int, ~'int8, ~'int16, ~'int32, ~'int64} {
143				kind := makekind(typ)
144				caselist = append(caselist, ~"{case ~,kind: binaryop; ~,opnode; ~,xconst; ~,yconst; ~,typ})
145			}
146		} else if t.Kind() == r.Uint {
147			// shortcut for all uint* types
148			for _, typ := range []ast.Expr{~'uint, ~'uint8, ~'uint16, ~'uint32, ~'uint64, ~'uintptr} {
149				kind := makekind(typ)
150				caselist = append(caselist, ~"{case ~,kind: binaryop; ~,opnode; ~,xconst; ~,yconst; ~,typ})
151			}
152		} else {
153			kind := makekind(typ)
154			caselist = append(caselist, ~"{case ~,kind: binaryop; ~,opnode; ~,xconst; ~,yconst; ~,typ})
155		}
156	}
157
158	if !foundnil {
159		caselist = append(caselist, ~'{default: return c.invalidBinaryExpr(node, xe, ye)})
160	}
161	return ~"{ switch k { ~,@caselist } }
162}
163
164func (c *Comp) Lss(node *ast.BinaryExpr, xe *Expr, ye *Expr) *Expr {
165	xc, yc := xe.Const(), ye.Const()
166	c.toSameFuncType(node, xe, ye)
167	k := xe.Type.Kind()
168
169	// if both x and y are constants, BinaryExpr will invoke EvalConst()
170	// on our return value. no need to optimize that.
171	var fun func (*Env) bool
172	if xc == yc {
173		x, y := xe.Fun, ye.Fun
174		{binaryops; token.LSS; false; false; { int; uint; float32; float64; string }}
175	} else if yc {
176		x := xe.Fun
177		yv := r.ValueOf(ye.Value)
178		{binaryops; token.LSS; false; true; { int; uint; float32; float64; string }}
179	} else {
180		xv := r.ValueOf(xe.Value)
181		y := ye.Fun
182		{binaryops; token.LSS; true; false; { int; uint; float32; float64; string }}
183	}
184	return c.exprBool(fun)
185}
186
187func (c *Comp) Gtr(node *ast.BinaryExpr, xe *Expr, ye *Expr) *Expr {
188	xc, yc := xe.Const(), ye.Const()
189	c.toSameFuncType(node, xe, ye)
190	k := xe.Type.Kind()
191
192	// if both x and y are constants, BinaryExpr will invoke EvalConst()
193	// on our return value. no need to optimize that.
194	var fun func (*Env) bool
195	if xc == yc {
196		x, y := xe.Fun, ye.Fun
197		{binaryops; token.GTR; false; false; { int; uint; float32; float64; string }}
198	} else if yc {
199		x := xe.Fun
200		yv := r.ValueOf(ye.Value)
201		{binaryops; token.GTR; false; true; { int; uint; float32; float64; string }}
202	} else {
203		xv := r.ValueOf(xe.Value)
204		y := ye.Fun
205		{binaryops; token.GTR; true; false; { int; uint; float32; float64; string }}
206	}
207	return c.exprBool(fun)
208}
209
210func (c *Comp) Leq(node *ast.BinaryExpr, xe *Expr, ye *Expr) *Expr {
211	xc, yc := xe.Const(), ye.Const()
212	c.toSameFuncType(node, xe, ye)
213	k := xe.Type.Kind()
214
215	// if both x and y are constants, BinaryExpr will invoke EvalConst()
216	// on our return value. no need to optimize that.
217	var fun func (*Env) bool
218	if xc == yc {
219		x, y := xe.Fun, ye.Fun
220		{binaryops; token.LEQ; false; false; { int; uint; float32; float64; string }}
221	} else if yc {
222		x := xe.Fun
223		yv := r.ValueOf(ye.Value)
224		{binaryops; token.LEQ; false; true; { int; uint; float32; float64; string }}
225	} else {
226		xv := r.ValueOf(xe.Value)
227		y := ye.Fun
228		{binaryops; token.LEQ; true; false; { int; uint; float32; float64; string }}
229	}
230	return c.exprBool(fun)
231}
232
233func (c *Comp) Geq(node *ast.BinaryExpr, xe *Expr, ye *Expr) *Expr {
234	xc, yc := xe.Const(), ye.Const()
235	c.toSameFuncType(node, xe, ye)
236	k := xe.Type.Kind()
237
238	// if both x and y are constants, BinaryExpr will invoke EvalConst()
239	// on our return value. no need to optimize that.
240	var fun func (*Env) bool
241	if xc == yc {
242		x, y := xe.Fun, ye.Fun
243		{binaryops; token.GEQ; false; false; { int; uint; float32; float64; string }}
244	} else if yc {
245		x := xe.Fun
246		yv := r.ValueOf(ye.Value)
247		{binaryops; token.GEQ; false; true; { int; uint; float32; float64; string }}
248	} else {
249		xv := r.ValueOf(xe.Value)
250		y := ye.Fun
251		{binaryops; token.GEQ; true; false; { int; uint; float32; float64; string }}
252	}
253	return c.exprBool(fun)
254}
255