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-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
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 09:21:02 +02:00
for i , v := range vs {
2020-05-31 16:57:03 +02:00
b . vars = append ( b . vars , v )
2020-06-07 12:36:38 +02:00
b . ir . LocalVars = append ( b . ir . LocalVars , v . typ )
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-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-07 10:50:28 +02:00
func ( s * compileState ) parseVariable ( block * block , vs * ast . ValueSpec ) ( [ ] variable , [ ] * shaderir . Expr , [ ] shaderir . Stmt ) {
2020-06-07 12:36:38 +02:00
var t shaderir . Type
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-06-07 12:36:38 +02:00
if t . Main == shaderir . None {
2020-06-07 16:32:50 +02:00
ts := s . detectType ( block , init )
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-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-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-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-06-07 16:32:50 +02:00
ts := cs . detectType ( block , l . Rhs [ i ] )
if len ( ts ) > 1 {
cs . addError ( l . Pos ( ) , fmt . Sprintf ( "the numbers of lhs and rhs don't match" ) )
}
v . typ = ts [ 0 ]
2020-05-10 12:32:40 +02:00
block . vars = append ( block . vars , v )
2020-06-07 12:36:38 +02:00
block . ir . LocalVars = append ( block . ir . LocalVars , v . typ )
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-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-06 17:49:28 +02:00
var (
callee shaderir . Expr
args [ ] shaderir . Expr
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-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.
2020-06-06 17:49:28 +02:00
args = append ( args , 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
2020-06-06 17:49:28 +02:00
// TODO: When len(ss) is not 0?
2020-06-07 10:50:28 +02:00
expr , ss := cs . parseExpr ( block , e . Fun )
2020-06-06 17:49:28 +02:00
callee = expr
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.
if expr . Type == shaderir . BuiltinFuncExpr {
return shaderir . Expr {
Type : shaderir . Call ,
Exprs : append ( [ ] shaderir . Expr { callee } , args ... ) ,
} , stmts
}
if expr . Type != shaderir . FunctionExpr {
cs . addError ( e . Pos ( ) , fmt . Sprintf ( "function callee must be a funciton name but %s" , e . Fun ) )
}
f := cs . funcs [ expr . Index ]
var outParams [ ] int
for _ , p := range f . ir . OutParams {
idx := len ( block . vars )
block . vars = append ( block . vars , variable {
typ : p ,
} )
block . ir . LocalVars = append ( block . ir . LocalVars , p )
args = append ( args , shaderir . Expr {
Type : shaderir . LocalVariable ,
Index : idx ,
} )
outParams = append ( outParams , idx )
}
if t := f . ir . Return ; t . Main != shaderir . None {
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.
return shaderir . Expr {
Type : shaderir . LocalVariable ,
Index : idx ,
} , stmts
}
// 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 ... ) ,
} ,
} ,
} )
// TODO: What about the other params?
if len ( outParams ) > 0 {
return shaderir . Expr {
Type : shaderir . LocalVariable ,
Index : outParams [ 0 ] ,
} , stmts
}
// TODO: Is an empty expression work?
return shaderir . Expr { } , 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-06 17:49:28 +02:00
if i , ok := cs . findFunction ( e . Name ) ; ok {
return shaderir . Expr {
Type : shaderir . FunctionExpr ,
Index : i ,
} , nil
}
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
}