]>
Commit | Line | Data |
---|---|---|
1 | package scanner | |
2 | ||
3 | // Peeker is a utility that wraps a token channel returned by Scan and | |
4 | // provides an interface that allows a caller (e.g. the parser) to | |
5 | // work with the token stream in a mode that allows one token of lookahead, | |
6 | // and provides utilities for more convenient processing of the stream. | |
7 | type Peeker struct { | |
8 | ch <-chan *Token | |
9 | peeked *Token | |
10 | } | |
11 | ||
12 | func NewPeeker(ch <-chan *Token) *Peeker { | |
13 | return &Peeker{ | |
14 | ch: ch, | |
15 | } | |
16 | } | |
17 | ||
18 | // Peek returns the next token in the stream without consuming it. A | |
19 | // subsequent call to Read will return the same token. | |
20 | func (p *Peeker) Peek() *Token { | |
21 | if p.peeked == nil { | |
22 | p.peeked = <-p.ch | |
23 | } | |
24 | return p.peeked | |
25 | } | |
26 | ||
27 | // Read consumes the next token in the stream and returns it. | |
28 | func (p *Peeker) Read() *Token { | |
29 | token := p.Peek() | |
30 | ||
31 | // As a special case, we will produce the EOF token forever once | |
32 | // it is reached. | |
33 | if token.Type != EOF { | |
34 | p.peeked = nil | |
35 | } | |
36 | ||
37 | return token | |
38 | } | |
39 | ||
40 | // Close ensures that the token stream has been exhausted, to prevent | |
41 | // the goroutine in the underlying scanner from leaking. | |
42 | // | |
43 | // It's not necessary to call this if the caller reads the token stream | |
44 | // to EOF, since that implicitly closes the scanner. | |
45 | func (p *Peeker) Close() { | |
46 | for _ = range p.ch { | |
47 | // discard | |
48 | } | |
49 | // Install a synthetic EOF token in 'peeked' in case someone | |
50 | // erroneously calls Peek() or Read() after we've closed. | |
51 | p.peeked = &Token{ | |
52 | Type: EOF, | |
53 | Content: "", | |
54 | } | |
55 | } |