This repository has been archived by the owner on Dec 22, 2018. It is now read-only.
/
parser.go
174 lines (145 loc) · 3.52 KB
/
parser.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
// The rst-extract utility extracts reStructured Text (RST) from Go
// source comments tagged with the special token "+rst" in the first
// line.
//
// Usage: rst-extract <source dir> <output dir>
// where "source dir" is a directory containing Go source files ending
// in .go, and "output dir" is a directory to output .rst files.
//
// rst-extract will create one .rst file per package. The source
// files are processed in a predictable order: (1) a file name
// matching the package name (for instance, "main.go" in a "main"
// package); (2) "doc.go"; and (3) lexicographic order. Comments
// within a file are processed in the order they appear. This
// predictable ordering allows you to add, for instance, a header to
// the output RST file by adding it to one of the special cases that
// are processed first.
package main
import (
"fmt"
"go/ast"
"go/parser"
"go/token"
"os"
"path"
"sort"
"strings"
)
func stripExt(filename string) string {
for i := len(filename) - 1; i >= 0; i-- {
if filename[i] == '.' {
return filename[:i]
}
}
return filename
}
// fileSorter sorts files passed in according to this heuristic:
// 1. file with name == package name
// 2. file with name "doc.go"
// 3. lexicographic order
type fileSorter struct {
pkg *ast.Package
files []*ast.File
names []string
}
func newFileSorter(pkg *ast.Package) *fileSorter {
fs := &fileSorter{}
fs.pkg = pkg
for name, f := range pkg.Files {
fs.files = append(fs.files, f)
fs.names = append(fs.names, path.Base(stripExt(name)))
}
sort.Sort(fs)
return fs
}
func (fs fileSorter) Len() int {
return len(fs.files)
}
func (fs fileSorter) Swap(i, j int) {
fs.files[i], fs.files[j] = fs.files[j], fs.files[i]
fs.names[i], fs.names[j] = fs.names[j], fs.names[i]
}
func (fs fileSorter) Less(i, j int) bool {
ni, nj := fs.names[i], fs.names[j]
switch ni {
case fs.pkg.Name:
switch nj {
case fs.pkg.Name:
return false
default:
return true
}
case "doc":
switch nj {
case fs.pkg.Name, "doc":
return false
default:
return true
}
default:
switch nj {
case fs.pkg.Name, "doc":
return false
default:
return ni < nj
}
}
}
var srcDir, outDir string
func rstComment(cgrp *ast.CommentGroup) (string, bool) {
s := cgrp.Text()
parts := strings.SplitN(s, "\n", 2)
if strings.TrimSpace(parts[0]) == "+rst" {
return parts[1], true
}
return "", false
}
func parsePackage(pkg *ast.Package) []string {
sorted := newFileSorter(pkg)
var comments []string
for _, f := range sorted.files {
for _, c := range f.Comments {
s, ok := rstComment(c)
if ok {
comments = append(comments, s)
}
}
}
return comments
}
func main() {
if len(os.Args) < 3 {
fmt.Printf("usage: %s <source dir> <output dir>\n", os.Args[0])
return
}
srcDir = os.Args[1]
outDir = os.Args[2]
fset := token.NewFileSet()
pkgs, err := parser.ParseDir(fset, srcDir, nil, parser.ParseComments)
if err != nil {
fmt.Printf("Error parsing files in %s: %s\n", srcDir, err)
os.Exit(1)
}
if err := os.MkdirAll(outDir, os.FileMode(0755)); err != nil {
fmt.Printf("Error creating %s: %s\n", outDir, err)
os.Exit(1)
}
for _, pkg := range pkgs {
var outf *os.File
filename := path.Join(outDir, pkg.Name+".rst")
comments := parsePackage(pkg)
if len(comments) > 0 {
outf, err = os.Create(filename)
if err != nil {
fmt.Printf("Error creating %s: %s\n", filename, err)
os.Exit(1)
}
for _, c := range comments {
outf.WriteString(c)
outf.WriteString("\n")
}
outf.Close()
fmt.Printf("Wrote %s\n", filename)
}
}
}