2020-07-04 16:47:23 +02:00
// Copyright 2020 The Ebiten Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package shader
import (
"fmt"
"go/ast"
gconstant "go/constant"
"go/token"
2020-07-05 20:36:15 +02:00
"regexp"
"strconv"
2022-11-19 14:37:31 +01:00
"strings"
2020-07-04 16:47:23 +02:00
2020-10-03 19:35:13 +02:00
"github.com/hajimehoshi/ebiten/v2/internal/shaderir"
2020-07-04 16:47:23 +02:00
)
2020-08-12 06:05:37 +02:00
func canTruncateToInteger ( v gconstant . Value ) bool {
return gconstant . ToInt ( v ) . Kind ( ) != gconstant . Unknown
}
2022-08-19 08:26:26 +02:00
func canTruncateToFloat ( v gconstant . Value ) bool {
return gconstant . ToFloat ( v ) . Kind ( ) != gconstant . Unknown
}
2022-03-09 17:54:11 +01:00
func isUntypedInteger ( expr * shaderir . Expr ) bool {
return expr . Const . Kind ( ) == gconstant . Int && expr . ConstType == shaderir . ConstTypeNone
}
2022-08-19 08:26:26 +02:00
func isModAvailableForConsts ( lhs , rhs * shaderir . Expr ) bool {
2022-03-09 17:54:11 +01:00
// % is available only when
2022-03-09 18:52:44 +01:00
// 1) both are untyped integers
2022-03-09 17:54:11 +01:00
// 2) either is an typed integer and the other is truncatable to an integer
if isUntypedInteger ( lhs ) && isUntypedInteger ( rhs ) {
return true
}
if lhs . ConstType == shaderir . ConstTypeInt && canTruncateToInteger ( rhs . Const ) {
return true
}
if rhs . ConstType == shaderir . ConstTypeInt && canTruncateToInteger ( lhs . Const ) {
return true
}
return false
}
2022-11-20 07:36:07 +01:00
func isValidForModOp ( lhs , rhs * shaderir . Expr , lhst , rhst shaderir . Type ) bool {
isInt := func ( s * shaderir . Expr , t shaderir . Type ) bool {
if t . Main == shaderir . Int {
return true
}
if s . Const == nil {
return false
}
if s . ConstType == shaderir . ConstTypeInt {
return true
}
if canTruncateToInteger ( s . Const ) {
return true
}
return false
}
if isInt ( lhs , lhst ) {
return isInt ( rhs , rhst )
}
if lhst . Main == shaderir . IVec2 || lhst . Main == shaderir . IVec3 || lhst . Main == shaderir . IVec4 {
return lhst . Equal ( & rhst ) || isInt ( rhs , rhst )
}
return false
}
2022-08-19 10:34:03 +02:00
func canApplyBinaryOp ( lhs , rhs * shaderir . Expr , lhst , rhst shaderir . Type , op shaderir . Op ) bool {
if op == shaderir . AndAnd || op == shaderir . OrOr {
return lhst . Main == shaderir . Bool && rhst . Main == shaderir . Bool
}
2022-08-19 08:26:26 +02:00
switch {
case lhs . Const != nil && rhs . Const != nil :
switch {
case lhs . ConstType == shaderir . ConstTypeNone && rhs . ConstType == shaderir . ConstTypeNone :
if canTruncateToFloat ( lhs . Const ) && canTruncateToFloat ( rhs . Const ) {
return true
}
if canTruncateToInteger ( lhs . Const ) && canTruncateToInteger ( rhs . Const ) {
return true
}
return lhs . Const . Kind ( ) == rhs . Const . Kind ( )
case lhs . ConstType == shaderir . ConstTypeNone :
switch rhs . ConstType {
case shaderir . ConstTypeFloat :
return canTruncateToFloat ( lhs . Const )
case shaderir . ConstTypeInt :
return canTruncateToInteger ( lhs . Const )
}
case rhs . ConstType == shaderir . ConstTypeNone :
switch lhs . ConstType {
case shaderir . ConstTypeInt :
return canTruncateToInteger ( rhs . Const )
case shaderir . ConstTypeFloat :
return canTruncateToFloat ( rhs . Const )
}
}
return lhs . ConstType == rhs . ConstType
case lhs . Const != nil :
switch lhs . ConstType {
case shaderir . ConstTypeNone :
if rhst . Main == shaderir . Float {
return canTruncateToFloat ( lhs . Const )
}
if rhst . Main == shaderir . Int {
return canTruncateToInteger ( lhs . Const )
}
case shaderir . ConstTypeFloat :
return rhst . Main == shaderir . Float
case shaderir . ConstTypeInt :
return rhst . Main == shaderir . Int
case shaderir . ConstTypeBool :
return rhst . Main == shaderir . Bool
}
case rhs . Const != nil :
switch rhs . ConstType {
case shaderir . ConstTypeNone :
if lhst . Main == shaderir . Float {
return canTruncateToFloat ( rhs . Const )
}
if lhst . Main == shaderir . Int {
return canTruncateToInteger ( rhs . Const )
}
case shaderir . ConstTypeFloat :
return lhst . Main == shaderir . Float
case shaderir . ConstTypeInt :
return lhst . Main == shaderir . Int
case shaderir . ConstTypeBool :
return lhst . Main == shaderir . Bool
}
}
// Comparing matrices are forbidden (#2187).
if lhst . IsMatrix ( ) || rhst . IsMatrix ( ) {
return false
}
return lhst . Equal ( & rhst )
}
2022-01-11 15:33:40 +01:00
func goConstantKindString ( k gconstant . Kind ) string {
switch k {
case gconstant . Bool :
return "bool"
case gconstant . String :
return "string"
case gconstant . Int :
return "int"
case gconstant . Float :
return "float"
case gconstant . Complex :
return "complex"
}
return "unknown"
}
2020-07-18 12:17:19 +02:00
var textureVariableRe = regexp . MustCompile ( ` \A__t(\d+)\z ` )
2020-07-05 20:36:15 +02:00
2022-08-17 16:13:21 +02:00
func ( cs * compileState ) parseExpr ( block * block , fname string , expr ast . Expr , markLocalVariableUsed bool ) ( [ ] shaderir . Expr , [ ] shaderir . Type , [ ] shaderir . Stmt , bool ) {
2020-07-04 16:47:23 +02:00
switch e := expr . ( type ) {
case * ast . BasicLit :
switch e . Kind {
case token . INT :
2022-11-20 09:18:24 +01:00
// The type is not determined yet.
2020-07-04 16:47:23 +02:00
return [ ] shaderir . Expr {
{
Type : shaderir . NumberExpr ,
Const : gconstant . MakeFromLiteral ( e . Value , e . Kind , 0 ) ,
} ,
2022-11-20 09:18:24 +01:00
} , [ ] shaderir . Type { { } } , nil , true
2020-07-04 16:47:23 +02:00
case token . FLOAT :
2022-11-20 09:18:24 +01:00
// The type is not determined yet.
2020-07-04 16:47:23 +02:00
return [ ] shaderir . Expr {
{
Type : shaderir . NumberExpr ,
Const : gconstant . MakeFromLiteral ( e . Value , e . Kind , 0 ) ,
} ,
2022-11-20 09:18:24 +01:00
} , [ ] shaderir . Type { { } } , nil , true
2020-07-04 16:47:23 +02:00
default :
cs . addError ( e . Pos ( ) , fmt . Sprintf ( "literal not implemented: %#v" , e ) )
}
2020-08-12 06:33:20 +02:00
2020-07-04 16:47:23 +02:00
case * ast . BinaryExpr :
var stmts [ ] shaderir . Stmt
// Prase LHS first for the order of the statements.
2022-08-17 16:13:21 +02:00
lhs , ts , ss , ok := cs . parseExpr ( block , fname , e . X , markLocalVariableUsed )
2020-07-04 16:47:23 +02:00
if ! ok {
return nil , nil , nil , false
}
if len ( lhs ) != 1 {
cs . addError ( e . Pos ( ) , fmt . Sprintf ( "multiple-value context is not available at a binary operator: %s" , e . X ) )
return nil , nil , nil , false
}
stmts = append ( stmts , ss ... )
lhst := ts [ 0 ]
2022-08-17 16:13:21 +02:00
rhs , ts , ss , ok := cs . parseExpr ( block , fname , e . Y , markLocalVariableUsed )
2020-07-04 16:47:23 +02:00
if ! ok {
return nil , nil , nil , false
}
if len ( rhs ) != 1 {
cs . addError ( e . Pos ( ) , fmt . Sprintf ( "multiple-value context is not available at a binary operator: %s" , e . Y ) )
return nil , nil , nil , false
}
stmts = append ( stmts , ss ... )
rhst := ts [ 0 ]
2022-08-19 08:24:00 +02:00
if lhs [ 0 ] . Const != nil && rhs [ 0 ] . Const != nil {
2020-07-04 16:47:23 +02:00
op := e . Op
2022-11-20 16:49:34 +01:00
// https://pkg.go.dev/go/constant/#BinaryOp
2020-07-04 16:47:23 +02:00
// "To force integer division of Int operands, use op == token.QUO_ASSIGN instead of
// token.QUO; the result is guaranteed to be Int in this case."
if op == token . QUO && lhs [ 0 ] . Const . Kind ( ) == gconstant . Int && rhs [ 0 ] . Const . Kind ( ) == gconstant . Int {
op = token . QUO_ASSIGN
}
2020-07-05 10:33:03 +02:00
var v gconstant . Value
var t shaderir . Type
switch op {
2022-08-19 10:34:03 +02:00
case token . EQL , token . NEQ , token . LSS , token . LEQ , token . GTR , token . GEQ , token . LAND , token . LOR :
op2 , ok := shaderir . OpFromToken ( op , lhst , rhst )
if ! ok {
cs . addError ( e . Pos ( ) , fmt . Sprintf ( "unexpected operator: %s" , op ) )
return nil , nil , nil , false
}
if ! canApplyBinaryOp ( & lhs [ 0 ] , & rhs [ 0 ] , lhst , rhst , op2 ) {
2022-08-19 08:26:26 +02:00
cs . addError ( e . Pos ( ) , fmt . Sprintf ( "types don't match: %s %s %s" , lhst . String ( ) , op , rhst . String ( ) ) )
return nil , nil , nil , false
}
2022-08-19 10:34:03 +02:00
switch op {
case token . LAND , token . LOR :
b := gconstant . BoolVal ( gconstant . BinaryOp ( lhs [ 0 ] . Const , op , rhs [ 0 ] . Const ) )
v = gconstant . MakeBool ( b )
default :
v = gconstant . MakeBool ( gconstant . Compare ( lhs [ 0 ] . Const , op , rhs [ 0 ] . Const ) )
}
2020-07-05 10:33:03 +02:00
t = shaderir . Type { Main : shaderir . Bool }
2022-11-20 16:49:34 +01:00
case token . REM :
if ! isModAvailableForConsts ( & lhs [ 0 ] , & rhs [ 0 ] ) {
var wrongTypeName string
if lhs [ 0 ] . Const . Kind ( ) != gconstant . Int {
wrongTypeName = goConstantKindString ( lhs [ 0 ] . Const . Kind ( ) )
} else {
wrongTypeName = goConstantKindString ( rhs [ 0 ] . Const . Kind ( ) )
2022-03-09 17:54:11 +01:00
}
2022-11-20 16:49:34 +01:00
cs . addError ( e . Pos ( ) , fmt . Sprintf ( "invalid operation: operator %% not defined on untyped %s" , wrongTypeName ) )
return nil , nil , nil , false
}
if ! cs . forceToInt ( e , & lhs [ 0 ] ) {
return nil , nil , nil , false
2022-01-11 15:33:40 +01:00
}
2022-11-20 16:49:34 +01:00
if ! cs . forceToInt ( e , & rhs [ 0 ] ) {
return nil , nil , nil , false
}
fallthrough
default :
2020-07-05 10:33:03 +02:00
v = gconstant . BinaryOp ( lhs [ 0 ] . Const , op , rhs [ 0 ] . Const )
if v . Kind ( ) == gconstant . Float {
t = shaderir . Type { Main : shaderir . Float }
} else {
t = shaderir . Type { Main : shaderir . Int }
}
2020-07-04 16:47:23 +02:00
}
2020-07-05 10:33:03 +02:00
2020-07-04 16:47:23 +02:00
return [ ] shaderir . Expr {
{
Type : shaderir . NumberExpr ,
Const : v ,
} ,
} , [ ] shaderir . Type { t } , stmts , true
}
2022-03-13 11:17:44 +01:00
op , ok := shaderir . OpFromToken ( e . Op , lhst , rhst )
2020-07-04 20:29:34 +02:00
if ! ok {
cs . addError ( e . Pos ( ) , fmt . Sprintf ( "unexpected operator: %s" , e . Op ) )
return nil , nil , nil , false
}
2020-07-04 16:47:23 +02:00
var t shaderir . Type
switch {
2022-07-08 19:26:33 +02:00
case op == shaderir . LessThanOp || op == shaderir . LessThanEqualOp || op == shaderir . GreaterThanOp || op == shaderir . GreaterThanEqualOp || op == shaderir . EqualOp || op == shaderir . NotEqualOp || op == shaderir . VectorEqualOp || op == shaderir . VectorNotEqualOp || op == shaderir . AndAnd || op == shaderir . OrOr :
2022-08-19 10:34:03 +02:00
if ! canApplyBinaryOp ( & lhs [ 0 ] , & rhs [ 0 ] , lhst , rhst , op ) {
2022-08-19 08:26:26 +02:00
cs . addError ( e . Pos ( ) , fmt . Sprintf ( "types don't match: %s %s %s" , lhst . String ( ) , e . Op , rhst . String ( ) ) )
return nil , nil , nil , false
}
2020-07-04 20:29:34 +02:00
t = shaderir . Type { Main : shaderir . Bool }
2022-08-19 08:24:00 +02:00
case lhs [ 0 ] . Const != nil && rhs [ 0 ] . Const == nil :
2022-01-20 18:39:28 +01:00
switch rhst . Main {
case shaderir . Mat2 , shaderir . Mat3 , shaderir . Mat4 :
2022-03-13 11:17:44 +01:00
if op != shaderir . MatrixMul {
2022-01-20 18:39:28 +01:00
cs . addError ( e . Pos ( ) , fmt . Sprintf ( "types don't match: %s %s %s" , lhst . String ( ) , e . Op , rhst . String ( ) ) )
return nil , nil , nil , false
}
2022-03-09 17:54:11 +01:00
fallthrough
case shaderir . Vec2 , shaderir . Vec3 , shaderir . Vec4 :
if lhs [ 0 ] . ConstType == shaderir . ConstTypeInt {
cs . addError ( e . Pos ( ) , fmt . Sprintf ( "types don't match: %s %s %s" , lhst . String ( ) , e . Op , rhst . String ( ) ) )
return nil , nil , nil , false
}
2022-11-20 07:36:07 +01:00
case shaderir . IVec2 , shaderir . IVec3 , shaderir . IVec4 :
if ! canTruncateToInteger ( lhs [ 0 ] . Const ) {
cs . addError ( e . Pos ( ) , fmt . Sprintf ( "types don't match: %s %s %s" , lhst . String ( ) , e . Op , rhst . String ( ) ) )
return nil , nil , nil , false
}
2022-03-09 17:54:11 +01:00
case shaderir . Int :
2020-08-12 06:33:20 +02:00
if ! canTruncateToInteger ( lhs [ 0 ] . Const ) {
cs . addError ( e . Pos ( ) , fmt . Sprintf ( "constant %s truncated to integer" , lhs [ 0 ] . Const . String ( ) ) )
return nil , nil , nil , false
}
lhs [ 0 ] . ConstType = shaderir . ConstTypeInt
}
t = rhst
2022-08-19 08:24:00 +02:00
case lhs [ 0 ] . Const == nil && rhs [ 0 ] . Const != nil :
2022-01-20 18:39:28 +01:00
switch lhst . Main {
case shaderir . Mat2 , shaderir . Mat3 , shaderir . Mat4 :
2022-03-13 11:17:44 +01:00
if op != shaderir . MatrixMul && op != shaderir . Div {
2022-01-20 18:39:28 +01:00
cs . addError ( e . Pos ( ) , fmt . Sprintf ( "types don't match: %s %s %s" , lhst . String ( ) , e . Op , rhst . String ( ) ) )
return nil , nil , nil , false
}
2022-03-09 17:54:11 +01:00
fallthrough
case shaderir . Vec2 , shaderir . Vec3 , shaderir . Vec4 :
if rhs [ 0 ] . ConstType == shaderir . ConstTypeInt {
cs . addError ( e . Pos ( ) , fmt . Sprintf ( "types don't match: %s %s %s" , lhst . String ( ) , e . Op , rhst . String ( ) ) )
return nil , nil , nil , false
}
2022-11-20 07:36:07 +01:00
case shaderir . IVec2 , shaderir . IVec3 , shaderir . IVec4 :
if ! canTruncateToInteger ( rhs [ 0 ] . Const ) {
cs . addError ( e . Pos ( ) , fmt . Sprintf ( "types don't match: %s %s %s" , lhst . String ( ) , e . Op , rhst . String ( ) ) )
return nil , nil , nil , false
}
2022-11-20 16:49:34 +01:00
if ! cs . forceToInt ( e , & rhs [ 0 ] ) {
return nil , nil , nil , false
}
2022-03-09 17:54:11 +01:00
case shaderir . Int :
2020-08-12 06:33:20 +02:00
if ! canTruncateToInteger ( rhs [ 0 ] . Const ) {
cs . addError ( e . Pos ( ) , fmt . Sprintf ( "constant %s truncated to integer" , rhs [ 0 ] . Const . String ( ) ) )
return nil , nil , nil , false
}
rhs [ 0 ] . ConstType = shaderir . ConstTypeInt
}
t = lhst
2020-07-04 16:47:23 +02:00
case lhst . Equal ( & rhst ) :
2022-01-20 19:37:31 +01:00
if op == shaderir . Div && ( rhst . Main == shaderir . Mat2 || rhst . Main == shaderir . Mat3 || rhst . Main == shaderir . Mat4 ) {
cs . addError ( e . Pos ( ) , fmt . Sprintf ( "invalid operation: operator %s not defined on %s" , e . Op , rhst . String ( ) ) )
return nil , nil , nil , false
}
2020-07-04 16:47:23 +02:00
t = lhst
2022-01-20 16:50:21 +01:00
case lhst . Main == shaderir . Float :
2020-07-04 16:47:23 +02:00
switch rhst . Main {
2022-01-20 18:39:28 +01:00
case shaderir . Float , shaderir . Vec2 , shaderir . Vec3 , shaderir . Vec4 :
2020-07-04 16:47:23 +02:00
t = rhst
2022-01-20 18:39:28 +01:00
case shaderir . Mat2 , shaderir . Mat3 , shaderir . Mat4 :
2022-03-13 11:17:44 +01:00
if op != shaderir . MatrixMul {
2022-01-20 18:39:28 +01:00
cs . addError ( e . Pos ( ) , fmt . Sprintf ( "types don't match: %s %s %s" , lhst . String ( ) , e . Op , rhst . String ( ) ) )
return nil , nil , nil , false
}
2022-03-26 17:22:50 +01:00
t = rhst
2020-07-04 16:47:23 +02:00
default :
cs . addError ( e . Pos ( ) , fmt . Sprintf ( "types don't match: %s %s %s" , lhst . String ( ) , e . Op , rhst . String ( ) ) )
return nil , nil , nil , false
}
2022-01-20 16:50:21 +01:00
case rhst . Main == shaderir . Float :
2020-07-04 16:47:23 +02:00
switch lhst . Main {
2022-01-20 18:39:28 +01:00
case shaderir . Float , shaderir . Vec2 , shaderir . Vec3 , shaderir . Vec4 :
t = lhst
case shaderir . Mat2 , shaderir . Mat3 , shaderir . Mat4 :
2022-03-13 11:17:44 +01:00
if op != shaderir . MatrixMul && op != shaderir . Div {
2022-01-20 18:39:28 +01:00
cs . addError ( e . Pos ( ) , fmt . Sprintf ( "types don't match: %s %s %s" , lhst . String ( ) , e . Op , rhst . String ( ) ) )
return nil , nil , nil , false
}
2020-07-04 16:47:23 +02:00
t = lhst
default :
cs . addError ( e . Pos ( ) , fmt . Sprintf ( "types don't match: %s %s %s" , lhst . String ( ) , e . Op , rhst . String ( ) ) )
return nil , nil , nil , false
}
2022-03-13 11:17:44 +01:00
case op == shaderir . MatrixMul && ( lhst . Main == shaderir . Vec2 && rhst . Main == shaderir . Mat2 ||
2022-01-20 16:50:21 +01:00
lhst . Main == shaderir . Mat2 && rhst . Main == shaderir . Vec2 ) :
2020-07-04 16:47:23 +02:00
t = shaderir . Type { Main : shaderir . Vec2 }
2022-03-13 11:17:44 +01:00
case op == shaderir . MatrixMul && ( lhst . Main == shaderir . Vec3 && rhst . Main == shaderir . Mat3 ||
2022-01-20 16:50:21 +01:00
lhst . Main == shaderir . Mat3 && rhst . Main == shaderir . Vec3 ) :
2020-07-04 16:47:23 +02:00
t = shaderir . Type { Main : shaderir . Vec3 }
2022-03-13 11:17:44 +01:00
case op == shaderir . MatrixMul && ( lhst . Main == shaderir . Vec4 && rhst . Main == shaderir . Mat4 ||
2022-01-20 16:50:21 +01:00
lhst . Main == shaderir . Mat4 && rhst . Main == shaderir . Vec4 ) :
2020-07-04 16:47:23 +02:00
t = shaderir . Type { Main : shaderir . Vec4 }
default :
cs . addError ( e . Pos ( ) , fmt . Sprintf ( "invalid expression: %s %s %s" , lhst . String ( ) , e . Op , rhst . String ( ) ) )
return nil , nil , nil , false
}
2022-01-13 19:50:30 +01:00
// For `%`, both types must be deducible to integers.
2022-01-11 15:33:40 +01:00
if op == shaderir . ModOp {
2022-11-20 07:36:07 +01:00
if ! isValidForModOp ( & lhs [ 0 ] , & rhs [ 0 ] , lhst , rhst ) {
2022-01-11 15:33:40 +01:00
var wrongType shaderir . Type
if lhst . Main != shaderir . Int {
wrongType = lhst
} else {
wrongType = rhst
}
cs . addError ( e . Pos ( ) , fmt . Sprintf ( "invalid operation: operator %% not defined on %s" , wrongType . String ( ) ) )
return nil , nil , nil , false
}
}
2020-07-04 16:47:23 +02:00
return [ ] shaderir . Expr {
{
Type : shaderir . Binary ,
Op : op ,
Exprs : [ ] shaderir . Expr { lhs [ 0 ] , rhs [ 0 ] } ,
} ,
} , [ ] shaderir . Type { t } , stmts , true
2020-08-12 06:33:20 +02:00
2020-07-04 16:47:23 +02:00
case * ast . CallExpr :
var (
callee shaderir . Expr
args [ ] shaderir . Expr
argts [ ] shaderir . Type
stmts [ ] shaderir . Stmt
)
// Parse the argument first for the order of the statements.
for _ , a := range e . Args {
2022-08-17 16:13:21 +02:00
es , ts , ss , ok := cs . parseExpr ( block , fname , a , markLocalVariableUsed )
2020-07-04 16:47:23 +02:00
if ! ok {
return nil , nil , nil , false
}
if len ( es ) > 1 && len ( e . Args ) > 1 {
cs . addError ( e . Pos ( ) , fmt . Sprintf ( "single-value context and multiple-value context cannot be mixed: %s" , e . Fun ) )
return nil , nil , nil , false
}
args = append ( args , es ... )
argts = append ( argts , ts ... )
stmts = append ( stmts , ss ... )
}
// TODO: When len(ss) is not 0?
2022-08-17 16:13:21 +02:00
es , _ , ss , ok := cs . parseExpr ( block , fname , e . Fun , markLocalVariableUsed )
2020-07-04 16:47:23 +02:00
if ! ok {
return nil , nil , nil , false
}
if len ( es ) != 1 {
cs . addError ( e . Pos ( ) , fmt . Sprintf ( "multiple-value context is not available at a callee: %s" , e . Fun ) )
return nil , nil , nil , false
}
callee = es [ 0 ]
stmts = append ( stmts , ss ... )
// For built-in functions, we can call this in this position. Return an expression for the function
// call.
if callee . Type == shaderir . BuiltinFuncExpr {
2022-03-09 16:46:11 +01:00
// Process compile-time evaluations.
switch callee . BuiltinFunc {
case shaderir . Len , shaderir . Cap :
2020-08-15 19:07:36 +02:00
if len ( args ) != 1 {
2020-10-17 16:36:07 +02:00
cs . addError ( e . Pos ( ) , fmt . Sprintf ( "number of %s's arguments must be 1 but %d" , callee . BuiltinFunc , len ( args ) ) )
2020-08-15 19:07:36 +02:00
return nil , nil , nil , false
}
if argts [ 0 ] . Main != shaderir . Array {
2020-10-17 16:36:07 +02:00
cs . addError ( e . Pos ( ) , fmt . Sprintf ( "%s takes an array but %s" , callee . BuiltinFunc , argts [ 0 ] . String ( ) ) )
2020-08-15 19:07:36 +02:00
return nil , nil , nil , false
}
return [ ] shaderir . Expr {
{
Type : shaderir . NumberExpr ,
Const : gconstant . MakeInt64 ( int64 ( argts [ 0 ] . Length ) ) ,
ConstType : shaderir . ConstTypeInt ,
} ,
} , [ ] shaderir . Type { { Main : shaderir . Int } } , stmts , true
2022-11-20 09:18:24 +01:00
case shaderir . BoolF :
if len ( args ) == 1 && args [ 0 ] . Const != nil {
if args [ 0 ] . Const . Kind ( ) != gconstant . Bool {
cs . addError ( e . Pos ( ) , fmt . Sprintf ( "cannot convert %s to type bool" , args [ 0 ] . Const . String ( ) ) )
return nil , nil , nil , false
}
return [ ] shaderir . Expr {
{
Type : shaderir . NumberExpr ,
Const : args [ 0 ] . Const ,
ConstType : shaderir . ConstTypeBool ,
} ,
} , [ ] shaderir . Type { { Main : shaderir . Bool } } , stmts , true
}
2022-03-09 16:46:11 +01:00
case shaderir . IntF :
2022-08-19 08:24:00 +02:00
if len ( args ) == 1 && args [ 0 ] . Const != nil {
2022-07-07 17:07:32 +02:00
if ! canTruncateToInteger ( args [ 0 ] . Const ) {
cs . addError ( e . Pos ( ) , fmt . Sprintf ( "cannot convert %s to type int" , args [ 0 ] . Const . String ( ) ) )
return nil , nil , nil , false
}
2022-03-09 16:46:11 +01:00
return [ ] shaderir . Expr {
{
Type : shaderir . NumberExpr ,
Const : gconstant . ToInt ( args [ 0 ] . Const ) ,
ConstType : shaderir . ConstTypeInt ,
} ,
} , [ ] shaderir . Type { { Main : shaderir . Int } } , stmts , true
}
case shaderir . FloatF :
2022-08-19 08:24:00 +02:00
if len ( args ) == 1 && args [ 0 ] . Const != nil {
2022-08-18 10:30:12 +02:00
if gconstant . ToFloat ( args [ 0 ] . Const ) . Kind ( ) != gconstant . Unknown {
2022-07-07 17:07:32 +02:00
return [ ] shaderir . Expr {
{
Type : shaderir . NumberExpr ,
Const : gconstant . ToFloat ( args [ 0 ] . Const ) ,
ConstType : shaderir . ConstTypeFloat ,
} ,
} , [ ] shaderir . Type { { Main : shaderir . Float } } , stmts , true
}
2022-03-09 16:46:11 +01:00
}
2020-08-15 19:07:36 +02:00
}
2022-07-07 18:09:42 +02:00
// Process the expression as a regular function call.
2020-07-04 16:47:23 +02:00
var t shaderir . Type
switch callee . BuiltinFunc {
2020-08-12 05:16:08 +02:00
case shaderir . BoolF :
2022-07-07 17:07:32 +02:00
if err := checkArgsForBoolBuiltinFunc ( args , argts ) ; err != nil {
cs . addError ( e . Pos ( ) , err . Error ( ) )
return nil , nil , nil , false
}
2020-08-12 05:16:08 +02:00
t = shaderir . Type { Main : shaderir . Bool }
case shaderir . IntF :
2022-07-07 17:07:32 +02:00
if err := checkArgsForIntBuiltinFunc ( args , argts ) ; err != nil {
cs . addError ( e . Pos ( ) , err . Error ( ) )
return nil , nil , nil , false
}
2020-08-12 05:16:08 +02:00
t = shaderir . Type { Main : shaderir . Int }
case shaderir . FloatF :
2022-07-07 17:07:32 +02:00
if err := checkArgsForFloatBuiltinFunc ( args , argts ) ; err != nil {
cs . addError ( e . Pos ( ) , err . Error ( ) )
return nil , nil , nil , false
}
2020-08-12 05:16:08 +02:00
t = shaderir . Type { Main : shaderir . Float }
2020-07-04 16:47:23 +02:00
case shaderir . Vec2F :
2022-07-05 18:25:40 +02:00
if err := checkArgsForVec2BuiltinFunc ( args , argts ) ; err != nil {
cs . addError ( e . Pos ( ) , err . Error ( ) )
return nil , nil , nil , false
}
2020-07-04 16:47:23 +02:00
t = shaderir . Type { Main : shaderir . Vec2 }
case shaderir . Vec3F :
2022-07-05 18:25:40 +02:00
if err := checkArgsForVec3BuiltinFunc ( args , argts ) ; err != nil {
cs . addError ( e . Pos ( ) , err . Error ( ) )
return nil , nil , nil , false
}
2020-07-04 16:47:23 +02:00
t = shaderir . Type { Main : shaderir . Vec3 }
case shaderir . Vec4F :
2022-07-05 18:25:40 +02:00
if err := checkArgsForVec4BuiltinFunc ( args , argts ) ; err != nil {
cs . addError ( e . Pos ( ) , err . Error ( ) )
return nil , nil , nil , false
}
2020-07-04 16:47:23 +02:00
t = shaderir . Type { Main : shaderir . Vec4 }
2022-11-20 07:36:07 +01:00
case shaderir . IVec2F :
if err := checkArgsForVec2BuiltinFunc ( args , argts ) ; err != nil {
cs . addError ( e . Pos ( ) , err . Error ( ) )
return nil , nil , nil , false
}
t = shaderir . Type { Main : shaderir . IVec2 }
case shaderir . IVec3F :
if err := checkArgsForVec3BuiltinFunc ( args , argts ) ; err != nil {
cs . addError ( e . Pos ( ) , err . Error ( ) )
return nil , nil , nil , false
}
t = shaderir . Type { Main : shaderir . IVec3 }
case shaderir . IVec4F :
if err := checkArgsForVec4BuiltinFunc ( args , argts ) ; err != nil {
cs . addError ( e . Pos ( ) , err . Error ( ) )
return nil , nil , nil , false
}
t = shaderir . Type { Main : shaderir . IVec4 }
2020-07-04 16:47:23 +02:00
case shaderir . Mat2F :
2022-07-07 14:25:02 +02:00
if err := checkArgsForMat2BuiltinFunc ( args , argts ) ; err != nil {
cs . addError ( e . Pos ( ) , err . Error ( ) )
return nil , nil , nil , false
}
2020-07-04 16:47:23 +02:00
t = shaderir . Type { Main : shaderir . Mat2 }
case shaderir . Mat3F :
2022-07-07 14:25:02 +02:00
if err := checkArgsForMat3BuiltinFunc ( args , argts ) ; err != nil {
cs . addError ( e . Pos ( ) , err . Error ( ) )
return nil , nil , nil , false
}
2020-07-04 16:47:23 +02:00
t = shaderir . Type { Main : shaderir . Mat3 }
case shaderir . Mat4F :
2022-07-07 14:25:02 +02:00
if err := checkArgsForMat4BuiltinFunc ( args , argts ) ; err != nil {
cs . addError ( e . Pos ( ) , err . Error ( ) )
return nil , nil , nil , false
}
2020-07-04 16:47:23 +02:00
t = shaderir . Type { Main : shaderir . Mat4 }
2023-04-23 15:57:30 +02:00
case shaderir . TexelAt :
2022-08-18 19:27:36 +02:00
if len ( args ) != 2 {
cs . addError ( e . Pos ( ) , fmt . Sprintf ( "number of %s's arguments must be 2 but %d" , callee . BuiltinFunc , len ( args ) ) )
return nil , nil , nil , false
}
if argts [ 0 ] . Main != shaderir . Texture {
cs . addError ( e . Pos ( ) , fmt . Sprintf ( "cannot use %s as texture value in argument to %s" , argts [ 0 ] . String ( ) , callee . BuiltinFunc ) )
return nil , nil , nil , false
}
if argts [ 1 ] . Main != shaderir . Vec2 {
cs . addError ( e . Pos ( ) , fmt . Sprintf ( "cannot use %s as vec2 value in argument to %s" , argts [ 1 ] . String ( ) , callee . BuiltinFunc ) )
return nil , nil , nil , false
}
2020-07-04 16:47:23 +02:00
t = shaderir . Type { Main : shaderir . Vec4 }
2022-08-09 04:31:56 +02:00
case shaderir . DiscardF :
if len ( args ) != 0 {
cs . addError ( e . Pos ( ) , fmt . Sprintf ( "number of %s's arguments must be 0 but %d" , callee . BuiltinFunc , len ( args ) ) )
2022-08-18 10:30:12 +02:00
return nil , nil , nil , false
2022-08-09 04:31:56 +02:00
}
2022-08-17 16:13:21 +02:00
if fname != cs . fragmentEntry {
cs . addError ( e . Pos ( ) , fmt . Sprintf ( "discard is available only in %s" , cs . fragmentEntry ) )
2022-08-18 10:30:12 +02:00
return nil , nil , nil , false
2022-08-17 16:13:21 +02:00
}
2022-08-09 04:31:56 +02:00
stmts = append ( stmts , shaderir . Stmt {
Type : shaderir . Discard ,
} )
return nil , nil , stmts , true
2022-08-18 16:44:58 +02:00
2022-08-18 18:43:40 +02:00
case shaderir . Clamp , shaderir . Mix , shaderir . Smoothstep , shaderir . Faceforward , shaderir . Refract :
2022-08-18 17:13:47 +02:00
// 3 arguments
if len ( args ) != 3 {
cs . addError ( e . Pos ( ) , fmt . Sprintf ( "number of %s's arguments must be 3 but %d" , callee . BuiltinFunc , len ( args ) ) )
return nil , nil , nil , false
}
for i := range args {
// If the argument is a non-typed constant value, treat this as a float value (#1874).
2022-08-19 08:24:00 +02:00
if args [ i ] . Const != nil && args [ i ] . ConstType == shaderir . ConstTypeNone && gconstant . ToFloat ( args [ i ] . Const ) . Kind ( ) != gconstant . Unknown {
2022-08-18 17:13:47 +02:00
args [ i ] . Const = gconstant . ToFloat ( args [ i ] . Const )
args [ i ] . ConstType = shaderir . ConstTypeFloat
argts [ i ] = shaderir . Type { Main : shaderir . Float }
}
if argts [ i ] . Main != shaderir . Float && argts [ i ] . Main != shaderir . Vec2 && argts [ i ] . Main != shaderir . Vec3 && argts [ i ] . Main != shaderir . Vec4 {
cs . addError ( e . Pos ( ) , fmt . Sprintf ( "cannot use %s as float, vec2, vec3, or vec4 value in argument to %s" , argts [ i ] . String ( ) , callee . BuiltinFunc ) )
return nil , nil , nil , false
}
}
2022-08-18 18:16:11 +02:00
switch callee . BuiltinFunc {
case shaderir . Clamp :
if ( ! argts [ 0 ] . Equal ( & argts [ 1 ] ) || ! argts [ 0 ] . Equal ( & argts [ 2 ] ) ) && ( argts [ 1 ] . Main != shaderir . Float || argts [ 2 ] . Main != shaderir . Float ) {
cs . addError ( e . Pos ( ) , fmt . Sprintf ( "the second and the third arguments for %s must equal to the first argument %s or float but %s and %s" , callee . BuiltinFunc , argts [ 0 ] . String ( ) , argts [ 1 ] . String ( ) , argts [ 2 ] . String ( ) ) )
return nil , nil , nil , false
}
case shaderir . Mix :
if ! argts [ 0 ] . Equal ( & argts [ 1 ] ) {
cs . addError ( e . Pos ( ) , fmt . Sprintf ( "%s and %s don't match in argument to %s" , argts [ 0 ] . String ( ) , argts [ 1 ] . String ( ) , callee . BuiltinFunc ) )
return nil , nil , nil , false
}
if ! argts [ 0 ] . Equal ( & argts [ 2 ] ) && argts [ 2 ] . Main != shaderir . Float {
cs . addError ( e . Pos ( ) , fmt . Sprintf ( "the third arguments for %s must equal to the first/second argument %s or float but %s" , callee . BuiltinFunc , argts [ 0 ] . String ( ) , argts [ 2 ] . String ( ) ) )
return nil , nil , nil , false
}
2022-08-18 18:26:57 +02:00
case shaderir . Smoothstep :
if ( ! argts [ 0 ] . Equal ( & argts [ 1 ] ) || ! argts [ 0 ] . Equal ( & argts [ 2 ] ) ) && ( argts [ 0 ] . Main != shaderir . Float || argts [ 1 ] . Main != shaderir . Float ) {
cs . addError ( e . Pos ( ) , fmt . Sprintf ( "the first and the second arguments for %s must equal to the third argument %s or float but %s and %s" , callee . BuiltinFunc , argts [ 2 ] . String ( ) , argts [ 0 ] . String ( ) , argts [ 1 ] . String ( ) ) )
2022-08-18 18:37:45 +02:00
return nil , nil , nil , false
}
2022-08-18 18:43:40 +02:00
case shaderir . Refract :
if ! argts [ 0 ] . Equal ( & argts [ 1 ] ) {
cs . addError ( e . Pos ( ) , fmt . Sprintf ( "%s and %s don't match in argument to %s" , argts [ 0 ] . String ( ) , argts [ 1 ] . String ( ) , callee . BuiltinFunc ) )
return nil , nil , nil , false
}
if argts [ 2 ] . Main != shaderir . Float {
cs . addError ( e . Pos ( ) , fmt . Sprintf ( "cannot use %s as float value in argument to %s" , argts [ 2 ] . String ( ) , callee . BuiltinFunc ) )
return nil , nil , nil , false
}
2022-08-18 18:37:45 +02:00
default :
if ! argts [ 0 ] . Equal ( & argts [ 1 ] ) || ! argts [ 0 ] . Equal ( & argts [ 2 ] ) {
cs . addError ( e . Pos ( ) , fmt . Sprintf ( "all the argument types for %s must be the same but %s, %s, and %s" , callee . BuiltinFunc , argts [ 0 ] . String ( ) , argts [ 1 ] . String ( ) , argts [ 2 ] . String ( ) ) )
2022-08-18 18:26:57 +02:00
return nil , nil , nil , false
}
2022-08-18 17:13:47 +02:00
}
2022-08-18 18:26:57 +02:00
switch callee . BuiltinFunc {
case shaderir . Smoothstep :
t = argts [ 2 ]
default :
t = argts [ 0 ]
}
2022-08-18 17:13:47 +02:00
2022-09-04 19:35:40 +02:00
case shaderir . Atan2 , shaderir . Pow , shaderir . Mod , shaderir . Min , shaderir . Max , shaderir . Step , shaderir . Distance , shaderir . Dot , shaderir . Cross , shaderir . Reflect :
2022-08-18 16:44:58 +02:00
// 2 arguments
2022-08-18 10:30:12 +02:00
if len ( args ) != 2 {
cs . addError ( e . Pos ( ) , fmt . Sprintf ( "number of %s's arguments must be 2 but %d" , callee . BuiltinFunc , len ( args ) ) )
return nil , nil , nil , false
}
for i := range args {
// If the argument is a non-typed constant value, treat this as a float value (#1874).
2022-08-19 08:24:00 +02:00
if args [ i ] . Const != nil && args [ i ] . ConstType == shaderir . ConstTypeNone && gconstant . ToFloat ( args [ i ] . Const ) . Kind ( ) != gconstant . Unknown {
2022-08-18 10:30:12 +02:00
args [ i ] . Const = gconstant . ToFloat ( args [ i ] . Const )
args [ i ] . ConstType = shaderir . ConstTypeFloat
argts [ i ] = shaderir . Type { Main : shaderir . Float }
}
if argts [ i ] . Main != shaderir . Float && argts [ i ] . Main != shaderir . Vec2 && argts [ i ] . Main != shaderir . Vec3 && argts [ i ] . Main != shaderir . Vec4 {
cs . addError ( e . Pos ( ) , fmt . Sprintf ( "cannot use %s as float, vec2, vec3, or vec4 value in argument to %s" , argts [ i ] . String ( ) , callee . BuiltinFunc ) )
return nil , nil , nil , false
}
}
2022-08-18 16:44:58 +02:00
switch callee . BuiltinFunc {
case shaderir . Mod , shaderir . Min , shaderir . Max :
2022-08-18 10:30:12 +02:00
if ! argts [ 0 ] . Equal ( & argts [ 1 ] ) && argts [ 1 ] . Main != shaderir . Float {
cs . addError ( e . Pos ( ) , fmt . Sprintf ( "the second argument for %s must equal to the first argument %s or float but %s" , callee . BuiltinFunc , argts [ 0 ] . String ( ) , argts [ 1 ] . String ( ) ) )
return nil , nil , nil , false
}
2022-08-18 17:24:59 +02:00
case shaderir . Step :
if ! argts [ 0 ] . Equal ( & argts [ 1 ] ) && argts [ 0 ] . Main != shaderir . Float {
cs . addError ( e . Pos ( ) , fmt . Sprintf ( "the first argument for %s must equal to the second argument %s or float but %s" , callee . BuiltinFunc , argts [ 1 ] . String ( ) , argts [ 0 ] . String ( ) ) )
return nil , nil , nil , false
}
2022-08-18 16:44:58 +02:00
case shaderir . Cross :
for i := range argts {
if argts [ i ] . Main != shaderir . Vec3 {
cs . addError ( e . Pos ( ) , fmt . Sprintf ( "cannot use %s as vec3 value in argument to %s" , argts [ i ] . String ( ) , callee . BuiltinFunc ) )
return nil , nil , nil , false
}
}
default :
2022-08-18 10:30:12 +02:00
if ! argts [ 0 ] . Equal ( & argts [ 1 ] ) {
cs . addError ( e . Pos ( ) , fmt . Sprintf ( "%s and %s don't match in argument to %s" , argts [ 0 ] . String ( ) , argts [ 1 ] . String ( ) , callee . BuiltinFunc ) )
return nil , nil , nil , false
}
}
2022-08-18 17:24:59 +02:00
switch callee . BuiltinFunc {
case shaderir . Distance , shaderir . Dot :
2022-08-18 10:30:12 +02:00
t = shaderir . Type { Main : shaderir . Float }
2022-08-18 17:24:59 +02:00
case shaderir . Step :
t = argts [ 1 ]
default :
2022-08-18 10:30:12 +02:00
t = argts [ 0 ]
}
2022-08-18 16:44:58 +02:00
2020-07-04 16:47:23 +02:00
default :
2022-08-18 16:44:58 +02:00
// 1 argument
2022-08-18 10:30:12 +02:00
if len ( args ) != 1 {
cs . addError ( e . Pos ( ) , fmt . Sprintf ( "number of %s's arguments must be 1 but %d" , callee . BuiltinFunc , len ( args ) ) )
return nil , nil , nil , false
}
// If the argument is a non-typed constant value, treat this as a float value (#1874).
2022-08-19 08:24:00 +02:00
if args [ 0 ] . Const != nil && args [ 0 ] . ConstType == shaderir . ConstTypeNone && gconstant . ToFloat ( args [ 0 ] . Const ) . Kind ( ) != gconstant . Unknown {
2022-08-18 10:30:12 +02:00
args [ 0 ] . Const = gconstant . ToFloat ( args [ 0 ] . Const )
2021-11-14 08:24:26 +01:00
args [ 0 ] . ConstType = shaderir . ConstTypeFloat
argts [ 0 ] = shaderir . Type { Main : shaderir . Float }
}
2022-08-18 18:57:13 +02:00
switch callee . BuiltinFunc {
case shaderir . Transpose :
if argts [ 0 ] . Main != shaderir . Mat2 && argts [ 0 ] . Main != shaderir . Mat3 && argts [ 0 ] . Main != shaderir . Mat4 {
cs . addError ( e . Pos ( ) , fmt . Sprintf ( "cannot use %s as mat2, mat3, or mat4 value in argument to %s" , argts [ 0 ] . String ( ) , callee . BuiltinFunc ) )
return nil , nil , nil , false
}
default :
if argts [ 0 ] . Main != shaderir . Float && argts [ 0 ] . Main != shaderir . Vec2 && argts [ 0 ] . Main != shaderir . Vec3 && argts [ 0 ] . Main != shaderir . Vec4 {
cs . addError ( e . Pos ( ) , fmt . Sprintf ( "cannot use %s as float, vec2, vec3, or vec4 value in argument to %s" , argts [ 0 ] . String ( ) , callee . BuiltinFunc ) )
return nil , nil , nil , false
}
2022-08-18 10:30:12 +02:00
}
if callee . BuiltinFunc == shaderir . Length {
t = shaderir . Type { Main : shaderir . Float }
} else {
t = argts [ 0 ]
}
2020-07-04 16:47:23 +02:00
}
return [ ] shaderir . Expr {
{
Type : shaderir . Call ,
Exprs : append ( [ ] shaderir . Expr { callee } , args ... ) ,
} ,
} , [ ] shaderir . Type { t } , stmts , true
}
if callee . Type != shaderir . FunctionExpr {
2022-08-03 15:40:39 +02:00
cs . addError ( e . Pos ( ) , fmt . Sprintf ( "function callee must be a function name but %s" , e . Fun ) )
2020-07-04 16:47:23 +02:00
return nil , nil , nil , false
}
f := cs . funcs [ callee . Index ]
2021-11-14 09:34:06 +01:00
if len ( f . ir . InParams ) < len ( args ) {
cs . addError ( e . Pos ( ) , fmt . Sprintf ( "too many arguments in call to %s" , e . Fun ) )
return nil , nil , nil , false
}
if len ( f . ir . InParams ) > len ( args ) {
cs . addError ( e . Pos ( ) , fmt . Sprintf ( "not enough arguments in call to %s" , e . Fun ) )
return nil , nil , nil , false
}
2020-09-02 19:45:22 +02:00
for i , p := range f . ir . InParams {
2022-08-19 08:24:00 +02:00
if args [ i ] . Const != nil && p . Main == shaderir . Int {
2020-09-02 19:45:22 +02:00
if ! cs . forceToInt ( e , & args [ i ] ) {
return nil , nil , nil , false
}
}
2022-04-08 17:32:25 +02:00
2022-11-20 07:36:07 +01:00
if ! canAssign ( & p , & argts [ i ] , args [ i ] . Const ) {
2022-04-08 17:32:25 +02:00
cs . addError ( e . Pos ( ) , fmt . Sprintf ( "cannot use type %s as type %s in argument" , argts [ i ] . String ( ) , p . String ( ) ) )
return nil , nil , nil , false
}
2020-09-02 19:45:22 +02:00
}
2020-07-04 16:47:23 +02:00
var outParams [ ] int
for _ , p := range f . ir . OutParams {
2022-07-12 18:50:19 +02:00
idx := block . totalLocalVariableCount ( )
2020-07-04 16:47:23 +02:00
block . vars = append ( block . vars , variable {
typ : p ,
} )
args = append ( args , shaderir . Expr {
Type : shaderir . LocalVariable ,
Index : idx ,
} )
outParams = append ( outParams , idx )
}
if t := f . ir . Return ; t . Main != shaderir . None {
if len ( outParams ) != 0 {
cs . addError ( e . Pos ( ) , fmt . Sprintf ( "a function returning value cannot have out-params so far: %s" , e . Fun ) )
return nil , nil , nil , false
}
// The actual expression here is just a local variable that includes the result of the
// function call.
return [ ] shaderir . Expr {
{
2022-07-10 11:58:39 +02:00
Type : shaderir . Call ,
Exprs : append ( [ ] shaderir . Expr { callee } , args ... ) ,
2020-07-04 16:47:23 +02:00
} ,
} , [ ] shaderir . Type { t } , stmts , true
}
// Even if the function doesn't return anything, calling the function should be done eariler to keep
// the evaluation order.
stmts = append ( stmts , shaderir . Stmt {
Type : shaderir . ExprStmt ,
Exprs : [ ] shaderir . Expr {
{
Type : shaderir . Call ,
Exprs : append ( [ ] shaderir . Expr { callee } , args ... ) ,
} ,
} ,
} )
if len ( outParams ) == 0 {
// TODO: Is this an error?
}
2021-12-27 15:14:05 +01:00
// These local-variable expressions are used for an outside function callers.
2020-07-04 16:47:23 +02:00
var exprs [ ] shaderir . Expr
for _ , p := range outParams {
exprs = append ( exprs , shaderir . Expr {
Type : shaderir . LocalVariable ,
Index : p ,
} )
}
return exprs , f . ir . OutParams , stmts , true
case * ast . Ident :
2020-09-02 18:15:37 +02:00
if e . Name == "_" {
2020-09-13 16:48:12 +02:00
// In the context where a local variable is marked as used, any expressions must have its
// meaning. Then, a blank identifier is not available there.
if markLocalVariableUsed {
cs . addError ( e . Pos ( ) , fmt . Sprintf ( "cannot use _ as value" ) )
return nil , nil , nil , false
}
2020-09-02 18:15:37 +02:00
return [ ] shaderir . Expr {
{
Type : shaderir . Blank ,
} ,
2020-09-13 16:48:12 +02:00
} , [ ] shaderir . Type { { } } , nil , true
2020-09-02 18:15:37 +02:00
}
2020-09-13 13:13:30 +02:00
if i , t , ok := block . findLocalVariable ( e . Name , markLocalVariableUsed ) ; ok {
2020-07-04 16:47:23 +02:00
return [ ] shaderir . Expr {
{
Type : shaderir . LocalVariable ,
Index : i ,
} ,
} , [ ] shaderir . Type { t } , nil , true
}
2021-04-08 16:55:35 +02:00
if c , ok := block . findConstant ( e . Name ) ; ok {
return [ ] shaderir . Expr {
{
2021-04-08 17:32:15 +02:00
Type : shaderir . NumberExpr ,
Const : c . value ,
ConstType : c . ctyp ,
2021-04-08 16:55:35 +02:00
} ,
} , [ ] shaderir . Type { c . typ } , nil , true
}
2020-07-04 16:47:23 +02:00
if i , ok := cs . findFunction ( e . Name ) ; ok {
return [ ] shaderir . Expr {
{
Type : shaderir . FunctionExpr ,
Index : i ,
} ,
} , nil , nil , true
}
if i , ok := cs . findUniformVariable ( e . Name ) ; ok {
return [ ] shaderir . Expr {
{
Type : shaderir . UniformVariable ,
Index : i ,
} ,
} , [ ] shaderir . Type { cs . ir . Uniforms [ i ] } , nil , true
}
if f , ok := shaderir . ParseBuiltinFunc ( e . Name ) ; ok {
return [ ] shaderir . Expr {
{
Type : shaderir . BuiltinFuncExpr ,
BuiltinFunc : f ,
} ,
} , nil , nil , true
}
2020-07-18 12:17:19 +02:00
if m := textureVariableRe . FindStringSubmatch ( e . Name ) ; m != nil {
2020-07-05 20:36:15 +02:00
i , _ := strconv . Atoi ( m [ 1 ] )
return [ ] shaderir . Expr {
{
2020-07-18 12:17:19 +02:00
Type : shaderir . TextureVariable ,
Index : i ,
2020-07-05 20:36:15 +02:00
} ,
2022-08-18 19:27:36 +02:00
} , [ ] shaderir . Type { { Main : shaderir . Texture } } , nil , true
2020-07-05 20:36:15 +02:00
}
2020-09-13 13:38:51 +02:00
if e . Name == "true" || e . Name == "false" {
return [ ] shaderir . Expr {
{
Type : shaderir . NumberExpr ,
Const : gconstant . MakeBool ( e . Name == "true" ) ,
} ,
} , [ ] shaderir . Type { { Main : shaderir . Bool } } , nil , true
}
2020-07-04 16:47:23 +02:00
cs . addError ( e . Pos ( ) , fmt . Sprintf ( "unexpected identifier: %s" , e . Name ) )
case * ast . ParenExpr :
2022-08-17 16:13:21 +02:00
return cs . parseExpr ( block , fname , e . X , markLocalVariableUsed )
2020-07-04 16:47:23 +02:00
case * ast . SelectorExpr :
2022-11-19 14:37:31 +01:00
exprs , types , stmts , ok := cs . parseExpr ( block , fname , e . X , true )
2020-07-04 16:47:23 +02:00
if ! ok {
return nil , nil , nil , false
}
if len ( exprs ) != 1 {
cs . addError ( e . Pos ( ) , fmt . Sprintf ( "multiple-value context is not available at a selector: %s" , e . X ) )
return nil , nil , nil , false
}
2022-11-19 14:37:31 +01:00
if ! isValidSwizzling ( e . Sel . Name , types [ 0 ] ) {
cs . addError ( e . Pos ( ) , fmt . Sprintf ( "unexpected swizzling: %s" , e . Sel . Name ) )
return nil , nil , nil , false
}
2020-07-04 16:47:23 +02:00
var t shaderir . Type
2022-11-20 07:36:07 +01:00
switch types [ 0 ] . Main {
case shaderir . Vec2 , shaderir . Vec3 , shaderir . Vec4 :
switch len ( e . Sel . Name ) {
case 1 :
t . Main = shaderir . Float
case 2 :
t . Main = shaderir . Vec2
case 3 :
t . Main = shaderir . Vec3
case 4 :
t . Main = shaderir . Vec4
}
case shaderir . IVec2 , shaderir . IVec3 , shaderir . IVec4 :
switch len ( e . Sel . Name ) {
case 1 :
t . Main = shaderir . Int
case 2 :
t . Main = shaderir . IVec2
case 3 :
t . Main = shaderir . IVec3
case 4 :
t . Main = shaderir . IVec4
}
}
if t . Equal ( & shaderir . Type { } ) {
2020-07-04 16:47:23 +02:00
cs . addError ( e . Pos ( ) , fmt . Sprintf ( "unexpected swizzling: %s" , e . Sel . Name ) )
return nil , nil , nil , false
}
return [ ] shaderir . Expr {
{
Type : shaderir . FieldSelector ,
Exprs : [ ] shaderir . Expr {
exprs [ 0 ] ,
{
Type : shaderir . SwizzlingExpr ,
Swizzling : e . Sel . Name ,
} ,
} ,
} ,
} , [ ] shaderir . Type { t } , stmts , true
case * ast . UnaryExpr :
2022-08-17 16:13:21 +02:00
exprs , t , stmts , ok := cs . parseExpr ( block , fname , e . X , markLocalVariableUsed )
2020-07-04 16:47:23 +02:00
if ! ok {
return nil , nil , nil , false
}
if len ( exprs ) != 1 {
cs . addError ( e . Pos ( ) , fmt . Sprintf ( "multiple-value context is not available at a unary operator: %s" , e . X ) )
return nil , nil , nil , false
}
2022-08-19 08:24:00 +02:00
if exprs [ 0 ] . Const != nil {
2020-07-04 16:47:23 +02:00
v := gconstant . UnaryOp ( e . Op , exprs [ 0 ] . Const , 0 )
t := shaderir . Type { Main : shaderir . Int }
if v . Kind ( ) == gconstant . Float {
t = shaderir . Type { Main : shaderir . Float }
}
return [ ] shaderir . Expr {
{
Type : shaderir . NumberExpr ,
Const : v ,
} ,
} , [ ] shaderir . Type { t } , stmts , true
}
var op shaderir . Op
switch e . Op {
case token . ADD :
op = shaderir . Add
case token . SUB :
op = shaderir . Sub
case token . NOT :
op = shaderir . NotOp
default :
cs . addError ( e . Pos ( ) , fmt . Sprintf ( "unexpected operator: %s" , e . Op ) )
return nil , nil , nil , false
}
return [ ] shaderir . Expr {
{
Type : shaderir . Unary ,
Op : op ,
Exprs : exprs ,
} ,
} , t , stmts , true
2020-07-29 16:25:16 +02:00
case * ast . CompositeLit :
2022-08-17 16:13:21 +02:00
t , ok := cs . parseType ( block , fname , e . Type )
2020-07-29 16:25:16 +02:00
if ! ok {
return nil , nil , nil , false
}
2023-02-20 15:07:08 +01:00
if t . Main != shaderir . Array {
cs . addError ( e . Pos ( ) , fmt . Sprintf ( "invalid composite literal type %s" , t . String ( ) ) )
return nil , nil , nil , false
}
2020-08-11 16:51:22 +02:00
if t . Main == shaderir . Array && t . Length == - 1 {
t . Length = len ( e . Elts )
}
2020-07-29 16:25:16 +02:00
2022-07-12 18:50:19 +02:00
idx := block . totalLocalVariableCount ( )
2020-07-29 16:25:16 +02:00
block . vars = append ( block . vars , variable {
typ : t ,
} )
var stmts [ ] shaderir . Stmt
for i , e := range e . Elts {
2022-08-17 16:13:21 +02:00
exprs , _ , ss , ok := cs . parseExpr ( block , fname , e , markLocalVariableUsed )
2020-07-29 16:25:16 +02:00
if ! ok {
return nil , nil , nil , false
}
if len ( exprs ) != 1 {
cs . addError ( e . Pos ( ) , fmt . Sprintf ( "multiple-value context is not available at a composite literal" ) )
return nil , nil , nil , false
}
stmts = append ( stmts , ss ... )
stmts = append ( stmts , shaderir . Stmt {
Type : shaderir . Assign ,
Exprs : [ ] shaderir . Expr {
{
Type : shaderir . Index ,
Exprs : [ ] shaderir . Expr {
{
Type : shaderir . LocalVariable ,
Index : idx ,
} ,
{
Type : shaderir . NumberExpr ,
Const : gconstant . MakeInt64 ( int64 ( i ) ) ,
ConstType : shaderir . ConstTypeInt ,
} ,
} ,
} ,
exprs [ 0 ] ,
} ,
} )
}
return [ ] shaderir . Expr {
{
Type : shaderir . LocalVariable ,
Index : idx ,
} ,
} , [ ] shaderir . Type { t } , stmts , true
2020-07-29 16:49:39 +02:00
case * ast . IndexExpr :
var stmts [ ] shaderir . Stmt
// Parse the index first
2022-08-17 16:13:21 +02:00
exprs , _ , ss , ok := cs . parseExpr ( block , fname , e . Index , markLocalVariableUsed )
2020-07-29 16:49:39 +02:00
if ! ok {
return nil , nil , nil , false
}
stmts = append ( stmts , ss ... )
if len ( exprs ) != 1 {
cs . addError ( e . Pos ( ) , fmt . Sprintf ( "multiple-value context is not available at an index expression" ) )
return nil , nil , nil , false
}
idx := exprs [ 0 ]
2022-08-19 08:24:00 +02:00
if idx . Const != nil {
2020-08-12 06:05:37 +02:00
if ! canTruncateToInteger ( idx . Const ) {
cs . addError ( e . Pos ( ) , fmt . Sprintf ( "constant %s truncated to integer" , idx . Const . String ( ) ) )
return nil , nil , nil , false
}
2020-08-09 15:50:53 +02:00
idx . ConstType = shaderir . ConstTypeInt
2020-07-29 16:49:39 +02:00
}
2022-08-17 16:13:21 +02:00
exprs , ts , ss , ok := cs . parseExpr ( block , fname , e . X , markLocalVariableUsed )
2020-07-29 16:49:39 +02:00
if ! ok {
return nil , nil , nil , false
}
stmts = append ( stmts , ss ... )
if len ( exprs ) != 1 {
cs . addError ( e . Pos ( ) , fmt . Sprintf ( "multiple-value context is not available at an index expression" ) )
return nil , nil , nil , false
}
x := exprs [ 0 ]
t := ts [ 0 ]
var typ shaderir . Type
switch t . Main {
case shaderir . Vec2 , shaderir . Vec3 , shaderir . Vec4 :
typ = shaderir . Type { Main : shaderir . Float }
2022-11-20 07:36:07 +01:00
case shaderir . IVec2 , shaderir . IVec3 , shaderir . IVec4 :
typ = shaderir . Type { Main : shaderir . Int }
2020-07-29 16:49:39 +02:00
case shaderir . Mat2 :
typ = shaderir . Type { Main : shaderir . Vec2 }
case shaderir . Mat3 :
typ = shaderir . Type { Main : shaderir . Vec3 }
case shaderir . Mat4 :
typ = shaderir . Type { Main : shaderir . Vec4 }
case shaderir . Array :
typ = t . Sub [ 0 ]
default :
cs . addError ( e . Pos ( ) , fmt . Sprintf ( "index operator cannot be applied to the type %s" , t . String ( ) ) )
return nil , nil , nil , false
}
return [ ] shaderir . Expr {
{
Type : shaderir . Index ,
Exprs : [ ] shaderir . Expr {
x ,
idx ,
} ,
} ,
} , [ ] shaderir . Type { typ } , stmts , true
2020-07-04 16:47:23 +02:00
default :
cs . addError ( e . Pos ( ) , fmt . Sprintf ( "expression not implemented: %#v" , e ) )
}
return nil , nil , nil , false
}
2022-11-19 14:37:31 +01:00
func isValidSwizzling ( swizzling string , t shaderir . Type ) bool {
if ! shaderir . IsValidSwizzling ( swizzling ) {
return false
}
switch t . Main {
2022-11-20 07:36:07 +01:00
case shaderir . Vec2 , shaderir . IVec2 :
2023-04-23 14:29:15 +02:00
return ! strings . ContainsAny ( swizzling , "zwbapq" )
2022-11-20 07:36:07 +01:00
case shaderir . Vec3 , shaderir . IVec3 :
2022-11-19 14:37:31 +01:00
return ! strings . ContainsAny ( swizzling , "waq" )
2022-11-20 07:36:07 +01:00
case shaderir . Vec4 , shaderir . IVec4 :
2022-11-19 14:37:31 +01:00
return true
default :
return false
}
}