twitchapon-anim

Basic Twitchapon Receiver/Visuals
git clone git://bsandro.tech/twitchapon-anim
Log | Files | Refs | README | LICENSE

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 }