stmt.go (17420B)
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, &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 block.addNamedLocalVariable(name, ts[0], e.Pos()) 524 } 525 526 if len(r) > 1 { 527 cs.addError(pos, fmt.Sprintf("single-value context and multiple-value context cannot be mixed")) 528 return nil, false 529 } 530 531 l, _, ss, ok := cs.parseExpr(block, lhs[i], false) 532 if !ok { 533 return nil, false 534 } 535 stmts = append(stmts, ss...) 536 537 if l[0].Type == shaderir.Blank { 538 continue 539 } 540 541 var isAssignmentForbidden func(e *shaderir.Expr) bool 542 isAssignmentForbidden = func(e *shaderir.Expr) bool { 543 switch e.Type { 544 case shaderir.UniformVariable: 545 return true 546 case shaderir.LocalVariable: 547 if fname == cs.vertexEntry || fname == cs.fragmentEntry { 548 return e.Index < len(inParams) 549 } 550 case shaderir.FieldSelector: 551 return isAssignmentForbidden(&e.Exprs[0]) 552 case shaderir.Index: 553 return isAssignmentForbidden(&e.Exprs[0]) 554 } 555 return false 556 } 557 558 if isAssignmentForbidden(&l[0]) { 559 cs.addError(pos, fmt.Sprintf("a uniform variable cannot be assigned")) 560 return nil, false 561 } 562 allblank = false 563 564 if r[0].Type == shaderir.NumberExpr { 565 t, ok := block.findLocalVariableByIndex(l[0].Index) 566 if !ok { 567 cs.addError(pos, fmt.Sprintf("unexpected local variable index: %d", l[0].Index)) 568 return nil, false 569 } 570 switch t.Main { 571 case shaderir.Int: 572 r[0].ConstType = shaderir.ConstTypeInt 573 case shaderir.Float: 574 r[0].ConstType = shaderir.ConstTypeFloat 575 } 576 } 577 578 if len(lhs) == 1 { 579 stmts = append(stmts, shaderir.Stmt{ 580 Type: shaderir.Assign, 581 Exprs: []shaderir.Expr{l[0], r[0]}, 582 }) 583 } else { 584 // For variable swapping, use temporary variables. 585 block.vars = append(block.vars, variable{ 586 typ: origts[0], 587 }) 588 idx := len(block.vars) - 1 589 stmts = append(stmts, 590 shaderir.Stmt{ 591 Type: shaderir.Assign, 592 Exprs: []shaderir.Expr{ 593 { 594 Type: shaderir.LocalVariable, 595 Index: idx, 596 }, 597 r[0], 598 }, 599 }, 600 shaderir.Stmt{ 601 Type: shaderir.Assign, 602 Exprs: []shaderir.Expr{ 603 l[0], 604 { 605 Type: shaderir.LocalVariable, 606 Index: idx, 607 }, 608 }, 609 }) 610 } 611 } else { 612 if i == 0 { 613 var ss []shaderir.Stmt 614 var ok bool 615 rhsExprs, rhsTypes, ss, ok = cs.parseExpr(block, rhs[0], true) 616 if !ok { 617 return nil, false 618 } 619 if len(rhsExprs) != len(lhs) { 620 cs.addError(pos, fmt.Sprintf("single-value context and multiple-value context cannot be mixed")) 621 } 622 stmts = append(stmts, ss...) 623 } 624 625 if define { 626 name := e.(*ast.Ident).Name 627 if name != "_" { 628 for _, v := range block.vars { 629 if v.name == name { 630 cs.addError(pos, fmt.Sprintf("duplicated local variable name: %s", name)) 631 return nil, false 632 } 633 } 634 } 635 block.addNamedLocalVariable(name, rhsTypes[i], e.Pos()) 636 } 637 638 l, _, ss, ok := cs.parseExpr(block, lhs[i], false) 639 if !ok { 640 return nil, false 641 } 642 stmts = append(stmts, ss...) 643 644 if l[0].Type == shaderir.Blank { 645 continue 646 } 647 allblank = false 648 649 stmts = append(stmts, shaderir.Stmt{ 650 Type: shaderir.Assign, 651 Exprs: []shaderir.Expr{l[0], rhsExprs[i]}, 652 }) 653 } 654 } 655 656 if define && allblank { 657 cs.addError(pos, fmt.Sprintf("no new variables on left side of :=")) 658 return nil, false 659 } 660 661 return stmts, true 662 }