blob: 77d0f0940e84e3048941f9fa7acfe823f91e99f2 [file] [log] [blame]
// 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 (
"fmt"
"reflect"
"android.googlesource.com/platform/tools/gpu/api/ast"
"android.googlesource.com/platform/tools/gpu/api/semantic"
"android.googlesource.com/platform/tools/gpu/parse"
)
type context struct {
errors parse.ErrorList
api *semantic.API
types map[string]semantic.Type
macros []*macroStub
scope *scope
nextId uint64
mappings ASTToSemantic
}
type scope struct {
outer *scope
entries map[string][]semantic.Node
inferType semantic.Type
block *[]semantic.Node
}
func (ctx *context) errorf(at interface{}, message string, args ...interface{}) {
n, ok := at.(ast.Node)
var f parse.Fragment
if !ok {
v := reflect.ValueOf(at)
if v.Kind() == reflect.Ptr {
v = v.Elem()
}
if v.Kind() == reflect.Struct {
a := v.FieldByName("AST")
if a.IsValid() {
n, ok = a.Interface().(ast.Node)
}
}
}
if !ok {
ctx.errors.Add(nil, nil, "Error non node %T", at)
} else if n == nil || reflect.ValueOf(n).IsNil() {
ctx.errors.Add(nil, nil, "Error nil node %T", at)
} else {
f = n.Fragment()
if f == nil {
ctx.errors.Add(nil, nil, "Error at %T node with nil fragment", at)
}
}
ctx.errors.Add(nil, f, message, args...)
}
func (ctx *context) icef(at ast.Node, message string, args ...interface{}) {
ctx.errorf(at, "INTERNAL ERROR: "+message, args...)
}
// with evaluates the action within a new nested scope.
// The scope is available as ctx.scope, and the original scope is
// restored before this function returns.
// Type t is used for type inference within the new scope (for
// instance when resolving untyped numeric constants)
func (ctx *context) with(t semantic.Type, action func()) {
original := ctx.scope
ctx.scope = &scope{
outer: ctx.scope,
block: ctx.scope.block,
entries: map[string][]semantic.Node{},
inferType: t,
}
defer func() { ctx.scope = original }()
action()
}
// add binds a name to a specific value within the current and nested scopes.
func (ctx *context) add(name string, value semantic.Node) {
list, _ := ctx.scope.entries[name]
ctx.scope.entries[name] = append(list, value)
}
// find searches the scope stack for a bindings that matches the name.
func (ctx *context) find(name string) []semantic.Node {
if ctx.scope == nil {
return nil
}
result, _ := ctx.scope.entries[name]
for search := ctx.scope.outer; search != nil; search = search.outer {
list, ok := search.entries[name]
if ok && len(list) > 0 {
result = append(result, list...)
}
}
return result
}
// disambiguate takes a list of possible scope values and attempts to see if
// one of them is unambiguously the right choice in the current context.
// For instance, if the values are all enum entries but only one of them
// matches the current enum inference.
func (ctx *context) disambiguate(matches []semantic.Node) []semantic.Node {
if len(matches) <= 1 {
return matches
}
var enum *semantic.Enum
for test := ctx.scope; test != nil; test = test.outer {
if test.inferType != nil {
if e, ok := test.inferType.(*semantic.Enum); ok {
enum = e
break
}
}
}
if enum == nil {
// No disambiguating enums present
return matches
}
var res *semantic.EnumEntry
for _, m := range matches {
if ev, ok := m.(*semantic.EnumEntry); ok {
if enum == ev.Enum {
// We found a disambiguation match
res = ev
}
} else {
// Non enum match found
return matches
}
}
if res == nil {
return matches
}
// Matched exactly once
return []semantic.Node{res}
}
// get searches the scope stack for a bindings that matches the name.
// If it cannot find exactly 1 unambiguous match, it reports an error, and
// nil is returned.
func (ctx *context) get(at ast.Node, name string) semantic.Node {
if name == "_" {
return &semantic.Ignore{AST: at}
}
matches := ctx.disambiguate(ctx.find(name))
switch len(matches) {
case 0:
ctx.errorf(at, "Unknown identifier %s", name)
return nil
case 1:
return matches[0]
default:
possibilities := ""
for i, m := range matches {
if i > 0 {
possibilities += ", "
}
switch t := m.(type) {
case *semantic.EnumEntry:
possibilities += fmt.Sprintf("%s.%s", t.Enum.Name, t.Name)
case *semantic.Parameter:
possibilities += fmt.Sprintf("parameter %q", t.Name)
case semantic.Type:
possibilities += fmt.Sprintf("type %q [%T]", typename(t), t)
default:
possibilities += fmt.Sprintf("[%T]%v", t, t)
}
}
ctx.errorf(at, "Ambiguous identifier %q [using %s].\n Could be: %s", name, typename(ctx.scope.inferType), possibilities)
return nil
}
}
func (ctx *context) addType(t semantic.Type) {
name := t.Typename()
if _, present := ctx.types[name]; present {
ctx.errorf(t, "Duplicate type %s", name)
}
ctx.types[name] = t
}
func (ctx *context) findType(at ast.Node, name string) semantic.Type {
t, found := ctx.types[name]
if !found {
return nil
}
return t
}
func (ctx *context) addStatement(s semantic.Node) {
if _, isInvalid := s.(invalid); isInvalid {
return
}
*ctx.scope.block = append(*ctx.scope.block, s)
}
func (ctx *context) uid() uint64 {
id := ctx.nextId
ctx.nextId++
return id
}