/** * The out-of-process parser programs (Go's `go/ast`, Python's `ast`) and the * plumbing that runs them. * * The single-file parsers (`go-parser.ts`, `py-parser.ts`) and the batch * parsers (`parser-batch.ts`) used to embed two hand-maintained copies of each * program. The copies drifted: the Go batch reported 1-based columns while the * single-file script reported 0-based ones, so the same file indexed with * different positions depending on which path parsed it. Each language now has * ONE extraction core; the single-file and batch programs differ only in their * `main` (one source on stdin vs. a JSON array of sources). */ /** One Go source on stdin → `{"symbols": [...], "refs": [...]}`. */ export declare const GO_PARSE_SCRIPT = "\npackage main\n\nimport (\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"go/ast\"\n\t\"go/parser\"\n\t\"go/token\"\n\t\"io\"\n\t\"os\"\n\t\"strconv\"\n\t\"strings\"\n)\n\ntype Sym struct {\n\tName string `json:\"name\"`\n\tKind string `json:\"kind\"`\n\tLine int `json:\"line\"`\n\tCol int `json:\"col\"`\n\tSignature string `json:\"signature\"`\n\tScope string `json:\"scope\"`\n\tDoc string `json:\"doc\"`\n}\n\n// Ref is a cross-reference emitted alongside the symbols, so one run yields\n// both. Module is the import path for CallType \"import\", else empty.\ntype Ref struct {\n\tToName string `json:\"toName\"`\n\tCallType string `json:\"callType\"`\n\tLine int `json:\"line\"`\n\tModule string `json:\"module\"`\n}\n\nfunc extract(src []byte) ([]Sym, []Ref, error) {\n\tfset := token.NewFileSet()\n\tnode, err := parser.ParseFile(fset, \"src.go\", src, parser.ParseComments)\n\tif err != nil {\n\t\treturn nil, nil, err\n\t}\n\tpkg := node.Name.Name\n\t// token columns are 1-based; the index schema's are 0-based.\n\tat := func(p token.Pos) (int, int) {\n\t\tpos := fset.Position(p)\n\t\treturn pos.Line, pos.Column - 1\n\t}\n\n\tsyms := []Sym{}\n\tfor _, decl := range node.Decls {\n\t\tswitch d := decl.(type) {\n\t\tcase *ast.FuncDecl:\n\t\t\tline, col := at(d.Pos())\n\t\t\tkind := \"function\"\n\t\t\tscope := pkg + \".\" + d.Name.Name\n\t\t\tif d.Recv != nil && len(d.Recv.List) > 0 {\n\t\t\t\tkind = \"method\"\n\t\t\t\tscope = pkg + \".\" + recvTypeName(d.Recv.List[0].Type) + \".\" + d.Name.Name\n\t\t\t}\n\t\t\tsyms = append(syms, Sym{Name: d.Name.Name, Kind: kind, Line: line, Col: col, Signature: formatFuncSig(d), Scope: scope, Doc: firstDocLine(d.Doc)})\n\n\t\tcase *ast.GenDecl:\n\t\t\t// An ungrouped declaration carries its comment on the GenDecl; a\n\t\t\t// grouped one's doc describes the group, not each member.\n\t\t\tvar declDoc *ast.CommentGroup\n\t\t\tif !d.Lparen.IsValid() {\n\t\t\t\tdeclDoc = d.Doc\n\t\t\t}\n\t\t\tfor _, spec := range d.Specs {\n\t\t\t\tswitch s := spec.(type) {\n\t\t\t\tcase *ast.TypeSpec:\n\t\t\t\t\tline, col := at(s.Pos())\n\t\t\t\t\tsig := \"type \" + s.Name.Name + formatTypeParams(s.TypeParams)\n\t\t\t\t\tif s.Assign.IsValid() {\n\t\t\t\t\t\tsig += \" =\"\n\t\t\t\t\t}\n\t\t\t\t\tsig += \" \" + formatType(s.Type)\n\t\t\t\t\tsyms = append(syms, Sym{Name: s.Name.Name, Kind: \"type\", Line: line, Col: col, Signature: sig, Scope: pkg, Doc: firstDocLine(s.Doc, declDoc)})\n\t\t\t\tcase *ast.ValueSpec:\n\t\t\t\t\tkind := \"var\"\n\t\t\t\t\tif d.Tok == token.CONST {\n\t\t\t\t\t\tkind = \"const\"\n\t\t\t\t\t}\n\t\t\t\t\tfor _, n := range s.Names {\n\t\t\t\t\t\tline, col := at(n.Pos())\n\t\t\t\t\t\tsig := kind + \" \" + n.Name\n\t\t\t\t\t\tif s.Type != nil {\n\t\t\t\t\t\t\tsig += \" \" + formatType(s.Type)\n\t\t\t\t\t\t}\n\t\t\t\t\t\tsyms = append(syms, Sym{Name: n.Name, Kind: kind, Line: line, Col: col, Signature: sig, Scope: pkg, Doc: firstDocLine(s.Doc, declDoc)})\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\trefs := []Ref{}\n\tast.Inspect(node, func(n ast.Node) bool {\n\t\tswitch expr := n.(type) {\n\t\tcase *ast.CallExpr:\n\t\t\tline := fset.Position(expr.Pos()).Line\n\t\t\tswitch fun := expr.Fun.(type) {\n\t\t\tcase *ast.Ident:\n\t\t\t\trefs = append(refs, Ref{ToName: fun.Name, CallType: \"call\", Line: line})\n\t\t\tcase *ast.SelectorExpr:\n\t\t\t\t// The selected name (`Join` of `filepath.Join`) is the declared\n\t\t\t\t// symbol name, so it resolves like the TS and Python call refs.\n\t\t\t\trefs = append(refs, Ref{ToName: fun.Sel.Name, CallType: \"call\", Line: line})\n\t\t\t}\n\t\tcase *ast.ImportSpec:\n\t\t\tif expr.Path != nil {\n\t\t\t\tif importPath, uerr := strconv.Unquote(expr.Path.Value); uerr == nil {\n\t\t\t\t\tline := fset.Position(expr.Pos()).Line\n\t\t\t\t\t// A Go import names a package; its last path segment is the\n\t\t\t\t\t// name it is referenced by.\n\t\t\t\t\tname := importPath\n\t\t\t\t\tif idx := strings.LastIndex(importPath, \"/\"); idx >= 0 {\n\t\t\t\t\t\tname = importPath[idx+1:]\n\t\t\t\t\t}\n\t\t\t\t\trefs = append(refs, Ref{ToName: name, CallType: \"import\", Line: line, Module: importPath})\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn true\n\t})\n\treturn syms, refs, nil\n}\n\nfunc firstDocLine(groups ...*ast.CommentGroup) string {\n\tfor _, g := range groups {\n\t\tif g == nil {\n\t\t\tcontinue\n\t\t}\n\t\tfor _, line := range strings.Split(g.Text(), \"\\n\") {\n\t\t\tif t := strings.TrimSpace(line); t != \"\" {\n\t\t\t\tif r := []rune(t); len(r) > 200 {\n\t\t\t\t\tt = string(r[:200])\n\t\t\t\t}\n\t\t\t\treturn t\n\t\t\t}\n\t\t}\n\t}\n\treturn \"\"\n}\n\n// recvTypeName unwraps pointers, parens and generic instantiation:\n// `(l *List[T])` is a method of List, not of \"?\".\nfunc recvTypeName(t ast.Expr) string {\n\tswitch v := t.(type) {\n\tcase *ast.Ident:\n\t\treturn v.Name\n\tcase *ast.StarExpr:\n\t\treturn recvTypeName(v.X)\n\tcase *ast.ParenExpr:\n\t\treturn recvTypeName(v.X)\n\tcase *ast.IndexExpr:\n\t\treturn recvTypeName(v.X)\n\tcase *ast.IndexListExpr:\n\t\treturn recvTypeName(v.X)\n\tdefault:\n\t\treturn \"?\"\n\t}\n}\n\nfunc formatFuncSig(d *ast.FuncDecl) string {\n\tsig := \"func \"\n\tif d.Recv != nil && len(d.Recv.List) > 0 {\n\t\tsig += formatFieldList(d.Recv.List) + \" \"\n\t}\n\treturn sig + d.Name.Name + formatTypeParams(d.Type.TypeParams) + formatFuncType(d.Type)\n}\n\nfunc fieldsOf(fl *ast.FieldList) []*ast.Field {\n\tif fl == nil {\n\t\treturn nil\n\t}\n\treturn fl.List\n}\n\nfunc formatFuncType(f *ast.FuncType) string {\n\tout := formatFieldList(fieldsOf(f.Params))\n\tresults := fieldsOf(f.Results)\n\tif len(results) == 1 && len(results[0].Names) == 0 {\n\t\tout += \" \" + formatType(results[0].Type)\n\t} else if len(results) > 0 {\n\t\tout += \" \" + formatFieldList(results)\n\t}\n\treturn out\n}\n\n// formatField keeps every name of a shared-type field: `a, b int`.\nfunc formatField(f *ast.Field) string {\n\tt := formatType(f.Type)\n\tif len(f.Names) == 0 {\n\t\treturn t\n\t}\n\tnames := make([]string, len(f.Names))\n\tfor i, n := range f.Names {\n\t\tnames[i] = n.Name\n\t}\n\treturn strings.Join(names, \", \") + \" \" + t\n}\n\nfunc formatFieldList(fields []*ast.Field) string {\n\tparts := make([]string, len(fields))\n\tfor i, f := range fields {\n\t\tparts[i] = formatField(f)\n\t}\n\treturn \"(\" + strings.Join(parts, \", \") + \")\"\n}\n\n// formatFields renders struct fields and interface members.\nfunc formatFields(fields []*ast.Field) string {\n\tparts := make([]string, len(fields))\n\tfor i, f := range fields {\n\t\tif ft, ok := f.Type.(*ast.FuncType); ok && len(f.Names) == 1 {\n\t\t\tparts[i] = f.Names[0].Name + formatFuncType(ft)\n\t\t} else {\n\t\t\tparts[i] = formatField(f)\n\t\t}\n\t}\n\treturn strings.Join(parts, \"; \")\n}\n\nfunc formatTypeParams(tp *ast.FieldList) string {\n\tif tp == nil || len(tp.List) == 0 {\n\t\treturn \"\"\n\t}\n\tparts := make([]string, len(tp.List))\n\tfor i, p := range tp.List {\n\t\tparts[i] = formatField(p)\n\t}\n\treturn \"[\" + strings.Join(parts, \", \") + \"]\"\n}\n\nfunc formatType(t ast.Expr) string {\n\tif t == nil {\n\t\treturn \"?\"\n\t}\n\tswitch v := t.(type) {\n\tcase *ast.Ident:\n\t\treturn v.Name\n\tcase *ast.SelectorExpr:\n\t\treturn formatType(v.X) + \".\" + v.Sel.Name\n\tcase *ast.StarExpr:\n\t\treturn \"*\" + formatType(v.X)\n\tcase *ast.ParenExpr:\n\t\treturn \"(\" + formatType(v.X) + \")\"\n\tcase *ast.Ellipsis:\n\t\tif v.Elt == nil {\n\t\t\treturn \"...\"\n\t\t}\n\t\treturn \"...\" + formatType(v.Elt)\n\tcase *ast.ArrayType:\n\t\tif v.Len == nil {\n\t\t\treturn \"[]\" + formatType(v.Elt)\n\t\t}\n\t\treturn \"[\" + formatType(v.Len) + \"]\" + formatType(v.Elt)\n\tcase *ast.MapType:\n\t\treturn \"map[\" + formatType(v.Key) + \"]\" + formatType(v.Value)\n\tcase *ast.ChanType:\n\t\tswitch v.Dir {\n\t\tcase ast.SEND:\n\t\t\treturn \"chan<- \" + formatType(v.Value)\n\t\tcase ast.RECV:\n\t\t\treturn \"<-chan \" + formatType(v.Value)\n\t\tdefault:\n\t\t\treturn \"chan \" + formatType(v.Value)\n\t\t}\n\tcase *ast.InterfaceType:\n\t\tif len(fieldsOf(v.Methods)) == 0 {\n\t\t\treturn \"interface{}\"\n\t\t}\n\t\treturn \"interface{ \" + formatFields(v.Methods.List) + \" }\"\n\tcase *ast.StructType:\n\t\tif len(fieldsOf(v.Fields)) == 0 {\n\t\t\treturn \"struct{}\"\n\t\t}\n\t\treturn \"struct{ \" + formatFields(v.Fields.List) + \" }\"\n\tcase *ast.FuncType:\n\t\treturn \"func\" + formatFuncType(v)\n\tcase *ast.UnaryExpr:\n\t\treturn v.Op.String() + formatType(v.X)\n\tcase *ast.BinaryExpr:\n\t\treturn formatType(v.X) + \" \" + v.Op.String() + \" \" + formatType(v.Y)\n\tcase *ast.BasicLit:\n\t\treturn v.Value\n\tcase *ast.IndexExpr:\n\t\treturn formatType(v.X) + \"[\" + formatType(v.Index) + \"]\"\n\tcase *ast.IndexListExpr:\n\t\targs := make([]string, len(v.Indices))\n\t\tfor i, idx := range v.Indices {\n\t\t\targs[i] = formatType(idx)\n\t\t}\n\t\treturn formatType(v.X) + \"[\" + strings.Join(args, \", \") + \"]\"\n\tdefault:\n\t\treturn \"?\"\n\t}\n}\n\ntype Result struct {\n\tSymbols []Sym `json:\"symbols\"`\n\tRefs []Ref `json:\"refs\"`\n}\n\nfunc main() {\n\tempty := `{\"symbols\":[],\"refs\":[]}`\n\tsrc, err := io.ReadAll(os.Stdin)\n\tif err != nil {\n\t\tfmt.Print(empty)\n\t\treturn\n\t}\n\tsyms, refs, err := extract(src)\n\tif err != nil {\n\t\tfmt.Print(empty)\n\t\treturn\n\t}\n\tdata, err := json.Marshal(Result{Symbols: syms, Refs: refs})\n\tif err != nil {\n\t\tfmt.Print(empty)\n\t\treturn\n\t}\n\tfmt.Print(string(data))\n}\n"; /** A JSON array of `{file, content}` on stdin → one envelope with per-file results. */ export declare const GO_BATCH_SCRIPT = "\npackage main\n\nimport (\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"go/ast\"\n\t\"go/parser\"\n\t\"go/token\"\n\t\"io\"\n\t\"os\"\n\t\"strconv\"\n\t\"strings\"\n)\n\ntype Sym struct {\n\tName string `json:\"name\"`\n\tKind string `json:\"kind\"`\n\tLine int `json:\"line\"`\n\tCol int `json:\"col\"`\n\tSignature string `json:\"signature\"`\n\tScope string `json:\"scope\"`\n\tDoc string `json:\"doc\"`\n}\n\n// Ref is a cross-reference emitted alongside the symbols, so one run yields\n// both. Module is the import path for CallType \"import\", else empty.\ntype Ref struct {\n\tToName string `json:\"toName\"`\n\tCallType string `json:\"callType\"`\n\tLine int `json:\"line\"`\n\tModule string `json:\"module\"`\n}\n\nfunc extract(src []byte) ([]Sym, []Ref, error) {\n\tfset := token.NewFileSet()\n\tnode, err := parser.ParseFile(fset, \"src.go\", src, parser.ParseComments)\n\tif err != nil {\n\t\treturn nil, nil, err\n\t}\n\tpkg := node.Name.Name\n\t// token columns are 1-based; the index schema's are 0-based.\n\tat := func(p token.Pos) (int, int) {\n\t\tpos := fset.Position(p)\n\t\treturn pos.Line, pos.Column - 1\n\t}\n\n\tsyms := []Sym{}\n\tfor _, decl := range node.Decls {\n\t\tswitch d := decl.(type) {\n\t\tcase *ast.FuncDecl:\n\t\t\tline, col := at(d.Pos())\n\t\t\tkind := \"function\"\n\t\t\tscope := pkg + \".\" + d.Name.Name\n\t\t\tif d.Recv != nil && len(d.Recv.List) > 0 {\n\t\t\t\tkind = \"method\"\n\t\t\t\tscope = pkg + \".\" + recvTypeName(d.Recv.List[0].Type) + \".\" + d.Name.Name\n\t\t\t}\n\t\t\tsyms = append(syms, Sym{Name: d.Name.Name, Kind: kind, Line: line, Col: col, Signature: formatFuncSig(d), Scope: scope, Doc: firstDocLine(d.Doc)})\n\n\t\tcase *ast.GenDecl:\n\t\t\t// An ungrouped declaration carries its comment on the GenDecl; a\n\t\t\t// grouped one's doc describes the group, not each member.\n\t\t\tvar declDoc *ast.CommentGroup\n\t\t\tif !d.Lparen.IsValid() {\n\t\t\t\tdeclDoc = d.Doc\n\t\t\t}\n\t\t\tfor _, spec := range d.Specs {\n\t\t\t\tswitch s := spec.(type) {\n\t\t\t\tcase *ast.TypeSpec:\n\t\t\t\t\tline, col := at(s.Pos())\n\t\t\t\t\tsig := \"type \" + s.Name.Name + formatTypeParams(s.TypeParams)\n\t\t\t\t\tif s.Assign.IsValid() {\n\t\t\t\t\t\tsig += \" =\"\n\t\t\t\t\t}\n\t\t\t\t\tsig += \" \" + formatType(s.Type)\n\t\t\t\t\tsyms = append(syms, Sym{Name: s.Name.Name, Kind: \"type\", Line: line, Col: col, Signature: sig, Scope: pkg, Doc: firstDocLine(s.Doc, declDoc)})\n\t\t\t\tcase *ast.ValueSpec:\n\t\t\t\t\tkind := \"var\"\n\t\t\t\t\tif d.Tok == token.CONST {\n\t\t\t\t\t\tkind = \"const\"\n\t\t\t\t\t}\n\t\t\t\t\tfor _, n := range s.Names {\n\t\t\t\t\t\tline, col := at(n.Pos())\n\t\t\t\t\t\tsig := kind + \" \" + n.Name\n\t\t\t\t\t\tif s.Type != nil {\n\t\t\t\t\t\t\tsig += \" \" + formatType(s.Type)\n\t\t\t\t\t\t}\n\t\t\t\t\t\tsyms = append(syms, Sym{Name: n.Name, Kind: kind, Line: line, Col: col, Signature: sig, Scope: pkg, Doc: firstDocLine(s.Doc, declDoc)})\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\trefs := []Ref{}\n\tast.Inspect(node, func(n ast.Node) bool {\n\t\tswitch expr := n.(type) {\n\t\tcase *ast.CallExpr:\n\t\t\tline := fset.Position(expr.Pos()).Line\n\t\t\tswitch fun := expr.Fun.(type) {\n\t\t\tcase *ast.Ident:\n\t\t\t\trefs = append(refs, Ref{ToName: fun.Name, CallType: \"call\", Line: line})\n\t\t\tcase *ast.SelectorExpr:\n\t\t\t\t// The selected name (`Join` of `filepath.Join`) is the declared\n\t\t\t\t// symbol name, so it resolves like the TS and Python call refs.\n\t\t\t\trefs = append(refs, Ref{ToName: fun.Sel.Name, CallType: \"call\", Line: line})\n\t\t\t}\n\t\tcase *ast.ImportSpec:\n\t\t\tif expr.Path != nil {\n\t\t\t\tif importPath, uerr := strconv.Unquote(expr.Path.Value); uerr == nil {\n\t\t\t\t\tline := fset.Position(expr.Pos()).Line\n\t\t\t\t\t// A Go import names a package; its last path segment is the\n\t\t\t\t\t// name it is referenced by.\n\t\t\t\t\tname := importPath\n\t\t\t\t\tif idx := strings.LastIndex(importPath, \"/\"); idx >= 0 {\n\t\t\t\t\t\tname = importPath[idx+1:]\n\t\t\t\t\t}\n\t\t\t\t\trefs = append(refs, Ref{ToName: name, CallType: \"import\", Line: line, Module: importPath})\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn true\n\t})\n\treturn syms, refs, nil\n}\n\nfunc firstDocLine(groups ...*ast.CommentGroup) string {\n\tfor _, g := range groups {\n\t\tif g == nil {\n\t\t\tcontinue\n\t\t}\n\t\tfor _, line := range strings.Split(g.Text(), \"\\n\") {\n\t\t\tif t := strings.TrimSpace(line); t != \"\" {\n\t\t\t\tif r := []rune(t); len(r) > 200 {\n\t\t\t\t\tt = string(r[:200])\n\t\t\t\t}\n\t\t\t\treturn t\n\t\t\t}\n\t\t}\n\t}\n\treturn \"\"\n}\n\n// recvTypeName unwraps pointers, parens and generic instantiation:\n// `(l *List[T])` is a method of List, not of \"?\".\nfunc recvTypeName(t ast.Expr) string {\n\tswitch v := t.(type) {\n\tcase *ast.Ident:\n\t\treturn v.Name\n\tcase *ast.StarExpr:\n\t\treturn recvTypeName(v.X)\n\tcase *ast.ParenExpr:\n\t\treturn recvTypeName(v.X)\n\tcase *ast.IndexExpr:\n\t\treturn recvTypeName(v.X)\n\tcase *ast.IndexListExpr:\n\t\treturn recvTypeName(v.X)\n\tdefault:\n\t\treturn \"?\"\n\t}\n}\n\nfunc formatFuncSig(d *ast.FuncDecl) string {\n\tsig := \"func \"\n\tif d.Recv != nil && len(d.Recv.List) > 0 {\n\t\tsig += formatFieldList(d.Recv.List) + \" \"\n\t}\n\treturn sig + d.Name.Name + formatTypeParams(d.Type.TypeParams) + formatFuncType(d.Type)\n}\n\nfunc fieldsOf(fl *ast.FieldList) []*ast.Field {\n\tif fl == nil {\n\t\treturn nil\n\t}\n\treturn fl.List\n}\n\nfunc formatFuncType(f *ast.FuncType) string {\n\tout := formatFieldList(fieldsOf(f.Params))\n\tresults := fieldsOf(f.Results)\n\tif len(results) == 1 && len(results[0].Names) == 0 {\n\t\tout += \" \" + formatType(results[0].Type)\n\t} else if len(results) > 0 {\n\t\tout += \" \" + formatFieldList(results)\n\t}\n\treturn out\n}\n\n// formatField keeps every name of a shared-type field: `a, b int`.\nfunc formatField(f *ast.Field) string {\n\tt := formatType(f.Type)\n\tif len(f.Names) == 0 {\n\t\treturn t\n\t}\n\tnames := make([]string, len(f.Names))\n\tfor i, n := range f.Names {\n\t\tnames[i] = n.Name\n\t}\n\treturn strings.Join(names, \", \") + \" \" + t\n}\n\nfunc formatFieldList(fields []*ast.Field) string {\n\tparts := make([]string, len(fields))\n\tfor i, f := range fields {\n\t\tparts[i] = formatField(f)\n\t}\n\treturn \"(\" + strings.Join(parts, \", \") + \")\"\n}\n\n// formatFields renders struct fields and interface members.\nfunc formatFields(fields []*ast.Field) string {\n\tparts := make([]string, len(fields))\n\tfor i, f := range fields {\n\t\tif ft, ok := f.Type.(*ast.FuncType); ok && len(f.Names) == 1 {\n\t\t\tparts[i] = f.Names[0].Name + formatFuncType(ft)\n\t\t} else {\n\t\t\tparts[i] = formatField(f)\n\t\t}\n\t}\n\treturn strings.Join(parts, \"; \")\n}\n\nfunc formatTypeParams(tp *ast.FieldList) string {\n\tif tp == nil || len(tp.List) == 0 {\n\t\treturn \"\"\n\t}\n\tparts := make([]string, len(tp.List))\n\tfor i, p := range tp.List {\n\t\tparts[i] = formatField(p)\n\t}\n\treturn \"[\" + strings.Join(parts, \", \") + \"]\"\n}\n\nfunc formatType(t ast.Expr) string {\n\tif t == nil {\n\t\treturn \"?\"\n\t}\n\tswitch v := t.(type) {\n\tcase *ast.Ident:\n\t\treturn v.Name\n\tcase *ast.SelectorExpr:\n\t\treturn formatType(v.X) + \".\" + v.Sel.Name\n\tcase *ast.StarExpr:\n\t\treturn \"*\" + formatType(v.X)\n\tcase *ast.ParenExpr:\n\t\treturn \"(\" + formatType(v.X) + \")\"\n\tcase *ast.Ellipsis:\n\t\tif v.Elt == nil {\n\t\t\treturn \"...\"\n\t\t}\n\t\treturn \"...\" + formatType(v.Elt)\n\tcase *ast.ArrayType:\n\t\tif v.Len == nil {\n\t\t\treturn \"[]\" + formatType(v.Elt)\n\t\t}\n\t\treturn \"[\" + formatType(v.Len) + \"]\" + formatType(v.Elt)\n\tcase *ast.MapType:\n\t\treturn \"map[\" + formatType(v.Key) + \"]\" + formatType(v.Value)\n\tcase *ast.ChanType:\n\t\tswitch v.Dir {\n\t\tcase ast.SEND:\n\t\t\treturn \"chan<- \" + formatType(v.Value)\n\t\tcase ast.RECV:\n\t\t\treturn \"<-chan \" + formatType(v.Value)\n\t\tdefault:\n\t\t\treturn \"chan \" + formatType(v.Value)\n\t\t}\n\tcase *ast.InterfaceType:\n\t\tif len(fieldsOf(v.Methods)) == 0 {\n\t\t\treturn \"interface{}\"\n\t\t}\n\t\treturn \"interface{ \" + formatFields(v.Methods.List) + \" }\"\n\tcase *ast.StructType:\n\t\tif len(fieldsOf(v.Fields)) == 0 {\n\t\t\treturn \"struct{}\"\n\t\t}\n\t\treturn \"struct{ \" + formatFields(v.Fields.List) + \" }\"\n\tcase *ast.FuncType:\n\t\treturn \"func\" + formatFuncType(v)\n\tcase *ast.UnaryExpr:\n\t\treturn v.Op.String() + formatType(v.X)\n\tcase *ast.BinaryExpr:\n\t\treturn formatType(v.X) + \" \" + v.Op.String() + \" \" + formatType(v.Y)\n\tcase *ast.BasicLit:\n\t\treturn v.Value\n\tcase *ast.IndexExpr:\n\t\treturn formatType(v.X) + \"[\" + formatType(v.Index) + \"]\"\n\tcase *ast.IndexListExpr:\n\t\targs := make([]string, len(v.Indices))\n\t\tfor i, idx := range v.Indices {\n\t\t\targs[i] = formatType(idx)\n\t\t}\n\t\treturn formatType(v.X) + \"[\" + strings.Join(args, \", \") + \"]\"\n\tdefault:\n\t\treturn \"?\"\n\t}\n}\n\ntype FileResult struct {\n\tFile string `json:\"file\"`\n\tError string `json:\"error,omitempty\"`\n\tSymbols []Sym `json:\"symbols\"`\n\tRefs []Ref `json:\"refs\"`\n}\n\ntype BatchResult struct {\n\tVersion int `json:\"version\"`\n\tResults []FileResult `json:\"results\"`\n}\n\ntype inputFile struct {\n\tFile string `json:\"file\"`\n\tContent string `json:\"content\"`\n}\n\nfunc main() {\n\tvar inputs []inputFile\n\traw, err := io.ReadAll(os.Stdin)\n\tif err == nil {\n\t\terr = json.Unmarshal(raw, &inputs)\n\t}\n\tresults := make([]FileResult, 0, len(inputs))\n\tif err == nil {\n\t\tfor _, in := range inputs {\n\t\t\tres := FileResult{File: in.File, Symbols: []Sym{}, Refs: []Ref{}}\n\t\t\tif syms, refs, perr := extract([]byte(in.Content)); perr != nil {\n\t\t\t\tres.Error = perr.Error()\n\t\t\t} else {\n\t\t\t\tres.Symbols, res.Refs = syms, refs\n\t\t\t}\n\t\t\tresults = append(results, res)\n\t\t}\n\t}\n\tdata, merr := json.Marshal(BatchResult{Version: 1, Results: results})\n\tif merr != nil {\n\t\tfmt.Print(`{\"version\":1,\"results\":[]}`)\n\t\treturn\n\t}\n\tfmt.Print(string(data))\n}\n"; /** One Python source on stdin (file path in argv[1]) → `{"symbols", "refs"}`. */ export declare const PY_PARSE_SCRIPT = "import ast, json, sys\n\n# Decorators whose function is an attribute accessor, by last dotted segment.\nPROPERTY_DECORATORS = (\"property\", \"cached_property\", \"abstractproperty\", \"setter\", \"getter\", \"deleter\")\n\ndef get_name(node):\n if isinstance(node, ast.Name):\n return node.id\n if isinstance(node, ast.Attribute):\n return get_name(node.value) + \".\" + node.attr\n if isinstance(node, ast.Subscript):\n return get_name(node.value)\n if isinstance(node, ast.Call):\n return get_name(node.func)\n if isinstance(node, ast.Constant):\n return str(node.value)\n return \"\"\n\ndef leaf_name(node):\n # Declared name of the callee: `join` of `os.path.join`. Matches how the\n # TypeScript and Go extractors record call refs.\n if isinstance(node, ast.Attribute):\n return node.attr\n if isinstance(node, ast.Name):\n return node.id\n return get_name(node).split(\".\")[-1]\n\ndef is_private(name):\n return name.startswith(\"__\") and not name.endswith(\"__\")\n\ndef first_doc_line(node):\n try:\n doc = ast.get_docstring(node)\n except Exception:\n return \"\"\n for line in (doc or \"\").splitlines():\n line = line.strip()\n if line:\n return line[:200]\n return \"\"\n\ndef module_name_of(path):\n base = path.replace(\"\\\\\", \"/\").rsplit(\"/\", 1)[-1]\n return base.rsplit(\".\", 1)[0] if \".\" in base else base\n\ndef read_stdin_text():\n # Decode explicitly: sys.stdin uses the locale code page on Windows\n # (cp1254, cp1252, ...), which mangles or rejects the UTF-8 we are sent.\n return sys.stdin.buffer.read().decode(\"utf-8\", \"replace\")\n\ndef parse_source(text, name):\n # ast.parse rejects a leading U+FEFF in a str source.\n if text.startswith(\"\\ufeff\"):\n text = text[1:]\n return ast.parse(text, filename=name or \"\")\n\ndef format_args(args):\n parts = [a.arg for a in list(getattr(args, \"posonlyargs\", [])) + list(args.args)]\n if args.vararg is not None:\n parts.append(\"*\" + args.vararg.arg)\n elif args.kwonlyargs:\n parts.append(\"*\")\n parts.extend(a.arg for a in args.kwonlyargs)\n if args.kwarg is not None:\n parts.append(\"**\" + args.kwarg.arg)\n return \", \".join(parts)\n\ndef extract(tree, module_name):\n syms = []\n refs = []\n scope = [module_name]\n\n def add(name, kind, line, col, signature, sym_scope, doc=\"\"):\n syms.append({\n \"name\": name, \"kind\": kind, \"line\": line, \"col\": col,\n \"signature\": signature, \"scope\": sym_scope, \"doc\": doc,\n })\n\n def qualified(name):\n return \".\".join(scope) + \".\" + name\n\n # Kinds are the index schema's (class/function/method/property/var/const/\n # type). Imports are refs, not symbols: an import symbol per file made\n # every imported name look like a definition in every importing module.\n class Visitor(ast.NodeVisitor):\n in_class = False\n\n def visit_ClassDef(self, node):\n bases = [get_name(b) for b in node.bases]\n sig = \"class \" + node.name + (\"(\" + \", \".join(bases) + \")\" if bases else \"\") + \": ...\"\n add(node.name, \"class\", node.lineno, node.col_offset, sig, qualified(node.name), first_doc_line(node))\n scope.append(node.name)\n outer = self.in_class\n self.in_class = True\n try:\n self.generic_visit(node)\n finally:\n self.in_class = outer\n scope.pop()\n\n def visit_FunctionDef(self, node):\n kind = \"method\" if self.in_class else \"function\"\n for dec in node.decorator_list:\n if get_name(dec).rsplit(\".\", 1)[-1] in PROPERTY_DECORATORS:\n kind = \"property\"\n prefix = \"async def \" if isinstance(node, ast.AsyncFunctionDef) else \"def \"\n sig = prefix + node.name + \"(\" + format_args(node.args) + \")\"\n returns = get_name(node.returns) if node.returns is not None else \"\"\n if returns:\n sig += \" -> \" + returns\n add(node.name, kind, node.lineno, node.col_offset, sig, qualified(node.name), first_doc_line(node))\n # Bodies are not visited: function locals are not index symbols.\n\n visit_AsyncFunctionDef = visit_FunctionDef\n\n def add_target(self, target, line, annotation=None, has_value=True):\n if isinstance(target, ast.Name):\n if is_private(target.id):\n return\n kind = \"const\" if target.id.isupper() else \"var\"\n if annotation is not None:\n sig = target.id + \": \" + get_name(annotation) + (\" = ...\" if has_value else \"\")\n else:\n sig = target.id + \" = ...\"\n add(target.id, kind, line, target.col_offset, sig, \".\".join(scope))\n elif isinstance(target, (ast.Tuple, ast.List)):\n for elt in target.elts:\n self.add_target(elt, line)\n elif isinstance(target, ast.Starred):\n self.add_target(target.value, line)\n\n def visit_Assign(self, node):\n for target in node.targets:\n self.add_target(target, node.lineno)\n\n def visit_AnnAssign(self, node):\n self.add_target(node.target, node.lineno, node.annotation, node.value is not None)\n\n def visit_TypeAlias(self, node):\n if isinstance(node.name, ast.Name):\n add(node.name.id, \"type\", node.lineno, node.col_offset, \"type \" + node.name.id + \" = ...\", \".\".join(scope))\n\n Visitor().visit(tree)\n\n # Refs need a full walk: the visitor deliberately skips function bodies,\n # which is exactly where the calls are.\n for node in ast.walk(tree):\n if isinstance(node, ast.Call):\n name = leaf_name(node.func)\n if name:\n refs.append({\"toName\": name, \"callType\": \"call\", \"line\": node.lineno})\n elif isinstance(node, ast.Import):\n for alias in node.names:\n refs.append({\n \"toName\": alias.name.split(\".\")[-1], \"callType\": \"import\",\n \"line\": node.lineno, \"module\": alias.name,\n })\n elif isinstance(node, ast.ImportFrom):\n # PEP 328: keep the leading dots so the resolver can walk up from\n # the importing file's package.\n module = (\".\" * (node.level or 0)) + (node.module or \"\")\n for alias in node.names:\n refs.append({\n \"toName\": alias.name, \"callType\": \"import\",\n \"line\": node.lineno, \"module\": module,\n })\n elif isinstance(node, ast.ClassDef):\n for base in node.bases:\n name = leaf_name(base)\n if name:\n refs.append({\"toName\": name, \"callType\": \"inherit\", \"line\": node.lineno})\n\n return syms, refs\n\ndef main():\n name = sys.argv[1] if len(sys.argv) > 1 else \"module.py\"\n try:\n syms, refs = extract(parse_source(read_stdin_text(), name), module_name_of(name))\n except Exception:\n syms, refs = [], []\n print(json.dumps({\"symbols\": syms, \"refs\": refs}))\n\nmain()\n"; /** A JSON array of `{file, content}` on stdin → one envelope with per-file results. */ export declare const PY_BATCH_SCRIPT = "import ast, json, sys\n\n# Decorators whose function is an attribute accessor, by last dotted segment.\nPROPERTY_DECORATORS = (\"property\", \"cached_property\", \"abstractproperty\", \"setter\", \"getter\", \"deleter\")\n\ndef get_name(node):\n if isinstance(node, ast.Name):\n return node.id\n if isinstance(node, ast.Attribute):\n return get_name(node.value) + \".\" + node.attr\n if isinstance(node, ast.Subscript):\n return get_name(node.value)\n if isinstance(node, ast.Call):\n return get_name(node.func)\n if isinstance(node, ast.Constant):\n return str(node.value)\n return \"\"\n\ndef leaf_name(node):\n # Declared name of the callee: `join` of `os.path.join`. Matches how the\n # TypeScript and Go extractors record call refs.\n if isinstance(node, ast.Attribute):\n return node.attr\n if isinstance(node, ast.Name):\n return node.id\n return get_name(node).split(\".\")[-1]\n\ndef is_private(name):\n return name.startswith(\"__\") and not name.endswith(\"__\")\n\ndef first_doc_line(node):\n try:\n doc = ast.get_docstring(node)\n except Exception:\n return \"\"\n for line in (doc or \"\").splitlines():\n line = line.strip()\n if line:\n return line[:200]\n return \"\"\n\ndef module_name_of(path):\n base = path.replace(\"\\\\\", \"/\").rsplit(\"/\", 1)[-1]\n return base.rsplit(\".\", 1)[0] if \".\" in base else base\n\ndef read_stdin_text():\n # Decode explicitly: sys.stdin uses the locale code page on Windows\n # (cp1254, cp1252, ...), which mangles or rejects the UTF-8 we are sent.\n return sys.stdin.buffer.read().decode(\"utf-8\", \"replace\")\n\ndef parse_source(text, name):\n # ast.parse rejects a leading U+FEFF in a str source.\n if text.startswith(\"\\ufeff\"):\n text = text[1:]\n return ast.parse(text, filename=name or \"\")\n\ndef format_args(args):\n parts = [a.arg for a in list(getattr(args, \"posonlyargs\", [])) + list(args.args)]\n if args.vararg is not None:\n parts.append(\"*\" + args.vararg.arg)\n elif args.kwonlyargs:\n parts.append(\"*\")\n parts.extend(a.arg for a in args.kwonlyargs)\n if args.kwarg is not None:\n parts.append(\"**\" + args.kwarg.arg)\n return \", \".join(parts)\n\ndef extract(tree, module_name):\n syms = []\n refs = []\n scope = [module_name]\n\n def add(name, kind, line, col, signature, sym_scope, doc=\"\"):\n syms.append({\n \"name\": name, \"kind\": kind, \"line\": line, \"col\": col,\n \"signature\": signature, \"scope\": sym_scope, \"doc\": doc,\n })\n\n def qualified(name):\n return \".\".join(scope) + \".\" + name\n\n # Kinds are the index schema's (class/function/method/property/var/const/\n # type). Imports are refs, not symbols: an import symbol per file made\n # every imported name look like a definition in every importing module.\n class Visitor(ast.NodeVisitor):\n in_class = False\n\n def visit_ClassDef(self, node):\n bases = [get_name(b) for b in node.bases]\n sig = \"class \" + node.name + (\"(\" + \", \".join(bases) + \")\" if bases else \"\") + \": ...\"\n add(node.name, \"class\", node.lineno, node.col_offset, sig, qualified(node.name), first_doc_line(node))\n scope.append(node.name)\n outer = self.in_class\n self.in_class = True\n try:\n self.generic_visit(node)\n finally:\n self.in_class = outer\n scope.pop()\n\n def visit_FunctionDef(self, node):\n kind = \"method\" if self.in_class else \"function\"\n for dec in node.decorator_list:\n if get_name(dec).rsplit(\".\", 1)[-1] in PROPERTY_DECORATORS:\n kind = \"property\"\n prefix = \"async def \" if isinstance(node, ast.AsyncFunctionDef) else \"def \"\n sig = prefix + node.name + \"(\" + format_args(node.args) + \")\"\n returns = get_name(node.returns) if node.returns is not None else \"\"\n if returns:\n sig += \" -> \" + returns\n add(node.name, kind, node.lineno, node.col_offset, sig, qualified(node.name), first_doc_line(node))\n # Bodies are not visited: function locals are not index symbols.\n\n visit_AsyncFunctionDef = visit_FunctionDef\n\n def add_target(self, target, line, annotation=None, has_value=True):\n if isinstance(target, ast.Name):\n if is_private(target.id):\n return\n kind = \"const\" if target.id.isupper() else \"var\"\n if annotation is not None:\n sig = target.id + \": \" + get_name(annotation) + (\" = ...\" if has_value else \"\")\n else:\n sig = target.id + \" = ...\"\n add(target.id, kind, line, target.col_offset, sig, \".\".join(scope))\n elif isinstance(target, (ast.Tuple, ast.List)):\n for elt in target.elts:\n self.add_target(elt, line)\n elif isinstance(target, ast.Starred):\n self.add_target(target.value, line)\n\n def visit_Assign(self, node):\n for target in node.targets:\n self.add_target(target, node.lineno)\n\n def visit_AnnAssign(self, node):\n self.add_target(node.target, node.lineno, node.annotation, node.value is not None)\n\n def visit_TypeAlias(self, node):\n if isinstance(node.name, ast.Name):\n add(node.name.id, \"type\", node.lineno, node.col_offset, \"type \" + node.name.id + \" = ...\", \".\".join(scope))\n\n Visitor().visit(tree)\n\n # Refs need a full walk: the visitor deliberately skips function bodies,\n # which is exactly where the calls are.\n for node in ast.walk(tree):\n if isinstance(node, ast.Call):\n name = leaf_name(node.func)\n if name:\n refs.append({\"toName\": name, \"callType\": \"call\", \"line\": node.lineno})\n elif isinstance(node, ast.Import):\n for alias in node.names:\n refs.append({\n \"toName\": alias.name.split(\".\")[-1], \"callType\": \"import\",\n \"line\": node.lineno, \"module\": alias.name,\n })\n elif isinstance(node, ast.ImportFrom):\n # PEP 328: keep the leading dots so the resolver can walk up from\n # the importing file's package.\n module = (\".\" * (node.level or 0)) + (node.module or \"\")\n for alias in node.names:\n refs.append({\n \"toName\": alias.name, \"callType\": \"import\",\n \"line\": node.lineno, \"module\": module,\n })\n elif isinstance(node, ast.ClassDef):\n for base in node.bases:\n name = leaf_name(base)\n if name:\n refs.append({\"toName\": name, \"callType\": \"inherit\", \"line\": node.lineno})\n\n return syms, refs\n\ndef main():\n try:\n inputs = json.loads(read_stdin_text())\n except Exception:\n print(json.dumps({\"version\": 1, \"results\": []}))\n return\n results = []\n for entry in inputs:\n name = entry.get(\"file\", \"\")\n result = {\"file\": name, \"symbols\": [], \"refs\": []}\n try:\n syms, refs = extract(parse_source(entry.get(\"content\", \"\"), name), module_name_of(name))\n result[\"symbols\"] = syms\n result[\"refs\"] = refs\n except Exception as e:\n result[\"error\"] = str(e) or type(e).__name__\n results.append(result)\n print(json.dumps({\"version\": 1, \"results\": results}))\n\nmain()\n"; /** * Path of `script` written to a PRIVATE, UNPREDICTABLE directory, written once * per process. * * Security: a fixed location such as `os.tmpdir()/ws-py-parse/parse.py` can be * pre-created by another local user, who then either symlinks the script path * (our write follows the link) or leaves their own `ast.py`/`json.py` next to * it — Python puts the script's directory first on `sys.path`, so indexing * would run their module. `fs.mkdtemp` creates the directory with a random * suffix (0700 on POSIX) and `wx` refuses to write through anything already at * the path. The in-flight promise is cached so concurrent first calls share * one directory instead of racing to create several. */ export declare function privateScriptPath(prefix: string, fileName: string, script: string): Promise; /** Test-only: every script path written so far (failed writes resolve to ''). */ export declare function __privateScriptPathsForTest(): Promise; interface ToolchainSpawnOptions { cwd: string; env?: NodeJS.ProcessEnv; } /** * Run `go run` from the script's own directory, never the indexed project. * * Launched from the project, the go command reads the project's `go.mod` and * `go.work`: a `go`/`toolchain` line newer than the installed Go made every * parse fail ("requires go >= …"), dropping the whole repository to the regex * fallback — or, with the default `GOTOOLCHAIN=auto`, made indexing download * and run whatever toolchain the scanned repository named. The parser only * needs the standard library, so it runs outside any module on the local * toolchain. */ export declare function goSpawnOptions(scriptPath: string): ToolchainSpawnOptions; /** Python runs from the script directory too, so the project is never on its path. */ export declare function pySpawnOptions(scriptPath: string): ToolchainSpawnOptions; /** * Spawn a parser child, feed `stdinPayload`, collect stdout. * Resolves `null` on spawn failure or timeout; never rejects. */ export declare function runToolchainChild(binary: string, args: string[], stdinPayload: string, timeoutMs: number, options: ToolchainSpawnOptions): Promise<{ code: number | null; stdout: string; } | null>; export {}; //# sourceMappingURL=toolchain-scripts.d.ts.map