github.com/hirochachacha/plua@v0.0.0-20170217012138-c82f520cc725/compiler/codegen/gen_other.go (about)

     1  package codegen
     2  
     3  import (
     4  	"github.com/hirochachacha/plua/compiler/ast"
     5  	"github.com/hirochachacha/plua/compiler/token"
     6  	"github.com/hirochachacha/plua/internal/version"
     7  	"github.com/hirochachacha/plua/object"
     8  	"github.com/hirochachacha/plua/opcode"
     9  )
    10  
    11  // gen with global scope
    12  func (g *generator) genFile(f *ast.File) {
    13  	g.newScope()
    14  
    15  	g.LastLineDefined = f.End().Line
    16  
    17  	g.IsVararg = true
    18  
    19  	g.declareEnviron()
    20  
    21  	for _, e := range f.Chunk {
    22  		g.genStmt(e)
    23  	}
    24  
    25  	g.pushReturn()
    26  
    27  	g.closeScope()
    28  
    29  	if g.scope != nil {
    30  		panic("unexpected")
    31  	}
    32  
    33  	g.closeJumps()
    34  }
    35  
    36  func (g *generator) genFuncBody(f *ast.FuncBody, self bool, endLine int) {
    37  	g.newScope()
    38  
    39  	g.LineDefined = f.Pos().Line
    40  
    41  	if self {
    42  		g.declareLocal("self", 0)
    43  
    44  		for i, param := range f.Params.List {
    45  			g.declareLocalName(param, i+1)
    46  		}
    47  
    48  		g.NParams = len(f.Params.List) + 1
    49  	} else {
    50  		for i, param := range f.Params.List {
    51  			g.declareLocalName(param, i)
    52  		}
    53  
    54  		g.NParams = len(f.Params.List)
    55  	}
    56  
    57  	g.addSP(g.NParams)
    58  
    59  	g.IsVararg = f.Params.Ellipsis.IsValid()
    60  
    61  	if f.Body != nil {
    62  		g.genBlock(f.Body)
    63  	}
    64  
    65  	g.LastLineDefined = endLine
    66  
    67  	g.pushReturn()
    68  
    69  	g.closeScope()
    70  
    71  	g.closeJumps()
    72  }
    73  
    74  func (g *generator) genBlock(b *ast.Block) {
    75  	for _, e := range b.List {
    76  		g.genStmt(e)
    77  	}
    78  }
    79  
    80  // gen test condtion or immediate bool
    81  func (g *generator) genTest(cond ast.Expr, not bool) immBool {
    82  	switch cond := cond.(type) {
    83  	case *ast.ParenExpr:
    84  		return g.genTest(cond.X, not)
    85  
    86  	case *ast.BasicLit:
    87  		if !skipDeadCodeElimination {
    88  			switch cond.Token.Type {
    89  			case token.FALSE, token.NIL:
    90  				if not {
    91  					return immTrue
    92  				}
    93  				return immFalse
    94  			default:
    95  				if not {
    96  					return immFalse
    97  				}
    98  				return immTrue
    99  			}
   100  		}
   101  
   102  		x := g.genExpr(cond, genR)
   103  		if not {
   104  			g.pushInst(opcode.AC(opcode.TEST, x, 1))
   105  		} else {
   106  			g.pushInst(opcode.AC(opcode.TEST, x, 0))
   107  		}
   108  	case *ast.UnaryExpr:
   109  		switch cond.Op {
   110  		case token.NOT:
   111  			if !skipDeadCodeElimination {
   112  				if val, ok := g.foldExpr(cond.X); ok {
   113  					if !object.ToGoBool(val) {
   114  						if not {
   115  							return immFalse
   116  						}
   117  						return immTrue
   118  					}
   119  
   120  					if not {
   121  						return immTrue
   122  					}
   123  					return immFalse
   124  				}
   125  			}
   126  
   127  			return g.genTest(cond.X, !not)
   128  		default:
   129  			x := g.genExpr(cond, genR)
   130  			if not {
   131  				g.pushInst(opcode.AC(opcode.TEST, x, 1))
   132  			} else {
   133  				g.pushInst(opcode.AC(opcode.TEST, x, 0))
   134  			}
   135  		}
   136  	case *ast.BinaryExpr:
   137  		if !skipDeadCodeElimination {
   138  			if val, ok := g.foldBinary(cond); ok {
   139  				if object.ToGoBool(val) {
   140  					if not {
   141  						return immFalse
   142  					}
   143  					return immTrue
   144  				}
   145  
   146  				if not {
   147  					return immTrue
   148  				}
   149  				return immFalse
   150  			}
   151  		}
   152  
   153  		switch cond.Op {
   154  		case token.EQ:
   155  			x := g.genExpr(cond.X, genR|genK)
   156  			y := g.genExpr(cond.Y, genR|genK)
   157  
   158  			if not {
   159  				g.pushInst(opcode.ABC(opcode.EQ, 1, x, y))
   160  			} else {
   161  				g.pushInst(opcode.ABC(opcode.EQ, 0, x, y))
   162  			}
   163  		case token.NE:
   164  			x := g.genExpr(cond.X, genR|genK)
   165  			y := g.genExpr(cond.Y, genR|genK)
   166  
   167  			if not {
   168  				g.pushInst(opcode.ABC(opcode.EQ, 0, x, y))
   169  			} else {
   170  				g.pushInst(opcode.ABC(opcode.EQ, 1, x, y))
   171  			}
   172  		case token.LT:
   173  			x := g.genExpr(cond.X, genR|genK)
   174  			y := g.genExpr(cond.Y, genR|genK)
   175  
   176  			if not {
   177  				g.pushInst(opcode.ABC(opcode.LT, 1, x, y))
   178  			} else {
   179  				g.pushInst(opcode.ABC(opcode.LT, 0, x, y))
   180  			}
   181  		case token.LE:
   182  			x := g.genExpr(cond.X, genR|genK)
   183  			y := g.genExpr(cond.Y, genR|genK)
   184  
   185  			if not {
   186  				g.pushInst(opcode.ABC(opcode.LE, 1, x, y))
   187  			} else {
   188  				g.pushInst(opcode.ABC(opcode.LE, 0, x, y))
   189  			}
   190  		case token.GT:
   191  			x := g.genExpr(cond.X, genR|genK)
   192  			y := g.genExpr(cond.Y, genR|genK)
   193  
   194  			if not {
   195  				g.pushInst(opcode.ABC(opcode.LT, 1, y, x))
   196  			} else {
   197  				g.pushInst(opcode.ABC(opcode.LT, 0, y, x))
   198  			}
   199  		case token.GE:
   200  			x := g.genExpr(cond.X, genR|genK)
   201  			y := g.genExpr(cond.Y, genR|genK)
   202  
   203  			if not {
   204  				g.pushInst(opcode.ABC(opcode.LE, 1, y, x))
   205  			} else {
   206  				g.pushInst(opcode.ABC(opcode.LE, 0, y, x))
   207  			}
   208  		case token.AND:
   209  			if !skipDeadCodeElimination {
   210  				if val, ok := g.foldExpr(cond.X); ok {
   211  					if !object.ToGoBool(val) {
   212  						if not {
   213  							return immTrue
   214  						}
   215  						return immFalse
   216  					}
   217  
   218  					return g.genTest(cond.Y, not)
   219  				}
   220  			}
   221  
   222  			x := g.genExpr(cond, genR)
   223  			if not {
   224  				g.pushInst(opcode.AC(opcode.TEST, x, 1))
   225  			} else {
   226  				g.pushInst(opcode.AC(opcode.TEST, x, 0))
   227  			}
   228  		case token.OR:
   229  			if !skipDeadCodeElimination {
   230  				if val, ok := g.foldExpr(cond.X); ok {
   231  					if object.ToGoBool(val) {
   232  						if not {
   233  							return immFalse
   234  						}
   235  						return immTrue
   236  					}
   237  
   238  					return g.genTest(cond.Y, not)
   239  				}
   240  			}
   241  
   242  			x := g.genExpr(cond, genR)
   243  			if not {
   244  				g.pushInst(opcode.AC(opcode.TEST, x, 1))
   245  			} else {
   246  				g.pushInst(opcode.AC(opcode.TEST, x, 0))
   247  			}
   248  		default:
   249  			x := g.genExpr(cond, genR)
   250  			if not {
   251  				g.pushInst(opcode.AC(opcode.TEST, x, 1))
   252  			} else {
   253  				g.pushInst(opcode.AC(opcode.TEST, x, 0))
   254  			}
   255  		}
   256  	// no optimization
   257  	default:
   258  		x := g.genExpr(cond, genR)
   259  		if not {
   260  			g.pushInst(opcode.AC(opcode.TEST, x, 1))
   261  		} else {
   262  			g.pushInst(opcode.AC(opcode.TEST, x, 0))
   263  		}
   264  	}
   265  
   266  	return immUndefined
   267  }
   268  
   269  func (g *generator) genConst(val object.Value, typ genType) (rk int) {
   270  	k := g.constant(val)
   271  
   272  	if typ&genK != 0 {
   273  		return g.markRK(k, true)
   274  	}
   275  
   276  	if k > opcode.MaxBx {
   277  		g.pushInst(opcode.ABx(opcode.LOADKX, g.sp, 0))
   278  		g.pushInst(opcode.Ax(opcode.EXTRAARG, k))
   279  	} else {
   280  		g.pushInst(opcode.ABx(opcode.LOADK, g.sp, k))
   281  	}
   282  
   283  	rk = g.sp
   284  
   285  	g.nextSP()
   286  
   287  	return
   288  }
   289  
   290  func (g *generator) genSetGlobal(name *ast.Name, r int) {
   291  	env, ok := g.resolve(version.LUA_ENV)
   292  	if !ok {
   293  		panic("unexpected")
   294  	}
   295  
   296  	rk := g.markRK(g.constant(object.String(name.Name)), false)
   297  
   298  	switch env.kind {
   299  	case linkLocal:
   300  		g.pushInst(opcode.ABC(opcode.SETTABLE, env.index, rk, r))
   301  	case linkUpval:
   302  		g.pushInst(opcode.ABC(opcode.SETTABUP, env.index, rk, r))
   303  	default:
   304  		panic("unreachable")
   305  	}
   306  }
   307  
   308  func (g *generator) genGetGlobal(name *ast.Name) (r int) {
   309  	env, ok := g.resolve(version.LUA_ENV)
   310  	if !ok {
   311  		panic("unexpected")
   312  	}
   313  
   314  	rk := g.markRK(g.constant(object.String(name.Name)), false)
   315  
   316  	switch env.kind {
   317  	case linkLocal:
   318  		g.pushInst(opcode.ABC(opcode.GETTABLE, g.sp, env.index, rk))
   319  	case linkUpval:
   320  		g.pushInst(opcode.ABC(opcode.GETTABUP, g.sp, env.index, rk))
   321  	default:
   322  		panic("unreachable")
   323  	}
   324  
   325  	r = g.sp
   326  
   327  	g.nextSP()
   328  
   329  	return
   330  }
   331  
   332  func (g *generator) genPrefix(prefix []*ast.Name) (r int) {
   333  	sp := g.sp
   334  
   335  	r = g.genName(prefix[0], genR)
   336  	for _, name := range prefix[1:] {
   337  		y := g.genName(name, genKey)
   338  
   339  		g.pushInst(opcode.ABC(opcode.GETTABLE, sp, r, y))
   340  
   341  		r = sp
   342  	}
   343  
   344  	if r == sp {
   345  		g.setSP(sp + 1)
   346  	}
   347  
   348  	return
   349  }