| // Copyright (C) 2014 The Android Open Source Project |
| // |
| // 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 resolver |
| |
| import ( |
| "android.googlesource.com/platform/tools/gpu/api/ast" |
| "android.googlesource.com/platform/tools/gpu/api/semantic" |
| ) |
| |
| func block(ctx *context, in *ast.Block, owner semantic.Node) *semantic.Block { |
| out := &semantic.Block{AST: in} |
| if in == nil { |
| return out |
| } |
| ctx.with(semantic.VoidType, func() { |
| ctx.scope.block = &out.Statements |
| r := body(ctx, in.Statements, owner) |
| if r != nil { |
| ctx.addStatement(r) |
| } |
| }) |
| ctx.mappings[in] = out |
| return out |
| } |
| |
| // body is a resolve function that processes a list of statements and injects them |
| // into the context's current block. |
| // the final return statement, if present, is not injected, but returned from the |
| // function, as it often needs special handling depending on the owner of the |
| // statements |
| func body(ctx *context, in []ast.Node, owner semantic.Node) *semantic.Return { |
| f, isFunction := owner.(*semantic.Function) |
| var returnStatement *ast.Return |
| // we need to check and strip the "return" if the function is supposed to have one |
| if isFunction && !isVoid(f.Return.Type) { |
| if len(in) == 0 { |
| ctx.errorf(f.AST, "Missing return statement") |
| } else if r, ok := in[len(in)-1].(*ast.Return); !ok { |
| ctx.errorf(f.AST, "Last statement must be a return") |
| } else { |
| in = in[0 : len(in)-1] |
| returnStatement = r |
| } |
| } |
| // now process the non return statements |
| for _, s := range in { |
| ctx.addStatement(statement(ctx, s)) |
| } |
| // and special case the return statement allowing access to the return parameter |
| if returnStatement != nil { |
| return return_(ctx, returnStatement, f) |
| } |
| return nil |
| } |
| |
| func statement(ctx *context, in ast.Node) semantic.Node { |
| switch in := in.(type) { |
| case *ast.Assign: |
| return assign(ctx, in) |
| case *ast.DeclareLocal: |
| return declareLocal(ctx, in) |
| case *ast.Branch: |
| return branch(ctx, in) |
| case *ast.Switch: |
| return switch_(ctx, in) |
| case *ast.Iteration: |
| return iteration(ctx, in) |
| case *ast.Call: |
| e := call(ctx, in) |
| if !isVoid(e.ExpressionType()) { |
| ctx.errorf(in, "function with return type as statement not allowed") |
| return invalid{} |
| } |
| return e |
| case *ast.Return: |
| ctx.errorf(in, "unexpected return") |
| return invalid{} |
| case *ast.Generic: |
| ctx.errorf(in.Name, "unexpected identifier %s", in.Name.Value) |
| return invalid{} |
| default: |
| ctx.errorf(in, "not a statement (%T)", in) |
| return invalid{} |
| } |
| } |
| |
| func assign(ctx *context, in *ast.Assign) semantic.Node { |
| lhs := expression(ctx, in.LHS) |
| var rhs semantic.Expression |
| ctx.with(lhs.ExpressionType(), func() { |
| rhs = expression(ctx, in.RHS) |
| }) |
| var out semantic.Node |
| inferUnknown(ctx, lhs, rhs) |
| lt := lhs.ExpressionType() |
| rt := rhs.ExpressionType() |
| if !assignable(lt, rt) { |
| ctx.errorf(in, "cannot assign %s to %s", typename(rt), typename(lt)) |
| } |
| switch lhs := lhs.(type) { |
| case *semantic.ArrayIndex: |
| out = &semantic.ArrayAssign{AST: in, To: lhs, Value: rhs, Operator: in.Operator} |
| case *semantic.MapIndex: |
| out = &semantic.MapAssign{AST: in, To: lhs, Value: rhs, Operator: in.Operator} |
| case *semantic.SliceIndex: |
| out = &semantic.SliceAssign{AST: in, To: lhs, Value: rhs, Operator: in.Operator} |
| default: |
| out = &semantic.Assign{AST: in, LHS: lhs, Operator: in.Operator, RHS: rhs} |
| } |
| ctx.mappings[in] = out |
| return out |
| } |
| |
| func addLocal(ctx *context, in *ast.DeclareLocal, name string, value semantic.Expression) *semantic.DeclareLocal { |
| out := &semantic.DeclareLocal{AST: in} |
| out.Local = &semantic.Local{ |
| Declaration: out, |
| Named: semantic.Named(name), |
| Value: value, |
| Type: value.ExpressionType(), |
| } |
| if isVoid(out.Local.Type) { |
| ctx.errorf(in, "void in local declaration") |
| } |
| ctx.addNamed(out.Local) |
| ctx.mappings[in] = out |
| return out |
| } |
| |
| func declareLocal(ctx *context, in *ast.DeclareLocal) *semantic.DeclareLocal { |
| out := addLocal(ctx, in, in.Name.Value, expression(ctx, in.RHS)) |
| ctx.mappings[in] = out |
| return out |
| } |
| |
| func branch(ctx *context, in *ast.Branch) *semantic.Branch { |
| out := &semantic.Branch{AST: in} |
| out.Condition = expression(ctx, in.Condition) |
| ct := out.Condition.ExpressionType() |
| if ct == nil { |
| ctx.errorf(in, "condition was not valid") |
| return out |
| } |
| if !equal(ct, semantic.BoolType) { |
| ctx.errorf(in, "if condition must be boolean (got %s)", typename(ct)) |
| } |
| out.True = block(ctx, in.True, out) |
| out.False = block(ctx, in.False, out) |
| ctx.mappings[in] = out |
| return out |
| } |
| |
| func switch_(ctx *context, in *ast.Switch) *semantic.Switch { |
| out := &semantic.Switch{AST: in} |
| out.Value = expression(ctx, in.Value) |
| vt := out.Value.ExpressionType() |
| for _, c := range in.Cases { |
| out.Cases = append(out.Cases, case_(ctx, c, vt)) |
| } |
| if in.Default != nil { |
| out.Default = block(ctx, in.Default.Block, out) |
| } |
| ctx.mappings[in] = out |
| return out |
| } |
| |
| // case_ translates Case in to a switch Case. |
| // vt is the resolved type of the switch value being compared against, and can |
| // be used to infer the case condition type. |
| func case_(ctx *context, in *ast.Case, vt semantic.Type) *semantic.Case { |
| out := &semantic.Case{AST: in} |
| ctx.with(vt, func() { |
| for _, cond := range in.Conditions { |
| exp := expression(ctx, cond) |
| out.Conditions = append(out.Conditions, exp) |
| ct := exp.ExpressionType() |
| if !comparable(vt, ct) { |
| ctx.errorf(cond, "switch value %s is not comparable with case condition %s", typename(vt), typename(ct)) |
| } |
| } |
| }) |
| out.Block = block(ctx, in.Block, out) |
| ctx.mappings[in] = out |
| return out |
| } |
| |
| func iteration(ctx *context, in *ast.Iteration) *semantic.Iteration { |
| v := &semantic.Local{Named: semantic.Named(in.Variable.Value)} |
| ctx.mappings[in.Variable] = v |
| out := &semantic.Iteration{AST: in, Iterator: v} |
| out.Iterable = expression(ctx, in.Iterable) |
| if b, ok := out.Iterable.(*semantic.BinaryOp); !ok { |
| ctx.errorf(in, "iterable can only be range operator, got %T", b) |
| } else if b.Operator != ast.OpRange { |
| ctx.errorf(in, "iterable can only be range operator, got %s\n", b.Operator) |
| } |
| v.Type = out.Iterable.ExpressionType() |
| ctx.with(semantic.VoidType, func() { |
| ctx.addNamed(v) |
| out.Block = block(ctx, in.Block, out) |
| }) |
| ctx.mappings[in] = out |
| return out |
| } |
| |
| func return_(ctx *context, in *ast.Return, f *semantic.Function) *semantic.Return { |
| out := &semantic.Return{AST: in} |
| out.Function = f |
| ctx.with(f.Return.Type, func() { |
| out.Value = expression(ctx, in.Value) |
| }) |
| inferUnknown(ctx, f.Return, out.Value) |
| rt := out.Value.ExpressionType() |
| if !assignable(f.Return.Type, rt) { |
| ctx.errorf(in, "cannot assign %s to %s", typename(rt), typename(f.Return.Type)) |
| } |
| ctx.mappings[in] = out |
| return out |
| } |