OpenCores
URL https://opencores.org/ocsvn/openrisc/openrisc/trunk

Subversion Repositories openrisc

[/] [openrisc/] [trunk/] [gnu-dev/] [or1k-gcc/] [libgo/] [go/] [exp/] [types/] [gcimporter.go] - Rev 747

Compare with Previous | Blame | View Log

// Copyright 2011 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// This file implements an ast.Importer for gc generated object files.
// TODO(gri) Eventually move this into a separate package outside types.

package types

import (
        "errors"
        "fmt"
        "go/ast"
        "go/token"
        "io"
        "math/big"
        "os"
        "path/filepath"
        "runtime"
        "strconv"
        "text/scanner"
)

const trace = false // set to true for debugging

var (
        pkgRoot = filepath.Join(runtime.GOROOT(), "pkg", runtime.GOOS+"_"+runtime.GOARCH)
        pkgExts = [...]string{".a", ".5", ".6", ".8"}
)

// findPkg returns the filename and package id for an import path.
// If no file was found, an empty filename is returned.
func findPkg(path string) (filename, id string) {
        if len(path) == 0 {
                return
        }

        id = path
        var noext string
        switch path[0] {
        default:
                // "x" -> "$GOROOT/pkg/$GOOS_$GOARCH/x.ext", "x"
                noext = filepath.Join(pkgRoot, path)

        case '.':
                // "./x" -> "/this/directory/x.ext", "/this/directory/x"
                cwd, err := os.Getwd()
                if err != nil {
                        return
                }
                noext = filepath.Join(cwd, path)
                id = noext

        case '/':
                // "/x" -> "/x.ext", "/x"
                noext = path
        }

        // try extensions
        for _, ext := range pkgExts {
                filename = noext + ext
                if f, err := os.Stat(filename); err == nil && !f.IsDir() {
                        return
                }
        }

        filename = "" // not found
        return
}

// gcParser parses the exports inside a gc compiler-produced
// object/archive file and populates its scope with the results.
type gcParser struct {
        scanner scanner.Scanner
        tok     rune                   // current token
        lit     string                 // literal string; only valid for Ident, Int, String tokens
        id      string                 // package id of imported package
        imports map[string]*ast.Object // package id -> package object
}

func (p *gcParser) init(filename, id string, src io.Reader, imports map[string]*ast.Object) {
        p.scanner.Init(src)
        p.scanner.Error = func(_ *scanner.Scanner, msg string) { p.error(msg) }
        p.scanner.Mode = scanner.ScanIdents | scanner.ScanInts | scanner.ScanChars | scanner.ScanStrings | scanner.ScanComments | scanner.SkipComments
        p.scanner.Whitespace = 1<<'\t' | 1<<' '
        p.scanner.Filename = filename // for good error messages
        p.next()
        p.id = id
        p.imports = imports
}

func (p *gcParser) next() {
        p.tok = p.scanner.Scan()
        switch p.tok {
        case scanner.Ident, scanner.Int, scanner.String:
                p.lit = p.scanner.TokenText()
        default:
                p.lit = ""
        }
        if trace {
                fmt.Printf("%s: %q -> %q\n", scanner.TokenString(p.tok), p.scanner.TokenText(), p.lit)
        }
}

// GcImporter implements the ast.Importer signature.
func GcImporter(imports map[string]*ast.Object, path string) (pkg *ast.Object, err error) {
        if path == "unsafe" {
                return Unsafe, nil
        }

        defer func() {
                if r := recover(); r != nil {
                        err = r.(importError) // will re-panic if r is not an importError
                        if trace {
                                panic(err) // force a stack trace
                        }
                }
        }()

        filename, id := findPkg(path)
        if filename == "" {
                err = errors.New("can't find import: " + id)
                return
        }

        if pkg = imports[id]; pkg != nil {
                return // package was imported before
        }

        buf, err := ExportData(filename)
        if err != nil {
                return
        }
        defer buf.Close()

        if trace {
                fmt.Printf("importing %s (%s)\n", id, filename)
        }

        var p gcParser
        p.init(filename, id, buf, imports)
        pkg = p.parseExport()
        return
}

// Declare inserts a named object of the given kind in scope.
func (p *gcParser) declare(scope *ast.Scope, kind ast.ObjKind, name string) *ast.Object {
        // the object may have been imported before - if it exists
        // already in the respective package scope, return that object
        if obj := scope.Lookup(name); obj != nil {
                assert(obj.Kind == kind)
                return obj
        }

        // otherwise create a new object and insert it into the package scope
        obj := ast.NewObj(kind, name)
        if scope.Insert(obj) != nil {
                p.errorf("already declared: %v %s", kind, obj.Name)
        }

        // a new type object is a named type and may be referred
        // to before the underlying type is known - set it up
        if kind == ast.Typ {
                obj.Type = &Name{Obj: obj}
        }

        return obj
}

// ----------------------------------------------------------------------------
// Error handling

// Internal errors are boxed as importErrors.
type importError struct {
        pos scanner.Position
        err error
}

func (e importError) Error() string {
        return fmt.Sprintf("import error %s (byte offset = %d): %s", e.pos, e.pos.Offset, e.err)
}

func (p *gcParser) error(err interface{}) {
        if s, ok := err.(string); ok {
                err = errors.New(s)
        }
        // panic with a runtime.Error if err is not an error
        panic(importError{p.scanner.Pos(), err.(error)})
}

func (p *gcParser) errorf(format string, args ...interface{}) {
        p.error(fmt.Sprintf(format, args...))
}

func (p *gcParser) expect(tok rune) string {
        lit := p.lit
        if p.tok != tok {
                panic(1)
                p.errorf("expected %s, got %s (%s)", scanner.TokenString(tok), scanner.TokenString(p.tok), lit)
        }
        p.next()
        return lit
}

func (p *gcParser) expectSpecial(tok string) {
        sep := 'x' // not white space
        i := 0
        for i < len(tok) && p.tok == rune(tok[i]) && sep > ' ' {
                sep = p.scanner.Peek() // if sep <= ' ', there is white space before the next token
                p.next()
                i++
        }
        if i < len(tok) {
                p.errorf("expected %q, got %q", tok, tok[0:i])
        }
}

func (p *gcParser) expectKeyword(keyword string) {
        lit := p.expect(scanner.Ident)
        if lit != keyword {
                p.errorf("expected keyword %s, got %q", keyword, lit)
        }
}

// ----------------------------------------------------------------------------
// Import declarations

// ImportPath = string_lit .
//
func (p *gcParser) parsePkgId() *ast.Object {
        id, err := strconv.Unquote(p.expect(scanner.String))
        if err != nil {
                p.error(err)
        }

        switch id {
        case "":
                // id == "" stands for the imported package id
                // (only known at time of package installation)
                id = p.id
        case "unsafe":
                // package unsafe is not in the imports map - handle explicitly
                return Unsafe
        }

        pkg := p.imports[id]
        if pkg == nil {
                scope = ast.NewScope(nil)
                pkg = ast.NewObj(ast.Pkg, "")
                pkg.Data = scope
                p.imports[id] = pkg
        }

        return pkg
}

// dotIdentifier = ( ident | '·' ) { ident | int | '·' } .
func (p *gcParser) parseDotIdent() string {
        ident := ""
        if p.tok != scanner.Int {
                sep := 'x' // not white space
                for (p.tok == scanner.Ident || p.tok == scanner.Int || p.tok == '·') && sep > ' ' {
                        ident += p.lit
                        sep = p.scanner.Peek() // if sep <= ' ', there is white space before the next token
                        p.next()
                }
        }
        if ident == "" {
                p.expect(scanner.Ident) // use expect() for error handling
        }
        return ident
}

// ExportedName = "@" ImportPath "." dotIdentifier .
//
func (p *gcParser) parseExportedName() (*ast.Object, string) {
        p.expect('@')
        pkg := p.parsePkgId()
        p.expect('.')
        name := p.parseDotIdent()
        return pkg, name
}

// ----------------------------------------------------------------------------
// Types

// BasicType = identifier .
//
func (p *gcParser) parseBasicType() Type {
        id := p.expect(scanner.Ident)
        obj := Universe.Lookup(id)
        if obj == nil || obj.Kind != ast.Typ {
                p.errorf("not a basic type: %s", id)
        }
        return obj.Type.(Type)
}

// ArrayType = "[" int_lit "]" Type .
//
func (p *gcParser) parseArrayType() Type {
        // "[" already consumed and lookahead known not to be "]"
        lit := p.expect(scanner.Int)
        p.expect(']')
        elt := p.parseType()
        n, err := strconv.ParseUint(lit, 10, 64)
        if err != nil {
                p.error(err)
        }
        return &Array{Len: n, Elt: elt}
}

// MapType = "map" "[" Type "]" Type .
//
func (p *gcParser) parseMapType() Type {
        p.expectKeyword("map")
        p.expect('[')
        key := p.parseType()
        p.expect(']')
        elt := p.parseType()
        return &Map{Key: key, Elt: elt}
}

// Name = identifier | "?" | ExportedName  .
//
func (p *gcParser) parseName() (name string) {
        switch p.tok {
        case scanner.Ident:
                name = p.lit
                p.next()
        case '?':
                // anonymous
                p.next()
        case '@':
                // exported name prefixed with package path
                _, name = p.parseExportedName()
        default:
                p.error("name expected")
        }
        return
}

// Field = Name Type [ string_lit ] .
//
func (p *gcParser) parseField() (fld *ast.Object, tag string) {
        name := p.parseName()
        ftyp := p.parseType()
        if name == "" {
                // anonymous field - ftyp must be T or *T and T must be a type name
                if _, ok := Deref(ftyp).(*Name); !ok {
                        p.errorf("anonymous field expected")
                }
        }
        if p.tok == scanner.String {
                tag = p.expect(scanner.String)
        }
        fld = ast.NewObj(ast.Var, name)
        fld.Type = ftyp
        return
}

// StructType = "struct" "{" [ FieldList ] "}" .
// FieldList  = Field { ";" Field } .
//
func (p *gcParser) parseStructType() Type {
        var fields []*ast.Object
        var tags []string

        parseField := func() {
                fld, tag := p.parseField()
                fields = append(fields, fld)
                tags = append(tags, tag)
        }

        p.expectKeyword("struct")
        p.expect('{')
        if p.tok != '}' {
                parseField()
                for p.tok == ';' {
                        p.next()
                        parseField()
                }
        }
        p.expect('}')

        return &Struct{Fields: fields, Tags: tags}
}

// Parameter = ( identifier | "?" ) [ "..." ] Type [ string_lit ] .
//
func (p *gcParser) parseParameter() (par *ast.Object, isVariadic bool) {
        name := p.parseName()
        if name == "" {
                name = "_" // cannot access unnamed identifiers
        }
        if p.tok == '.' {
                p.expectSpecial("...")
                isVariadic = true
        }
        ptyp := p.parseType()
        // ignore argument tag
        if p.tok == scanner.String {
                p.expect(scanner.String)
        }
        par = ast.NewObj(ast.Var, name)
        par.Type = ptyp
        return
}

// Parameters    = "(" [ ParameterList ] ")" .
// ParameterList = { Parameter "," } Parameter .
//
func (p *gcParser) parseParameters() (list []*ast.Object, isVariadic bool) {
        parseParameter := func() {
                par, variadic := p.parseParameter()
                list = append(list, par)
                if variadic {
                        if isVariadic {
                                p.error("... not on final argument")
                        }
                        isVariadic = true
                }
        }

        p.expect('(')
        if p.tok != ')' {
                parseParameter()
                for p.tok == ',' {
                        p.next()
                        parseParameter()
                }
        }
        p.expect(')')

        return
}

// Signature = Parameters [ Result ] .
// Result    = Type | Parameters .
//
func (p *gcParser) parseSignature() *Func {
        params, isVariadic := p.parseParameters()

        // optional result type
        var results []*ast.Object
        switch p.tok {
        case scanner.Ident, '[', '*', '<', '@':
                // single, unnamed result
                result := ast.NewObj(ast.Var, "_")
                result.Type = p.parseType()
                results = []*ast.Object{result}
        case '(':
                // named or multiple result(s)
                var variadic bool
                results, variadic = p.parseParameters()
                if variadic {
                        p.error("... not permitted on result type")
                }
        }

        return &Func{Params: params, Results: results, IsVariadic: isVariadic}
}

// MethodOrEmbedSpec = Name [ Signature ] .
//
func (p *gcParser) parseMethodOrEmbedSpec() *ast.Object {
        p.parseName()
        if p.tok == '(' {
                p.parseSignature()
                // TODO(gri) compute method object
                return ast.NewObj(ast.Fun, "_")
        }
        // TODO lookup name and return that type
        return ast.NewObj(ast.Typ, "_")
}

// InterfaceType = "interface" "{" [ MethodOrEmbedList ] "}" .
// MethodOrEmbedList = MethodOrEmbedSpec { ";" MethodOrEmbedSpec } .
//
func (p *gcParser) parseInterfaceType() Type {
        var methods ObjList

        parseMethod := func() {
                switch m := p.parseMethodOrEmbedSpec(); m.Kind {
                case ast.Typ:
                        // TODO expand embedded methods
                case ast.Fun:
                        methods = append(methods, m)
                }
        }

        p.expectKeyword("interface")
        p.expect('{')
        if p.tok != '}' {
                parseMethod()
                for p.tok == ';' {
                        p.next()
                        parseMethod()
                }
        }
        p.expect('}')

        methods.Sort()
        return &Interface{Methods: methods}
}

// ChanType = ( "chan" [ "<-" ] | "<-" "chan" ) Type .
//
func (p *gcParser) parseChanType() Type {
        dir := ast.SEND | ast.RECV
        if p.tok == scanner.Ident {
                p.expectKeyword("chan")
                if p.tok == '<' {
                        p.expectSpecial("<-")
                        dir = ast.SEND
                }
        } else {
                p.expectSpecial("<-")
                p.expectKeyword("chan")
                dir = ast.RECV
        }
        elt := p.parseType()
        return &Chan{Dir: dir, Elt: elt}
}

// Type =
//      BasicType | TypeName | ArrayType | SliceType | StructType |
//      PointerType | FuncType | InterfaceType | MapType | ChanType |
//      "(" Type ")" .
// BasicType = ident .
// TypeName = ExportedName .
// SliceType = "[" "]" Type .
// PointerType = "*" Type .
// FuncType = "func" Signature .
//
func (p *gcParser) parseType() Type {
        switch p.tok {
        case scanner.Ident:
                switch p.lit {
                default:
                        return p.parseBasicType()
                case "struct":
                        return p.parseStructType()
                case "func":
                        // FuncType
                        p.next()
                        return p.parseSignature()
                case "interface":
                        return p.parseInterfaceType()
                case "map":
                        return p.parseMapType()
                case "chan":
                        return p.parseChanType()
                }
        case '@':
                // TypeName
                pkg, name := p.parseExportedName()
                return p.declare(pkg.Data.(*ast.Scope), ast.Typ, name).Type.(Type)
        case '[':
                p.next() // look ahead
                if p.tok == ']' {
                        // SliceType
                        p.next()
                        return &Slice{Elt: p.parseType()}
                }
                return p.parseArrayType()
        case '*':
                // PointerType
                p.next()
                return &Pointer{Base: p.parseType()}
        case '<':
                return p.parseChanType()
        case '(':
                // "(" Type ")"
                p.next()
                typ := p.parseType()
                p.expect(')')
                return typ
        }
        p.errorf("expected type, got %s (%q)", scanner.TokenString(p.tok), p.lit)
        return nil
}

// ----------------------------------------------------------------------------
// Declarations

// ImportDecl = "import" identifier string_lit .
//
func (p *gcParser) parseImportDecl() {
        p.expectKeyword("import")
        // The identifier has no semantic meaning in the import data.
        // It exists so that error messages can print the real package
        // name: binary.ByteOrder instead of "encoding/binary".ByteOrder.
        name := p.expect(scanner.Ident)
        pkg := p.parsePkgId()
        assert(pkg.Name == "" || pkg.Name == name)
        pkg.Name = name
}

// int_lit = [ "+" | "-" ] { "0" ... "9" } .
//
func (p *gcParser) parseInt() (sign, val string) {
        switch p.tok {
        case '-':
                p.next()
                sign = "-"
        case '+':
                p.next()
        }
        val = p.expect(scanner.Int)
        return
}

// number = int_lit [ "p" int_lit ] .
//
func (p *gcParser) parseNumber() Const {
        // mantissa
        sign, val := p.parseInt()
        mant, ok := new(big.Int).SetString(sign+val, 10)
        assert(ok)

        if p.lit == "p" {
                // exponent (base 2)
                p.next()
                sign, val = p.parseInt()
                exp64, err := strconv.ParseUint(val, 10, 0)
                if err != nil {
                        p.error(err)
                }
                exp := uint(exp64)
                if sign == "-" {
                        denom := big.NewInt(1)
                        denom.Lsh(denom, exp)
                        return Const{new(big.Rat).SetFrac(mant, denom)}
                }
                if exp > 0 {
                        mant.Lsh(mant, exp)
                }
                return Const{new(big.Rat).SetInt(mant)}
        }

        return Const{mant}
}

// ConstDecl   = "const" ExportedName [ Type ] "=" Literal .
// Literal     = bool_lit | int_lit | float_lit | complex_lit | string_lit .
// bool_lit    = "true" | "false" .
// complex_lit = "(" float_lit "+" float_lit ")" .
// rune_lit = "(" int_lit "+" int_lit ")" .
// string_lit  = `"` { unicode_char } `"` .
//
func (p *gcParser) parseConstDecl() {
        p.expectKeyword("const")
        pkg, name := p.parseExportedName()
        obj := p.declare(pkg.Data.(*ast.Scope), ast.Con, name)
        var x Const
        var typ Type
        if p.tok != '=' {
                obj.Type = p.parseType()
        }
        p.expect('=')
        switch p.tok {
        case scanner.Ident:
                // bool_lit
                if p.lit != "true" && p.lit != "false" {
                        p.error("expected true or false")
                }
                x = Const{p.lit == "true"}
                typ = Bool.Underlying
                p.next()
        case '-', scanner.Int:
                // int_lit
                x = p.parseNumber()
                typ = Int.Underlying
                if _, ok := x.val.(*big.Rat); ok {
                        typ = Float64.Underlying
                }
        case '(':
                // complex_lit or rune_lit
                p.next()
                if p.tok == scanner.Char {
                        p.next()
                        p.expect('+')
                        p.parseNumber()
                        p.expect(')')
                        // TODO: x = ...
                        break
                }
                re := p.parseNumber()
                p.expect('+')
                im := p.parseNumber()
                p.expect(')')
                x = Const{cmplx{re.val.(*big.Rat), im.val.(*big.Rat)}}
                typ = Complex128.Underlying
        case scanner.Char:
                // TODO: x = ...
                p.next()
        case scanner.String:
                // string_lit
                x = MakeConst(token.STRING, p.lit)
                p.next()
                typ = String.Underlying
        default:
                println(p.tok)
                p.errorf("expected literal got %s", scanner.TokenString(p.tok))
        }
        if obj.Type == nil {
                obj.Type = typ
        }
        obj.Data = x
}

// TypeDecl = "type" ExportedName Type .
//
func (p *gcParser) parseTypeDecl() {
        p.expectKeyword("type")
        pkg, name := p.parseExportedName()
        obj := p.declare(pkg.Data.(*ast.Scope), ast.Typ, name)

        // The type object may have been imported before and thus already
        // have a type associated with it. We still need to parse the type
        // structure, but throw it away if the object already has a type.
        // This ensures that all imports refer to the same type object for
        // a given type declaration.
        typ := p.parseType()

        if name := obj.Type.(*Name); name.Underlying == nil {
                assert(Underlying(typ) == typ)
                name.Underlying = typ
        }
}

// VarDecl = "var" ExportedName Type .
//
func (p *gcParser) parseVarDecl() {
        p.expectKeyword("var")
        pkg, name := p.parseExportedName()
        obj := p.declare(pkg.Data.(*ast.Scope), ast.Var, name)
        obj.Type = p.parseType()
}

// FuncBody = "{" ... "}" .
// 
func (p *gcParser) parseFuncBody() {
        p.expect('{')
        for i := 1; i > 0; p.next() {
                switch p.tok {
                case '{':
                        i++
                case '}':
                        i--
                }
        }
}

// FuncDecl = "func" ExportedName Signature [ FuncBody ] .
//
func (p *gcParser) parseFuncDecl() {
        // "func" already consumed
        pkg, name := p.parseExportedName()
        obj := p.declare(pkg.Data.(*ast.Scope), ast.Fun, name)
        obj.Type = p.parseSignature()
        if p.tok == '{' {
                p.parseFuncBody()
        }
}

// MethodDecl = "func" Receiver Name Signature .
// Receiver   = "(" ( identifier | "?" ) [ "*" ] ExportedName ")" [ FuncBody ].
//
func (p *gcParser) parseMethodDecl() {
        // "func" already consumed
        p.expect('(')
        p.parseParameter() // receiver
        p.expect(')')
        p.parseName() // unexported method names in imports are qualified with their package.
        p.parseSignature()
        if p.tok == '{' {
                p.parseFuncBody()
        }
}

// Decl = [ ImportDecl | ConstDecl | TypeDecl | VarDecl | FuncDecl | MethodDecl ] "\n" .
//
func (p *gcParser) parseDecl() {
        switch p.lit {
        case "import":
                p.parseImportDecl()
        case "const":
                p.parseConstDecl()
        case "type":
                p.parseTypeDecl()
        case "var":
                p.parseVarDecl()
        case "func":
                p.next() // look ahead
                if p.tok == '(' {
                        p.parseMethodDecl()
                } else {
                        p.parseFuncDecl()
                }
        }
        p.expect('\n')
}

// ----------------------------------------------------------------------------
// Export

// Export        = "PackageClause { Decl } "$$" .
// PackageClause = "package" identifier [ "safe" ] "\n" .
//
func (p *gcParser) parseExport() *ast.Object {
        p.expectKeyword("package")
        name := p.expect(scanner.Ident)
        if p.tok != '\n' {
                // A package is safe if it was compiled with the -u flag,
                // which disables the unsafe package.
                // TODO(gri) remember "safe" package
                p.expectKeyword("safe")
        }
        p.expect('\n')

        assert(p.imports[p.id] == nil)
        pkg := ast.NewObj(ast.Pkg, name)
        pkg.Data = ast.NewScope(nil)
        p.imports[p.id] = pkg

        for p.tok != '$' && p.tok != scanner.EOF {
                p.parseDecl()
        }

        if ch := p.scanner.Peek(); p.tok != '$' || ch != '$' {
                // don't call next()/expect() since reading past the
                // export data may cause scanner errors (e.g. NUL chars)
                p.errorf("expected '$$', got %s %c", scanner.TokenString(p.tok), ch)
        }

        if n := p.scanner.ErrorCount; n != 0 {
                p.errorf("expected no scanner errors, got %d", n)
        }

        return pkg
}

Compare with Previous | Blame | View Log

powered by: WebSVN 2.1.0

© copyright 1999-2024 OpenCores.org, equivalent to Oliscience, all rights reserved. OpenCores®, registered trademark.