twitchapon-anim

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

expr.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 	"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 {
    224 				if len(args) != 1 {
    225 					cs.addError(e.Pos(), fmt.Sprintf("number of len's arguments must be 1 but %d", len(args)))
    226 					return nil, nil, nil, false
    227 				}
    228 				if argts[0].Main != shaderir.Array {
    229 					cs.addError(e.Pos(), fmt.Sprintf("len takes an array but %s", 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 i, ok := cs.findFunction(e.Name); ok {
    394 			return []shaderir.Expr{
    395 				{
    396 					Type:  shaderir.FunctionExpr,
    397 					Index: i,
    398 				},
    399 			}, nil, nil, true
    400 		}
    401 		if i, ok := cs.findUniformVariable(e.Name); ok {
    402 			return []shaderir.Expr{
    403 				{
    404 					Type:  shaderir.UniformVariable,
    405 					Index: i,
    406 				},
    407 			}, []shaderir.Type{cs.ir.Uniforms[i]}, nil, true
    408 		}
    409 		if f, ok := shaderir.ParseBuiltinFunc(e.Name); ok {
    410 			return []shaderir.Expr{
    411 				{
    412 					Type:        shaderir.BuiltinFuncExpr,
    413 					BuiltinFunc: f,
    414 				},
    415 			}, nil, nil, true
    416 		}
    417 		if m := textureVariableRe.FindStringSubmatch(e.Name); m != nil {
    418 			i, _ := strconv.Atoi(m[1])
    419 			return []shaderir.Expr{
    420 				{
    421 					Type:  shaderir.TextureVariable,
    422 					Index: i,
    423 				},
    424 			}, nil, nil, true
    425 		}
    426 		if e.Name == "true" || e.Name == "false" {
    427 			return []shaderir.Expr{
    428 				{
    429 					Type:  shaderir.NumberExpr,
    430 					Const: gconstant.MakeBool(e.Name == "true"),
    431 				},
    432 			}, []shaderir.Type{{Main: shaderir.Bool}}, nil, true
    433 		}
    434 		cs.addError(e.Pos(), fmt.Sprintf("unexpected identifier: %s", e.Name))
    435 
    436 	case *ast.ParenExpr:
    437 		return cs.parseExpr(block, e.X, markLocalVariableUsed)
    438 
    439 	case *ast.SelectorExpr:
    440 		exprs, _, stmts, ok := cs.parseExpr(block, e.X, true)
    441 		if !ok {
    442 			return nil, nil, nil, false
    443 		}
    444 		if len(exprs) != 1 {
    445 			cs.addError(e.Pos(), fmt.Sprintf("multiple-value context is not available at a selector: %s", e.X))
    446 			return nil, nil, nil, false
    447 		}
    448 		var t shaderir.Type
    449 		switch len(e.Sel.Name) {
    450 		case 1:
    451 			t.Main = shaderir.Float
    452 		case 2:
    453 			t.Main = shaderir.Vec2
    454 		case 3:
    455 			t.Main = shaderir.Vec3
    456 		case 4:
    457 			t.Main = shaderir.Vec4
    458 		default:
    459 			cs.addError(e.Pos(), fmt.Sprintf("unexpected swizzling: %s", e.Sel.Name))
    460 			return nil, nil, nil, false
    461 		}
    462 		return []shaderir.Expr{
    463 			{
    464 				Type: shaderir.FieldSelector,
    465 				Exprs: []shaderir.Expr{
    466 					exprs[0],
    467 					{
    468 						Type:      shaderir.SwizzlingExpr,
    469 						Swizzling: e.Sel.Name,
    470 					},
    471 				},
    472 			},
    473 		}, []shaderir.Type{t}, stmts, true
    474 
    475 	case *ast.UnaryExpr:
    476 		exprs, t, stmts, ok := cs.parseExpr(block, e.X, markLocalVariableUsed)
    477 		if !ok {
    478 			return nil, nil, nil, false
    479 		}
    480 		if len(exprs) != 1 {
    481 			cs.addError(e.Pos(), fmt.Sprintf("multiple-value context is not available at a unary operator: %s", e.X))
    482 			return nil, nil, nil, false
    483 		}
    484 
    485 		if exprs[0].Type == shaderir.NumberExpr {
    486 			v := gconstant.UnaryOp(e.Op, exprs[0].Const, 0)
    487 			t := shaderir.Type{Main: shaderir.Int}
    488 			if v.Kind() == gconstant.Float {
    489 				t = shaderir.Type{Main: shaderir.Float}
    490 			}
    491 			return []shaderir.Expr{
    492 				{
    493 					Type:  shaderir.NumberExpr,
    494 					Const: v,
    495 				},
    496 			}, []shaderir.Type{t}, stmts, true
    497 		}
    498 
    499 		var op shaderir.Op
    500 		switch e.Op {
    501 		case token.ADD:
    502 			op = shaderir.Add
    503 		case token.SUB:
    504 			op = shaderir.Sub
    505 		case token.NOT:
    506 			op = shaderir.NotOp
    507 		default:
    508 			cs.addError(e.Pos(), fmt.Sprintf("unexpected operator: %s", e.Op))
    509 			return nil, nil, nil, false
    510 		}
    511 		return []shaderir.Expr{
    512 			{
    513 				Type:  shaderir.Unary,
    514 				Op:    op,
    515 				Exprs: exprs,
    516 			},
    517 		}, t, stmts, true
    518 
    519 	case *ast.CompositeLit:
    520 		t, ok := cs.parseType(block, e.Type)
    521 		if !ok {
    522 			return nil, nil, nil, false
    523 		}
    524 		if t.Main == shaderir.Array && t.Length == -1 {
    525 			t.Length = len(e.Elts)
    526 		}
    527 
    528 		idx := block.totalLocalVariableNum()
    529 		block.vars = append(block.vars, variable{
    530 			typ: t,
    531 		})
    532 
    533 		var stmts []shaderir.Stmt
    534 		for i, e := range e.Elts {
    535 			exprs, _, ss, ok := cs.parseExpr(block, e, markLocalVariableUsed)
    536 			if !ok {
    537 				return nil, nil, nil, false
    538 			}
    539 			if len(exprs) != 1 {
    540 				cs.addError(e.Pos(), fmt.Sprintf("multiple-value context is not available at a composite literal"))
    541 				return nil, nil, nil, false
    542 			}
    543 			stmts = append(stmts, ss...)
    544 			stmts = append(stmts, shaderir.Stmt{
    545 				Type: shaderir.Assign,
    546 				Exprs: []shaderir.Expr{
    547 					{
    548 						Type: shaderir.Index,
    549 						Exprs: []shaderir.Expr{
    550 							{
    551 								Type:  shaderir.LocalVariable,
    552 								Index: idx,
    553 							},
    554 							{
    555 								Type:      shaderir.NumberExpr,
    556 								Const:     gconstant.MakeInt64(int64(i)),
    557 								ConstType: shaderir.ConstTypeInt,
    558 							},
    559 						},
    560 					},
    561 					exprs[0],
    562 				},
    563 			})
    564 		}
    565 
    566 		return []shaderir.Expr{
    567 			{
    568 				Type:  shaderir.LocalVariable,
    569 				Index: idx,
    570 			},
    571 		}, []shaderir.Type{t}, stmts, true
    572 
    573 	case *ast.IndexExpr:
    574 		var stmts []shaderir.Stmt
    575 
    576 		// Parse the index first
    577 		exprs, _, ss, ok := cs.parseExpr(block, e.Index, markLocalVariableUsed)
    578 		if !ok {
    579 			return nil, nil, nil, false
    580 		}
    581 		stmts = append(stmts, ss...)
    582 
    583 		if len(exprs) != 1 {
    584 			cs.addError(e.Pos(), fmt.Sprintf("multiple-value context is not available at an index expression"))
    585 			return nil, nil, nil, false
    586 		}
    587 		idx := exprs[0]
    588 		if idx.Type == shaderir.NumberExpr {
    589 			if !canTruncateToInteger(idx.Const) {
    590 				cs.addError(e.Pos(), fmt.Sprintf("constant %s truncated to integer", idx.Const.String()))
    591 				return nil, nil, nil, false
    592 			}
    593 			idx.ConstType = shaderir.ConstTypeInt
    594 		}
    595 
    596 		exprs, ts, ss, ok := cs.parseExpr(block, e.X, markLocalVariableUsed)
    597 		if !ok {
    598 			return nil, nil, nil, false
    599 		}
    600 		stmts = append(stmts, ss...)
    601 		if len(exprs) != 1 {
    602 			cs.addError(e.Pos(), fmt.Sprintf("multiple-value context is not available at an index expression"))
    603 			return nil, nil, nil, false
    604 		}
    605 		x := exprs[0]
    606 		t := ts[0]
    607 
    608 		var typ shaderir.Type
    609 		switch t.Main {
    610 		case shaderir.Vec2, shaderir.Vec3, shaderir.Vec4:
    611 			typ = shaderir.Type{Main: shaderir.Float}
    612 		case shaderir.Mat2:
    613 			typ = shaderir.Type{Main: shaderir.Vec2}
    614 		case shaderir.Mat3:
    615 			typ = shaderir.Type{Main: shaderir.Vec3}
    616 		case shaderir.Mat4:
    617 			typ = shaderir.Type{Main: shaderir.Vec4}
    618 		case shaderir.Array:
    619 			typ = t.Sub[0]
    620 		default:
    621 			cs.addError(e.Pos(), fmt.Sprintf("index operator cannot be applied to the type %s", t.String()))
    622 			return nil, nil, nil, false
    623 		}
    624 
    625 		return []shaderir.Expr{
    626 			{
    627 				Type: shaderir.Index,
    628 				Exprs: []shaderir.Expr{
    629 					x,
    630 					idx,
    631 				},
    632 			},
    633 		}, []shaderir.Type{typ}, stmts, true
    634 
    635 	default:
    636 		cs.addError(e.Pos(), fmt.Sprintf("expression not implemented: %#v", e))
    637 	}
    638 	return nil, nil, nil, false
    639 }