Repository: goplus/gop
Pull request #2244 (2025-04-01 17:14:28)

gopfullspec


Patch:
diff --git a/cmd/chore/gopfullspec/fullspec.gop b/cmd/chore/gopfullspec/fullspec.gop
new file mode 100644
index 00000000..b05300ab
--- /dev/null
+++ b/cmd/chore/gopfullspec/fullspec.gop
@@ -0,0 +1,124 @@
+/*
+ * Copyright (c) 2025 The GoPlus Authors (goplus.org). All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import (
+	"flag"
+	"gop/parser"
+	"gop/parser/parsertest"
+	"gop/token"
+	"gop/x/gopprojs"
+	"os"
+	"path/filepath"
+)
+
+func dump(f any) {
+	parsertest.FprintNode os.Stdout, "", f, "", "  "
+}
+
+func isGopFile(file string) bool {
+	if len(file) > 4 {
+		switch file[len(file)-4:] {
+		case ".gop", ".gox", ".gsh", ".spx", ".yap":
+			return true
+		}
+	}
+	return false
+}
+
+func parseFiles(files []string, dumpAST bool) (nErr int) {
+	fset := token.newFileSet
+	for file in files {
+		fprintln os.Stderr, "\n==> Parsing ${file}"
+		f, err := parser.parseFile(fset, file, nil, parser.ParseComments)
+		if err != nil {
+			fprintln os.Stderr, err
+			nErr++
+		} else if dumpAST {
+			dump f
+		}
+	}
+	return
+}
+
+func parseDir(dir string, recursively, dumpAST bool) (nErr int) {
+	fset := token.newFileSet
+	filepath.walkDir dir, (file, d, err) => {
+		if err != nil {
+			return err
+		} else if fname := d.name; d.isDir {
+			if recursively {
+				if fname.hasPrefix("_") || fname == "fullspec" {
+					return filepath.SkipDir
+				}
+			} else if file != dir {
+				return filepath.SkipDir
+			}
+		} else if !fname.hasPrefix("_") && isGopFile(fname) {
+			fprintln os.Stderr, "\n==> Parsing ${file}"
+			f, err := parser.parseFile(fset, file, nil, parser.ParseComments)
+			if err != nil {
+				fprintln os.Stderr, err
+				nErr++
+			} else if dumpAST {
+				dump f
+			}
+		}
+		return nil
+	}
+	return
+}
+
+var (
+	flagDumpAST = flag.Bool("ast", false, "dump AST")
+)
+
+flag.parse
+
+pattern := flag.args
+if pattern.len == 0 {
+	echo "Usage: gopfullspec [-ast] packages"
+	return
+}
+
+dumpAST := *flagDumpAST
+nErr := 0
+
+projs, err := gopprojs.parseAll(pattern...)
+if err != nil {
+	fprintln os.Stderr, err
+	os.exit 1
+}
+
+for proj in projs {
+	switch v := proj.(type) {
+	case *gopprojs.DirProj:
+		dir := v.Dir
+		recursively := dir.hasSuffix("/...")
+		if recursively {
+			dir = dir[:len(dir)-4]
+		}
+		nErr += parseDir(dir, recursively, dumpAST)
+	case *gopprojs.FilesProj:
+		nErr += parseFiles(v.Files, dumpAST)
+	default:
+		panic "only support local files and directories"
+	}
+}
+fprintln os.Stderr
+
+if nErr > 0 {
+	os.exit 1
+}
diff --git a/cmd/chore/gopminispec/minispec.gop b/cmd/chore/gopminispec/minispec.gop
index a674a437..71209ccf 100644
--- a/cmd/chore/gopminispec/minispec.gop
+++ b/cmd/chore/gopminispec/minispec.gop
@@ -84,7 +84,7 @@ flag.parse
 
 pattern := flag.args
 if pattern.len == 0 {
-	echo "Usage: gopminispec [-v] packages"
+	echo "Usage: gopminispec [-v -ast] packages"
 	return
 }
 


Test patch:
diff --git a/parser/parsertest/parsertest.go b/parser/parsertest/parsertest.go
index f0de995a..d3bc6d1d 100644
--- a/parser/parsertest/parsertest.go
+++ b/parser/parsertest/parsertest.go
@@ -46,8 +46,10 @@ func sortedKeys(m any) []string {
 var (
 	tyNode      = reflect.TypeOf((*ast.Node)(nil)).Elem()
 	tyString    = reflect.TypeOf("")
+	tyBytes     = reflect.TypeOf([]byte(nil))
 	tyToken     = reflect.TypeOf(token.Token(0))
 	tyObjectPtr = reflect.TypeOf((*ast.Object)(nil))
+	tyScopePtr  = reflect.TypeOf((*ast.Scope)(nil))
 	tplToken    = reflect.TypeOf(tpltoken.Token(0))
 )
 
@@ -65,7 +67,7 @@ func FprintNode(w io.Writer, lead string, v any, prefix, indent string) {
 		}
 	case reflect.Ptr:
 		t := val.Type()
-		if val.IsNil() || t == tyObjectPtr {
+		if val.IsNil() || t == tyObjectPtr || t == tyScopePtr {
 			return
 		}
 		if t.Implements(tyNode) {
@@ -82,6 +84,7 @@ func FprintNode(w io.Writer, lead string, v any, prefix, indent string) {
 				switch sf.Type {
 				case tyString, tyToken, tplToken:
 					fmt.Fprintf(w, "%s%v: %v\n", prefix, sf.Name, sfv)
+				case tyBytes: // skip
 				default:
 					FprintNode(w, fmt.Sprintf("%s%v:\n", prefix, sf.Name), sfv, prefix+indent, indent)
 				}