mirror of
https://github.com/hajimehoshi/ebiten.git
synced 2025-01-12 20:18:59 +01:00
shader: Refactoring: Split files
This commit is contained in:
parent
49b389c8b1
commit
b00666df22
420
internal/shader/expr.go
Normal file
420
internal/shader/expr.go
Normal file
@ -0,0 +1,420 @@
|
||||
// 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"
|
||||
gconstant "go/constant"
|
||||
"go/token"
|
||||
|
||||
"github.com/hajimehoshi/ebiten/internal/shaderir"
|
||||
)
|
||||
|
||||
func (cs *compileState) parseExpr(block *block, expr ast.Expr) ([]shaderir.Expr, []shaderir.Type, []shaderir.Stmt, bool) {
|
||||
switch e := expr.(type) {
|
||||
case *ast.BasicLit:
|
||||
switch e.Kind {
|
||||
case token.INT:
|
||||
return []shaderir.Expr{
|
||||
{
|
||||
Type: shaderir.NumberExpr,
|
||||
Const: gconstant.MakeFromLiteral(e.Value, e.Kind, 0),
|
||||
},
|
||||
}, []shaderir.Type{{Main: shaderir.Int}}, nil, true
|
||||
case token.FLOAT:
|
||||
return []shaderir.Expr{
|
||||
{
|
||||
Type: shaderir.NumberExpr,
|
||||
Const: gconstant.MakeFromLiteral(e.Value, e.Kind, 0),
|
||||
},
|
||||
}, []shaderir.Type{{Main: shaderir.Float}}, nil, true
|
||||
default:
|
||||
cs.addError(e.Pos(), fmt.Sprintf("literal not implemented: %#v", e))
|
||||
}
|
||||
case *ast.BinaryExpr:
|
||||
var stmts []shaderir.Stmt
|
||||
|
||||
// Prase LHS first for the order of the statements.
|
||||
lhs, ts, ss, ok := cs.parseExpr(block, e.X)
|
||||
if !ok {
|
||||
return nil, nil, nil, false
|
||||
}
|
||||
if len(lhs) != 1 {
|
||||
cs.addError(e.Pos(), fmt.Sprintf("multiple-value context is not available at a binary operator: %s", e.X))
|
||||
return nil, nil, nil, false
|
||||
}
|
||||
stmts = append(stmts, ss...)
|
||||
lhst := ts[0]
|
||||
|
||||
rhs, ts, ss, ok := cs.parseExpr(block, e.Y)
|
||||
if !ok {
|
||||
return nil, nil, nil, false
|
||||
}
|
||||
if len(rhs) != 1 {
|
||||
cs.addError(e.Pos(), fmt.Sprintf("multiple-value context is not available at a binary operator: %s", e.Y))
|
||||
return nil, nil, nil, false
|
||||
}
|
||||
stmts = append(stmts, ss...)
|
||||
rhst := ts[0]
|
||||
|
||||
if lhs[0].Type == shaderir.NumberExpr && rhs[0].Type == shaderir.NumberExpr {
|
||||
op := e.Op
|
||||
// https://golang.org/pkg/go/constant/#BinaryOp
|
||||
// "To force integer division of Int operands, use op == token.QUO_ASSIGN instead of
|
||||
// token.QUO; the result is guaranteed to be Int in this case."
|
||||
if op == token.QUO && lhs[0].Const.Kind() == gconstant.Int && rhs[0].Const.Kind() == gconstant.Int {
|
||||
op = token.QUO_ASSIGN
|
||||
}
|
||||
v := gconstant.BinaryOp(lhs[0].Const, op, rhs[0].Const)
|
||||
t := shaderir.Type{Main: shaderir.Int}
|
||||
if v.Kind() == gconstant.Float {
|
||||
t = shaderir.Type{Main: shaderir.Float}
|
||||
}
|
||||
return []shaderir.Expr{
|
||||
{
|
||||
Type: shaderir.NumberExpr,
|
||||
Const: v,
|
||||
},
|
||||
}, []shaderir.Type{t}, stmts, true
|
||||
}
|
||||
|
||||
var t shaderir.Type
|
||||
switch {
|
||||
case lhst.Equal(&rhst):
|
||||
t = lhst
|
||||
case lhst.Main == shaderir.Float || lhst.Main == shaderir.Int:
|
||||
switch rhst.Main {
|
||||
case shaderir.Int:
|
||||
t = lhst
|
||||
case shaderir.Float, shaderir.Vec2, shaderir.Vec3, shaderir.Vec4, shaderir.Mat2, shaderir.Mat3, shaderir.Mat4:
|
||||
t = rhst
|
||||
default:
|
||||
cs.addError(e.Pos(), fmt.Sprintf("types don't match: %s %s %s", lhst.String(), e.Op, rhst.String()))
|
||||
return nil, nil, nil, false
|
||||
}
|
||||
case rhst.Main == shaderir.Float || rhst.Main == shaderir.Int:
|
||||
switch lhst.Main {
|
||||
case shaderir.Int:
|
||||
t = rhst
|
||||
case shaderir.Float, shaderir.Vec2, shaderir.Vec3, shaderir.Vec4, shaderir.Mat2, shaderir.Mat3, shaderir.Mat4:
|
||||
t = lhst
|
||||
default:
|
||||
cs.addError(e.Pos(), fmt.Sprintf("types don't match: %s %s %s", lhst.String(), e.Op, rhst.String()))
|
||||
return nil, nil, nil, false
|
||||
}
|
||||
case lhst.Main == shaderir.Vec2 && rhst.Main == shaderir.Mat2 ||
|
||||
lhst.Main == shaderir.Mat2 && rhst.Main == shaderir.Vec2:
|
||||
t = shaderir.Type{Main: shaderir.Vec2}
|
||||
case lhst.Main == shaderir.Vec3 && rhst.Main == shaderir.Mat3 ||
|
||||
lhst.Main == shaderir.Mat3 && rhst.Main == shaderir.Vec3:
|
||||
t = shaderir.Type{Main: shaderir.Vec3}
|
||||
case lhst.Main == shaderir.Vec4 && rhst.Main == shaderir.Mat4 ||
|
||||
lhst.Main == shaderir.Mat4 && rhst.Main == shaderir.Vec4:
|
||||
t = shaderir.Type{Main: shaderir.Vec4}
|
||||
default:
|
||||
cs.addError(e.Pos(), fmt.Sprintf("invalid expression: %s %s %s", lhst.String(), e.Op, rhst.String()))
|
||||
return nil, nil, nil, false
|
||||
}
|
||||
|
||||
op, ok := shaderir.OpFromToken(e.Op)
|
||||
if !ok {
|
||||
cs.addError(e.Pos(), fmt.Sprintf("unexpected operator: %s", e.Op))
|
||||
return nil, nil, nil, false
|
||||
}
|
||||
|
||||
return []shaderir.Expr{
|
||||
{
|
||||
Type: shaderir.Binary,
|
||||
Op: op,
|
||||
Exprs: []shaderir.Expr{lhs[0], rhs[0]},
|
||||
},
|
||||
}, []shaderir.Type{t}, stmts, true
|
||||
case *ast.CallExpr:
|
||||
var (
|
||||
callee shaderir.Expr
|
||||
args []shaderir.Expr
|
||||
argts []shaderir.Type
|
||||
stmts []shaderir.Stmt
|
||||
)
|
||||
|
||||
// Parse the argument first for the order of the statements.
|
||||
for _, a := range e.Args {
|
||||
es, ts, ss, ok := cs.parseExpr(block, a)
|
||||
if !ok {
|
||||
return nil, nil, nil, false
|
||||
}
|
||||
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))
|
||||
return nil, nil, nil, false
|
||||
}
|
||||
args = append(args, es...)
|
||||
argts = append(argts, ts...)
|
||||
stmts = append(stmts, ss...)
|
||||
}
|
||||
|
||||
// TODO: When len(ss) is not 0?
|
||||
es, _, ss, ok := cs.parseExpr(block, e.Fun)
|
||||
if !ok {
|
||||
return nil, nil, nil, false
|
||||
}
|
||||
if len(es) != 1 {
|
||||
cs.addError(e.Pos(), fmt.Sprintf("multiple-value context is not available at a callee: %s", e.Fun))
|
||||
return nil, nil, nil, false
|
||||
}
|
||||
callee = es[0]
|
||||
stmts = append(stmts, ss...)
|
||||
|
||||
// For built-in functions, we can call this in this position. Return an expression for the function
|
||||
// call.
|
||||
if callee.Type == shaderir.BuiltinFuncExpr {
|
||||
var t shaderir.Type
|
||||
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]
|
||||
}
|
||||
return []shaderir.Expr{
|
||||
{
|
||||
Type: shaderir.Call,
|
||||
Exprs: append([]shaderir.Expr{callee}, args...),
|
||||
},
|
||||
}, []shaderir.Type{t}, stmts, true
|
||||
}
|
||||
|
||||
if callee.Type != shaderir.FunctionExpr {
|
||||
cs.addError(e.Pos(), fmt.Sprintf("function callee must be a funciton name but %s", e.Fun))
|
||||
return nil, nil, nil, false
|
||||
}
|
||||
|
||||
f := cs.funcs[callee.Index]
|
||||
|
||||
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 {
|
||||
if len(outParams) != 0 {
|
||||
cs.addError(e.Pos(), fmt.Sprintf("a function returning value cannot have out-params so far: %s", e.Fun))
|
||||
return nil, nil, nil, false
|
||||
}
|
||||
|
||||
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,
|
||||
},
|
||||
}, []shaderir.Type{t}, stmts, true
|
||||
}
|
||||
|
||||
// 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...),
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
if len(outParams) == 0 {
|
||||
// TODO: Is this an error?
|
||||
}
|
||||
|
||||
var exprs []shaderir.Expr
|
||||
for _, p := range outParams {
|
||||
exprs = append(exprs, shaderir.Expr{
|
||||
Type: shaderir.LocalVariable,
|
||||
Index: p,
|
||||
})
|
||||
}
|
||||
return exprs, f.ir.OutParams, stmts, true
|
||||
|
||||
case *ast.Ident:
|
||||
if i, t, ok := block.findLocalVariable(e.Name); ok {
|
||||
return []shaderir.Expr{
|
||||
{
|
||||
Type: shaderir.LocalVariable,
|
||||
Index: i,
|
||||
},
|
||||
}, []shaderir.Type{t}, nil, true
|
||||
}
|
||||
if i, ok := cs.findFunction(e.Name); ok {
|
||||
return []shaderir.Expr{
|
||||
{
|
||||
Type: shaderir.FunctionExpr,
|
||||
Index: i,
|
||||
},
|
||||
}, nil, nil, true
|
||||
}
|
||||
if i, ok := cs.findUniformVariable(e.Name); ok {
|
||||
return []shaderir.Expr{
|
||||
{
|
||||
Type: shaderir.UniformVariable,
|
||||
Index: i,
|
||||
},
|
||||
}, []shaderir.Type{cs.ir.Uniforms[i]}, nil, true
|
||||
}
|
||||
if f, ok := shaderir.ParseBuiltinFunc(e.Name); ok {
|
||||
return []shaderir.Expr{
|
||||
{
|
||||
Type: shaderir.BuiltinFuncExpr,
|
||||
BuiltinFunc: f,
|
||||
},
|
||||
}, nil, nil, true
|
||||
}
|
||||
cs.addError(e.Pos(), fmt.Sprintf("unexpected identifier: %s", e.Name))
|
||||
|
||||
case *ast.ParenExpr:
|
||||
return cs.parseExpr(block, e.X)
|
||||
|
||||
case *ast.SelectorExpr:
|
||||
exprs, _, stmts, ok := cs.parseExpr(block, e.X)
|
||||
if !ok {
|
||||
return nil, nil, nil, false
|
||||
}
|
||||
if len(exprs) != 1 {
|
||||
cs.addError(e.Pos(), fmt.Sprintf("multiple-value context is not available at a selector: %s", e.X))
|
||||
return nil, nil, nil, false
|
||||
}
|
||||
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, false
|
||||
}
|
||||
return []shaderir.Expr{
|
||||
{
|
||||
Type: shaderir.FieldSelector,
|
||||
Exprs: []shaderir.Expr{
|
||||
exprs[0],
|
||||
{
|
||||
Type: shaderir.SwizzlingExpr,
|
||||
Swizzling: e.Sel.Name,
|
||||
},
|
||||
},
|
||||
},
|
||||
}, []shaderir.Type{t}, stmts, true
|
||||
|
||||
case *ast.UnaryExpr:
|
||||
exprs, t, stmts, ok := cs.parseExpr(block, e.X)
|
||||
if !ok {
|
||||
return nil, nil, nil, false
|
||||
}
|
||||
if len(exprs) != 1 {
|
||||
cs.addError(e.Pos(), fmt.Sprintf("multiple-value context is not available at a unary operator: %s", e.X))
|
||||
return nil, nil, nil, false
|
||||
}
|
||||
|
||||
if exprs[0].Type == shaderir.NumberExpr {
|
||||
v := gconstant.UnaryOp(e.Op, exprs[0].Const, 0)
|
||||
t := shaderir.Type{Main: shaderir.Int}
|
||||
if v.Kind() == gconstant.Float {
|
||||
t = shaderir.Type{Main: shaderir.Float}
|
||||
}
|
||||
return []shaderir.Expr{
|
||||
{
|
||||
Type: shaderir.NumberExpr,
|
||||
Const: v,
|
||||
},
|
||||
}, []shaderir.Type{t}, stmts, true
|
||||
}
|
||||
|
||||
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))
|
||||
return nil, nil, nil, false
|
||||
}
|
||||
return []shaderir.Expr{
|
||||
{
|
||||
Type: shaderir.Unary,
|
||||
Op: op,
|
||||
Exprs: exprs,
|
||||
},
|
||||
}, t, stmts, true
|
||||
default:
|
||||
cs.addError(e.Pos(), fmt.Sprintf("expression not implemented: %#v", e))
|
||||
}
|
||||
return nil, nil, nil, false
|
||||
}
|
@ -17,7 +17,6 @@ package shader
|
||||
import (
|
||||
"fmt"
|
||||
"go/ast"
|
||||
gconstant "go/constant"
|
||||
"go/token"
|
||||
"strings"
|
||||
|
||||
@ -622,619 +621,10 @@ func (cs *compileState) parseBlock(outer *block, b *ast.BlockStmt, inParams, out
|
||||
}()
|
||||
|
||||
for _, l := range b.List {
|
||||
switch l := l.(type) {
|
||||
case *ast.AssignStmt:
|
||||
switch l.Tok {
|
||||
case token.DEFINE:
|
||||
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, false
|
||||
}
|
||||
|
||||
if !cs.assign(block, l.Pos(), l.Lhs, l.Rhs, true) {
|
||||
return nil, false
|
||||
}
|
||||
case token.ASSIGN:
|
||||
// TODO: What about the statement `a,b = b,a?`
|
||||
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, false
|
||||
}
|
||||
if !cs.assign(block, l.Pos(), l.Lhs, l.Rhs, false) {
|
||||
return nil, false
|
||||
}
|
||||
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
|
||||
}
|
||||
rhs, _, stmts, ok := cs.parseExpr(block, l.Rhs[0])
|
||||
if !ok {
|
||||
return nil, false
|
||||
}
|
||||
block.ir.Stmts = append(block.ir.Stmts, stmts...)
|
||||
lhs, _, stmts, ok := cs.parseExpr(block, l.Lhs[0])
|
||||
if !ok {
|
||||
return nil, false
|
||||
}
|
||||
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))
|
||||
}
|
||||
case *ast.BlockStmt:
|
||||
b, ok := cs.parseBlock(block, l, nil, nil)
|
||||
if !ok {
|
||||
return nil, false
|
||||
}
|
||||
block.ir.Stmts = append(block.ir.Stmts, shaderir.Stmt{
|
||||
Type: shaderir.BlockStmt,
|
||||
Blocks: []shaderir.Block{
|
||||
b.ir,
|
||||
},
|
||||
})
|
||||
case *ast.DeclStmt:
|
||||
if !cs.parseDecl(block, l.Decl) {
|
||||
return nil, false
|
||||
}
|
||||
case *ast.ReturnStmt:
|
||||
for i, r := range l.Results {
|
||||
exprs, _, stmts, ok := cs.parseExpr(block, r)
|
||||
if !ok {
|
||||
return nil, false
|
||||
}
|
||||
block.ir.Stmts = append(block.ir.Stmts, stmts...)
|
||||
if len(exprs) == 0 {
|
||||
continue
|
||||
}
|
||||
if len(exprs) > 1 {
|
||||
cs.addError(r.Pos(), "multiple-context with return is not implemented yet")
|
||||
continue
|
||||
}
|
||||
block.ir.Stmts = append(block.ir.Stmts, shaderir.Stmt{
|
||||
Type: shaderir.Assign,
|
||||
Exprs: []shaderir.Expr{
|
||||
{
|
||||
Type: shaderir.LocalVariable,
|
||||
Index: len(inParams) + i,
|
||||
},
|
||||
exprs[0],
|
||||
},
|
||||
})
|
||||
}
|
||||
block.ir.Stmts = append(block.ir.Stmts, shaderir.Stmt{
|
||||
Type: shaderir.Return,
|
||||
})
|
||||
case *ast.ExprStmt:
|
||||
exprs, _, stmts, ok := cs.parseExpr(block, l.X)
|
||||
if !ok {
|
||||
return nil, false
|
||||
}
|
||||
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))
|
||||
if !cs.parseStmt(block, l, inParams) {
|
||||
return nil, false
|
||||
}
|
||||
}
|
||||
|
||||
return block, true
|
||||
}
|
||||
|
||||
func (cs *compileState) assign(block *block, pos token.Pos, lhs, rhs []ast.Expr, define bool) bool {
|
||||
var rhsExprs []shaderir.Expr
|
||||
var rhsTypes []shaderir.Type
|
||||
|
||||
for i, e := range lhs {
|
||||
if len(lhs) == len(rhs) {
|
||||
// Prase RHS first for the order of the statements.
|
||||
r, origts, stmts, ok := cs.parseExpr(block, rhs[i])
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
block.ir.Stmts = append(block.ir.Stmts, stmts...)
|
||||
|
||||
if define {
|
||||
v := variable{
|
||||
name: e.(*ast.Ident).Name,
|
||||
}
|
||||
ts, ok := cs.functionReturnTypes(block, rhs[i])
|
||||
if !ok {
|
||||
ts = origts
|
||||
}
|
||||
if len(ts) > 1 {
|
||||
cs.addError(pos, fmt.Sprintf("single-value context and multiple-value context cannot be mixed"))
|
||||
return false
|
||||
}
|
||||
if len(ts) == 1 {
|
||||
v.typ = ts[0]
|
||||
}
|
||||
block.vars = append(block.vars, v)
|
||||
}
|
||||
|
||||
if len(r) > 1 {
|
||||
cs.addError(pos, fmt.Sprintf("single-value context and multiple-value context cannot be mixed"))
|
||||
return false
|
||||
}
|
||||
|
||||
l, _, stmts, ok := cs.parseExpr(block, lhs[i])
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
block.ir.Stmts = append(block.ir.Stmts, stmts...)
|
||||
|
||||
if r[0].Type == shaderir.NumberExpr {
|
||||
switch block.vars[l[0].Index].typ.Main {
|
||||
case shaderir.Int:
|
||||
r[0].ConstType = shaderir.ConstTypeInt
|
||||
case shaderir.Float:
|
||||
r[0].ConstType = shaderir.ConstTypeFloat
|
||||
}
|
||||
}
|
||||
|
||||
block.ir.Stmts = append(block.ir.Stmts, shaderir.Stmt{
|
||||
Type: shaderir.Assign,
|
||||
Exprs: []shaderir.Expr{l[0], r[0]},
|
||||
})
|
||||
} else {
|
||||
if i == 0 {
|
||||
var stmts []shaderir.Stmt
|
||||
var ok bool
|
||||
rhsExprs, rhsTypes, stmts, ok = cs.parseExpr(block, rhs[0])
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
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...)
|
||||
}
|
||||
|
||||
if define {
|
||||
v := variable{
|
||||
name: e.(*ast.Ident).Name,
|
||||
}
|
||||
v.typ = rhsTypes[i]
|
||||
block.vars = append(block.vars, v)
|
||||
}
|
||||
|
||||
l, _, stmts, ok := cs.parseExpr(block, lhs[i])
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
block.ir.Stmts = append(block.ir.Stmts, stmts...)
|
||||
|
||||
block.ir.Stmts = append(block.ir.Stmts, shaderir.Stmt{
|
||||
Type: shaderir.Assign,
|
||||
Exprs: []shaderir.Expr{l[0], rhsExprs[i]},
|
||||
})
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func (cs *compileState) parseExpr(block *block, expr ast.Expr) ([]shaderir.Expr, []shaderir.Type, []shaderir.Stmt, bool) {
|
||||
switch e := expr.(type) {
|
||||
case *ast.BasicLit:
|
||||
switch e.Kind {
|
||||
case token.INT:
|
||||
return []shaderir.Expr{
|
||||
{
|
||||
Type: shaderir.NumberExpr,
|
||||
Const: gconstant.MakeFromLiteral(e.Value, e.Kind, 0),
|
||||
},
|
||||
}, []shaderir.Type{{Main: shaderir.Int}}, nil, true
|
||||
case token.FLOAT:
|
||||
return []shaderir.Expr{
|
||||
{
|
||||
Type: shaderir.NumberExpr,
|
||||
Const: gconstant.MakeFromLiteral(e.Value, e.Kind, 0),
|
||||
},
|
||||
}, []shaderir.Type{{Main: shaderir.Float}}, nil, true
|
||||
default:
|
||||
cs.addError(e.Pos(), fmt.Sprintf("literal not implemented: %#v", e))
|
||||
}
|
||||
case *ast.BinaryExpr:
|
||||
var stmts []shaderir.Stmt
|
||||
|
||||
// Prase LHS first for the order of the statements.
|
||||
lhs, ts, ss, ok := cs.parseExpr(block, e.X)
|
||||
if !ok {
|
||||
return nil, nil, nil, false
|
||||
}
|
||||
if len(lhs) != 1 {
|
||||
cs.addError(e.Pos(), fmt.Sprintf("multiple-value context is not available at a binary operator: %s", e.X))
|
||||
return nil, nil, nil, false
|
||||
}
|
||||
stmts = append(stmts, ss...)
|
||||
lhst := ts[0]
|
||||
|
||||
rhs, ts, ss, ok := cs.parseExpr(block, e.Y)
|
||||
if !ok {
|
||||
return nil, nil, nil, false
|
||||
}
|
||||
if len(rhs) != 1 {
|
||||
cs.addError(e.Pos(), fmt.Sprintf("multiple-value context is not available at a binary operator: %s", e.Y))
|
||||
return nil, nil, nil, false
|
||||
}
|
||||
stmts = append(stmts, ss...)
|
||||
rhst := ts[0]
|
||||
|
||||
if lhs[0].Type == shaderir.NumberExpr && rhs[0].Type == shaderir.NumberExpr {
|
||||
op := e.Op
|
||||
// https://golang.org/pkg/go/constant/#BinaryOp
|
||||
// "To force integer division of Int operands, use op == token.QUO_ASSIGN instead of
|
||||
// token.QUO; the result is guaranteed to be Int in this case."
|
||||
if op == token.QUO && lhs[0].Const.Kind() == gconstant.Int && rhs[0].Const.Kind() == gconstant.Int {
|
||||
op = token.QUO_ASSIGN
|
||||
}
|
||||
v := gconstant.BinaryOp(lhs[0].Const, op, rhs[0].Const)
|
||||
t := shaderir.Type{Main: shaderir.Int}
|
||||
if v.Kind() == gconstant.Float {
|
||||
t = shaderir.Type{Main: shaderir.Float}
|
||||
}
|
||||
return []shaderir.Expr{
|
||||
{
|
||||
Type: shaderir.NumberExpr,
|
||||
Const: v,
|
||||
},
|
||||
}, []shaderir.Type{t}, stmts, true
|
||||
}
|
||||
|
||||
var t shaderir.Type
|
||||
switch {
|
||||
case lhst.Equal(&rhst):
|
||||
t = lhst
|
||||
case lhst.Main == shaderir.Float || lhst.Main == shaderir.Int:
|
||||
switch rhst.Main {
|
||||
case shaderir.Int:
|
||||
t = lhst
|
||||
case shaderir.Float, shaderir.Vec2, shaderir.Vec3, shaderir.Vec4, shaderir.Mat2, shaderir.Mat3, shaderir.Mat4:
|
||||
t = rhst
|
||||
default:
|
||||
cs.addError(e.Pos(), fmt.Sprintf("types don't match: %s %s %s", lhst.String(), e.Op, rhst.String()))
|
||||
return nil, nil, nil, false
|
||||
}
|
||||
case rhst.Main == shaderir.Float || rhst.Main == shaderir.Int:
|
||||
switch lhst.Main {
|
||||
case shaderir.Int:
|
||||
t = rhst
|
||||
case shaderir.Float, shaderir.Vec2, shaderir.Vec3, shaderir.Vec4, shaderir.Mat2, shaderir.Mat3, shaderir.Mat4:
|
||||
t = lhst
|
||||
default:
|
||||
cs.addError(e.Pos(), fmt.Sprintf("types don't match: %s %s %s", lhst.String(), e.Op, rhst.String()))
|
||||
return nil, nil, nil, false
|
||||
}
|
||||
case lhst.Main == shaderir.Vec2 && rhst.Main == shaderir.Mat2 ||
|
||||
lhst.Main == shaderir.Mat2 && rhst.Main == shaderir.Vec2:
|
||||
t = shaderir.Type{Main: shaderir.Vec2}
|
||||
case lhst.Main == shaderir.Vec3 && rhst.Main == shaderir.Mat3 ||
|
||||
lhst.Main == shaderir.Mat3 && rhst.Main == shaderir.Vec3:
|
||||
t = shaderir.Type{Main: shaderir.Vec3}
|
||||
case lhst.Main == shaderir.Vec4 && rhst.Main == shaderir.Mat4 ||
|
||||
lhst.Main == shaderir.Mat4 && rhst.Main == shaderir.Vec4:
|
||||
t = shaderir.Type{Main: shaderir.Vec4}
|
||||
default:
|
||||
cs.addError(e.Pos(), fmt.Sprintf("invalid expression: %s %s %s", lhst.String(), e.Op, rhst.String()))
|
||||
return nil, nil, nil, false
|
||||
}
|
||||
|
||||
op, ok := shaderir.OpFromToken(e.Op)
|
||||
if !ok {
|
||||
cs.addError(e.Pos(), fmt.Sprintf("unexpected operator: %s", e.Op))
|
||||
return nil, nil, nil, false
|
||||
}
|
||||
|
||||
return []shaderir.Expr{
|
||||
{
|
||||
Type: shaderir.Binary,
|
||||
Op: op,
|
||||
Exprs: []shaderir.Expr{lhs[0], rhs[0]},
|
||||
},
|
||||
}, []shaderir.Type{t}, stmts, true
|
||||
case *ast.CallExpr:
|
||||
var (
|
||||
callee shaderir.Expr
|
||||
args []shaderir.Expr
|
||||
argts []shaderir.Type
|
||||
stmts []shaderir.Stmt
|
||||
)
|
||||
|
||||
// Parse the argument first for the order of the statements.
|
||||
for _, a := range e.Args {
|
||||
es, ts, ss, ok := cs.parseExpr(block, a)
|
||||
if !ok {
|
||||
return nil, nil, nil, false
|
||||
}
|
||||
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))
|
||||
return nil, nil, nil, false
|
||||
}
|
||||
args = append(args, es...)
|
||||
argts = append(argts, ts...)
|
||||
stmts = append(stmts, ss...)
|
||||
}
|
||||
|
||||
// TODO: When len(ss) is not 0?
|
||||
es, _, ss, ok := cs.parseExpr(block, e.Fun)
|
||||
if !ok {
|
||||
return nil, nil, nil, false
|
||||
}
|
||||
if len(es) != 1 {
|
||||
cs.addError(e.Pos(), fmt.Sprintf("multiple-value context is not available at a callee: %s", e.Fun))
|
||||
return nil, nil, nil, false
|
||||
}
|
||||
callee = es[0]
|
||||
stmts = append(stmts, ss...)
|
||||
|
||||
// For built-in functions, we can call this in this position. Return an expression for the function
|
||||
// call.
|
||||
if callee.Type == shaderir.BuiltinFuncExpr {
|
||||
var t shaderir.Type
|
||||
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]
|
||||
}
|
||||
return []shaderir.Expr{
|
||||
{
|
||||
Type: shaderir.Call,
|
||||
Exprs: append([]shaderir.Expr{callee}, args...),
|
||||
},
|
||||
}, []shaderir.Type{t}, stmts, true
|
||||
}
|
||||
|
||||
if callee.Type != shaderir.FunctionExpr {
|
||||
cs.addError(e.Pos(), fmt.Sprintf("function callee must be a funciton name but %s", e.Fun))
|
||||
return nil, nil, nil, false
|
||||
}
|
||||
|
||||
f := cs.funcs[callee.Index]
|
||||
|
||||
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 {
|
||||
if len(outParams) != 0 {
|
||||
cs.addError(e.Pos(), fmt.Sprintf("a function returning value cannot have out-params so far: %s", e.Fun))
|
||||
return nil, nil, nil, false
|
||||
}
|
||||
|
||||
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,
|
||||
},
|
||||
}, []shaderir.Type{t}, stmts, true
|
||||
}
|
||||
|
||||
// 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...),
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
if len(outParams) == 0 {
|
||||
// TODO: Is this an error?
|
||||
}
|
||||
|
||||
var exprs []shaderir.Expr
|
||||
for _, p := range outParams {
|
||||
exprs = append(exprs, shaderir.Expr{
|
||||
Type: shaderir.LocalVariable,
|
||||
Index: p,
|
||||
})
|
||||
}
|
||||
return exprs, f.ir.OutParams, stmts, true
|
||||
|
||||
case *ast.Ident:
|
||||
if i, t, ok := block.findLocalVariable(e.Name); ok {
|
||||
return []shaderir.Expr{
|
||||
{
|
||||
Type: shaderir.LocalVariable,
|
||||
Index: i,
|
||||
},
|
||||
}, []shaderir.Type{t}, nil, true
|
||||
}
|
||||
if i, ok := cs.findFunction(e.Name); ok {
|
||||
return []shaderir.Expr{
|
||||
{
|
||||
Type: shaderir.FunctionExpr,
|
||||
Index: i,
|
||||
},
|
||||
}, nil, nil, true
|
||||
}
|
||||
if i, ok := cs.findUniformVariable(e.Name); ok {
|
||||
return []shaderir.Expr{
|
||||
{
|
||||
Type: shaderir.UniformVariable,
|
||||
Index: i,
|
||||
},
|
||||
}, []shaderir.Type{cs.ir.Uniforms[i]}, nil, true
|
||||
}
|
||||
if f, ok := shaderir.ParseBuiltinFunc(e.Name); ok {
|
||||
return []shaderir.Expr{
|
||||
{
|
||||
Type: shaderir.BuiltinFuncExpr,
|
||||
BuiltinFunc: f,
|
||||
},
|
||||
}, nil, nil, true
|
||||
}
|
||||
cs.addError(e.Pos(), fmt.Sprintf("unexpected identifier: %s", e.Name))
|
||||
|
||||
case *ast.ParenExpr:
|
||||
return cs.parseExpr(block, e.X)
|
||||
|
||||
case *ast.SelectorExpr:
|
||||
exprs, _, stmts, ok := cs.parseExpr(block, e.X)
|
||||
if !ok {
|
||||
return nil, nil, nil, false
|
||||
}
|
||||
if len(exprs) != 1 {
|
||||
cs.addError(e.Pos(), fmt.Sprintf("multiple-value context is not available at a selector: %s", e.X))
|
||||
return nil, nil, nil, false
|
||||
}
|
||||
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, false
|
||||
}
|
||||
return []shaderir.Expr{
|
||||
{
|
||||
Type: shaderir.FieldSelector,
|
||||
Exprs: []shaderir.Expr{
|
||||
exprs[0],
|
||||
{
|
||||
Type: shaderir.SwizzlingExpr,
|
||||
Swizzling: e.Sel.Name,
|
||||
},
|
||||
},
|
||||
},
|
||||
}, []shaderir.Type{t}, stmts, true
|
||||
|
||||
case *ast.UnaryExpr:
|
||||
exprs, t, stmts, ok := cs.parseExpr(block, e.X)
|
||||
if !ok {
|
||||
return nil, nil, nil, false
|
||||
}
|
||||
if len(exprs) != 1 {
|
||||
cs.addError(e.Pos(), fmt.Sprintf("multiple-value context is not available at a unary operator: %s", e.X))
|
||||
return nil, nil, nil, false
|
||||
}
|
||||
|
||||
if exprs[0].Type == shaderir.NumberExpr {
|
||||
v := gconstant.UnaryOp(e.Op, exprs[0].Const, 0)
|
||||
t := shaderir.Type{Main: shaderir.Int}
|
||||
if v.Kind() == gconstant.Float {
|
||||
t = shaderir.Type{Main: shaderir.Float}
|
||||
}
|
||||
return []shaderir.Expr{
|
||||
{
|
||||
Type: shaderir.NumberExpr,
|
||||
Const: v,
|
||||
},
|
||||
}, []shaderir.Type{t}, stmts, true
|
||||
}
|
||||
|
||||
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))
|
||||
return nil, nil, nil, false
|
||||
}
|
||||
return []shaderir.Expr{
|
||||
{
|
||||
Type: shaderir.Unary,
|
||||
Op: op,
|
||||
Exprs: exprs,
|
||||
},
|
||||
}, t, stmts, true
|
||||
default:
|
||||
cs.addError(e.Pos(), fmt.Sprintf("expression not implemented: %#v", e))
|
||||
}
|
||||
return nil, nil, nil, false
|
||||
}
|
||||
|
243
internal/shader/stmt.go
Normal file
243
internal/shader/stmt.go
Normal file
@ -0,0 +1,243 @@
|
||||
// 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"
|
||||
|
||||
"github.com/hajimehoshi/ebiten/internal/shaderir"
|
||||
)
|
||||
|
||||
func (cs *compileState) parseStmt(block *block, stmt ast.Stmt, inParams []variable) bool {
|
||||
switch stmt := stmt.(type) {
|
||||
case *ast.AssignStmt:
|
||||
switch stmt.Tok {
|
||||
case token.DEFINE:
|
||||
if len(stmt.Lhs) != len(stmt.Rhs) && len(stmt.Rhs) != 1 {
|
||||
cs.addError(stmt.Pos(), fmt.Sprintf("single-value context and multiple-value context cannot be mixed"))
|
||||
return false
|
||||
}
|
||||
|
||||
if !cs.assign(block, stmt.Pos(), stmt.Lhs, stmt.Rhs, true) {
|
||||
return false
|
||||
}
|
||||
case token.ASSIGN:
|
||||
// TODO: What about the statement `a,b = b,a?`
|
||||
if len(stmt.Lhs) != len(stmt.Rhs) && len(stmt.Rhs) != 1 {
|
||||
cs.addError(stmt.Pos(), fmt.Sprintf("single-value context and multiple-value context cannot be mixed"))
|
||||
return false
|
||||
}
|
||||
if !cs.assign(block, stmt.Pos(), stmt.Lhs, stmt.Rhs, false) {
|
||||
return false
|
||||
}
|
||||
case token.ADD_ASSIGN, token.SUB_ASSIGN, token.MUL_ASSIGN, token.QUO_ASSIGN, token.REM_ASSIGN:
|
||||
var op shaderir.Op
|
||||
switch stmt.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
|
||||
}
|
||||
rhs, _, stmts, ok := cs.parseExpr(block, stmt.Rhs[0])
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
block.ir.Stmts = append(block.ir.Stmts, stmts...)
|
||||
lhs, _, stmts, ok := cs.parseExpr(block, stmt.Lhs[0])
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
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(stmt.Pos(), fmt.Sprintf("unexpected token: %s", stmt.Tok))
|
||||
}
|
||||
case *ast.BlockStmt:
|
||||
b, ok := cs.parseBlock(block, stmt, nil, nil)
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
block.ir.Stmts = append(block.ir.Stmts, shaderir.Stmt{
|
||||
Type: shaderir.BlockStmt,
|
||||
Blocks: []shaderir.Block{
|
||||
b.ir,
|
||||
},
|
||||
})
|
||||
case *ast.DeclStmt:
|
||||
if !cs.parseDecl(block, stmt.Decl) {
|
||||
return false
|
||||
}
|
||||
case *ast.ReturnStmt:
|
||||
for i, r := range stmt.Results {
|
||||
exprs, _, stmts, ok := cs.parseExpr(block, r)
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
block.ir.Stmts = append(block.ir.Stmts, stmts...)
|
||||
if len(exprs) == 0 {
|
||||
continue
|
||||
}
|
||||
if len(exprs) > 1 {
|
||||
cs.addError(r.Pos(), "multiple-context with return is not implemented yet")
|
||||
continue
|
||||
}
|
||||
block.ir.Stmts = append(block.ir.Stmts, shaderir.Stmt{
|
||||
Type: shaderir.Assign,
|
||||
Exprs: []shaderir.Expr{
|
||||
{
|
||||
Type: shaderir.LocalVariable,
|
||||
Index: len(inParams) + i,
|
||||
},
|
||||
exprs[0],
|
||||
},
|
||||
})
|
||||
}
|
||||
block.ir.Stmts = append(block.ir.Stmts, shaderir.Stmt{
|
||||
Type: shaderir.Return,
|
||||
})
|
||||
case *ast.ExprStmt:
|
||||
exprs, _, stmts, ok := cs.parseExpr(block, stmt.X)
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
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(stmt.Pos(), fmt.Sprintf("unexpected statement: %#v", stmt))
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func (cs *compileState) assign(block *block, pos token.Pos, lhs, rhs []ast.Expr, define bool) bool {
|
||||
var rhsExprs []shaderir.Expr
|
||||
var rhsTypes []shaderir.Type
|
||||
|
||||
for i, e := range lhs {
|
||||
if len(lhs) == len(rhs) {
|
||||
// Prase RHS first for the order of the statements.
|
||||
r, origts, stmts, ok := cs.parseExpr(block, rhs[i])
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
block.ir.Stmts = append(block.ir.Stmts, stmts...)
|
||||
|
||||
if define {
|
||||
v := variable{
|
||||
name: e.(*ast.Ident).Name,
|
||||
}
|
||||
ts, ok := cs.functionReturnTypes(block, rhs[i])
|
||||
if !ok {
|
||||
ts = origts
|
||||
}
|
||||
if len(ts) > 1 {
|
||||
cs.addError(pos, fmt.Sprintf("single-value context and multiple-value context cannot be mixed"))
|
||||
return false
|
||||
}
|
||||
if len(ts) == 1 {
|
||||
v.typ = ts[0]
|
||||
}
|
||||
block.vars = append(block.vars, v)
|
||||
}
|
||||
|
||||
if len(r) > 1 {
|
||||
cs.addError(pos, fmt.Sprintf("single-value context and multiple-value context cannot be mixed"))
|
||||
return false
|
||||
}
|
||||
|
||||
l, _, stmts, ok := cs.parseExpr(block, lhs[i])
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
block.ir.Stmts = append(block.ir.Stmts, stmts...)
|
||||
|
||||
if r[0].Type == shaderir.NumberExpr {
|
||||
switch block.vars[l[0].Index].typ.Main {
|
||||
case shaderir.Int:
|
||||
r[0].ConstType = shaderir.ConstTypeInt
|
||||
case shaderir.Float:
|
||||
r[0].ConstType = shaderir.ConstTypeFloat
|
||||
}
|
||||
}
|
||||
|
||||
block.ir.Stmts = append(block.ir.Stmts, shaderir.Stmt{
|
||||
Type: shaderir.Assign,
|
||||
Exprs: []shaderir.Expr{l[0], r[0]},
|
||||
})
|
||||
} else {
|
||||
if i == 0 {
|
||||
var stmts []shaderir.Stmt
|
||||
var ok bool
|
||||
rhsExprs, rhsTypes, stmts, ok = cs.parseExpr(block, rhs[0])
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
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...)
|
||||
}
|
||||
|
||||
if define {
|
||||
v := variable{
|
||||
name: e.(*ast.Ident).Name,
|
||||
}
|
||||
v.typ = rhsTypes[i]
|
||||
block.vars = append(block.vars, v)
|
||||
}
|
||||
|
||||
l, _, stmts, ok := cs.parseExpr(block, lhs[i])
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
block.ir.Stmts = append(block.ir.Stmts, stmts...)
|
||||
|
||||
block.ir.Stmts = append(block.ir.Stmts, shaderir.Stmt{
|
||||
Type: shaderir.Assign,
|
||||
Exprs: []shaderir.Expr{l[0], rhsExprs[i]},
|
||||
})
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
Loading…
Reference in New Issue
Block a user