github.com/amarpal/go-tools@v0.0.0-20240422043104-40142f59f616/internal/gosmith/expr.go (about) 1 package main 2 3 import ( 4 "bytes" 5 "fmt" 6 ) 7 8 func (smith *Smith) initExpressions() { 9 smith.expressions = []func(res *Type) string{ 10 smith.exprLiteral, 11 smith.exprVar, 12 smith.exprFunc, 13 smith.exprSelectorField, 14 smith.exprRecv, 15 smith.exprArith, 16 smith.exprEqual, 17 smith.exprOrder, 18 smith.exprCall, 19 smith.exprCallBuiltin, 20 smith.exprAddress, 21 smith.exprDeref, 22 smith.exprSlice, 23 smith.exprIndexSlice, 24 smith.exprIndexArray, 25 smith.exprIndexString, 26 smith.exprIndexMap, 27 smith.exprConversion, 28 } 29 } 30 31 func (smith *Smith) expression(res *Type) string { 32 smith.exprCount++ 33 smith.totalExprCount++ 34 if smith.exprDepth >= NExprDepth || smith.exprCount >= NExprCount || smith.totalExprCount >= NTotalExprCount { 35 return res.literal() 36 } 37 for { 38 smith.exprDepth++ 39 s := smith.expressions[smith.rnd(len(smith.expressions))](res) 40 smith.exprDepth-- 41 if s != "" { 42 return s 43 } 44 } 45 } 46 47 func (smith *Smith) rvalue(t *Type) string { 48 return smith.expression(t) 49 } 50 51 // rvalue, but not a const 52 // used to index arrays and strings 53 func (smith *Smith) nonconstRvalue(t *Type) string { 54 if t.class != ClassNumeric { 55 panic("bad") 56 } 57 trying: 58 for { 59 res := "" 60 switch smith.choice("lvalue", "call", "len", "selector", "recv", "arith", "indexMap", "conv") { 61 case "lvalue": 62 res = smith.lvalue(t) 63 case "call": 64 res = smith.exprCall(t) 65 case "len": 66 tt := smith.atype(TraitLenCapable) 67 fn := smith.choice("len", "cap") 68 if (tt.class == ClassString || tt.class == ClassMap) && fn == "cap" { 69 break 70 } 71 if tt.class == ClassArray { 72 // len/cap are const 73 break 74 } 75 res = F("(%v)((%v)(%v))", t.id, fn, smith.lvalue(tt)) 76 case "selector": 77 res = smith.exprSelectorField(t) 78 case "recv": 79 res = smith.exprRecv(t) 80 case "arith": 81 res = F("(%v) %v (%v)", smith.lvalue(t), smith.choice("+", "-"), smith.rvalue(t)) 82 case "indexMap": 83 res = smith.exprIndexMap(t) 84 case "conv": 85 res = F("(%v)(%v %v)", t.id, smith.lvalue(smith.atype(ClassNumeric)), smith.choice("", ",")) 86 default: 87 panic("bad") 88 } 89 if res == "" { 90 continue trying 91 } 92 return res 93 } 94 } 95 96 func (smith *Smith) lvalue(t *Type) string { 97 for { 98 switch smith.choice("var", "indexSlice", "indexArray", "selector", "deref") { 99 case "var": 100 return smith.exprVar(t) 101 case "indexSlice": 102 return smith.exprIndexSlice(t) 103 case "indexArray": 104 return F("(%v)[%v]", smith.lvalue(smith.arrayOf(t)), smith.nonconstRvalue(smith.intType)) 105 case "selector": 106 for i := 0; i < 10; i++ { 107 st := smith.atype(ClassStruct) 108 for _, e := range st.elems { 109 if e.typ == t { 110 return F("(%v).%v", smith.lvalue(st), e.id) 111 } 112 } 113 } 114 continue 115 case "deref": 116 return smith.exprDeref(t) 117 default: 118 panic("bad") 119 } 120 } 121 } 122 123 func (smith *Smith) lvalueOrBlank(t *Type) string { 124 for { 125 switch smith.choice("lvalue", "map", "blank") { 126 case "lvalue": 127 return smith.lvalue(t) 128 case "map": 129 if e := smith.exprIndexMap(t); e != "" { 130 return e 131 } 132 case "blank": 133 return "_" 134 default: 135 panic("bad") 136 } 137 } 138 } 139 140 func (smith *Smith) lvalueOrMapIndex(t *Type) string { 141 for { 142 switch smith.choice("lvalue", "map") { 143 case "lvalue": 144 return smith.lvalue(t) 145 case "map": 146 if e := smith.exprIndexMap(t); e != "" { 147 return e 148 } 149 default: 150 panic("bad") 151 } 152 } 153 } 154 155 func (smith *Smith) fmtRvalueList(list []*Type) string { 156 var buf bytes.Buffer 157 for i, t := range list { 158 if i != 0 { 159 buf.Write([]byte{','}) 160 } 161 fmt.Fprintf(&buf, "%v", smith.rvalue(t)) 162 } 163 return buf.String() 164 } 165 166 func (smith *Smith) fmtLvalueList(list []*Type) string { 167 var buf bytes.Buffer 168 for i, t := range list { 169 if i != 0 { 170 buf.Write([]byte{','}) 171 } 172 buf.WriteString(smith.lvalueOrBlank(t)) 173 } 174 return buf.String() 175 } 176 177 func (smith *Smith) fmtOasVarList(list []*Type) (str string, newVars []*Var) { 178 allVars := smith.vars() 179 var buf bytes.Buffer 180 for i, t := range list { 181 expr := "_" 182 // First, try to find an existing var in the same scope. 183 if smith.rndBool() { 184 for i, v := range allVars { 185 if v.typ == t && v.block == smith.curBlock { 186 allVars[i] = allVars[len(allVars)-1] 187 allVars = allVars[:len(allVars)-1] 188 expr = v.id 189 break 190 } 191 } 192 } 193 if smith.rndBool() || (i == len(list)-1 && len(newVars) == 0) { 194 expr = smith.newId("Var") 195 newVars = append(newVars, &Var{id: expr, typ: t}) 196 } 197 198 if i != 0 { 199 buf.WriteString(", ") 200 } 201 buf.WriteString(expr) 202 } 203 return buf.String(), newVars 204 } 205 206 func (smith *Smith) exprLiteral(res *Type) string { 207 if res.complexLiteral != nil { 208 return res.complexLiteral() 209 } 210 return res.literal() 211 } 212 213 func (smith *Smith) exprVar(res *Type) string { 214 for _, v := range smith.vars() { 215 if v.typ == res { 216 return v.id 217 } 218 } 219 return smith.materializeVar(res) 220 } 221 222 func (smith *Smith) exprSelectorField(res *Type) string { 223 for i := 0; i < 10; i++ { 224 st := smith.atype(ClassStruct) 225 for _, e := range st.elems { 226 if e.typ == res { 227 return F("(%v).%v", smith.rvalue(st), e.id) 228 } 229 } 230 } 231 return "" 232 } 233 234 func (smith *Smith) exprFunc(res *Type) string { 235 if !smith.satisfiesTrait(res, TraitGlobal) { 236 return "" 237 } 238 var f *Func 239 for _, f1 := range smith.packages[smith.curPackage].toplevFuncs { 240 if len(f1.rets) == 1 && f1.rets[0] == res { 241 f = f1 242 break 243 } 244 } 245 if f == nil { 246 f = smith.materializeFunc([]*Type{res}) 247 } 248 if smith.rndBool() { 249 return F("%v(%v)", f.name, smith.fmtRvalueList(f.args)) 250 } else { 251 var f0 *Func 252 loop: 253 for _, f1 := range smith.packages[smith.curPackage].toplevFuncs { 254 if len(f1.rets) == len(f.args) { 255 for i := range f.args { 256 // TODO: check assignability 257 if f1.rets[i] != f.args[i] { 258 continue loop 259 } 260 } 261 f0 = f1 262 break 263 } 264 } 265 if f0 == nil { 266 f0 = smith.materializeFunc(f.args) 267 } 268 return F("%v(%v(%v))", f.name, f0.name, smith.fmtRvalueList(f0.args)) 269 } 270 } 271 272 func (smith *Smith) exprAddress(res *Type) string { 273 if res.class != ClassPointer { 274 return "" 275 } 276 if res.ktyp.class == ClassStruct && smith.rndBool() { 277 return F("&%v", res.ktyp.complexLiteral()) 278 } 279 return F("(%v)(&(%v))", res.id, smith.lvalue(res.ktyp)) 280 } 281 282 func (smith *Smith) exprDeref(res *Type) string { 283 return F("(*(%v))", smith.lvalue(pointerTo(res))) 284 } 285 286 func (smith *Smith) exprRecv(res *Type) string { 287 t := smith.chanOf(res) 288 return F("(<- %v)", smith.rvalue(t)) 289 } 290 291 func (smith *Smith) exprArith(res *Type) string { 292 if res.class != ClassNumeric && res.class != ClassComplex { 293 return "" 294 } 295 // "/" causes division by zero 296 // "*" causes generation of -1 index in int(real(1i * 1i)) 297 return F("(%v) + (%v)", smith.rvalue(res), smith.rvalue(res)) 298 } 299 300 func (smith *Smith) exprEqual(res *Type) string { 301 if res != smith.boolType { 302 return "" 303 } 304 t := smith.atype(TraitComparable) 305 return F("(%v) %v (%v)", smith.rvalue(t), smith.choice("==", "!="), smith.rvalue(t)) 306 } 307 308 func (smith *Smith) exprOrder(res *Type) string { 309 if res != smith.boolType { 310 return "" 311 } 312 t := smith.atype(TraitOrdered) 313 return F("(%v) %v (%v)", smith.rvalue(t), smith.choice("<", "<=", ">", ">="), smith.rvalue(t)) 314 315 } 316 317 func (smith *Smith) exprCall(ret *Type) string { 318 t := smith.funcOf(smith.atypeList(TraitAny), []*Type{ret}) 319 return F("%v(%v)", smith.rvalue(t), smith.fmtRvalueList(t.styp)) 320 } 321 322 func (smith *Smith) exprCallBuiltin(ret *Type) string { 323 switch fn := smith.choice("append", "cap", "complex", "copy", "imag", "len", "make", "new", "real", "recover"); fn { 324 case "append": 325 if ret.class != ClassSlice { 326 return "" 327 } 328 switch smith.choice("one", "two", "slice") { 329 case "one": 330 return F("%v(%v, %v)", fn, smith.rvalue(ret), smith.rvalue(ret.ktyp)) 331 case "two": 332 return F("%v(%v, %v, %v)", fn, smith.rvalue(ret), smith.rvalue(ret.ktyp), smith.rvalue(ret.ktyp)) 333 case "slice": 334 return F("%v(%v, %v...)", fn, smith.rvalue(ret), smith.rvalue(ret)) 335 default: 336 panic("bad") 337 } 338 case "len", "cap": 339 if ret != smith.intType { // TODO: must be convertable 340 return "" 341 } 342 t := smith.atype(TraitLenCapable) 343 if (t.class == ClassString || t.class == ClassMap) && fn == "cap" { 344 return "" 345 346 } 347 return F("%v(%v)", fn, smith.rvalue(t)) 348 case "copy": 349 if ret != smith.intType { 350 return "" 351 } 352 return F("%v", smith.exprCopySlice()) 353 case "make": 354 if ret.class != ClassSlice && ret.class != ClassMap && ret.class != ClassChan { 355 return "" 356 } 357 cap := "" 358 if ret.class == ClassSlice { 359 if smith.rndBool() { 360 cap = F(", %v", smith.rvalue(smith.intType)) 361 } else { 362 // Careful to not generate "len larger than cap". 363 cap = F(", 0, %v", smith.rvalue(smith.intType)) 364 } 365 } else if smith.rndBool() { 366 cap = F(", %v", smith.rvalue(smith.intType)) 367 } 368 return F("make(%v %v)", ret.id, cap) 369 case "new": 370 if ret.class != ClassPointer { 371 return "" 372 } 373 return F("new(%v)", ret.ktyp.id) 374 case "recover": 375 if ret != smith.efaceType { 376 return "" 377 } 378 return "recover()" 379 case "real", "imag": 380 if ret == smith.float32Type { 381 return F("real(%v)", smith.rvalue(smith.complex64Type)) 382 } 383 if ret == smith.float64Type { 384 return F("real(%v)", smith.rvalue(smith.complex128Type)) 385 } 386 return "" 387 case "complex": 388 if ret == smith.complex64Type { 389 return F("complex(%v, %v)", smith.rvalue(smith.float32Type), smith.rvalue(smith.float32Type)) 390 } 391 if ret == smith.complex128Type { 392 return F("complex(%v, %v)", smith.rvalue(smith.float64Type), smith.rvalue(smith.float64Type)) 393 } 394 return "" 395 default: 396 panic("bad") 397 } 398 } 399 400 func (smith *Smith) exprCopySlice() string { 401 if smith.rndBool() { 402 t := smith.atype(ClassSlice) 403 return F("copy(%v, %v)", smith.rvalue(t), smith.rvalue(t)) 404 } else { 405 return F("copy(%v, %v)", smith.rvalue(smith.sliceOf(smith.byteType)), smith.rvalue(smith.stringType)) 406 } 407 } 408 409 func (smith *Smith) exprSlice(ret *Type) string { 410 if ret.class != ClassSlice { 411 return "" 412 } 413 i0 := "" 414 if smith.rndBool() { 415 i0 = smith.nonconstRvalue(smith.intType) 416 } 417 i2 := "" 418 if smith.rndBool() { 419 i2 = ":" + smith.nonconstRvalue(smith.intType) 420 } 421 i1 := ":" 422 if smith.rndBool() || i2 != "" { 423 i1 = ":" + smith.nonconstRvalue(smith.intType) 424 } 425 return F("(%v)[%v%v%v]", smith.rvalue(ret), i0, i1, i2) 426 } 427 428 func (smith *Smith) exprIndexSlice(ret *Type) string { 429 return F("(%v)[%v]", smith.rvalue(smith.sliceOf(ret)), smith.nonconstRvalue(smith.intType)) 430 } 431 432 func (smith *Smith) exprIndexString(ret *Type) string { 433 if ret != smith.byteType { 434 return "" 435 } 436 return F("(%v)[%v]", smith.rvalue(smith.stringType), smith.nonconstRvalue(smith.intType)) 437 } 438 439 func (smith *Smith) exprIndexArray(ret *Type) string { 440 // TODO: also handle indexing of pointers to arrays 441 return F("(%v)[%v]", smith.rvalue(smith.arrayOf(ret)), smith.nonconstRvalue(smith.intType)) 442 } 443 444 func (smith *Smith) exprIndexMap(ret *Type) string { 445 // TODO: figure out something better 446 for i := 0; i < 10; i++ { 447 t := smith.atype(ClassMap) 448 if t.vtyp == ret { 449 return F("(%v)[%v]", smith.rvalue(t), smith.rvalue(t.ktyp)) 450 } 451 } 452 return "" 453 } 454 455 func (smith *Smith) exprConversion(ret *Type) string { 456 if ret.class == ClassNumeric { 457 return F("(%v)(%v %v)", ret.id, smith.rvalue(smith.atype(ClassNumeric)), smith.choice("", ",")) 458 } 459 if ret.class == ClassComplex { 460 return F("(%v)(%v %v)", ret.id, smith.rvalue(smith.atype(ClassComplex)), smith.choice("", ",")) 461 } 462 if ret == smith.stringType { 463 switch smith.choice("int", "byteSlice", "runeSlice") { 464 case "int": 465 // We produce a string of length at least 3, to not produce 466 // "invalid string index 1 (out of bounds for 1-byte string)" 467 return F("(%v)((%v) + (1<<24) %v)", ret.id, smith.rvalue(smith.intType), smith.choice("", ",")) 468 case "byteSlice": 469 return F("(%v)(%v %v)", ret.id, smith.rvalue(smith.sliceOf(smith.byteType)), smith.choice("", ",")) 470 case "runeSlice": 471 return F("(%v)(%v %v)", ret.id, smith.rvalue(smith.sliceOf(smith.runeType)), smith.choice("", ",")) 472 default: 473 panic("bad") 474 } 475 } 476 if ret.class == ClassSlice && (ret.ktyp == smith.byteType || ret.ktyp == smith.runeType) { 477 return F("(%v)(%v %v)", ret.id, smith.rvalue(smith.stringType), smith.choice("", ",")) 478 } 479 // TODO: handle "x is assignable to T" 480 // TODO: handle "x's type and T have identical underlying types" 481 // TODO: handle "x's type and T are unnamed pointer types and their pointer base types have identical underlying types" 482 return "" 483 }