zorldo

Goofing around with Ebiten
git clone git://bsandro.tech/zorldo
Log | Files | Refs | README

expr.go (18493B)


      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 	"regexp"
     23 	"strconv"
     24 
     25 	"github.com/hajimehoshi/ebiten/v2/internal/shaderir"
     26 )
     27 
     28 func canTruncateToInteger(v gconstant.Value) bool {
     29 	return gconstant.ToInt(v).Kind() != gconstant.Unknown
     30 }
     31 
     32 var textureVariableRe = regexp.MustCompile(`\A__t(\d+)\z`)
     33 
     34 func (cs *compileState) parseExpr(block *block, expr ast.Expr, markLocalVariableUsed bool) ([]shaderir.Expr, []shaderir.Type, []shaderir.Stmt, bool) {
     35 	switch e := expr.(type) {
     36 	case *ast.BasicLit:
     37 		switch e.Kind {
     38 		case token.INT:
     39 			return []shaderir.Expr{
     40 				{
     41 					Type:  shaderir.NumberExpr,
     42 					Const: gconstant.MakeFromLiteral(e.Value, e.Kind, 0),
     43 				},
     44 			}, []shaderir.Type{{Main: shaderir.Int}}, nil, true
     45 		case token.FLOAT:
     46 			return []shaderir.Expr{
     47 				{
     48 					Type:  shaderir.NumberExpr,
     49 					Const: gconstant.MakeFromLiteral(e.Value, e.Kind, 0),
     50 				},
     51 			}, []shaderir.Type{{Main: shaderir.Float}}, nil, true
     52 		default:
     53 			cs.addError(e.Pos(), fmt.Sprintf("literal not implemented: %#v", e))
     54 		}
     55 
     56 	case *ast.BinaryExpr:
     57 		var stmts []shaderir.Stmt
     58 
     59 		// Prase LHS first for the order of the statements.
     60 		lhs, ts, ss, ok := cs.parseExpr(block, e.X, markLocalVariableUsed)
     61 		if !ok {
     62 			return nil, nil, nil, false
     63 		}
     64 		if len(lhs) != 1 {
     65 			cs.addError(e.Pos(), fmt.Sprintf("multiple-value context is not available at a binary operator: %s", e.X))
     66 			return nil, nil, nil, false
     67 		}
     68 		stmts = append(stmts, ss...)
     69 		lhst := ts[0]
     70 
     71 		rhs, ts, ss, ok := cs.parseExpr(block, e.Y, markLocalVariableUsed)
     72 		if !ok {
     73 			return nil, nil, nil, false
     74 		}
     75 		if len(rhs) != 1 {
     76 			cs.addError(e.Pos(), fmt.Sprintf("multiple-value context is not available at a binary operator: %s", e.Y))
     77 			return nil, nil, nil, false
     78 		}
     79 		stmts = append(stmts, ss...)
     80 		rhst := ts[0]
     81 
     82 		if lhs[0].Type == shaderir.NumberExpr && rhs[0].Type == shaderir.NumberExpr {
     83 			op := e.Op
     84 			// https://golang.org/pkg/go/constant/#BinaryOp
     85 			// "To force integer division of Int operands, use op == token.QUO_ASSIGN instead of
     86 			// token.QUO; the result is guaranteed to be Int in this case."
     87 			if op == token.QUO && lhs[0].Const.Kind() == gconstant.Int && rhs[0].Const.Kind() == gconstant.Int {
     88 				op = token.QUO_ASSIGN
     89 			}
     90 			var v gconstant.Value
     91 			var t shaderir.Type
     92 			switch op {
     93 			case token.EQL, token.NEQ, token.LSS, token.LEQ, token.GTR, token.GEQ:
     94 				v = gconstant.MakeBool(gconstant.Compare(lhs[0].Const, op, rhs[0].Const))
     95 				t = shaderir.Type{Main: shaderir.Bool}
     96 			default:
     97 				v = gconstant.BinaryOp(lhs[0].Const, op, rhs[0].Const)
     98 				if v.Kind() == gconstant.Float {
     99 					t = shaderir.Type{Main: shaderir.Float}
    100 				} else {
    101 					t = shaderir.Type{Main: shaderir.Int}
    102 				}
    103 			}
    104 
    105 			return []shaderir.Expr{
    106 				{
    107 					Type:  shaderir.NumberExpr,
    108 					Const: v,
    109 				},
    110 			}, []shaderir.Type{t}, stmts, true
    111 		}
    112 
    113 		op, ok := shaderir.OpFromToken(e.Op)
    114 		if !ok {
    115 			cs.addError(e.Pos(), fmt.Sprintf("unexpected operator: %s", e.Op))
    116 			return nil, nil, nil, false
    117 		}
    118 
    119 		var t shaderir.Type
    120 		switch {
    121 		case op == shaderir.LessThanOp || op == shaderir.LessThanEqualOp || op == shaderir.GreaterThanOp || op == shaderir.GreaterThanEqualOp || op == shaderir.EqualOp || op == shaderir.NotEqualOp || op == shaderir.AndAnd || op == shaderir.OrOr:
    122 			t = shaderir.Type{Main: shaderir.Bool}
    123 		case lhs[0].Type == shaderir.NumberExpr && rhs[0].Type != shaderir.NumberExpr:
    124 			if rhst.Main == shaderir.Int {
    125 				if !canTruncateToInteger(lhs[0].Const) {
    126 					cs.addError(e.Pos(), fmt.Sprintf("constant %s truncated to integer", lhs[0].Const.String()))
    127 					return nil, nil, nil, false
    128 				}
    129 				lhs[0].ConstType = shaderir.ConstTypeInt
    130 			}
    131 			t = rhst
    132 		case lhs[0].Type != shaderir.NumberExpr && rhs[0].Type == shaderir.NumberExpr:
    133 			if lhst.Main == shaderir.Int {
    134 				if !canTruncateToInteger(rhs[0].Const) {
    135 					cs.addError(e.Pos(), fmt.Sprintf("constant %s truncated to integer", rhs[0].Const.String()))
    136 					return nil, nil, nil, false
    137 				}
    138 				rhs[0].ConstType = shaderir.ConstTypeInt
    139 			}
    140 			t = lhst
    141 		case lhst.Equal(&rhst):
    142 			t = lhst
    143 		case lhst.Main == shaderir.Float || lhst.Main == shaderir.Int:
    144 			switch rhst.Main {
    145 			case shaderir.Int:
    146 				t = lhst
    147 			case shaderir.Float, shaderir.Vec2, shaderir.Vec3, shaderir.Vec4, shaderir.Mat2, shaderir.Mat3, shaderir.Mat4:
    148 				t = rhst
    149 			default:
    150 				cs.addError(e.Pos(), fmt.Sprintf("types don't match: %s %s %s", lhst.String(), e.Op, rhst.String()))
    151 				return nil, nil, nil, false
    152 			}
    153 		case rhst.Main == shaderir.Float || rhst.Main == shaderir.Int:
    154 			switch lhst.Main {
    155 			case shaderir.Int:
    156 				t = rhst
    157 			case shaderir.Float, shaderir.Vec2, shaderir.Vec3, shaderir.Vec4, shaderir.Mat2, shaderir.Mat3, shaderir.Mat4:
    158 				t = lhst
    159 			default:
    160 				cs.addError(e.Pos(), fmt.Sprintf("types don't match: %s %s %s", lhst.String(), e.Op, rhst.String()))
    161 				return nil, nil, nil, false
    162 			}
    163 		case lhst.Main == shaderir.Vec2 && rhst.Main == shaderir.Mat2 ||
    164 			lhst.Main == shaderir.Mat2 && rhst.Main == shaderir.Vec2:
    165 			t = shaderir.Type{Main: shaderir.Vec2}
    166 		case lhst.Main == shaderir.Vec3 && rhst.Main == shaderir.Mat3 ||
    167 			lhst.Main == shaderir.Mat3 && rhst.Main == shaderir.Vec3:
    168 			t = shaderir.Type{Main: shaderir.Vec3}
    169 		case lhst.Main == shaderir.Vec4 && rhst.Main == shaderir.Mat4 ||
    170 			lhst.Main == shaderir.Mat4 && rhst.Main == shaderir.Vec4:
    171 			t = shaderir.Type{Main: shaderir.Vec4}
    172 		default:
    173 			cs.addError(e.Pos(), fmt.Sprintf("invalid expression: %s %s %s", lhst.String(), e.Op, rhst.String()))
    174 			return nil, nil, nil, false
    175 		}
    176 
    177 		return []shaderir.Expr{
    178 			{
    179 				Type:  shaderir.Binary,
    180 				Op:    op,
    181 				Exprs: []shaderir.Expr{lhs[0], rhs[0]},
    182 			},
    183 		}, []shaderir.Type{t}, stmts, true
    184 
    185 	case *ast.CallExpr:
    186 		var (
    187 			callee shaderir.Expr
    188 			args   []shaderir.Expr
    189 			argts  []shaderir.Type
    190 			stmts  []shaderir.Stmt
    191 		)
    192 
    193 		// Parse the argument first for the order of the statements.
    194 		for _, a := range e.Args {
    195 			es, ts, ss, ok := cs.parseExpr(block, a, markLocalVariableUsed)
    196 			if !ok {
    197 				return nil, nil, nil, false
    198 			}
    199 			if len(es) > 1 && len(e.Args) > 1 {
    200 				cs.addError(e.Pos(), fmt.Sprintf("single-value context and multiple-value context cannot be mixed: %s", e.Fun))
    201 				return nil, nil, nil, false
    202 			}
    203 			args = append(args, es...)
    204 			argts = append(argts, ts...)
    205 			stmts = append(stmts, ss...)
    206 		}
    207 
    208 		// TODO: When len(ss) is not 0?
    209 		es, _, ss, ok := cs.parseExpr(block, e.Fun, markLocalVariableUsed)
    210 		if !ok {
    211 			return nil, nil, nil, false
    212 		}
    213 		if len(es) != 1 {
    214 			cs.addError(e.Pos(), fmt.Sprintf("multiple-value context is not available at a callee: %s", e.Fun))
    215 			return nil, nil, nil, false
    216 		}
    217 		callee = es[0]
    218 		stmts = append(stmts, ss...)
    219 
    220 		// For built-in functions, we can call this in this position. Return an expression for the function
    221 		// call.
    222 		if callee.Type == shaderir.BuiltinFuncExpr {
    223 			if callee.BuiltinFunc == shaderir.Len || callee.BuiltinFunc == shaderir.Cap {
    224 				if len(args) != 1 {
    225 					cs.addError(e.Pos(), fmt.Sprintf("number of %s's arguments must be 1 but %d", callee.BuiltinFunc, len(args)))
    226 					return nil, nil, nil, false
    227 				}
    228 				if argts[0].Main != shaderir.Array {
    229 					cs.addError(e.Pos(), fmt.Sprintf("%s takes an array but %s", callee.BuiltinFunc, argts[0].String()))
    230 					return nil, nil, nil, false
    231 				}
    232 				return []shaderir.Expr{
    233 					{
    234 						Type:      shaderir.NumberExpr,
    235 						Const:     gconstant.MakeInt64(int64(argts[0].Length)),
    236 						ConstType: shaderir.ConstTypeInt,
    237 					},
    238 				}, []shaderir.Type{{Main: shaderir.Int}}, stmts, true
    239 			}
    240 
    241 			var t shaderir.Type
    242 			switch callee.BuiltinFunc {
    243 			case shaderir.BoolF:
    244 				t = shaderir.Type{Main: shaderir.Bool}
    245 			case shaderir.IntF:
    246 				t = shaderir.Type{Main: shaderir.Int}
    247 			case shaderir.FloatF:
    248 				t = shaderir.Type{Main: shaderir.Float}
    249 			case shaderir.Vec2F:
    250 				t = shaderir.Type{Main: shaderir.Vec2}
    251 			case shaderir.Vec3F:
    252 				t = shaderir.Type{Main: shaderir.Vec3}
    253 			case shaderir.Vec4F:
    254 				t = shaderir.Type{Main: shaderir.Vec4}
    255 			case shaderir.Mat2F:
    256 				t = shaderir.Type{Main: shaderir.Mat2}
    257 			case shaderir.Mat3F:
    258 				t = shaderir.Type{Main: shaderir.Mat3}
    259 			case shaderir.Mat4F:
    260 				t = shaderir.Type{Main: shaderir.Mat4}
    261 			case shaderir.Step:
    262 				t = argts[1]
    263 			case shaderir.Smoothstep:
    264 				t = argts[2]
    265 			case shaderir.Length, shaderir.Distance, shaderir.Dot:
    266 				t = shaderir.Type{Main: shaderir.Float}
    267 			case shaderir.Cross:
    268 				t = shaderir.Type{Main: shaderir.Vec3}
    269 			case shaderir.Texture2DF:
    270 				t = shaderir.Type{Main: shaderir.Vec4}
    271 			default:
    272 				t = argts[0]
    273 			}
    274 			return []shaderir.Expr{
    275 				{
    276 					Type:  shaderir.Call,
    277 					Exprs: append([]shaderir.Expr{callee}, args...),
    278 				},
    279 			}, []shaderir.Type{t}, stmts, true
    280 		}
    281 
    282 		if callee.Type != shaderir.FunctionExpr {
    283 			cs.addError(e.Pos(), fmt.Sprintf("function callee must be a funciton name but %s", e.Fun))
    284 			return nil, nil, nil, false
    285 		}
    286 
    287 		f := cs.funcs[callee.Index]
    288 
    289 		for i, p := range f.ir.InParams {
    290 			if args[i].Type == shaderir.NumberExpr && p.Main == shaderir.Int {
    291 				if !cs.forceToInt(e, &args[i]) {
    292 					return nil, nil, nil, false
    293 				}
    294 			}
    295 		}
    296 
    297 		var outParams []int
    298 		for _, p := range f.ir.OutParams {
    299 			idx := block.totalLocalVariableNum()
    300 			block.vars = append(block.vars, variable{
    301 				typ: p,
    302 			})
    303 			args = append(args, shaderir.Expr{
    304 				Type:  shaderir.LocalVariable,
    305 				Index: idx,
    306 			})
    307 			outParams = append(outParams, idx)
    308 		}
    309 
    310 		if t := f.ir.Return; t.Main != shaderir.None {
    311 			if len(outParams) != 0 {
    312 				cs.addError(e.Pos(), fmt.Sprintf("a function returning value cannot have out-params so far: %s", e.Fun))
    313 				return nil, nil, nil, false
    314 			}
    315 
    316 			idx := block.totalLocalVariableNum()
    317 			block.vars = append(block.vars, variable{
    318 				typ: t,
    319 			})
    320 
    321 			// Calling the function should be done eariler to treat out-params correctly.
    322 			stmts = append(stmts, shaderir.Stmt{
    323 				Type: shaderir.Assign,
    324 				Exprs: []shaderir.Expr{
    325 					{
    326 						Type:  shaderir.LocalVariable,
    327 						Index: idx,
    328 					},
    329 					{
    330 						Type:  shaderir.Call,
    331 						Exprs: append([]shaderir.Expr{callee}, args...),
    332 					},
    333 				},
    334 			})
    335 
    336 			// The actual expression here is just a local variable that includes the result of the
    337 			// function call.
    338 			return []shaderir.Expr{
    339 				{
    340 					Type:  shaderir.LocalVariable,
    341 					Index: idx,
    342 				},
    343 			}, []shaderir.Type{t}, stmts, true
    344 		}
    345 
    346 		// Even if the function doesn't return anything, calling the function should be done eariler to keep
    347 		// the evaluation order.
    348 		stmts = append(stmts, shaderir.Stmt{
    349 			Type: shaderir.ExprStmt,
    350 			Exprs: []shaderir.Expr{
    351 				{
    352 					Type:  shaderir.Call,
    353 					Exprs: append([]shaderir.Expr{callee}, args...),
    354 				},
    355 			},
    356 		})
    357 
    358 		if len(outParams) == 0 {
    359 			// TODO: Is this an error?
    360 		}
    361 
    362 		var exprs []shaderir.Expr
    363 		for _, p := range outParams {
    364 			exprs = append(exprs, shaderir.Expr{
    365 				Type:  shaderir.LocalVariable,
    366 				Index: p,
    367 			})
    368 		}
    369 		return exprs, f.ir.OutParams, stmts, true
    370 
    371 	case *ast.Ident:
    372 		if e.Name == "_" {
    373 			// In the context where a local variable is marked as used, any expressions must have its
    374 			// meaning. Then, a blank identifier is not available there.
    375 			if markLocalVariableUsed {
    376 				cs.addError(e.Pos(), fmt.Sprintf("cannot use _ as value"))
    377 				return nil, nil, nil, false
    378 			}
    379 			return []shaderir.Expr{
    380 				{
    381 					Type: shaderir.Blank,
    382 				},
    383 			}, []shaderir.Type{{}}, nil, true
    384 		}
    385 		if i, t, ok := block.findLocalVariable(e.Name, markLocalVariableUsed); ok {
    386 			return []shaderir.Expr{
    387 				{
    388 					Type:  shaderir.LocalVariable,
    389 					Index: i,
    390 				},
    391 			}, []shaderir.Type{t}, nil, true
    392 		}
    393 		if c, ok := block.findConstant(e.Name); ok {
    394 			return []shaderir.Expr{
    395 				{
    396 					Type:      shaderir.NumberExpr,
    397 					Const:     c.value,
    398 					ConstType: c.ctyp,
    399 				},
    400 			}, []shaderir.Type{c.typ}, nil, true
    401 		}
    402 		if i, ok := cs.findFunction(e.Name); ok {
    403 			return []shaderir.Expr{
    404 				{
    405 					Type:  shaderir.FunctionExpr,
    406 					Index: i,
    407 				},
    408 			}, nil, nil, true
    409 		}
    410 		if i, ok := cs.findUniformVariable(e.Name); ok {
    411 			return []shaderir.Expr{
    412 				{
    413 					Type:  shaderir.UniformVariable,
    414 					Index: i,
    415 				},
    416 			}, []shaderir.Type{cs.ir.Uniforms[i]}, nil, true
    417 		}
    418 		if f, ok := shaderir.ParseBuiltinFunc(e.Name); ok {
    419 			return []shaderir.Expr{
    420 				{
    421 					Type:        shaderir.BuiltinFuncExpr,
    422 					BuiltinFunc: f,
    423 				},
    424 			}, nil, nil, true
    425 		}
    426 		if m := textureVariableRe.FindStringSubmatch(e.Name); m != nil {
    427 			i, _ := strconv.Atoi(m[1])
    428 			return []shaderir.Expr{
    429 				{
    430 					Type:  shaderir.TextureVariable,
    431 					Index: i,
    432 				},
    433 			}, nil, nil, true
    434 		}
    435 		if e.Name == "true" || e.Name == "false" {
    436 			return []shaderir.Expr{
    437 				{
    438 					Type:  shaderir.NumberExpr,
    439 					Const: gconstant.MakeBool(e.Name == "true"),
    440 				},
    441 			}, []shaderir.Type{{Main: shaderir.Bool}}, nil, true
    442 		}
    443 		cs.addError(e.Pos(), fmt.Sprintf("unexpected identifier: %s", e.Name))
    444 
    445 	case *ast.ParenExpr:
    446 		return cs.parseExpr(block, e.X, markLocalVariableUsed)
    447 
    448 	case *ast.SelectorExpr:
    449 		exprs, _, stmts, ok := cs.parseExpr(block, e.X, true)
    450 		if !ok {
    451 			return nil, nil, nil, false
    452 		}
    453 		if len(exprs) != 1 {
    454 			cs.addError(e.Pos(), fmt.Sprintf("multiple-value context is not available at a selector: %s", e.X))
    455 			return nil, nil, nil, false
    456 		}
    457 		var t shaderir.Type
    458 		switch len(e.Sel.Name) {
    459 		case 1:
    460 			t.Main = shaderir.Float
    461 		case 2:
    462 			t.Main = shaderir.Vec2
    463 		case 3:
    464 			t.Main = shaderir.Vec3
    465 		case 4:
    466 			t.Main = shaderir.Vec4
    467 		default:
    468 			cs.addError(e.Pos(), fmt.Sprintf("unexpected swizzling: %s", e.Sel.Name))
    469 			return nil, nil, nil, false
    470 		}
    471 		return []shaderir.Expr{
    472 			{
    473 				Type: shaderir.FieldSelector,
    474 				Exprs: []shaderir.Expr{
    475 					exprs[0],
    476 					{
    477 						Type:      shaderir.SwizzlingExpr,
    478 						Swizzling: e.Sel.Name,
    479 					},
    480 				},
    481 			},
    482 		}, []shaderir.Type{t}, stmts, true
    483 
    484 	case *ast.UnaryExpr:
    485 		exprs, t, stmts, ok := cs.parseExpr(block, e.X, markLocalVariableUsed)
    486 		if !ok {
    487 			return nil, nil, nil, false
    488 		}
    489 		if len(exprs) != 1 {
    490 			cs.addError(e.Pos(), fmt.Sprintf("multiple-value context is not available at a unary operator: %s", e.X))
    491 			return nil, nil, nil, false
    492 		}
    493 
    494 		if exprs[0].Type == shaderir.NumberExpr {
    495 			v := gconstant.UnaryOp(e.Op, exprs[0].Const, 0)
    496 			t := shaderir.Type{Main: shaderir.Int}
    497 			if v.Kind() == gconstant.Float {
    498 				t = shaderir.Type{Main: shaderir.Float}
    499 			}
    500 			return []shaderir.Expr{
    501 				{
    502 					Type:  shaderir.NumberExpr,
    503 					Const: v,
    504 				},
    505 			}, []shaderir.Type{t}, stmts, true
    506 		}
    507 
    508 		var op shaderir.Op
    509 		switch e.Op {
    510 		case token.ADD:
    511 			op = shaderir.Add
    512 		case token.SUB:
    513 			op = shaderir.Sub
    514 		case token.NOT:
    515 			op = shaderir.NotOp
    516 		default:
    517 			cs.addError(e.Pos(), fmt.Sprintf("unexpected operator: %s", e.Op))
    518 			return nil, nil, nil, false
    519 		}
    520 		return []shaderir.Expr{
    521 			{
    522 				Type:  shaderir.Unary,
    523 				Op:    op,
    524 				Exprs: exprs,
    525 			},
    526 		}, t, stmts, true
    527 
    528 	case *ast.CompositeLit:
    529 		t, ok := cs.parseType(block, e.Type)
    530 		if !ok {
    531 			return nil, nil, nil, false
    532 		}
    533 		if t.Main == shaderir.Array && t.Length == -1 {
    534 			t.Length = len(e.Elts)
    535 		}
    536 
    537 		idx := block.totalLocalVariableNum()
    538 		block.vars = append(block.vars, variable{
    539 			typ: t,
    540 		})
    541 
    542 		var stmts []shaderir.Stmt
    543 		for i, e := range e.Elts {
    544 			exprs, _, ss, ok := cs.parseExpr(block, e, markLocalVariableUsed)
    545 			if !ok {
    546 				return nil, nil, nil, false
    547 			}
    548 			if len(exprs) != 1 {
    549 				cs.addError(e.Pos(), fmt.Sprintf("multiple-value context is not available at a composite literal"))
    550 				return nil, nil, nil, false
    551 			}
    552 			stmts = append(stmts, ss...)
    553 			stmts = append(stmts, shaderir.Stmt{
    554 				Type: shaderir.Assign,
    555 				Exprs: []shaderir.Expr{
    556 					{
    557 						Type: shaderir.Index,
    558 						Exprs: []shaderir.Expr{
    559 							{
    560 								Type:  shaderir.LocalVariable,
    561 								Index: idx,
    562 							},
    563 							{
    564 								Type:      shaderir.NumberExpr,
    565 								Const:     gconstant.MakeInt64(int64(i)),
    566 								ConstType: shaderir.ConstTypeInt,
    567 							},
    568 						},
    569 					},
    570 					exprs[0],
    571 				},
    572 			})
    573 		}
    574 
    575 		return []shaderir.Expr{
    576 			{
    577 				Type:  shaderir.LocalVariable,
    578 				Index: idx,
    579 			},
    580 		}, []shaderir.Type{t}, stmts, true
    581 
    582 	case *ast.IndexExpr:
    583 		var stmts []shaderir.Stmt
    584 
    585 		// Parse the index first
    586 		exprs, _, ss, ok := cs.parseExpr(block, e.Index, markLocalVariableUsed)
    587 		if !ok {
    588 			return nil, nil, nil, false
    589 		}
    590 		stmts = append(stmts, ss...)
    591 
    592 		if len(exprs) != 1 {
    593 			cs.addError(e.Pos(), fmt.Sprintf("multiple-value context is not available at an index expression"))
    594 			return nil, nil, nil, false
    595 		}
    596 		idx := exprs[0]
    597 		if idx.Type == shaderir.NumberExpr {
    598 			if !canTruncateToInteger(idx.Const) {
    599 				cs.addError(e.Pos(), fmt.Sprintf("constant %s truncated to integer", idx.Const.String()))
    600 				return nil, nil, nil, false
    601 			}
    602 			idx.ConstType = shaderir.ConstTypeInt
    603 		}
    604 
    605 		exprs, ts, ss, ok := cs.parseExpr(block, e.X, markLocalVariableUsed)
    606 		if !ok {
    607 			return nil, nil, nil, false
    608 		}
    609 		stmts = append(stmts, ss...)
    610 		if len(exprs) != 1 {
    611 			cs.addError(e.Pos(), fmt.Sprintf("multiple-value context is not available at an index expression"))
    612 			return nil, nil, nil, false
    613 		}
    614 		x := exprs[0]
    615 		t := ts[0]
    616 
    617 		var typ shaderir.Type
    618 		switch t.Main {
    619 		case shaderir.Vec2, shaderir.Vec3, shaderir.Vec4:
    620 			typ = shaderir.Type{Main: shaderir.Float}
    621 		case shaderir.Mat2:
    622 			typ = shaderir.Type{Main: shaderir.Vec2}
    623 		case shaderir.Mat3:
    624 			typ = shaderir.Type{Main: shaderir.Vec3}
    625 		case shaderir.Mat4:
    626 			typ = shaderir.Type{Main: shaderir.Vec4}
    627 		case shaderir.Array:
    628 			typ = t.Sub[0]
    629 		default:
    630 			cs.addError(e.Pos(), fmt.Sprintf("index operator cannot be applied to the type %s", t.String()))
    631 			return nil, nil, nil, false
    632 		}
    633 
    634 		return []shaderir.Expr{
    635 			{
    636 				Type: shaderir.Index,
    637 				Exprs: []shaderir.Expr{
    638 					x,
    639 					idx,
    640 				},
    641 			},
    642 		}, []shaderir.Type{typ}, stmts, true
    643 
    644 	default:
    645 		cs.addError(e.Pos(), fmt.Sprintf("expression not implemented: %#v", e))
    646 	}
    647 	return nil, nil, nil, false
    648 }