github.com/arnodel/golua@v0.0.0-20230215163904-e0b5347eaaa1/astcomp/compexp.go (about)

     1  package astcomp
     2  
     3  import (
     4  	"github.com/arnodel/golua/ast"
     5  	"github.com/arnodel/golua/ir"
     6  	"github.com/arnodel/golua/ops"
     7  )
     8  
     9  //
    10  // Expression compilation
    11  //
    12  
    13  type expCompiler struct {
    14  	*compiler
    15  	dst ir.Register
    16  }
    17  
    18  var _ ast.ExpProcessor = (*expCompiler)(nil)
    19  
    20  // ProcessBFunctionCallExp compiles a BFunctionCall
    21  func (c *expCompiler) ProcessBFunctionCallExp(f ast.BFunctionCall) {
    22  	c.compileCall(f, false)
    23  	c.emitInstr(f, ir.Receive{Dst: []ir.Register{c.dst}})
    24  }
    25  
    26  // ProcessBinOpExp compiles a BinOpExp.
    27  func (c *expCompiler) ProcessBinOpExp(b ast.BinOp) {
    28  	if b.OpType == ops.OpAnd {
    29  		c.compileLogicalOp(b, true)
    30  		return
    31  	}
    32  	if b.OpType == ops.OpOr {
    33  		c.compileLogicalOp(b, false)
    34  		return
    35  	}
    36  	lsrc := c.compileExpNoDestHint(b.Left)
    37  	for _, r := range b.Right {
    38  		c.TakeRegister(lsrc)
    39  		rsrc := c.compileExpNoDestHint(r.Operand)
    40  		switch r.Op {
    41  		case ops.OpNeq:
    42  			// x ~= y ==> ~(x = y)
    43  			c.emitInstr(b, ir.Combine{
    44  				Op:   ops.OpEq,
    45  				Dst:  c.dst,
    46  				Lsrc: lsrc,
    47  				Rsrc: rsrc,
    48  			})
    49  			c.emitInstr(b, ir.Transform{
    50  				Op:  ops.OpNot,
    51  				Dst: c.dst,
    52  				Src: c.dst,
    53  			})
    54  		case ops.OpGt:
    55  			// x > y ==> y < x
    56  			c.emitInstr(b, ir.Combine{
    57  				Op:   ops.OpLt,
    58  				Dst:  c.dst,
    59  				Lsrc: rsrc,
    60  				Rsrc: lsrc,
    61  			})
    62  		case ops.OpGeq:
    63  			// x >= y ==> y <= x
    64  			c.emitInstr(b, ir.Combine{
    65  				Op:   ops.OpLeq,
    66  				Dst:  c.dst,
    67  				Lsrc: rsrc,
    68  				Rsrc: lsrc,
    69  			})
    70  		default:
    71  			c.emitInstr(b, ir.Combine{
    72  				Op:   r.Op,
    73  				Dst:  c.dst,
    74  				Lsrc: lsrc,
    75  				Rsrc: rsrc,
    76  			})
    77  		}
    78  		c.ReleaseRegister(lsrc)
    79  		lsrc = c.dst
    80  	}
    81  }
    82  
    83  // This implements short-circuiting in logical expressions.
    84  func (c *expCompiler) compileLogicalOp(b ast.BinOp, not bool) {
    85  	doneLbl := c.GetNewLabel()
    86  	c.compileExpInto(b.Left, c.dst)
    87  	c.emitInstr(b.Left, ir.JumpIf{Cond: c.dst, Label: doneLbl, Not: not})
    88  	for i, r := range b.Right {
    89  		c.compileExpInto(r.Operand, c.dst)
    90  		if i < len(b.Right) {
    91  			c.emitInstr(r.Operand, ir.JumpIf{Cond: c.dst, Label: doneLbl, Not: not})
    92  		}
    93  	}
    94  	must(c.EmitLabelNoLine(doneLbl))
    95  }
    96  
    97  // ProcesBoolExp compiles a oolExp.
    98  func (c *expCompiler) ProcesBoolExp(b ast.Bool) {
    99  	c.emitLoadConst(b, ir.Bool(b.Val), c.dst)
   100  }
   101  
   102  // ProcessEtcExp compiles a EtcExp.
   103  func (c *expCompiler) ProcessEtcExp(e ast.Etc) {
   104  	reg := c.getEllipsisReg()
   105  	c.emitInstr(e, ir.EtcLookup{Dst: c.dst, Etc: reg})
   106  }
   107  
   108  // ProcessFunctionExp compiles a Function.
   109  func (c *expCompiler) ProcessFunctionExp(f ast.Function) {
   110  	fc := c.NewChild(f.Name)
   111  	fc.compileFunctionBody(f)
   112  	kidx, upvalues := fc.Close()
   113  	c.emitInstr(f, ir.MkClosure{
   114  		Dst:      c.dst,
   115  		Code:     kidx,
   116  		Upvalues: upvalues,
   117  	})
   118  }
   119  
   120  // ProcessFunctionCallExp compiles a FunctionCall.
   121  func (c *expCompiler) ProcessFunctionCallExp(f ast.FunctionCall) {
   122  	c.ProcessBFunctionCallExp(*f.BFunctionCall)
   123  }
   124  
   125  // ProcessIndexExp compiles a IndexExp.
   126  func (c *expCompiler) ProcessIndexExp(e ast.IndexExp) {
   127  	tReg := c.compileExpNoDestHint(e.Coll)
   128  	c.TakeRegister(tReg)
   129  	iReg := c.compileExpNoDestHint(e.Idx)
   130  	c.emitInstr(e, ir.Lookup{
   131  		Dst:   c.dst,
   132  		Table: tReg,
   133  		Index: iReg,
   134  	})
   135  	c.ReleaseRegister(tReg)
   136  }
   137  
   138  // ProcessNameExp compiles a NameExp.
   139  func (c *expCompiler) ProcessNameExp(n ast.Name) {
   140  	// Is it bound to a local name?
   141  	reg, ok := c.GetRegister(ir.Name(n.Val))
   142  	if ok {
   143  		c.dst = reg
   144  		return
   145  	}
   146  	// If not, try _ENV.
   147  	c.CompileExp(globalVar(n))
   148  }
   149  
   150  // ProcessNilExp compiles a NilExp.
   151  func (c *expCompiler) ProcessNilExp(n ast.Nil) {
   152  	c.emitLoadConst(n, ir.NilType{}, c.dst)
   153  }
   154  
   155  // ProcessIntExp compiles a IntExp.
   156  func (c *expCompiler) ProcessIntExp(n ast.Int) {
   157  	c.emitLoadConst(n, ir.Int(n.Val), c.dst)
   158  }
   159  
   160  // ProcessFloatExp compiles a FloatExp.
   161  func (c *expCompiler) ProcessFloatExp(f ast.Float) {
   162  	c.emitLoadConst(f, ir.Float(f.Val), c.dst)
   163  }
   164  
   165  // ProcessStringExp compiles a StringExp.
   166  func (c *expCompiler) ProcessStringExp(s ast.String) {
   167  	c.emitLoadConst(s, ir.String(s.Val), c.dst)
   168  }
   169  
   170  // ProcessTableConstructorExp compiles a TableConstructorExp.
   171  func (c *expCompiler) ProcessTableConstructorExp(t ast.TableConstructor) {
   172  	c.emitInstr(t, ir.MkTable{Dst: c.dst})
   173  	c.TakeRegister(c.dst)
   174  	currImplicitKey := 1
   175  	for i, field := range t.Fields {
   176  		keyExp := field.Key
   177  		_, noKey := keyExp.(ast.NoTableKey)
   178  		if i == len(t.Fields)-1 && noKey {
   179  			tailExp, ok := field.Value.(ast.TailExpNode)
   180  			if ok {
   181  				etc := c.compileEtcExp(tailExp, c.GetFreeRegister())
   182  				c.emitInstr(field.Value, ir.FillTable{
   183  					Dst: c.dst,
   184  					Idx: currImplicitKey,
   185  					Etc: etc,
   186  				})
   187  				break
   188  			}
   189  		}
   190  		valReg := c.compileExpNoDestHint(field.Value)
   191  		c.TakeRegister(valReg)
   192  		if noKey {
   193  			keyExp = ast.Int{Val: uint64(currImplicitKey)}
   194  			currImplicitKey++
   195  		}
   196  		keyReg := c.compileExpNoDestHint(keyExp)
   197  		c.emitInstr(field.Value, ir.SetIndex{
   198  			Table: c.dst,
   199  			Index: keyReg,
   200  			Src:   valReg,
   201  		})
   202  		c.ReleaseRegister(valReg)
   203  	}
   204  	c.ReleaseRegister(c.dst)
   205  }
   206  
   207  // ProcessUnOpExp compiles a UnOpExp.
   208  func (c *expCompiler) ProcessUnOpExp(u ast.UnOp) {
   209  	c.emitInstr(u, ir.Transform{
   210  		Op:  u.Op,
   211  		Dst: c.dst,
   212  		Src: c.compileExpNoDestHint(u.Operand),
   213  	})
   214  }
   215  
   216  func (c *expCompiler) CompileExp(e ast.ExpNode) {
   217  	e.ProcessExp(c)
   218  }
   219  
   220  //
   221  // Tail Expression compilation
   222  //
   223  
   224  type tailExpCompiler struct {
   225  	*compiler
   226  	dsts []ir.Register
   227  }
   228  
   229  var _ ast.TailExpProcessor = tailExpCompiler{}
   230  
   231  // ProcessEtcTailExp compiles an Etc tail expression.
   232  func (c tailExpCompiler) ProcessEtcTailExp(e ast.Etc) {
   233  	reg := c.getEllipsisReg()
   234  	for i, dst := range c.dsts {
   235  		c.emitInstr(e, ir.EtcLookup{
   236  			Dst: dst,
   237  			Etc: reg,
   238  			Idx: i,
   239  		})
   240  	}
   241  }
   242  
   243  // ProcessFunctionCallTailExp compiles a FunctionCall tail expression.
   244  func (c tailExpCompiler) ProcessFunctionCallTailExp(f ast.FunctionCall) {
   245  	c.compileCall(*f.BFunctionCall, false)
   246  	c.emitInstr(f, ir.Receive{Dst: c.dsts})
   247  }
   248  
   249  func (c tailExpCompiler) CompileTailExp(e ast.TailExpNode) {
   250  	e.ProcessTailExp(c)
   251  }
   252  
   253  //
   254  // Etc Expression compilation
   255  //
   256  
   257  type etcExpCompiler struct {
   258  	*compiler
   259  	dst ir.Register
   260  }
   261  
   262  var _ ast.TailExpProcessor = (*etcExpCompiler)(nil)
   263  
   264  func (c *etcExpCompiler) ProcessEtcTailExp(e ast.Etc) {
   265  	c.dst = c.getEllipsisReg()
   266  }
   267  
   268  func (c *etcExpCompiler) ProcessFunctionCallTailExp(f ast.FunctionCall) {
   269  	c.compileCall(*f.BFunctionCall, false)
   270  	c.emitInstr(f, ir.ReceiveEtc{Etc: c.dst})
   271  }
   272  
   273  func (c *etcExpCompiler) CompileTailExp(e ast.TailExpNode) {
   274  	e.ProcessTailExp(c)
   275  }
   276  
   277  //
   278  // Helper functions
   279  //
   280  
   281  // compileExp compiles the given expression into a register and returns it.
   282  func (c *compiler) compileExp(e ast.ExpNode, dst ir.Register) ir.Register {
   283  	ec := expCompiler{
   284  		compiler: c,
   285  		dst:      dst,
   286  	}
   287  	ec.CompileExp(e)
   288  	return ec.dst
   289  }
   290  
   291  // compileExpNoDestHint compiles the given expression into any register (perhaps a
   292  // new free one) and returns it.
   293  func (c *compiler) compileExpNoDestHint(e ast.ExpNode) ir.Register {
   294  	return c.compileExp(e, c.GetFreeRegister())
   295  }
   296  
   297  // compileExpInto compiles the given expression into the given register.
   298  func (c *compiler) compileExpInto(e ast.ExpNode, dst ir.Register) {
   299  	c.emitMove(e, dst, c.compileExp(e, dst))
   300  }
   301  
   302  // compileTailExp compiles the given tail expression into the register slice.
   303  func (c *compiler) compileTailExp(e ast.TailExpNode, dsts []ir.Register) {
   304  	tailExpCompiler{
   305  		compiler: c,
   306  		dsts:     dsts,
   307  	}.CompileTailExp(e)
   308  }
   309  
   310  // compileEtcExp compiles the given tail expression as an etc and returns the
   311  // register the result lives in.
   312  func (c *compiler) compileEtcExp(e ast.TailExpNode, dst ir.Register) ir.Register {
   313  	ec := etcExpCompiler{
   314  		compiler: c,
   315  		dst:      dst,
   316  	}
   317  	ec.CompileTailExp(e)
   318  	return ec.dst
   319  }
   320  
   321  // compileExpList compiles the given expressions into free registers, which are
   322  // recorded into dstRegs (hence exps and dstRegs must have the same length).
   323  // Those registers are taken so need to be released by the caller when no longer
   324  // needed.
   325  func (c *compiler) compileExpList(exps []ast.ExpNode, dstRegs []ir.Register) {
   326  	commonCount := len(exps)
   327  	if commonCount > len(dstRegs) {
   328  		commonCount = len(dstRegs)
   329  	}
   330  	var tailExp ast.TailExpNode
   331  	doTailExp := false
   332  	if len(dstRegs) > len(exps) && len(exps) > 0 {
   333  		tailExp, doTailExp = exps[len(exps)-1].(ast.TailExpNode)
   334  		if doTailExp {
   335  			commonCount--
   336  		}
   337  	}
   338  	for i, exp := range exps[:commonCount] {
   339  		dst := c.GetFreeRegister()
   340  		c.compileExpInto(exp, dst)
   341  		c.TakeRegister(dst)
   342  		dstRegs[i] = dst
   343  	}
   344  	for i := commonCount; i < len(dstRegs); i++ {
   345  		dst := c.GetFreeRegister()
   346  		c.TakeRegister(dst)
   347  		dstRegs[i] = dst
   348  	}
   349  	if doTailExp {
   350  		c.compileTailExp(tailExp, dstRegs[commonCount:])
   351  	} else if len(dstRegs) > len(exps) {
   352  		nilK := ir.NilType{}
   353  		for _, dst := range dstRegs[len(exps):] {
   354  			c.emitLoadConst(nil, nilK, dst)
   355  		}
   356  	}
   357  }
   358  
   359  func (c *compiler) compileFunctionBody(f ast.Function) {
   360  	recvRegs := make([]ir.Register, len(f.Params))
   361  	callerReg := c.GetFreeRegister()
   362  	c.DeclareLocal(callerRegName, callerReg)
   363  	for i, p := range f.Params {
   364  		reg := c.GetFreeRegister()
   365  		c.DeclareLocal(ir.Name(p.Val), reg)
   366  		recvRegs[i] = reg
   367  	}
   368  	if !f.HasDots {
   369  		c.emitInstr(f, ir.Receive{Dst: recvRegs})
   370  	} else {
   371  		reg := c.GetFreeRegister()
   372  		c.DeclareLocal(ellipsisRegName, reg)
   373  		c.emitInstr(f, ir.ReceiveEtc{Dst: recvRegs, Etc: reg})
   374  	}
   375  
   376  	// Need to make sure there is a return instruction emitted at the
   377  	// end.
   378  	body := f.Body
   379  	if body.Return == nil {
   380  		body.Return = []ast.ExpNode{}
   381  	}
   382  	c.compileBlock(body)
   383  
   384  }
   385  
   386  func (c *compiler) getEllipsisReg() ir.Register {
   387  	reg, ok := c.GetRegister(ellipsisRegName)
   388  	if !ok {
   389  		panic("... not defined")
   390  	}
   391  	return reg
   392  }
   393  
   394  func (c *compiler) getCallerReg() ir.Register {
   395  	reg, ok := c.GetRegister(callerRegName)
   396  	if !ok {
   397  		panic("no caller register")
   398  	}
   399  	return reg
   400  }
   401  
   402  func (c *compiler) compileCall(f ast.BFunctionCall, tail bool) {
   403  	fReg := c.compileExpNoDestHint(f.Target)
   404  	var contReg ir.Register
   405  	if f.Method.Val != "" {
   406  		self := fReg
   407  		c.TakeRegister(self)
   408  		fReg = c.GetFreeRegister()
   409  		mReg := c.GetFreeRegister()
   410  		c.emitLoadConst(f.Method, ir.String(f.Method.Val), mReg)
   411  		c.emitInstr(f.Target, ir.Lookup{
   412  			Dst:   fReg,
   413  			Table: self,
   414  			Index: mReg,
   415  		})
   416  		contReg = c.GetFreeRegister()
   417  		c.emitInstr(f, ir.MkCont{
   418  			Dst:     contReg,
   419  			Closure: fReg,
   420  			Tail:    tail,
   421  		})
   422  		c.emitInstr(f, ir.Push{
   423  			Cont: fReg,
   424  			Item: self,
   425  		})
   426  		c.ReleaseRegister(self)
   427  	} else {
   428  		contReg = c.GetFreeRegister()
   429  		c.emitInstr(f, ir.MkCont{
   430  			Dst:     contReg,
   431  			Closure: fReg,
   432  			Tail:    tail,
   433  		})
   434  	}
   435  	c.compilePushArgs(f.Args, contReg)
   436  	c.emitInstr(f, ir.Call{
   437  		Cont: contReg,
   438  		Tail: tail,
   439  	})
   440  }
   441  
   442  // TODO: move this to somewhere better
   443  func (c *compiler) compilePushArgs(args []ast.ExpNode, contReg ir.Register) {
   444  	c.TakeRegister(contReg)
   445  	for i, arg := range args {
   446  		var argReg ir.Register
   447  		var isTailArg bool
   448  		if i == len(args)-1 {
   449  			var tailArg ast.TailExpNode
   450  			tailArg, isTailArg = arg.(ast.TailExpNode)
   451  			if isTailArg {
   452  				argReg = c.compileEtcExp(tailArg, c.GetFreeRegister())
   453  			}
   454  		}
   455  		if !isTailArg {
   456  			argReg = c.compileExpNoDestHint(arg)
   457  		}
   458  		c.emitInstr(arg, ir.Push{
   459  			Cont: contReg,
   460  			Item: argReg,
   461  			Etc:  isTailArg,
   462  		})
   463  	}
   464  	c.ReleaseRegister(contReg)
   465  }
   466  
   467  func globalVar(n ast.Name) ast.IndexExp {
   468  	return ast.IndexExp{
   469  		Location: n.Location,
   470  		Coll:     ast.Name{Location: n.Location, Val: "_ENV"},
   471  		Idx:      n.AstString(),
   472  	}
   473  }