From 5e6be91eacc428bfda00f4f044e509d59eb2e8f1 Mon Sep 17 00:00:00 2001 From: Hajime Hoshi Date: Sat, 9 May 2020 23:38:52 +0900 Subject: [PATCH] shader: Parse block variables --- internal/shader/block.go | 42 +++++++++++++++++ internal/shader/shader.go | 84 ++++++++++++++++++++++++++++------ internal/shader/shader_test.go | 12 ++++- internal/shader/type.go | 21 +++------ 4 files changed, 128 insertions(+), 31 deletions(-) create mode 100644 internal/shader/block.go diff --git a/internal/shader/block.go b/internal/shader/block.go new file mode 100644 index 000000000..207eaa1d4 --- /dev/null +++ b/internal/shader/block.go @@ -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 { +} diff --git a/internal/shader/shader.go b/internal/shader/shader.go index 6854602be..5c71ccd94 100644 --- a/internal/shader/shader.go +++ b/internal/shader/shader.go @@ -43,6 +43,7 @@ type function struct { name string args []variable rets []variable + body *block } type Shader struct { @@ -89,6 +90,9 @@ func NewShader(src []byte) (*Shader, error) { return nil, &ParseError{s.errs} } + // TODO: Resolve identifiers? + // TODO: Resolve constants + sort.Slice(s.varyings, func(a, b int) bool { return s.varyings[a].name < s.varyings[b].name }) @@ -129,7 +133,14 @@ func (sh *Shader) parse(f *ast.File) { case token.VAR: for _, s := range d.Specs { 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: 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) { - t, err := parseType(vs.Type) - if err != nil { - s.addError(vs.Type.Pos(), err.Error()) - return +func (s *Shader) parseVariable(vs *ast.ValueSpec) []variable { + var t typ + if vs.Type != nil { + var err error + 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 { name := n.Name - val := variable{ + vars = append(vars, variable{ name: name, 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) { @@ -302,10 +314,51 @@ func (sh *Shader) parseFunc(d *ast.FuncDecl) { name: d.Name.Name, args: args, rets: rets, + body: sh.parseBlock(d.Body), } 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. func (s *Shader) Dump() string { var lines []string @@ -348,7 +401,10 @@ func (s *Shader) Dump() string { if len(rets) > 0 { l += " (" + strings.Join(rets, ", ") + ")" } + l += " {" lines = append(lines, l) + lines = append(lines, f.body.dump(1)...) + lines = append(lines, "}") } return strings.Join(lines, "\n") + "\n" diff --git a/internal/shader/shader_test.go b/internal/shader/shader_test.go index 6d391dda7..ed5ad2669 100644 --- a/internal/shader/shader_test.go +++ b/internal/shader/shader_test.go @@ -44,7 +44,11 @@ var ( const C1 float = 1 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 @@ -58,7 +62,11 @@ const C1 float = 1 const C2 float = 2 const C3 float = 3 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) +} `, }, } diff --git a/internal/shader/type.go b/internal/shader/type.go index 33c280c29..85fe989eb 100644 --- a/internal/shader/type.go +++ b/internal/shader/type.go @@ -24,8 +24,7 @@ type typ int // TODO: What about array types? const ( - typBool typ = iota - typInt + typNone typ = iota typFloat typVec2 typVec3 @@ -40,10 +39,6 @@ func parseType(expr ast.Expr) (typ, error) { switch t := expr.(type) { case *ast.Ident: switch t.Name { - case "bool": - return typBool, nil - case "int": - return typInt, nil case "float": return typFloat, nil case "vec2": @@ -68,10 +63,8 @@ func parseType(expr ast.Expr) (typ, error) { func (t typ) String() string { switch t { - case typBool: - return "bool" - case typInt: - return "int" + case typNone: + return "(none)" case typFloat: return "float" case typVec2: @@ -94,15 +87,13 @@ func (t typ) String() string { } func (t typ) numeric() bool { - return t != typSampler2d + return t != typNone && t != typSampler2d } func (t typ) glslString() string { switch t { - case typBool: - return "bool" - case typInt: - return "int" + case typNone: + return "?(none)" case typFloat: return "float" case typVec2: