Frédéric G. MARAND 1 year ago
parent
commit
3d784fdb6f

+ 0 - 0
parse_just_imports_in_single_file/main.go → 01-parse_just_imports_in_single_file/main.go


+ 0 - 0
decl.go → 02-basic_tree_walk/decl.go


+ 0 - 0
expr.go → 02-basic_tree_walk/expr.go


+ 3 - 0
main.go → 02-basic_tree_walk/main.go

@@ -11,6 +11,9 @@ import (
 	"log"
 )
 
+//go:embed main.go
+var mainFile string
+
 type (
 	psDefined bool
 	psAlias   = bool

+ 0 - 0
parse.go → 02-basic_tree_walk/parse.go


+ 1 - 1
stmt.go → 02-basic_tree_walk/stmt.go

@@ -90,7 +90,7 @@ func parseTypeSwitchStmt(loc string, s *ast.TypeSwitchStmt) {
 	for _, clause := range s.Body.List {
 		body := clause.(*ast.CaseClause).Body
 		if body == nil {
-			//fmt.Printf("%s/empty clause\n", loc)
+			// fmt.Printf("%s/empty clause\n", loc)
 			continue
 		}
 		for _, rs := range body {

+ 33 - 0
03-tree_walk_with_packages/decl.go

@@ -0,0 +1,33 @@
+package main
+
+import (
+	"fmt"
+	"go/ast"
+)
+
+func parseFuncDecl(loc string, d *ast.FuncDecl) {
+	loc += "." + d.Name.String()
+	// fmt.Printf("%s()\n", loc)
+	if d.Body == nil {
+		fmt.Println("nil func body")
+		return
+	}
+	for _, s := range d.Body.List {
+		parseStmt(loc, s)
+	}
+}
+
+func parseGenDecl(loc string, d *ast.GenDecl) {
+	loc += "/gendecl"
+	for _, s := range d.Specs {
+		switch s := s.(type) {
+		case *ast.TypeSpec:
+			parseTypeSpec(loc, s)
+		case *ast.ImportSpec, *ast.ValueSpec:
+			// rien
+		default:
+			fmt.Printf("%s/(%T)\n", loc, s)
+		}
+
+	}
+}

+ 33 - 0
03-tree_walk_with_packages/expr.go

@@ -0,0 +1,33 @@
+package main
+
+import (
+	"fmt"
+	"go/ast"
+)
+
+func parseTypeExpr(loc string, s ast.Expr, ln bool) {
+	switch s := s.(type) {
+	case *ast.ArrayType:
+		if s.Len == nil {
+			fmt.Print("slice of ")
+		} else {
+			fmt.Print("array of ")
+		}
+		parseTypeExpr(loc, s.Elt, false)
+	case *ast.ChanType:
+		fmt.Print("chan of ")
+		parseTypeExpr(loc, s.Value, false)
+	case *ast.Ident:
+		fmt.Print(s)
+	case *ast.MapType:
+		fmt.Print("map of ")
+		parseTypeExpr(loc, s.Value, false)
+		fmt.Print(" by ")
+		parseTypeExpr(loc, s.Key, false)
+	default:
+		fmt.Printf("type: %#v\n", s)
+	}
+	if ln {
+		fmt.Println()
+	}
+}

+ 6 - 0
03-tree_walk_with_packages/helpers/helpers.go

@@ -0,0 +1,6 @@
+package helpers
+
+type (
+	Exported bool
+	private  int
+)

+ 3 - 0
03-tree_walk_with_packages/helpers/other.go

@@ -0,0 +1,3 @@
+package helpers
+
+var Other string = ""

+ 71 - 0
03-tree_walk_with_packages/main.go

@@ -0,0 +1,71 @@
+// Package main is a demo of go/parser and go/token.
+package main
+
+import (
+	"embed"
+	_ "embed"
+	. "fmt"
+	"go/ast"
+	"go/parser"
+	"go/token"
+	"io/fs"
+	"log"
+)
+
+//go:embed helpers
+var hs embed.FS
+
+//go:embed main.go
+var ma string
+
+type (
+	psDefined bool
+	psAlias   = bool
+	psSlice   []bool
+	psArray   [4]bool
+	psChan    chan bool
+	psMap     map[bool]bool
+)
+
+// showPackageImports will usually show nothing
+func showPackageImports(pkg *ast.Package) {
+	type funcScope bool
+	Printf("Package imports (?):\n")
+	for impName, imp := range pkg.Imports {
+		type forScope bool
+		Printf("- %s %#v\n", impName, imp)
+	}
+}
+
+func showFileImports(imps []*ast.ImportSpec) {
+	if len(imps) == 0 {
+		return
+	}
+	Printf("      - Imports:\n")
+	for _, imp := range imps {
+		Printf("        - %s", imp.Path.Value)
+		if imp.Name != nil {
+			Printf(" as %s", imp.Name)
+		}
+		Println()
+	}
+}
+
+func main() {
+	const dir = "."
+	fset := token.NewFileSet() // positions are relative to fset
+	pkgs, err := parser.ParseDir(fset, dir, func(fi fs.FileInfo) bool { return true },
+		parser.ParseComments|parser.AllErrors)
+	if err != nil {
+		log.Fatalf("parsing %s: %v", dir, err)
+	}
+
+	for pkgName, pkg := range pkgs {
+		Printf("- Package %s:\n", pkgName)
+		for fileName, file := range pkg.Files {
+			Printf("    - %s = %s\n", fileName, file.Name)
+			// showFileImports(file.Imports)
+			parseFile(file)
+		}
+	}
+}

+ 34 - 0
03-tree_walk_with_packages/parse.go

@@ -0,0 +1,34 @@
+package main
+
+import (
+	"fmt"
+	"go/ast"
+	"go/token"
+)
+
+func parseFile(f *ast.File) {
+	loc := f.Name.String()
+	for _, d := range f.Decls {
+		switch d := d.(type) {
+		case *ast.GenDecl:
+			parseGenDecl(loc, d)
+		case *ast.FuncDecl:
+			parseFuncDecl(loc, d)
+		default:
+			fmt.Printf("Unknown decl type in %s: %T\n", loc, d)
+		}
+	}
+}
+
+// type specification: "type [=] <typ>"
+func parseTypeSpec(loc string, s *ast.TypeSpec) {
+	// type foo bool: .Name + .Type / .Assign = NoPos (== 0)
+	// type foo = bool: .Name + .Type / .Assign = <pos>
+	name := s.Name.String()
+	if s.Assign == token.NoPos {
+		fmt.Printf("%s/Defined %s: ", loc, name)
+	} else {
+		fmt.Printf("%s/Aliased %s: ", loc, name)
+	}
+	parseTypeExpr(loc, s.Type, true)
+}

+ 100 - 0
03-tree_walk_with_packages/stmt.go

@@ -0,0 +1,100 @@
+package main
+
+import (
+	"fmt"
+	"go/ast"
+)
+
+func parseStmt(loc string, s ast.Stmt) {
+	switch s := s.(type) {
+	case *ast.CaseClause:
+		parseCaseClause(loc, s)
+	case *ast.DeclStmt:
+		parseDeclStmt(loc, s)
+	case *ast.ExprStmt, *ast.AssignStmt, *ast.ReturnStmt,
+		*ast.BranchStmt: // break, continue, fallthrough, goto,
+		// fmt.Printf("%s/(%T)\n", loc, s)
+	case *ast.IfStmt:
+		parseIfStmt(loc, s)
+	case *ast.RangeStmt:
+		parseRangeStmt(loc, s)
+	case *ast.TypeSwitchStmt:
+		parseTypeSwitchStmt(loc, s)
+	default:
+		fmt.Printf("Unhandled type %T\n", s)
+	}
+}
+
+func parseCaseClause(loc string, s *ast.CaseClause) {
+	loc += "/case"
+	if s.Body == nil {
+		// fmt.Printf("%s/(nil case)\n", loc)
+		return
+	}
+	for _, rs := range s.Body {
+		parseStmt(loc, rs)
+	}
+}
+
+func parseDeclStmt(loc string, s *ast.DeclStmt) {
+	loc += "/decl"
+	switch d := s.Decl.(type) {
+	case *ast.GenDecl:
+		parseGenDecl(loc, d)
+	default:
+		fmt.Printf("DeclStmt: %T\n", d)
+	}
+}
+
+func parseIfStmt(loc string, s *ast.IfStmt) {
+	loc += "/if"
+	// fmt.Println(loc)
+	if s.Body == nil {
+		fmt.Printf("%s/(nil if)\n", loc)
+	} else {
+		thenLoc := loc + "/then"
+		for _, rs := range s.Body.List {
+			parseStmt(thenLoc, rs)
+		}
+	}
+	if s.Else == nil {
+		// fmt.Printf("%s/(nil else)\n", loc)
+		return
+	} else {
+		elseLoc := loc + "/else"
+		for _, rs := range s.Else.(*ast.BlockStmt).List {
+			parseStmt(elseLoc, rs)
+		}
+	}
+}
+
+func parseRangeStmt(loc string, s *ast.RangeStmt) {
+	loc += "/range"
+	// fmt.Println(loc)
+	if s.Body == nil {
+		fmt.Printf("%s/(nil range)\n", loc)
+		return
+	}
+	for _, rs := range s.Body.List {
+		parseStmt(loc, rs)
+	}
+}
+
+func parseTypeSwitchStmt(loc string, s *ast.TypeSwitchStmt) {
+	loc += "/(type)"
+	// fmt.Println(loc)
+	if s.Body == nil {
+		fmt.Printf("%s/(nil body)\n", loc)
+		return
+	}
+	for _, clause := range s.Body.List {
+		body := clause.(*ast.CaseClause).Body
+		if body == nil {
+			// fmt.Printf("%s/empty clause\n", loc)
+			continue
+		}
+		for _, rs := range body {
+			parseStmt(loc, rs)
+		}
+	}
+}

+ 51 - 0
04-walk_packages/walk_packages.go

@@ -0,0 +1,51 @@
+package main
+
+import (
+	"flag"
+	"fmt"
+	"os"
+
+	"golang.org/x/tools/go/packages"
+	"gopkg.in/yaml.v3"
+)
+
+func main() {
+	flag.Parse()
+
+	// Many tools pass their command-line arguments (after any flags)
+	// uninterpreted to packages.Load so that it can interpret them
+	// according to the conventions of the underlying build system.
+	cfg := &packages.Config{
+		Mode: packages.NeedCompiledGoFiles | // CompiledGoFiles in the package, as absolute source paths
+				// packages.NeedDeps | // Make TypesInfo include imported types and resolve Imports beyond just names
+				packages.NeedEmbedFiles | // EmbedFiles as absolute paths. For embed.FS, all resolved files
+				packages.NeedEmbedPatterns | // EmbedPatterns as absolute paths. For embed.FS, the embed path
+				packages.NeedExportFile | // ExportFile: the compiled package, for use with ar or go tool nm
+				packages.NeedFiles | // GoFiles and OtherFiles as absolute paths
+				packages.NeedImports | // Imports, as a map[string]packages.Package. Use
+				packages.NeedModule | // Modules, as a packages.Module
+				packages.NeedName | // e.g. main
+				packages.NeedSyntax | // Syntax, a []*ast.File containing the parsed files in the package
+				packages.NeedTypes | // Types, Fset (the set of files in the compile result) and IllTyped (bool)
+				packages.NeedTypesInfo | // TypesInfo for all types in the compile result
+				packages.NeedTypesSizes, // TypesSizes: WordSize (8) and MaxAlign (8)
+	}
+	pkgs, err := packages.Load(cfg, flag.Args()...)
+	if err != nil {
+		fmt.Fprintf(os.Stderr, "load: %v\n", err)
+		os.Exit(1)
+	}
+	if packages.PrintErrors(pkgs) > 0 {
+		os.Exit(1)
+	}
+
+	info := make(map[string][]string)
+	// Print the names of the source files
+	// for each package listed on the command line.
+	for _, pkg := range pkgs {
+		info[pkg.ID] = pkg.GoFiles
+	}
+	enc := yaml.NewEncoder(os.Stdout)
+	enc.SetIndent(2)
+	enc.Encode(info)
+}

+ 10 - 0
go.mod

@@ -1,3 +1,13 @@
 module code.osinet.fr/fgm/parsing_demo
 
 go 1.19
+
+require (
+	golang.org/x/tools v0.3.0
+	gopkg.in/yaml.v3 v3.0.1
+)
+
+require (
+	golang.org/x/mod v0.7.0 // indirect
+	golang.org/x/sys v0.2.0 // indirect
+)

+ 11 - 0
go.sum

@@ -0,0 +1,11 @@
+golang.org/x/mod v0.7.0 h1:LapD9S96VoQRhi/GrNTqeBJFrUjs5UHCAtTlgwA5oZA=
+golang.org/x/mod v0.7.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
+golang.org/x/sync v0.1.0 h1:wsuoTGHzEhffawBOhz5CYhcrV4IdKZbEyZjBMuTp12o=
+golang.org/x/sys v0.2.0 h1:ljd4t30dBnAvMZaQCevtY0xLLD0A+bRZXbgLMLU1F/A=
+golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/tools v0.3.0 h1:SrNbZl6ECOS1qFzgTdQfWXZM9XBkiA6tkFrH9YSTPHM=
+golang.org/x/tools v0.3.0/go.mod h1:/rWhSS2+zyEVwoJf8YAX6L2f0ntZ7Kn/mGgAWcipA5k=
+gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
+gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
+gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
+gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=