mirror of
https://github.com/beego/bee.git
synced 2024-11-25 15:00:55 +00:00
263 lines
6.4 KiB
Go
263 lines
6.4 KiB
Go
|
// Copyright 2011 Gary Burd
|
||
|
// Copyright 2013 Unknown
|
||
|
//
|
||
|
// 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 main
|
||
|
|
||
|
import (
|
||
|
"go/ast"
|
||
|
"go/printer"
|
||
|
"go/scanner"
|
||
|
"go/token"
|
||
|
"math"
|
||
|
"strconv"
|
||
|
)
|
||
|
|
||
|
const (
|
||
|
notPredeclared = iota
|
||
|
predeclaredType
|
||
|
predeclaredConstant
|
||
|
predeclaredFunction
|
||
|
)
|
||
|
|
||
|
// predeclared represents the set of all predeclared identifiers.
|
||
|
var predeclared = map[string]int{
|
||
|
"bool": predeclaredType,
|
||
|
"byte": predeclaredType,
|
||
|
"complex128": predeclaredType,
|
||
|
"complex64": predeclaredType,
|
||
|
"error": predeclaredType,
|
||
|
"float32": predeclaredType,
|
||
|
"float64": predeclaredType,
|
||
|
"int16": predeclaredType,
|
||
|
"int32": predeclaredType,
|
||
|
"int64": predeclaredType,
|
||
|
"int8": predeclaredType,
|
||
|
"int": predeclaredType,
|
||
|
"rune": predeclaredType,
|
||
|
"string": predeclaredType,
|
||
|
"uint16": predeclaredType,
|
||
|
"uint32": predeclaredType,
|
||
|
"uint64": predeclaredType,
|
||
|
"uint8": predeclaredType,
|
||
|
"uint": predeclaredType,
|
||
|
"uintptr": predeclaredType,
|
||
|
|
||
|
"true": predeclaredConstant,
|
||
|
"false": predeclaredConstant,
|
||
|
"iota": predeclaredConstant,
|
||
|
"nil": predeclaredConstant,
|
||
|
|
||
|
"append": predeclaredFunction,
|
||
|
"cap": predeclaredFunction,
|
||
|
"close": predeclaredFunction,
|
||
|
"complex": predeclaredFunction,
|
||
|
"copy": predeclaredFunction,
|
||
|
"delete": predeclaredFunction,
|
||
|
"imag": predeclaredFunction,
|
||
|
"len": predeclaredFunction,
|
||
|
"make": predeclaredFunction,
|
||
|
"new": predeclaredFunction,
|
||
|
"panic": predeclaredFunction,
|
||
|
"print": predeclaredFunction,
|
||
|
"println": predeclaredFunction,
|
||
|
"real": predeclaredFunction,
|
||
|
"recover": predeclaredFunction,
|
||
|
}
|
||
|
|
||
|
const (
|
||
|
ExportLinkAnnotation AnnotationKind = iota
|
||
|
AnchorAnnotation
|
||
|
CommentAnnotation
|
||
|
PackageLinkAnnotation
|
||
|
BuiltinAnnotation
|
||
|
)
|
||
|
|
||
|
// annotationVisitor collects annotations.
|
||
|
type annotationVisitor struct {
|
||
|
annotations []Annotation
|
||
|
}
|
||
|
|
||
|
func (v *annotationVisitor) add(kind AnnotationKind, importPath string) {
|
||
|
v.annotations = append(v.annotations, Annotation{Kind: kind, ImportPath: importPath})
|
||
|
}
|
||
|
|
||
|
func (v *annotationVisitor) ignoreName() {
|
||
|
v.add(-1, "")
|
||
|
}
|
||
|
|
||
|
func (v *annotationVisitor) Visit(n ast.Node) ast.Visitor {
|
||
|
switch n := n.(type) {
|
||
|
case *ast.TypeSpec:
|
||
|
v.ignoreName()
|
||
|
ast.Walk(v, n.Type)
|
||
|
case *ast.FuncDecl:
|
||
|
if n.Recv != nil {
|
||
|
ast.Walk(v, n.Recv)
|
||
|
}
|
||
|
v.ignoreName()
|
||
|
ast.Walk(v, n.Type)
|
||
|
case *ast.Field:
|
||
|
for _ = range n.Names {
|
||
|
v.ignoreName()
|
||
|
}
|
||
|
ast.Walk(v, n.Type)
|
||
|
case *ast.ValueSpec:
|
||
|
for _ = range n.Names {
|
||
|
v.add(AnchorAnnotation, "")
|
||
|
}
|
||
|
if n.Type != nil {
|
||
|
ast.Walk(v, n.Type)
|
||
|
}
|
||
|
for _, x := range n.Values {
|
||
|
ast.Walk(v, x)
|
||
|
}
|
||
|
case *ast.Ident:
|
||
|
switch {
|
||
|
case n.Obj == nil && predeclared[n.Name] != notPredeclared:
|
||
|
v.add(BuiltinAnnotation, "")
|
||
|
case n.Obj != nil && ast.IsExported(n.Name):
|
||
|
v.add(ExportLinkAnnotation, "")
|
||
|
default:
|
||
|
v.ignoreName()
|
||
|
}
|
||
|
case *ast.SelectorExpr:
|
||
|
if x, _ := n.X.(*ast.Ident); x != nil {
|
||
|
if obj := x.Obj; obj != nil && obj.Kind == ast.Pkg {
|
||
|
if spec, _ := obj.Decl.(*ast.ImportSpec); spec != nil {
|
||
|
if path, err := strconv.Unquote(spec.Path.Value); err == nil {
|
||
|
v.add(PackageLinkAnnotation, path)
|
||
|
if path == "C" {
|
||
|
v.ignoreName()
|
||
|
} else {
|
||
|
v.add(ExportLinkAnnotation, path)
|
||
|
}
|
||
|
return nil
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
ast.Walk(v, n.X)
|
||
|
v.ignoreName()
|
||
|
default:
|
||
|
return v
|
||
|
}
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
func printDecl(decl ast.Node, fset *token.FileSet, buf []byte) (Code, []byte) {
|
||
|
v := &annotationVisitor{}
|
||
|
ast.Walk(v, decl)
|
||
|
|
||
|
buf = buf[:0]
|
||
|
err := (&printer.Config{Mode: printer.UseSpaces, Tabwidth: 4}).Fprint(sliceWriter{&buf}, fset, decl)
|
||
|
if err != nil {
|
||
|
return Code{Text: err.Error()}, buf
|
||
|
}
|
||
|
|
||
|
var annotations []Annotation
|
||
|
var s scanner.Scanner
|
||
|
fset = token.NewFileSet()
|
||
|
file := fset.AddFile("", fset.Base(), len(buf))
|
||
|
s.Init(file, buf, nil, scanner.ScanComments)
|
||
|
loop:
|
||
|
for {
|
||
|
pos, tok, lit := s.Scan()
|
||
|
switch tok {
|
||
|
case token.EOF:
|
||
|
break loop
|
||
|
case token.COMMENT:
|
||
|
p := file.Offset(pos)
|
||
|
e := p + len(lit)
|
||
|
if p > math.MaxInt16 || e > math.MaxInt16 {
|
||
|
break loop
|
||
|
}
|
||
|
annotations = append(annotations, Annotation{Kind: CommentAnnotation, Pos: int16(p), End: int16(e)})
|
||
|
case token.IDENT:
|
||
|
if len(v.annotations) == 0 {
|
||
|
// Oops!
|
||
|
break loop
|
||
|
}
|
||
|
annotation := v.annotations[0]
|
||
|
v.annotations = v.annotations[1:]
|
||
|
if annotation.Kind == -1 {
|
||
|
continue
|
||
|
}
|
||
|
p := file.Offset(pos)
|
||
|
e := p + len(lit)
|
||
|
if p > math.MaxInt16 || e > math.MaxInt16 {
|
||
|
break loop
|
||
|
}
|
||
|
annotation.Pos = int16(p)
|
||
|
annotation.End = int16(e)
|
||
|
if len(annotations) > 0 && annotation.Kind == ExportLinkAnnotation {
|
||
|
prev := annotations[len(annotations)-1]
|
||
|
if prev.Kind == PackageLinkAnnotation &&
|
||
|
prev.ImportPath == annotation.ImportPath &&
|
||
|
prev.End+1 == annotation.Pos {
|
||
|
// merge with previous
|
||
|
annotation.Pos = prev.Pos
|
||
|
annotations[len(annotations)-1] = annotation
|
||
|
continue loop
|
||
|
}
|
||
|
}
|
||
|
annotations = append(annotations, annotation)
|
||
|
}
|
||
|
}
|
||
|
return Code{Text: string(buf), Annotations: annotations}, buf
|
||
|
}
|
||
|
|
||
|
type AnnotationKind int16
|
||
|
|
||
|
type Annotation struct {
|
||
|
Pos, End int16
|
||
|
Kind AnnotationKind
|
||
|
ImportPath string
|
||
|
}
|
||
|
|
||
|
type Code struct {
|
||
|
Text string
|
||
|
Annotations []Annotation
|
||
|
}
|
||
|
|
||
|
func commentAnnotations(src string) []Annotation {
|
||
|
var annotations []Annotation
|
||
|
var s scanner.Scanner
|
||
|
fset := token.NewFileSet()
|
||
|
file := fset.AddFile("", fset.Base(), len(src))
|
||
|
s.Init(file, []byte(src), nil, scanner.ScanComments)
|
||
|
for {
|
||
|
pos, tok, lit := s.Scan()
|
||
|
switch tok {
|
||
|
case token.EOF:
|
||
|
return annotations
|
||
|
case token.COMMENT:
|
||
|
p := file.Offset(pos)
|
||
|
e := p + len(lit)
|
||
|
if p > math.MaxInt16 || e > math.MaxInt16 {
|
||
|
return annotations
|
||
|
}
|
||
|
annotations = append(annotations, Annotation{Kind: CommentAnnotation, Pos: int16(p), End: int16(e)})
|
||
|
}
|
||
|
}
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
type sliceWriter struct{ p *[]byte }
|
||
|
|
||
|
func (w sliceWriter) Write(p []byte) (int, error) {
|
||
|
*w.p = append(*w.p, p...)
|
||
|
return len(p), nil
|
||
|
}
|