2020-05-08 17:46:01 +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"
"go/token"
2020-05-31 11:11:55 +02:00
"strconv"
2020-05-08 17:46:01 +02:00
"strings"
2020-05-30 10:03:43 +02:00
"github.com/hajimehoshi/ebiten/internal/shaderir"
2020-05-08 17:46:01 +02:00
)
2020-05-08 21:21:45 +02:00
type variable struct {
2020-05-10 17:13:08 +02:00
name string
2020-06-07 12:36:38 +02:00
typ shaderir . Type
2020-05-09 17:39:26 +02:00
}
type constant struct {
2020-05-10 17:13:08 +02:00
name string
2020-06-07 12:36:38 +02:00
typ shaderir . Type
2020-05-10 17:13:08 +02:00
init ast . Expr
2020-05-08 17:46:01 +02:00
}
2020-05-09 11:05:30 +02:00
type function struct {
2020-05-30 19:23:51 +02:00
name string
block * block
2020-05-31 11:01:12 +02:00
ir shaderir . Func
2020-05-09 11:05:30 +02:00
}
2020-05-30 10:03:43 +02:00
type compileState struct {
2020-05-09 09:47:07 +02:00
fs * token . FileSet
2020-06-04 18:11:39 +02:00
vertexEntry string
fragmentEntry string
2020-05-30 18:56:07 +02:00
ir shaderir . Program
2020-05-30 10:03:43 +02:00
2020-05-30 18:56:07 +02:00
// uniforms is a collection of uniform variable names.
uniforms [ ] string
2020-05-08 20:38:37 +02:00
2020-06-06 17:49:28 +02:00
funcs [ ] function
2020-05-09 17:59:18 +02:00
global block
2020-05-09 11:05:30 +02:00
2020-06-03 16:56:08 +02:00
varyingParsed bool
2020-05-08 17:46:01 +02:00
errs [ ] string
}
2020-06-06 17:49:28 +02:00
func ( cs * compileState ) findFunction ( name string ) ( int , bool ) {
for i , f := range cs . funcs {
if f . name == name {
return i , true
}
}
return 0 , false
}
2020-06-02 15:45:44 +02:00
func ( cs * compileState ) findUniformVariable ( name string ) ( int , bool ) {
for i , u := range cs . uniforms {
if u == name {
return i , true
}
}
return 0 , false
}
2020-06-07 12:36:38 +02:00
type typ struct {
name string
ir shaderir . Type
}
2020-05-30 19:23:51 +02:00
type block struct {
types [ ] typ
vars [ ] variable
consts [ ] constant
pos token . Pos
outer * block
2020-05-31 11:01:12 +02:00
ir shaderir . Block
2020-05-30 19:23:51 +02:00
}
2020-06-18 19:37:03 +02:00
func ( b * block ) findLocalVariable ( name string ) ( int , shaderir . Type , bool ) {
2020-05-31 16:57:03 +02:00
idx := 0
for outer := b . outer ; outer != nil ; outer = outer . outer {
idx += len ( outer . vars )
}
for i , v := range b . vars {
if v . name == name {
2020-06-18 19:37:03 +02:00
return idx + i , v . typ , true
2020-05-31 16:57:03 +02:00
}
}
if b . outer != nil {
return b . outer . findLocalVariable ( name )
}
2020-06-18 19:37:03 +02:00
return 0 , shaderir . Type { } , false
2020-05-31 16:57:03 +02:00
}
2020-05-08 17:46:01 +02:00
type ParseError struct {
errs [ ] string
}
func ( p * ParseError ) Error ( ) string {
return strings . Join ( p . errs , "\n" )
}
2020-06-06 16:11:59 +02:00
func Compile ( fs * token . FileSet , f * ast . File , vertexEntry , fragmentEntry string ) ( * shaderir . Program , error ) {
2020-05-30 10:03:43 +02:00
s := & compileState {
2020-06-04 18:11:39 +02:00
fs : fs ,
vertexEntry : vertexEntry ,
fragmentEntry : fragmentEntry ,
2020-05-09 09:47:07 +02:00
}
2020-05-08 17:46:01 +02:00
s . parse ( f )
if len ( s . errs ) > 0 {
return nil , & ParseError { s . errs }
}
2020-05-09 16:38:52 +02:00
// TODO: Resolve identifiers?
// TODO: Resolve constants
2020-05-08 17:46:01 +02:00
// TODO: Make a call graph and reorder the elements.
2020-05-30 18:56:07 +02:00
return & s . ir , nil
2020-05-08 17:46:01 +02:00
}
2020-05-30 10:03:43 +02:00
func ( s * compileState ) addError ( pos token . Pos , str string ) {
2020-05-09 09:47:07 +02:00
p := s . fs . Position ( pos )
s . errs = append ( s . errs , fmt . Sprintf ( "%s: %s" , p , str ) )
2020-05-08 20:38:37 +02:00
}
2020-05-30 10:03:43 +02:00
func ( cs * compileState ) parse ( f * ast . File ) {
2020-06-03 15:30:34 +02:00
// Parse GenDecl for global variables, and then parse functions.
2020-05-09 10:32:10 +02:00
for _ , d := range f . Decls {
2020-06-03 15:30:34 +02:00
if _ , ok := d . ( * ast . FuncDecl ) ; ! ok {
cs . parseDecl ( & cs . global , d )
}
}
2020-06-04 20:00:43 +02:00
// Sort the uniform variable so that special variable starting with __ should come first.
var unames [ ] string
var utypes [ ] shaderir . Type
for i , u := range cs . uniforms {
if strings . HasPrefix ( u , "__" ) {
unames = append ( unames , u )
utypes = append ( utypes , cs . ir . Uniforms [ i ] )
}
}
for i , u := range cs . uniforms {
if ! strings . HasPrefix ( u , "__" ) {
unames = append ( unames , u )
utypes = append ( utypes , cs . ir . Uniforms [ i ] )
}
}
cs . uniforms = unames
cs . ir . Uniforms = utypes
2020-06-06 17:49:28 +02:00
// Parse function names so that any other function call the others.
// The function data is provisional and will be updated soon.
for _ , d := range f . Decls {
fd , ok := d . ( * ast . FuncDecl )
if ! ok {
continue
}
n := fd . Name . Name
if n == cs . vertexEntry {
continue
}
if n == cs . fragmentEntry {
continue
}
inParams , outParams := cs . parseFuncParams ( fd )
var inT , outT [ ] shaderir . Type
for _ , v := range inParams {
inT = append ( inT , v . typ )
}
for _ , v := range outParams {
outT = append ( outT , v . typ )
}
cs . funcs = append ( cs . funcs , function {
name : n ,
ir : shaderir . Func {
Index : len ( cs . funcs ) ,
InParams : inT ,
OutParams : outT ,
} ,
} )
}
2020-06-04 20:00:43 +02:00
// Parse functions.
2020-06-03 15:30:34 +02:00
for _ , d := range f . Decls {
if _ , ok := d . ( * ast . FuncDecl ) ; ok {
cs . parseDecl ( & cs . global , d )
}
2020-05-09 17:59:18 +02:00
}
2020-05-31 11:01:12 +02:00
if len ( cs . errs ) > 0 {
return
}
2020-06-06 17:49:28 +02:00
for _ , f := range cs . funcs {
2020-05-31 11:01:12 +02:00
cs . ir . Funcs = append ( cs . ir . Funcs , f . ir )
}
2020-05-09 17:59:18 +02:00
}
2020-05-31 16:57:03 +02:00
func ( cs * compileState ) parseDecl ( b * block , d ast . Decl ) {
2020-05-09 17:59:18 +02:00
switch d := d . ( type ) {
case * ast . GenDecl :
switch d . Tok {
case token . TYPE :
2020-05-10 16:19:39 +02:00
// TODO: Parse other types
2020-05-09 17:59:18 +02:00
for _ , s := range d . Specs {
s := s . ( * ast . TypeSpec )
2020-05-30 10:03:43 +02:00
t := cs . parseType ( s . Type )
2020-06-07 12:36:38 +02:00
b . types = append ( b . types , typ {
name : s . Name . Name ,
ir : t ,
} )
2020-05-08 20:38:37 +02:00
}
2020-05-09 17:59:18 +02:00
case token . CONST :
for _ , s := range d . Specs {
s := s . ( * ast . ValueSpec )
2020-05-30 10:03:43 +02:00
cs := cs . parseConstant ( s )
2020-05-09 17:59:18 +02:00
b . consts = append ( b . consts , cs ... )
}
case token . VAR :
for _ , s := range d . Specs {
s := s . ( * ast . ValueSpec )
2020-06-07 10:50:28 +02:00
vs , inits , stmts := cs . parseVariable ( b , s )
b . ir . Stmts = append ( b . ir . Stmts , stmts ... )
2020-05-31 16:57:03 +02:00
if b == & cs . global {
2020-06-07 09:21:02 +02:00
// TODO: Should rhs be ignored?
2020-05-31 16:57:03 +02:00
for i , v := range vs {
2020-06-04 20:00:43 +02:00
if ! strings . HasPrefix ( v . name , "__" ) {
if v . name [ 0 ] < 'A' || 'Z' < v . name [ 0 ] {
cs . addError ( s . Names [ i ] . Pos ( ) , fmt . Sprintf ( "global variables must be exposed: %s" , v . name ) )
}
2020-05-31 16:57:03 +02:00
}
cs . uniforms = append ( cs . uniforms , v . name )
2020-06-07 12:36:38 +02:00
cs . ir . Uniforms = append ( cs . ir . Uniforms , v . typ )
2020-05-31 16:57:03 +02:00
}
2020-05-10 14:57:12 +02:00
continue
}
2020-06-07 16:23:58 +02:00
base := len ( b . vars )
b . vars = append ( b . vars , vs ... )
if len ( inits ) > 0 {
for i := range vs {
2020-06-07 09:21:02 +02:00
b . ir . Stmts = append ( b . ir . Stmts , shaderir . Stmt {
Type : shaderir . Assign ,
Exprs : [ ] shaderir . Expr {
{
Type : shaderir . LocalVariable ,
2020-06-07 16:23:58 +02:00
Index : base + i ,
2020-06-07 09:21:02 +02:00
} ,
2020-06-07 16:23:58 +02:00
inits [ i ] ,
2020-06-07 09:21:02 +02:00
} ,
} )
}
2020-05-10 14:57:12 +02:00
}
2020-05-09 17:59:18 +02:00
}
case token . IMPORT :
2020-05-30 10:03:43 +02:00
cs . addError ( d . Pos ( ) , "import is forbidden" )
2020-05-09 10:32:10 +02:00
default :
2020-05-30 10:03:43 +02:00
cs . addError ( d . Pos ( ) , "unexpected token" )
2020-05-08 17:46:01 +02:00
}
2020-05-09 17:59:18 +02:00
case * ast . FuncDecl :
2020-06-02 15:45:44 +02:00
f := cs . parseFunc ( b , d )
2020-06-06 17:49:28 +02:00
if b != & cs . global {
cs . addError ( d . Pos ( ) , "non-global function is not implemented" )
return
}
switch d . Name . Name {
case cs . vertexEntry :
cs . ir . VertexFunc . Block = f . ir . Block
case cs . fragmentEntry :
cs . ir . FragmentFunc . Block = f . ir . Block
default :
// The function is already registered for their names.
for i := range cs . funcs {
if cs . funcs [ i ] . name == d . Name . Name {
// Index is already determined by the provisional parsing.
f . ir . Index = cs . funcs [ i ] . ir . Index
cs . funcs [ i ] = f
break
}
2020-06-03 16:56:08 +02:00
}
2020-06-02 15:45:44 +02:00
}
2020-05-09 17:59:18 +02:00
default :
2020-05-30 10:03:43 +02:00
cs . addError ( d . Pos ( ) , "unexpected decl" )
2020-05-08 17:46:01 +02:00
}
}
2020-06-19 17:20:17 +02:00
// functionReturnTypes returns the original returning value types, if the given expression is call.
//
// Note that parseExpr returns the returning types for IR, not the original function.
func ( cs * compileState ) functionReturnTypes ( block * block , expr ast . Expr ) ( [ ] shaderir . Type , bool ) {
call , ok := expr . ( * ast . CallExpr )
if ! ok {
return nil , false
}
ident , ok := call . Fun . ( * ast . Ident )
if ! ok {
return nil , false
}
for _ , f := range cs . funcs {
if f . name == ident . Name {
// TODO: Is it correct to combine out-params and return param?
ts := f . ir . OutParams
if f . ir . Return . Main != shaderir . None {
ts = append ( ts , f . ir . Return )
}
return ts , true
}
}
return nil , false
}
2020-06-07 16:23:58 +02:00
func ( s * compileState ) parseVariable ( block * block , vs * ast . ValueSpec ) ( [ ] variable , [ ] shaderir . Expr , [ ] shaderir . Stmt ) {
if len ( vs . Names ) != len ( vs . Values ) && len ( vs . Values ) != 1 && len ( vs . Values ) != 0 {
s . addError ( vs . Pos ( ) , fmt . Sprintf ( "the numbers of lhs and rhs don't match" ) )
return nil , nil , nil
}
2020-06-19 17:20:17 +02:00
var declt shaderir . Type
2020-05-09 16:38:52 +02:00
if vs . Type != nil {
2020-06-19 17:20:17 +02:00
declt = s . parseType ( vs . Type )
2020-05-08 20:38:37 +02:00
}
2020-05-09 16:38:52 +02:00
2020-06-07 16:23:58 +02:00
var (
vars [ ] variable
inits [ ] shaderir . Expr
stmts [ ] shaderir . Stmt
)
2020-05-09 19:01:28 +02:00
for i , n := range vs . Names {
2020-06-19 17:20:17 +02:00
// TODO: Reduce calls of parseExpr
2020-05-09 19:01:28 +02:00
var init ast . Expr
2020-06-19 17:20:17 +02:00
t := declt
2020-06-07 16:23:58 +02:00
switch len ( vs . Values ) {
case 0 :
case 1 :
init = vs . Values [ 0 ]
if t . Main == shaderir . None {
2020-06-19 17:20:17 +02:00
ts , ok := s . functionReturnTypes ( block , init )
if ! ok {
_ , ts , _ = s . parseExpr ( block , init )
}
2020-06-07 16:23:58 +02:00
if len ( ts ) != len ( vs . Names ) {
s . addError ( vs . Pos ( ) , fmt . Sprintf ( "the numbers of lhs and rhs don't match" ) )
continue
}
t = ts [ i ]
}
default :
2020-05-09 19:01:28 +02:00
init = vs . Values [ i ]
2020-06-07 12:36:38 +02:00
if t . Main == shaderir . None {
2020-06-19 17:20:17 +02:00
ts , ok := s . functionReturnTypes ( block , init )
if ! ok {
_ , ts , _ = s . parseExpr ( block , init )
}
2020-06-07 16:32:50 +02:00
if len ( ts ) > 1 {
s . addError ( vs . Pos ( ) , fmt . Sprintf ( "the numbers of lhs and rhs don't match" ) )
}
t = ts [ 0 ]
2020-05-10 12:32:40 +02:00
}
2020-05-09 19:01:28 +02:00
}
2020-06-07 16:23:58 +02:00
2020-05-09 10:32:10 +02:00
name := n . Name
2020-05-09 16:38:52 +02:00
vars = append ( vars , variable {
2020-05-10 17:13:08 +02:00
name : name ,
typ : t ,
2020-05-09 16:38:52 +02:00
} )
2020-06-07 09:21:02 +02:00
2020-06-07 16:23:58 +02:00
if len ( vs . Values ) > 1 || ( len ( vs . Values ) == 1 && len ( inits ) == 0 ) {
2020-06-18 19:37:03 +02:00
es , _ , ss := s . parseExpr ( block , init )
2020-06-07 16:23:58 +02:00
inits = append ( inits , es ... )
2020-06-07 10:50:28 +02:00
stmts = append ( stmts , ss ... )
2020-06-07 09:21:02 +02:00
}
2020-05-08 21:21:45 +02:00
}
2020-06-07 16:23:58 +02:00
if len ( inits ) > 0 && len ( vars ) != len ( inits ) {
s . addError ( vs . Pos ( ) , fmt . Sprintf ( "single-value context and multiple-value context cannot be mixed" ) )
return nil , nil , nil
}
2020-06-07 10:50:28 +02:00
return vars , inits , stmts
2020-05-08 21:21:45 +02:00
}
2020-05-30 10:03:43 +02:00
func ( s * compileState ) parseConstant ( vs * ast . ValueSpec ) [ ] constant {
2020-06-07 12:36:38 +02:00
var t shaderir . Type
2020-05-09 17:39:26 +02:00
if vs . Type != nil {
2020-05-10 16:56:56 +02:00
t = s . parseType ( vs . Type )
2020-05-08 21:21:45 +02:00
}
2020-05-09 17:39:26 +02:00
var cs [ ] constant
2020-05-09 10:32:10 +02:00
for i , n := range vs . Names {
2020-05-09 17:39:26 +02:00
cs = append ( cs , constant {
2020-05-10 17:13:08 +02:00
name : n . Name ,
typ : t ,
init : vs . Values [ i ] ,
2020-05-09 17:39:26 +02:00
} )
2020-05-08 20:38:37 +02:00
}
2020-05-09 17:39:26 +02:00
return cs
2020-05-08 17:46:01 +02:00
}
2020-06-06 17:49:28 +02:00
func ( cs * compileState ) parseFuncParams ( d * ast . FuncDecl ) ( in , out [ ] variable ) {
2020-05-09 12:21:01 +02:00
for _ , f := range d . Type . Params . List {
2020-05-30 10:03:43 +02:00
t := cs . parseType ( f . Type )
2020-05-09 12:21:01 +02:00
for _ , n := range f . Names {
2020-06-06 17:49:28 +02:00
in = append ( in , variable {
2020-05-31 11:11:55 +02:00
name : n . Name ,
typ : t ,
} )
2020-05-09 12:21:01 +02:00
}
}
2020-06-06 17:49:28 +02:00
if d . Type . Results == nil {
return
}
2020-06-02 15:45:44 +02:00
2020-06-06 17:49:28 +02:00
for _ , f := range d . Type . Results . List {
t := cs . parseType ( f . Type )
if len ( f . Names ) == 0 {
out = append ( out , variable {
name : "" ,
typ : t ,
} )
} else {
for _ , n := range f . Names {
out = append ( out , variable {
name : n . Name ,
2020-05-31 11:11:55 +02:00
typ : t ,
} )
2020-05-09 12:21:01 +02:00
}
}
}
2020-06-06 17:49:28 +02:00
return
}
func ( cs * compileState ) parseFunc ( block * block , d * ast . FuncDecl ) function {
if d . Name == nil {
cs . addError ( d . Pos ( ) , "function must have a name" )
return function { }
}
if d . Body == nil {
cs . addError ( d . Pos ( ) , "function must have a body" )
return function { }
}
2020-05-09 12:21:01 +02:00
2020-06-06 17:49:28 +02:00
inParams , outParams := cs . parseFuncParams ( d )
checkVaryings := func ( vs [ ] variable ) {
if len ( cs . ir . Varyings ) != len ( vs ) {
2020-06-03 16:56:08 +02:00
cs . addError ( d . Pos ( ) , fmt . Sprintf ( "the number of vertex entry point's returning values and the number of framgent entry point's params must be the same" ) )
return
2020-06-02 15:45:44 +02:00
}
2020-06-03 16:56:08 +02:00
for i , t := range cs . ir . Varyings {
2020-06-06 17:49:28 +02:00
if t . Main != vs [ i ] . typ . Main {
2020-06-03 16:56:08 +02:00
cs . addError ( d . Pos ( ) , fmt . Sprintf ( "vertex entry point's returning value types and framgent entry point's param types must match" ) )
}
2020-06-02 15:45:44 +02:00
}
2020-06-03 16:56:08 +02:00
}
2020-06-02 15:45:44 +02:00
2020-06-03 16:56:08 +02:00
if block == & cs . global {
switch d . Name . Name {
2020-06-04 18:11:39 +02:00
case cs . vertexEntry :
2020-06-06 17:49:28 +02:00
for _ , v := range inParams {
cs . ir . Attributes = append ( cs . ir . Attributes , v . typ )
2020-06-03 16:56:08 +02:00
}
2020-06-02 15:45:44 +02:00
2020-06-03 16:56:08 +02:00
// The first out-param is treated as gl_Position in GLSL.
if len ( outParams ) == 0 {
cs . addError ( d . Pos ( ) , fmt . Sprintf ( "vertex entry point must have at least one returning vec4 value for a position" ) )
return function { }
}
2020-06-06 17:49:28 +02:00
if outParams [ 0 ] . typ . Main != shaderir . Vec4 {
2020-06-03 16:56:08 +02:00
cs . addError ( d . Pos ( ) , fmt . Sprintf ( "vertex entry point must have at least one returning vec4 value for a position" ) )
return function { }
}
if cs . varyingParsed {
2020-06-06 17:49:28 +02:00
checkVaryings ( outParams [ 1 : ] )
2020-06-03 16:56:08 +02:00
} else {
2020-06-06 17:49:28 +02:00
for _ , v := range outParams [ 1 : ] {
2020-06-03 16:56:08 +02:00
// TODO: Check that these params are not arrays or structs
2020-06-06 17:49:28 +02:00
cs . ir . Varyings = append ( cs . ir . Varyings , v . typ )
2020-06-03 16:56:08 +02:00
}
}
cs . varyingParsed = true
2020-06-04 18:11:39 +02:00
case cs . fragmentEntry :
2020-06-03 16:56:08 +02:00
if len ( inParams ) == 0 {
cs . addError ( d . Pos ( ) , fmt . Sprintf ( "fragment entry point must have at least one vec4 parameter for a position" ) )
return function { }
}
2020-06-06 17:49:28 +02:00
if inParams [ 0 ] . typ . Main != shaderir . Vec4 {
2020-06-03 16:56:08 +02:00
cs . addError ( d . Pos ( ) , fmt . Sprintf ( "fragment entry point must have at least one vec4 parameter for a position" ) )
return function { }
}
if len ( outParams ) != 1 {
cs . addError ( d . Pos ( ) , fmt . Sprintf ( "fragment entry point must have one returning vec4 value for a color" ) )
return function { }
}
2020-06-06 17:49:28 +02:00
if outParams [ 0 ] . typ . Main != shaderir . Vec4 {
2020-06-03 16:57:45 +02:00
cs . addError ( d . Pos ( ) , fmt . Sprintf ( "fragment entry point must have one returning vec4 value for a color" ) )
2020-06-03 16:56:08 +02:00
return function { }
}
if cs . varyingParsed {
2020-06-06 17:49:28 +02:00
checkVaryings ( inParams [ 1 : ] )
2020-06-03 16:56:08 +02:00
} else {
2020-06-06 17:49:28 +02:00
for _ , v := range inParams [ 1 : ] {
cs . ir . Varyings = append ( cs . ir . Varyings , v . typ )
2020-06-03 16:56:08 +02:00
}
}
cs . varyingParsed = true
2020-06-02 15:45:44 +02:00
}
}
2020-05-31 12:20:53 +02:00
b := cs . parseBlock ( block , d . Body , inParams , outParams )
2020-05-31 09:20:36 +02:00
2020-06-06 17:49:28 +02:00
var inT , outT [ ] shaderir . Type
for _ , v := range inParams {
inT = append ( inT , v . typ )
}
for _ , v := range outParams {
outT = append ( outT , v . typ )
}
2020-05-09 17:59:18 +02:00
return function {
2020-05-31 11:01:12 +02:00
name : d . Name . Name ,
block : b ,
ir : shaderir . Func {
InParams : inT ,
OutParams : outT ,
Block : b . ir ,
} ,
2020-05-09 11:05:30 +02:00
}
}
2020-05-31 12:20:53 +02:00
func ( cs * compileState ) parseBlock ( outer * block , b * ast . BlockStmt , inParams , outParams [ ] variable ) * block {
vars := make ( [ ] variable , 0 , len ( inParams ) + len ( outParams ) )
vars = append ( vars , inParams ... )
vars = append ( vars , outParams ... )
2020-05-10 12:32:40 +02:00
block := & block {
2020-05-31 12:20:53 +02:00
vars : vars ,
2020-05-10 12:32:40 +02:00
outer : outer ,
}
2020-06-07 16:50:53 +02:00
defer func ( ) {
for _ , v := range block . vars [ len ( inParams ) + len ( outParams ) : ] {
block . ir . LocalVars = append ( block . ir . LocalVars , v . typ )
}
} ( )
2020-05-09 16:38:52 +02:00
for _ , l := range b . List {
switch l := l . ( type ) {
case * ast . AssignStmt :
2020-05-09 19:01:28 +02:00
switch l . Tok {
case token . DEFINE :
2020-06-08 04:57:54 +02:00
if len ( l . Lhs ) != len ( l . Rhs ) && len ( l . Rhs ) != 1 {
cs . addError ( l . Pos ( ) , fmt . Sprintf ( "single-value context and multiple-value context cannot be mixed" ) )
return nil
}
2020-06-19 17:20:17 +02:00
// TODO: Reduce calls of parseExpr
2020-06-08 04:57:54 +02:00
var rhsTypes [ ] shaderir . Type
2020-05-31 19:23:27 +02:00
for i , e := range l . Lhs {
2020-05-10 12:32:40 +02:00
v := variable {
2020-05-31 19:23:27 +02:00
name : e . ( * ast . Ident ) . Name ,
2020-05-10 12:32:40 +02:00
}
2020-06-08 04:57:54 +02:00
if len ( l . Lhs ) == len ( l . Rhs ) {
2020-06-19 17:20:17 +02:00
ts , ok := cs . functionReturnTypes ( block , l . Rhs [ i ] )
if ! ok {
_ , ts , _ = cs . parseExpr ( block , l . Rhs [ i ] )
}
2020-06-08 04:57:54 +02:00
if len ( ts ) > 1 {
cs . addError ( l . Pos ( ) , fmt . Sprintf ( "single-value context and multiple-value context cannot be mixed" ) )
}
2020-06-10 17:29:01 +02:00
if len ( ts ) == 1 {
v . typ = ts [ 0 ]
}
2020-06-08 04:57:54 +02:00
} else {
if i == 0 {
2020-06-19 17:20:17 +02:00
var ok bool
rhsTypes , ok = cs . functionReturnTypes ( block , l . Rhs [ 0 ] )
if ! ok {
_ , rhsTypes , _ = cs . parseExpr ( block , l . Rhs [ 0 ] )
}
2020-06-08 04:57:54 +02:00
if len ( rhsTypes ) != len ( l . Lhs ) {
cs . addError ( l . Pos ( ) , fmt . Sprintf ( "single-value context and multiple-value context cannot be mixed" ) )
}
}
v . typ = rhsTypes [ i ]
2020-06-07 16:32:50 +02:00
}
2020-05-10 12:32:40 +02:00
block . vars = append ( block . vars , v )
2020-06-08 04:57:54 +02:00
}
2020-06-07 10:50:28 +02:00
2020-06-10 15:25:40 +02:00
cs . assign ( block , l . Pos ( ) , l . Lhs , l . Rhs )
2020-05-09 19:01:28 +02:00
case token . ASSIGN :
// TODO: What about the statement `a,b = b,a?`
2020-06-10 15:25:40 +02:00
if len ( l . Lhs ) != len ( l . Rhs ) && len ( l . Rhs ) != 1 {
cs . addError ( l . Pos ( ) , fmt . Sprintf ( "single-value context and multiple-value context cannot be mixed" ) )
return nil
2020-05-09 19:01:28 +02:00
}
2020-06-10 15:25:40 +02:00
cs . assign ( block , l . Pos ( ) , l . Lhs , l . Rhs )
2020-06-10 17:29:01 +02:00
case token . ADD_ASSIGN , token . SUB_ASSIGN , token . MUL_ASSIGN , token . QUO_ASSIGN , token . REM_ASSIGN :
var op shaderir . Op
switch l . Tok {
case token . ADD_ASSIGN :
op = shaderir . Add
case token . SUB_ASSIGN :
op = shaderir . Sub
case token . MUL_ASSIGN :
op = shaderir . Mul
case token . QUO_ASSIGN :
op = shaderir . Div
case token . REM_ASSIGN :
op = shaderir . ModOp
}
2020-06-18 19:37:03 +02:00
rhs , _ , stmts := cs . parseExpr ( block , l . Rhs [ 0 ] )
2020-06-10 17:29:01 +02:00
block . ir . Stmts = append ( block . ir . Stmts , stmts ... )
2020-06-18 19:37:03 +02:00
lhs , _ , stmts := cs . parseExpr ( block , l . Lhs [ 0 ] )
2020-06-10 17:29:01 +02:00
block . ir . Stmts = append ( block . ir . Stmts , stmts ... )
block . ir . Stmts = append ( block . ir . Stmts , shaderir . Stmt {
Type : shaderir . Assign ,
Exprs : [ ] shaderir . Expr {
lhs [ 0 ] ,
{
Type : shaderir . Binary ,
Op : op ,
Exprs : [ ] shaderir . Expr {
lhs [ 0 ] ,
rhs [ 0 ] ,
} ,
} ,
} ,
} )
default :
cs . addError ( l . Pos ( ) , fmt . Sprintf ( "unexpected token: %s" , l . Tok ) )
2020-05-09 16:38:52 +02:00
}
2020-05-10 14:37:59 +02:00
case * ast . BlockStmt :
2020-05-31 16:57:03 +02:00
b := cs . parseBlock ( block , l , nil , nil )
block . ir . Stmts = append ( block . ir . Stmts , shaderir . Stmt {
Type : shaderir . BlockStmt ,
Blocks : [ ] shaderir . Block {
b . ir ,
} ,
} )
2020-05-09 16:38:52 +02:00
case * ast . DeclStmt :
2020-05-31 16:57:03 +02:00
cs . parseDecl ( block , l . Decl )
2020-05-09 16:38:52 +02:00
case * ast . ReturnStmt :
2020-05-31 12:20:53 +02:00
for i , r := range l . Results {
2020-06-18 19:37:03 +02:00
exprs , _ , stmts := cs . parseExpr ( block , r )
2020-06-07 10:50:28 +02:00
block . ir . Stmts = append ( block . ir . Stmts , stmts ... )
2020-06-07 17:51:20 +02:00
if len ( exprs ) == 0 {
continue
}
if len ( exprs ) > 1 {
cs . addError ( r . Pos ( ) , "multiple-context with return is not implemented yet" )
continue
}
2020-05-31 11:01:12 +02:00
block . ir . Stmts = append ( block . ir . Stmts , shaderir . Stmt {
Type : shaderir . Assign ,
Exprs : [ ] shaderir . Expr {
{
Type : shaderir . LocalVariable ,
2020-05-31 12:20:53 +02:00
Index : len ( inParams ) + i ,
2020-05-31 11:01:12 +02:00
} ,
2020-06-07 17:51:20 +02:00
exprs [ 0 ] ,
2020-05-31 11:01:12 +02:00
} ,
} )
2020-05-09 16:58:22 +02:00
}
2020-05-31 11:01:12 +02:00
block . ir . Stmts = append ( block . ir . Stmts , shaderir . Stmt {
Type : shaderir . Return ,
} )
2020-06-07 19:19:37 +02:00
case * ast . ExprStmt :
2020-06-18 19:37:03 +02:00
exprs , _ , stmts := cs . parseExpr ( block , l . X )
2020-06-07 19:19:37 +02:00
block . ir . Stmts = append ( block . ir . Stmts , stmts ... )
for _ , expr := range exprs {
if expr . Type != shaderir . Call {
continue
}
block . ir . Stmts = append ( block . ir . Stmts , shaderir . Stmt {
Type : shaderir . ExprStmt ,
Exprs : [ ] shaderir . Expr { expr } ,
} )
}
default :
cs . addError ( l . Pos ( ) , fmt . Sprintf ( "unexpected statement: %#v" , l ) )
2020-05-09 16:38:52 +02:00
}
}
return block
}
2020-06-10 15:25:40 +02:00
func ( cs * compileState ) assign ( block * block , pos token . Pos , lhs , rhs [ ] ast . Expr ) {
var rhsExprs [ ] shaderir . Expr
for i := range lhs {
// Prase RHS first for the order of the statements.
if len ( lhs ) == len ( rhs ) {
2020-06-18 19:37:03 +02:00
rhs , _ , stmts := cs . parseExpr ( block , rhs [ i ] )
2020-06-10 15:25:40 +02:00
if len ( rhs ) > 1 {
cs . addError ( pos , fmt . Sprintf ( "single-value context and multiple-value context cannot be mixed" ) )
}
block . ir . Stmts = append ( block . ir . Stmts , stmts ... )
2020-06-18 19:37:03 +02:00
lhs , _ , stmts := cs . parseExpr ( block , lhs [ i ] )
2020-06-10 15:25:40 +02:00
block . ir . Stmts = append ( block . ir . Stmts , stmts ... )
block . ir . Stmts = append ( block . ir . Stmts , shaderir . Stmt {
Type : shaderir . Assign ,
Exprs : [ ] shaderir . Expr { lhs [ 0 ] , rhs [ 0 ] } ,
} )
} else {
if i == 0 {
var stmts [ ] shaderir . Stmt
2020-06-18 19:37:03 +02:00
rhsExprs , _ , stmts = cs . parseExpr ( block , rhs [ 0 ] )
2020-06-10 15:25:40 +02:00
if len ( rhsExprs ) != len ( lhs ) {
cs . addError ( pos , fmt . Sprintf ( "single-value context and multiple-value context cannot be mixed" ) )
}
block . ir . Stmts = append ( block . ir . Stmts , stmts ... )
}
2020-06-18 19:37:03 +02:00
lhs , _ , stmts := cs . parseExpr ( block , lhs [ i ] )
2020-06-10 15:25:40 +02:00
block . ir . Stmts = append ( block . ir . Stmts , stmts ... )
block . ir . Stmts = append ( block . ir . Stmts , shaderir . Stmt {
Type : shaderir . Assign ,
Exprs : [ ] shaderir . Expr { lhs [ 0 ] , rhsExprs [ i ] } ,
} )
}
}
}
2020-06-18 19:37:03 +02:00
func ( cs * compileState ) parseExpr ( block * block , expr ast . Expr ) ( [ ] shaderir . Expr , [ ] shaderir . Type , [ ] shaderir . Stmt ) {
2020-05-31 11:01:12 +02:00
switch e := expr . ( type ) {
2020-05-31 11:11:55 +02:00
case * ast . BasicLit :
switch e . Kind {
case token . INT :
v , err := strconv . ParseInt ( e . Value , 10 , 32 )
if err != nil {
cs . addError ( e . Pos ( ) , fmt . Sprintf ( "unexpected literal: %s" , e . Value ) )
2020-06-18 19:37:03 +02:00
return nil , nil , nil
2020-05-31 11:11:55 +02:00
}
2020-06-07 17:51:20 +02:00
return [ ] shaderir . Expr {
{
Type : shaderir . IntExpr ,
Int : int32 ( v ) ,
} ,
2020-06-18 19:37:03 +02:00
} , [ ] shaderir . Type { { Main : shaderir . Int } } , nil
2020-05-31 11:11:55 +02:00
case token . FLOAT :
v , err := strconv . ParseFloat ( e . Value , 32 )
if err != nil {
cs . addError ( e . Pos ( ) , fmt . Sprintf ( "unexpected literal: %s" , e . Value ) )
2020-06-18 19:37:03 +02:00
return nil , nil , nil
2020-05-31 11:11:55 +02:00
}
2020-06-07 17:51:20 +02:00
return [ ] shaderir . Expr {
{
Type : shaderir . FloatExpr ,
Float : float32 ( v ) ,
} ,
2020-06-18 19:37:03 +02:00
} , [ ] shaderir . Type { { Main : shaderir . Float } } , nil
2020-05-31 11:11:55 +02:00
default :
cs . addError ( e . Pos ( ) , fmt . Sprintf ( "literal not implemented: %#v" , e ) )
}
2020-06-02 15:45:44 +02:00
case * ast . BinaryExpr :
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
case token . MUL :
op = shaderir . Mul
case token . QUO :
op = shaderir . Div
case token . REM :
op = shaderir . ModOp
case token . SHL :
op = shaderir . LeftShift
case token . SHR :
op = shaderir . RightShift
case token . LSS :
op = shaderir . LessThanOp
case token . LEQ :
op = shaderir . LessThanEqualOp
case token . GTR :
op = shaderir . GreaterThanOp
case token . GEQ :
op = shaderir . GreaterThanEqualOp
case token . EQL :
op = shaderir . EqualOp
case token . NEQ :
op = shaderir . NotEqualOp
case token . AND :
op = shaderir . And
case token . XOR :
op = shaderir . Xor
case token . OR :
op = shaderir . Or
case token . LAND :
op = shaderir . AndAnd
case token . LOR :
op = shaderir . OrOr
default :
cs . addError ( e . Pos ( ) , fmt . Sprintf ( "unexpected operator: %s" , e . Op ) )
2020-06-18 19:37:03 +02:00
return nil , nil , nil
2020-06-02 15:45:44 +02:00
}
2020-06-07 10:50:28 +02:00
var stmts [ ] shaderir . Stmt
// Prase RHS first for the order of the statements.
2020-06-18 19:37:03 +02:00
rhs , t0 , ss := cs . parseExpr ( block , e . Y )
2020-06-07 17:51:20 +02:00
if len ( rhs ) != 1 {
cs . addError ( e . Pos ( ) , fmt . Sprintf ( "multiple-value context is not available at a binary operator: %s" , e . Y ) )
2020-06-18 19:37:03 +02:00
return nil , nil , nil
2020-06-07 17:51:20 +02:00
}
2020-06-07 10:50:28 +02:00
stmts = append ( stmts , ss ... )
2020-06-07 17:51:20 +02:00
2020-06-18 19:37:03 +02:00
lhs , t1 , ss := cs . parseExpr ( block , e . X )
2020-06-07 17:51:20 +02:00
if len ( lhs ) != 1 {
cs . addError ( e . Pos ( ) , fmt . Sprintf ( "multiple-value context is not available at a binary operator: %s" , e . X ) )
2020-06-18 19:37:03 +02:00
return nil , nil , nil
2020-06-07 17:51:20 +02:00
}
2020-06-07 10:50:28 +02:00
stmts = append ( stmts , ss ... )
2020-06-18 19:37:03 +02:00
// TODO: Check the compatibility of t0 and t1
_ = t1
2020-06-07 17:51:20 +02:00
return [ ] shaderir . Expr {
{
Type : shaderir . Binary ,
Op : op ,
Exprs : [ ] shaderir . Expr { lhs [ 0 ] , rhs [ 0 ] } ,
} ,
2020-06-18 19:37:03 +02:00
} , t0 , stmts
2020-05-31 11:01:12 +02:00
case * ast . CallExpr :
2020-06-06 17:49:28 +02:00
var (
callee shaderir . Expr
args [ ] shaderir . Expr
2020-06-19 17:20:17 +02:00
argts [ ] shaderir . Type
2020-06-06 17:49:28 +02:00
stmts [ ] shaderir . Stmt
)
2020-06-07 10:50:28 +02:00
// Parse the argument first for the order of the statements.
2020-05-31 11:11:55 +02:00
for _ , a := range e . Args {
2020-06-19 17:20:17 +02:00
es , ts , ss := cs . parseExpr ( block , a )
2020-06-07 17:51:20 +02:00
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 ) )
2020-06-18 19:37:03 +02:00
return nil , nil , nil
2020-06-07 17:51:20 +02:00
}
args = append ( args , es ... )
2020-06-19 17:20:17 +02:00
argts = append ( argts , ts ... )
2020-06-07 10:50:28 +02:00
stmts = append ( stmts , ss ... )
2020-05-31 11:11:55 +02:00
}
2020-06-07 10:50:28 +02:00
2020-06-06 17:49:28 +02:00
// TODO: When len(ss) is not 0?
2020-06-18 19:37:03 +02:00
es , _ , ss := cs . parseExpr ( block , e . Fun )
2020-06-07 17:51:20 +02:00
if len ( es ) != 1 {
cs . addError ( e . Pos ( ) , fmt . Sprintf ( "multiple-value context is not available at a callee: %s" , e . Fun ) )
2020-06-18 19:37:03 +02:00
return nil , nil , nil
2020-06-07 17:51:20 +02:00
}
callee = es [ 0 ]
2020-06-07 10:50:28 +02:00
stmts = append ( stmts , ss ... )
2020-06-06 17:49:28 +02:00
// For built-in functions, we can call this in this position. Return an expression for the function
// call.
2020-06-07 17:51:20 +02:00
if callee . Type == shaderir . BuiltinFuncExpr {
2020-06-18 19:37:03 +02:00
var t shaderir . Type
2020-06-19 17:20:17 +02:00
switch callee . BuiltinFunc {
case shaderir . Vec2F :
t = shaderir . Type { Main : shaderir . Vec2 }
case shaderir . Vec3F :
t = shaderir . Type { Main : shaderir . Vec3 }
case shaderir . Vec4F :
t = shaderir . Type { Main : shaderir . Vec4 }
case shaderir . Mat2F :
t = shaderir . Type { Main : shaderir . Mat2 }
case shaderir . Mat3F :
t = shaderir . Type { Main : shaderir . Mat3 }
case shaderir . Mat4F :
t = shaderir . Type { Main : shaderir . Mat4 }
case shaderir . Step :
t = argts [ 1 ]
case shaderir . Smoothstep :
t = argts [ 2 ]
case shaderir . Length , shaderir . Distance , shaderir . Dot :
t = shaderir . Type { Main : shaderir . Float }
case shaderir . Cross :
t = shaderir . Type { Main : shaderir . Vec3 }
case shaderir . Texture2DF :
t = shaderir . Type { Main : shaderir . Vec4 }
default :
t = argts [ 0 ]
}
2020-06-07 17:51:20 +02:00
return [ ] shaderir . Expr {
{
Type : shaderir . Call ,
Exprs : append ( [ ] shaderir . Expr { callee } , args ... ) ,
} ,
2020-06-18 19:37:03 +02:00
} , [ ] shaderir . Type { t } , stmts
2020-06-06 17:49:28 +02:00
}
2020-06-07 17:51:20 +02:00
if callee . Type != shaderir . FunctionExpr {
2020-06-06 17:49:28 +02:00
cs . addError ( e . Pos ( ) , fmt . Sprintf ( "function callee must be a funciton name but %s" , e . Fun ) )
2020-06-18 19:37:03 +02:00
return nil , nil , nil
2020-06-06 17:49:28 +02:00
}
2020-06-07 17:51:20 +02:00
f := cs . funcs [ callee . Index ]
2020-06-06 17:49:28 +02:00
var outParams [ ] int
for _ , p := range f . ir . OutParams {
idx := len ( block . vars )
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 {
2020-06-19 17:20:17 +02:00
if len ( outParams ) != 0 {
2020-06-07 16:23:58 +02:00
cs . addError ( e . Pos ( ) , fmt . Sprintf ( "a function returning value cannot have out-params so far: %s" , e . Fun ) )
2020-06-18 19:37:03 +02:00
return nil , nil , nil
2020-06-07 16:23:58 +02:00
}
2020-06-06 17:49:28 +02:00
idx := len ( block . vars )
block . vars = append ( block . vars , variable {
typ : t ,
} )
// Calling the function should be done eariler to treat out-params correctly.
stmts = append ( stmts , shaderir . Stmt {
Type : shaderir . Assign ,
Exprs : [ ] shaderir . Expr {
{
Type : shaderir . LocalVariable ,
Index : idx ,
} ,
{
Type : shaderir . Call ,
Exprs : append ( [ ] shaderir . Expr { callee } , args ... ) ,
} ,
} ,
} )
// The actual expression here is just a local variable that includes the result of the
// function call.
2020-06-07 17:51:20 +02:00
return [ ] shaderir . Expr {
{
Type : shaderir . LocalVariable ,
Index : idx ,
} ,
2020-06-18 19:37:03 +02:00
} , [ ] shaderir . Type { t } , stmts
2020-06-06 17:49:28 +02:00
}
// 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 ... ) ,
} ,
} ,
} )
2020-06-07 16:23:58 +02:00
if len ( outParams ) == 0 {
// TODO: Is this an error?
2020-06-06 17:49:28 +02:00
}
2020-06-07 16:23:58 +02:00
var exprs [ ] shaderir . Expr
for _ , p := range outParams {
exprs = append ( exprs , shaderir . Expr {
Type : shaderir . LocalVariable ,
Index : p ,
} )
}
2020-06-19 17:20:17 +02:00
return exprs , f . ir . OutParams , stmts
2020-05-31 11:01:12 +02:00
case * ast . Ident :
2020-06-18 19:37:03 +02:00
if i , t , ok := block . findLocalVariable ( e . Name ) ; ok {
2020-06-07 17:51:20 +02:00
return [ ] shaderir . Expr {
{
Type : shaderir . LocalVariable ,
Index : i ,
} ,
2020-06-18 19:37:03 +02:00
} , [ ] shaderir . Type { t } , nil
2020-05-31 11:11:55 +02:00
}
2020-06-06 17:49:28 +02:00
if i , ok := cs . findFunction ( e . Name ) ; ok {
2020-06-07 17:51:20 +02:00
return [ ] shaderir . Expr {
{
Type : shaderir . FunctionExpr ,
Index : i ,
} ,
2020-06-18 19:37:03 +02:00
} , nil , nil
2020-06-06 17:49:28 +02:00
}
2020-06-02 15:45:44 +02:00
if i , ok := cs . findUniformVariable ( e . Name ) ; ok {
2020-06-07 17:51:20 +02:00
return [ ] shaderir . Expr {
{
Type : shaderir . UniformVariable ,
Index : i ,
} ,
2020-06-18 19:37:03 +02:00
} , [ ] shaderir . Type { cs . ir . Uniforms [ i ] } , nil
2020-06-02 15:45:44 +02:00
}
if f , ok := shaderir . ParseBuiltinFunc ( e . Name ) ; ok {
2020-06-07 17:51:20 +02:00
return [ ] shaderir . Expr {
{
Type : shaderir . BuiltinFuncExpr ,
BuiltinFunc : f ,
} ,
2020-06-18 19:37:03 +02:00
} , nil , nil
2020-05-31 11:01:12 +02:00
}
2020-05-31 11:11:55 +02:00
cs . addError ( e . Pos ( ) , fmt . Sprintf ( "unexpected identifier: %s" , e . Name ) )
2020-05-31 12:20:53 +02:00
case * ast . SelectorExpr :
2020-06-18 19:37:03 +02:00
exprs , _ , stmts := cs . parseExpr ( block , e . X )
2020-06-07 17:51:20 +02:00
if len ( exprs ) != 1 {
cs . addError ( e . Pos ( ) , fmt . Sprintf ( "multiple-value context is not available at a selector: %s" , e . X ) )
2020-06-18 19:37:03 +02:00
return nil , nil , nil
}
var t shaderir . Type
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
default :
cs . addError ( e . Pos ( ) , fmt . Sprintf ( "unexpected swizzling: %s" , e . Sel . Name ) )
return nil , nil , nil
2020-06-07 17:51:20 +02:00
}
return [ ] shaderir . Expr {
{
Type : shaderir . FieldSelector ,
Exprs : [ ] shaderir . Expr {
exprs [ 0 ] ,
{
Type : shaderir . SwizzlingExpr ,
Swizzling : e . Sel . Name ,
} ,
2020-05-31 12:20:53 +02:00
} ,
} ,
2020-06-18 19:37:03 +02:00
} , [ ] shaderir . Type { t } , stmts
2020-06-02 15:45:44 +02:00
case * ast . UnaryExpr :
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 ) )
2020-06-18 19:37:03 +02:00
return nil , nil , nil
2020-06-07 17:51:20 +02:00
}
2020-06-18 19:37:03 +02:00
exprs , t , stmts := cs . parseExpr ( block , e . X )
2020-06-07 17:51:20 +02:00
if len ( exprs ) != 1 {
cs . addError ( e . Pos ( ) , fmt . Sprintf ( "multiple-value context is not available at a unary operator: %s" , e . X ) )
2020-06-18 19:37:03 +02:00
return nil , nil , nil
2020-06-07 17:51:20 +02:00
}
return [ ] shaderir . Expr {
{
Type : shaderir . Unary ,
Op : op ,
Exprs : exprs ,
} ,
2020-06-18 19:37:03 +02:00
} , t , stmts
2020-05-31 11:01:12 +02:00
default :
2020-05-31 11:11:55 +02:00
cs . addError ( e . Pos ( ) , fmt . Sprintf ( "expression not implemented: %#v" , e ) )
2020-05-31 11:01:12 +02:00
}
2020-06-18 19:37:03 +02:00
return nil , nil , nil
2020-05-31 11:01:12 +02:00
}