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 }