github.com/gotranspile/cxgo@v0.3.7/bools.go (about) 1 package cxgo 2 3 import ( 4 "go/ast" 5 "go/token" 6 7 "github.com/gotranspile/cxgo/types" 8 ) 9 10 // BoolExpr is a expression that returns a bool value. 11 type BoolExpr interface { 12 Expr 13 // Negate a bool expression. Alternative of !x, but for any expression. 14 // It may invert the comparison operator or just return !x. 15 Negate() BoolExpr 16 } 17 18 // ToBool converts an expression to bool expression. 19 func (g *translator) ToBool(x Expr) BoolExpr { 20 if x, ok := cUnwrap(x).(BoolExpr); ok { 21 return x 22 } 23 if v, ok := cIsBoolConst(x); ok { 24 if v { 25 return Bool(true) 26 } 27 return Bool(false) 28 } 29 if x, ok := cUnwrap(x).(IntLit); ok { 30 if x.IsZero() { 31 return Bool(false) 32 } else if x.IsOne() { 33 return Bool(true) 34 } 35 } 36 if x.CType(nil).Kind().IsBool() { 37 if x, ok := x.(Ident); ok { 38 return BoolIdent{x.Identifier()} 39 } 40 return BoolAssert{x} 41 } 42 if types.IsPtr(x.CType(nil)) { 43 return ComparePtrs( 44 g.ToPointer(x), 45 BinOpNeq, 46 g.Nil(), 47 ) 48 } 49 return g.Compare( 50 x, 51 BinOpNeq, 52 cIntLit(0), 53 ) 54 } 55 56 func cIsBoolConst(x Expr) (bool, bool) { 57 x = cUnwrap(x) 58 switch x := x.(type) { 59 case Bool: 60 if x { 61 return true, true 62 } 63 return false, true 64 case IntLit: 65 if x.IsOne() { 66 return true, true 67 } else if x.IsZero() { 68 return false, true 69 } 70 } 71 return false, false 72 } 73 74 var ( 75 _ BoolExpr = Bool(false) 76 ) 77 78 // Bool is a constant bool value. 79 type Bool bool 80 81 func (Bool) Visit(v Visitor) {} 82 83 func (e Bool) CType(types.Type) types.Type { 84 return types.BoolT() 85 } 86 87 func (e Bool) AsExpr() GoExpr { 88 if e { 89 return ident("true") 90 } 91 return ident("false") 92 } 93 94 func (e Bool) IsConst() bool { 95 return true 96 } 97 98 func (e Bool) HasSideEffects() bool { 99 return false 100 } 101 102 func (e Bool) Negate() BoolExpr { 103 return !e 104 } 105 106 func (e Bool) Uses() []types.Usage { 107 return nil 108 } 109 110 var ( 111 _ BoolExpr = BoolIdent{} 112 _ Ident = BoolIdent{} 113 ) 114 115 type BoolIdent struct { 116 *types.Ident 117 } 118 119 func (BoolIdent) Visit(v Visitor) {} 120 121 func (e BoolIdent) Identifier() *types.Ident { 122 return e.Ident 123 } 124 125 func (e BoolIdent) IsConst() bool { 126 return false 127 } 128 129 func (e BoolIdent) HasSideEffects() bool { 130 return false 131 } 132 133 func (e BoolIdent) AsExpr() GoExpr { 134 return e.GoIdent() 135 } 136 137 func (e BoolIdent) Negate() BoolExpr { 138 return &Not{X: e} 139 } 140 141 func (e BoolIdent) Uses() []types.Usage { 142 return []types.Usage{{Ident: e.Ident, Access: types.AccessUnknown}} 143 } 144 145 var _ BoolExpr = (*Not)(nil) 146 147 // Not negates a bool expression. It's only useful for identifiers and function calls. 148 type Not struct { 149 X BoolExpr 150 } 151 152 func (e *Not) Visit(v Visitor) { 153 v(e.X) 154 } 155 156 func (e *Not) CType(types.Type) types.Type { 157 return types.BoolT() 158 } 159 160 func (e *Not) AsExpr() GoExpr { 161 return &ast.UnaryExpr{ 162 Op: token.NOT, 163 X: e.X.AsExpr(), 164 } 165 } 166 167 func (e *Not) IsConst() bool { 168 return e.X.IsConst() 169 } 170 171 func (e *Not) HasSideEffects() bool { 172 return e.X.HasSideEffects() 173 } 174 175 func (e *Not) Negate() BoolExpr { 176 return e.X 177 } 178 179 func (e *Not) Uses() []types.Usage { 180 return e.X.Uses() 181 } 182 183 func (g *translator) cNot(x Expr) BoolExpr { 184 if x, ok := x.(BoolExpr); ok { 185 return x.Negate() 186 } 187 return g.ToBool(x).Negate() 188 } 189 190 const ( 191 BinOpAnd BoolOp = "&&" 192 BinOpOr BoolOp = "||" 193 ) 194 195 type BoolOp string 196 197 func (op BoolOp) Negate() BoolOp { 198 switch op { 199 case BinOpAnd: 200 return BinOpOr 201 case BinOpOr: 202 return BinOpAnd 203 } 204 panic(op) 205 } 206 207 func (op BoolOp) GoToken() token.Token { 208 var tok token.Token 209 switch op { 210 case BinOpAnd: 211 tok = token.LAND 212 case BinOpOr: 213 tok = token.LOR 214 default: 215 panic(op) 216 } 217 return tok 218 } 219 220 func And(x, y BoolExpr) BoolExpr { 221 return &BinaryBoolExpr{ 222 X: x, Op: BinOpAnd, Y: y, 223 } 224 } 225 226 func Or(x, y BoolExpr) BoolExpr { 227 return &BinaryBoolExpr{ 228 X: x, Op: BinOpOr, Y: y, 229 } 230 } 231 232 var _ BoolExpr = (*BinaryBoolExpr)(nil) 233 234 type BinaryBoolExpr struct { 235 X BoolExpr 236 Op BoolOp 237 Y BoolExpr 238 } 239 240 func (e *BinaryBoolExpr) Visit(v Visitor) { 241 v(e.X) 242 v(e.Y) 243 } 244 245 func (e *BinaryBoolExpr) CType(types.Type) types.Type { 246 return types.BoolT() 247 } 248 249 func (e *BinaryBoolExpr) AsExpr() GoExpr { 250 return &ast.BinaryExpr{ 251 X: e.X.AsExpr(), 252 Op: e.Op.GoToken(), 253 Y: e.Y.AsExpr(), 254 } 255 } 256 257 func (e *BinaryBoolExpr) IsConst() bool { 258 return e.X.IsConst() && e.Y.IsConst() 259 } 260 261 func (e *BinaryBoolExpr) HasSideEffects() bool { 262 return e.X.HasSideEffects() || e.Y.HasSideEffects() 263 } 264 265 func (e *BinaryBoolExpr) Negate() BoolExpr { 266 return &BinaryBoolExpr{ 267 X: e.X.Negate(), 268 Op: e.Op.Negate(), 269 Y: e.Y.Negate(), 270 } 271 } 272 273 func (e *BinaryBoolExpr) Uses() []types.Usage { 274 return types.UseRead(e.X, e.Y) 275 } 276 277 const ( 278 BinOpEq ComparisonOp = "==" 279 BinOpNeq ComparisonOp = "!=" 280 BinOpLt ComparisonOp = "<" 281 BinOpGt ComparisonOp = ">" 282 BinOpLte ComparisonOp = "<=" 283 BinOpGte ComparisonOp = ">=" 284 ) 285 286 // ComparisonOp is a comparison operator. 287 type ComparisonOp string 288 289 func (op ComparisonOp) IsEquality() bool { 290 return op == BinOpEq || op == BinOpNeq 291 } 292 293 func (op ComparisonOp) IsRelational() bool { 294 switch op { 295 case BinOpLt, BinOpGt, BinOpLte, BinOpGte: 296 return true 297 } 298 return false 299 } 300 301 func (op ComparisonOp) Negate() ComparisonOp { 302 switch op { 303 case BinOpEq: 304 return BinOpNeq 305 case BinOpNeq: 306 return BinOpEq 307 case BinOpLt: 308 return BinOpGte 309 case BinOpGt: 310 return BinOpLte 311 case BinOpLte: 312 return BinOpGt 313 case BinOpGte: 314 return BinOpLt 315 } 316 panic(op) 317 } 318 319 func (op ComparisonOp) GoToken() token.Token { 320 var tok token.Token 321 switch op { 322 case BinOpLt: 323 tok = token.LSS 324 case BinOpGt: 325 tok = token.GTR 326 case BinOpLte: 327 tok = token.LEQ 328 case BinOpGte: 329 tok = token.GEQ 330 case BinOpEq: 331 tok = token.EQL 332 case BinOpNeq: 333 tok = token.NEQ 334 default: 335 panic(op) 336 } 337 return tok 338 } 339 340 // Compare two expression values. 341 func (g *translator) Compare(x Expr, op ComparisonOp, y Expr) BoolExpr { 342 // compare pointers and functions separately 343 if xt := x.CType(nil); xt.Kind().IsFunc() { 344 fx := g.ToFunc(x, nil) 345 return CompareFuncs(fx, op, g.ToFunc(y, fx.FuncType(nil))) 346 } 347 if yt := y.CType(nil); yt.Kind().IsFunc() { 348 fy := g.ToFunc(y, nil) 349 return CompareFuncs(g.ToFunc(x, fy.FuncType(nil)), op, fy) 350 } 351 if x.CType(nil).Kind().IsPtr() || y.CType(nil).Kind().IsPtr() { 352 return ComparePtrs(g.ToPointer(x), op, g.ToPointer(y)) 353 } 354 if x.CType(nil).Kind().Is(types.Array) || y.CType(nil).Kind().Is(types.Array) { 355 return ComparePtrs(g.ToPointer(x), op, g.ToPointer(y)) 356 } 357 if op.IsRelational() { 358 typ := g.env.CommonType(x.CType(nil), y.CType(nil)) 359 x = g.cCast(typ, x) 360 y = g.cCast(typ, y) 361 return &Comparison{ 362 g: g, 363 X: x, Op: op, Y: y, 364 } 365 } 366 if !op.IsEquality() { 367 panic("must not happen") 368 } 369 // always check equality with the constant on the right 370 if x.IsConst() && !y.IsConst() { 371 return g.Compare(y, op, x) 372 } 373 // optimizations for bool equality 374 if v, ok := cIsBoolConst(y); ok { 375 x := cUnwrap(x) 376 if x.CType(nil).Kind().IsBool() { 377 x = g.ToBool(x) 378 } 379 if x, ok := x.(BoolExpr); ok { 380 if v == (op == BinOpEq) { 381 // (bool(x) == true) -> (x) 382 // (bool(x) != false) -> (x) 383 return x 384 } 385 // (bool(x) != true) -> (!x) 386 // (bool(x) == false) -> (!x) 387 return x.Negate() 388 } 389 } 390 typ := g.env.CommonType(x.CType(nil), y.CType(nil)) 391 x = g.cCast(typ, x) 392 y = g.cCast(typ, y) 393 return &Comparison{ 394 g: g, 395 X: x, Op: op, Y: y, 396 } 397 } 398 399 var _ BoolExpr = (*Comparison)(nil) 400 401 type Comparison struct { 402 g *translator 403 X Expr 404 Op ComparisonOp 405 Y Expr 406 } 407 408 func (e *Comparison) Visit(v Visitor) { 409 v(e.X) 410 v(e.Y) 411 } 412 413 func (e *Comparison) CType(types.Type) types.Type { 414 return types.BoolT() 415 } 416 417 func (e *Comparison) AsExpr() GoExpr { 418 x := e.X.AsExpr() 419 if _, ok := x.(*ast.CompositeLit); ok { 420 x = paren(x) 421 } 422 y := e.Y.AsExpr() 423 if _, ok := y.(*ast.CompositeLit); ok { 424 y = paren(y) 425 } 426 return &ast.BinaryExpr{ 427 X: x, 428 Op: e.Op.GoToken(), 429 Y: y, 430 } 431 } 432 433 func (e *Comparison) IsConst() bool { 434 return e.X.IsConst() && e.Y.IsConst() 435 } 436 437 func (e *Comparison) HasSideEffects() bool { 438 return e.X.HasSideEffects() || e.Y.HasSideEffects() 439 } 440 441 func (e *Comparison) Negate() BoolExpr { 442 return e.g.Compare(e.X, e.Op.Negate(), e.Y) 443 } 444 445 func (e *Comparison) Uses() []types.Usage { 446 return types.UseRead(e.X, e.Y) 447 } 448 449 var _ BoolExpr = BoolAssert{} 450 451 type BoolAssert struct { 452 X Expr 453 } 454 455 func (e BoolAssert) Visit(v Visitor) { 456 v(e.X) 457 } 458 459 func (e BoolAssert) CType(types.Type) types.Type { 460 return types.BoolT() 461 } 462 463 func (e BoolAssert) AsExpr() GoExpr { 464 return e.X.AsExpr() 465 } 466 467 func (e BoolAssert) IsConst() bool { 468 return false 469 } 470 471 func (e BoolAssert) HasSideEffects() bool { 472 return e.X.HasSideEffects() 473 } 474 475 func (e BoolAssert) Negate() BoolExpr { 476 return &Not{X: e} 477 } 478 479 func (e BoolAssert) Uses() []types.Usage { 480 return e.X.Uses() 481 } 482 483 var _ Expr = (*BoolToInt)(nil) 484 485 type BoolToInt struct { 486 X BoolExpr 487 } 488 489 func (e *BoolToInt) Visit(v Visitor) { 490 v(e.X) 491 } 492 493 func (e *BoolToInt) CType(types.Type) types.Type { 494 return types.IntT(4) 495 } 496 497 func (e *BoolToInt) AsExpr() GoExpr { 498 return call(ident("libc.BoolToInt"), e.X.AsExpr()) 499 } 500 501 func (e *BoolToInt) IsConst() bool { 502 return false 503 } 504 505 func (e *BoolToInt) HasSideEffects() bool { 506 return e.X.HasSideEffects() 507 } 508 509 func (e *BoolToInt) Uses() []types.Usage { 510 return e.X.Uses() 511 }