| // 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 api holds the main interface to the api language libraries. |
| // It provides functions for going from api files to abstract syntax trees and |
| // processed semantic trees. |
| package api |
| |
| import ( |
| "fmt" |
| "io/ioutil" |
| "path/filepath" |
| "sort" |
| |
| "android.googlesource.com/platform/tools/gpu/api/ast" |
| "android.googlesource.com/platform/tools/gpu/api/parser" |
| "android.googlesource.com/platform/tools/gpu/api/resolver" |
| "android.googlesource.com/platform/tools/gpu/api/semantic" |
| "android.googlesource.com/platform/tools/gpu/parse" |
| ) |
| |
| // Processor holds the state when resolving multiple api files. |
| type Processor struct { |
| Parsed map[string]*ast.API |
| Resolved map[string]*semantic.API |
| } |
| |
| // DefaultProcessor is the Processor used in the package level functions. |
| // Most applications will not need multiple instances of a Processor, and can |
| // just use this one. |
| var DefaultProcessor = Processor{ |
| Parsed: map[string]*ast.API{}, |
| Resolved: map[string]*semantic.API{}, |
| } |
| |
| // Parse parses the api file with the DefaultProcessor. |
| // See Processor.Parse for details. |
| func Parse(apiname string) (*ast.API, parse.ErrorList) { |
| return DefaultProcessor.Parse(apiname) |
| } |
| |
| // Parse returns an ast that represents the supplied filename. |
| // It if the file has already been parsed, the cached ast will be returned, |
| // otherwise it invokes parser.Parse on the content of the supplied file name. |
| func (p *Processor) Parse(path string) (*ast.API, parse.ErrorList) { |
| if api, ok := p.Parsed[path]; ok { |
| return api, nil |
| } |
| info, err := ioutil.ReadFile(path) |
| if err != nil { |
| return nil, parse.ErrorList{parse.Error{Message: err.Error()}} |
| } |
| return parser.Parse(string(info)) |
| } |
| |
| // Resolve resolves the api file with the DefaultProcessor. |
| // See Processor.Resolve for details. |
| func Resolve(apiname string, mappings resolver.ASTToSemantic) (*semantic.API, parse.ErrorList) { |
| return DefaultProcessor.Resolve(apiname, mappings) |
| } |
| |
| // Resolve returns a semantic.API that represents the supplied api file name. |
| // If the file has already been resolved, the cached semantic tree is returned, |
| // otherwise the file and all dependant files are parsed using Processor.Parse. |
| // Recursive calls are made to Resolve for all named imports, and then finally |
| // the ast and all included ast's are handed to resolver.Resolve to do semantic |
| // processing. |
| func (p *Processor) Resolve(apiname string, mappings resolver.ASTToSemantic) (*semantic.API, parse.ErrorList) { |
| absname, err := filepath.Abs(apiname) |
| if err != nil { |
| return nil, parse.ErrorList{parse.Error{Message: err.Error()}} |
| } |
| wd, name := filepath.Split(absname) |
| return p.resolve(wd, name, mappings) |
| } |
| |
| func (p *Processor) resolve(wd, name string, mappings resolver.ASTToSemantic) (*semantic.API, parse.ErrorList) { |
| absname := filepath.Join(wd, name) |
| if api, ok := p.Resolved[absname]; ok { |
| if api == nil { // reentry detected |
| return nil, parse.ErrorList{parse.Error{ |
| Message: fmt.Sprintf("Recursive import %s", absname)}, |
| } |
| } |
| return api, nil |
| } |
| p.Resolved[absname] = nil // mark to prevent reentry |
| // Parse all the includes |
| includes := map[string]*ast.API{} |
| errs := p.include(includes, wd, name) |
| if len(errs) > 0 { |
| return nil, errs |
| } |
| // Build a sorted list of includes |
| names := make(sort.StringSlice, 0, len(includes)) |
| for name := range includes { |
| names = append(names, name) |
| } |
| names.Sort() |
| list := make([]*ast.API, len(names)) |
| for i, name := range names { |
| list[i] = includes[name] |
| } |
| // Resolve all the imports |
| imports := &semantic.Symbols{} |
| importPaths := map[string]string{} |
| for _, api := range list { |
| for _, i := range api.Imports { |
| if i.Name == nil { |
| // unnamed imports have already been included |
| continue |
| } |
| path := filepath.Join(wd, i.Path.Value) |
| if importedPath, seen := importPaths[i.Name.Value]; seen { |
| if path == importedPath { |
| // import with same path and name already included |
| continue |
| } |
| return nil, parse.ErrorList{parse.Error{ |
| Message: fmt.Sprintf("Import name '%s' used for different paths (%s != %s)", |
| i.Name.Value, path, importedPath)}, |
| } |
| } |
| api, errs := p.resolve(wd, i.Path.Value, mappings) |
| if len(errs) > 0 { |
| return nil, errs |
| } |
| imports.Add(i.Name.Value, api) |
| importPaths[i.Name.Value] = path |
| } |
| } |
| // Now resolve the api set as a single unit |
| api, errs := resolver.Resolve(list, imports, mappings) |
| if len(errs) == 0 { |
| p.Resolved[absname] = api |
| } |
| return api, errs |
| } |
| |
| func (p *Processor) include(includes map[string]*ast.API, wd string, apiname string) parse.ErrorList { |
| absname, err := filepath.Abs(filepath.Join(wd, apiname)) |
| if err != nil { |
| return parse.ErrorList{parse.Error{Message: err.Error()}} |
| } |
| if _, seen := includes[absname]; seen { |
| return nil |
| } |
| api, errs := p.Parse(absname) |
| if len(errs) > 0 { |
| return errs |
| } |
| includes[absname] = api |
| for _, i := range api.Imports { |
| if i.Name != nil { |
| // named imports don't get merged |
| continue |
| } |
| errs := p.include(includes, wd, i.Path.Value) |
| if len(errs) > 0 { |
| return errs |
| } |
| } |
| return nil |
| } |