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  }