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
}
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 ... )
2024-03-13 03:34:57 +01:00
if len ( ts ) == 0 {
cs . addError ( e . Pos ( ) , fmt . Sprintf ( "unexpected binary operator: %s" , e . X ) )
return nil , nil , nil , false
}
2020-07-04 16:47:23 +02:00
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 ... )
2024-07-25 17:06:09 +02:00
if len ( ts ) == 0 {
cs . addError ( e . Pos ( ) , fmt . Sprintf ( "unexpected binary operator: %s" , e . Y ) )
return nil , nil , nil , false
}
2020-07-04 16:47:23 +02:00
rhst := ts [ 0 ]
2023-07-23 18:49:37 +02:00
op := e . Op
// https://pkg.go.dev/go/constant/#BinaryOp
// "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 != nil && lhs [ 0 ] . Const . Kind ( ) == gconstant . Int && rhs [ 0 ] . Const != nil && rhs [ 0 ] . Const . Kind ( ) == gconstant . Int {
op = token . QUO_ASSIGN
}
op2 , ok := shaderir . OpFromToken ( e . Op , lhst , rhst )
if ! ok {
cs . addError ( e . Pos ( ) , fmt . Sprintf ( "unexpected operator: %s" , e . Op ) )
return nil , nil , nil , false
}
2023-07-24 16:27:52 +02:00
// Resolve untyped constants.
2024-03-22 10:14:03 +01:00
l , r , ok := shaderir . ResolveUntypedConstsForBinaryOp ( op2 , lhs [ 0 ] . Const , rhs [ 0 ] . Const , lhst , rhst )
2023-07-24 16:27:52 +02:00
if ! ok {
// TODO: Show a better type name for untyped constants.
cs . addError ( e . Pos ( ) , fmt . Sprintf ( "types don't match: %s %s %s" , lhst . String ( ) , op , rhst . String ( ) ) )
return nil , nil , nil , false
}
lhs [ 0 ] . Const , rhs [ 0 ] . Const = l , r
2023-07-24 05:29:27 +02:00
2023-07-24 16:27:52 +02:00
// If either is typed, resolve the other type.
// If both are untyped, keep them untyped.
if lhst . Main != shaderir . None || rhst . Main != shaderir . None {
if lhs [ 0 ] . Const != nil {
switch lhs [ 0 ] . Const . Kind ( ) {
case gconstant . Float :
lhst = shaderir . Type { Main : shaderir . Float }
case gconstant . Int :
lhst = shaderir . Type { Main : shaderir . Int }
case gconstant . Bool :
lhst = shaderir . Type { Main : shaderir . Bool }
}
2023-07-24 05:29:27 +02:00
}
2023-07-24 16:27:52 +02:00
if rhs [ 0 ] . Const != nil {
switch rhs [ 0 ] . Const . Kind ( ) {
case gconstant . Float :
rhst = shaderir . Type { Main : shaderir . Float }
case gconstant . Int :
rhst = shaderir . Type { Main : shaderir . Int }
case gconstant . Bool :
rhst = shaderir . Type { Main : shaderir . Bool }
}
2023-07-24 05:29:27 +02:00
}
}
2023-09-12 20:25:42 +02:00
t , ok := shaderir . TypeFromBinaryOp ( op2 , lhst , rhst , lhs [ 0 ] . Const , rhs [ 0 ] . Const )
2023-09-12 17:15:17 +02:00
if ! ok {
2023-07-24 18:53:08 +02:00
// TODO: Show a better type name for untyped constants.
cs . addError ( e . Pos ( ) , fmt . Sprintf ( "types don't match: %s %s %s" , lhst . String ( ) , op , rhst . String ( ) ) )
return nil , nil , nil , false
}
2023-07-24 05:29:27 +02:00
2022-08-19 08:24:00 +02:00
if lhs [ 0 ] . Const != nil && rhs [ 0 ] . Const != nil {
2020-07-05 10:33:03 +02:00
var v gconstant . Value
switch op {
2023-07-24 18:53:08 +02:00
case token . LAND , token . LOR :
b := gconstant . BoolVal ( gconstant . BinaryOp ( lhs [ 0 ] . Const , op , rhs [ 0 ] . Const ) )
v = gconstant . MakeBool ( b )
case token . EQL , token . NEQ , token . LSS , token . LEQ , token . GTR , token . GEQ :
v = gconstant . MakeBool ( gconstant . Compare ( lhs [ 0 ] . Const , op , rhs [ 0 ] . Const ) )
2024-03-22 10:14:03 +01:00
case token . SHL , token . SHR :
shift , ok := gconstant . Int64Val ( rhs [ 0 ] . Const )
if ! ok {
cs . addError ( e . Pos ( ) , fmt . Sprintf ( "unexpected %s type for: %s" , rhs [ 0 ] . Const . String ( ) , e . Op ) )
return nil , nil , nil , false
}
v = gconstant . Shift ( lhs [ 0 ] . Const , op , uint ( shift ) )
2022-11-20 16:49:34 +01:00
default :
2020-07-05 10:33:03 +02:00
v = gconstant . BinaryOp ( lhs [ 0 ] . Const , op , rhs [ 0 ] . Const )
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
}
return [ ] shaderir . Expr {
{
Type : shaderir . Binary ,
2023-07-23 18:49:37 +02:00
Op : op2 ,
2020-07-04 16:47:23 +02:00
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
}
2024-02-11 13:09:47 +01:00
for _ , expr := range es {
2024-04-29 09:44:31 +02:00
if expr . Type == shaderir . FunctionExpr || expr . Type == shaderir . BuiltinFuncExpr {
2024-02-11 13:09:47 +01:00
cs . addError ( e . Pos ( ) , fmt . Sprintf ( "function name cannot be an argument: %s" , e . Fun ) )
return nil , nil , nil , false
}
}
2020-07-04 16:47:23 +02:00
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 {
{
2023-07-29 07:10:31 +02:00
Type : shaderir . NumberExpr ,
Const : gconstant . MakeInt64 ( int64 ( argts [ 0 ] . Length ) ) ,
2020-08-15 19:07:36 +02:00
} ,
} , [ ] 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 {
{
2023-07-29 07:10:31 +02:00
Type : shaderir . NumberExpr ,
Const : args [ 0 ] . Const ,
2022-11-20 09:18:24 +01:00
} ,
} , [ ] 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 {
2023-07-27 17:13:40 +02:00
// For constants, a cast-like function doesn't work as a cast.
// For example, `int(1.1)` is invalid.
v := gconstant . ToInt ( args [ 0 ] . Const )
if v . Kind ( ) == gconstant . Unknown {
2023-07-27 17:54:36 +02:00
cs . addError ( e . Pos ( ) , fmt . Sprintf ( "cannot convert %s to type int" , args [ 0 ] . Const . String ( ) ) )
return nil , nil , nil , false
2022-07-07 17:07:32 +02:00
}
2022-03-09 16:46:11 +01:00
return [ ] shaderir . Expr {
{
2023-07-29 07:10:31 +02:00
Type : shaderir . NumberExpr ,
Const : v ,
2022-03-09 16:46:11 +01:00
} ,
} , [ ] 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 {
2023-07-27 17:13:40 +02:00
v := gconstant . ToFloat ( args [ 0 ] . Const )
if v . Kind ( ) == gconstant . Unknown {
cs . addError ( e . Pos ( ) , fmt . Sprintf ( "cannot convert %s to type float" , args [ 0 ] . Const . String ( ) ) )
return nil , nil , nil , false
2022-07-07 17:07:32 +02:00
}
2023-07-27 17:13:40 +02:00
return [ ] shaderir . Expr {
{
2023-07-29 07:10:31 +02:00
Type : shaderir . NumberExpr ,
Const : v ,
2023-07-27 17:13:40 +02:00
} ,
} , [ ] 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.
2024-03-10 11:14:05 +01:00
var finalType shaderir . Type
2020-07-04 16:47:23 +02:00
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
}
2024-03-10 11:14:05 +01:00
finalType = shaderir . Type { Main : shaderir . Bool }
2020-08-12 05:16:08 +02:00
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
}
2024-03-10 11:14:05 +01:00
finalType = shaderir . Type { Main : shaderir . Int }
2020-08-12 05:16:08 +02:00
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
}
2024-03-10 11:14:05 +01:00
finalType = 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
}
2023-07-27 16:00:55 +02:00
for i := range args {
if args [ i ] . Const == nil {
continue
}
args [ i ] . Const = gconstant . ToFloat ( args [ i ] . Const )
argts [ i ] = shaderir . Type { Main : shaderir . Float }
}
2024-03-10 11:14:05 +01:00
finalType = shaderir . Type { Main : shaderir . Vec2 }
2020-07-04 16:47:23 +02:00
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
}
2023-07-27 16:00:55 +02:00
for i := range args {
if args [ i ] . Const == nil {
continue
}
args [ i ] . Const = gconstant . ToFloat ( args [ i ] . Const )
argts [ i ] = shaderir . Type { Main : shaderir . Float }
}
2024-03-10 11:14:05 +01:00
finalType = shaderir . Type { Main : shaderir . Vec3 }
2020-07-04 16:47:23 +02:00
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
}
2023-07-27 16:00:55 +02:00
for i := range args {
if args [ i ] . Const == nil {
continue
}
args [ i ] . Const = gconstant . ToFloat ( args [ i ] . Const )
argts [ i ] = shaderir . Type { Main : shaderir . Float }
}
2024-03-10 11:14:05 +01:00
finalType = shaderir . Type { Main : shaderir . Vec4 }
2022-11-20 07:36:07 +01:00
case shaderir . IVec2F :
2023-07-27 17:13:40 +02:00
if err := checkArgsForIVec2BuiltinFunc ( args , argts ) ; err != nil {
2022-11-20 07:36:07 +01:00
cs . addError ( e . Pos ( ) , err . Error ( ) )
return nil , nil , nil , false
}
2024-03-10 11:14:05 +01:00
finalType = shaderir . Type { Main : shaderir . IVec2 }
2022-11-20 07:36:07 +01:00
case shaderir . IVec3F :
2023-07-27 17:13:40 +02:00
if err := checkArgsForIVec3BuiltinFunc ( args , argts ) ; err != nil {
2022-11-20 07:36:07 +01:00
cs . addError ( e . Pos ( ) , err . Error ( ) )
return nil , nil , nil , false
}
2024-03-10 11:14:05 +01:00
finalType = shaderir . Type { Main : shaderir . IVec3 }
2022-11-20 07:36:07 +01:00
case shaderir . IVec4F :
2023-07-27 17:13:40 +02:00
if err := checkArgsForIVec4BuiltinFunc ( args , argts ) ; err != nil {
2022-11-20 07:36:07 +01:00
cs . addError ( e . Pos ( ) , err . Error ( ) )
return nil , nil , nil , false
}
2024-03-10 11:14:05 +01:00
finalType = 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
}
2023-07-27 16:00:55 +02:00
for i := range args {
if args [ i ] . Const == nil {
continue
}
args [ i ] . Const = gconstant . ToFloat ( args [ i ] . Const )
argts [ i ] = shaderir . Type { Main : shaderir . Float }
}
2024-03-10 11:14:05 +01:00
finalType = shaderir . Type { Main : shaderir . Mat2 }
2020-07-04 16:47:23 +02:00
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
}
2023-07-27 16:00:55 +02:00
for i := range args {
if args [ i ] . Const == nil {
continue
}
args [ i ] . Const = gconstant . ToFloat ( args [ i ] . Const )
argts [ i ] = shaderir . Type { Main : shaderir . Float }
}
2024-03-10 11:14:05 +01:00
finalType = shaderir . Type { Main : shaderir . Mat3 }
2020-07-04 16:47:23 +02:00
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
}
2023-07-27 16:00:55 +02:00
for i := range args {
if args [ i ] . Const == nil {
continue
}
args [ i ] . Const = gconstant . ToFloat ( args [ i ] . Const )
argts [ i ] = shaderir . Type { Main : shaderir . Float }
}
2024-03-10 11:14:05 +01:00
finalType = 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
}
2024-03-10 11:14:05 +01:00
finalType = 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
}
2024-02-25 12:26:43 +01:00
switch callee . BuiltinFunc {
case shaderir . Clamp :
2024-03-10 11:14:05 +01:00
if kind , _ := resolveConstKind ( args , argts ) ; kind != gconstant . Unknown {
2024-02-25 12:26:43 +01:00
switch kind {
case gconstant . Int :
for i , arg := range args {
2024-03-10 11:14:05 +01:00
if arg . Const == nil {
if argts [ i ] . Main != shaderir . Int {
cs . addError ( e . Pos ( ) , fmt . Sprintf ( "%s's arguments don't match: %s, %s, and %s" , callee . BuiltinFunc , argts [ 0 ] . String ( ) , argts [ 1 ] . String ( ) , argts [ 2 ] . String ( ) ) )
return nil , nil , nil , false
}
continue
}
2024-02-25 12:26:43 +01:00
v := gconstant . ToInt ( arg . Const )
if v . Kind ( ) == gconstant . Unknown {
cs . addError ( e . Pos ( ) , fmt . Sprintf ( "cannot convert %s to type int" , arg . Const . String ( ) ) )
return nil , nil , nil , false
}
args [ i ] . Const = gconstant . ToInt ( args [ i ] . Const )
argts [ i ] = shaderir . Type { Main : shaderir . Int }
}
case gconstant . Float :
for i , arg := range args {
2024-03-10 11:14:05 +01:00
if arg . Const == nil {
if argts [ i ] . Main != shaderir . Float {
cs . addError ( e . Pos ( ) , fmt . Sprintf ( "%s's arguments don't match: %s, %s, and %s" , callee . BuiltinFunc , argts [ 0 ] . String ( ) , argts [ 1 ] . String ( ) , argts [ 2 ] . String ( ) ) )
return nil , nil , nil , false
}
continue
}
2024-02-25 12:26:43 +01:00
v := gconstant . ToFloat ( arg . Const )
if v . Kind ( ) == gconstant . Unknown {
cs . addError ( e . Pos ( ) , fmt . Sprintf ( "cannot convert %s to type float" , arg . Const . String ( ) ) )
return nil , nil , nil , false
}
args [ i ] . Const = gconstant . ToFloat ( args [ i ] . Const )
argts [ i ] = shaderir . Type { Main : shaderir . Float }
}
}
2022-08-18 17:13:47 +02:00
}
2024-02-25 12:26:43 +01:00
if argts [ 0 ] . IsIntVector ( ) {
for i := 1 ; i < 3 ; i ++ {
if args [ i ] . Const != nil {
v := gconstant . ToInt ( args [ i ] . Const )
if v . Kind ( ) == gconstant . Unknown {
cs . addError ( e . Pos ( ) , fmt . Sprintf ( "cannot convert %s to type int" , args [ i ] . Const . String ( ) ) )
return nil , nil , nil , false
}
args [ i ] . Const = v
argts [ i ] = shaderir . Type { Main : shaderir . Int }
}
}
}
if argts [ 0 ] . IsFloatVector ( ) {
for i := 1 ; i < 3 ; i ++ {
if args [ i ] . Const != nil {
v := gconstant . ToFloat ( args [ i ] . Const )
if v . Kind ( ) == gconstant . Unknown {
cs . addError ( e . Pos ( ) , fmt . Sprintf ( "cannot convert %s to type float" , args [ i ] . Const . String ( ) ) )
return nil , nil , nil , false
}
args [ i ] . Const = v
argts [ i ] = shaderir . Type { Main : shaderir . Float }
}
}
}
default :
for i := range args {
// If the argument is a non-typed constant value, treat this as a float value (#1874).
if args [ i ] . Const != nil && argts [ i ] . Main == shaderir . None && gconstant . ToFloat ( args [ i ] . Const ) . Kind ( ) != gconstant . Unknown {
args [ i ] . Const = gconstant . ToFloat ( args [ i ] . Const )
argts [ i ] = shaderir . Type { Main : shaderir . Float }
}
}
}
for i := range args {
switch callee . BuiltinFunc {
case shaderir . Clamp :
if argts [ i ] . Main != shaderir . Float && ! argts [ i ] . IsFloatVector ( ) && argts [ i ] . Main != shaderir . Int && ! argts [ i ] . IsIntVector ( ) {
cs . addError ( e . Pos ( ) , fmt . Sprintf ( "cannot use %s as float, vecN, int, or ivecN value in argument to %s" , argts [ i ] . String ( ) , callee . BuiltinFunc ) )
return nil , nil , nil , false
}
default :
if argts [ i ] . Main != shaderir . Float && ! argts [ i ] . IsFloatVector ( ) {
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 17:13:47 +02:00
}
}
2022-08-18 18:16:11 +02:00
switch callee . BuiltinFunc {
case shaderir . Clamp :
2024-02-25 12:26:43 +01:00
if ! ( ( argts [ 0 ] . Equal ( & argts [ 1 ] ) && argts [ 0 ] . Equal ( & argts [ 2 ] ) ) ||
( argts [ 0 ] . IsFloatVector ( ) && argts [ 1 ] . Main == shaderir . Float && argts [ 2 ] . Main == shaderir . Float ) ||
( argts [ 0 ] . IsIntVector ( ) && argts [ 1 ] . Main == shaderir . Int && argts [ 2 ] . Main == shaderir . Int ) ) {
cs . addError ( e . Pos ( ) , fmt . Sprintf ( "the second and the third arguments for %s must equal to the first argument %s or float or int but %s and %s" , callee . BuiltinFunc , argts [ 0 ] . String ( ) , argts [ 1 ] . String ( ) , argts [ 2 ] . String ( ) ) )
2022-08-18 18:16:11 +02:00
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 :
2024-03-10 11:14:05 +01:00
finalType = argts [ 2 ]
2022-08-18 18:26:57 +02:00
default :
2024-03-10 11:14:05 +01:00
finalType = argts [ 0 ]
2022-08-18 18:26:57 +02:00
}
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
}
2024-02-25 10:32:29 +01:00
switch callee . BuiltinFunc {
case shaderir . Min , shaderir . Max :
2024-03-10 11:14:05 +01:00
if kind , _ := resolveConstKind ( args , argts ) ; kind != gconstant . Unknown {
2024-02-25 13:11:36 +01:00
switch kind {
case gconstant . Int :
for i , arg := range args {
2024-03-10 11:14:05 +01:00
if arg . Const == nil {
if argts [ i ] . Main != shaderir . Int {
cs . addError ( e . Pos ( ) , fmt . Sprintf ( "%s's arguments don't match: %s and %s" , callee . BuiltinFunc , argts [ 0 ] . String ( ) , argts [ 1 ] . String ( ) ) )
return nil , nil , nil , false
}
continue
}
2024-02-25 13:11:36 +01:00
v := gconstant . ToInt ( arg . Const )
if v . Kind ( ) == gconstant . Unknown {
cs . addError ( e . Pos ( ) , fmt . Sprintf ( "cannot convert %s to type int" , arg . Const . String ( ) ) )
return nil , nil , nil , false
}
args [ i ] . Const = gconstant . ToInt ( args [ i ] . Const )
argts [ i ] = shaderir . Type { Main : shaderir . Int }
}
case gconstant . Float :
for i , arg := range args {
2024-03-10 11:14:05 +01:00
if arg . Const == nil {
if argts [ i ] . Main != shaderir . Float {
cs . addError ( e . Pos ( ) , fmt . Sprintf ( "%s's arguments don't match: %s and %s" , callee . BuiltinFunc , argts [ 0 ] . String ( ) , argts [ 1 ] . String ( ) ) )
return nil , nil , nil , false
}
continue
}
2024-02-25 13:11:36 +01:00
v := gconstant . ToFloat ( arg . Const )
if v . Kind ( ) == gconstant . Unknown {
cs . addError ( e . Pos ( ) , fmt . Sprintf ( "cannot convert %s to type float" , arg . Const . String ( ) ) )
return nil , nil , nil , false
}
args [ i ] . Const = gconstant . ToFloat ( args [ i ] . Const )
argts [ i ] = shaderir . Type { Main : shaderir . Float }
}
2024-02-25 10:32:29 +01:00
}
2022-08-18 10:30:12 +02:00
}
2024-02-25 13:11:36 +01:00
2024-02-25 10:32:29 +01:00
if argts [ 0 ] . IsIntVector ( ) && args [ 1 ] . Const != nil {
v := gconstant . ToInt ( args [ 1 ] . Const )
if v . Kind ( ) == gconstant . Unknown {
cs . addError ( e . Pos ( ) , fmt . Sprintf ( "cannot convert %s to type int" , args [ 1 ] . Const . String ( ) ) )
return nil , nil , nil , false
}
args [ 1 ] . Const = v
argts [ 1 ] = shaderir . Type { Main : shaderir . Int }
}
if argts [ 0 ] . IsFloatVector ( ) && args [ 1 ] . Const != nil {
v := gconstant . ToFloat ( args [ 1 ] . Const )
if v . Kind ( ) == gconstant . Unknown {
cs . addError ( e . Pos ( ) , fmt . Sprintf ( "cannot convert %s to type float" , args [ 1 ] . Const . String ( ) ) )
return nil , nil , nil , false
}
args [ 1 ] . Const = v
argts [ 1 ] = shaderir . Type { Main : shaderir . Float }
}
default :
for i := range args {
if args [ i ] . Const != nil && argts [ i ] . Main == shaderir . None {
// If the argument is a non-typed constant value, treat this as a float value (#1874).
if gconstant . ToFloat ( args [ i ] . Const ) . Kind ( ) != gconstant . Unknown {
args [ i ] . Const = gconstant . ToFloat ( args [ i ] . Const )
argts [ i ] = shaderir . Type { Main : shaderir . Float }
}
}
}
}
for i := range args {
switch callee . BuiltinFunc {
case shaderir . Min , shaderir . Max :
if argts [ i ] . Main != shaderir . Float && ! argts [ i ] . IsFloatVector ( ) && argts [ i ] . Main != shaderir . Int && ! argts [ i ] . IsIntVector ( ) {
cs . addError ( e . Pos ( ) , fmt . Sprintf ( "cannot use %s as float, vecN, int, or ivecN value in argument to %s" , argts [ i ] . String ( ) , callee . BuiltinFunc ) )
return nil , nil , nil , false
}
default :
if argts [ i ] . Main != shaderir . Float && ! argts [ i ] . IsFloatVector ( ) {
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 10:30:12 +02:00
}
}
2022-08-18 16:44:58 +02:00
switch callee . BuiltinFunc {
2024-02-25 10:32:29 +01:00
case shaderir . Mod :
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
}
2024-02-25 10:32:29 +01:00
case shaderir . Min , shaderir . Max :
if ! ( argts [ 0 ] . Equal ( & argts [ 1 ] ) || ( argts [ 0 ] . IsFloatVector ( ) && argts [ 1 ] . Main == shaderir . Float ) || ( argts [ 0 ] . IsIntVector ( ) && argts [ 1 ] . Main == shaderir . Int ) ) {
cs . addError ( e . Pos ( ) , fmt . Sprintf ( "the second argument for %s must equal to the first argument %s or float or int 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 :
2024-03-10 11:14:05 +01:00
finalType = shaderir . Type { Main : shaderir . Float }
2022-08-18 17:24:59 +02:00
case shaderir . Step :
2024-03-10 11:14:05 +01:00
finalType = argts [ 1 ]
2022-08-18 17:24:59 +02:00
default :
2024-03-10 11:14:05 +01:00
finalType = argts [ 0 ]
2022-08-18 10:30:12 +02:00
}
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
}
2024-02-25 10:16:42 +01:00
if args [ 0 ] . Const != nil && argts [ 0 ] . Main == shaderir . None {
switch callee . BuiltinFunc {
case shaderir . Abs , shaderir . Sign :
if args [ 0 ] . Const . Kind ( ) == gconstant . Int {
argts [ 0 ] = shaderir . Type { Main : shaderir . Int }
}
if args [ 0 ] . Const . Kind ( ) == gconstant . Float {
argts [ 0 ] = shaderir . Type { Main : shaderir . Float }
}
default :
// If the argument is a non-typed constant value, treat this as a float value (#1874).
if gconstant . ToFloat ( args [ 0 ] . Const ) . Kind ( ) != gconstant . Unknown {
args [ 0 ] . Const = gconstant . ToFloat ( args [ 0 ] . Const )
argts [ 0 ] = shaderir . Type { Main : shaderir . Float }
}
}
2021-11-14 08:24:26 +01:00
}
2022-08-18 18:57:13 +02:00
switch callee . BuiltinFunc {
case shaderir . Transpose :
2024-02-25 10:16:42 +01:00
if ! argts [ 0 ] . IsMatrix ( ) {
2022-08-18 18:57:13 +02:00
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
}
2024-02-25 10:16:42 +01:00
case shaderir . Abs , shaderir . Sign :
if argts [ 0 ] . Main != shaderir . Float && ! argts [ 0 ] . IsFloatVector ( ) && argts [ 0 ] . Main != shaderir . Int && ! argts [ 0 ] . IsIntVector ( ) {
cs . addError ( e . Pos ( ) , fmt . Sprintf ( "cannot use %s as float, vecN, int, or ivenN value in argument to %s" , argts [ 0 ] . String ( ) , callee . BuiltinFunc ) )
return nil , nil , nil , false
}
2022-08-18 18:57:13 +02:00
default :
2024-02-25 10:16:42 +01:00
if argts [ 0 ] . Main != shaderir . Float && ! argts [ 0 ] . IsFloatVector ( ) {
2022-08-18 18:57:13 +02:00
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 {
2024-03-10 11:14:05 +01:00
finalType = shaderir . Type { Main : shaderir . Float }
2022-08-18 10:30:12 +02:00
} else {
2024-03-10 11:14:05 +01:00
finalType = argts [ 0 ]
2022-08-18 10:30:12 +02:00
}
2020-07-04 16:47:23 +02:00
}
return [ ] shaderir . Expr {
{
Type : shaderir . Call ,
Exprs : append ( [ ] shaderir . Expr { callee } , args ... ) ,
} ,
2024-03-10 11:14:05 +01:00
} , [ ] shaderir . Type { finalType } , stmts , true
2020-07-04 16:47:23 +02:00
}
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-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
}
2023-07-27 16:00:55 +02:00
if args [ i ] . Const != nil {
switch p . Main {
case shaderir . Int :
args [ i ] . Const = gconstant . ToInt ( args [ i ] . Const )
argts [ i ] = shaderir . Type { Main : shaderir . Int }
case shaderir . Float :
args [ i ] . Const = gconstant . ToFloat ( args [ i ] . Const )
argts [ i ] = shaderir . Type { Main : shaderir . Float }
}
}
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
}
2023-12-17 14:42:34 +01:00
// Even if the function doesn't return anything, calling the function should be done earlier to keep
2020-07-04 16:47:23 +02:00
// 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 {
2023-07-05 09:36:29 +02:00
cs . addError ( e . Pos ( ) , "cannot use _ as value" )
2020-09-13 16:48:12 +02:00
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 {
{
2023-07-29 07:10:31 +02:00
Type : shaderir . NumberExpr ,
Const : c . value ,
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
2024-03-13 03:34:57 +01:00
if len ( types ) == 0 || ! isValidSwizzling ( e . Sel . Name , types [ 0 ] ) {
2022-11-19 14:37:31 +01:00
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 :
2023-07-23 19:19:42 +02:00
exprs , ts , 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
}
2024-03-13 03:34:57 +01:00
if len ( ts ) == 0 {
cs . addError ( e . Pos ( ) , fmt . Sprintf ( "unexpected unary operator: %s" , e . X ) )
return nil , nil , nil , false
}
2020-07-04 16:47:23 +02:00
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 )
2023-07-23 19:19:42 +02:00
// Use the original type as it is.
// Keep the type untyped if the original expression is untyped (#2705).
2020-07-04 16:47:23 +02:00
return [ ] shaderir . Expr {
{
Type : shaderir . NumberExpr ,
Const : v ,
} ,
2023-07-23 19:19:42 +02:00
} , ts [ : 1 ] , stmts , true
2020-07-04 16:47:23 +02:00
}
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 ,
} ,
2023-07-23 19:19:42 +02:00
} , ts [ : 1 ] , 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 {
2023-07-05 09:36:29 +02:00
cs . addError ( e . Pos ( ) , "multiple-value context is not available at a composite literal" )
2020-07-29 16:25:16 +02:00
return nil , nil , nil , false
}
2023-11-14 16:36:35 +01:00
expr := exprs [ 0 ]
if expr . Const != nil {
switch t . Sub [ 0 ] . Main {
case shaderir . Bool :
if expr . Const . Kind ( ) != gconstant . Bool {
cs . addError ( e . Pos ( ) , fmt . Sprintf ( "cannot %s to type bool" , expr . Const . String ( ) ) )
}
case shaderir . Int :
if ! canTruncateToInteger ( expr . Const ) {
cs . addError ( e . Pos ( ) , fmt . Sprintf ( "constant %s truncated to integer" , expr . Const . String ( ) ) )
return nil , nil , nil , false
}
expr . Const = gconstant . ToInt ( expr . Const )
case shaderir . Float :
if ! canTruncateToFloat ( expr . Const ) {
cs . addError ( e . Pos ( ) , fmt . Sprintf ( "constant %s truncated to float" , expr . Const . String ( ) ) )
return nil , nil , nil , false
}
expr . Const = gconstant . ToFloat ( expr . Const )
default :
cs . addError ( e . Pos ( ) , fmt . Sprintf ( "constant %s cannot be used for the array type %s" , expr . Const . String ( ) , t . String ( ) ) )
return nil , nil , nil , false
}
}
2020-07-29 16:25:16 +02:00
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 ,
} ,
{
2023-07-29 07:10:31 +02:00
Type : shaderir . NumberExpr ,
Const : gconstant . MakeInt64 ( int64 ( i ) ) ,
2020-07-29 16:25:16 +02:00
} ,
} ,
} ,
2023-11-14 16:36:35 +01:00
expr ,
2020-07-29 16:25:16 +02:00
} ,
} )
}
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
2023-11-19 06:50:40 +01:00
exprs , _ , ss , ok := cs . parseExpr ( block , fname , e . Index , true )
2020-07-29 16:49:39 +02:00
if ! ok {
return nil , nil , nil , false
}
stmts = append ( stmts , ss ... )
if len ( exprs ) != 1 {
2023-07-05 09:36:29 +02:00
cs . addError ( e . Pos ( ) , "multiple-value context is not available at an index expression" )
2020-07-29 16:49:39 +02:00
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-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 {
2023-07-05 09:36:29 +02:00
cs . addError ( e . Pos ( ) , "multiple-value context is not available at an index expression" )
2020-07-29 16:49:39 +02:00
return nil , nil , nil , false
}
2024-03-13 03:34:57 +01:00
if len ( ts ) == 0 {
cs . addError ( e . Pos ( ) , "unexpected index expression" )
return nil , nil , nil , false
}
2020-07-29 16:49:39 +02:00
x := exprs [ 0 ]
t := ts [ 0 ]
2024-08-03 12:03:40 +02:00
if ( t . IsFloatVector ( ) || t . IsIntVector ( ) ) && idx . Const == nil {
cs . addError ( e . Pos ( ) , fmt . Sprintf ( "index must be a constant for the type %s" , t . String ( ) ) )
return nil , nil , nil , false
}
2020-07-29 16:49:39 +02:00
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
}
}
2024-02-25 13:11:36 +01:00
func resolveConstKind ( exprs [ ] shaderir . Expr , ts [ ] shaderir . Type ) ( kind gconstant . Kind , allConsts bool ) {
if len ( exprs ) != len ( ts ) {
panic ( "not reached" )
}
2024-03-10 11:14:05 +01:00
allConsts = true
2024-02-25 13:11:36 +01:00
for _ , expr := range exprs {
if expr . Const == nil {
2024-03-10 11:14:05 +01:00
allConsts = false
}
}
if ! allConsts {
for _ , t := range ts {
if t . Main == shaderir . None {
continue
}
if t . Main == shaderir . Float {
return gconstant . Float , false
}
if t . Main == shaderir . Int {
return gconstant . Int , false
}
2024-02-25 13:11:36 +01:00
return gconstant . Unknown , false
}
}
kind = gconstant . Unknown
for _ , t := range ts {
switch t . Main {
case shaderir . None :
continue
case shaderir . Int :
switch kind {
case gconstant . Unknown :
kind = gconstant . Int
case gconstant . Int :
case gconstant . Float :
return gconstant . Unknown , true
}
case shaderir . Float :
switch kind {
case gconstant . Unknown :
kind = gconstant . Float
case gconstant . Int :
return gconstant . Unknown , true
case gconstant . Float :
}
}
}
2024-03-10 11:14:05 +01:00
if kind != gconstant . Unknown {
return kind , true
2024-03-10 03:12:20 +01:00
}
2024-02-25 13:11:36 +01:00
// Prefer floats over integers for non-typed constant values.
// For example, max(1.0, 1) should return a float value.
2024-03-10 11:14:05 +01:00
for _ , expr := range exprs {
if expr . Const . Kind ( ) == gconstant . Float {
return gconstant . Float , true
2024-02-25 13:11:36 +01:00
}
}
return gconstant . Int , true
}