Skip to content

Commit 4ab6976

Browse files
authored
Merge pull request #20 from mariotoffia/refactor/goparser
Refactor Parser
2 parents 5e93af4 + 8591be1 commit 4ab6976

16 files changed

+3066
-1009
lines changed

goparser/api.go

Lines changed: 302 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,302 @@
1+
package goparser
2+
3+
// This file implements the simplified, modern API for goparser using the
4+
// functional options pattern for better usability and discoverability.
5+
6+
// Option is a functional option for configuring a Parser.
7+
type Option func(*Parser)
8+
9+
// Parser holds parsing configuration and provides methods for parsing Go source code.
10+
// Create a new Parser using NewParser with optional configuration.
11+
//
12+
// Example:
13+
//
14+
// parser := goparser.NewParser(
15+
// goparser.WithModule(mod),
16+
// goparser.WithBuildTags("linux"))
17+
// file, err := parser.ParseFile("main.go")
18+
type Parser struct {
19+
config ParseConfig
20+
// virtualPath is used only for ParseCode to specify a virtual file path
21+
virtualPath string
22+
}
23+
24+
// NewParser creates a new Parser with the specified options.
25+
// If no options are provided, sensible defaults are used.
26+
//
27+
// Example:
28+
//
29+
// parser := goparser.NewParser(
30+
// goparser.WithModule(mod),
31+
// goparser.WithTests(true))
32+
func NewParser(opts ...Option) *Parser {
33+
p := &Parser{
34+
config: ParseConfig{
35+
// Sensible defaults
36+
Test: false,
37+
Internal: false,
38+
UnderScore: false,
39+
DocConcatenation: DocConcatenationNone,
40+
IgnoreMarkdownHeadings: false,
41+
},
42+
}
43+
for _, opt := range opts {
44+
opt(p)
45+
}
46+
return p
47+
}
48+
49+
// ParseFile parses a single Go source file.
50+
//
51+
// Example:
52+
//
53+
// file, err := parser.ParseFile("main.go")
54+
func (p *Parser) ParseFile(path string) (*GoFile, error) {
55+
return parseSingleFileWithConfig(p.config, path)
56+
}
57+
58+
// ParseFiles parses multiple Go source files.
59+
// All files are parsed with the same configuration.
60+
//
61+
// Example:
62+
//
63+
// files, err := parser.ParseFiles("file1.go", "file2.go", "file3.go")
64+
func (p *Parser) ParseFiles(paths ...string) ([]*GoFile, error) {
65+
return parseFiles(p.config, paths...)
66+
}
67+
68+
// ParseDir recursively parses one or more directories.
69+
// It also accepts individual file paths.
70+
//
71+
// Example:
72+
//
73+
// files, err := parser.ParseDir("./src", "./cmd")
74+
func (p *Parser) ParseDir(paths ...string) ([]*GoFile, error) {
75+
return ParseAny(p.config, paths...)
76+
}
77+
78+
// ParseCode parses inline Go source code.
79+
// The virtualPath parameter is optional and can be empty string.
80+
//
81+
// Example:
82+
//
83+
// code := `package main
84+
// func main() {}`
85+
// file, err := parser.ParseCode(code, "main.go")
86+
func (p *Parser) ParseCode(code string, virtualPath string) (*GoFile, error) {
87+
path := virtualPath
88+
if path == "" && p.virtualPath != "" {
89+
path = p.virtualPath
90+
}
91+
return ParseInlineFileWithConfig(p.config, path, code)
92+
}
93+
94+
// WalkFiles walks through the specified files one at a time, calling fn for each.
95+
// This is memory efficient for processing large numbers of files.
96+
//
97+
// Example:
98+
//
99+
// err := parser.WalkFiles(func(file *GoFile) error {
100+
// fmt.Println(file.Package)
101+
// return nil
102+
// }, "./src")
103+
func (p *Parser) WalkFiles(fn func(*GoFile) error, paths ...string) error {
104+
return ParseSingleFileWalker(p.config, fn, paths...)
105+
}
106+
107+
// WalkPackages walks through packages one at a time, calling fn for each.
108+
// Files in the same directory are grouped into a single GoPackage.
109+
// This is memory efficient for processing large codebases.
110+
//
111+
// Example:
112+
//
113+
// err := parser.WalkPackages(func(pkg *GoPackage) error {
114+
// fmt.Printf("Package %s has %d files\n", pkg.Package, len(pkg.Files))
115+
// return nil
116+
// }, "./src")
117+
func (p *Parser) WalkPackages(fn func(*GoPackage) error, paths ...string) error {
118+
return ParseSinglePackageWalker(p.config, fn, paths...)
119+
}
120+
121+
// Option Functions
122+
123+
// WithModule sets the Go module context for resolving package paths.
124+
//
125+
// Example:
126+
//
127+
// mod, _ := goparser.NewModule("go.mod")
128+
// parser := goparser.NewParser(goparser.WithModule(mod))
129+
func WithModule(mod *GoModule) Option {
130+
return func(p *Parser) {
131+
p.config.Module = mod
132+
}
133+
}
134+
135+
// WithBuildTags sets build tags for conditional compilation.
136+
// Each string represents a set of comma-separated tags (e.g., "linux,amd64").
137+
//
138+
// Example:
139+
//
140+
// parser := goparser.NewParser(
141+
// goparser.WithBuildTags("linux,amd64", "integration"))
142+
func WithBuildTags(tags ...string) Option {
143+
return func(p *Parser) {
144+
p.config.BuildTags = tags
145+
}
146+
}
147+
148+
// WithAllBuildTags attempts to discover and load all build tag variants.
149+
//
150+
// Example:
151+
//
152+
// parser := goparser.NewParser(goparser.WithAllBuildTags())
153+
func WithAllBuildTags() Option {
154+
return func(p *Parser) {
155+
p.config.AllBuildTags = true
156+
}
157+
}
158+
159+
// WithTests includes test files (*_test.go) in parsing.
160+
// By default, test files are excluded.
161+
//
162+
// Example:
163+
//
164+
// parser := goparser.NewParser(goparser.WithTests(true))
165+
func WithTests(include bool) Option {
166+
return func(p *Parser) {
167+
p.config.Test = include
168+
}
169+
}
170+
171+
// WithInternal includes internal packages in parsing.
172+
// By default, internal packages are excluded.
173+
//
174+
// Example:
175+
//
176+
// parser := goparser.NewParser(goparser.WithInternal(true))
177+
func WithInternal(include bool) Option {
178+
return func(p *Parser) {
179+
p.config.Internal = include
180+
}
181+
}
182+
183+
// WithUnderScore includes directories starting with underscore.
184+
// By default, underscore directories are excluded.
185+
//
186+
// Example:
187+
//
188+
// parser := goparser.NewParser(goparser.WithUnderScore(true))
189+
func WithUnderScore(include bool) Option {
190+
return func(p *Parser) {
191+
p.config.UnderScore = include
192+
}
193+
}
194+
195+
// WithDebug sets a debug logging function.
196+
// The function is called with debug messages during parsing.
197+
//
198+
// Example:
199+
//
200+
// parser := goparser.NewParser(
201+
// goparser.WithDebug(func(format string, args ...interface{}) {
202+
// log.Printf(format, args...)
203+
// }))
204+
func WithDebug(fn DebugFunc) Option {
205+
return func(p *Parser) {
206+
p.config.Debug = fn
207+
}
208+
}
209+
210+
// WithDocConcatenation sets the documentation concatenation mode.
211+
// DocConcatenationFull concatenates doc comments separated by blank lines.
212+
//
213+
// Example:
214+
//
215+
// parser := goparser.NewParser(
216+
// goparser.WithDocConcatenation(goparser.DocConcatenationFull))
217+
func WithDocConcatenation(mode DocConcatenationMode) Option {
218+
return func(p *Parser) {
219+
p.config.DocConcatenation = mode
220+
}
221+
}
222+
223+
// WithIgnoreMarkdownHeadings removes markdown heading markers from documentation.
224+
// When enabled, "# Title" becomes "Title" in doc comments.
225+
//
226+
// Example:
227+
//
228+
// parser := goparser.NewParser(goparser.WithIgnoreMarkdownHeadings(true))
229+
func WithIgnoreMarkdownHeadings(ignore bool) Option {
230+
return func(p *Parser) {
231+
p.config.IgnoreMarkdownHeadings = ignore
232+
}
233+
}
234+
235+
// WithPath sets a virtual path for inline code parsing.
236+
// This is only used when calling Parser.ParseCode without an explicit path.
237+
//
238+
// Example:
239+
//
240+
// parser := goparser.NewParser(goparser.WithPath("virtual.go"))
241+
// file, err := parser.ParseCode(code, "") // Uses "virtual.go"
242+
func WithPath(path string) Option {
243+
return func(p *Parser) {
244+
p.virtualPath = path
245+
}
246+
}
247+
248+
// Convenience Functions (Package Level)
249+
250+
// ParseFile parses a single Go source file with optional configuration.
251+
//
252+
// Example:
253+
//
254+
// // Simple
255+
// file, err := goparser.ParseFile("main.go")
256+
//
257+
// // With module
258+
// mod, _ := goparser.NewModule("go.mod")
259+
// file, err := goparser.ParseFile("main.go", goparser.WithModule(mod))
260+
func ParseFile(path string, opts ...Option) (*GoFile, error) {
261+
p := NewParser(opts...)
262+
return p.ParseFile(path)
263+
}
264+
265+
// Note: Package-level ParseFiles convenience function is not provided to avoid
266+
// conflict with the legacy ParseFiles(mod *GoModule, paths ...string) function.
267+
// Use Parser.ParseFiles() instead for multiple files:
268+
//
269+
// parser := goparser.NewParser(goparser.WithModule(mod))
270+
// files, err := parser.ParseFiles("file1.go", "file2.go")
271+
272+
// ParseDir recursively parses a directory with optional configuration.
273+
//
274+
// Example:
275+
//
276+
// // Simple
277+
// files, err := goparser.ParseDir("./src")
278+
//
279+
// // With options
280+
// files, err := goparser.ParseDir("./src",
281+
// goparser.WithModule(mod),
282+
// goparser.WithTests(true),
283+
// goparser.WithBuildTags("linux"))
284+
func ParseDir(path string, opts ...Option) ([]*GoFile, error) {
285+
p := NewParser(opts...)
286+
return p.ParseDir(path)
287+
}
288+
289+
// ParseCode parses inline Go source code with optional configuration.
290+
//
291+
// Example:
292+
//
293+
// code := `package main
294+
// func main() {}`
295+
// file, err := goparser.ParseCode(code)
296+
//
297+
// // With virtual path
298+
// file, err := goparser.ParseCode(code, goparser.WithPath("main.go"))
299+
func ParseCode(code string, opts ...Option) (*GoFile, error) {
300+
p := NewParser(opts...)
301+
return p.ParseCode(code, "")
302+
}

0 commit comments

Comments
 (0)