Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
124 changes: 124 additions & 0 deletions cmd/chore/gopfullspec/fullspec.gop
Original file line number Diff line number Diff line change
@@ -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
}
2 changes: 1 addition & 1 deletion cmd/chore/gopminispec/minispec.gop
Original file line number Diff line number Diff line change
Expand Up @@ -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
}

Expand Down
5 changes: 4 additions & 1 deletion parser/parsertest/parsertest.go
Original file line number Diff line number Diff line change
Expand Up @@ -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))
)

Expand All @@ -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) {
Expand All @@ -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)
}
Expand Down