func Parse(fset *token.FileSet, filename string, content []byte) ([]*Note, error) {
	var src interface{}
	if content != nil {
		src = content
	}
	switch filepath.Ext(filename) {
	case ".go":
		file, err := parser.ParseFile(fset, filename, src, parser.ParseComments)
		if file == nil {
			return nil, err
		}
		return ExtractGo(fset, file)
	case ".mod":
		file, err := modfile.Parse(filename, content, nil)
		if err != nil {
			return nil, err
		}
		f := fset.AddFile(filename, -1, len(content))
		f.SetLinesForContent(content)
		notes, err := extractMod(fset, file)
		if err != nil {
			return nil, err
		}
		for _, note := range notes {
			note.Pos += token.Pos(f.Base())
		}
		return notes, nil
	}
	return nil, nil
}
Parse collects all the notes present in a file.
If content is nil, the filename specified is read and parsed, otherwise the
content is used and the filename is used for positions and error messages.
Each comment whose text starts with @ is parsed as a comma-separated
sequence of notes.
See the package documentation for details about the syntax of those
notes.
package expect
import (
	"fmt"
	"go/ast"
	"go/parser"
	"go/token"
	"path/filepath"
	"regexp"
	"strconv"
	"strings"
	"text/scanner"
	"golang.org/x/mod/modfile"
)
const commentStart = "@"
const commentStartLen = len(commentStart)
type Identifier string