stmt.go (18200B)
1 // Copyright 2020 The Ebiten Authors 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package shader 16 17 import ( 18 "fmt" 19 "go/ast" 20 gconstant "go/constant" 21 "go/token" 22 "strings" 23 24 "github.com/hajimehoshi/ebiten/v2/internal/shaderir" 25 ) 26 27 func (cs *compileState) forceToInt(node ast.Node, expr *shaderir.Expr) bool { 28 if !canTruncateToInteger(expr.Const) { 29 cs.addError(node.Pos(), fmt.Sprintf("constant %s truncated to integer", expr.Const.String())) 30 return false 31 } 32 expr.ConstType = shaderir.ConstTypeInt 33 return true 34 } 35 36 func (cs *compileState) parseStmt(block *block, fname string, stmt ast.Stmt, inParams, outParams []variable) ([]shaderir.Stmt, bool) { 37 var stmts []shaderir.Stmt 38 39 switch stmt := stmt.(type) { 40 case *ast.AssignStmt: 41 switch stmt.Tok { 42 case token.DEFINE: 43 if len(stmt.Lhs) != len(stmt.Rhs) && len(stmt.Rhs) != 1 { 44 cs.addError(stmt.Pos(), fmt.Sprintf("single-value context and multiple-value context cannot be mixed")) 45 return nil, false 46 } 47 48 ss, ok := cs.assign(block, fname, stmt.Pos(), stmt.Lhs, stmt.Rhs, inParams, true) 49 if !ok { 50 return nil, false 51 } 52 stmts = append(stmts, ss...) 53 case token.ASSIGN: 54 if len(stmt.Lhs) != len(stmt.Rhs) && len(stmt.Rhs) != 1 { 55 cs.addError(stmt.Pos(), fmt.Sprintf("single-value context and multiple-value context cannot be mixed")) 56 return nil, false 57 } 58 ss, ok := cs.assign(block, fname, stmt.Pos(), stmt.Lhs, stmt.Rhs, inParams, false) 59 if !ok { 60 return nil, false 61 } 62 stmts = append(stmts, ss...) 63 case token.ADD_ASSIGN, token.SUB_ASSIGN, token.MUL_ASSIGN, token.QUO_ASSIGN, token.REM_ASSIGN: 64 var op shaderir.Op 65 switch stmt.Tok { 66 case token.ADD_ASSIGN: 67 op = shaderir.Add 68 case token.SUB_ASSIGN: 69 op = shaderir.Sub 70 case token.MUL_ASSIGN: 71 op = shaderir.Mul 72 case token.QUO_ASSIGN: 73 op = shaderir.Div 74 case token.REM_ASSIGN: 75 op = shaderir.ModOp 76 } 77 78 rhs, _, ss, ok := cs.parseExpr(block, stmt.Rhs[0], true) 79 if !ok { 80 return nil, false 81 } 82 stmts = append(stmts, ss...) 83 84 lhs, ts, ss, ok := cs.parseExpr(block, stmt.Lhs[0], true) 85 if !ok { 86 return nil, false 87 } 88 stmts = append(stmts, ss...) 89 90 if rhs[0].Type == shaderir.NumberExpr && ts[0].Main == shaderir.Int { 91 if !cs.forceToInt(stmt, &rhs[0]) { 92 return nil, false 93 } 94 } 95 96 stmts = append(stmts, shaderir.Stmt{ 97 Type: shaderir.Assign, 98 Exprs: []shaderir.Expr{ 99 lhs[0], 100 { 101 Type: shaderir.Binary, 102 Op: op, 103 Exprs: []shaderir.Expr{ 104 lhs[0], 105 rhs[0], 106 }, 107 }, 108 }, 109 }) 110 default: 111 cs.addError(stmt.Pos(), fmt.Sprintf("unexpected token: %s", stmt.Tok)) 112 } 113 case *ast.BlockStmt: 114 b, ok := cs.parseBlock(block, fname, stmt.List, inParams, outParams, true) 115 if !ok { 116 return nil, false 117 } 118 stmts = append(stmts, shaderir.Stmt{ 119 Type: shaderir.BlockStmt, 120 Blocks: []*shaderir.Block{ 121 b.ir, 122 }, 123 }) 124 case *ast.DeclStmt: 125 ss, ok := cs.parseDecl(block, stmt.Decl) 126 if !ok { 127 return nil, false 128 } 129 stmts = append(stmts, ss...) 130 131 case *ast.ForStmt: 132 msg := "for-statement must follow this format: for (varname) := (constant); (varname) (op) (constant); (varname) (op) (constant) { ..." 133 if stmt.Init == nil { 134 cs.addError(stmt.Pos(), msg) 135 return nil, false 136 } 137 if stmt.Cond == nil { 138 cs.addError(stmt.Pos(), msg) 139 return nil, false 140 } 141 if stmt.Post == nil { 142 cs.addError(stmt.Pos(), msg) 143 return nil, false 144 } 145 146 // Create a new pseudo block for the initial statement, so that the counter variable belongs to the 147 // new pseudo block for each for-loop. Without this, the samely named counter variables in different 148 // for-loops confuses the parser. 149 pseudoBlock, ok := cs.parseBlock(block, fname, []ast.Stmt{stmt.Init}, inParams, outParams, false) 150 if !ok { 151 return nil, false 152 } 153 ss := pseudoBlock.ir.Stmts 154 155 if len(ss) != 1 { 156 cs.addError(stmt.Pos(), msg) 157 return nil, false 158 } 159 if ss[0].Type != shaderir.Assign { 160 cs.addError(stmt.Pos(), msg) 161 return nil, false 162 } 163 if ss[0].Exprs[0].Type != shaderir.LocalVariable { 164 cs.addError(stmt.Pos(), msg) 165 return nil, false 166 } 167 varidx := ss[0].Exprs[0].Index 168 if ss[0].Exprs[1].Type != shaderir.NumberExpr { 169 cs.addError(stmt.Pos(), msg) 170 return nil, false 171 } 172 173 vartype := pseudoBlock.vars[0].typ 174 init := ss[0].Exprs[1].Const 175 176 exprs, ts, ss, ok := cs.parseExpr(pseudoBlock, stmt.Cond, true) 177 if !ok { 178 return nil, false 179 } 180 if len(exprs) != 1 { 181 cs.addError(stmt.Pos(), msg) 182 return nil, false 183 } 184 if len(ts) != 1 || ts[0].Main != shaderir.Bool { 185 cs.addError(stmt.Pos(), "for-statement's condition must be bool") 186 return nil, false 187 } 188 if len(ss) != 0 { 189 cs.addError(stmt.Pos(), msg) 190 return nil, false 191 } 192 if exprs[0].Type != shaderir.Binary { 193 cs.addError(stmt.Pos(), msg) 194 return nil, false 195 } 196 op := exprs[0].Op 197 if op != shaderir.LessThanOp && op != shaderir.LessThanEqualOp && op != shaderir.GreaterThanOp && op != shaderir.GreaterThanEqualOp && op != shaderir.EqualOp && op != shaderir.NotEqualOp { 198 cs.addError(stmt.Pos(), "for-statement's condition must have one of these operators: <, <=, >, >=, ==, !=") 199 return nil, false 200 } 201 if exprs[0].Exprs[0].Type != shaderir.LocalVariable { 202 cs.addError(stmt.Pos(), msg) 203 return nil, false 204 } 205 if exprs[0].Exprs[0].Index != varidx { 206 cs.addError(stmt.Pos(), msg) 207 return nil, false 208 } 209 if exprs[0].Exprs[1].Type != shaderir.NumberExpr { 210 cs.addError(stmt.Pos(), msg) 211 return nil, false 212 } 213 end := exprs[0].Exprs[1].Const 214 215 postSs, ok := cs.parseStmt(pseudoBlock, fname, stmt.Post, inParams, outParams) 216 if !ok { 217 return nil, false 218 } 219 if len(postSs) != 1 { 220 cs.addError(stmt.Pos(), msg) 221 return nil, false 222 } 223 if postSs[0].Type != shaderir.Assign { 224 cs.addError(stmt.Pos(), msg) 225 return nil, false 226 } 227 if postSs[0].Exprs[0].Type != shaderir.LocalVariable { 228 cs.addError(stmt.Pos(), msg) 229 return nil, false 230 } 231 if postSs[0].Exprs[0].Index != varidx { 232 cs.addError(stmt.Pos(), msg) 233 return nil, false 234 } 235 if postSs[0].Exprs[1].Type != shaderir.Binary { 236 cs.addError(stmt.Pos(), msg) 237 return nil, false 238 } 239 if postSs[0].Exprs[1].Exprs[0].Type != shaderir.LocalVariable { 240 cs.addError(stmt.Pos(), msg) 241 return nil, false 242 } 243 if postSs[0].Exprs[1].Exprs[0].Index != varidx { 244 cs.addError(stmt.Pos(), msg) 245 return nil, false 246 } 247 if postSs[0].Exprs[1].Exprs[1].Type != shaderir.NumberExpr { 248 cs.addError(stmt.Pos(), msg) 249 return nil, false 250 } 251 delta := postSs[0].Exprs[1].Exprs[1].Const 252 switch postSs[0].Exprs[1].Op { 253 case shaderir.Add: 254 case shaderir.Sub: 255 delta = gconstant.UnaryOp(token.SUB, delta, 0) 256 default: 257 cs.addError(stmt.Pos(), "for-statement's post statement must have one of these operators: +=, -=, ++, --") 258 return nil, false 259 } 260 261 b, ok := cs.parseBlock(pseudoBlock, fname, []ast.Stmt{stmt.Body}, inParams, outParams, true) 262 if !ok { 263 return nil, false 264 } 265 bodyir := b.ir 266 for len(bodyir.Stmts) == 1 && bodyir.Stmts[0].Type == shaderir.BlockStmt { 267 bodyir = bodyir.Stmts[0].Blocks[0] 268 } 269 270 // As the pseudo block is not actually used, copy the variable part to the actual block. 271 // This must be done after parsing the for-loop is done, or the duplicated variables confuses the 272 // parsing. 273 v := pseudoBlock.vars[0] 274 v.forLoopCounter = true 275 block.vars = append(block.vars, v) 276 277 stmts = append(stmts, shaderir.Stmt{ 278 Type: shaderir.For, 279 Blocks: []*shaderir.Block{bodyir}, 280 ForVarType: vartype, 281 ForVarIndex: varidx, 282 ForInit: init, 283 ForEnd: end, 284 ForOp: op, 285 ForDelta: delta, 286 }) 287 288 case *ast.IfStmt: 289 if stmt.Init != nil { 290 init := stmt.Init 291 stmt.Init = nil 292 b, ok := cs.parseBlock(block, fname, []ast.Stmt{init, stmt}, inParams, outParams, true) 293 if !ok { 294 return nil, false 295 } 296 297 stmts = append(stmts, shaderir.Stmt{ 298 Type: shaderir.BlockStmt, 299 Blocks: []*shaderir.Block{b.ir}, 300 }) 301 return stmts, true 302 } 303 304 exprs, ts, ss, ok := cs.parseExpr(block, stmt.Cond, true) 305 if !ok { 306 return nil, false 307 } 308 if len(ts) != 1 || ts[0].Main != shaderir.Bool { 309 var tss []string 310 for _, t := range ts { 311 tss = append(tss, t.String()) 312 } 313 cs.addError(stmt.Pos(), fmt.Sprintf("if-condition must be bool but: %s", strings.Join(tss, ", "))) 314 return nil, false 315 } 316 stmts = append(stmts, ss...) 317 318 var bs []*shaderir.Block 319 b, ok := cs.parseBlock(block, fname, stmt.Body.List, inParams, outParams, true) 320 if !ok { 321 return nil, false 322 } 323 bs = append(bs, b.ir) 324 325 if stmt.Else != nil { 326 switch s := stmt.Else.(type) { 327 case *ast.BlockStmt: 328 b, ok := cs.parseBlock(block, fname, s.List, inParams, outParams, true) 329 if !ok { 330 return nil, false 331 } 332 bs = append(bs, b.ir) 333 default: 334 b, ok := cs.parseBlock(block, fname, []ast.Stmt{s}, inParams, outParams, true) 335 if !ok { 336 return nil, false 337 } 338 bs = append(bs, b.ir) 339 } 340 } 341 342 stmts = append(stmts, shaderir.Stmt{ 343 Type: shaderir.If, 344 Exprs: exprs, 345 Blocks: bs, 346 }) 347 348 case *ast.IncDecStmt: 349 exprs, _, ss, ok := cs.parseExpr(block, stmt.X, true) 350 if !ok { 351 return nil, false 352 } 353 stmts = append(stmts, ss...) 354 var op shaderir.Op 355 switch stmt.Tok { 356 case token.INC: 357 op = shaderir.Add 358 case token.DEC: 359 op = shaderir.Sub 360 } 361 stmts = append(stmts, shaderir.Stmt{ 362 Type: shaderir.Assign, 363 Exprs: []shaderir.Expr{ 364 exprs[0], 365 { 366 Type: shaderir.Binary, 367 Op: op, 368 Exprs: []shaderir.Expr{ 369 exprs[0], 370 { 371 Type: shaderir.NumberExpr, 372 Const: gconstant.MakeInt64(1), 373 ConstType: shaderir.ConstTypeInt, 374 }, 375 }, 376 }, 377 }, 378 }) 379 380 case *ast.ReturnStmt: 381 if len(stmt.Results) != len(outParams) && len(stmt.Results) != 1 { 382 if !(len(stmt.Results) == 0 && len(outParams) > 0 && outParams[0].name != "") { 383 // TODO: Check variable shadowings. 384 // https://golang.org/ref/spec#Return_statements 385 cs.addError(stmt.Pos(), fmt.Sprintf("the number of returning variables must be %d but %d", len(outParams), len(stmt.Results))) 386 return nil, false 387 } 388 } 389 390 for i, r := range stmt.Results { 391 exprs, ts, ss, ok := cs.parseExpr(block, r, true) 392 if !ok { 393 return nil, false 394 } 395 stmts = append(stmts, ss...) 396 397 if len(exprs) > 1 { 398 if len(stmt.Results) > 1 || len(outParams) == 1 { 399 cs.addError(r.Pos(), "single-value context and multiple-value context cannot be mixed") 400 return nil, false 401 } 402 } 403 404 if len(outParams) > 1 && len(stmt.Results) == 1 { 405 if len(exprs) == 1 { 406 cs.addError(stmt.Pos(), fmt.Sprintf("the number of returning variables must be %d but %d", len(outParams), len(stmt.Results))) 407 return nil, false 408 } 409 if len(exprs) > 1 && len(exprs) != len(outParams) { 410 cs.addError(stmt.Pos(), fmt.Sprintf("the number of returning variables must be %d but %d", len(outParams), len(exprs))) 411 return nil, false 412 } 413 } 414 415 for j, t := range ts { 416 expr := exprs[j] 417 if expr.Type == shaderir.NumberExpr { 418 switch outParams[i+j].typ.Main { 419 case shaderir.Int: 420 if !cs.forceToInt(stmt, &expr) { 421 return nil, false 422 } 423 t = shaderir.Type{Main: shaderir.Int} 424 case shaderir.Float: 425 t = shaderir.Type{Main: shaderir.Float} 426 } 427 } 428 429 if !t.Equal(&outParams[i+j].typ) { 430 cs.addError(stmt.Pos(), fmt.Sprintf("cannot use type %s as type %s in return argument", t.String(), &outParams[i].typ)) 431 return nil, false 432 } 433 434 stmts = append(stmts, shaderir.Stmt{ 435 Type: shaderir.Assign, 436 Exprs: []shaderir.Expr{ 437 { 438 Type: shaderir.LocalVariable, 439 Index: len(inParams) + i + j, 440 }, 441 expr, 442 }, 443 }) 444 } 445 } 446 stmts = append(stmts, shaderir.Stmt{ 447 Type: shaderir.Return, 448 }) 449 450 case *ast.BranchStmt: 451 switch stmt.Tok { 452 case token.BREAK: 453 stmts = append(stmts, shaderir.Stmt{ 454 Type: shaderir.Break, 455 }) 456 case token.CONTINUE: 457 stmts = append(stmts, shaderir.Stmt{ 458 Type: shaderir.Continue, 459 }) 460 default: 461 cs.addError(stmt.Pos(), fmt.Sprintf("invalid token: %s", stmt.Tok)) 462 return nil, false 463 } 464 465 case *ast.ExprStmt: 466 exprs, _, ss, ok := cs.parseExpr(block, stmt.X, true) 467 if !ok { 468 return nil, false 469 } 470 stmts = append(stmts, ss...) 471 472 for _, expr := range exprs { 473 if expr.Type != shaderir.Call { 474 continue 475 } 476 stmts = append(stmts, shaderir.Stmt{ 477 Type: shaderir.ExprStmt, 478 Exprs: []shaderir.Expr{expr}, 479 }) 480 } 481 482 default: 483 cs.addError(stmt.Pos(), fmt.Sprintf("unexpected statement: %#v", stmt)) 484 return nil, false 485 } 486 return stmts, true 487 } 488 489 func (cs *compileState) assign(block *block, fname string, pos token.Pos, lhs, rhs []ast.Expr, inParams []variable, define bool) ([]shaderir.Stmt, bool) { 490 var stmts []shaderir.Stmt 491 var rhsExprs []shaderir.Expr 492 var rhsTypes []shaderir.Type 493 allblank := true 494 495 for i, e := range lhs { 496 if len(lhs) == len(rhs) { 497 // Prase RHS first for the order of the statements. 498 r, origts, ss, ok := cs.parseExpr(block, rhs[i], true) 499 if !ok { 500 return nil, false 501 } 502 stmts = append(stmts, ss...) 503 504 if define { 505 name := e.(*ast.Ident).Name 506 if name != "_" { 507 for _, v := range block.vars { 508 if v.name == name { 509 cs.addError(pos, fmt.Sprintf("duplicated local variable name: %s", name)) 510 return nil, false 511 } 512 } 513 } 514 ts, ok := cs.functionReturnTypes(block, rhs[i]) 515 if !ok { 516 ts = origts 517 } 518 if len(ts) > 1 { 519 cs.addError(pos, fmt.Sprintf("single-value context and multiple-value context cannot be mixed")) 520 return nil, false 521 } 522 523 t := ts[0] 524 if t.Main == shaderir.None { 525 t = toDefaultType(r[0].Const) 526 } 527 block.addNamedLocalVariable(name, t, e.Pos()) 528 } 529 530 if len(r) > 1 { 531 cs.addError(pos, fmt.Sprintf("single-value context and multiple-value context cannot be mixed")) 532 return nil, false 533 } 534 535 l, _, ss, ok := cs.parseExpr(block, lhs[i], false) 536 if !ok { 537 return nil, false 538 } 539 stmts = append(stmts, ss...) 540 541 if l[0].Type == shaderir.Blank { 542 continue 543 } 544 545 var isAssignmentForbidden func(e *shaderir.Expr) bool 546 isAssignmentForbidden = func(e *shaderir.Expr) bool { 547 switch e.Type { 548 case shaderir.UniformVariable: 549 return true 550 case shaderir.LocalVariable: 551 if fname == cs.vertexEntry || fname == cs.fragmentEntry { 552 return e.Index < len(inParams) 553 } 554 case shaderir.FieldSelector: 555 return isAssignmentForbidden(&e.Exprs[0]) 556 case shaderir.Index: 557 return isAssignmentForbidden(&e.Exprs[0]) 558 } 559 return false 560 } 561 562 if isAssignmentForbidden(&l[0]) { 563 cs.addError(pos, fmt.Sprintf("a uniform variable cannot be assigned")) 564 return nil, false 565 } 566 allblank = false 567 568 if r[0].Type == shaderir.NumberExpr { 569 t, ok := block.findLocalVariableByIndex(l[0].Index) 570 if !ok { 571 cs.addError(pos, fmt.Sprintf("unexpected local variable index: %d", l[0].Index)) 572 return nil, false 573 } 574 switch t.Main { 575 case shaderir.Int: 576 r[0].ConstType = shaderir.ConstTypeInt 577 case shaderir.Float: 578 r[0].ConstType = shaderir.ConstTypeFloat 579 } 580 } 581 582 if len(lhs) == 1 { 583 stmts = append(stmts, shaderir.Stmt{ 584 Type: shaderir.Assign, 585 Exprs: []shaderir.Expr{l[0], r[0]}, 586 }) 587 } else { 588 // For variable swapping, use temporary variables. 589 t := origts[0] 590 if t.Main == shaderir.None { 591 t = toDefaultType(r[0].Const) 592 } 593 block.vars = append(block.vars, variable{ 594 typ: t, 595 }) 596 idx := len(block.vars) - 1 597 stmts = append(stmts, 598 shaderir.Stmt{ 599 Type: shaderir.Assign, 600 Exprs: []shaderir.Expr{ 601 { 602 Type: shaderir.LocalVariable, 603 Index: idx, 604 }, 605 r[0], 606 }, 607 }, 608 shaderir.Stmt{ 609 Type: shaderir.Assign, 610 Exprs: []shaderir.Expr{ 611 l[0], 612 { 613 Type: shaderir.LocalVariable, 614 Index: idx, 615 }, 616 }, 617 }) 618 } 619 } else { 620 if i == 0 { 621 var ss []shaderir.Stmt 622 var ok bool 623 rhsExprs, rhsTypes, ss, ok = cs.parseExpr(block, rhs[0], true) 624 if !ok { 625 return nil, false 626 } 627 if len(rhsExprs) != len(lhs) { 628 cs.addError(pos, fmt.Sprintf("single-value context and multiple-value context cannot be mixed")) 629 } 630 stmts = append(stmts, ss...) 631 } 632 633 if define { 634 name := e.(*ast.Ident).Name 635 if name != "_" { 636 for _, v := range block.vars { 637 if v.name == name { 638 cs.addError(pos, fmt.Sprintf("duplicated local variable name: %s", name)) 639 return nil, false 640 } 641 } 642 } 643 t := rhsTypes[i] 644 if t.Main == shaderir.None { 645 // TODO: This is to determine a type when the rhs values are constants (not literals), 646 // but there are no actual cases when len(lhs) != len(rhs). Is this correct? 647 t = toDefaultType(rhsExprs[i].Const) 648 } 649 block.addNamedLocalVariable(name, t, e.Pos()) 650 } 651 652 l, _, ss, ok := cs.parseExpr(block, lhs[i], false) 653 if !ok { 654 return nil, false 655 } 656 stmts = append(stmts, ss...) 657 658 if l[0].Type == shaderir.Blank { 659 continue 660 } 661 allblank = false 662 663 stmts = append(stmts, shaderir.Stmt{ 664 Type: shaderir.Assign, 665 Exprs: []shaderir.Expr{l[0], rhsExprs[i]}, 666 }) 667 } 668 } 669 670 if define && allblank { 671 cs.addError(pos, fmt.Sprintf("no new variables on left side of :=")) 672 return nil, false 673 } 674 675 return stmts, true 676 } 677 678 func toDefaultType(v gconstant.Value) shaderir.Type { 679 switch v.Kind() { 680 case gconstant.Bool: 681 return shaderir.Type{Main: shaderir.Bool} 682 case gconstant.Int: 683 return shaderir.Type{Main: shaderir.Int} 684 case gconstant.Float: 685 return shaderir.Type{Main: shaderir.Float} 686 } 687 // TODO: Should this be an error? 688 return shaderir.Type{} 689 }