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
typ typ
2020-05-09 17:39:26 +02:00
}
type constant struct {
2020-05-10 17:13:08 +02:00
name string
typ typ
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-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-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-05-30 19:23:51 +02:00
type block struct {
types [ ] typ
vars [ ] variable
consts [ ] constant
funcs [ ] function
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-05-31 16:57:03 +02:00
func ( b * block ) findLocalVariable ( name string ) ( int , bool ) {
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 {
return idx + i , true
}
}
if b . outer != nil {
return b . outer . findLocalVariable ( name )
}
return 0 , false
}
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
// 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
}
for _ , f := range cs . global . funcs {
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-05-10 17:13:08 +02:00
t . name = s . Name . Name
b . types = append ( b . types , 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 )
cs . ir . Uniforms = append ( cs . ir . Uniforms , v . typ . ir )
}
2020-05-10 14:57:12 +02:00
continue
}
2020-06-07 09:21:02 +02:00
for i , v := range vs {
2020-05-31 16:57:03 +02:00
b . vars = append ( b . vars , v )
b . ir . LocalVars = append ( b . ir . LocalVars , v . typ . ir )
2020-06-07 09:21:02 +02:00
if inits [ i ] != nil {
b . ir . Stmts = append ( b . ir . Stmts , shaderir . Stmt {
Type : shaderir . Assign ,
Exprs : [ ] shaderir . Expr {
{
Type : shaderir . LocalVariable ,
Index : len ( b . vars ) - 1 ,
} ,
* inits [ i ] ,
} ,
} )
}
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-03 16:56:08 +02:00
if b == & cs . global {
switch d . Name . Name {
2020-06-04 18:11:39 +02:00
case cs . vertexEntry :
2020-06-03 16:56:08 +02:00
cs . ir . VertexFunc . Block = f . ir . Block
2020-06-04 18:11:39 +02:00
case cs . fragmentEntry :
2020-06-03 16:56:08 +02:00
cs . ir . FragmentFunc . Block = f . ir . Block
default :
b . funcs = append ( b . funcs , f )
}
2020-06-02 15:45:44 +02:00
} else {
b . funcs = append ( b . funcs , f )
}
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-07 10:50:28 +02:00
func ( s * compileState ) parseVariable ( block * block , vs * ast . ValueSpec ) ( [ ] variable , [ ] * shaderir . Expr , [ ] shaderir . Stmt ) {
2020-05-10 17:13:08 +02:00
var t typ
2020-05-09 16:38:52 +02:00
if vs . Type != nil {
2020-05-10 16:56:56 +02:00
t = s . parseType ( vs . Type )
2020-05-08 20:38:37 +02:00
}
2020-05-09 16:38:52 +02:00
var vars [ ] variable
2020-06-07 09:21:02 +02:00
var inits [ ] * shaderir . Expr
2020-06-07 10:50:28 +02:00
var stmts [ ] shaderir . Stmt
2020-05-09 19:01:28 +02:00
for i , n := range vs . Names {
var init ast . Expr
if len ( vs . Values ) > 0 {
init = vs . Values [ i ]
2020-05-30 10:03:43 +02:00
if t . ir . Main == shaderir . None {
2020-05-10 12:32:40 +02:00
t = s . detectType ( block , init )
}
2020-05-09 19:01:28 +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
var expr * shaderir . Expr
if init != nil {
2020-06-07 10:50:28 +02:00
e , ss := s . parseExpr ( block , init )
2020-06-07 09:21:02 +02:00
expr = & e
2020-06-07 10:50:28 +02:00
stmts = append ( stmts , ss ... )
2020-06-07 09:21:02 +02:00
}
inits = append ( inits , expr )
2020-05-08 21:21:45 +02:00
}
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-05-10 17:13:08 +02:00
var t typ
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-02 15:45:44 +02:00
func ( cs * compileState ) parseFunc ( block * block , d * ast . FuncDecl ) function {
2020-05-09 11:05:30 +02:00
if d . Name == nil {
2020-05-30 10:03:43 +02:00
cs . addError ( d . Pos ( ) , "function must have a name" )
2020-05-09 17:59:18 +02:00
return function { }
2020-05-09 11:05:30 +02:00
}
if d . Body == nil {
2020-05-30 10:03:43 +02:00
cs . addError ( d . Pos ( ) , "function must have a body" )
2020-05-09 17:59:18 +02:00
return function { }
2020-05-09 11:05:30 +02:00
}
2020-05-31 09:20:36 +02:00
var inT [ ] shaderir . Type
2020-05-31 12:20:53 +02:00
var inParams [ ] variable
2020-06-02 15:45:44 +02:00
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-05-31 12:20:53 +02:00
inParams = append ( inParams , variable {
2020-05-31 11:11:55 +02:00
name : n . Name ,
typ : t ,
} )
2020-05-31 09:20:36 +02:00
inT = append ( inT , t . ir )
2020-05-09 12:21:01 +02:00
}
}
2020-05-31 09:20:36 +02:00
var outT [ ] shaderir . Type
2020-05-31 12:20:53 +02:00
var outParams [ ] variable
2020-06-02 15:45:44 +02:00
2020-05-10 13:41:43 +02:00
if d . Type . Results != nil {
for _ , f := range d . Type . Results . List {
2020-05-30 10:03:43 +02:00
t := cs . parseType ( f . Type )
2020-05-10 13:41:43 +02:00
if len ( f . Names ) == 0 {
2020-05-31 12:20:53 +02:00
outParams = append ( outParams , variable {
2020-05-31 11:11:55 +02:00
name : "" ,
typ : t ,
} )
2020-05-31 09:20:36 +02:00
outT = append ( outT , t . ir )
2020-05-10 13:41:43 +02:00
} else {
for _ , n := range f . Names {
2020-05-31 12:20:53 +02:00
outParams = append ( outParams , variable {
2020-05-31 11:11:55 +02:00
name : n . Name ,
typ : t ,
} )
2020-05-31 09:20:36 +02:00
outT = append ( outT , t . ir )
2020-05-10 13:41:43 +02:00
}
2020-05-09 12:21:01 +02:00
}
}
}
2020-06-03 16:56:08 +02:00
checkVaryings := func ( types [ ] shaderir . Type ) {
if len ( cs . ir . Varyings ) != len ( types ) {
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 {
if t . Main != types [ i ] . Main {
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-03 16:56:08 +02:00
for _ , t := range inT {
cs . ir . Attributes = append ( cs . ir . Attributes , t )
}
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 { }
}
if outT [ 0 ] . Main != shaderir . Vec4 {
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 {
checkVaryings ( outT [ 1 : ] )
} else {
for _ , t := range outT [ 1 : ] {
// TODO: Check that these params are not arrays or structs
cs . ir . Varyings = append ( cs . ir . Varyings , t )
}
}
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 { }
}
if inT [ 0 ] . Main != shaderir . Vec4 {
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 { }
}
if outT [ 0 ] . 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 {
checkVaryings ( inT [ 1 : ] )
} else {
for _ , t := range inT [ 1 : ] {
cs . ir . Varyings = append ( cs . ir . Varyings , t )
}
}
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-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 {
Index : len ( cs . ir . Funcs ) ,
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-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-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-05-31 19:23:27 +02:00
v . typ = cs . detectType ( block , l . Rhs [ i ] )
2020-05-10 12:32:40 +02:00
block . vars = append ( block . vars , v )
2020-05-31 19:23:27 +02:00
block . ir . LocalVars = append ( block . ir . LocalVars , v . typ . ir )
2020-06-07 10:50:28 +02:00
// Prase RHS first for the order of the statements.
rhs , stmts := cs . parseExpr ( block , l . Rhs [ i ] )
block . ir . Stmts = append ( block . ir . Stmts , stmts ... )
lhs , stmts := cs . parseExpr ( block , l . Lhs [ i ] )
block . ir . Stmts = append ( block . ir . Stmts , stmts ... )
2020-05-31 19:23:27 +02:00
block . ir . Stmts = append ( block . ir . Stmts , shaderir . Stmt {
2020-06-07 10:50:28 +02:00
Type : shaderir . Assign ,
Exprs : [ ] shaderir . Expr { lhs , rhs } ,
2020-05-31 19:23:27 +02:00
} )
2020-05-09 19:01:28 +02:00
}
case token . ASSIGN :
// TODO: What about the statement `a,b = b,a?`
2020-05-31 16:57:03 +02:00
for i := range l . Rhs {
2020-06-07 10:50:28 +02:00
// Prase RHS first for the order of the statements.
rhs , stmts := cs . parseExpr ( block , l . Rhs [ i ] )
block . ir . Stmts = append ( block . ir . Stmts , stmts ... )
lhs , stmts := cs . parseExpr ( block , l . Lhs [ i ] )
block . ir . Stmts = append ( block . ir . Stmts , stmts ... )
2020-05-31 16:57:03 +02:00
block . ir . Stmts = append ( block . ir . Stmts , shaderir . Stmt {
2020-06-07 10:50:28 +02:00
Type : shaderir . Assign ,
Exprs : [ ] shaderir . Expr { lhs , rhs } ,
2020-05-31 16:57:03 +02:00
} )
2020-05-09 19:01:28 +02:00
}
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-07 10:50:28 +02:00
e , stmts := cs . parseExpr ( block , r )
block . ir . Stmts = append ( block . ir . Stmts , stmts ... )
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
} ,
e ,
} ,
} )
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-05-09 16:38:52 +02:00
}
}
return block
}
2020-05-30 10:03:43 +02:00
func ( s * compileState ) detectType ( b * block , expr ast . Expr ) typ {
2020-05-10 12:32:40 +02:00
switch e := expr . ( type ) {
case * ast . BasicLit :
2020-05-31 19:23:27 +02:00
switch e . Kind {
case token . FLOAT :
2020-05-10 17:13:08 +02:00
return typ {
2020-05-30 10:03:43 +02:00
ir : shaderir . Type { Main : shaderir . Float } ,
2020-05-10 17:13:08 +02:00
}
2020-05-31 19:23:27 +02:00
case token . INT :
2020-05-30 18:56:07 +02:00
return typ {
ir : shaderir . Type { Main : shaderir . Int } ,
}
2020-05-10 13:41:43 +02:00
}
2020-05-30 18:56:07 +02:00
s . addError ( expr . Pos ( ) , fmt . Sprintf ( "unexpected literal: %s" , e . Value ) )
2020-05-10 17:13:08 +02:00
return typ { }
2020-05-31 19:23:27 +02:00
case * ast . CallExpr :
n := e . Fun . ( * ast . Ident ) . Name
f , ok := shaderir . ParseBuiltinFunc ( n )
if ok {
switch f {
case shaderir . Vec2F :
return typ { ir : shaderir . Type { Main : shaderir . Vec2 } }
case shaderir . Vec3F :
return typ { ir : shaderir . Type { Main : shaderir . Vec3 } }
case shaderir . Vec4F :
return typ { ir : shaderir . Type { Main : shaderir . Vec4 } }
case shaderir . Mat2F :
return typ { ir : shaderir . Type { Main : shaderir . Mat2 } }
case shaderir . Mat3F :
return typ { ir : shaderir . Type { Main : shaderir . Mat3 } }
case shaderir . Mat4F :
return typ { ir : shaderir . Type { Main : shaderir . Mat4 } }
// TODO: Add more functions
}
}
s . addError ( expr . Pos ( ) , fmt . Sprintf ( "unexpected call: %s" , n ) )
return typ { }
2020-05-10 12:32:40 +02:00
case * ast . CompositeLit :
2020-05-10 16:56:56 +02:00
return s . parseType ( e . Type )
2020-05-10 12:32:40 +02:00
case * ast . Ident :
n := e . Name
for _ , v := range b . vars {
if v . name == n {
2020-05-10 17:13:08 +02:00
return v . typ
2020-05-10 12:32:40 +02:00
}
}
2020-05-10 14:57:12 +02:00
if b == & s . global {
2020-05-30 18:56:07 +02:00
for i , v := range s . uniforms {
if v == n {
return typ { ir : s . ir . Uniforms [ i ] }
2020-05-10 14:57:12 +02:00
}
}
}
2020-05-10 12:32:40 +02:00
if b . outer != nil {
return s . detectType ( b . outer , e )
}
2020-06-07 10:03:40 +02:00
s . addError ( expr . Pos ( ) , fmt . Sprintf ( "unexpected identifier: %s" , n ) )
2020-05-10 17:13:08 +02:00
return typ { }
2020-05-10 12:32:40 +02:00
//case *ast.SelectorExpr:
//return fmt.Sprintf("%s.%s", dumpExpr(e.X), dumpExpr(e.Sel))
default :
s . addError ( expr . Pos ( ) , fmt . Sprintf ( "detecting type not implemented: %#v" , expr ) )
2020-05-10 17:13:08 +02:00
return typ { }
2020-05-10 12:32:40 +02:00
}
}
2020-05-31 11:01:12 +02:00
2020-06-07 10:50:28 +02:00
func ( cs * compileState ) parseExpr ( block * block , expr ast . Expr ) ( shaderir . Expr , [ ] 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-07 10:50:28 +02:00
return shaderir . Expr { } , nil
2020-05-31 11:11:55 +02:00
}
return shaderir . Expr {
Type : shaderir . IntExpr ,
Int : int32 ( v ) ,
2020-06-07 10:50:28 +02:00
} , 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-07 10:50:28 +02:00
return shaderir . Expr { } , nil
2020-05-31 11:11:55 +02:00
}
return shaderir . Expr {
Type : shaderir . FloatExpr ,
Float : float32 ( v ) ,
2020-06-07 10:50:28 +02:00
} , 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-07 10:50:28 +02:00
return shaderir . Expr { } , 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.
rhs , ss := cs . parseExpr ( block , e . Y )
stmts = append ( stmts , ss ... )
lhs , ss := cs . parseExpr ( block , e . X )
stmts = append ( stmts , ss ... )
2020-06-02 15:45:44 +02:00
return shaderir . Expr {
2020-06-07 10:50:28 +02:00
Type : shaderir . Binary ,
Op : op ,
Exprs : [ ] shaderir . Expr { lhs , rhs } ,
} , stmts
2020-05-31 11:01:12 +02:00
case * ast . CallExpr :
2020-06-07 10:50:28 +02:00
var exprs [ ] shaderir . Expr
var stmts [ ] shaderir . Stmt
// 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-07 10:50:28 +02:00
e , ss := cs . parseExpr ( block , a )
2020-05-31 19:23:27 +02:00
// TODO: Convert integer literals to float literals if necessary.
exprs = append ( exprs , e )
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
// TODO: When len(stmts) is not 0?
expr , ss := cs . parseExpr ( block , e . Fun )
exprs = append ( [ ] shaderir . Expr { expr } , exprs ... )
stmts = append ( stmts , ss ... )
// TODO: Return statements to call the function separately.
2020-05-31 11:01:12 +02:00
return shaderir . Expr {
2020-05-31 11:11:55 +02:00
Type : shaderir . Call ,
Exprs : exprs ,
2020-06-07 10:50:28 +02:00
} , stmts
2020-05-31 11:01:12 +02:00
case * ast . Ident :
2020-06-02 15:45:44 +02:00
if i , ok := block . findLocalVariable ( e . Name ) ; ok {
2020-05-31 11:11:55 +02:00
return shaderir . Expr {
Type : shaderir . LocalVariable ,
Index : i ,
2020-06-07 10:50:28 +02:00
} , nil
2020-05-31 11:11:55 +02:00
}
2020-06-02 15:45:44 +02:00
if i , ok := cs . findUniformVariable ( e . Name ) ; ok {
return shaderir . Expr {
Type : shaderir . UniformVariable ,
Index : i ,
2020-06-07 10:50:28 +02:00
} , nil
2020-06-02 15:45:44 +02:00
}
if f , ok := shaderir . ParseBuiltinFunc ( e . Name ) ; ok {
2020-05-31 11:11:55 +02:00
return shaderir . Expr {
Type : shaderir . BuiltinFuncExpr ,
BuiltinFunc : f ,
2020-06-07 10:50:28 +02:00
} , 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-07 10:50:28 +02:00
expr , stmts := cs . parseExpr ( block , e . X )
2020-05-31 12:20:53 +02:00
return shaderir . Expr {
Type : shaderir . FieldSelector ,
Exprs : [ ] shaderir . Expr {
2020-06-07 10:50:28 +02:00
expr ,
2020-05-31 12:20:53 +02:00
{
Type : shaderir . SwizzlingExpr ,
Swizzling : e . Sel . Name ,
} ,
} ,
2020-06-07 10:50:28 +02:00
} , 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-07 10:50:28 +02:00
return shaderir . Expr { } , nil
2020-06-02 15:45:44 +02:00
}
2020-06-07 10:50:28 +02:00
expr , stmts := cs . parseExpr ( block , e . X )
2020-06-02 15:45:44 +02:00
return shaderir . Expr {
2020-06-07 10:50:28 +02:00
Type : shaderir . Unary ,
Op : op ,
Exprs : [ ] shaderir . Expr { expr } ,
} , 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-07 10:50:28 +02:00
return shaderir . Expr { } , nil
2020-05-31 11:01:12 +02:00
}