Browse Source

First commit: locate type spec declarations.

Frédéric G. MARAND 1 year ago
parent
commit
0b426adbd6
10 changed files with 284 additions and 0 deletions
  1. 8 0
      .idea/.gitignore
  2. 8 0
      .idea/modules.xml
  3. 11 0
      .idea/parsing_demo.iml
  4. 6 0
      .idea/vcs.xml
  5. 33 0
      decl.go
  6. 3 0
      go.mod
  7. 57 0
      main.go
  8. 24 0
      parse.go
  9. 35 0
      parse_just_imports_in_single_file/main.go
  10. 99 0
      stmt.go

+ 8 - 0
.idea/.gitignore

@@ -0,0 +1,8 @@
+# Default ignored files
+/shelf/
+/workspace.xml
+# Editor-based HTTP Client requests
+/httpRequests/
+# Datasource local storage ignored files
+/dataSources/
+/dataSources.local.xml

+ 8 - 0
.idea/modules.xml

@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project version="4">
+  <component name="ProjectModuleManager">
+    <modules>
+      <module fileurl="file://$PROJECT_DIR$/.idea/parsing_demo.iml" filepath="$PROJECT_DIR$/.idea/parsing_demo.iml" />
+    </modules>
+  </component>
+</project>

+ 11 - 0
.idea/parsing_demo.iml

@@ -0,0 +1,11 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<module type="WEB_MODULE" version="4">
+  <component name="Go" enabled="true" />
+  <component name="NewModuleRootManager">
+    <content url="file://$MODULE_DIR$">
+      <excludeFolder url="file://$MODULE_DIR$/testdata" />
+    </content>
+    <orderEntry type="inheritedJdk" />
+    <orderEntry type="sourceFolder" forTests="false" />
+  </component>
+</module>

+ 6 - 0
.idea/vcs.xml

@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project version="4">
+  <component name="VcsDirectoryMappings">
+    <mapping directory="$PROJECT_DIR$" vcs="Git" />
+  </component>
+</project>

+ 33 - 0
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:
+			// rien
+		default:
+			fmt.Printf("%s/(%T)\n", loc, s)
+		}
+
+	}
+}

+ 3 - 0
go.mod

@@ -0,0 +1,3 @@
+module code.osinet.fr/fgm/parsing_demo
+
+go 1.19

+ 57 - 0
main.go

@@ -0,0 +1,57 @@
+// Package main is a demo of go/parser and go/token.
+package main
+
+import (
+	_ "embed"
+	. "fmt"
+	"go/ast"
+	"go/parser"
+	"go/token"
+	"io/fs"
+	"log"
+)
+
+type packageScope 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)
+		}
+	}
+}

+ 24 - 0
parse.go

@@ -0,0 +1,24 @@
+package main
+
+import (
+	"fmt"
+	"go/ast"
+)
+
+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)
+		}
+	}
+}
+
+func parseTypeSpec(loc string, s *ast.TypeSpec) {
+	fmt.Printf("%s/typeSpec\n", loc)
+}

+ 35 - 0
parse_just_imports_in_single_file/main.go

@@ -0,0 +1,35 @@
+package main
+
+import (
+	_ "embed"
+	"fmt"
+	"go/parser"
+	"go/token"
+)
+
+func main() {
+	fset := token.NewFileSet() // positions are relative to fset
+
+	const src = `package foo
+
+import (
+	"fmt"
+	"time"
+)
+
+func bar() {
+	fmt.Println(time.Now())
+}`
+
+	// Parse src but stop after processing the imports.
+	f, err := parser.ParseFile(fset, "", src, parser.ImportsOnly)
+	if err != nil {
+		fmt.Println(err)
+		return
+	}
+
+	// Print the imports from the file's AST.
+	for _, s := range f.Imports {
+		fmt.Println(s.Path.Value)
+	}
+}

+ 99 - 0
stmt.go

@@ -0,0 +1,99 @@
+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:
+		// 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)
+		}
+	}
+}