shader: Parse block variables

This commit is contained in:
Hajime Hoshi 2020-05-09 23:38:52 +09:00
parent e848eacedf
commit 5e6be91eac
4 changed files with 128 additions and 31 deletions

42
internal/shader/block.go Normal file
View File

@ -0,0 +1,42 @@
// 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/token"
"strings"
)
type block struct {
vars []variable
stmts []stmt
pos token.Pos
}
func (b *block) dump(indent int) []string {
idt := strings.Repeat("\t", indent)
var lines []string
for _, v := range b.vars {
lines = append(lines, fmt.Sprintf("%svar %s %s", idt, v.name, v.typ))
}
return lines
}
type stmt struct {
}

View File

@ -43,6 +43,7 @@ type function struct {
name string name string
args []variable args []variable
rets []variable rets []variable
body *block
} }
type Shader struct { type Shader struct {
@ -89,6 +90,9 @@ func NewShader(src []byte) (*Shader, error) {
return nil, &ParseError{s.errs} return nil, &ParseError{s.errs}
} }
// TODO: Resolve identifiers?
// TODO: Resolve constants
sort.Slice(s.varyings, func(a, b int) bool { sort.Slice(s.varyings, func(a, b int) bool {
return s.varyings[a].name < s.varyings[b].name return s.varyings[a].name < s.varyings[b].name
}) })
@ -129,7 +133,14 @@ func (sh *Shader) parse(f *ast.File) {
case token.VAR: case token.VAR:
for _, s := range d.Specs { for _, s := range d.Specs {
s := s.(*ast.ValueSpec) s := s.(*ast.ValueSpec)
sh.parseTopLevelVariable(s) vs := sh.parseVariable(s)
for _, v := range vs {
if 'A' <= v.name[0] && v.name[0] <= 'Z' {
sh.uniforms = append(sh.uniforms, v)
} else {
sh.globals = append(sh.globals, v)
}
}
} }
case token.IMPORT: case token.IMPORT:
sh.addError(d.Pos(), "import is forbidden") sh.addError(d.Pos(), "import is forbidden")
@ -200,25 +211,26 @@ func (sh *Shader) parseVaryingStruct(t *ast.TypeSpec) {
} }
} }
func (s *Shader) parseTopLevelVariable(vs *ast.ValueSpec) { func (s *Shader) parseVariable(vs *ast.ValueSpec) []variable {
t, err := parseType(vs.Type) var t typ
if err != nil { if vs.Type != nil {
s.addError(vs.Type.Pos(), err.Error()) var err error
return t, err = parseType(vs.Type)
if err != nil {
s.addError(vs.Type.Pos(), err.Error())
return nil
}
} }
var vars []variable
for _, n := range vs.Names { for _, n := range vs.Names {
name := n.Name name := n.Name
val := variable{ vars = append(vars, variable{
name: name, name: name,
typ: t, typ: t,
} })
// TODO: Parse initial value.
if 'A' <= name[0] && name[0] <= 'Z' {
s.uniforms = append(s.uniforms, val)
} else {
s.globals = append(s.globals, val)
}
} }
return vars
} }
func (s *Shader) parseTopLevelConstant(vs *ast.ValueSpec) { func (s *Shader) parseTopLevelConstant(vs *ast.ValueSpec) {
@ -302,10 +314,51 @@ func (sh *Shader) parseFunc(d *ast.FuncDecl) {
name: d.Name.Name, name: d.Name.Name,
args: args, args: args,
rets: rets, rets: rets,
body: sh.parseBlock(d.Body),
} }
sh.funcs = append(sh.funcs, f) sh.funcs = append(sh.funcs, f)
} }
func (sh *Shader) parseBlock(b *ast.BlockStmt) *block {
block := &block{}
for _, l := range b.List {
switch l := l.(type) {
case *ast.AssignStmt:
if l.Tok == token.DEFINE {
for _, s := range l.Lhs {
ident := s.(*ast.Ident)
block.vars = append(block.vars, variable{
name: ident.Name,
})
}
} else {
// TODO
}
case *ast.DeclStmt:
switch d := l.Decl.(type) {
case *ast.GenDecl:
switch d.Tok {
case token.VAR:
for _, s := range d.Specs {
s := s.(*ast.ValueSpec)
vs := sh.parseVariable(s)
block.vars = append(block.vars, vs...)
}
}
}
case *ast.ReturnStmt:
default:
}
}
sort.Slice(block.vars, func(a, b int) bool {
return block.vars[a].name < block.vars[b].name
})
return block
}
// Dump dumps the shader state in an intermediate language. // Dump dumps the shader state in an intermediate language.
func (s *Shader) Dump() string { func (s *Shader) Dump() string {
var lines []string var lines []string
@ -348,7 +401,10 @@ func (s *Shader) Dump() string {
if len(rets) > 0 { if len(rets) > 0 {
l += " (" + strings.Join(rets, ", ") + ")" l += " (" + strings.Join(rets, ", ") + ")"
} }
l += " {"
lines = append(lines, l) lines = append(lines, l)
lines = append(lines, f.body.dump(1)...)
lines = append(lines, "}")
} }
return strings.Join(lines, "\n") + "\n" return strings.Join(lines, "\n") + "\n"

View File

@ -44,7 +44,11 @@ var (
const C1 float = 1 const C1 float = 1
const C2, C3 float = 2, 3 const C2, C3 float = 2, 3
func foo(a, b vec2) vec4 { func F1(a, b vec2) vec4 {
var c0 vec2 = a
var c1 = b
c2 := vec4{c0, c1}
return c2
} }
`, `,
Dump: `var Position varying vec4 // position Dump: `var Position varying vec4 // position
@ -58,7 +62,11 @@ const C1 float = 1
const C2 float = 2 const C2 float = 2
const C3 float = 3 const C3 float = 3
var qux vec4 var qux vec4
func foo(a vec2, b vec2) (_ vec4) func F1(a vec2, b vec2) (_ vec4) {
var c0 vec2
var c1 (none)
var c2 (none)
}
`, `,
}, },
} }

View File

@ -24,8 +24,7 @@ type typ int
// TODO: What about array types? // TODO: What about array types?
const ( const (
typBool typ = iota typNone typ = iota
typInt
typFloat typFloat
typVec2 typVec2
typVec3 typVec3
@ -40,10 +39,6 @@ func parseType(expr ast.Expr) (typ, error) {
switch t := expr.(type) { switch t := expr.(type) {
case *ast.Ident: case *ast.Ident:
switch t.Name { switch t.Name {
case "bool":
return typBool, nil
case "int":
return typInt, nil
case "float": case "float":
return typFloat, nil return typFloat, nil
case "vec2": case "vec2":
@ -68,10 +63,8 @@ func parseType(expr ast.Expr) (typ, error) {
func (t typ) String() string { func (t typ) String() string {
switch t { switch t {
case typBool: case typNone:
return "bool" return "(none)"
case typInt:
return "int"
case typFloat: case typFloat:
return "float" return "float"
case typVec2: case typVec2:
@ -94,15 +87,13 @@ func (t typ) String() string {
} }
func (t typ) numeric() bool { func (t typ) numeric() bool {
return t != typSampler2d return t != typNone && t != typSampler2d
} }
func (t typ) glslString() string { func (t typ) glslString() string {
switch t { switch t {
case typBool: case typNone:
return "bool" return "?(none)"
case typInt:
return "int"
case typFloat: case typFloat:
return "float" return "float"
case typVec2: case typVec2: