GoPLS Viewer

Home|gopls/internal/stack/parse.go
1// Copyright 2020 The Go Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style
3// license that can be found in the LICENSE file.
4
5package stack
6
7import (
8    "bufio"
9    "errors"
10    "io"
11    "regexp"
12    "strconv"
13)
14
15var (
16    reBlank     = regexp.MustCompile(`^\s*$`)
17    reGoroutine = regexp.MustCompile(`^\s*goroutine (\d+) \[([^\]]*)\]:\s*$`)
18    reCall      = regexp.MustCompile(`^\s*` +
19        `(created by )?` + //marker
20        `(([\w/.]+/)?[\w]+)\.` + //package
21        `(\(([^:.)]*)\)\.)?` + //optional type
22        `([\w\.]+)` + //function
23        `(\(.*\))?` + // args
24        `\s*$`)
25    rePos = regexp.MustCompile(`^\s*(.*):(\d+)( .*)?$`)
26
27    errBreakParse = errors.New("break parse")
28)
29
30// Scanner splits an input stream into lines in a way that is consumable by
31// the parser.
32type Scanner struct {
33    lines *bufio.Scanner
34    done  bool
35}
36
37// NewScanner creates a scanner on top of a reader.
38func NewScanner(r io.Reader) *Scanner {
39    s := &Scanner{
40        linesbufio.NewScanner(r),
41    }
42    s.Skip() // prefill
43    return s
44}
45
46// Peek returns the next line without consuming it.
47func (s *ScannerPeek() string {
48    if s.done {
49        return ""
50    }
51    return s.lines.Text()
52}
53
54// Skip consumes the next line without looking at it.
55// Normally used after it has already been looked at using Peek.
56func (s *ScannerSkip() {
57    if !s.lines.Scan() {
58        s.done = true
59    }
60}
61
62// Next consumes and returns the next line.
63func (s *ScannerNext() string {
64    line := s.Peek()
65    s.Skip()
66    return line
67}
68
69// Done returns true if the scanner has reached the end of the underlying
70// stream.
71func (s *ScannerDone() bool {
72    return s.done
73}
74
75// Err returns true if the scanner has reached the end of the underlying
76// stream.
77func (s *ScannerErr() error {
78    return s.lines.Err()
79}
80
81// Match returns the submatchs of the regular expression against the next line.
82// If it matched the line is also consumed.
83func (s *ScannerMatch(re *regexp.Regexp) []string {
84    if s.done {
85        return nil
86    }
87    match := re.FindStringSubmatch(s.Peek())
88    if match != nil {
89        s.Skip()
90    }
91    return match
92}
93
94// SkipBlank skips any number of pure whitespace lines.
95func (s *ScannerSkipBlank() {
96    for !s.done {
97        line := s.Peek()
98        if len(line) != 0 && !reBlank.MatchString(line) {
99            return
100        }
101        s.Skip()
102    }
103}
104
105// Parse the current contiguous block of goroutine stack traces until the
106// scanned content no longer matches.
107func Parse(scanner *Scanner) (Dumperror) {
108    dump := Dump{}
109    for {
110        grok := parseGoroutine(scanner)
111        if !ok {
112            return dumpnil
113        }
114        dump = append(dumpgr)
115    }
116}
117
118func parseGoroutine(scanner *Scanner) (Goroutinebool) {
119    match := scanner.Match(reGoroutine)
120    if match == nil {
121        return Goroutine{}, false
122    }
123    id_ := strconv.ParseInt(match[1], 032)
124    gr := Goroutine{
125        ID:    int(id),
126        Statematch[2],
127    }
128    for {
129        frameok := parseFrame(scanner)
130        if !ok {
131            scanner.SkipBlank()
132            return grtrue
133        }
134        if frame.Position.Filename != "" {
135            gr.Stack = append(gr.Stackframe)
136        }
137    }
138}
139
140func parseFrame(scanner *Scanner) (Framebool) {
141    funok := parseFunction(scanner)
142    if !ok {
143        return Frame{}, false
144    }
145    frame := Frame{
146        Functionfun,
147    }
148    frame.Positionok = parsePosition(scanner)
149    // if ok is false, then this is a broken state.
150    // we got the func but not the file that must follow
151    // the consumed line can be recovered from the frame
152    //TODO: push back the fun raw
153    return frameok
154}
155
156func parseFunction(scanner *Scanner) (Functionbool) {
157    match := scanner.Match(reCall)
158    if match == nil {
159        return Function{}, false
160    }
161    return Function{
162        Packagematch[2],
163        Type:    match[5],
164        Name:    match[6],
165    }, true
166}
167
168func parsePosition(scanner *Scanner) (Positionbool) {
169    match := scanner.Match(rePos)
170    if match == nil {
171        return Position{}, false
172    }
173    line_ := strconv.ParseInt(match[2], 032)
174    return Position{Filenamematch[1], Lineint(line)}, true
175}
176
MembersX
parseGoroutine._
parseGoroutine.BlockStmt.ok
parseGoroutine.scanner
Scanner.Done
Scanner.SkipBlank
Scanner.Skip.s
parseFrame.frame
Scanner.Err.s
Parse.dump
Parse.BlockStmt.ok
parseFunction.match
strconv
NewScanner
Scanner.Peek
Scanner.Done.s
Parse
parseFrame.scanner
NewScanner.r
Scanner.Next.s
parseGoroutine.match
parseGoroutine.gr
parseFunction.scanner
parsePosition._
Scanner.Peek.s
parseFrame
parsePosition.match
NewScanner.s
Scanner.Next.line
Scanner.Match
Scanner.SkipBlank.s
parseGoroutine
parseGoroutine.id
parseGoroutine.BlockStmt.frame
Scanner.lines
Scanner.Match.s
Scanner.Match.re
Scanner.Match.match
bufio
regexp
Scanner
Scanner.done
parsePosition.line
Scanner.Next
parseFrame.fun
Scanner.SkipBlank.BlockStmt.line
Parse.scanner
Parse.BlockStmt.gr
parseFunction
parsePosition
io
Scanner.Skip
parsePosition.scanner
errors
Scanner.Err
parseFrame.ok
Members
X