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