12
12
// See the License for the specific language governing permissions and
13
13
// limitations under the License.
14
14
15
- package imports
15
+ package fastscan
16
16
17
17
import (
18
18
"io"
@@ -26,23 +26,38 @@ var closeSymbol = map[tokenType]tokenType{
26
26
openAngleToken : closeAngleToken ,
27
27
}
28
28
29
- // ScanForImports scans the given reader, which should contain Protobuf source, and
30
- // returns the set of imports declared in the file. It returns an error if there is
29
+ // Result is the result of scanning a Protobuf source file. It contains the
30
+ // information extracted from the file.
31
+ type Result struct {
32
+ PackageName string
33
+ Imports []string
34
+ }
35
+
36
+ // Scan scans the given reader, which should contain Protobuf source, and
37
+ // returns the set of imports declared in the file. The result also contains the
38
+ // value of any package declaration in the file. It returns an error if there is
31
39
// an I/O error reading from r. In the event of such an error, it will still return
32
- // a slice of imports that contains as many imports as were found before the I/O
33
- // error occurred.
34
- func ScanForImports (r io.Reader ) ([]string , error ) {
35
- var imports []string
40
+ // a result that contains as much information as was found before the I/O error
41
+ // occurred.
42
+ func Scan (r io.Reader ) (Result , error ) {
43
+ var res Result
44
+
45
+ var currentImport []string // if non-nil, parsing an import statement
46
+ var packageComponents []string // if non-nil, parsing a package statement
47
+
48
+ // current stack of open blocks -- those starting with {, [, (, or < for
49
+ // which we haven't yet encountered the closing }, ], ), or >
36
50
var contextStack []tokenType
37
- var currentImport []string
51
+ declarationStart := true
52
+
38
53
lexer := newLexer (r )
39
54
for {
40
55
token , text , err := lexer .Lex ()
41
56
if err != nil {
42
- return imports , err
57
+ return res , err
43
58
}
44
59
if token == eofToken {
45
- return imports , nil
60
+ return res , nil
46
61
}
47
62
48
63
if currentImport != nil {
@@ -51,12 +66,26 @@ func ScanForImports(r io.Reader) ([]string, error) {
51
66
currentImport = append (currentImport , text .(string ))
52
67
default :
53
68
if len (currentImport ) > 0 {
54
- imports = append (imports , strings .Join (currentImport , "" ))
69
+ res . Imports = append (res . Imports , strings .Join (currentImport , "" ))
55
70
}
56
71
currentImport = nil
57
72
}
58
73
}
59
74
75
+ if packageComponents != nil {
76
+ switch token {
77
+ case identifierToken :
78
+ packageComponents = append (packageComponents , text .(string ))
79
+ case periodToken :
80
+ packageComponents = append (packageComponents , "." )
81
+ default :
82
+ if len (packageComponents ) > 0 {
83
+ res .PackageName = strings .Join (packageComponents , "" )
84
+ }
85
+ packageComponents = nil
86
+ }
87
+ }
88
+
60
89
switch token {
61
90
case openParenToken , openBraceToken , openBracketToken , openAngleToken :
62
91
contextStack = append (contextStack , closeSymbol [token ])
@@ -65,9 +94,15 @@ func ScanForImports(r io.Reader) ([]string, error) {
65
94
contextStack = contextStack [:len (contextStack )- 1 ]
66
95
}
67
96
case identifierToken :
68
- if text == "import" && len (contextStack ) == 0 {
69
- currentImport = []string {}
97
+ if declarationStart && len (contextStack ) == 0 {
98
+ if text == "import" {
99
+ currentImport = []string {}
100
+ } else if text == "package" {
101
+ packageComponents = []string {}
102
+ }
70
103
}
71
104
}
105
+
106
+ declarationStart = token == closeBraceToken || token == semicolonToken
72
107
}
73
108
}
0 commit comments